[CLAUDE] FE-User FE-Admin: Plan AA wrap fix - sidebar label dài wrap về đầu hàng + text smaller
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m25s

UAT feedback 2026-05-15 sau Run #213 deploy: bro screenshot sidebar label custom
Mig 27 dài "1. Duyệt Nhà Cung Cấp - Thầu phụ (NCC -TP)" wrap 2 dòng, dòng 2
"(NCC -TP)" indent SAU icon thay vì về đầu hàng.

Root cause: flex container [items-center, gap-2] + inner span chứa Icon + label
text → text wraps within INNER span (đã indent past icon area). Pattern phù
hợp cho 1-dòng label, KHÔNG phù hợp khi multi-line.

Fix pattern (3 sites fe-user + 2 sites fe-admin mirror rule §3.9):

- MenuGroup button: flex → relative block + inline-block icon + inline text +
  absolute ChevronDown right. Text wrap về left edge button (under icon).
- MenuLeaf NavLink: flex → block + inline-block icon + inline text.
- StaticLeaf NavLink (fe-user only): mirror MenuLeaf pattern.

Smaller text:
- text-[13px] → text-[12px] (medium label group + leaf)
- text-sm (14px) → text-[12px] (MenuLeaf top level)
- text-[12px] → text-[11px] (MenuLeaf deep level)
- leading-snug (1.375) compact 2-line height

Icon adjust: -mt-0.5 align with inline text baseline.
Button px-3 pr-7: pad right 28px reserve cho absolute ChevronDown (KHÔNG bị đẩy
xuống khi label wrap).

Verify:
- npm run build fe-user PASS clean 432ms
- npm run build fe-admin PASS clean 494ms

