From 99cabd8ebe18c661cb4d8922f2cbd8703235f4f4 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 5 Aug 2026 21:47:12 +0700 Subject: [PATCH] =?UTF-8?q?[CLAUDE]=20Contract:=20KHKK=20UAT=20S175=20dot-?= =?UTF-8?q?F=20=E2=80=94=20gom=20nut=20thao=20tac=20+=20hang=20muc=20du=20?= =?UTF-8?q?an=20+=20loc=20nhom=20(0-mig)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anh review truc tiep tren prod, khoanh do tung cho. Land tron cum: === 1. GOM NUT THAO TAC (anh: "Bo het di" + "phia duoi them cac nut thao tac" + "cho menu dang duyet -> Duyet/Tra lai vay thoi") === · THANH NUT DAY moi o `KhkkDetailContent` (sticky bottom-0, khuon PE workspace): [Xoa phieu] · dong "Cac thay doi da tu dong luu" · [Luu & Gui duyet ->] · Panel 3 GO "Gui duyet" + "Xoa phieu" => nay CHI con **Duyet / Tra lai**. · GO nut "+ Lap ke hoach" (header) · nut "Sua" (muc 1) · dai banner "Dang xem ke hoach". 🔴 THU TU CO CHU DICH: KHONG go "Gui duyet" o panel 3 TRUOC khi thanh day co mat — go truoc la nguoi soan mat duong trinh duyet, phieu ket o Nhap. Vi vay ca cum di CHUNG 1 commit. 🔴 Thanh day chi om nhanh **Submit** — nhanh Approve/Return (o-tich level-finalize + upload file sau transition) O LAI panel 3. Nhan ban nhanh do sang day la de su-that thu hai cho duong DUYET. 🔸 KHONG lam nut "Luu" roi: moi o o man nay luu NGAY khi roi o. Them nut khong lam gi la hua hao — noi thang bang dong chu ben canh thay vi ve nut gia. 🔸 Giu lai dung 1 dong nho "Ve form tao" — bo han thi mo 1 phieu la ket, khong co duong lui. · `onDeleted` noi vao ca 3 host (List / Create / Detail) de xoa xong khong dung o phieu ma. === 2. HANG MUC CUA DU AN (anh: "hang muc cua Du an -. Duyet NCC ay") === 🔴 Lead bay NHAM o luot truoc: bay `line.tenHangMuc` = danh-muc **SP-002** (86 dong, dung phan loai HOP DONG). Anh muon **hang muc CONG VIEC cua du an** tu phieu Duyet NCC (`PE.WorkItemId`). Hai thu trung ten tieng Viet nhung KHAC TRUC — tron la chi sai goi thau. · BE: `ContractSigningPlanDetailDto` +`PeWorkItemName`; handler join `WorkItems` (loose-Guid Mig 49, KHONG FK vat ly => join tay null-safe). · FE: o "Hang muc (du an)" doc `plan.peWorkItemName`; go het khoi liet-ke SP-002 khoi muc 1. === 3. MUC 2 RUT VE "HANG MUC + GIA -> TONG" === Anh: "Cho nay doi lai la hang muc la dc roi, chon hang muc tien -> thanh tong con lai bo het" + "cho chon nhieu dong nhap gia thanh gia tong, ko can so sanh j voi so duyet PRO hay CCM". · Tieu de: "2. Gia de xuat theo NCC trung thau" -> "2. Hang muc". · Cot: 6 -> 3 (HANG MUC | GIA | thao-tac). GO **Nha cung cap · Gia tham chieu · Gia duyet**. · Ghi chu xuong DONG PHU duoi o hang muc (van nhap duoc, khong chiem cot). · +hang TONG = Sigma gia cac dong. 🔸 Du-lieu 2 cot go di VAN trong DTO; `peReferenceAmount` con dung lam DAU phan biet dong-goc / dong-them-tay o nut xoa. === 4. LOC THEO NHOM (anh: "Loc theo quy trinh luon") === Tang duoi man Thao tac hien phieu N1/N7 trong khi dang dung o leaf N2 => doc nham "nhom nay dang co viec". Nay loc theo `approvalGroup` cua leaf menu. `group == null` => khong loc (giu duong cu cho host khong truyen). === 5. GO DAI ACCENT + 2 O LINK HO SO/GHI CHU === · `card-accent` boc `KhkkDetailContent` GO HAN (dai xanh doc man Thao tac). GO CLASS chu KHONG de `border-0` — class nay ngoai `@layer` nen thang utility (gotcha #66). · Khoi "Link ho so + Ghi chu" GO TRON ca 2 nhanh ternary. Luot truoc lead moi go o luoi tren nen 2 o van hien o khoi duoi — anh chup lai dung cho do. === CON NO === 🔴 Cay man Thao tac: anh muon **Nam > Du an > Hang muc > 4 giai doan** (giong cay o man Danh sach). Hien moi co Du an > Hang muc (gap/mo duoc). Chua land. 🔴 CHUA soi mat prod 375/1366/1440. Co-Authored-By: Claude Opus 5 --- .../src/components/khkk/KhkkDetailContent.tsx | 314 +++++++++--------- .../src/components/khkk/KhkkWorkListPanel.tsx | 10 +- .../src/components/khkk/KhkkWorkflowPanel.tsx | 38 +-- fe-admin/src/pages/khkk/KhkkCreatePage.tsx | 60 ++-- fe-admin/src/pages/khkk/KhkkDetailPage.tsx | 2 +- fe-admin/src/pages/khkk/KhkkListPage.tsx | 15 +- fe-admin/src/types/khkk.ts | 3 + .../src/components/khkk/KhkkDetailContent.tsx | 314 +++++++++--------- .../src/components/khkk/KhkkWorkListPanel.tsx | 10 +- .../src/components/khkk/KhkkWorkflowPanel.tsx | 38 +-- fe-user/src/pages/khkk/KhkkCreatePage.tsx | 60 ++-- fe-user/src/pages/khkk/KhkkDetailPage.tsx | 2 +- fe-user/src/pages/khkk/KhkkListPage.tsx | 15 +- fe-user/src/types/khkk.ts | 3 + .../ContractSigningPlanFeatures.cs | 17 +- 15 files changed, 430 insertions(+), 471 deletions(-) diff --git a/fe-admin/src/components/khkk/KhkkDetailContent.tsx b/fe-admin/src/components/khkk/KhkkDetailContent.tsx index 64c7a12..d8dd535 100644 --- a/fe-admin/src/components/khkk/KhkkDetailContent.tsx +++ b/fe-admin/src/components/khkk/KhkkDetailContent.tsx @@ -21,7 +21,7 @@ import { useState, type ReactNode } from 'react' import { useMutation, useQuery, useQueryClient } from '@tanstack/react-query' import { - ClipboardList, Download, Eye, FileCheck, Lock, Paperclip, Pencil, Plus, Save, Trash2, Upload, Users, + ClipboardList, Download, Eye, FileCheck, Lock, Paperclip, Pencil, Plus, Trash2, Upload, Users, } from 'lucide-react' // [S175] Dùng LẠI dialog xem-trước của PE (đã có prop `basePath` từ đợt B) thay vì dựng bản // thứ hai cho KHKK — 2 bản = 2 sự thật, sửa 1 chỗ quên chỗ kia. @@ -40,7 +40,7 @@ import { DOSSIER_KIND_LABELS, DOSSIER_STATUS_BADGE, DOSSIER_STATUS_LABELS, DossierItemKind, DossierItemStatus, KHKK_APPROVAL_GROUP_LABELS, KHKK_PHASE_BADGE, KHKK_PHASE_LABELS, - KhkkAttachmentPurpose, KhkkPhase, + KhkkAttachmentPurpose, KhkkPhase, KhkkTransitionAction, type KhkkTransitionInput, type DossierItemKindValue, type DossierItemStatusValue, type KhkkAttachmentDto, type KhkkDetailDto, type KhkkDossierItemDto, type KhkkPhaseValue, type UpsertKhkkDossierItemInput, @@ -149,17 +149,17 @@ export function KhkkDetailContent({ plan, readOnly, onChanged, + onDeleted, }: { plan: KhkkDetailDto /** [P2-7] Host quyết: `true` khi phiếu ≠ Nháp/Trả lại HOẶC user ≠ người soạn. */ readOnly: boolean onChanged?: () => void + /** [S175] Gọi sau khi XÓA phiếu ở thanh nút đáy — host bỏ chọn / quay về danh sách. */ + onDeleted?: () => void }) { const qc = useQueryClient() const id = plan.id - const [editHeader, setEditHeader] = useState(false) - const [ghiChu, setGhiChu] = useState('') - const [hoSoLink, setHoSoLink] = useState('') const [itemForm, setItemForm] = useState(null) // [S175] File đang xem-trước inline trong khối "File đính kèm" đã gộp. const [previewAtt, setPreviewAtt] = useState(null) @@ -180,16 +180,6 @@ export function KhkkDetailContent({ onChanged?.() } - const saveHeader = useMutation({ - mutationFn: async () => { - await api.put(`/contract-signing-plans/${id}`, { - ghiChu: ghiChu.trim() || null, - hoSoLink: hoSoLink.trim() || null, - }) - }, - onSuccess: () => { toast.success('Đã lưu'); setEditHeader(false); invalidate() }, - onError: (e) => toast.error(getErrorMessage(e)), - }) // Danh mục hạng mục SP-002 — chỉ tải khi phiếu còn sửa được (tránh 1 request thừa). const catalogOptions = useQuery({ @@ -234,6 +224,26 @@ export function KhkkDetailContent({ onError: (e) => toast.error(getErrorMessage(e)), }) + // [S175 — anh: "phía dưới thêm các nút thao tác"] 2 mutation cho THANH NÚT ĐÁY. + // 🔴 CỐ Ý chỉ ôm nhánh **Submit** — nhánh Approve/Return có máy phức tạp (ô-tích + // level-finalize + upload file đính kèm SAU khi transition thành công) và nó ở LẠI + // `KhkkWorkflowPanel`. Nhân bản nhánh đó sang đây là đẻ sự-thật thứ hai cho đường DUYỆT; + // nhánh Submit thì chỉ có đúng `action` + `comment: null` nên bê là an toàn. + const submitPlan = useMutation({ + mutationFn: async () => { + const body: KhkkTransitionInput = { action: KhkkTransitionAction.Submit, comment: null } + return api.post(`/contract-signing-plans/${id}/transitions`, body) + }, + onSuccess: () => { toast.success('Đã gửi duyệt'); invalidate(); onChanged?.() }, + onError: (e) => toast.error(getErrorMessage(e)), + }) + + const deletePlan = useMutation({ + mutationFn: async () => api.delete(`/contract-signing-plans/${id}`), + onSuccess: () => { toast.success('Đã xóa phiếu'); onDeleted?.() }, + onError: (e) => toast.error(getErrorMessage(e)), + }) + const upsertItem = useMutation({ mutationFn: async (body: UpsertKhkkDossierItemInput) => { if (!body.name.trim()) throw new Error('Vui lòng nhập tên căn cứ') @@ -306,11 +316,6 @@ export function KhkkDetailContent({ } } - const startEditHeader = () => { - setGhiChu(plan.ghiChu ?? '') - setHoSoLink(plan.hoSoLink ?? '') - setEditHeader(true) - } // File "đính kèm khi duyệt" thuộc panel 3 (mục 5) — Section 4 chỉ liệt kê hồ-sơ của phiếu. // [S175 · O4] Chia 2 rổ: file GẮN VÀO một căn cứ hiện ngay dưới dòng căn cứ đó; Section 4 @@ -403,6 +408,11 @@ export function KhkkDetailContent({
{/* ─────────────── 1 ─────────────── */} + {/* 🧊 [S175 — anh: "Bỏ hết đi"] Nút "Sửa" ĐÃ GỠ khỏi `actions`. Nó chỉ mở form 2 trường + **Link hồ sơ + Ghi chú** — mà chính 2 trường đó anh cũng bảo bỏ khỏi màn ở cùng lượt. + Giữ nút mở form sửa một thứ không còn hiển thị ở đâu là mâu thuẫn. + 🔸 Máy KHÔNG mất: `hoSoLink`/`ghiChu` vẫn nằm trong DTO + BE + (`UpdateContractSigningPlanDraftCommand`), muốn bày lại chỉ là thêm chỗ vẽ. */}
} @@ -410,13 +420,6 @@ export function KhkkDetailContent({ head="text-brand-800" chipBg="var(--color-brand-50)" chipFg="var(--color-brand-600)" - actions={ - isDraft && !editHeader ? ( - - ) : undefined - } > {/* [S175 — anh chốt trên ảnh khoanh đỏ] Mục 1 RÚT GỌN còn 4 ô: · "Bỏ hết đi" → GỠ **Phòng ban · Quy trình · Hạn xử lý** @@ -428,68 +431,25 @@ export function KhkkDetailContent({
- {/* 1 phiếu nay được N hạng mục (S175) ⇒ liệt kê DUY-NHẤT, giữ thứ tự dòng. - Dòng chưa gán KHÔNG bịa tên — đếm riêng để người soạn biết còn nợ mấy dòng, - vì submit-guard sẽ chặn trình duyệt khi còn dòng chưa gán. */} - - {(() => { - const names = [...new Set(plan.lines.map(l => l.tenHangMuc).filter(Boolean))] as string[] - const unassigned = plan.lines.filter(l => !l.catalogEntryId).length - if (names.length === 0 && unassigned === 0) return DASH - return ( - - {names.map(n => ( - - {n} - - ))} - {unassigned > 0 && ( - - còn {unassigned} dòng chưa gán - - )} - - ) - })()} - + {/* [S175 — anh: "hạng mục của Dự án, Duyệt NCC ấy"] Đây là HẠNG MỤC CÔNG VIỆC của + dự án, lấy từ phiếu Duyệt NCC nguồn (`PE.WorkItemId`). + 🔴 KHÁC hẳn `line.tenHangMuc` — cái đó là danh-mục SP-002 dùng phân loại HỢP + ĐỒNG (86 dòng, chọn ở mục 2). Hai thứ trùng tên tiếng Việt nhưng khác trục; + lượt trước lead bày nhầm cái SP-002 ở đây nên anh bảo "Bỏ đi". */} +
- {editHeader ? ( -
-
- - setHoSoLink(e.target.value)} maxLength={1000} placeholder="\\\\nas\\du-an\\..." /> -
-
- -