From 3f339c02c67fde652962a2cefb8afc8f97fc6f25 Mon Sep 17 00:00:00 2001 From: pqhuy1987 Date: Wed, 5 Aug 2026 15:26:35 +0700 Subject: [PATCH] [CLAUDE] Contract: man KHKK Thao tac thanh host 2-panel giong Duyet NCC + REVERT cot da chen nham vao man PE MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Anh bac ban truoc: "Bo phan nay di cho duyet NCC da golive va da tot roi, y tao la dang muon may lam giong giao dien thao tac cua Duyet NCC". REVERT (quan trong nhat) - `PurchaseEvaluationWorkspacePage` x2 app tro ve DUNG BYTE cua 1b3bcaa (grid 2 panel, 0 import, 0 markup thua). Verify bang sha256 vs `git show 1b3bcaa:` + control-duong grep 0 hit ten panel trong pages/pe/. - Bai hoc ghi vao dau file panel: "lam giong man X" != "sua man X". Ban dau doc anh 2/3 (anh khoanh o man PE) thanh "chen them cot vao man PE"; anh 7 moi lo cho dat that la cot TRAI cua man Thao tac KHKK. DI DOI - `components/pe/PeKhkkSidePanel.tsx` -> `components/khkk/KhkkWorkListPanel.tsx` (git mv, giu lich su) — no phuc vu KHKK chu khong phai PE. MAN KHKK — THAO TAC (`/khkk/create`, 8 leaf nhom) doi tu trang-giay-cuon sang HOST 2-PANEL - Khuon be nguyen tu `PurchaseEvaluationWorkspacePage`: header 1 dong + grid `lg:grid-cols-[300px_1fr]` + Panel1 danh sach + nut dinh day + Panel2 noi dung. - Panel 1 = 2 TANG dung anh anh khoanh: tang TREN "Cot danh sach Duyet NCC" — PE da duyet CHUA lap KHKK, gom theo DU AN; bam = nap thang vao form tao (tao luon KHKK). tang DUOI "Cot KHKK da tao" — bam = XEM tai cho, read-only. - 🔴 KHONG re-direct o BAT KY duong nao (anh chot "cho hien phieu do luon, chi view la dc, ko re-direct ve phieu goc"): panel nhan CALLBACK onPickPe/onPickPlan thay vi tu navigate. Van con nut phu "Mo trang day du" cho ai muon roi man — la lua chon, khong phai ep buoc. - Section 1 cua form doi tu DANH SACH CHON thanh KHOI XAC NHAN phieu dang chon — danh sach da o Panel 1, de 2 cho chon cung 1 thu = 2 nguon su that tren cung mot man. - Tao xong KHONG navigate('/khkk/:id') nua ma mo luon phieu vua tao o Panel 2 + tra form ve trang. Cung tinh than "khong roi man". - Nut "Them moi" (dinh day Panel 1) = tra Panel 2 ve form trang. - Gate `KeHoachKyKet.Read` giu nguyen trong panel (gotcha #85); thieu quyen thi NOI RA, va nut hanh-dong VAN hien de man khong thanh ngo cut. Verify: build 2 app PASS; mirror SHA256 3/3 cap KHOP; PE workspace 0 du luong (byte-exact vs 1b3bcaa) + control-duong grep 0 hit. Co-Authored-By: Claude Opus 5 --- .../KhkkWorkListPanel.tsx} | 65 ++- fe-admin/src/pages/khkk/KhkkCreatePage.tsx | 536 +++++++++--------- .../pe/PurchaseEvaluationWorkspacePage.tsx | 10 +- .../KhkkWorkListPanel.tsx} | 65 ++- fe-user/src/pages/khkk/KhkkCreatePage.tsx | 536 +++++++++--------- .../pe/PurchaseEvaluationWorkspacePage.tsx | 10 +- 6 files changed, 662 insertions(+), 560 deletions(-) rename fe-admin/src/components/{pe/PeKhkkSidePanel.tsx => khkk/KhkkWorkListPanel.tsx} (77%) rename fe-user/src/components/{pe/PeKhkkSidePanel.tsx => khkk/KhkkWorkListPanel.tsx} (77%) diff --git a/fe-admin/src/components/pe/PeKhkkSidePanel.tsx b/fe-admin/src/components/khkk/KhkkWorkListPanel.tsx similarity index 77% rename from fe-admin/src/components/pe/PeKhkkSidePanel.tsx rename to fe-admin/src/components/khkk/KhkkWorkListPanel.tsx index 8952a81..d918fdf 100644 --- a/fe-admin/src/components/pe/PeKhkkSidePanel.tsx +++ b/fe-admin/src/components/khkk/KhkkWorkListPanel.tsx @@ -1,9 +1,15 @@ -// [S174 owner] Cột PHẢI của màn "Duyệt NCC — Thao tác" — 1 cột, 2 TẦNG 50/50: +// [S174 owner] PANEL 1 (danh sách) của màn "Kế hoạch ký kết HĐ — Thao tác" — 1 cột, 2 TẦNG: // · tầng TRÊN = phiếu Duyệt NCC ĐÃ DUYỆT (có NCC trúng thầu) mà CHƯA lập kế hoạch ký kết, -// gom theo DỰ ÁN — đây là "việc còn phải làm"; -// · tầng DƯỚI = kế hoạch ký kết ĐÃ TẠO — đây là "việc đã làm rồi". -// Anh chốt: "Thử ý tưởng 1 cột 2 tầng thử, 50% tầng trên là duyệt NCC, còn 50% tầng dưới -// là KHKK". File MIRROR SHA256 identical với fe-user counterpart. +// gom theo DỰ ÁN — "việc còn phải làm"; bấm = nạp thẳng vào form tạo; +// · tầng DƯỚI = kế hoạch ký kết ĐÃ TẠO — "việc đã làm rồi"; bấm = XEM tại chỗ (read-only). +// File MIRROR SHA256 identical với fe-user counterpart. +// +// 🔴 LỊCH SỬ ĐẶT SAI CHỖ — ghi để không tái phạm: bản đầu S174 gắn panel này thành cột 3 của +// màn *Duyệt NCC — Thao tác* (`PurchaseEvaluationWorkspacePage`). Anh bác: **"chỗ duyệt NCC +// đã golive và đã tốt rồi"** — ý anh từ đầu là làm màn Thao tác của KHKK **GIỐNG GIAO DIỆN** +// Thao tác của Duyệt NCC, chứ không phải chèn thêm thứ vào màn Duyệt NCC. Đã revert +// `PurchaseEvaluationWorkspacePage` về đúng byte của `1b3bcaa`. Bài: "làm giống màn X" ≠ +// "sửa màn X". // // 🔴 0 ENDPOINT MỚI — dùng lại đúng 2 cửa đã sống: // · `GET /contract-signing-plans/approved-pe-awaiting-plan` (chính cửa `KhkkCreatePage` @@ -18,7 +24,6 @@ // con). Người chỉ có quyền PE mà không có KHKK sẽ ăn 403 rải khắp cột. ⇒ gate bằng ĐÚNG key // của endpoint, và khi thiếu quyền thì NÓI RA thay vì để cột trắng trông như "chưa có gì". import { useQuery } from '@tanstack/react-query' -import { useNavigate } from 'react-router-dom' import { FileCheck, FileSignature, Lock } from 'lucide-react' import { usePermission } from '@/hooks/usePermission' import { api } from '@/lib/api' @@ -68,8 +73,23 @@ function Tier({ ) } -export function PeKhkkSidePanel() { - const navigate = useNavigate() +export function KhkkWorkListPanel({ + onPickPe, + onPickPlan, + selectedPeId, + selectedPlanId, + footer, +}: { + /** Bấm PE ở tầng trên → host nạp phiếu đó vào form tạo (Ở LẠI màn, không điều hướng). */ + onPickPe: (peId: string) => void + /** Bấm KHKK ở tầng dưới → host mở phiếu đó ở panel 2, CHỈ XEM. Anh chốt: "click vào phiếu + * nào thì cho hiện phiếu đó luôn, chỉ view là đc, ko re-direct về phiếu gốc". */ + onPickPlan: (planId: string) => void + selectedPeId?: string | null + selectedPlanId?: string | null + /** Nút "+ Thêm mới" dính đáy — mirror `PeListPanel` của màn Duyệt NCC — Thao tác. */ + footer?: React.ReactNode +}) { const { can } = usePermission() // 🔴 ĐÚNG key policy của endpoint — KHÔNG dùng `Khkk_*` (đó là key MENU, tầng hiển thị). const canKhkk = can('KeHoachKyKet', 'Read') @@ -109,7 +129,7 @@ export function PeKhkkSidePanel() { const planItems = plans.data?.items ?? [] return ( -