From 63234b2cce2466463c64aa2d87170bb9e0dbe48c Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 13 May 2026 20:07:10 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-Admin:=20S21=20t5=20Chunk=20B=20?= =?UTF-8?q?=E2=80=94=20Designer=20move=205=20checkbox=20xu=E1=BB=91ng=20pe?= =?UTF-8?q?r-Level=20slot?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ApprovalWorkflowsV2Page.tsx refactor Designer modal theo Mig 29 per-NV: Types update: - `LevelDto` +5 Allow* (mirror BE AwLevelDto) - `DefinitionDto` REMOVE 6 workflow-level Allow* (no longer used) - `EditLevelEntry` +5 Allow* (form state per slot entry) - `makeDefaultLevelEntry(order, userId)` helper — 4 false + AllowReturnToDrafter true (S17 backward compat) - `copyFromDefinition` propagate 5 Allow* từ existing Levels Form state: - REMOVE 6 useState workflow-level (allowReturnOneLevel...allowApproverEditDetails) - POST body remove 6 workflow-level field - POST body levels[].* propagate 5 Allow* per slot UI refactor: - REMOVE entire section "Cấu hình nâng cao" workflow-level (amber bg 6 checkbox) - REPLACE với info banner violet ngắn "ⓘ Cấu hình quyền duyệt riêng cho từng NV ở mỗi Cấp dưới đây. F2 cấu hình ở User Management." - Mỗi Level entry (NV row) ADD inline panel amber-50/30 5 checkbox grid-cols-2: - Trả về 1 Cấp trước - Trả về 1 Bước trước - Trả về Người chỉ định - Trả về Drafter (mặc định checked) - Cho phép chỉnh sửa Section 2 (col-span-2, full row) - Header "Quyền duyệt NV #N" [10px] uppercase amber-700 - `updateField()` helper inline update per entry index F2 (AllowDrafterSkipToFinal) cần UX riêng ở User Management page (per-Drafter user global). Defer Chunk B Plus hoặc commit sau khi user UAT request. Verify: - npm run build fe-admin pass 498ms cached - 0 TS6 err, warning chunk size pre-existing Pending Chunk C: FE eOffice (PeWorkflowPanel + PeDetailTabs) read `evaluation.currentLevelOptions` + `evaluation.drafterAllowSkipToFinal` thay vì `workflowOptions`. Mirror 2 app. Co-Authored-By: Claude Opus 4.7 (1M context) --- .../pages/system/ApprovalWorkflowsV2Page.tsx | 272 +++++++++--------- 1 file changed, 137 insertions(+), 135 deletions(-) diff --git a/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx b/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx index 211a868..fae853d 100644 --- a/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx +++ b/fe-admin/src/pages/system/ApprovalWorkflowsV2Page.tsx @@ -41,6 +41,12 @@ type LevelDto = { approverUserId: string approverUserName: string | null approverEmail: string | null + // Mig 29 (S21 t5) — 5 Allow* options per slot Approver + allowReturnOneLevel: boolean + allowReturnOneStep: boolean + allowReturnToAssignee: boolean + allowReturnToDrafter: boolean + allowApproverEditDetails: boolean } type StepDto = { id: string @@ -60,13 +66,9 @@ type DefinitionDto = { description: string | null isActive: boolean isUserSelectable: boolean // Mig 25 — admin toggle cho user pick - // Mig 28 (S21 t4) — 6 advanced options per workflow version - allowReturnOneLevel: boolean - allowReturnOneStep: boolean - allowReturnToAssignee: boolean - allowReturnToDrafter: boolean // default true backward compat S17 - allowDrafterSkipToFinal: boolean - allowApproverEditDetails: boolean + // Mig 29 (S21 t5) — 6 Allow* options MOVED: + // - 5 flag F1+F3 xuống per slot Level (xem LevelDto) + // - 1 flag F2 AllowDrafterSkipToFinal xuống per User (User Management) activatedAt: string | null createdAt: string steps: StepDto[] @@ -79,7 +81,17 @@ type TypeSummaryDto = { } type LevelOrder = 1 | 2 | 3 -type EditLevelEntry = { order: LevelOrder; approverUserId: string } +type EditLevelEntry = { + order: LevelOrder + approverUserId: string + // Mig 29 (S21 t5) — 5 Allow* per slot (default backward compat S17: chỉ + // AllowReturnToDrafter=true, 4 còn lại false). + allowReturnOneLevel: boolean + allowReturnOneStep: boolean + allowReturnToAssignee: boolean + allowReturnToDrafter: boolean + allowApproverEditDetails: boolean +} type EditStep = { name: string; departmentId: string | null; levelEntries: EditLevelEntry[] } type ApproverUser = { id: string; fullName: string; email: string; departmentId: string | null } @@ -110,16 +122,39 @@ function makeEmptyStep(stepNo: number, deptId: string | null = null): EditStep { } // Clone existing definition: filter Order ∈ {1,2,3}, drop entries vượt giới hạn. +// Mig 29 (S21 t5) — clone 5 Allow* per slot từ existing Level. function copyFromDefinition(d: DefinitionDto): EditStep[] { return d.steps.map(s => ({ name: s.name, departmentId: s.departmentId, levelEntries: s.levels .filter(l => l.order >= 1 && l.order <= MAX_LEVELS_PER_STEP) - .map(l => ({ order: l.order as LevelOrder, approverUserId: l.approverUserId })), + .map(l => ({ + order: l.order as LevelOrder, + approverUserId: l.approverUserId, + allowReturnOneLevel: l.allowReturnOneLevel ?? false, + allowReturnOneStep: l.allowReturnOneStep ?? false, + allowReturnToAssignee: l.allowReturnToAssignee ?? false, + allowReturnToDrafter: l.allowReturnToDrafter ?? true, + allowApproverEditDetails: l.allowApproverEditDetails ?? false, + })), })) } +// Mig 29 — Factory default cho entry mới (admin click "+ Thêm NV"). 5 flag +// default backward compat S17: chỉ AllowReturnToDrafter=true. +function makeDefaultLevelEntry(order: LevelOrder, approverUserId: string): EditLevelEntry { + return { + order, + approverUserId, + allowReturnOneLevel: false, + allowReturnOneStep: false, + allowReturnToAssignee: false, + allowReturnToDrafter: true, + allowApproverEditDetails: false, + } +} + // Filter NV theo Phòng. Nếu Phòng = null → fallback all (chưa chọn phòng). function usersForDept(all: ApproverUser[] | undefined, deptId: string | null): ApproverUser[] { if (!all) return [] @@ -452,14 +487,9 @@ function Designer({ const [description, setDescription] = useState(cloneFrom?.description ?? '') const [steps, setSteps] = useState(initialSteps) - // Mig 28 (S21 t4) — 6 advanced options. Default clone từ cloneFrom (giữ - // config version trước) hoặc backward compat S17 (chỉ Drafter mode). - const [allowReturnOneLevel, setAllowReturnOneLevel] = useState(cloneFrom?.allowReturnOneLevel ?? false) - const [allowReturnOneStep, setAllowReturnOneStep] = useState(cloneFrom?.allowReturnOneStep ?? false) - const [allowReturnToAssignee, setAllowReturnToAssignee] = useState(cloneFrom?.allowReturnToAssignee ?? false) - const [allowReturnToDrafter, setAllowReturnToDrafter] = useState(cloneFrom?.allowReturnToDrafter ?? true) - const [allowDrafterSkipToFinal, setAllowDrafterSkipToFinal] = useState(cloneFrom?.allowDrafterSkipToFinal ?? false) - const [allowApproverEditDetails, setAllowApproverEditDetails] = useState(cloneFrom?.allowApproverEditDetails ?? false) + // Mig 29 (S21 t5) — 6 Allow* options MOVED: + // - 5 flag F1+F3 xuống per Level slot (xem EditLevelEntry, render mỗi Level row) + // - 1 flag F2 AllowDrafterSkipToFinal xuống per User (User Management page) const usersList = useQuery({ queryKey: ['users-for-approver-v2'], @@ -513,19 +543,18 @@ function Designer({ departmentId: s.departmentId, // Mỗi entry → 1 Level row. Multiple rows cùng Order = same Cấp với // N approvers (BE iterate group by Order). + // Mig 29 (S21 t5) — 5 Allow* options per slot Approver. levels: s.levelEntries.map(e => ({ order: e.order, name: `Cấp ${e.order}`, approverUserId: e.approverUserId, + allowReturnOneLevel: e.allowReturnOneLevel, + allowReturnOneStep: e.allowReturnOneStep, + allowReturnToAssignee: e.allowReturnToAssignee, + allowReturnToDrafter: e.allowReturnToDrafter, + allowApproverEditDetails: e.allowApproverEditDetails, })), })), - // Mig 28 (S21 t4) — 6 advanced options - allowReturnOneLevel, - allowReturnOneStep, - allowReturnToAssignee, - allowReturnToDrafter, - allowDrafterSkipToFinal, - allowApproverEditDetails, }) }, onSuccess: () => { @@ -584,116 +613,14 @@ function Designer({ - {/* Mig 28 (S21 t4) — Section Cấu hình nâng cao (F1+F2+F3 advanced options). - 6 checkbox per workflow: 4 mode Trả lại + 1 Skip CEO + 1 Approver edit. */} -
- -

- Bật/tắt mode duyệt mở rộng cho workflow này. Mặc định chỉ "Trả về Người soạn thảo" enabled - (tương thích quy trình cũ). Các mode khác opt-in để audit nghiêm. -

- -
-
-
- Mode Trả lại (Approver chọn khi nhấn ← Trả lại) -
-
- - - - -
-
- -
-
- Drafter gửi duyệt (Workspace "Lưu & Gửi Duyệt") -
- -
- -
-
- Approver chỉnh sửa phiếu -
- -
-
+ {/* Mig 29 (S21 t5) — 6 Allow* options MOVED per-NV: + - 5 flag F1+F3 xuống mỗi Level row (xem level entry inline below). + - 1 flag F2 AllowDrafterSkipToFinal xuống Users page (System → Users). + Section "Cấu hình nâng cao" workflow-level cũ Mig 28 đã DROP. */} +
+ ⓘ 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ấp dưới đây. F2 "Gửi thẳng Cấp cuối" (Drafter) cấu hình ở + User Management (mỗi NV global).
@@ -830,7 +757,7 @@ function Designer({ const firstUser = availableUsers[0] setSteps(steps.map((x, i) => i === idx - ? { ...x, levelEntries: [...x.levelEntries, { order, approverUserId: firstUser.id }] } + ? { ...x, levelEntries: [...x.levelEntries, makeDefaultLevelEntry(order, firstUser.id)] } : x, )) }} @@ -913,6 +840,81 @@ function Designer({
) })} + {/* Mig 29 (S21 t5) — 5 Allow* checkbox inline cho mỗi + NV entry. Mặc định AllowReturnToDrafter=true (S17 + backward compat). Admin tick mở mode khác per slot. */} + {entries.map((entry, ei) => { + const globalIdx = s.levelEntries.findIndex(x => x === entry) + const updateField = (field: keyof EditLevelEntry, value: boolean) => { + setSteps(steps.map((x, i) => + i === idx + ? { + ...x, + levelEntries: x.levelEntries.map((y, j) => + j === globalIdx ? { ...y, [field]: value } : y, + ), + } + : x, + )) + } + return ( +
+
+ Quyền duyệt NV #{ei + 1} +
+
+ + + + + +
+
+ ) + })}
)}