257 lines
7.9 KiB
Markdown
257 lines
7.9 KiB
Markdown
# 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/<feature-name>/`.
|
|
|
|
Preferred structure:
|
|
|
|
```text
|
|
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.tsx` and `<feature>-columns.tsx`, or `components/<feature>-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/<feature>/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.
|