# 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: 1. Existing project implementation 2. `docs/AI_DEVELOPMENT_GUIDE.md` 3. `docs/PROJECT_ARCHITECTURE.md` 4. `AGENTS.md` 5. `kiranism-shadcn-dashboard` 6. shadcn/ui 7. TanStack Table, TanStack Query, TanStack Form, and nuqs docs 8. Next.js best practices ## Canonical Features Use these features as references before creating or changing a feature: 1. `training-records`: canonical full feature for DB-backed CRUD, tables, forms, upload, review flow, React Query, and Route Handlers. 2. `employee-directory`: canonical responsive DataTable and table toolbar customization. 3. `announcements`: canonical lightweight content feature with publish/archive state and file attachment. 4. `audit-logs`: canonical read-only table with filters. ## Feature Structure Runtime features should live under `src/features//`. Preferred structure: ```text src/features// 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//page.tsx`. Route handlers live in `src/app/api//route.ts` and `src/app/api//[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 `-table.tsx` and `-columns.tsx`, or `components/-tables/index.tsx` and `columns.tsx` when the feature already uses that folder. - Name action menu components consistently, for example `pending-review-action-menu.tsx` or `employee-directory-action-menu.tsx`. ## Data Flow Use the established app-owned flow: ```text 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 actor - `employees` = HR master data, employee profile, and training ownership Current runtime reality is transitional: - `users.employeeId` still exists as a compatibility seam - `user_employee_maps` still 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 to `users` - 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: - `DataTable` - `DataTableToolbar` - `DataTablePagination` - `DataTableViewOptions` - `DataTableColumnHeader` - `useDataTable` - TanStack `ColumnDef` - action menu components for row commands Canonical references: - `src/features/training-records/components/training-record-tables/index.tsx` - `src/features/training-records/components/pending-review-table.tsx` - `src/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 `DataTableToolbar` or a small wrapper around it is enough. - Use `columnPinning: { right: ['actions'] }` for row actions. - Use `DataTableColumnHeader` for sortable headers. - Use `meta` on 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: - `useAppForm` - `useFormFields` - field wrappers from `@/components/ui/tanstack-form` - Zod schemas from `src/features//schemas` - `scrollToFirstError()` where helpful Canonical references: - `src/features/training-records/components/training-record-form.tsx` - `src/features/courses/components/course-form.tsx` - `src/features/training-policy/components/training-policy-form.tsx` ## Mutation Pattern Use TanStack Query mutation option factories in `api/mutations.ts`. ```text 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.ts` - `src/lib/db.ts` - feature `server/*-data.ts` helpers - route handlers under `src/app/api` Do not introduce Prisma. ## Dialog And Action Pattern Use existing shadcn/Radix wrappers: - `Dialog` - `Sheet` - `AlertDialog` - `AlertModal` - 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: - `FileUploader` - `certificate-storage.ts` - `announcement-storage.ts` - `online-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-full` on table containers. - Keep overflow scoped to tables with `overflow-x-auto`. - Buttons must not clip on mobile; use wrapping containers and `shrink-0 whitespace-nowrap` when needed. ## UI Rules - Use `@/components/icons` only for icons. - Use `cn()` for className composition. - Use shadcn/ui primitives from `@/components/ui`. - Use `Badge` for status. - Use `Button` variants 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.tsx` entrypoints must call a shared `require*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.