[CLAUDE] FE-Admin: Chunk C — Mig 31 K3 Designer 7th checkbox AllowApproverSkipToFinal + banner rewrite

ApprovalWorkflowsV2Page Designer inline panel mỗi Level entry thêm checkbox thứ 7:
"Cho phép duyệt thẳng Cấp cuối khi đang duyệt" (F2 admin opt-in per-slot Approver).
Group cuối list sau F4 AllowApproverEditBudget (Mig 30) — pattern mirror Mig 29/30
admin opt-in reinforced 3× cumulative.

Types LevelDto + EditLevelEntry +allowApproverSkipToFinal: boolean field.
Helper makeDefaultLevelEntry default false (opt-out — admin tick explicit).
Helper copyFromDefinition propagate flag từ workflow cũ.
POST/PATCH mutation body propagate 7th flag mỗi Level entry.

Banner line ~623-631 rewrite: "F2 cấu hình ở User Management" (Plan D S22 wire) →
"Cấu hình quyền duyệt riêng cho từng NV trong slot Approver bên dưới" — phản ánh
schema Mig 31 (F2 storage moved per-slot).

Per bro decision S23 t1 Plan K: "Tất cả đều cấu hình ngay trong chỗ setup quy trình duyệt".

Verify:
- npm run build fe-admin pass clean
- 0 TS error
- Bundle size 1395.74 KB (unchanged trivial)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-14 23:19:48 +07:00
parent 364aef63fd
commit dd52d16ca9
2 changed files with 27 additions and 7 deletions

View File

