Compare commits

...

2 Commits

Author SHA1 Message Date
3794d89da5 [CLAUDE] Contract: KHKK thung rac theo khuon man Duyet NCC — banner + dau xoa (FE-only, 0-mig)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 6m41s
Owner gui anh man "Da xoa" cua module Duyet NCC lam MAU ("anh chi la mau —
em soi lai KHKK"). Do KHKK thieu 2 thu so voi mau:

1. Banner do "🗑 Phieu da xoa — chi xem. Khong khoi phuc, khong thao tac."
   PE co o PurchaseEvaluationsListPage:429; KHKK truoc do chi co tooltip hover
   o panel 2, khong co banner nhin thay duoc.
2. Dau "🗑 xoa ~ gio ngay" tren tung the. PE co o :584; KhkkPlanCard = 0 hit.

Banner THAY dropdown trang thai khi xem thung rac (mirror PE): phieu giu
NGUYEN phase luc bi xoa, `IsDeleted` moi la thu phan loai => loc trang thai
o day la nut chet. Chinh `plansForPe` cung co y KHONG loc phase khi xem
thung rac, nen de dropdown lai thi bam ma khong thay gi doi.

🔸 CO Y BO ve "khong con tinh vao luy ke ngan sach" cua ban PE:
`grep 'lũy kế'` trong toan bo components/khkk + pages/khkk = 0 hit => be
nguyen cau do la khai mot dieu chua chung minh cho module nay. Giu dung
phan verify duoc tu chinh ma: khong khoi phuc, khong thao tac.

🔸 Dau xoa dung `updatedAt` chu KHONG phai `deletedAt`: API /deleted tra DTO
DUNG CHUNG voi list song nen khong co `deletedAt`/`deletedBy` (KhkkListItemDto
chi co createdAt + updatedAt). `AuditingInterceptor` ghi updatedAt ngay luot
SaveChanges cua xoa mem => XAP XI. Nhan "~" de khong hua chinh xac; ten nguoi
xoa API khong tra nen co y bo trong thay vi doan. Y het ly do ban PE.

Co truyen o CA HAI cho dung the (than cay + danh sach phang) — thieu 1 cho la
cung mot phieu hien khac nhau o 2 che-do.

mirror sha256 khop; build xanh x2 app; tsc sach x2.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 09:31:58 +07:00
4f327c36ec wal: vong-4 ship 097e41a + deploy VERIFIED 2-ve x2 app
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
2026-08-06 09:14:23 +07:00
3 changed files with 93 additions and 23 deletions

View File

@ -13,7 +13,7 @@ chain:
- [x] cổng C2 @PAUSE-3 KÊU (A\B={investigator-codebase, ctx-verifier}) → seed 2 entry → A\B=∅
- [x] `ctx-curator` @PAUSE-3 = `SUA — 6 điểm`, áp đủ 6 rồi mới chèn MIND-4 (bắt được lead ĐẾM SAI commit của chính mình: viết 7 liệt 6)
- [x] anh soi prod → **vòng-3 "tạm ổn"**; câu (2) cột DỰ ÁN 11 ô + (3) `BudgetTotal` 61/69 = anh **PARK** ("chưa quan tâm lúc này") — đừng hỏi lại, đừng tự làm
- [!] 🔴 **vòng-4 anh giao: 3 menu Đang duyệt / Đã duyệt / Đã xóa → cây NămDự ánHạng mụcphiếu KHKK** (= chính O5, anh phán GIỮ CÂY). CODE XONG build xanh ×2, **CHƯA anh soi prod** kết luận giao diện vẫn là ĐỌC MÃ (gotcha #3)
- [!] 🔴 **vòng-4: 3 menu Đang duyệt / Đã duyệt / Đã xóa → cây NămDự ánHạng mụcphiếu** (= chính O5, anh phán GIỮ CÂY)**ship `097e41a` + deploy VERIFIED 2-vế ×2 app** (admin `C-92w73N` · eoffice `B7jZIgBO`; dấu phân-biệt = chuỗi rỗng 1→**2**/2 cả 2 app, control-dương `Cây tải 200 phiếu mới nhất`=1/1). 🔴 **CHƯA anh soi prod** — vẫn là ĐỌC MÃ (gotcha #3)
- [x] **UAT vòng-3 ship `79936ad` + deploy VERIFIED 2-vế ×2 app** (admin `--p-YD6EX` · eoffice `DNq8Gz8s`; khung thẻ `border-[#e9eef4]`=1/1 · 2 chuỗi cũ=0/0 · control-dương `Hạng mục (dự án)`+`Lưu & Gửi duyệt`=1/1) — 4 defect: ① TRẢ LẠI khung thẻ (lead lượt trước gỡ CẢ `.card-accent` ⇒ mất luôn khung; anh chỉ muốn bỏ dải xanh — dựng lại khung bằng utility giữ đúng `#e9eef4`/`rounded-xl`) · ② cây Thao tác `approvedPeOnly` + `stagesOnly={[1]}` (lọc phase TRƯỚC khi dựng cây để badge đếm không nói dối; 4 host HĐ = 0/0 không đụng) · ③ ô Hạng mục (dự án) sang cột phải, nằm dưới Dự án · ④ bỏ 2 dòng chữ thừa
- [x] `ctx-curator` @PAUSE-4 = `SUA — 9 điểm`, áp đủ 9 rồi mới chèn MIND-5 — vai sửa CÁCH LEAD TRÍCH gotcha #66 ("đè là vô hiệu" là OVERCLAIM; `gotchas.md:1165` ghi rõ fix ĐIỂM bằng `!important` THẮNG) + bắt lead đếm sai commit **lần thứ HAI liên tiếp**
- [ ] 🔴 2 NỢ MỚI (dư-lượng sau khi gỡ `.card-accent`): ① khung thẻ có **2 nguồn sự-thật** (`index.css:112` ⟂ utility hardcode `#e9eef4`/`rounded-xl` ×2 app) — đổi CSS không kéo theo KHKK · ② comment đã ship overclaim gotcha #66 — 🔴 **ĐO ĐÍCH @S176 = 4 site / 2 CHUỖI KHÁC NHAU** (sổ cũ ghi mờ "≥3 chỗ"): `KhkkWorkflowPanel.tsx:278` ×2 app = *"đè là vô hiệu"* (SAI thẳng) ⟂ `PipelineTreePanel.tsx:226` ×2 app = *"vô hiệu IM LẶNG… Phải GỠ CLASS, không đè"* (chẩn ĐÚNG cho `border-0` trần, chỉ câu kết quá rộng — `border-0!` vẫn thắng). **Grep 1 chuỗi = vá 2/4 rồi tưởng xong**
@ -28,10 +28,10 @@ chain:
- [ ] `ring2-audit/MEMORY.md` 26.440 B > cap 25.600 (A1 treo từ PAUSE-1) · monthly drift-audit quá hạn · dọn 9 dir rỗng `runs/.../agent-memory/*` @session-end
- [ ] kéo 2 thư `loi-do-luong` (ĐÃ CÓ ở `AI_INFRA/outbox/all`, SE chưa kéo) + 4× `upgrade-pack-phased` thiếu adap-report — đo @S176: `se=0` (23/23 đã vào `_index`), nợ đúng bằng sổ
next: vòng-4 (cây 4 tầng cho 3 menu lọc) ĐÃ code+build xanh ×2 → ship+deploy → CHỜ anh soi prod 375/1366/1440 → [đuôi, đừng rơi] E4 ➊ · synthesis **2** run-folder orphan (S173 + S175-duan-budget) · 5 điểm governance · kéo thư + adap-report · A1 ring2
next: vòng-4 (cây 4 tầng cho 3 menu lọc) ship `097e41a` + deploy VERIFIED 2-vế ×2 app → CHỜ anh soi prod 375/1366/1440 → [đuôi, đừng rơi] E4 ➊ · synthesis **2** run-folder orphan (S173 + S175-duan-budget) · 5 điểm governance · kéo thư + adap-report · A1 ring2
verify:
- `curl -s https://admin.solutions.com.vn/ | grep -oE 'index-[A-Za-z0-9_-]+\.js'` → khác `index--p-YD6EX.js` = đợt SAU đã tới (mốc @PAUSE-4 = bundle `79936ad`)
- `curl -s https://admin.solutions.com.vn/ | grep -oE 'index-[A-Za-z0-9_-]+\.js'` → khác `index-C-92w73N.js` = đợt SAU đã tới (mốc @S176 vòng-4 = bundle `097e41a`)
- 🔴 LUẬT ĐO bundle: `grep -o '<chuỗi>' | wc -l`, **KHÔNG** `grep -c` (minify = 1 mega-dòng). Chuỗi DÙNG-CHUNG phải hỏi "còn ai dùng nữa không" TRƯỚC khi kết luận (lead từng báo động giả với `card-accent overflow-hidden` = 2 hit hợp lệ ở `HardCopiesPage`+`ProposalsListPage`)
- `grep -cE 'className=.*card-accent' fe-admin/src/components/khkk/KhkkDetailContent.tsx` → 0 (use ⟂ mention: 2 hit còn lại là comment)
- `wc -c .claude/agent-memory/ring2-audit/MEMORY.md` → 26440 (>25600 = còn nợ A1)

View File

@ -105,12 +105,15 @@ export function KhkkPlanCard({
selected,
onSelect,
finalize,
deleted,
}: {
plan: KhkkListItemDto
selected: boolean
onSelect: (id: string) => void
/** Chỉ có khi phiếu ĐANG được chọn (detail đã tải) — list payload không mang cờ này. */
finalize?: { endedByLevelFinalize: boolean; finalizeStepName: string | null }
/** [S176] Đang xem thùng rác ⇒ hiện dấu thời-điểm xóa. Mirror khuôn màn Duyệt NCC. */
deleted?: boolean
}) {
const frozen = plan.phase === KhkkPhase.DaDuyet
const amount = frozen ? (plan.totalApprovedAmount ?? plan.totalProposedAmount) : plan.totalProposedAmount
@ -160,6 +163,22 @@ export function KhkkPlanCard({
)}
<span>·</span>
<span className="truncate">{formatDateTime(plan.createdAt)}</span>
{/* [S176] "Xóa lúc" — mirror khuôn màn Duyệt NCC (`PurchaseEvaluationsListPage:573`).
🔴 API `/contract-signing-plans/deleted` trả DTO DÙNG CHUNG với list sống ⇒ KHÔNG
có `deletedAt`/`deletedBy`. Mốc dưới là `updatedAt`, mà `AuditingInterceptor` ghi
ngay lượt SaveChanges của thao tác xóa mềm ⇒ XẤP XỈ thời điểm xóa. Nhãn "≈" để
KHÔNG hứa chính xác. Tên người xóa API không trả — cố ý bỏ trống thay vì đoán. */}
{deleted && plan.updatedAt && (
<>
<span>·</span>
<span
className="shrink-0 rounded bg-red-50 px-1 py-0.5 text-[10px] font-medium text-red-700"
title="Thời điểm xóa (xấp xỉ — lấy từ mốc cập nhật cuối, ghi cùng lượt xóa mềm). Xem lịch sử phiếu để biết ai xóa và lý do."
>
🗑 xóa {formatDateTime(plan.updatedAt)}
</span>
</>
)}
</div>
<div className="mt-0.5 flex items-center justify-between gap-2 text-[11px] text-slate-500">
<span className="min-w-0 truncate">👤 {plan.drafterFullName ?? '—'}</span>
@ -293,6 +312,7 @@ export function KhkkListPanel({
selected={selectedId === k.id}
onSelect={deletedView ? () => { /* phiếu đã xóa: chỉ liệt kê, không mở */ } : onSelect}
finalize={selectedId === k.id ? selectedFinalize : undefined}
deleted={deletedView}
/>
</li>
))}
@ -316,6 +336,19 @@ export function KhkkListPanel({
className="pl-8"
/>
</div>
{/* [S176] Thùng rác ⇒ THAY dropdown bằng banner, mirror khuôn màn Duyệt NCC
(`PurchaseEvaluationsListPage:427-431`).
🔴 Ẩn dropdown là CÓ LÝ DO, không phải cho gọn: phiếu giữ NGUYÊN phase lúc bị xóa,
`IsDeleted` mới là thứ phân loại ⇒ lọc trạng thái ở đây là nút chết. Chính `plansForPe`
bên dưới cũng cố ý KHÔNG lọc phase khi xem thùng rác — để dropdown lại thì người dùng
bấm mà không thấy gì đổi.
🔸 CỐ Ý BỎ vế "không còn tính vào lũy kế ngân sách" của bản PE: `grep 'lũy kế'` trong
toàn bộ KHKK = 0 hit ⇒ bê nguyên là khai một điều chưa chứng minh cho module này. */}
{deletedView ? (
<div className="rounded border border-red-200 bg-red-50 px-2 py-1.5 text-[11px] text-red-700">
🗑 <strong>Phiếu đã xóa</strong> chỉ xem. Không khôi phục, không thao tác.
</div>
) : (
<Select
value={phase ?? ''}
aria-label="Lọc trạng thái"
@ -326,6 +359,7 @@ export function KhkkListPanel({
<option key={p} value={p}>{KHKK_PHASE_LABELS[Number(p) as KhkkPhaseValue]}</option>
))}
</Select>
)}
{/* 🧊 [S175] Dòng "Đang lọc — danh sách lấy thẳng từ máy chủ…" ĐÃ GỠ (anh: "Bỏ mấy dòng
này đi"). Đó là chú-thích về CƠ-CHẾ bên trong, người dùng không cần biết cây tải 200
phiếu hay lấy thẳng máy chủ — họ chỉ cần thấy đúng phiếu. */}
@ -412,6 +446,7 @@ export function KhkkListPanel({
selected={selectedId === k.id}
onSelect={deletedView ? () => { /* phiếu đã xóa: chỉ liệt kê, không mở */ } : onSelect}
finalize={selectedId === k.id ? selectedFinalize : undefined}
deleted={deletedView}
/>
</li>
))}

View File

@ -105,12 +105,15 @@ export function KhkkPlanCard({
selected,
onSelect,
finalize,
deleted,
}: {
plan: KhkkListItemDto
selected: boolean
onSelect: (id: string) => void
/** Chỉ có khi phiếu ĐANG được chọn (detail đã tải) — list payload không mang cờ này. */
finalize?: { endedByLevelFinalize: boolean; finalizeStepName: string | null }
/** [S176] Đang xem thùng rác ⇒ hiện dấu thời-điểm xóa. Mirror khuôn màn Duyệt NCC. */
deleted?: boolean
}) {
const frozen = plan.phase === KhkkPhase.DaDuyet
const amount = frozen ? (plan.totalApprovedAmount ?? plan.totalProposedAmount) : plan.totalProposedAmount
@ -160,6 +163,22 @@ export function KhkkPlanCard({
)}
<span>·</span>
<span className="truncate">{formatDateTime(plan.createdAt)}</span>
{/* [S176] "Xóa lúc" — mirror khuôn màn Duyệt NCC (`PurchaseEvaluationsListPage:573`).
🔴 API `/contract-signing-plans/deleted` trả DTO DÙNG CHUNG với list sống ⇒ KHÔNG
có `deletedAt`/`deletedBy`. Mốc dưới là `updatedAt`, mà `AuditingInterceptor` ghi
ngay lượt SaveChanges của thao tác xóa mềm ⇒ XẤP XỈ thời điểm xóa. Nhãn "≈" để
KHÔNG hứa chính xác. Tên người xóa API không trả — cố ý bỏ trống thay vì đoán. */}
{deleted && plan.updatedAt && (
<>
<span>·</span>
<span
className="shrink-0 rounded bg-red-50 px-1 py-0.5 text-[10px] font-medium text-red-700"
title="Thời điểm xóa (xấp xỉ — lấy từ mốc cập nhật cuối, ghi cùng lượt xóa mềm). Xem lịch sử phiếu để biết ai xóa và lý do."
>
🗑 xóa {formatDateTime(plan.updatedAt)}
</span>
</>
)}
</div>
<div className="mt-0.5 flex items-center justify-between gap-2 text-[11px] text-slate-500">
<span className="min-w-0 truncate">👤 {plan.drafterFullName ?? '—'}</span>
@ -293,6 +312,7 @@ export function KhkkListPanel({
selected={selectedId === k.id}
onSelect={deletedView ? () => { /* phiếu đã xóa: chỉ liệt kê, không mở */ } : onSelect}
finalize={selectedId === k.id ? selectedFinalize : undefined}
deleted={deletedView}
/>
</li>
))}
@ -316,6 +336,19 @@ export function KhkkListPanel({
className="pl-8"
/>
</div>
{/* [S176] Thùng rác ⇒ THAY dropdown bằng banner, mirror khuôn màn Duyệt NCC
(`PurchaseEvaluationsListPage:427-431`).
🔴 Ẩn dropdown là CÓ LÝ DO, không phải cho gọn: phiếu giữ NGUYÊN phase lúc bị xóa,
`IsDeleted` mới là thứ phân loại ⇒ lọc trạng thái ở đây là nút chết. Chính `plansForPe`
bên dưới cũng cố ý KHÔNG lọc phase khi xem thùng rác — để dropdown lại thì người dùng
bấm mà không thấy gì đổi.
🔸 CỐ Ý BỎ vế "không còn tính vào lũy kế ngân sách" của bản PE: `grep 'lũy kế'` trong
toàn bộ KHKK = 0 hit ⇒ bê nguyên là khai một điều chưa chứng minh cho module này. */}
{deletedView ? (
<div className="rounded border border-red-200 bg-red-50 px-2 py-1.5 text-[11px] text-red-700">
🗑 <strong>Phiếu đã xóa</strong> chỉ xem. Không khôi phục, không thao tác.
</div>
) : (
<Select
value={phase ?? ''}
aria-label="Lọc trạng thái"
@ -326,6 +359,7 @@ export function KhkkListPanel({
<option key={p} value={p}>{KHKK_PHASE_LABELS[Number(p) as KhkkPhaseValue]}</option>
))}
</Select>
)}
{/* 🧊 [S175] Dòng "Đang lọc — danh sách lấy thẳng từ máy chủ…" ĐÃ GỠ (anh: "Bỏ mấy dòng
này đi"). Đó là chú-thích về CƠ-CHẾ bên trong, người dùng không cần biết cây tải 200
phiếu hay lấy thẳng máy chủ — họ chỉ cần thấy đúng phiếu. */}
@ -412,6 +446,7 @@ export function KhkkListPanel({
selected={selectedId === k.id}
onSelect={deletedView ? () => { /* phiếu đã xóa: chỉ liệt kê, không mở */ } : onSelect}
finalize={selectedId === k.id ? selectedFinalize : undefined}
deleted={deletedView}
/>
</li>
))}