[CLAUDE] Docs: S124 adap-wave AI_INFRA + owner worker-tier-pin (14 agent opus+effort:max) + user-memory relocate R6 + H24 dogfood
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 5m26s

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-07-15 23:17:45 +07:00
parent b764ac791a
commit 23bbe97f39
92 changed files with 2221 additions and 89 deletions

View File

@ -0,0 +1,114 @@
# 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
```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<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)