7.9 KiB
AI Development Guide
This document is the project operating guide for AI coding agents. It records the current implementation patterns that must be reused before adding new code.
Priority Order
When instructions conflict, follow this order:
- Existing project implementation
docs/AI_DEVELOPMENT_GUIDE.mddocs/PROJECT_ARCHITECTURE.mdAGENTS.mdkiranism-shadcn-dashboard- shadcn/ui
- TanStack Table, TanStack Query, TanStack Form, and nuqs docs
- Next.js best practices
Canonical Features
Use these features as references before creating or changing a feature:
training-records: canonical full feature for DB-backed CRUD, tables, forms, upload, review flow, React Query, and Route Handlers.employee-directory: canonical responsive DataTable and table toolbar customization.announcements: canonical lightweight content feature with publish/archive state and file attachment.audit-logs: canonical read-only table with filters.
Feature Structure
Runtime features should live under src/features/<feature-name>/.
Preferred structure:
src/features/<feature>/
api/
types.ts
service.ts
queries.ts
mutations.ts
components/
constants/
schemas/
server/
Use only the folders a feature actually needs. Do not create placeholder folders.
Dashboard routes live in src/app/dashboard/<route>/page.tsx.
Route handlers live in src/app/api/<feature>/route.ts and src/app/api/<feature>/[id]/route.ts.
Naming Rules
- Use kebab-case filenames for feature components, routes, and docs.
- Use PascalCase React components.
- Keep API contracts in
api/types.ts. - Name table files after local convention: either
<feature>-table.tsxand<feature>-columns.tsx, orcomponents/<feature>-tables/index.tsxandcolumns.tsxwhen the feature already uses that folder. - Name action menu components consistently, for example
pending-review-action-menu.tsxoremployee-directory-action-menu.tsx.
Data Flow
Use the established app-owned flow:
Page Server Component
-> require*DashboardAccess()
-> searchParamsCache.parse()
-> Listing Component
-> getQueryClient().fetchQuery()
-> HydrationBoundary
-> Client Table/Form Component
-> useSuspenseQuery()/useQuery()
-> api/service.ts
-> local Route Handler
-> server/* data helper
-> Drizzle
Do not import Drizzle into UI components. Do not import mock API data into runtime UI.
Authentication And Authorization
Use Auth.js and the shared helpers:
requireEmployeeDashboardAccess()requireHRDDashboardAccess()requireITDashboardAccess()requireOrganizationAccess()requireHRD()isHRD(),isIT(),getBusinessRole()
Nav filtering is only UX. Route handlers and server pages must enforce access.
User And Employee Boundary
Use these definitions consistently during the current migration state:
users= system identity, authentication, session, permissions, and audit actoremployees= HR master data, employee profile, and training ownership
Current runtime reality is transitional:
users.employeeIdstill exists as a compatibility seamuser_employee_mapsstill exists as an explicit linking table- training ownership and reporting scope still resolve primarily through
employees
Treat the canonical relationship as User 0..1 <-> 0..1 Employee until a later migration removes the seam.
Rules:
- actor fields such as
createdBy,approvedBy,publishedBy, and audit actor must point tousers - owner fields for training targets, compliance, and matrix applicability must point to
employees - do not introduce a third user-employee linking mechanism
Table Pattern
Default table implementation must reuse:
DataTableDataTableToolbarDataTablePaginationDataTableViewOptionsDataTableColumnHeaderuseDataTable- TanStack
ColumnDef - action menu components for row commands
Canonical references:
src/features/training-records/components/training-record-tables/index.tsxsrc/features/training-records/components/pending-review-table.tsxsrc/features/employee-directory/components/employee-directory-table.tsx
Rules:
- Do not build manual table pagination for new data tables.
- Do not create a new toolbar if
DataTableToolbaror a small wrapper around it is enough. - Use
columnPinning: { right: ['actions'] }for row actions. - Use
DataTableColumnHeaderfor sortable headers. - Use
metaon columns for labels, placeholders, filter variants, options, and class names. - Put horizontal overflow inside the DataTable shell, not the full page.
Manual tables are acceptable only for tiny static layouts or legacy code being intentionally left unchanged.
Form Pattern
The project uses TanStack Form, not React Hook Form.
Use:
useAppFormuseFormFields- field wrappers from
@/components/ui/tanstack-form - Zod schemas from
src/features/<feature>/schemas scrollToFirstError()where helpful
Canonical references:
src/features/training-records/components/training-record-form.tsxsrc/features/courses/components/course-form.tsxsrc/features/training-policy/components/training-policy-form.tsx
Mutation Pattern
Use TanStack Query mutation option factories in api/mutations.ts.
component -> useMutation({ ...featureMutation })
mutation -> api/service.ts
service -> apiClient()
route handler -> Drizzle
This repo does not use next-safe-action as a canonical runtime pattern.
Persistence Pattern
The project uses PostgreSQL with Drizzle ORM.
Use:
src/db/schema.tssrc/lib/db.ts- feature
server/*-data.tshelpers - route handlers under
src/app/api
Do not introduce Prisma.
Dialog And Action Pattern
Use existing shadcn/Radix wrappers:
DialogSheetAlertDialogAlertModal- dropdown action menus via
DropdownMenu
Row actions should be grouped into action menu components when there are multiple commands.
Upload Pattern
Reuse existing upload utilities and storage helpers:
FileUploadercertificate-storage.tsannouncement-storage.tsonline-lesson-storage.ts
Do not create new storage logic until the existing helper cannot support the feature.
Layout Pattern
Dashboard pages must use PageContainer.
Rules:
- Do not create a new dashboard shell.
- Do not nest cards inside cards.
- Use cards for forms, filters, empty states, and repeated content items.
- Use
w-full min-w-0 max-w-fullon table containers. - Keep overflow scoped to tables with
overflow-x-auto. - Buttons must not clip on mobile; use wrapping containers and
shrink-0 whitespace-nowrapwhen needed.
UI Rules
- Use
@/components/iconsonly for icons. - Use
cn()for className composition. - Use shadcn/ui primitives from
@/components/ui. - Use
Badgefor status. - Use
Buttonvariants rather than custom button styling. - Prefer Thai labels in user-facing training-system UI.
Refactoring Rules
- First inspect existing feature, shared components, hooks, forms, tables, and dialogs.
- If code is off-pattern, propose or perform a scoped refactor before expanding it.
- Do not propagate legacy patterns into new work.
- Avoid broad rewrites unless the requested change touches shared contracts or UI behavior.
- Dashboard
page.tsxentrypoints must call a sharedrequire*DashboardAccess()helper unless the route is an explicit redirect shell or documented exception.
Required Checklist
Before final response, verify:
- Existing pattern was inspected.
- Shared components/hooks were reused.
- DataTable was used for data tables.
- TanStack Form and Zod were used for forms.
- Route handlers and Drizzle were used for persistence.
- Server-side access checks remain in place.
- Page layout is responsive and does not overflow.
- Icons come from
@/components/icons. - No mock data was imported into runtime UI.
Final principle: consistency is more important than novelty. New work should look like it has always belonged to this codebase.