[CLAUDE] FE-PE: S21 t5 Chunk C — eOffice read currentLevelOptions + drafterAllowSkipToFinal (per-NV) mirror 2 app
Types refactor `fe-{admin,user}/src/types/purchaseEvaluation.ts`:
- `ApprovalWorkflowOptions` REMOVE allowDrafterSkipToFinal (F2 đã move per-User).
Còn 5 flag (F1 4 mode + F3 EditDetails).
- `PeDetailBundle`:
- RENAME `workflowOptions` → `currentLevelOptions` (clearer semantic per-slot)
- ADD `drafterAllowSkipToFinal: boolean` (BE resolve từ DrafterUserId → User entity)
PeWorkflowPanel.tsx (mirror 2 app):
- RENAME local var `wfOptions` → `levelOptions`
- READ `evaluation.currentLevelOptions` (Cấp hiện tại)
- 4 mode radio render conditional theo levelOptions.allowReturnXxx (unchanged
logic, just rename source)
PeDetailTabs.tsx (mirror 2 app):
- F3 approverEditMode: READ `evaluation.currentLevelOptions?.allowApproverEditDetails`
thay vì workflowOptions.allowApproverEditDetails (semantic per-NV slot)
- F2 allowSkipToFinal: READ `evaluation.drafterAllowSkipToFinal` thay vì
workflowOptions.allowDrafterSkipToFinal (semantic per-Drafter user)
Backward compat verified:
- Phiếu cũ trước Mig 29 vẫn return currentLevelOptions populated (BE backfill
Mig 29 đã copy 5 Allow* per Level)
- drafterAllowSkipToFinal: BE backfill chỉ TRUE cho user từng Drafter PE link
workflow.AllowDrafterSkipToFinal=true (preserve admin config S21 t4)
- Phiếu V1 legacy: currentLevelOptions=null → FE fallback chỉ Drafter mode
Verify:
- npm run build × 2 app pass (fe-user 450ms + fe-admin 439ms, cache hot)
- 0 TS6 err, warning chunk size pre-existing
Pending Chunk D: Docs (schema-diagram §14 update + STATUS + HANDOFF + session log).
Note: User Management page chưa có F2 checkbox UX (defer commit sau khi admin
UAT request — BE field đã có, FE chỉ cần thêm 1 toggle vào UserEdit dialog).
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -108,16 +108,17 @@ export function PeDetailTabs({
|
||||
const actorMatchesLevel = isAdmin
|
||||
|| (currentUser?.id != null && v2Approvers.some(a => a.userId === currentUser.id))
|
||||
const approverEditMode = evaluation.phase === PurchaseEvaluationPhase.ChoDuyet
|
||||
&& (evaluation.workflowOptions?.allowApproverEditDetails ?? false)
|
||||
// Mig 29 (S21 t5) — read F3 từ currentLevelOptions (per-NV slot)
|
||||
&& (evaluation.currentLevelOptions?.allowApproverEditDetails ?? false)
|
||||
&& actorMatchesLevel
|
||||
const itemsReadOnly = readOnly && !approverEditMode
|
||||
|
||||
// "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 28 (S21 t4) — F2: Drafter skip thẳng Cấp cuối. Workflow phải bật flag.
|
||||
// Mig 29 (S21 t5) — F2: per-Drafter user flag (User Management page).
|
||||
const [skipToFinal, setSkipToFinal] = useState(false)
|
||||
const allowSkipToFinal = evaluation.workflowOptions?.allowDrafterSkipToFinal ?? false
|
||||
const allowSkipToFinal = evaluation.drafterAllowSkipToFinal ?? false
|
||||
|
||||
const submitForApproval = useMutation({
|
||||
mutationFn: async (opts: { skipToFinal: boolean }) => {
|
||||
|
||||
@ -41,8 +41,8 @@ export function PeWorkflowPanel({
|
||||
const { user: currentUser } = useAuth()
|
||||
const isAdmin = currentUser?.roles?.includes('Admin') ?? false
|
||||
|
||||
// Mig 28 — F1 workflow options. Null nếu V1 legacy → fallback chỉ "Trả về Drafter".
|
||||
const wfOptions = evaluation.workflowOptions
|
||||
// Mig 29 (S21 t5) — F1 options per-Level (Cấp Approver hiện tại).
|
||||
const levelOptions = evaluation.currentLevelOptions
|
||||
// List approvers đã ký (cho mode Assignee dropdown pick)
|
||||
const signedApprovers = (evaluation.levelOpinions ?? [])
|
||||
.map(o => ({ userId: o.approverUserId, fullName: o.approverFullName ?? 'NV' }))
|
||||
@ -307,15 +307,15 @@ export function PeWorkflowPanel({
|
||||
<>
|
||||
{/* Mig 28 (S21 t4) — F1 mode picker khi Trả lại. Show modes
|
||||
enabled per workflow.options. Default Drafter (S17 fallback). */}
|
||||
{(wfOptions?.allowReturnOneLevel
|
||||
|| wfOptions?.allowReturnOneStep
|
||||
|| wfOptions?.allowReturnToAssignee
|
||||
|| wfOptions?.allowReturnToDrafter
|
||||
|| !wfOptions) && (
|
||||
{(levelOptions?.allowReturnOneLevel
|
||||
|| levelOptions?.allowReturnOneStep
|
||||
|| levelOptions?.allowReturnToAssignee
|
||||
|| levelOptions?.allowReturnToDrafter
|
||||
|| !levelOptions) && (
|
||||
<div className="mb-3 space-y-1.5">
|
||||
<Label className="text-[12px]">Chọn cách Trả lại</Label>
|
||||
<div className="space-y-1">
|
||||
{(wfOptions?.allowReturnOneLevel) && (
|
||||
{(levelOptions?.allowReturnOneLevel) && (
|
||||
<label className="flex items-start gap-2 rounded border border-amber-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
|
||||
<input
|
||||
type="radio"
|
||||
@ -329,7 +329,7 @@ export function PeWorkflowPanel({
|
||||
</span>
|
||||
</label>
|
||||
)}
|
||||
{(wfOptions?.allowReturnOneStep) && (
|
||||
{(levelOptions?.allowReturnOneStep) && (
|
||||
<label className="flex items-start gap-2 rounded border border-amber-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
|
||||
<input
|
||||
type="radio"
|
||||
@ -343,7 +343,7 @@ export function PeWorkflowPanel({
|
||||
</span>
|
||||
</label>
|
||||
)}
|
||||
{(wfOptions?.allowReturnToAssignee) && (
|
||||
{(levelOptions?.allowReturnToAssignee) && (
|
||||
<label className="flex items-start gap-2 rounded border border-amber-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
|
||||
<input
|
||||
type="radio"
|
||||
@ -369,7 +369,7 @@ export function PeWorkflowPanel({
|
||||
</span>
|
||||
</label>
|
||||
)}
|
||||
{(wfOptions?.allowReturnToDrafter !== false) && (
|
||||
{(levelOptions?.allowReturnToDrafter !== false) && (
|
||||
<label className="flex items-start gap-2 rounded border border-amber-200 bg-white px-2 py-1.5 text-[12px] hover:bg-amber-50/40">
|
||||
<input
|
||||
type="radio"
|
||||
|
||||
Reference in New Issue
Block a user