[CLAUDE] FE-User+FE-Admin: Plan AG3 — PE List tree consistent (drop single-PE flat branch)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m19s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m19s
Anh feedback 2026-05-21: "nếu có 1 thì cũng để tương tự luôn nhé, đừng để khác các thằng kia". Plan AG2 render single-PE project flat card + UPPERCASE label phía trên — khác phong cách với multi-PE project (folder <details>). UX inconsistent. Plan AG3 drop nhánh single-PE flat. Mọi dự án dù 1 hay nhiều PE đều render <details> folder collapsed với badge count "(N)" — consistent visual. Diff: -60 LOC (drop entire single-PE flat block). Verify: - npm build fe-user PASS 0 TS err - npm build fe-admin PASS 0 TS err - 2 file SHA256 IDENTICAL 749FF703... (mirror §3.9) - KHÔNG BE change, KHÔNG Mig, KHÔNG test (UAT mode) Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -251,68 +251,13 @@ export function PurchaseEvaluationsListPage() {
|
||||
<EmptyState icon={ClipboardCheck} title="Chưa có phiếu" description="Tạo phiếu mới để bắt đầu quy trình." />
|
||||
</div>
|
||||
)}
|
||||
{/* Plan AG2 — Tree view 1-level Project > PE (drop gói thầu per anh 2026-05-21).
|
||||
<details>/<summary> HTML native. Single-PE project → render flat card no wrapper.
|
||||
Multi-PE project → <details> tree với toggle expand + localStorage persist.
|
||||
{/* Plan AG3 — Tree view 1-level Project > PE consistent (anh feedback 2026-05-21:
|
||||
"nếu có 1 thì cũng để tương tự luôn nhé, đừng để khác các thằng kia").
|
||||
Mọi dự án dù có 1 hay nhiều PE đều render <details> folder collapsed.
|
||||
Tailwind v3 named group group/proj + [&::-webkit-details-marker]:hidden. */}
|
||||
<div className="divide-y divide-slate-100">
|
||||
{projectGroups.map(pg => {
|
||||
const projKey = pg.projectId ?? '__no_project__'
|
||||
// Single-PE project → render flat card (no <details> wrapper) — anh feedback "ko cần treedow"
|
||||
if (pg.items.length === 1) {
|
||||
const p = pg.items[0]
|
||||
return (
|
||||
<button
|
||||
key={p.id}
|
||||
onClick={() => selectRow(p.id)}
|
||||
className={cn(
|
||||
'block w-full px-3 py-2.5 text-left transition hover:bg-slate-50',
|
||||
selectedId === p.id && 'bg-brand-50 ring-1 ring-inset ring-brand-200',
|
||||
)}
|
||||
>
|
||||
<div className="mb-1 flex items-center gap-1.5 text-[10px] uppercase tracking-wide text-slate-400">
|
||||
<span>📁</span>
|
||||
<span className="truncate">{pg.projectName}</span>
|
||||
</div>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-[13px] font-medium text-slate-900">{p.tenGoiThau}</div>
|
||||
<div className="mt-0.5 flex items-center gap-1.5 text-[11px] text-slate-500">
|
||||
<span className="font-mono">{p.maPhieu ?? '—'}</span>
|
||||
</div>
|
||||
{p.selectedSupplierName && (
|
||||
<div className="mt-0.5 truncate text-[11px] text-emerald-600">
|
||||
✓ {p.selectedSupplierName}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
'shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium',
|
||||
PeDisplayStatusColor[getPeDisplayStatus(p.phase)],
|
||||
)}
|
||||
>
|
||||
{PeDisplayStatusLabel[getPeDisplayStatus(p.phase)]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center justify-between text-[11px] text-slate-500">
|
||||
<span className="rounded bg-slate-100 px-1.5 py-0.5 text-slate-600">
|
||||
{PurchaseEvaluationTypeLabel[p.type]}
|
||||
</span>
|
||||
<span className="font-medium text-slate-600" title={`Tạo lúc ${new Date(p.createdAt).toLocaleString('vi-VN')}`}>
|
||||
{new Date(p.createdAt).toLocaleString('vi-VN', {
|
||||
day: '2-digit', month: '2-digit', year: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
{p.contractId && (
|
||||
<div className="mt-1 text-[10px] text-brand-600">✓ Đã tạo HĐ</div>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
// Multi-PE project → render <details> tree
|
||||
return (
|
||||
<details
|
||||
key={projKey}
|
||||
|
||||
@ -251,68 +251,13 @@ export function PurchaseEvaluationsListPage() {
|
||||
<EmptyState icon={ClipboardCheck} title="Chưa có phiếu" description="Tạo phiếu mới để bắt đầu quy trình." />
|
||||
</div>
|
||||
)}
|
||||
{/* Plan AG2 — Tree view 1-level Project > PE (drop gói thầu per anh 2026-05-21).
|
||||
<details>/<summary> HTML native. Single-PE project → render flat card no wrapper.
|
||||
Multi-PE project → <details> tree với toggle expand + localStorage persist.
|
||||
{/* Plan AG3 — Tree view 1-level Project > PE consistent (anh feedback 2026-05-21:
|
||||
"nếu có 1 thì cũng để tương tự luôn nhé, đừng để khác các thằng kia").
|
||||
Mọi dự án dù có 1 hay nhiều PE đều render <details> folder collapsed.
|
||||
Tailwind v3 named group group/proj + [&::-webkit-details-marker]:hidden. */}
|
||||
<div className="divide-y divide-slate-100">
|
||||
{projectGroups.map(pg => {
|
||||
const projKey = pg.projectId ?? '__no_project__'
|
||||
// Single-PE project → render flat card (no <details> wrapper) — anh feedback "ko cần treedow"
|
||||
if (pg.items.length === 1) {
|
||||
const p = pg.items[0]
|
||||
return (
|
||||
<button
|
||||
key={p.id}
|
||||
onClick={() => selectRow(p.id)}
|
||||
className={cn(
|
||||
'block w-full px-3 py-2.5 text-left transition hover:bg-slate-50',
|
||||
selectedId === p.id && 'bg-brand-50 ring-1 ring-inset ring-brand-200',
|
||||
)}
|
||||
>
|
||||
<div className="mb-1 flex items-center gap-1.5 text-[10px] uppercase tracking-wide text-slate-400">
|
||||
<span>📁</span>
|
||||
<span className="truncate">{pg.projectName}</span>
|
||||
</div>
|
||||
<div className="flex items-start justify-between gap-2">
|
||||
<div className="min-w-0 flex-1">
|
||||
<div className="truncate text-[13px] font-medium text-slate-900">{p.tenGoiThau}</div>
|
||||
<div className="mt-0.5 flex items-center gap-1.5 text-[11px] text-slate-500">
|
||||
<span className="font-mono">{p.maPhieu ?? '—'}</span>
|
||||
</div>
|
||||
{p.selectedSupplierName && (
|
||||
<div className="mt-0.5 truncate text-[11px] text-emerald-600">
|
||||
✓ {p.selectedSupplierName}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<span
|
||||
className={cn(
|
||||
'shrink-0 rounded px-1.5 py-0.5 text-[10px] font-medium',
|
||||
PeDisplayStatusColor[getPeDisplayStatus(p.phase)],
|
||||
)}
|
||||
>
|
||||
{PeDisplayStatusLabel[getPeDisplayStatus(p.phase)]}
|
||||
</span>
|
||||
</div>
|
||||
<div className="mt-1 flex items-center justify-between text-[11px] text-slate-500">
|
||||
<span className="rounded bg-slate-100 px-1.5 py-0.5 text-slate-600">
|
||||
{PurchaseEvaluationTypeLabel[p.type]}
|
||||
</span>
|
||||
<span className="font-medium text-slate-600" title={`Tạo lúc ${new Date(p.createdAt).toLocaleString('vi-VN')}`}>
|
||||
{new Date(p.createdAt).toLocaleString('vi-VN', {
|
||||
day: '2-digit', month: '2-digit', year: 'numeric',
|
||||
hour: '2-digit', minute: '2-digit',
|
||||
})}
|
||||
</span>
|
||||
</div>
|
||||
{p.contractId && (
|
||||
<div className="mt-1 text-[10px] text-brand-600">✓ Đã tạo HĐ</div>
|
||||
)}
|
||||
</button>
|
||||
)
|
||||
}
|
||||
// Multi-PE project → render <details> tree
|
||||
return (
|
||||
<details
|
||||
key={projKey}
|
||||
|
||||
Reference in New Issue
Block a user