@ -204,6 +204,7 @@ KHÔNG `*` / `latest`. Critical pins:
## 📅 Recent activity (last 10 FIFO) ## 📅 Recent activity (last 10 FIFO)
- **2026-05-14 (S23 t1, K3 Chunk C PASS):** FE Admin Designer 7th checkbox AllowApproverSkipToFinal + banner rewrite. Pattern Mig 29/30 admin opt-in per-slot mirror **reinforced 3×** cumulative (Mig 29 F1+F3 5 checkbox + Mig 30 F4 1 checkbox + Mig 31 F2-refactor 1 checkbox = 7 checkbox total per slot). Cookie-cutter 1 file fe-admin only (`ApprovalWorkflowsV2Page.tsx`, fe-user no Designer per Investigator K0 S1). 7 sub-items atomic: (1) LevelDto type +`allowApproverSkipToFinal: boolean`, (2) EditLevelEntry type +same, (3) `makeDefaultLevelEntry` default false, (4) `copyFromDefinition` propagate `?? false`, (5) inline checkbox row position **cuối list** sau F4 AllowApproverEditBudget logical grouping (Edit Section 2 → Edit Budget → Skip to Final), (6) banner rewrite line ~623 từ "F2 cấu hình ở User Management" (Plan D S22 stale) → "Cấu hình quyền duyệt riêng cho từng NV trong slot Approver bên dưới (Trả lại / Edit Section 2 / Edit Budget / Duyệt thẳng Cấp cuối)", (7) POST/PATCH mutation body `levels.map` +allowApproverSkipToFinal. Verify: `npm run build` fe-admin PASS clean 0 TS error, 0 new warning. Bundle 1395.74 KB (unchanged trivial vs baseline). Diff +26/-7 LOC. Token ~6k. K5 next chunk cleanup zombie endpoint + UsersPage column.
- **2026-05-14 (S23 t1, K1 Chunk A PASS):** Mig 31 schema swap F2 storage Users → ApprovalWorkflowLevels. Pattern Mig 29 ADD-DROP no-BACKFILL Option A (accept lose 4 prod user value `fin.pp` + `pm.nv` + `nv.test` + `truong.nguyen`). Cookie-cutter 6 BE file (User.cs -1 prop + ApprovalWorkflow.cs +1 prop `AllowApproverSkipToFinal` per-Approver-slot + ApprovalWorkflowConfiguration.cs +HasDefaultValue + PurchaseEvaluationWorkflowService.cs surgical -37 LOC F2 Drafter SUBMIT branch line 121-157 stub + Mig 3-file). TransitionAsync `bool skipToFinal` 8th param KEPT cho K2 repurpose APPROVE STEP. 4 Application compile-break sites (UserFeatures.cs LIST + GET DTO mapping + SetUserAllowDrafterSkipToFinalCommandHandler NoOp + PurchaseEvaluationFeatures.cs drafter flag = false) patched với sentinel `false` + K2 marker comment (DTO/Command signature unchanged per spec — K2 sẽ refactor). Mig 31 Up() manual reorder ADD-DROP correct (no BACKFILL). Both DBs Dev + Design applied successful. Build production projects clean 0 err 0 warn. Test compile error `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` left for K7 chunk (spec exclude test scope). Pattern `feedback_per_nv_permission_scope.md` reinforced 3× cumulative (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2-refactor). UserConfiguration.cs file không tồn tại — User entity configured inline `ApplicationDbContext.OnModelCreating` ~line 86, không có HasDefaultValue cho `AllowDrafterSkipToFinal`, EF picks prop change tự động. - **2026-05-14 (S23 t1, K1 Chunk A PASS):** Mig 31 schema swap F2 storage Users → ApprovalWorkflowLevels. Pattern Mig 29 ADD-DROP no-BACKFILL Option A (accept lose 4 prod user value `fin.pp` + `pm.nv` + `nv.test` + `truong.nguyen`). Cookie-cutter 6 BE file (User.cs -1 prop + ApprovalWorkflow.cs +1 prop `AllowApproverSkipToFinal` per-Approver-slot + ApprovalWorkflowConfiguration.cs +HasDefaultValue + PurchaseEvaluationWorkflowService.cs surgical -37 LOC F2 Drafter SUBMIT branch line 121-157 stub + Mig 3-file). TransitionAsync `bool skipToFinal` 8th param KEPT cho K2 repurpose APPROVE STEP. 4 Application compile-break sites (UserFeatures.cs LIST + GET DTO mapping + SetUserAllowDrafterSkipToFinalCommandHandler NoOp + PurchaseEvaluationFeatures.cs drafter flag = false) patched với sentinel `false` + K2 marker comment (DTO/Command signature unchanged per spec — K2 sẽ refactor). Mig 31 Up() manual reorder ADD-DROP correct (no BACKFILL). Both DBs Dev + Design applied successful. Build production projects clean 0 err 0 warn. Test compile error `PurchaseEvaluationWorkflowServiceReturnModeTests.cs:253` left for K7 chunk (spec exclude test scope). Pattern `feedback_per_nv_permission_scope.md` reinforced 3× cumulative (Mig 29 F1+F3 + Mig 30 F4 + Mig 31 F2-refactor). UserConfiguration.cs file không tồn tại — User entity configured inline `ApplicationDbContext.OnModelCreating` ~line 86, không có HasDefaultValue cho `AllowDrafterSkipToFinal`, EF picks prop change tự động.
- **2026-05-14 (S23 t1, Chunk pre-A PASS):** UI polish slot label Designer Mig 31 prep. File `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx` line 873 replaced `Quyền duyệt NV #{ei + 1}``Quyền duyệt {usersList.data?.find(u => u.id === entry.approverUserId)?.fullName ?? 'Chưa chọn NV'}`. Pattern: lookup user fullName từ `usersList` query (Option A — DTO `EditLevelEntry` không có `approverFullName` field, chỉ `LevelDto` BE response có `approverUserName` nhưng edit state dùng `EditLevelEntry`). `usersList` đã in scope ~line 500, pattern lookup `.find(x => x.id === e.approverUserId)` đã dùng tại line 535 cho validation. Cookie-cutter 1 file fe-admin only (fe-user KHÔNG có Designer per Investigator K0 S1). Tailwind classes preserved (`mb-1 text-[10px] font-medium uppercase text-amber-700`). Verify: `npm run build` fe-admin PASS clean 0 TS error, 0 new warning. Bundle 1395 KB (unchanged trivial). Token ~5k. - **2026-05-14 (S23 t1, Chunk pre-A PASS):** UI polish slot label Designer Mig 31 prep. File `fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx` line 873 replaced `Quyền duyệt NV #{ei + 1}``Quyền duyệt {usersList.data?.find(u => u.id === entry.approverUserId)?.fullName ?? 'Chưa chọn NV'}`. Pattern: lookup user fullName từ `usersList` query (Option A — DTO `EditLevelEntry` không có `approverFullName` field, chỉ `LevelDto` BE response có `approverUserName` nhưng edit state dùng `EditLevelEntry`). `usersList` đã in scope ~line 500, pattern lookup `.find(x => x.id === e.approverUserId)` đã dùng tại line 535 cho validation. Cookie-cutter 1 file fe-admin only (fe-user KHÔNG có Designer per Investigator K0 S1). Tailwind classes preserved (`mb-1 text-[10px] font-medium uppercase text-amber-700`). Verify: `npm run build` fe-admin PASS clean 0 TS error, 0 new warning. Bundle 1395 KB (unchanged trivial). Token ~5k.
- **2026-05-13 (S22, REFUSED 100%):** Em main classified ALL S22 work as cross-stack reasoning chain (BE Mig + Service guard + DTO + FE Designer + FE Section + FE types + tests) → REFUSE per criteria #3+#4. Em main solo executed. State chốt S22: **30 migrations** (+1 Mig 30 AllowApproverEditSection1 per-NV F4 flag), **104 test PASS** (+20 từ 84 — gồm PE WF ReturnMode + Draft guard + Reflection-based Authorize policy regression), ~146 endpoints (+3), 46 gotchas unchanged, 33 active prod users (13 cũ + 20 mới S22+2). 7 patterns successfully applied throughout S22 (validated continued effectiveness): Pattern 7 per-NV admin opt-in flag (Mig 30 follow Mig 29), Pattern 2 EF migration 3-file rule, Pattern 8 tách endpoint narrow scope (AdjustBudget vs UpdatePeDraft), Pattern 9 defense-in-depth FE+BE guard pair (S22+1 disable 3 button), Pattern 10 Reflection-based regression test cho Authorize policy (Plan C task 4 #44, 5 test ~50 LOC), Pattern 11 test infra helper cookie-cutter (SeedWorkflowAsync + SeedApproversAsync), Pattern 12 InternalsVisibleTo csproj expose internal helper cho test. Mismatches discovered S22: (1) "Đang trong quá trình duyệt = người điều chỉnh cũng là người duyệt" — em first interpret default Approver scope always allowed → bro corrected per-NV admin opt-in flag (Mig 30). Lesson: clarify default behavior vs admin opt-in TRƯỚC khi default scope expansion. (2) `PE.changelogs` field KHÔNG có trong PeDetailBundle — em first design history display trong BudgetAdjustSection, build FAIL TS2339. Fix: removed history display (defer S23+ via separate fetch endpoint). (3) Dialog `size="xl"` NOT supported — only "sm" | "md" | "lg". Use "lg" cho preview iframe. (4) API auth field `accessToken` không phải `token`. Script `seed-test-users-prod.ps1` lần đầu FAIL 401 sau auth — em fix `$authResp.accessToken`. - **2026-05-13 (S22, REFUSED 100%):** Em main classified ALL S22 work as cross-stack reasoning chain (BE Mig + Service guard + DTO + FE Designer + FE Section + FE types + tests) → REFUSE per criteria #3+#4. Em main solo executed. State chốt S22: **30 migrations** (+1 Mig 30 AllowApproverEditSection1 per-NV F4 flag), **104 test PASS** (+20 từ 84 — gồm PE WF ReturnMode + Draft guard + Reflection-based Authorize policy regression), ~146 endpoints (+3), 46 gotchas unchanged, 33 active prod users (13 cũ + 20 mới S22+2). 7 patterns successfully applied throughout S22 (validated continued effectiveness): Pattern 7 per-NV admin opt-in flag (Mig 30 follow Mig 29), Pattern 2 EF migration 3-file rule, Pattern 8 tách endpoint narrow scope (AdjustBudget vs UpdatePeDraft), Pattern 9 defense-in-depth FE+BE guard pair (S22+1 disable 3 button), Pattern 10 Reflection-based regression test cho Authorize policy (Plan C task 4 #44, 5 test ~50 LOC), Pattern 11 test infra helper cookie-cutter (SeedWorkflowAsync + SeedApproversAsync), Pattern 12 InternalsVisibleTo csproj expose internal helper cho test. Mismatches discovered S22: (1) "Đang trong quá trình duyệt = người điều chỉnh cũng là người duyệt" — em first interpret default Approver scope always allowed → bro corrected per-NV admin opt-in flag (Mig 30). Lesson: clarify default behavior vs admin opt-in TRƯỚC khi default scope expansion. (2) `PE.changelogs` field KHÔNG có trong PeDetailBundle — em first design history display trong BudgetAdjustSection, build FAIL TS2339. Fix: removed history display (defer S23+ via separate fetch endpoint). (3) Dialog `size="xl"` NOT supported — only "sm" | "md" | "lg". Use "lg" cho preview iframe. (4) API auth field `accessToken` không phải `token`. Script `seed-test-users-prod.ps1` lần đầu FAIL 401 sau auth — em fix `$authResp.accessToken`.

