From ebe24694708749b374eb78fc3b13268dbc3e05be Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Thu, 14 May 2026 23:34:39 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-Admin=20FE-User:=20Chunk=20E=20?= =?UTF-8?q?=E2=80=94=20K6=20Workspace=20DROP=20Drafter=20checkbox=20+=20AD?= =?UTF-8?q?D=20Approver=20toggle=20(Mig=2031=20F2=20refactor)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Plan K Chunk E mirror 2 app rule §3.9. Refactor F2 UX flow: DROP fe-admin + fe-user Workspace Drafter checkbox: - PeDetailTabs.tsx Workspace action bar: REMOVE "Gửi thẳng Cấp cuối (skip trung gian)" violet label + state skipToFinal + allowSkipToFinal lookup + skipToFinal payload - submitForApproval mutation signature simplify: opts: { skipToFinal: boolean } → void - Confirm dialog text + button label drop skipToFinal conditional ADD fe-admin + fe-user Approver toggle trong PeWorkflowPanel dialog: - State skipToFinalApprover default false - Visible khi Approve forward (NOT Cancel + NOT SendBack) + currentLevelOptions?.allowApproverSkipToFinal - Checkbox violet panel với description "Phiếu sẽ tiến thẳng tới Đã duyệt (terminal)" - Amber warning khi checked: "Hành động KHÔNG quay lại được" - Mutation payload +skipToFinal: !isReject && skipToFinalApprover - onSuccess reset state Type ApprovalWorkflowOptions × 2 app: +allowApproverSkipToFinal: boolean (7th) Type PeDetailBundle × 2 app: REMOVE drafterAllowSkipToFinal field + comment Mig 29+30+31 UX design Dialog approach (consistent với Trả lại Mode picker pattern): - Skip thẳng Cấp cuối = destructive action → confirm dialog amber warning - Mirror Mig 28 Trả lại 4 mode picker UX consistency - Em main solo K6 per UX flow decision criteria Per bro decision Plan K S23 t1: "Chỗ cấu hình cho phép skip → duyệt thẳng cho phép trong trạng thái đang duyệt" + "Tất cả đều cấu hình ngay trong chỗ setup quy trình duyệt". Verify: - npm run build × 2 app pass clean (0 TS err) - Pre-existing warnings unchanged (chunk size + INEFFECTIVE_DYNAMIC_IMPORT) - Bundle hash rotated × 2 app Co-Authored-By: Claude Opus 4.7 (1M context) --- fe-admin/src/components/pe/PeDetailTabs.tsx | 28 ++++------------ .../src/components/pe/PeWorkflowPanel.tsx | 33 +++++++++++++++++++ fe-admin/src/types/purchaseEvaluation.ts | 11 ++++--- fe-user/src/components/pe/PeDetailTabs.tsx | 32 ++++-------------- fe-user/src/components/pe/PeWorkflowPanel.tsx | 33 +++++++++++++++++++ fe-user/src/types/purchaseEvaluation.ts | 11 +++---- 6 files changed, 90 insertions(+), 58 deletions(-) diff --git a/fe-admin/src/components/pe/PeDetailTabs.tsx b/fe-admin/src/components/pe/PeDetailTabs.tsx index 2b72382..92875ec 100644 --- a/fe-admin/src/components/pe/PeDetailTabs.tsx +++ b/fe-admin/src/components/pe/PeDetailTabs.tsx @@ -117,19 +117,17 @@ export function PeDetailTabs({ // "Lưu & Gửi Duyệt" workspace mode (user 2026-05-07): trigger transition // sang phase tiếp theo (= Đã gửi duyệt). nextPhases[0] thường là ChoPurchasing // (skip TuChoi). Sau success → toast + invalidate + onBack đóng workspace. - // Mig 29 (S21 t5) — F2: per-Drafter user flag (User Management page). - const [skipToFinal, setSkipToFinal] = useState(false) - const allowSkipToFinal = evaluation.drafterAllowSkipToFinal ?? false + // Mig 31 (S23 t1) — F2 Drafter-from-Nháp semantic deprecated. skipToFinal moved + // sang Approver scope ChoDuyet (per-Level slot — xem PeWorkflowPanel). const submitForApproval = useMutation({ - mutationFn: async (opts: { skipToFinal: boolean }) => { + mutationFn: async () => { const next = evaluation.workflow.nextPhases.find(p => p !== PurchaseEvaluationPhase.TuChoi && p !== PurchaseEvaluationPhase.TraLai) if (!next) throw new Error('Không có phase tiếp theo để gửi duyệt') return api.post(`/purchase-evaluations/${evaluation.id}/transitions`, { targetPhase: next, decision: 1, comment: null, - skipToFinal: opts.skipToFinal, }) }, onSuccess: () => { @@ -283,33 +281,19 @@ export function PeDetailTabs({ > Lưu - {/* Mig 28 (S21 t4) — F2: Drafter skip checkbox */} - {allowSkipToFinal && canSubmitForApproval && ( - - )} diff --git a/fe-admin/src/components/pe/PeWorkflowPanel.tsx b/fe-admin/src/components/pe/PeWorkflowPanel.tsx index 5f67745..ea82b29 100644 --- a/fe-admin/src/components/pe/PeWorkflowPanel.tsx +++ b/fe-admin/src/components/pe/PeWorkflowPanel.tsx @@ -37,6 +37,9 @@ export function PeWorkflowPanel({ // Mig 28 (S21 t4) — F1 mode Trả lại. Default Drafter (backward compat S17). const [returnMode, setReturnMode] = useState(WorkflowReturnMode.Drafter) const [returnTargetUserId, setReturnTargetUserId] = useState(null) + // Mig 31 (S23 t1) — F2 Approver duyệt thẳng Cấp cuối. Default false (admin opt-in + // per slot tick → checkbox visible trong dialog Approve, default unchecked). + const [skipToFinalApprover, setSkipToFinalApprover] = useState(false) const qc = useQueryClient() const { user: currentUser } = useAuth() const isAdmin = currentUser?.roles?.includes('Admin') ?? false @@ -97,6 +100,9 @@ export function PeWorkflowPanel({ returnMode: isTraLaiAction ? returnMode : null, returnTargetUserId: isTraLaiAction && returnMode === WorkflowReturnMode.Assignee ? returnTargetUserId : null, + // Mig 31 (S23 t1) — F2 Approver scope ChoDuyet duyệt thẳng Cấp cuối. + // BE check matchingLevel.AllowApproverSkipToFinal (admin opt-in per slot). + skipToFinal: !isReject && skipToFinalApprover, }) }, onSuccess: () => { @@ -108,6 +114,7 @@ export function PeWorkflowPanel({ setComment('') setReturnMode(WorkflowReturnMode.Drafter) setReturnTargetUserId(null) + setSkipToFinalApprover(false) }, onError: e => toast.error(getErrorMessage(e)), }) @@ -397,6 +404,32 @@ export function PeWorkflowPanel({ )} + {/* Mig 31 (S23 t1) — F2 Approver toggle: chỉ visible khi Approve forward + + admin tick AllowApproverSkipToFinal cho slot Cấp hiện tại. */} + {!isCancel && !isSendBack && levelOptions?.allowApproverSkipToFinal && ( +
+ +
+ )} + {!isCancel && !isSendBack && skipToFinalApprover && ( +
+ ⚠ Hành động KHÔNG quay lại được (trừ khi Drafter reset toàn bộ). Phiếu sẽ + skip qua tất cả Cấp/Bước còn lại và chuyển thẳng "Đã duyệt". +
+ )}