Compare commits
2 Commits
097e41a085
...
3794d89da5
| Author | SHA1 | Date | |
|---|---|---|---|
| 3794d89da5 | |||
| 4f327c36ec |
@ -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ăm›Dự án›Hạng mục›phiế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ăm›Dự án›Hạng mục›phiế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)
|
||||
|
||||
@ -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,16 +336,30 @@ export function KhkkListPanel({
|
||||
className="pl-8"
|
||||
/>
|
||||
</div>
|
||||
<Select
|
||||
value={phase ?? ''}
|
||||
aria-label="Lọc trạng thái"
|
||||
onChange={e => onPhaseChange(e.target.value === '' ? null : (Number(e.target.value) as KhkkPhaseValue))}
|
||||
>
|
||||
<option value="">Tất cả trạng thái</option>
|
||||
{(Object.keys(KHKK_PHASE_LABELS) as unknown as number[]).map(p => (
|
||||
<option key={p} value={p}>{KHKK_PHASE_LABELS[Number(p) as KhkkPhaseValue]}</option>
|
||||
))}
|
||||
</Select>
|
||||
{/* [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"
|
||||
onChange={e => onPhaseChange(e.target.value === '' ? null : (Number(e.target.value) as KhkkPhaseValue))}
|
||||
>
|
||||
<option value="">Tất cả trạng thái</option>
|
||||
{(Object.keys(KHKK_PHASE_LABELS) as unknown as number[]).map(p => (
|
||||
<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,16 +336,30 @@ export function KhkkListPanel({
|
||||
className="pl-8"
|
||||
/>
|
||||
</div>
|
||||
<Select
|
||||
value={phase ?? ''}
|
||||
aria-label="Lọc trạng thái"
|
||||
onChange={e => onPhaseChange(e.target.value === '' ? null : (Number(e.target.value) as KhkkPhaseValue))}
|
||||
>
|
||||
<option value="">Tất cả trạng thái</option>
|
||||
{(Object.keys(KHKK_PHASE_LABELS) as unknown as number[]).map(p => (
|
||||
<option key={p} value={p}>{KHKK_PHASE_LABELS[Number(p) as KhkkPhaseValue]}</option>
|
||||
))}
|
||||
</Select>
|
||||
{/* [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"
|
||||
onChange={e => onPhaseChange(e.target.value === '' ? null : (Number(e.target.value) as KhkkPhaseValue))}
|
||||
>
|
||||
<option value="">Tất cả trạng thái</option>
|
||||
{(Object.keys(KHKK_PHASE_LABELS) as unknown as number[]).map(p => (
|
||||
<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