[CLAUDE] Contract: man KHKK Thao tac thanh host 2-panel giong Duyet NCC + REVERT cot da chen nham vao man PE
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m40s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m40s
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 cua1b3bcaa(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 vs1b3bcaa) + control-duong grep 0 hit. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@ -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 (
|
||||
<aside className="hidden min-h-0 flex-col overflow-hidden border-l border-slate-200 bg-white xl:flex">
|
||||
<aside className="flex min-h-0 flex-col overflow-hidden border-r border-slate-200 bg-white">
|
||||
{!canKhkk ? (
|
||||
// Cột trắng đọc thành "chưa có gì" — phải nói ĐÚNG lý do là thiếu quyền.
|
||||
<div className="flex flex-1 flex-col items-center justify-center gap-2 px-4 text-center">
|
||||
@ -158,11 +178,14 @@ export function PeKhkkSidePanel() {
|
||||
<li key={pe.id}>
|
||||
<button
|
||||
type="button"
|
||||
// Bấm = đi thẳng tới màn lập kế hoạch. Không preset `?group=` vì
|
||||
// PE này chưa dùng nhóm nào ⇒ để màn tạo giữ default của nó.
|
||||
onClick={() => navigate('/khkk/create')}
|
||||
// Bấm = nạp THẲNG phiếu này vào form tạo bên panel 2. Không điều hướng,
|
||||
// không mở lại màn khác — đúng "click vào cột này để tạo luôn KHKK".
|
||||
onClick={() => onPickPe(pe.id)}
|
||||
title="Lập kế hoạch ký kết cho phiếu này"
|
||||
className="block w-full px-3 py-2 text-left transition hover:bg-amber-50/60"
|
||||
className={cn(
|
||||
'block w-full px-3 py-2 text-left transition hover:bg-amber-50/60',
|
||||
selectedPeId === pe.id && 'bg-amber-50 ring-1 ring-inset ring-amber-300',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="font-mono text-[10px] text-brand-700">{pe.maPhieu ?? '—'}</span>
|
||||
@ -212,9 +235,14 @@ export function PeKhkkSidePanel() {
|
||||
<li key={k.id}>
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => navigate(`/khkk/${k.id}`)}
|
||||
title="Mở kế hoạch ký kết"
|
||||
className="block w-full px-3 py-2 text-left transition hover:bg-violet-50/60"
|
||||
// 🔴 CHỈ XEM tại chỗ — KHÔNG `navigate('/khkk/:id')`. Anh chốt: "cho hiện
|
||||
// phiếu đó luôn, chỉ view là đc, ko re-direct về phiếu gốc".
|
||||
onClick={() => onPickPlan(k.id)}
|
||||
title="Xem kế hoạch ký kết này (chỉ xem)"
|
||||
className={cn(
|
||||
'block w-full px-3 py-2 text-left transition hover:bg-violet-50/60',
|
||||
selectedPlanId === k.id && 'bg-violet-50 ring-1 ring-inset ring-violet-300',
|
||||
)}
|
||||
>
|
||||
<div className="flex items-center gap-1.5">
|
||||
<span className="font-mono text-[10px] text-violet-700">{k.maKeHoach ?? '(chưa cấp mã)'}</span>
|
||||
@ -247,6 +275,11 @@ export function PeKhkkSidePanel() {
|
||||
</Tier>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* Nút hành-động dính ĐÁY, ngoài vùng cuộn của 2 tầng — mirror "+ Thêm mới" của
|
||||
`PeListPanel` (màn Duyệt NCC — Thao tác). Hiện KỂ CẢ khi thiếu quyền KHKK: cột
|
||||
không dựng được thì vẫn phải còn đường bấm, không thì màn thành ngõ cụt. */}
|
||||
{footer && <div className="shrink-0 border-t border-slate-200 p-2">{footer}</div>}
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user