6.0 KiB
6.0 KiB
Organizers Module Enhancement Report
Executive Summary
The organizers module was upgraded from a minimal create-and-list page into a CRUD-oriented management flow that supports soft delete, shared validation, audit logging, and runtime filtering for inactive organizers. The implementation keeps the existing Auth.js and Drizzle architecture intact and avoids introducing a new permission pattern that would conflict with the current super-admin entry flow.
Current Architecture
- Next.js App Router with route handlers under
src/app/api - Auth.js session enrichment in
src/auth.ts - Drizzle ORM schema in
src/db/schema.ts - Feature-owned API/query/mutation contracts in
src/features/organizers - Shared UI stack with
Sheet,DropdownMenu,AlertModal, andPageContainer
Current Limitation
Before this enhancement, organizers only supported create and basic list rendering. There was no edit action, no soft delete, no inactive filtering in session flows, no feature API layer, and no organizer-specific audit events.
Root Cause Analysis
- The original organizers page used direct
fetch()calls and local state instead of the shared feature API/query pattern. - The database schema did not include soft-delete metadata for organizations.
- Auth session enrichment loaded organizations without filtering inactive records.
- Route handlers existed only for
GETandPOST, so update and delete behavior was missing.
Feature Comparison
Before
- View organizers
- Create organizer
After
- View active and inactive organizers
- Create organizer
- Edit organizer name
- Soft delete organizer
- Validation for required, trim, duplicate, length, and invalid characters
- Audit log for create, update, and soft delete
- Session and switcher filtering for inactive organizers
Files Modified
src/db/schema.tsdrizzle/0017_organizers_soft_delete.sqlsrc/features/audit-logs/server/audit-service.tssrc/features/organizers/api/types.tssrc/features/organizers/api/service.tssrc/features/organizers/api/queries.tssrc/features/organizers/api/mutations.tssrc/features/organizers/schemas/organizer.tssrc/features/organizers/server/organizer-data.tssrc/app/api/organizations/route.tssrc/app/api/organizations/[id]/route.tssrc/app/api/organizations/active/route.tssrc/auth.tssrc/lib/auth/session.tssrc/features/users/server/user-data.tssrc/components/modal/alert-modal.tsxsrc/components/org-switcher.tsxsrc/features/organizers/components/organizer-form-sheet.tsxsrc/features/organizers/components/organizer-card-actions.tsxsrc/features/organizers/components/organizer-listing.tsxsrc/features/organizers/components/organizers-page.tsxsrc/app/dashboard/organizers/page.tsx
Database Changes
- Added
organizations.is_active - Added
organizations.deleted_at - Added
organizations.deleted_by - Added foreign key from
deleted_bytousers.id
API Changes
GET /api/organizationssupports organizer status filteringPOST /api/organizationsuses shared zod validation and duplicate checksPATCH /api/organizations/[id]updates organizer nameDELETE /api/organizations/[id]performs soft delete onlyPATCH /api/organizations/activerejects inactive organizers
UI Changes
- Replaced the local-state page with a React Query driven listing
- Added shared
Sheetform for create and edit - Added shared dropdown row actions
- Added confirmation modal for soft delete
- Added active/inactive status display and management notes
Permission Changes
- Read access remains aligned to the existing page gate for HRD and super admin
- Create, edit, and soft delete are restricted to
requireSuperAdmin()in the current architecture - The implementation intentionally did not force org-scoped permission helpers onto the organizer entry flow because they depend on an active organization
Validation Changes
- Organizer name is required
- Input is trimmed
- Maximum length is enforced
- Invalid characters are rejected
- Duplicate active organizer names are rejected
- Inactive and missing records are rejected on update/delete
Soft Delete Design
- Soft delete updates the organizer record instead of physically deleting it
- Disabled organizers are marked with
is_active = false - Deletion metadata is stored in
deleted_atanddeleted_by - Related users, memberships, training data, permissions, and audit history remain intact
- Inactive organizers are filtered out from runtime session and switcher flows
Audit Log
- Added
ORGANIZATIONSmodule - Added
ORGANIZATION_CREATE - Added
ORGANIZATION_UPDATE - Added
ORGANIZATION_SOFT_DELETE
Testing Result
Create
- Added backend validation and duplicate checks
- Manual runtime verification still required
Edit
- Added update route and audit logging
- Manual runtime verification still required
Delete
- Soft delete only
- Inactive organizers are blocked from re-delete
Permission
- Management actions remain server-protected by super-admin checks
- UI hides management actions for non-managing users
Regression
- Session loading now excludes inactive organizers
- Organization switching now rejects inactive organizers
- User management lookups now reject inactive target organizations
Risks
- Existing data with duplicate organizer names may need manual review before relying on new duplicate constraints operationally
- Super-admin users who disable many organizers should still be tested against real seeded data to confirm expected fallback behavior
Recommendations
- Add focused route-handler tests for organizers create/update/delete flows
- Add browser-level regression coverage for the organizers page and organization switcher
- Consider introducing a dedicated system-scoped permission helper if organizer administration expands beyond super admin
Future Improvements
- Add search and status filtering controls on the organizers page
- Surface audit history for an organizer directly from the management UI
- Support organization metadata expansion when the schema grows beyond name and plan