Files
solution-erp/.claude/auto-memory/feedback_declarative_kind_config_pattern.md
pqhuy1987 23bbe97f39
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m26s
[CLAUDE] Docs: S124 adap-wave AI_INFRA + owner worker-tier-pin (14 agent opus+effort:max) + user-memory relocate R6 + H24 dogfood
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
2026-07-15 23:17:45 +07:00

5.0 KiB
Raw Blame History

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<Kind, ...>. KHÔNG split N page riêng (clutter routes + duplicate boilerplate).

Structure

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<Kind, {
  label: string
  icon: React.ComponentType<{className?: string}>
  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<Input type={field.type}>
  • number<Input type="number" step={field.step}>
  • textarea<Textarea>
  • checkbox<input type="checkbox">
  • select<select> với field.options
  • multiselect-weekday (NEW S35) → 7 checkbox Mon-Sun + comma-join string

Reusable cross-project

  • VIPIX: Tags + Categories + Collections (3 catalog) — Pattern fit
  • NAMGROUP: Loại NV + Phòng ban + Vị trí (3 catalog đơn giản) — Pattern fit
  • ASHICO: 5-site config + 7-DB selector → có thể adapt
  • DH_Y_DUOC: Loại sáng kiến + Cấp duyệt (2 catalog) — Pattern fit
  • BVAAU: Loại bệnh nhân + Bảo hiểm (2-3 catalog) — Pattern fit

Pattern relationship

Pattern Scope Token cost Notes
Declarative KIND_CONFIG Single-page multi-kind CRUD ~25K Implementer spawn 1 page + KIND_CONFIG Record
12-bis Cross-module mirror BE ~25K Implementer spawn BE mega 4 region
12-ter Within-module N-satellite ~22K spawn 1 mega features + N region
16-bis Cross-app FE 4-place mirror ~15K Implementer spawn types + page + Routes + Layout staticMap

Declarative KIND_CONFIG + 12-bis + 16-bis = Trinity pattern cho master catalog feature S29 + S35 proven 2×.

Anti-pattern

KHÔNG split N page per kind — clutter routes + duplicate boilerplate State/Mutation/Dialog KHÔNG hardcode per-kind switch trong render JSX — dùng KIND_CONFIG Record + renderField switch tách logic KHÔNG quên FieldDef.type extend khi NEW kind có shape khác — vd time picker / multiselect

Verify checklist

  • 1 page với :kind URL param + useParams extract
  • KIND_CONFIG: Record<Kind, {...}> declarative N kind config
  • 1 useQuery list + 1 useMutation save (Create + Update branch via isEdit) + 1 useMutation remove
  • Sub-tab nav 4 kind (NavLink active state per kind)
  • Dialog modal CRUD generic (KHÔNG per-kind dialog)
  • renderField() helper switch case per FieldDef.type
  • BE URL /api/{prefix}/{kind} × 4 verb (List + Create + Update + Delete) mirror controller
  • Mirror SHA256 IDENTICAL × 2 app (Pattern 16-bis)
  • Layout.tsx staticMap entry N leaf (Pattern 16-bis 4-place)