[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
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>
This commit is contained in:
@ -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>
|
||||
))}
|
||||
|
||||
@ -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>
|
||||
))}
|
||||
|
||||
Reference in New Issue
Block a user