[CLAUDE] Contract: KHKK UAT S175 dot-E — rut gon man theo anh khoanh do (FE-only, 0-mig)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m28s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m28s
Anh review truc tiep tren prod, khoanh do tung cho. Land dung nhung gi anh chi:
=== GO (anh: "Bo di" / "Bo het di" / "Bo may dong nay di") ===
· 2 o TONG (Sigma de xuat / Sigma duyet) — chinh anh chon "hien 2 tong rieng" khi em hoi
o vong AskUser truoc, nhin that thi thua => go. Giu 1 dong vet trong ma de lan sau khong
ai dung lai tuong la thieu.
· 3 dong chu-thich duoi bang gia (SNAPSHOT / gan hang muc / dong them tay). Noi dung khong
mat: rang-buoc "phai gan hang muc moi trinh duyet" van do submit-guard BE chan THAT, va o
Hang muc o muc 1 da dem "con N dong chua gan".
· Muc 1: GO **Phong ban · Quy trinh · Han xu ly** — Quy trinh van xem duoc o PANEL 3, go o
day la bo cho ve TRUNG chu khong mat thong tin.
· Muc 1: GO **Link ho so · Ghi chu** khoi o xem (form Sua van con 2 truong nay).
· Chip "Giai doan 2 · <ten nhom dai>" o header — ten nhom da co trong tieu de ("— N2") va
dai 4 giai doan da chi ro dang o GD2 => chip la lan ke thu BA.
· Dong "Dang loc — danh sach lay thang tu may chu…" + dai dau cot "DANH SACH KE HOACH".
🔸 Dai dau cot chinh la thu em THEM o dot B de chua "cot cut dau" — anh xem that roi bao
bo. Ghi lai de khong tu dung lai.
=== THEM / DOI ===
· Muc 1 +o **Hang muc**: 1 phieu nay duoc N hang muc => liet ke DUY-NHAT theo thu tu dong,
kem badge amber "con N dong chua gan" (dong chua gan KHONG bia ten).
· Header phieu +**ma phieu PE nguon** +**NCC trung thau** (anh: "Dua len tren phieu — NCC").
NCC lay duy-nhat tu lines nen lien-danh N NCC liet ke het.
· Cho tai file: be DUNG khuon Duyet NCC (`PeDetailTabs.tsx:3527-3531` + `:3601-3608`) —
dong chu mo-ta o TREN, nut vien DUT nen brand-nhat o DUOI danh sach, thay nut dac o goc
header. Nut vien dut doc ra la "cho tha them vao"; nut dac o header doc ra la "hanh dong
cua ca khoi" — day la cai thu nhat. +`accept` bo loc dinh dang y PE.
· Cay panel Thao tac: 2 cap Du an > Hang muc nay GAP/MO duoc (anh: "Cho hien thi theo cay").
🔴 Dung tap-GAP (khong phai tap-MO) => mac dinh MO HET: du an/hang muc moi xuat hien sau
nay tu mo, khong bi an im lang vi chua ai bam mo bao gio.
=== CON NO (chua land, ghi ra de khong roi) ===
🔴 "So tien CCM va PRO duyet" dua len header — CHUA lam duoc: `KhkkDetailDto` hien 0 truong
ngan sach (do: grep -c ccm khkk.ts = 0, control-duong pro = 50) => can BE tra them.
🔴 Gom nut thao tac: go "+ Lap ke hoach" (header) · "Sua" (muc 1) · khoi "HANH DONG" (panel 3)
· banner "Dang xem ke hoach" => THAY bang 1 THANH NUT DAY khuon PE workspace
(Xoa phieu | Luu | Luu & Gui Duyet).
🔴 "Bo cai dam ke ben" — dai doc giua panel 1 va panel 2, chua dinh vi duoc.
🔴 Danh sach hien theo ngay thang > du an > hang muc > phieu PE + phieu KHKK tuong ung.
🔴 CHUA soi mat prod 375/1366/1440.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@ -24,7 +24,8 @@
|
||||
// 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 { FileCheck, FileSignature, FolderTree, Lock, Pencil } from 'lucide-react'
|
||||
import { ChevronRight, FileCheck, FileSignature, FolderTree, Lock, Pencil } from 'lucide-react'
|
||||
import { useState } from 'react'
|
||||
import { usePermission } from '@/hooks/usePermission'
|
||||
import { api } from '@/lib/api'
|
||||
import { cn } from '@/lib/cn'
|
||||
@ -92,6 +93,10 @@ export function KhkkWorkListPanel({
|
||||
* không vẽ bút chì (cột giữ đúng hình cũ). */
|
||||
onEditPlan?: (planId: string) => void
|
||||
}) {
|
||||
// [S175 — anh: "Cho hiển thị theo cây nằm dự án hạng mục"] Tập KHOÁ đang GẬP.
|
||||
// Dùng tập-gập (không phải tập-mở) để mặc-định là MỞ HẾT: dự án/hạng mục mới xuất hiện sau
|
||||
// này tự mở, không bị ẩn im lặng vì chưa ai bấm mở bao giờ.
|
||||
const [collapsed, setCollapsed] = useState<Set<string>>(new Set())
|
||||
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')
|
||||
@ -193,23 +198,52 @@ export function KhkkWorkListPanel({
|
||||
{/* [S175 · O8] CẤP 1 = Dự án (sticky, `z-20`) · CẤP 2 = Hạng mục (sticky dưới nó,
|
||||
`z-10` + thụt lề + vạch trái). 🔴 Hai `z` khác nhau là BẮT BUỘC: cùng `z` thì
|
||||
header hạng mục trượt ĐÈ lên header dự án khi cuộn, mất mất cấp cha. */}
|
||||
{[...byProject.entries()].map(([projectName, byWorkItem]) => (
|
||||
{[...byProject.entries()].map(([projectName, byWorkItem]) => {
|
||||
const pCollapsed = collapsed.has(projectName)
|
||||
return (
|
||||
<div key={projectName}>
|
||||
<div className="sticky top-0 z-20 flex items-center gap-1.5 border-b border-slate-200 bg-slate-50/95 px-3 py-1 backdrop-blur">
|
||||
{/* [S175 — anh: "Cho hiển thị theo cây nằm dự án hạng mục"] Header CẤP 1 nay là
|
||||
NÚT gập/mở. Mặc-định MỞ: gập sẵn thì mở panel ra thấy toàn thư-mục đóng,
|
||||
người dùng tưởng chưa có việc nào. */}
|
||||
<button
|
||||
type="button"
|
||||
onClick={() => setCollapsed(prev => {
|
||||
const next = new Set(prev)
|
||||
if (next.has(projectName)) next.delete(projectName); else next.add(projectName)
|
||||
return next
|
||||
})}
|
||||
aria-expanded={!pCollapsed}
|
||||
className="sticky top-0 z-20 flex w-full items-center gap-1.5 border-b border-slate-200 bg-slate-50/95 px-3 py-1 text-left backdrop-blur transition hover:bg-slate-100"
|
||||
>
|
||||
<ChevronRight className={cn('h-3 w-3 shrink-0 text-slate-400 transition-transform', !pCollapsed && 'rotate-90')} aria-hidden />
|
||||
<FolderTree className="h-3 w-3 shrink-0 text-slate-400" aria-hidden />
|
||||
<span className="min-w-0 flex-1 truncate text-[10px] font-semibold uppercase tracking-wide text-slate-600">
|
||||
{projectName}
|
||||
</span>
|
||||
<span className="shrink-0 text-[10px] tabular-nums text-slate-500">{countProject(byWorkItem)}</span>
|
||||
</div>
|
||||
{[...byWorkItem.entries()].map(([workItemName, items]) => (
|
||||
</button>
|
||||
{!pCollapsed && [...byWorkItem.entries()].map(([workItemName, items]) => {
|
||||
const wKey = `${projectName} | ||||