View File

@ -43,12 +43,14 @@ type LevelDto = {
approverEmail: string | null approverEmail: string | null
// Mig 29 (S21 t5) — 5 Allow* options per slot Approver // Mig 29 (S21 t5) — 5 Allow* options per slot Approver
// Mig 30 (S22+5) — +AllowApproverEditBudget cho Section ngân sách // Mig 30 (S22+5) — +AllowApproverEditBudget cho Section ngân sách
// Mig 31 (S23 t1) — +AllowApproverSkipToFinal F2 storage swap Users→Level (per-Approver-slot)
allowReturnOneLevel: boolean allowReturnOneLevel: boolean
allowReturnOneStep: boolean allowReturnOneStep: boolean
allowReturnToAssignee: boolean allowReturnToAssignee: boolean
allowReturnToDrafter: boolean allowReturnToDrafter: boolean
allowApproverEditDetails: boolean allowApproverEditDetails: boolean
allowApproverEditBudget: boolean allowApproverEditBudget: boolean
allowApproverSkipToFinal: boolean
} }
type StepDto = { type StepDto = {
id: string id: string
@ -89,12 +91,14 @@ type EditLevelEntry = {
// Mig 29 (S21 t5) — 5 Allow* per slot (default backward compat S17: chỉ // Mig 29 (S21 t5) — 5 Allow* per slot (default backward compat S17: chỉ
// AllowReturnToDrafter=true, 4 còn lại false). // AllowReturnToDrafter=true, 4 còn lại false).
// Mig 30 (S22+5) — +AllowApproverEditBudget cho Section ngân sách (default false). // Mig 30 (S22+5) — +AllowApproverEditBudget cho Section ngân sách (default false).
// Mig 31 (S23 t1) — +AllowApproverSkipToFinal F2 admin opt-in per-Approver-slot (default false).
allowReturnOneLevel: boolean allowReturnOneLevel: boolean
allowReturnOneStep: boolean allowReturnOneStep: boolean
allowReturnToAssignee: boolean allowReturnToAssignee: boolean
allowReturnToDrafter: boolean allowReturnToDrafter: boolean
allowApproverEditDetails: boolean allowApproverEditDetails: boolean
allowApproverEditBudget: boolean allowApproverEditBudget: boolean
allowApproverSkipToFinal: boolean
} }
type EditStep = { name: string; departmentId: string | null; levelEntries: EditLevelEntry[] } type EditStep = { name: string; departmentId: string | null; levelEntries: EditLevelEntry[] }
@ -142,6 +146,7 @@ function copyFromDefinition(d: DefinitionDto): EditStep[] {
allowReturnToDrafter: l.allowReturnToDrafter ?? true, allowReturnToDrafter: l.allowReturnToDrafter ?? true,
allowApproverEditDetails: l.allowApproverEditDetails ?? false, allowApproverEditDetails: l.allowApproverEditDetails ?? false,
allowApproverEditBudget: l.allowApproverEditBudget ?? false, allowApproverEditBudget: l.allowApproverEditBudget ?? false,
allowApproverSkipToFinal: l.allowApproverSkipToFinal ?? false,
})), })),
})) }))
} }
@ -158,6 +163,7 @@ function makeDefaultLevelEntry(order: LevelOrder, approverUserId: string): EditL
allowReturnToDrafter: true, allowReturnToDrafter: true,
allowApproverEditDetails: false, allowApproverEditDetails: false,
allowApproverEditBudget: false, allowApproverEditBudget: false,
allowApproverSkipToFinal: false,
} }
} }
@ -550,6 +556,8 @@ function Designer({
// Mỗi entry → 1 Level row. Multiple rows cùng Order = same Cấp với // Mỗi entry → 1 Level row. Multiple rows cùng Order = same Cấp với
// N approvers (BE iterate group by Order). // N approvers (BE iterate group by Order).
// Mig 29 (S21 t5) — 5 Allow* options per slot Approver. // Mig 29 (S21 t5) — 5 Allow* options per slot Approver.
// Mig 30 (S22+5) — +AllowApproverEditBudget.
// Mig 31 (S23 t1) — +AllowApproverSkipToFinal F2 storage swap per-slot.
levels: s.levelEntries.map(e => ({ levels: s.levelEntries.map(e => ({
order: e.order, order: e.order,
name: `Cấp ${e.order}`, name: `Cấp ${e.order}`,
@ -560,6 +568,7 @@ function Designer({
allowReturnToDrafter: e.allowReturnToDrafter, allowReturnToDrafter: e.allowReturnToDrafter,
allowApproverEditDetails: e.allowApproverEditDetails, allowApproverEditDetails: e.allowApproverEditDetails,
allowApproverEditBudget: e.allowApproverEditBudget, allowApproverEditBudget: e.allowApproverEditBudget,
allowApproverSkipToFinal: e.allowApproverSkipToFinal,
})), })),
})), })),
}) })
@ -620,14 +629,13 @@ function Designer({
</div> </div>
</div> </div>
{/* Mig 29 (S21 t5) — 6 Allow* options MOVED per-NV: {/* Mig 29 (S21 t5) — 5 Allow* F1+F3 per slot Approver.
- 5 flag F1+F3 xuống mỗi Level row (xem level entry inline below). Mig 30 (S22+5) — +AllowApproverEditBudget per slot.
- 1 flag F2 AllowDrafterSkipToFinal xuống Users page (System → Users). Mig 31 (S23 t1) — F2 storage swap Users→Level: per-Approver-slot.
Section "Cấu hình nâng cao" workflow-level cũ Mig 28 đã DROP. */} ALL Allow* options now configured PER NV trong slot Approver dưới đây. */}
<div className="rounded-lg border border-violet-200 bg-violet-50/30 px-3 py-2 text-[11px] leading-relaxed text-violet-800"> <div className="rounded-lg border border-violet-200 bg-violet-50/30 px-3 py-2 text-[11px] leading-relaxed text-violet-800">
Cấu hình quyền duyệt (Trả lại modes + Edit Section 2) đt RIÊNG cho từng NV mỗi Cấu hình quyền duyệt riêng cho từng NV trong slot Approver bên dưới
Cấp dưới đây. F2 "Gửi thẳng Cấp cuối" (Drafter) cấu hình (Trả lại / Edit Section 2 / Edit Budget / Duyệt thẳng Cấp cuối).
<span className="font-medium"> User Management</span> (mỗi NV global).
</div> </div>
<div className="space-y-2 rounded-lg border border-slate-200 p-3"> <div className="space-y-2 rounded-lg border border-slate-200 p-3">
@ -927,6 +935,17 @@ function Designer({
/> />
<span>Cho phép chỉnh sửa Section ngân sách lúc đang duyệt</span> <span>Cho phép chỉnh sửa Section ngân sách lúc đang duyệt</span>
</label> </label>
{/* Mig 31 (S23 t1) — F2 AllowApproverSkipToFinal admin opt-in per-slot.
Approver tick = skip thẳng Cấp cuối khi đang ChoDuyet. */}
<label className="col-span-2 flex items-center gap-1 text-[11px] text-slate-700">
<input
type="checkbox"
className="h-3 w-3"
checked={entry.allowApproverSkipToFinal}
onChange={e => updateField('allowApproverSkipToFinal', e.target.checked)}
/>
<span>Cho phép duyệt thẳng Cấp cuối khi đang duyệt</span>
</label>
</div> </div>
</div> </div>
) )