[CLAUDE] FE-Admin: Chunk C — ApprovalWorkflowDesigner section "Cấu hình nâng cao" 6 checkbox (F1+F2+F3)

Thêm section "Cấu hình nâng cao" trong Designer modal (giữa Description và
Steps), 3 sub-group 6 checkbox per workflow version:

1. Mode Trả lại (Approver chọn khi nhấn ← Trả lại):
   - Trả về 1 Cấp trước (peer review chain trong cùng Bước)
   - Trả về 1 Bước trước (Cấp cuối Bước trước nhận lại)
   - Trả về Người chỉ định (pick runtime từ list NV đã duyệt)
   - Trả về Người soạn thảo (default checked = backward compat S17)

2. Drafter gửi duyệt:
   - Cho phép Drafter gửi thẳng Cấp cuối (F2 skip mọi Bước/Cấp trung gian)

3. Approver chỉnh sửa phiếu:
   - Cho phép Approver chỉnh sửa Section 2 Hạng mục/NCC/Báo giá (F3, giữ Cấp)

DTO types update:
- DefinitionDto +6 boolean field (mirror BE AwDefinitionDto)
- 6 useState cho 6 flag, default từ cloneFrom (giữ config version trước) hoặc
  S17 backward compat (chỉ AllowReturnToDrafter=true)
- POST body extend 6 field gửi BE

Styling:
- Container amber-50/30 + border amber-200 (visual distinction với Steps section)
- Mỗi checkbox: card border-slate-200 bg-white, hover bg-amber-50/40
- Helper text [10px] text-slate-500 dưới label giải thích mode
- Headers [11px] uppercase text-slate-500 group sub-section

fe-user KHÔNG mirror — ApprovalWorkflowsV2Page admin-only. PeWorkspaceCreateView
chỉ filter IsUserSelectable, không cần Allow* flag lúc create phiếu.

Verify:
- npm run build fe-admin pass (8.72s, 0 TS6 err)
- Warning chunk size pre-existing

Pending Chunk D: FE eOffice (Trả lại modal dropdown + Skip submit + Edit
Section 2 enable conditional theo workflow.options) mirror 2 app.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-13 18:59:45 +07:00
parent c56024ba25
commit a508564b45

View File

@ -60,6 +60,13 @@ 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
activatedAt: string | null
createdAt: string
steps: StepDto[]
@ -445,6 +452,15 @@ function Designer({
const [description, setDescription] = useState(cloneFrom?.description ?? '')
const [steps, setSteps] = useState<EditStep[]>(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)
const usersList = useQuery({
queryKey: ['users-for-approver-v2'],
queryFn: async () =>
@ -503,6 +519,13 @@ function Designer({
approverUserId: e.approverUserId,
})),
})),
// Mig 28 (S21 t4) — 6 advanced options
allowReturnOneLevel,
allowReturnOneStep,
allowReturnToAssignee,
allowReturnToDrafter,
allowDrafterSkipToFinal,
allowApproverEditDetails,
})
},
onSuccess: () => {
@ -561,6 +584,118 @@ function Designer({
</div>
</div>
{/* 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. */}
<div className="space-y-2 rounded-lg border border-amber-200 bg-amber-50/30 p-3">
<Label className="text-amber-900">
Cấu hình nâng cao quyền duyệt mở rộng
</Label>
<p className="text-[11px] leading-relaxed text-slate-600">
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 mode khác opt-in đ audit nghiêm.
</p>
<div className="mt-2 space-y-3">
<div>
<div className="mb-1 text-[11px] font-semibold uppercase text-slate-500">
Mode Trả lại (Approver chọn khi nhấn Trả lại)
</div>
<div className="grid grid-cols-2 gap-1.5">
<label className="flex items-start gap-2 rounded border border-slate-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
<input
type="checkbox"
className="mt-0.5 h-3.5 w-3.5"
checked={allowReturnOneLevel}
onChange={e => setAllowReturnOneLevel(e.target.checked)}
/>
<span>
<span className="font-medium">Trả về 1 Cấp trước</span>
<span className="block text-[10px] text-slate-500">Lùi 1 Cấp trong cùng Bước, peer review chain</span>
</span>
</label>
<label className="flex items-start gap-2 rounded border border-slate-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
<input
type="checkbox"
className="mt-0.5 h-3.5 w-3.5"
checked={allowReturnOneStep}
onChange={e => setAllowReturnOneStep(e.target.checked)}
/>
<span>
<span className="font-medium">Trả về 1 Bước trước</span>
<span className="block text-[10px] text-slate-500">Lùi sang Bước trước, Cấp cuối nhận lại</span>
</span>
</label>
<label className="flex items-start gap-2 rounded border border-slate-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
<input
type="checkbox"
className="mt-0.5 h-3.5 w-3.5"
checked={allowReturnToAssignee}
onChange={e => setAllowReturnToAssignee(e.target.checked)}
/>
<span>
<span className="font-medium">Trả về Người chỉ đnh</span>
<span className="block text-[10px] text-slate-500">Pick runtime từ list NV đã duyệt</span>
</span>
</label>
<label className="flex items-start gap-2 rounded border border-slate-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
<input
type="checkbox"
className="mt-0.5 h-3.5 w-3.5"
checked={allowReturnToDrafter}
onChange={e => setAllowReturnToDrafter(e.target.checked)}
/>
<span>
<span className="font-medium">Trả về Người soạn thảo</span>
<span className="block text-[10px] text-slate-500">Phase=TraLai, Drafter sửa rồi gửi lại (mặc đnh)</span>
</span>
</label>
</div>
</div>
<div>
<div className="mb-1 text-[11px] font-semibold uppercase text-slate-500">
Drafter gửi duyệt (Workspace "Lưu &amp; Gửi Duyệt")
</div>
<label className="flex items-start gap-2 rounded border border-slate-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
<input
type="checkbox"
className="mt-0.5 h-3.5 w-3.5"
checked={allowDrafterSkipToFinal}
onChange={e => setAllowDrafterSkipToFinal(e.target.checked)}
/>
<span>
<span className="font-medium">Cho phép Drafter gửi thẳng Cấp cuối</span>
<span className="block text-[10px] text-slate-500">
Skip mọi Bước/Cấp trung gian đi thẳng NV Cấp cuối (vd CEO).
Workspace hiện dropdown 2 option "Gửi tuần tự" vs "Gửi thẳng Cấp cuối".
</span>
</span>
</label>
</div>
<div>
<div className="mb-1 text-[11px] font-semibold uppercase text-slate-500">
Approver chỉnh sửa phiếu
</div>
<label className="flex items-start gap-2 rounded border border-slate-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
<input
type="checkbox"
className="mt-0.5 h-3.5 w-3.5"
checked={allowApproverEditDetails}
onChange={e => setAllowApproverEditDetails(e.target.checked)}
/>
<span>
<span className="font-medium">Cho phép Approver chỉnh sửa Section 2 (Hạng mục + NCC + Báo giá)</span>
<span className="block text-[10px] text-slate-500">
NV Cấp đang duyệt đưc edit chi tiết phiếu (không reset workflow,
giữ Cấp hiện tại). Mọi thay đi log vào Lịch sử chỉnh sửa.
</span>
</span>
</label>
</div>
</div>
</div>
<div className="space-y-2 rounded-lg border border-slate-200 p-3">
<div className="flex items-center justify-between">
<Label>