[CLAUDE] FE-Admin FE-User: Chunk U — Sidebar truncate long label + tooltip (Mig 27 DisplayLabel dài wrap fix)
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m30s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 3m30s
Bro UAT screenshot 2026-05-15: Submenu "1. Duyệt Nhà Cung Cấp - Thầu phụ
(NCC -TP)" trong sidebar fe-user wrap 2 dòng (label dài ~50 chars vs
sidebar w-60 = 240px chỉ fit ~25 chars).
Root: Admin đã set DisplayLabel custom qua Mig 27 (S20 t7 Menu eOffice
admin page) — `MenuItems.Pe_DuyetNcc` DisplayLabel = "1. Duyệt Nhà Cung
Cấp - Thầu phụ (NCC -TP)" (Label gốc = "Duyệt NCC" ngắn). FE render
{effectiveLabel(node)} thẳng vào span flex KHÔNG có truncate.
Fix Plan U mirror 2 app (rule §3.9):
3 nơi render label trong fe-user/Layout.tsx + 2 nơi mirror fe-admin:
1. MenuNodeRenderer button (accordion toggle):
```diff
- <span className="flex items-center gap-2">
+ <span className="flex min-w-0 flex-1 items-center gap-2">
- <Icon className="h-4 w-4" />
- {effectiveLabel(node)}
+ <Icon className="h-4 w-4 shrink-0" />
+ <span className="truncate" title={effectiveLabel(node)}>{effectiveLabel(node)}</span>
</span>
- <ChevronDown ... transition />
+ <ChevronDown ... shrink-0 ... transition />
```
2. MenuLeaf NavLink:
```diff
- <NavLink to={path} className={cn('flex items-center gap-2.5...')}>
+ <NavLink to={path} title={effectiveLabel(node)} className={cn('flex min-w-0 items-center gap-2.5...')}>
- <Icon className={cn(isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
- {effectiveLabel(node)}
+ <Icon className={cn('shrink-0', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
+ <span className="truncate">{effectiveLabel(node)}</span>
</NavLink>
```
3. StaticLeaf NavLink (fe-user only — Hộp thư static entry):
Pattern tương tự MenuLeaf
fe-admin dùng `node.label` thay vì `effectiveLabel(node)` (admin sidebar
luôn show Label gốc, KHÔNG đụng DisplayLabel per S20 t7 Q2=b).
Pattern key:
- `min-w-0 flex-1` trên flex parent — cần thiết để truncate child shrink
- `shrink-0` trên Icon + ChevronDown — giữ size không co
- `truncate` (Tailwind = overflow-hidden text-ellipsis whitespace-nowrap) trên span text
- `title={label}` tooltip hover show full label nếu user cần đọc đầy đủ
Verify:
- npm run build fe-user PASS 16.79s clean
- npm run build fe-admin PASS 8.16s clean
- 0 TS error
KHÔNG đụng BE. Admin tự control DisplayLabel qua Mig 27 Menu eOffice page
— Plan U chỉ ensure FE render gracefully với label dài (truncate +
tooltip hover) thay vì wrap broken visual.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -141,11 +141,13 @@ function MenuGroup({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
: 'px-3 py-1.5 text-[13px] font-medium text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
||||
)}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Icon className="h-4 w-4" />
|
||||
{node.label}
|
||||
{/* Plan U S23 t11 — truncate label dài + tooltip hover (mirror fe-user
|
||||
rule §3.9). min-w-0 cần thiết cho truncate flex child. */}
|
||||
<span className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<Icon className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate" title={node.label}>{node.label}</span>
|
||||
</span>
|
||||
<ChevronDown className={cn('h-3.5 w-3.5 text-slate-400 transition', !open && '-rotate-90')} />
|
||||
<ChevronDown className={cn('h-3.5 w-3.5 shrink-0 text-slate-400 transition', !open && '-rotate-90')} />
|
||||
</button>
|
||||
{open && (
|
||||
<div className={cn(
|
||||
@ -193,16 +195,17 @@ function MenuLeaf({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
return (
|
||||
<NavLink
|
||||
to={path}
|
||||
title={node.label}
|
||||
className={cn(
|
||||
'flex items-center gap-2.5 rounded-md transition',
|
||||
'flex min-w-0 items-center gap-2.5 rounded-md transition',
|
||||
isDeep ? 'px-3 py-1 text-[12px]' : 'px-3 py-2 text-sm font-medium',
|
||||
isActive
|
||||
? 'bg-brand-50 text-brand-700'
|
||||
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
||||
)}
|
||||
>
|
||||
<Icon className={cn(isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
|
||||
{node.label}
|
||||
<Icon className={cn('shrink-0', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
|
||||
<span className="truncate">{node.label}</span>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
@ -180,11 +180,14 @@ function MenuGroup({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
isAccordion && open && 'bg-slate-50 text-slate-900',
|
||||
)}
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Icon className="h-4 w-4" />
|
||||
{effectiveLabel(node)}
|
||||
{/* Plan U S23 t11 — truncate label dài + tooltip hover (admin Mig 27
|
||||
có thể đặt DisplayLabel dài). min-w-0 cần thiết để truncate hoạt
|
||||
động trong flex child. shrink-0 giữ icon + chevron không co. */}
|
||||
<span className="flex min-w-0 flex-1 items-center gap-2">
|
||||
<Icon className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate" title={effectiveLabel(node)}>{effectiveLabel(node)}</span>
|
||||
</span>
|
||||
<ChevronDown className={cn('h-3.5 w-3.5 text-slate-400 transition', !open && '-rotate-90')} />
|
||||
<ChevronDown className={cn('h-3.5 w-3.5 shrink-0 text-slate-400 transition', !open && '-rotate-90')} />
|
||||
</button>
|
||||
{open && (
|
||||
<div className={cn(
|
||||
@ -235,16 +238,17 @@ function MenuLeaf({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
return (
|
||||
<NavLink
|
||||
to={path}
|
||||
title={effectiveLabel(node)}
|
||||
className={cn(
|
||||
'flex items-center gap-2.5 rounded-md transition',
|
||||
'flex min-w-0 items-center gap-2.5 rounded-md transition',
|
||||
isDeep ? 'px-3 py-1 text-[12px]' : 'px-3 py-2 text-sm font-medium',
|
||||
isActive
|
||||
? 'bg-brand-50 text-brand-700'
|
||||
: 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
||||
)}
|
||||
>
|
||||
<Icon className={cn(isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
|
||||
{effectiveLabel(node)}
|
||||
<Icon className={cn('shrink-0', isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
|
||||
<span className="truncate">{effectiveLabel(node)}</span>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
@ -268,15 +272,16 @@ function StaticLeaf({ node }: { node: MenuNode }) {
|
||||
<NavLink
|
||||
to={path}
|
||||
end
|
||||
title={effectiveLabel(node)}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
'flex items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium transition',
|
||||
'flex min-w-0 items-center gap-2.5 rounded-md px-3 py-2 text-sm font-medium transition',
|
||||
isActive ? 'bg-brand-50 text-brand-700' : 'text-slate-600 hover:bg-slate-100 hover:text-slate-900',
|
||||
)
|
||||
}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
{effectiveLabel(node)}
|
||||
<Icon className="h-4 w-4 shrink-0" />
|
||||
<span className="truncate">{effectiveLabel(node)}</span>
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user