[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)]">
<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 => { {list.data.items.map(e => {
const active = selectedId === e.id const active = selectedId === e.id
return ( return (
<tr <button
key={e.id} key={e.id}
type="button"
onClick={() => setParam('id', e.id)} onClick={() => setParam('id', e.id)}
className={cn( className={cn(
'cursor-pointer border-b border-slate-100 transition last:border-0 hover:bg-slate-50', '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', active && 'bg-brand-50 hover:bg-brand-50',
)} )}
> >
<td className="px-3 py-2"> <Avatar name={e.fullName} size={30} dim={e.status === 3} />
<div className="flex items-center gap-2.5"> <div className="min-w-0 flex-1">
<Avatar name={e.fullName} size={32} dim={e.status === 3} /> <div className="flex items-center justify-between gap-2">
<div className="min-w-0"> <span className="truncate text-[13px] font-medium text-brand-800">{e.fullName ?? '—'}</span>
<div className="truncate font-medium text-brand-800">{e.fullName ?? '—'}</div> <span className="shrink-0"><StatusBadge status={e.status} /></span>
<div className="font-mono text-[11px] text-slate-400">{e.employeeCode}</div> </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>
</div> </div>
</td> </button>
<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> </div>
</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)]">
<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 => { {list.data.items.map(e => {
const active = selectedId === e.id const active = selectedId === e.id
return ( return (
<tr <button
key={e.id} key={e.id}
type="button"
onClick={() => setParam('id', e.id)} onClick={() => setParam('id', e.id)}
className={cn( className={cn(
'cursor-pointer border-b border-slate-100 transition last:border-0 hover:bg-slate-50', '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', active && 'bg-brand-50 hover:bg-brand-50',
)} )}
> >
<td className="px-3 py-2"> <Avatar name={e.fullName} size={30} dim={e.status === 3} />
<div className="flex items-center gap-2.5"> <div className="min-w-0 flex-1">
<Avatar name={e.fullName} size={32} dim={e.status === 3} /> <div className="flex items-center justify-between gap-2">
<div className="min-w-0"> <span className="truncate text-[13px] font-medium text-brand-800">{e.fullName ?? '—'}</span>
<div className="truncate font-medium text-brand-800">{e.fullName ?? '—'}</div> <span className="shrink-0"><StatusBadge status={e.status} /></span>
<div className="font-mono text-[11px] text-slate-400">{e.employeeCode}</div> </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>
</div> </div>
</td> </button>
<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> </div>
</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></>)}