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\\..." /> -
-
- -