[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">
<div className="flex items-center justify-between gap-2">
<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">
{selectedDeptName ? `Phòng: ${selectedDeptName}` : 'Tất cả phòng ban'}
{' · '}
@ -297,8 +297,8 @@ export function EmployeesListPage() {
)}
</form>
{/* list table */}
<div className="min-h-0 flex-1 overflow-auto rounded-xl border border-slate-200 bg-white shadow-sm">
{/* 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-y-auto overflow-x-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
{list.isLoading ? (
<div className="p-10 text-center text-sm text-slate-500">Đang tải</div>
) : !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."
/>
) : (
<table className="w-full border-collapse text-sm">
<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)]">
<tr>
<th className="px-3 py-2 text-left font-semibold">Nhân viên</th>
<th className="px-3 py-2 text-left font-semibold">Phòng ban</th>
<th className="px-3 py-2 text-left font-semibold">Trạng thái</th>
</tr>
</thead>
<tbody>
{list.data.items.map(e => {
const active = selectedId === e.id
return (
<tr
key={e.id}
onClick={() => setParam('id', e.id)}
className={cn(
'cursor-pointer border-b border-slate-100 transition last:border-0 hover:bg-slate-50',
active && 'bg-brand-50 hover:bg-brand-50',
)}
>
<td className="px-3 py-2">
<div className="flex items-center gap-2.5">
<Avatar name={e.fullName} size={32} dim={e.status === 3} />
<div className="min-w-0">
<div className="truncate font-medium text-brand-800">{e.fullName ?? '—'}</div>
<div className="font-mono text-[11px] text-slate-400">{e.employeeCode}</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 className="divide-y divide-slate-100">
{list.data.items.map(e => {
const active = selectedId === e.id
return (
<button
key={e.id}
type="button"
onClick={() => setParam('id', e.id)}
className={cn(
'flex w-full items-center gap-2.5 px-3 py-2 text-left transition hover:bg-slate-50',
active && 'bg-brand-50 hover:bg-brand-50',
)}
>
<Avatar name={e.fullName} size={30} dim={e.status === 3} />
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-2">
<span className="truncate text-[13px] font-medium text-brand-800">{e.fullName ?? '—'}</span>
<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">
<span className="font-mono text-slate-400">{e.employeeCode}</span>
{e.departmentName && <><span className="text-slate-300">·</span><span className="truncate">{e.departmentName}</span></>}
</div>
</div>
</button>
)
})}
</div>
)}
</div>
</section>
@ -654,13 +644,13 @@ function EmployeeDetailTabs({ detail, onDelete }: { detail: EmployeeDetail; onDe
<div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 items-center gap-3.5">
<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
>
{initials(detail.fullName)}
</span>
<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">
<span className="font-mono">{detail.employeeCode}</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">
<div className="flex items-center justify-between gap-2">
<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">
{selectedDeptName ? `Phòng: ${selectedDeptName}` : 'Tất cả phòng ban'}
{' · '}
@ -297,8 +297,8 @@ export function EmployeesListPage() {
)}
</form>
{/* list table */}
<div className="min-h-0 flex-1 overflow-auto rounded-xl border border-slate-200 bg-white shadow-sm">
{/* 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-y-auto overflow-x-hidden rounded-xl border border-slate-200 bg-white shadow-sm">
{list.isLoading ? (
<div className="p-10 text-center text-sm text-slate-500">Đang tải</div>
) : !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."
/>
) : (
<table className="w-full border-collapse text-sm">
<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)]">
<tr>
<th className="px-3 py-2 text-left font-semibold">Nhân viên</th>
<th className="px-3 py-2 text-left font-semibold">Phòng ban</th>
<th className="px-3 py-2 text-left font-semibold">Trạng thái</th>
</tr>
</thead>
<tbody>
{list.data.items.map(e => {
const active = selectedId === e.id
return (
<tr
key={e.id}
onClick={() => setParam('id', e.id)}
className={cn(
'cursor-pointer border-b border-slate-100 transition last:border-0 hover:bg-slate-50',
active && 'bg-brand-50 hover:bg-brand-50',
)}
>
<td className="px-3 py-2">
<div className="flex items-center gap-2.5">
<Avatar name={e.fullName} size={32} dim={e.status === 3} />
<div className="min-w-0">
<div className="truncate font-medium text-brand-800">{e.fullName ?? '—'}</div>
<div className="font-mono text-[11px] text-slate-400">{e.employeeCode}</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 className="divide-y divide-slate-100">
{list.data.items.map(e => {
const active = selectedId === e.id
return (
<button
key={e.id}
type="button"
onClick={() => setParam('id', e.id)}
className={cn(
'flex w-full items-center gap-2.5 px-3 py-2 text-left transition hover:bg-slate-50',
active && 'bg-brand-50 hover:bg-brand-50',
)}
>
<Avatar name={e.fullName} size={30} dim={e.status === 3} />
<div className="min-w-0 flex-1">
<div className="flex items-center justify-between gap-2">
<span className="truncate text-[13px] font-medium text-brand-800">{e.fullName ?? '—'}</span>
<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">
<span className="font-mono text-slate-400">{e.employeeCode}</span>
{e.departmentName && <><span className="text-slate-300">·</span><span className="truncate">{e.departmentName}</span></>}
</div>
</div>
</button>
)
})}
</div>
)}
</div>
</section>
@ -654,13 +644,13 @@ function EmployeeDetailTabs({ detail, onDelete }: { detail: EmployeeDetail; onDe
<div className="flex items-start justify-between gap-3">
<div className="flex min-w-0 items-center gap-3.5">
<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
>
{initials(detail.fullName)}
</span>
<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">
<span className="font-mono">{detail.employeeCode}</span>
{detail.departmentName && (<><span className="opacity-50"></span><span className="truncate">{detail.departmentName}</span></>)}