[CLAUDE] FE: Hồ sơ NS — list pane flex-row gọn (hết tràn ngang rail) + đồng nhất cỡ chữ (x2 app SHA256)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 4m33s

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-06-16 14:20:23 +07:00
parent bcd619dbdb
commit 91aaf058fb
2 changed files with 66 additions and 86 deletions

View File

@ -241,7 +241,7 @@ export function EmployeesListPage() {
<section className="flex min-h-0 flex-1 flex-col gap-3"> <section className="flex min-h-0 flex-1 flex-col gap-3">
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<div className="min-w-0"> <div className="min-w-0">
<h2 className="truncate text-base font-semibold tracking-tight text-brand-800">Hồ Nhân sự</h2> <h2 className="truncate text-sm font-semibold tracking-tight text-brand-800">Hồ Nhân sự</h2>
<p className="truncate text-xs text-slate-500"> <p className="truncate text-xs text-slate-500">
{selectedDeptName ? `Phòng: ${selectedDeptName}` : 'Tất cả phòng ban'} {selectedDeptName ? `Phòng: ${selectedDeptName}` : 'Tất cả phòng ban'}
{' · '} {' · '}
@ -297,8 +297,8 @@ export function EmployeesListPage() {
)} )}
</form> </form>
{/* list table */} {/* list — compact flex rows (KHÔNG bảng 3 cột → không tràn ngang rail hẹp) */}
<div className="min-h-0 flex-1 overflow-auto rounded-xl border border-slate-200 bg-white shadow-sm"> <div className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
{list.isLoading ? ( {list.isLoading ? (
<div className="p-10 text-center text-sm text-slate-500">Đang tải</div> <div className="p-10 text-center text-sm text-slate-500">Đang tải</div>
) : !list.data || list.data.items.length === 0 ? ( ) : !list.data || list.data.items.length === 0 ? (
@ -308,44 +308,34 @@ export function EmployeesListPage() {
description="Đổi bộ lọc, hoặc bấm 'Tạo mới' để thêm hồ sơ nhân viên." description="Đổi bộ lọc, hoặc bấm 'Tạo mới' để thêm hồ sơ nhân viên."
/> />
) : ( ) : (
<table className="w-full border-collapse text-sm"> <div className="divide-y divide-slate-100">
<thead className="sticky top-0 z-10 bg-slate-50 text-[11px] uppercase tracking-wide text-slate-500 shadow-[0_1px_0_theme(colors.slate.200)]"> {list.data.items.map(e => {
<tr> const active = selectedId === e.id
<th className="px-3 py-2 text-left font-semibold">Nhân viên</th> return (
<th className="px-3 py-2 text-left font-semibold">Phòng ban</th> <button
<th className="px-3 py-2 text-left font-semibold">Trạng thái</th> key={e.id}
</tr> type="button"
</thead> onClick={() => setParam('id', e.id)}
<tbody> className={cn(
{list.data.items.map(e => { 'flex w-full items-center gap-2.5 px-3 py-2 text-left transition hover:bg-slate-50',
const active = selectedId === e.id active && 'bg-brand-50 hover:bg-brand-50',
return ( )}
<tr >
key={e.id} <Avatar name={e.fullName} size={30} dim={e.status === 3} />
onClick={() => setParam('id', e.id)} <div className="min-w-0 flex-1">
className={cn( <div className="flex items-center justify-between gap-2">
'cursor-pointer border-b border-slate-100 transition last:border-0 hover:bg-slate-50', <span className="truncate text-[13px] font-medium text-brand-800">{e.fullName ?? '—'}</span>
active && 'bg-brand-50 hover:bg-brand-50', <span className="shrink-0"><StatusBadge status={e.status} /></span>
)} </div>
> <div className="mt-0.5 flex items-center gap-1.5 truncate text-[11px] text-slate-500">
<td className="px-3 py-2"> <span className="font-mono text-slate-400">{e.employeeCode}</span>
<div className="flex items-center gap-2.5"> {e.departmentName && <><span className="text-slate-300">·</span><span className="truncate">{e.departmentName}</span></>}
<Avatar name={e.fullName} size={32} dim={e.status === 3} /> </div>
<div className="min-w-0"> </div>
<div className="truncate font-medium text-brand-800">{e.fullName ?? '—'}</div> </button>
<div className="font-mono text-[11px] text-slate-400">{e.employeeCode}</div> )
</div> })}
</div> </div>
</td>
<td className="px-3 py-2 text-slate-600">{e.departmentName ?? '—'}</td>
<td className="px-3 py-2">
<StatusBadge status={e.status} />
</td>
</tr>
)
})}
</tbody>
</table>
)} )}
</div> </div>
</section> </section>
@ -654,13 +644,13 @@ function EmployeeDetailTabs({ detail, onDelete }: { detail: EmployeeDetail; onDe
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 items-center gap-3.5"> <div className="flex min-w-0 items-center gap-3.5">
<span <span
className="grid h-16 w-16 shrink-0 place-items-center rounded-2xl bg-white/15 text-2xl font-bold ring-1 ring-white/25 backdrop-blur-sm" className="grid h-14 w-14 shrink-0 place-items-center rounded-2xl bg-white/15 text-xl font-bold ring-1 ring-white/25 backdrop-blur-sm"
aria-hidden aria-hidden
> >
{initials(detail.fullName)} {initials(detail.fullName)}
</span> </span>
<div className="min-w-0"> <div className="min-w-0">
<h2 className="truncate text-xl font-bold leading-tight text-white">{detail.fullName ?? '—'}</h2> <h2 className="truncate text-lg font-bold leading-tight text-white">{detail.fullName ?? '—'}</h2>
<div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-white/85"> <div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-white/85">
<span className="font-mono">{detail.employeeCode}</span> <span className="font-mono">{detail.employeeCode}</span>
{detail.departmentName && (<><span className="opacity-50"></span><span className="truncate">{detail.departmentName}</span></>)} {detail.departmentName && (<><span className="opacity-50"></span><span className="truncate">{detail.departmentName}</span></>)}

View File

@ -241,7 +241,7 @@ export function EmployeesListPage() {
<section className="flex min-h-0 flex-1 flex-col gap-3"> <section className="flex min-h-0 flex-1 flex-col gap-3">
<div className="flex items-center justify-between gap-2"> <div className="flex items-center justify-between gap-2">
<div className="min-w-0"> <div className="min-w-0">
<h2 className="truncate text-base font-semibold tracking-tight text-brand-800">Hồ Nhân sự</h2> <h2 className="truncate text-sm font-semibold tracking-tight text-brand-800">Hồ Nhân sự</h2>
<p className="truncate text-xs text-slate-500"> <p className="truncate text-xs text-slate-500">
{selectedDeptName ? `Phòng: ${selectedDeptName}` : 'Tất cả phòng ban'} {selectedDeptName ? `Phòng: ${selectedDeptName}` : 'Tất cả phòng ban'}
{' · '} {' · '}
@ -297,8 +297,8 @@ export function EmployeesListPage() {
)} )}
</form> </form>
{/* list table */} {/* list — compact flex rows (KHÔNG bảng 3 cột → không tràn ngang rail hẹp) */}
<div className="min-h-0 flex-1 overflow-auto rounded-xl border border-slate-200 bg-white shadow-sm"> <div className="min-h-0 flex-1 overflow-y-auto overflow-x-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
{list.isLoading ? ( {list.isLoading ? (
<div className="p-10 text-center text-sm text-slate-500">Đang tải</div> <div className="p-10 text-center text-sm text-slate-500">Đang tải</div>
) : !list.data || list.data.items.length === 0 ? ( ) : !list.data || list.data.items.length === 0 ? (
@ -308,44 +308,34 @@ export function EmployeesListPage() {
description="Đổi bộ lọc, hoặc bấm 'Tạo mới' để thêm hồ sơ nhân viên." description="Đổi bộ lọc, hoặc bấm 'Tạo mới' để thêm hồ sơ nhân viên."
/> />
) : ( ) : (
<table className="w-full border-collapse text-sm"> <div className="divide-y divide-slate-100">
<thead className="sticky top-0 z-10 bg-slate-50 text-[11px] uppercase tracking-wide text-slate-500 shadow-[0_1px_0_theme(colors.slate.200)]"> {list.data.items.map(e => {
<tr> const active = selectedId === e.id
<th className="px-3 py-2 text-left font-semibold">Nhân viên</th> return (
<th className="px-3 py-2 text-left font-semibold">Phòng ban</th> <button
<th className="px-3 py-2 text-left font-semibold">Trạng thái</th> key={e.id}
</tr> type="button"
</thead> onClick={() => setParam('id', e.id)}
<tbody> className={cn(
{list.data.items.map(e => { 'flex w-full items-center gap-2.5 px-3 py-2 text-left transition hover:bg-slate-50',
const active = selectedId === e.id active && 'bg-brand-50 hover:bg-brand-50',
return ( )}
<tr >
key={e.id} <Avatar name={e.fullName} size={30} dim={e.status === 3} />
onClick={() => setParam('id', e.id)} <div className="min-w-0 flex-1">
className={cn( <div className="flex items-center justify-between gap-2">
'cursor-pointer border-b border-slate-100 transition last:border-0 hover:bg-slate-50', <span className="truncate text-[13px] font-medium text-brand-800">{e.fullName ?? '—'}</span>
active && 'bg-brand-50 hover:bg-brand-50', <span className="shrink-0"><StatusBadge status={e.status} /></span>
)} </div>
> <div className="mt-0.5 flex items-center gap-1.5 truncate text-[11px] text-slate-500">
<td className="px-3 py-2"> <span className="font-mono text-slate-400">{e.employeeCode}</span>
<div className="flex items-center gap-2.5"> {e.departmentName && <><span className="text-slate-300">·</span><span className="truncate">{e.departmentName}</span></>}
<Avatar name={e.fullName} size={32} dim={e.status === 3} /> </div>
<div className="min-w-0"> </div>
<div className="truncate font-medium text-brand-800">{e.fullName ?? '—'}</div> </button>
<div className="font-mono text-[11px] text-slate-400">{e.employeeCode}</div> )
</div> })}
</div> </div>
</td>
<td className="px-3 py-2 text-slate-600">{e.departmentName ?? '—'}</td>
<td className="px-3 py-2">
<StatusBadge status={e.status} />
</td>
</tr>
)
})}
</tbody>
</table>
)} )}
</div> </div>
</section> </section>
@ -654,13 +644,13 @@ function EmployeeDetailTabs({ detail, onDelete }: { detail: EmployeeDetail; onDe
<div className="flex items-start justify-between gap-3"> <div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 items-center gap-3.5"> <div className="flex min-w-0 items-center gap-3.5">
<span <span
className="grid h-16 w-16 shrink-0 place-items-center rounded-2xl bg-white/15 text-2xl font-bold ring-1 ring-white/25 backdrop-blur-sm" className="grid h-14 w-14 shrink-0 place-items-center rounded-2xl bg-white/15 text-xl font-bold ring-1 ring-white/25 backdrop-blur-sm"
aria-hidden aria-hidden
> >
{initials(detail.fullName)} {initials(detail.fullName)}
</span> </span>
<div className="min-w-0"> <div className="min-w-0">
<h2 className="truncate text-xl font-bold leading-tight text-white">{detail.fullName ?? '—'}</h2> <h2 className="truncate text-lg font-bold leading-tight text-white">{detail.fullName ?? '—'}</h2>
<div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-white/85"> <div className="mt-1 flex flex-wrap items-center gap-x-2 gap-y-1 text-xs text-white/85">
<span className="font-mono">{detail.employeeCode}</span> <span className="font-mono">{detail.employeeCode}</span>
{detail.departmentName && (<><span className="opacity-50"></span><span className="truncate">{detail.departmentName}</span></>)} {detail.departmentName && (<><span className="opacity-50"></span><span className="truncate">{detail.departmentName}</span></>)}