[CLAUDE] FE-User: cây tổ chức gốc "SOLUTION COMPANY" -> toả xuống phòng ban
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m17s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m17s
Anh: hiển thị cha-con với gốc SOLUTION COMPANY trên cùng, phòng ban fan-out xuống (giống NamGroup "Nam Group" là gốc). Gộp nút "Tất cả phòng ban" + list phẳng thành 1 node gốc công ty trong EmployeesListPage org-tree panel: chevron mở/gập (companyOpen, mở mặc định) + bấm tên = pickDept(null) tất cả NV + CountBadge tổng; các phòng ban render TreeNode depth=1 toả xuống dưới. Build PASS fe-user (tsc -b). fe-user only (mirror fe-admin defer cùng Phase B). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -78,6 +78,7 @@ export function EmployeesListPage() {
|
|||||||
|
|
||||||
const [localSearch, setLocalSearch] = useState(search)
|
const [localSearch, setLocalSearch] = useState(search)
|
||||||
const [treeOpenMobile, setTreeOpenMobile] = useState(false)
|
const [treeOpenMobile, setTreeOpenMobile] = useState(false)
|
||||||
|
const [companyOpen, setCompanyOpen] = useState(true) // gốc công ty mở mặc định
|
||||||
|
|
||||||
// Org tree (consume /departments/tree). Class-level [Authorize] only → any
|
// Org tree (consume /departments/tree). Class-level [Authorize] only → any
|
||||||
// authenticated user. Counts come pre-rolled-up from BE (TotalEmployeeCount).
|
// authenticated user. Counts come pre-rolled-up from BE (TotalEmployeeCount).
|
||||||
@ -175,30 +176,54 @@ export function EmployeesListPage() {
|
|||||||
</div>
|
</div>
|
||||||
</header>
|
</header>
|
||||||
<div className="min-h-0 flex-1 overflow-auto p-2">
|
<div className="min-h-0 flex-1 overflow-auto p-2">
|
||||||
<button
|
|
||||||
type="button"
|
|
||||||
onClick={() => pickDept(null)}
|
|
||||||
className={cn(
|
|
||||||
'mb-1 flex w-full items-center justify-between gap-2 rounded-lg px-2.5 py-1.5 text-left text-sm transition',
|
|
||||||
!deptFilter ? 'bg-brand-50 font-semibold text-brand-700' : 'text-slate-700 hover:bg-slate-50',
|
|
||||||
)}
|
|
||||||
>
|
|
||||||
<span className="flex items-center gap-2">
|
|
||||||
<Users className="h-4 w-4 shrink-0 text-slate-400" />
|
|
||||||
Tất cả phòng ban
|
|
||||||
</span>
|
|
||||||
<CountBadge value={list.data?.total ?? 0} active={!deptFilter} />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
{tree.isLoading ? (
|
{tree.isLoading ? (
|
||||||
<div className="px-2 py-6 text-center text-xs text-slate-400">Đang tải cây tổ chức…</div>
|
<div className="px-2 py-6 text-center text-xs text-slate-400">Đang tải cây tổ chức…</div>
|
||||||
) : !tree.data || tree.data.length === 0 ? (
|
|
||||||
<div className="px-2 py-6 text-center text-xs text-slate-400">Chưa có phòng ban.</div>
|
|
||||||
) : (
|
) : (
|
||||||
<ul className="space-y-0.5">
|
<ul className="space-y-0.5">
|
||||||
{tree.data.map(n => (
|
{/* Gốc công ty — bấm = tất cả NV; các phòng ban toả xuống dưới (cha→con) */}
|
||||||
<TreeNode key={n.id} node={n} depth={0} selectedId={deptFilter} onPick={pickDept} />
|
<li>
|
||||||
))}
|
<div
|
||||||
|
className={cn(
|
||||||
|
'group flex items-center gap-1 rounded-lg pr-1.5 transition',
|
||||||
|
!deptFilter ? 'bg-brand-50' : 'hover:bg-slate-50',
|
||||||
|
)}
|
||||||
|
>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => setCompanyOpen(v => !v)}
|
||||||
|
className="grid h-5 w-5 shrink-0 place-items-center rounded text-slate-400 hover:text-slate-700"
|
||||||
|
aria-label={companyOpen ? 'Thu gọn' : 'Mở rộng'}
|
||||||
|
aria-expanded={companyOpen}
|
||||||
|
>
|
||||||
|
<ChevronRight className={cn('h-3.5 w-3.5 transition-transform', companyOpen && 'rotate-90')} />
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
onClick={() => pickDept(null)}
|
||||||
|
className={cn(
|
||||||
|
'flex min-w-0 flex-1 items-center justify-between gap-2 py-1.5 text-left text-sm transition',
|
||||||
|
!deptFilter ? 'font-semibold text-brand-700' : 'text-slate-700',
|
||||||
|
)}
|
||||||
|
title="SOLUTION COMPANY"
|
||||||
|
>
|
||||||
|
<span className="flex min-w-0 items-center gap-2">
|
||||||
|
<Building2 className="h-4 w-4 shrink-0 text-brand-500" />
|
||||||
|
<span className="truncate font-semibold tracking-tight">SOLUTION COMPANY</span>
|
||||||
|
</span>
|
||||||
|
<CountBadge value={list.data?.total ?? 0} active={!deptFilter} />
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
{companyOpen &&
|
||||||
|
(!tree.data || tree.data.length === 0 ? (
|
||||||
|
<div className="py-3 pl-9 text-xs text-slate-400">Chưa có phòng ban.</div>
|
||||||
|
) : (
|
||||||
|
<ul className="space-y-0.5">
|
||||||
|
{tree.data.map(n => (
|
||||||
|
<TreeNode key={n.id} node={n} depth={1} selectedId={deptFilter} onPick={pickDept} />
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
))}
|
||||||
|
</li>
|
||||||
</ul>
|
</ul>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
Reference in New Issue
Block a user