# Pattern Declarative KIND_CONFIG Record — Single-page multi-kind CRUD reuse > **Discovered:** S29 Plan CA (Master/Catalogs/CatalogsPage.tsx 321 LOC) — first proven instance. > **Reinforced:** S35 G-H2 FE Admin (Hrm/HrmConfigsPage.tsx ~470 LOC) — Pattern 2× cumulative cross-module. ## Pattern Single-page handle **N kind** of catalog/config via URL `:kind` param + declarative `KIND_CONFIG: Record`. KHÔNG split N page riêng (clutter routes + duplicate boilerplate). ### Structure ```tsx type Kind = 'kind-a' | 'kind-b' | ... // union literal (TS6 erasableSyntaxOnly) type FieldDef = { key: string label: string type: 'text' | 'textarea' | 'checkbox' | 'number' | 'date' | 'time' | 'select' | 'multiselect-weekday' | ... required?: boolean placeholder?: string step?: number options?: Array<{value, label}> } const KIND_CONFIG: Record fields: FieldDef[] columns: string[] }> = { 'kind-a': { label, icon, fields: [...], columns: [...] }, 'kind-b': { ... }, } export function CatalogPage() { const { kind } = useParams<{kind?: string}>() const config = KIND_CONFIG[kind ?? 'default'] // Render sub-tab nav + table + Dialog form (declarative renderField per FieldDef.type) } ``` ## Decision **Khi nào dùng Pattern Declarative KIND_CONFIG (vs split page):** - N kind ≤ 6, mỗi kind có fields shape DIFFERENT nhưng UX flow SAME (List + Create/Edit dialog + Delete) - KHÔNG có per-kind business logic phức tạp (workflow / approval flow / codeGen) - Mỗi kind chỉ khác fields list + columns - Total page LOC ~300-500 (acceptable single-file maintain) **KHÔNG dùng khi:** - Per-kind business logic phức tạp → split per-page (vd Contract type 7 có 7 form khác + 9 phase workflow) - N kind > 6 → file > 1000 LOC khó scan - Fields shape differ rất nhiều (vd file upload field + signature picker mix vào) ## Example proven cumulative ### Plan CA Chunk B (S29) — Master/Catalogs - 4 kind: units / materials / services / work-items - File: `fe-admin/src/pages/master/CatalogsPage.tsx` 321 LOC + mirror fe-user - Field shape: code/name + variant per kind (defaultUnit/category/originCountry/etc.) - BE: `/api/catalogs/{kind}` × 4 verb mirror controller ### S35 G-H2 (this session) — Hrm/HrmConfigs - 4 kind: leave-types / holidays / shifts / ot-policies - File: `fe-admin/src/pages/hrm/HrmConfigsPage.tsx` ~470 LOC + mirror fe-user - Field shape: complex (multiselect-weekday cho ShiftPattern WorkDays + TimeOnly cho startTime/endTime + 3 multiplier OtPolicy) - BE: `/api/hrm-configs/{kind}` × 4 verb mirror controller - NEW FieldDef.type extend: `number/date/time/select/multiselect-weekday` ## renderField helper extend pattern Mỗi kind có thể introduce NEW field type → extend `renderField(field, value, onChange)` switch: - `text/date/time` → `` - `number` → `` - `textarea` → `