230 lines
5.1 KiB
Markdown
230 lines
5.1 KiB
Markdown
# 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
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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:
|
|
|
|
```text
|
|
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-table`
|
|
- `use-debounce`
|
|
- `use-debounced-callback`
|
|
- `use-nav`
|
|
- `use-mobile`
|
|
- `use-media-query`
|
|
|
|
Shared lib:
|
|
|
|
- `api-client.ts`
|
|
- `db.ts`
|
|
- `query-client.ts`
|
|
- `searchparams.ts`
|
|
- `parsers.ts`
|
|
- `format.ts`
|
|
- `auth/session.ts`
|
|
- `auth/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 actor
|
|
- `employees`: HR-owned worker profile, training owner, matrix targeting, and compliance/reporting subject
|
|
|
|
Current compatibility seams:
|
|
|
|
- `users.employeeId`
|
|
- `user_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:
|
|
|
|
- `users`
|
|
- `organizations`
|
|
- `memberships`
|
|
- `courses`
|
|
- `training_records`
|
|
- `training_matrix`
|
|
- `reports`
|
|
- `announcements`
|
|
- `online_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:
|
|
|
|
- `IT`
|
|
- `HRD`
|
|
- `EMPLOYEE`
|
|
|
|
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:
|
|
|
|
```text
|
|
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
|
|
- `useDataTable`
|
|
- `DataTable`
|
|
- `DataTableToolbar`
|
|
- `DataTablePagination`
|
|
- `DataTableViewOptions`
|
|
- `DataTableColumnHeader`
|
|
|
|
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:
|
|
|
|
- `products`
|
|
- `employees`
|
|
- `forms`
|
|
- `kanban`
|
|
- `chat`
|
|
- demo/react-query pages
|
|
|
|
Treat these as legacy/template features unless the user specifically asks to modernize them.
|