[CLAUDE] FE-User: YC-029 W2 — hoán đổi cây toàn trình sang trái + Select loại + list phải (mirror cặp pipeline ×2 app)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-08-12 19:29:15 +07:00
parent 09ebd20eda
commit 7b291014fc
5 changed files with 182 additions and 63 deletions

View File

@ -108,7 +108,10 @@ export function PipelineTreePanel({
// NGUYÊN hành vi cũ (8 ngăn nhóm + navigate) ở trang Duyệt NCC / GĐ3 / GĐ4.
// [S175 · O6] +`onOpenPe`: leaf GĐ1 (phiếu Duyệt NCC) cũng cho host bắt tại chỗ, đối
// xứng với `onOpenKhkk` của leaf GĐ2. Cả 2 đều OPT-IN nên 4 host không truyền giữ nguyên.
stage2Options?: Pick<BuildStagesOptions, 'stage2Content' | 'stage2Count' | 'onOpenKhkk' | 'onOpenPe'>
// [S190 · W2 YC-029] +`onOpenContract` (leaf GĐ3 — khép kín bộ ba GĐ1/GĐ2/GĐ3) và
// +`stage3TypeFilter` (lọc leaf GĐ3 theo loại HĐ, nguồn = Select loại ở host `my-contracts`).
// GIỮ TÊN prop `stage2Options` dù nay mang cả tuỳ-chọn GĐ3: đổi tên = chạm 7 host, ngoài wave.
stage2Options?: Pick<BuildStagesOptions, 'stage2Content' | 'stage2Count' | 'onOpenKhkk' | 'onOpenPe' | 'onOpenContract' | 'stage3TypeFilter'>
// [S168 GĐ3-D gate F-1] Host 3-panel (Panel-3 TỰ CUỘN) truyền class trần riêng cho THÂN
// cây (vd `lg:max-h-none` — panel giữ đúng 1 thanh cuộn; max-h mặc định 100vh16rem là
// hiệu-chỉnh cho trang giấy-cuộn, vượt chỗ Panel-3 /inbox ~26px ⇒ 2 cuộn lồng nhau).

View File

@ -190,6 +190,22 @@ export type BuildStagesOptions = {
* `navigate('/purchase-evaluations?id=…&expand=1')` ⇒ 5 host còn lại 0 đổi hành vi.
*/
onOpenPe?: (id: string) => void
/**
* [S190 · W2 YC-029] Bộ ba khép kín với `onOpenKhkk` (GĐ2) / `onOpenPe` (GĐ1): leaf GĐ3 (HĐ)
* cho HOST bắt tại chỗ. Màn `my-contracts` sau hoán-đổi đặt cây ở panel-1 TRÁI ⇒ bấm leaf phải
* chọn HĐ vào panel-2 (URL ở lại `/my-contracts`), không rời trang.
* không truyền ⇒ giữ nguyên `navigate('/contracts/:id')` — 6 host còn lại (kể cả fe-admin
* `ContractsListPage.tsx:265`) 0 đổi hành vi.
*/
onOpenContract?: (id: string) => void
/**
* [S190 · W2 YC-029] Lọc leaf GĐ3 theo `ContractType` (1..7) — nguồn là thanh `Select` loại HĐ
* ở đầu panel cây `my-contracts` (Q2 nhánh-đã-chọn).
* không truyền ⇒ giữ nguyên cây đủ MỌI loại HĐ — 6 host còn lại 0 đổi hành vi.
* 🔴 HĐ nằm NGOÀI cửa sổ index 200 KHÔNG suy được loại ⇒ vẫn hiện dù filter bật; lý-do ghi tại
* chỗ dùng (`passStage3Type`).
*/
stage3TypeFilter?: number
}
/**
@ -365,7 +381,13 @@ export function usePipelineStages() {
peItems.find(p => p.contractId === contractId)?.tenGoiThau
?? khkkItems.find(k => khkkPlanContractIds(k).includes(contractId))?.peTenGoiThau
?? null
if (linkage.canContract) {
// [S190 W2] Vế lọc loại HĐ — CHỈ chạy khi host bật `stage3TypeFilter` (opt-in).
// 🔴 QUYẾT ĐỊNH "THÀ THỪA CÒN HƠN THIẾU": `c` vắng = HĐ ngoài cửa sổ index 200 (xem
// `primary` ngay dưới) ⇒ KHÔNG có `c.type` để phân loại. Ẩn nó đi khi filter bật là làm
// HĐ biến mất IM LẶNG — đúng cái bệnh F-3 S162 đã vá bằng nhãn "(ngoài chỉ mục 200)".
// Vậy nên `!c` được cho QUA: người dùng thấy thừa 1 leaf còn hơn mất trắng 1 HĐ.
const passStage3Type = !opts.stage3TypeFilter || !c || c.type === opts.stage3TypeFilter
if (linkage.canContract && passStage3Type) {
stage3Leaves.push({
id: contractId,
// [F-3 S162] c vắng = HĐ NGOÀI cửa sổ index 200 — đừng dán nhãn
@ -374,7 +396,11 @@ export function usePipelineStages() {
secondary: c?.tenHopDong ?? fallbackName,
badgeLabel: c ? ContractPhaseLabel[c.phase] : null,
badgeClass: c ? ContractPhaseColor[c.phase] : null,
onOpen: () => navigate(`/contracts/${contractId}`),
// [S190 W2] Host truyền callback ⇒ chọn tại chỗ; không truyền ⇒ navigate như cũ.
// Ternary y khuôn leaf GĐ1 `:228-230` / GĐ2 `:244` — 3 giai-đoạn cùng một luật.
onOpen: opts.onOpenContract
? () => opts.onOpenContract!(contractId)
: () => navigate(`/contracts/${contractId}`),
})
}
if (linkage.canHardCopy && c?.hasSealedCopy) {