From 88a5be1afd6f20cf4d6cea45316725577e103ae3 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Fri, 8 May 2026 12:11:39 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20FE-Admin:=20Designer=20flat=20UI=20P?= =?UTF-8?q?h=C3=B2ng=20=C3=97=20C=E1=BA=A5p=20+=20types=20ChoDuyet=3D10=20?= =?UTF-8?q?(Chunk=20B)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit PeWorkflowsPage + WorkflowsPage rewrite for flat workflow model (Mig 21): - Drop InnerStepDto + EditInnerStep types - Drop PHASE_OPTIONS (auto-assign ChoDuyet=10 behind scenes) - StepDto + EditStep extend với departmentId, positionLevel - copyFromDefinition simplified - Designer step UI: Tên + Phòng Select + Cấp Select + SLA + Approvers Role/User optional fallback (drop entire InnerSteps sub-section) - DefinitionCard view: hiển thị badge Phòng (emerald) + Cấp NV/PP/TP (violet) + SLA per step - Save payload: phase=10 (ChoDuyet), departmentId, positionLevel - Hint amber: "Mig 21 flat workflow: User cùng Phòng + Cấp ≥ step → duyệt được (OR-of-many)" types/purchaseEvaluation.ts (fe-admin + fe-user mirror): - + ChoDuyet=10 enum value + label "Đang duyệt" + color amber - Legacy 2-6 + 98 keep cho data cũ display OK - getPeDisplayStatus: ChoDuyet + legacy intermediate → "Đã gửi duyệt" Verify: npm build fe-admin + fe-user pass. Pending Chunk D: Docs + Skill + Memory + session log. Co-Authored-By: Claude Opus 4.7 (1M context) --- fe-admin/src/pages/system/PeWorkflowsPage.tsx | 295 +++++----------- fe-admin/src/pages/system/WorkflowsPage.tsx | 317 +++++------------- fe-admin/src/types/purchaseEvaluation.ts | 20 +- fe-user/src/types/purchaseEvaluation.ts | 19 +- 4 files changed, 207 insertions(+), 444 deletions(-) diff --git a/fe-admin/src/pages/system/PeWorkflowsPage.tsx b/fe-admin/src/pages/system/PeWorkflowsPage.tsx index f141da3..116b91b 100644 --- a/fe-admin/src/pages/system/PeWorkflowsPage.tsx +++ b/fe-admin/src/pages/system/PeWorkflowsPage.tsx @@ -1,6 +1,6 @@ -// PE Workflow admin — mirror WorkflowsPage cho module Duyệt NCC. URL pattern -// /system/pe-workflows/:typeCode (DuyetNcc | DuyetNccPhuongAn). Phase enum -// khác Contract (1=DangSoanThao..7=DaDuyet, 99=TuChoi). +// PE Workflow admin — Session 16 drastic refactor (Mig 21): +// Flat workflow model. Mỗi step = 1 (Phòng × Cấp + Approvers). Bỏ Phase Select +// (auto-assign ChoDuyet=10 behind scenes). Bỏ InnerSteps sub-section. import { useMemo, useState, type FormEvent } from 'react' import { useParams } from 'react-router-dom' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -15,32 +15,23 @@ import { Select } from '@/components/ui/Select' import { Textarea } from '@/components/ui/Textarea' import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' -import { AVAILABLE_ROLES, RoleLabel, PositionLevel, PositionLevelLabel } from '@/types/users' +import { AVAILABLE_ROLES, RoleLabel, PositionLevel, PositionLevelLabel, PositionLevelShort } from '@/types/users' import type { Department, Paged } from '@/types/master' -// ===== Types (mirror BE PeWorkflowAdminOverviewDto) ===== +// ===== Types (mirror BE PeWorkflowAdminOverviewDto post-Mig 21) ===== type ApproverDto = { kind: number; assignmentValue: string; displayName: string | null } -// Mig 18 — N-stage inner step DTO -type InnerStepDto = { - id: string - order: number - departmentId: string - departmentName: string | null - positionLevel: number // 1=NV, 2=PP, 3=TP - name: string | null - slaDays: number | null - isRequired: boolean -} type StepDto = { id: string order: number - phase: number + phase: number // [DEPRECATED] always ChoDuyet=10 for new phaseLabel: string - name: string + name: string // "Phòng A — Cấp 1" slaDays: number | null + departmentId: string | null + departmentName: string | null + positionLevel: number | null // 1=NV, 2=PP, 3=TP approvers: ApproverDto[] - innerSteps: InnerStepDto[] } type DefinitionDto = { id: string @@ -63,55 +54,27 @@ type TypeSummaryDto = { history: DefinitionDto[] } -// PE Phase 1..7 (state thường); 99=TuChoi không là step quy trình. -const PHASE_OPTIONS: { value: number; label: string }[] = [ - { value: 1, label: 'Đang soạn thảo' }, - { value: 2, label: 'Chờ Purchasing' }, - { value: 3, label: 'Chờ Dự án' }, - { value: 4, label: 'Chờ CCM' }, - { value: 5, label: 'Chờ CEO duyệt PA' }, - { value: 6, label: 'Chờ CEO duyệt NCC' }, - { value: 7, label: 'Đã duyệt' }, -] +const PHASE_CHO_DUYET = 10 // Mig 21 — generic intermediate phase type EditStepApprover = { kind: 1 | 2; assignmentValue: string } -// Mig 18 — Inner step level con -type EditInnerStep = { - order: number - departmentId: string - positionLevel: number // 1/2/3 - name: string - slaDays: number | null - isRequired: boolean -} type EditStep = { - phase: number - name: string + name: string // "Phòng A — Cấp 1" slaDays: number | null + departmentId: string | null + positionLevel: number | null // 1/2/3 approvers: EditStepApprover[] - innerSteps: EditInnerStep[] } function copyFromDefinition(d: DefinitionDto): EditStep[] { return d.steps.map(s => ({ - phase: s.phase, name: s.name, slaDays: s.slaDays, + departmentId: s.departmentId, + positionLevel: s.positionLevel, approvers: s.approvers.map(a => ({ kind: a.kind as 1 | 2, assignmentValue: a.assignmentValue })), - innerSteps: (s.innerSteps ?? []).map(i => ({ - order: i.order, - departmentId: i.departmentId, - positionLevel: i.positionLevel, - name: i.name ?? '', - slaDays: i.slaDays, - isRequired: i.isRequired, - })), })) } -// ===== Page ===== - -// Map URL type code → int (mirror PeWf_ menu key) const PE_TYPE_CODE_TO_INT: Record = { DuyetNcc: 1, DuyetNccPhuongAn: 2, @@ -141,7 +104,7 @@ export function PeWorkflowsPage() { } description={ currentType - ? 'Tạo version mới → phiếu PE tương lai dùng. Phiếu đã tạo giữ version cũ (pinned lúc tạo).' + ? 'Mỗi bước = 1 Phòng × Cấp duyệt. Order asc tuần tự. Hết bước = đã duyệt.' : 'Chọn loại Duyệt NCC từ menu bên trái để xem + chỉnh quy trình.' } /> @@ -261,7 +224,16 @@ function DefinitionCard({ def, isActive, onClone }: { def: DefinitionDto; isActi
{s.name} - ({s.phaseLabel}) + {s.departmentName && ( + + {s.departmentName} + + )} + {s.positionLevel != null && ( + + {PositionLevelShort[s.positionLevel]} + + )} {s.slaDays != null && ( SLA {s.slaDays}d @@ -269,8 +241,8 @@ function DefinitionCard({ def, isActive, onClone }: { def: DefinitionDto; isActi )}
- {s.approvers.length === 0 && ( - Chưa có người duyệt + {s.approvers.length === 0 && s.departmentName == null && ( + Chưa cấu hình người duyệt )} {s.approvers.map((a, i) => ( cloneFrom ? copyFromDefinition(cloneFrom) - : [{ phase: 1, name: 'Soạn thảo', slaDays: 3, approvers: [], innerSteps: [] }], + : [{ name: 'Phòng 1 — Cấp 1', slaDays: 3, departmentId: null, positionLevel: PositionLevel.NhanVien, approvers: [] }], [cloneFrom], ) @@ -333,7 +305,7 @@ function PeWorkflowDesigner({ }) const departmentsList = useQuery({ - queryKey: ['departments-for-inner-step'], + queryKey: ['departments-list'], queryFn: async () => (await api.get>('/departments', { params: { page: 1, pageSize: 200 } })).data.items, }) @@ -347,18 +319,12 @@ function PeWorkflowDesigner({ description: description || null, steps: steps.map((s, i) => ({ order: i + 1, - phase: s.phase, + phase: PHASE_CHO_DUYET, // Mig 21 — always ChoDuyet=10 for new definitions name: s.name, slaDays: s.slaDays, + departmentId: s.departmentId, + positionLevel: s.positionLevel, approvers: s.approvers, - innerSteps: s.innerSteps.map((ii, ix) => ({ - order: ix + 1, - departmentId: ii.departmentId, - positionLevel: ii.positionLevel, - name: ii.name || null, - slaDays: ii.slaDays, - isRequired: ii.isRequired, - })), })), }) }, @@ -398,7 +364,7 @@ function PeWorkflowDesigner({
setCode(e.target.value)} required className="font-mono" /> -
Ví dụ QT-DN-A, QT-DN-B. Version auto-tăng mỗi lần lưu.
+
Vd QT-DN-A. Version auto-tăng mỗi lần lưu.
@@ -412,12 +378,18 @@ function PeWorkflowDesigner({
- +
-
-
- +
+
+ + + setSteps(steps.map((x, i) => (i === idx ? { ...x, slaDays: e.target.value ? Number(e.target.value) : null } : x))) + } + /> +
+
+
- {s.approvers.length === 0 && ( -
- Chưa có người duyệt — tối thiểu nên có 1 Role hoặc 1 User. -
- )} -
+
+ + {s.approvers.length > 0 && ( +
{s.approvers.map((a, ai) => (
{a.kind === 1 ? ( @@ -539,105 +522,7 @@ function PeWorkflowDesigner({
))}
-
- - {/* Inner Steps (Mig 18) — N-stage approval Phòng × Cấp chức danh */} -
-
- - -
- {s.innerSteps.length === 0 && ( -
- Chưa cấu hình cấp con — workflow fallback logic 2-cấp NV/TPB legacy. -
- )} - {s.innerSteps.length > 0 && ( -
- {s.innerSteps.map((ii, ix) => ( -
- - {ix + 1} - - - - - -
- ))} -
- )} -
+ )}
))}
@@ -645,8 +530,8 @@ function PeWorkflowDesigner({
- Khi lưu: version mới tự động tăng từ {code}, thành version đang áp dụng. - Phiếu hiện tại vẫn giữ version cũ (được pin tại thời điểm tạo), chỉ phiếu MỚI đi theo version này. + Mig 21 flat workflow: mỗi bước = 1 Phòng × Cấp. User cùng Phòng + Cấp ≥ step's level → được duyệt (OR-of-many). + User có CanBypassReview cấp cao hơn cùng dept → skip cấp dưới. Hết bước = phiếu DaDuyet.
diff --git a/fe-admin/src/pages/system/WorkflowsPage.tsx b/fe-admin/src/pages/system/WorkflowsPage.tsx index afa35e1..6f94a49 100644 --- a/fe-admin/src/pages/system/WorkflowsPage.tsx +++ b/fe-admin/src/pages/system/WorkflowsPage.tsx @@ -1,3 +1,5 @@ +// Contract Workflow admin — Session 16 drastic refactor (Mig 21): +// Flat workflow model. Mỗi step = 1 (Phòng × Cấp + Approvers). Bỏ Phase Select. import { useMemo, useState, type FormEvent } from 'react' import { useParams } from 'react-router-dom' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' @@ -12,32 +14,23 @@ import { Select } from '@/components/ui/Select' import { Textarea } from '@/components/ui/Textarea' import { api } from '@/lib/api' import { getErrorMessage } from '@/lib/apiError' -import { AVAILABLE_ROLES, RoleLabel, PositionLevel, PositionLevelLabel } from '@/types/users' +import { AVAILABLE_ROLES, RoleLabel, PositionLevel, PositionLevelLabel, PositionLevelShort } from '@/types/users' import type { Department, Paged } from '@/types/master' -// ===== Types ===== +// ===== Types post-Mig 21 ===== type ApproverDto = { kind: number; assignmentValue: string; displayName: string | null } -// Mig 20 — N-stage inner step DTO mirror PE Mig 18 -type InnerStepDto = { - id: string - order: number - departmentId: string - departmentName: string | null - positionLevel: number // 1=NV, 2=PP, 3=TP - name: string | null - slaDays: number | null - isRequired: boolean -} type StepDto = { id: string order: number - phase: number + phase: number // [DEPRECATED] always ChoDuyet=10 for new phaseLabel: string name: string slaDays: number | null + departmentId: string | null + departmentName: string | null + positionLevel: number | null approvers: ApproverDto[] - innerSteps: InnerStepDto[] } type DefinitionDto = { id: string @@ -60,55 +53,27 @@ type TypeSummaryDto = { history: DefinitionDto[] } -const PHASE_OPTIONS: { value: number; label: string }[] = [ - { value: 2, label: 'Đang soạn thảo' }, - { value: 3, label: 'Đang góp ý' }, - { value: 4, label: 'Đang đàm phán' }, - { value: 5, label: 'Đang in ký' }, - { value: 6, label: 'CCM kiểm tra' }, - { value: 7, label: 'Đang trình ký' }, - { value: 8, label: 'Đang đóng dấu' }, - { value: 9, label: 'Đã phát hành' }, -] +const PHASE_CHO_DUYET = 10 type EditStepApprover = { kind: 1 | 2; assignmentValue: string } -// Mig 20 — Inner step level con -type EditInnerStep = { - order: number - departmentId: string - positionLevel: number // 1/2/3 - name: string - slaDays: number | null - isRequired: boolean -} type EditStep = { - phase: number name: string slaDays: number | null + departmentId: string | null + positionLevel: number | null approvers: EditStepApprover[] - innerSteps: EditInnerStep[] } function copyFromDefinition(d: DefinitionDto): EditStep[] { return d.steps.map(s => ({ - phase: s.phase, name: s.name, slaDays: s.slaDays, + departmentId: s.departmentId, + positionLevel: s.positionLevel, approvers: s.approvers.map(a => ({ kind: a.kind as 1 | 2, assignmentValue: a.assignmentValue })), - innerSteps: (s.innerSteps ?? []).map(i => ({ - order: i.order, - departmentId: i.departmentId, - positionLevel: i.positionLevel, - name: i.name ?? '', - slaDays: i.slaDays, - isRequired: i.isRequired, - })), })) } -// ===== Page ===== - -// Map URL type code → int. Mirror Wf_ menu key. const TYPE_CODE_TO_INT: Record = { ThauPhu: 1, GiaoKhoan: 2, @@ -127,8 +92,6 @@ export function WorkflowsPage() { queryFn: async () => (await api.get<{ types: TypeSummaryDto[] }>('/workflows')).data, }) - // URL drives which type to show. `/system/workflows` (no param) → show - // landing hint to pick from sidebar; `/system/workflows/` → open that. const selectedTypeInt = typeCode ? TYPE_CODE_TO_INT[typeCode] : null const currentType = selectedTypeInt ? overview.data?.types.find(t => t.contractType === selectedTypeInt) @@ -140,19 +103,18 @@ export function WorkflowsPage() { title={ - {currentType ? `Quy trình: ${currentType.contractTypeLabel}` : 'Quy trình duyệt hợp đồng'} + {currentType ? `Quy trình: ${currentType.contractTypeLabel}` : 'Quy trình duyệt HĐ'} } description={ currentType - ? 'Tạo version mới → HĐ tương lai dùng. HĐ đã tạo giữ version cũ (pinned lúc tạo).' - : 'Chọn loại HĐ từ menu bên trái để xem + chỉnh quy trình duyệt.' + ? 'Mỗi bước = 1 Phòng × Cấp duyệt. Order asc tuần tự. Hết bước = HĐ phát hành.' + : 'Chọn loại HĐ từ menu bên trái để xem + chỉnh quy trình.' } /> {overview.isLoading &&
Đang tải…
} - {/* Landing: no type picked yet */} {overview.data && !currentType && (
{overview.data.types.map(t => ( @@ -180,8 +142,6 @@ export function WorkflowsPage() { ) } -// ===== Per-type panel ===== - function TypePanel({ type, onSaved }: { type: TypeSummaryDto; onSaved: () => void }) { const [designerOpen, setDesignerOpen] = useState(false) const [cloneFrom, setCloneFrom] = useState(null) @@ -192,7 +152,7 @@ function TypePanel({ type, onSaved }: { type: TypeSummaryDto; onSaved: () => voi { setCloneFrom(d); setDesignerOpen(true) }} /> ) : (
- Chưa có quy trình cho loại này. Tạo version đầu tiên bên dưới. + Chưa có quy trình cho loại này.
)} @@ -206,16 +166,14 @@ function TypePanel({ type, onSaved }: { type: TypeSummaryDto; onSaved: () => voi {type.history.filter(d => !d.isActive).length === 0 && (
- Chưa có version cũ. Khi tạo version mới, version hiện tại tự động archive. + Chưa có version cũ.
)}
- {type.history - .filter(d => !d.isActive) - .map(d => ( - { setCloneFrom(dd); setDesignerOpen(true) }} /> - ))} + {type.history.filter(d => !d.isActive).map(d => ( + { setCloneFrom(dd); setDesignerOpen(true) }} /> + ))}
{designerOpen && ( @@ -231,17 +189,13 @@ function TypePanel({ type, onSaved }: { type: TypeSummaryDto; onSaved: () => voi ) } -// ===== Definition card (read-only view) ===== - function DefinitionCard({ def, isActive, onClone }: { def: DefinitionDto; isActive: boolean; onClone: (d: DefinitionDto) => void }) { return (
-

- {def.name} -

+

{def.name}

{def.code} v{String(def.version).padStart(2, '0')} @@ -268,7 +222,16 @@ function DefinitionCard({ def, isActive, onClone }: { def: DefinitionDto; isActi
{s.name} - ({s.phaseLabel}) + {s.departmentName && ( + + {s.departmentName} + + )} + {s.positionLevel != null && ( + + {PositionLevelShort[s.positionLevel]} + + )} {s.slaDays != null && ( SLA {s.slaDays}d @@ -276,9 +239,6 @@ function DefinitionCard({ def, isActive, onClone }: { def: DefinitionDto; isActi )}
- {s.approvers.length === 0 && ( - Chưa có người duyệt - )} {s.approvers.map((a, i) => ( cloneFrom ? copyFromDefinition(cloneFrom) - : [{ phase: 2, name: 'Soạn thảo', slaDays: 7, approvers: [], innerSteps: [] }], + : [{ name: 'Phòng 1 — Cấp 1', slaDays: 7, departmentId: null, positionLevel: PositionLevel.NhanVien, approvers: [] }], [cloneFrom], ) @@ -339,7 +297,7 @@ function WorkflowDesigner({ }) const departmentsList = useQuery({ - queryKey: ['departments-for-inner-step'], + queryKey: ['departments-list'], queryFn: async () => (await api.get>('/departments', { params: { page: 1, pageSize: 200 } })).data.items, }) @@ -353,18 +311,12 @@ function WorkflowDesigner({ description: description || null, steps: steps.map((s, i) => ({ order: i + 1, - phase: s.phase, + phase: PHASE_CHO_DUYET, name: s.name, slaDays: s.slaDays, + departmentId: s.departmentId, + positionLevel: s.positionLevel, approvers: s.approvers, - innerSteps: s.innerSteps.map((ii, ix) => ({ - order: ix + 1, - departmentId: ii.departmentId, - positionLevel: ii.positionLevel, - name: ii.name || null, - slaDays: ii.slaDays, - isRequired: ii.isRequired, - })), })), }) }, @@ -404,7 +356,6 @@ function WorkflowDesigner({
setCode(e.target.value)} required className="font-mono" /> -
Ví dụ QT-TP, QT-MB. Version auto-tăng mỗi lần lưu.
@@ -418,14 +369,18 @@ function WorkflowDesigner({
- +
-
-
- +
+
+ + + setSteps(steps.map((x, i) => (i === idx ? { ...x, slaDays: e.target.value ? Number(e.target.value) : null } : x))) + } + /> +
+
+
- {s.approvers.length === 0 && ( -
- Chưa có người duyệt — tối thiểu nên có 1 Role hoặc 1 User. -
- )} -
+
+ + {s.approvers.length > 0 && ( +
{s.approvers.map((a, ai) => (
{a.kind === 1 ? ( @@ -547,105 +512,7 @@ function WorkflowDesigner({
))}
-
- - {/* Inner Steps (Mig 20) — N-stage approval Phòng × Cấp chức danh — mirror PE Mig 18 */} -
-
- - -
- {s.innerSteps.length === 0 && ( -
- Chưa cấu hình cấp con — workflow fallback logic 2-cấp NV/TPB legacy. -
- )} - {s.innerSteps.length > 0 && ( -
- {s.innerSteps.map((ii, ix) => ( -
- - {ix + 1} - - - - - -
- ))} -
- )} -
+ )}
))}
@@ -653,8 +520,8 @@ function WorkflowDesigner({
- Khi lưu: version mới tự động tăng từ {code}, thành version đang áp dụng. - HĐ hiện tại vẫn giữ version cũ (được pin tại thời điểm tạo), chỉ HĐ MỚI đi theo version này. + Mig 21 flat workflow: mỗi bước = 1 Phòng × Cấp. User cùng Phòng + Cấp ≥ step's level → được duyệt (OR-of-many). + Hết bước = HĐ DaPhatHanh + auto-gen mã HĐ.
diff --git a/fe-admin/src/types/purchaseEvaluation.ts b/fe-admin/src/types/purchaseEvaluation.ts index 149b05f..6ecd051 100644 --- a/fe-admin/src/types/purchaseEvaluation.ts +++ b/fe-admin/src/types/purchaseEvaluation.ts @@ -16,16 +16,19 @@ export const PurchaseEvaluationTypeCode: Record = { 2: 'DuyetNccPhuongAn', } +// Mig 21 drastic refactor: enum simplified. ChoDuyet=10 generic intermediate. +// Legacy 2-9 + 98 deprecated, giữ values cho data cũ đọc OK. export const PurchaseEvaluationPhase = { DangSoanThao: 1, - ChoPurchasing: 2, - ChoDuAn: 3, - ChoCCM: 4, - ChoCEODuyetPA: 5, - ChoCEODuyetNCC: 6, + ChoPurchasing: 2, // [LEGACY] + ChoDuAn: 3, // [LEGACY] + ChoCCM: 4, // [LEGACY] + ChoCEODuyetPA: 5, // [LEGACY] + ChoCEODuyetNCC: 6, // [LEGACY] DaDuyet: 7, - TraLai: 98, // approver trả về Drafter sửa — vẫn cho edit - TuChoi: 99, // terminal từ chối — KHÔNG edit + ChoDuyet: 10, // [Mig 21] generic intermediate + TraLai: 98, // [LEGACY] + TuChoi: 99, } as const export type PurchaseEvaluationPhase = typeof PurchaseEvaluationPhase[keyof typeof PurchaseEvaluationPhase] @@ -37,6 +40,7 @@ export const PurchaseEvaluationPhaseLabel: Record = { 5: 'Chờ CEO duyệt PA', 6: 'Chờ CEO duyệt NCC', 7: 'Đã duyệt', + 10: 'Đang duyệt', 98: 'Trả lại', 99: 'Từ chối', } @@ -49,6 +53,7 @@ export const PurchaseEvaluationPhaseColor: Record = { 5: 'bg-fuchsia-100 text-fuchsia-700', 6: 'bg-pink-100 text-pink-700', 7: 'bg-emerald-100 text-emerald-700', + 10: 'bg-amber-100 text-amber-700', 98: 'bg-yellow-100 text-yellow-800', 99: 'bg-red-100 text-red-700', } @@ -97,6 +102,7 @@ export function getPeDisplayStatus(phase: number): PeDisplayStatus { if (phase === PurchaseEvaluationPhase.DaDuyet) return PeDisplayStatus.DaDuyet if (phase === PurchaseEvaluationPhase.TraLai) return PeDisplayStatus.TraLai if (phase === PurchaseEvaluationPhase.TuChoi) return PeDisplayStatus.TuChoi + // Mig 21 ChoDuyet=10 + legacy intermediate 2-6 → all map "Đã gửi duyệt" return PeDisplayStatus.DaGuiDuyet } diff --git a/fe-user/src/types/purchaseEvaluation.ts b/fe-user/src/types/purchaseEvaluation.ts index 149b05f..0dea333 100644 --- a/fe-user/src/types/purchaseEvaluation.ts +++ b/fe-user/src/types/purchaseEvaluation.ts @@ -16,16 +16,19 @@ export const PurchaseEvaluationTypeCode: Record = { 2: 'DuyetNccPhuongAn', } +// Mig 21 drastic refactor: enum simplified. ChoDuyet=10 generic intermediate. +// Legacy 2-9 + 98 deprecated, giữ values cho data cũ đọc OK. export const PurchaseEvaluationPhase = { DangSoanThao: 1, - ChoPurchasing: 2, - ChoDuAn: 3, - ChoCCM: 4, - ChoCEODuyetPA: 5, - ChoCEODuyetNCC: 6, + ChoPurchasing: 2, // [LEGACY] + ChoDuAn: 3, // [LEGACY] + ChoCCM: 4, // [LEGACY] + ChoCEODuyetPA: 5, // [LEGACY] + ChoCEODuyetNCC: 6, // [LEGACY] DaDuyet: 7, - TraLai: 98, // approver trả về Drafter sửa — vẫn cho edit - TuChoi: 99, // terminal từ chối — KHÔNG edit + ChoDuyet: 10, // [Mig 21] generic intermediate + TraLai: 98, // [LEGACY] + TuChoi: 99, } as const export type PurchaseEvaluationPhase = typeof PurchaseEvaluationPhase[keyof typeof PurchaseEvaluationPhase] @@ -37,6 +40,7 @@ export const PurchaseEvaluationPhaseLabel: Record = { 5: 'Chờ CEO duyệt PA', 6: 'Chờ CEO duyệt NCC', 7: 'Đã duyệt', + 10: 'Đang duyệt', 98: 'Trả lại', 99: 'Từ chối', } @@ -49,6 +53,7 @@ export const PurchaseEvaluationPhaseColor: Record = { 5: 'bg-fuchsia-100 text-fuchsia-700', 6: 'bg-pink-100 text-pink-700', 7: 'bg-emerald-100 text-emerald-700', + 10: 'bg-amber-100 text-amber-700', 98: 'bg-yellow-100 text-yellow-800', 99: 'bg-red-100 text-red-700', }