5.1 KiB
Project Architecture
This project is a Next.js 16 App Router training-system dashboard using Auth.js, Drizzle ORM, PostgreSQL, shadcn/ui, Tailwind CSS v4, TanStack Query, TanStack Table, TanStack Form, and nuqs.
Top-Level Structure
src/
app/
api/
auth/
dashboard/
components/
layout/
ui/
forms/
config/
db/
features/
hooks/
lib/
styles/
types/
App Router
Dashboard pages live under src/app/dashboard.
Common page pattern:
page.tsx
-> require*DashboardAccess()
-> parse search params
-> PageContainer
-> feature listing/detail/form component
API route handlers live under src/app/api. Route handlers are the app boundary for persistence and access enforcement.
Feature Modules
Feature modules live under src/features/<name>.
Strong feature examples:
training-records: complete DB-backed feature with tables, forms, upload, review, and certificates.courses: DB-backed CRUD with table and form.users: app-owned user management.employee-directory: responsive directory table and detail/edit flows.announcements: content publishing with role-aware visibility.online-lessons: DB-backed lessons with upload, role-aware list/detail/manage flows.audit-logs: read-only audit table.
Feature dependency direction:
feature components
-> feature api queries/mutations
-> feature api service
-> apiClient
-> route handlers
-> feature server data helpers
-> db/schema and db client
Feature components may import shared UI, hooks, utilities, and feature contracts. They must not import Drizzle or database schema directly.
Shared Modules
Shared UI:
src/components/ui/*: shadcn/Radix primitives and app wrappers.src/components/ui/table/*: canonical DataTable stack.src/components/ui/tanstack-form.tsx: canonical form stack.src/components/layout/page-container.tsx: dashboard page shell.src/components/icons.tsx: icon registry.
Shared hooks:
use-data-tableuse-debounceuse-debounced-callbackuse-navuse-mobileuse-media-query
Shared lib:
api-client.tsdb.tsquery-client.tssearchparams.tsparsers.tsformat.tsauth/session.tsauth/roles.ts- storage helpers for certificates, announcements, and online lessons
Data Model
Core runtime identity is users, not legacy employees.
During the current remediation phase, the implementation is intentionally split across two related domains:
users: authentication, session, permissions, organization context, and audit actoremployees: HR-owned worker profile, training owner, matrix targeting, and compliance/reporting subject
Current compatibility seams:
users.employeeIduser_employee_maps
Current canonical relationship:
User 0..1 <-> 0..1 Employee
This means some authenticated users may not have employee profiles, and some employees may exist before a user account is provisioned.
Important tables and domains:
usersorganizationsmembershipscoursestraining_recordstraining_matrixreportsannouncementsonline_lessons- audit logs
employees is legacy migration data and should not be used for new runtime behavior.
Auth And RBAC
Authentication is Auth.js Credentials.
Access helpers:
requireSession()requireOrganizationAccess()requireEmployeeDashboardAccess()requireHRDDashboardAccess()requireITDashboardAccess()
Business roles:
ITHRDEMPLOYEE
System role:
super_admin
Access must be enforced server-side in pages and route handlers. Sidebar filtering is user experience only.
Data Fetching
Preferred list flow:
server listing component
-> getQueryClient()
-> fetchQuery(queryOptions(filters))
-> HydrationBoundary
-> client component
-> useSuspenseQuery(queryOptions(filters))
Query options live in src/features/<feature>/api/queries.ts.
Mutations live in src/features/<feature>/api/mutations.ts.
Service functions call local route handlers through apiClient.
Tables
Canonical stack:
- TanStack Table
useDataTableDataTableDataTableToolbarDataTablePaginationDataTableViewOptionsDataTableColumnHeader
Tables should manage URL state through nuqs via useDataTable and parser helpers.
Forms
Canonical stack:
- TanStack Form
- Zod
useAppForm- project field wrappers
Form submit should call feature mutation options. Forms should not call route handlers or Drizzle directly.
Uploads
Upload flows are feature-specific but should reuse existing helpers:
- certificates: training records
- announcements: announcement attachments
- online lessons: video, attachment, thumbnail
Route handlers validate file type and size before storage.
Navigation
Navigation lives in src/config/nav-config.ts.
Role-aware display is handled through nav access metadata and use-nav. This does not replace server-side guards.
Current Legacy Areas
Some template or older areas remain:
productsemployeesformskanbanchat- demo/react-query pages
Treat these as legacy/template features unless the user specifically asks to modernize them.