Files
alla-tms/docs/AI_DEVELOPMENT_GUIDE.md
2026-07-16 09:53:14 +07:00

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:

  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:

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:

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.

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.