Em main solo CSS polish < 30 min (criteria #6 REFUSE Implementer).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
pqhuy1987
2026-05-15 17:32:21 +07:00
parent fbbd361929
commit ee0902ac13
2 changed files with 42 additions and 42 deletions

View File

@ -135,21 +135,21 @@ function MenuGroup({ node, depth }: { node: MenuNode; depth: number }) {
<button <button
onClick={() => setOpen(o => !o)} onClick={() => setOpen(o => !o)}
className={cn( className={cn(
'flex w-full items-center justify-between rounded-md transition', 'relative block w-full rounded-md text-left transition',
isTopLevel isTopLevel
? 'px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500 hover:bg-slate-100 whitespace-nowrap' ? 'px-3 py-2 pr-7 text-[11px] font-semibold uppercase tracking-wide text-slate-500 hover:bg-slate-100 whitespace-nowrap'
: 'px-3 py-1.5 text-[13px] font-medium text-slate-600 hover:bg-slate-100 hover:text-slate-900', : 'px-3 py-1.5 pr-7 text-[12px] font-medium leading-snug text-slate-600 hover:bg-slate-100 hover:text-slate-900',
)} )}
> >
{/* [Plan AA S24 t1] Revert truncate Plan U S23 t11 — mirror fe-user {/* [Plan AA S24 t2 wrap fix] inline-block icon + inline text — label
rule §3.9. Admin sidebar không có label custom Mig 27 (luôn Label dài wrap về đầu hàng (under icon, KHÔNG indent sau icon). Mirror
gốc) nên label rarely overflow, nhưng pattern uniform với fe-user. fe-user rule §3.9. ChevronDown absolute right. */}
min-w-0 flex-1 + shrink-0 giữ responsive smooth. */} <Icon className="mr-1.5 -mt-0.5 inline-block h-4 w-4 align-middle" />
<span className="flex min-w-0 flex-1 items-center gap-2"> <span className="align-middle" title={node.label}>{node.label}</span>
<Icon className="h-4 w-4 shrink-0" /> <ChevronDown className={cn(
<span title={node.label}>{node.label}</span> 'absolute right-2 top-2 h-3.5 w-3.5 text-slate-400 transition',
</span> !open && '-rotate-90',
<ChevronDown className={cn('h-3.5 w-3.5 shrink-0 text-slate-400 transition', !open && '-rotate-90')} /> )} />
</button> </button>
{open && ( {open && (
<div className={cn( <div className={cn(
@ -199,16 +199,16 @@ function MenuLeaf({ node, depth }: { node: MenuNode; depth: number }) {
to={path} to={path}
title={node.label} title={node.label}
className={cn( className={cn(
'flex min-w-0 items-center gap-2.5 rounded-md transition', 'block rounded-md leading-snug transition',
isDeep ? 'px-3 py-1 text-[12px]' : 'px-3 py-2 text-sm font-medium', isDeep ? 'px-3 py-1 text-[11px]' : 'px-3 py-2 text-[12px] font-medium',
isActive isActive
? 'bg-brand-50 text-brand-700' ? 'bg-brand-50 text-brand-700'
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900', : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
)} )}
> >
<Icon className={cn('shrink-0', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} /> {/* [Plan AA S24 t2 wrap fix] inline-block icon + inline text → mirror fe-user. */}
{/* [Plan AA S24 t1] Revert truncate Plan U S23 t11 — mirror fe-user. */} <Icon className={cn('mr-2 -mt-0.5 inline-block align-middle', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
<span>{node.label}</span> <span className="align-middle">{node.label}</span>
</NavLink> </NavLink>
) )
} }

View File

@ -176,24 +176,25 @@ function MenuGroup({ node, depth }: { node: MenuNode; depth: number }) {
<button <button
onClick={toggle} onClick={toggle}
className={cn( className={cn(
'flex w-full items-center justify-between rounded-md transition', 'relative block w-full rounded-md text-left transition',
isTopLevel isTopLevel
? 'px-3 py-2 text-[11px] font-semibold uppercase tracking-wide text-slate-500 hover:bg-slate-100 whitespace-nowrap' ? 'px-3 py-2 pr-7 text-[11px] font-semibold uppercase tracking-wide text-slate-500 hover:bg-slate-100 whitespace-nowrap'
: 'px-3 py-1.5 text-[13px] font-medium text-slate-600 hover:bg-slate-100 hover:text-slate-900', : 'px-3 py-1.5 pr-7 text-[12px] font-medium leading-snug text-slate-600 hover:bg-slate-100 hover:text-slate-900',
// Highlight Ct_ group đang active (accordion open) bằng tinted background // Highlight Ct_ group đang active (accordion open) bằng tinted background
isAccordion && open && 'bg-slate-50 text-slate-900', isAccordion && open && 'bg-slate-50 text-slate-900',
)} )}
> >
{/* [Plan AA S24 t1] Revert truncate Plan U S23 t11 — bro request hiển {/* [Plan AA S24 t2 wrap fix] Bro UAT request: label dài 2 dòng phải về
thị đầy đủ label custom. Sidebar widen w-72 xl:w-80 fit ~44 chars đầu hàng (under icon, KHÔNG indent sau icon). Pattern: inline-block
1 dòng. Label cực dài fallback wrap multi-line natural CSS. icon + inline text → text wrap natural về left edge button.
min-w-0 flex-1 + shrink-0 icon/chevron giữ responsive smooth. title ChevronDown absolute right để KHÔNG bị đẩy xuống khi text wrap.
tooltip giữ (no harm — accessibility bonus). */} Smaller text-[12px] + leading-snug compact 2-line height. */}
<span className="flex min-w-0 flex-1 items-center gap-2"> <Icon className="mr-1.5 -mt-0.5 inline-block h-4 w-4 align-middle" />
<Icon className="h-4 w-4 shrink-0" /> <span className="align-middle" title={effectiveLabel(node)}>{effectiveLabel(node)}</span>
<span title={effectiveLabel(node)}>{effectiveLabel(node)}</span> <ChevronDown className={cn(
</span> 'absolute right-2 top-2 h-3.5 w-3.5 text-slate-400 transition',
<ChevronDown className={cn('h-3.5 w-3.5 shrink-0 text-slate-400 transition', !open && '-rotate-90')} /> !open && '-rotate-90',
)} />
</button> </button>
{open && ( {open && (
<div className={cn( <div className={cn(
@ -246,17 +247,17 @@ function MenuLeaf({ node, depth }: { node: MenuNode; depth: number }) {
to={path} to={path}
title={effectiveLabel(node)} title={effectiveLabel(node)}
className={cn( className={cn(
'flex min-w-0 items-center gap-2.5 rounded-md transition', 'block rounded-md leading-snug transition',
isDeep ? 'px-3 py-1 text-[12px]' : 'px-3 py-2 text-sm font-medium', isDeep ? 'px-3 py-1 text-[11px]' : 'px-3 py-2 text-[12px] font-medium',
isActive isActive
? 'bg-brand-50 text-brand-700' ? 'bg-brand-50 text-brand-700'
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900', : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
)} )}
> >
<Icon className={cn('shrink-0', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} /> {/* [Plan AA S24 t2 wrap fix] inline-block icon + inline text → label dài
{/* [Plan AA S24 t1] Revert truncate Plan U S23 t11 — fit full label hoặc wrap về đầu hàng (under icon, KHÔNG indent sau icon). */}
wrap natural. NavLink `title` (line 241) giữ tooltip accessibility. */} <Icon className={cn('mr-2 -mt-0.5 inline-block align-middle', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
<span>{effectiveLabel(node)}</span> <span className="align-middle">{effectiveLabel(node)}</span>
</NavLink> </NavLink>
) )
} }
@ -283,16 +284,15 @@ function StaticLeaf({ node }: { node: MenuNode }) {
title={effectiveLabel(node)} title={effectiveLabel(node)}
className={({ isActive }) => className={({ isActive }) =>
cn( cn(
'flex min-w-0 items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium transition', 'block rounded-md px-3 py-2 text-[12px] font-medium leading-snug transition',
isActive ? 'bg-brand-50 text-brand-700' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900', isActive ? 'bg-brand-50 text-brand-700' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
) )
} }
> >
<Icon className="h-4 w-4 shrink-0" /> {/* [Plan AA S24 t2 wrap fix] inline-block icon + inline text — consistent
{/* [Plan AA S24 t1] Revert truncate Plan U S23 t11 — StaticLeaf "Hộp thư" với MenuLeaf + MenuGroup. */}
label ngắn (7 chars) không bao giờ overflow, drop truncate cho <Icon className="mr-2 -mt-0.5 inline-block h-4 w-4 align-middle" />
consistent với MenuLeaf + MenuGroup. */} <span className="align-middle">{effectiveLabel(node)}</span>
<span>{effectiveLabel(node)}</span>
</NavLink> </NavLink>
) )
} }