[CLAUDE] FE-User: Chunk D — Layout filter !isVisible + render displayLabel
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
All checks were successful
Deploy SOLUTION_ERP / build-deploy (push) Successful in 2m51s
Session 20 turn 7 Chunk D. fe-user (eOffice) áp dụng Ẩn/Hiện + Đổi tên menu
admin set qua MenuVisibilityPage.
fe-user/types/menu.ts: MenuItem + MenuNode +isVisible bool +displayLabel
string|null (mirror fe-admin).
fe-user/components/Layout.tsx:
- filterForUser: filter 2 tầng — USER_HIDDEN_KEYS hardcode (Master/System/
Forms/Reports — structural never-show) + dynamic !isVisible (Mig 27)
- Helper effectiveLabel(n) = displayLabel?.trim() || label — admin custom
label thắng, fallback gốc
- Replace 3 callsite `{node.label}` → `{effectiveLabel(node)}` (Group/Leaf/
NavLink render)
- USER_FIXED_TOP entry "__inbox" +isVisible:true +displayLabel:null (giữ
type check pass)
fe-admin Layout KHÔNG đụng — admin sidebar luôn dùng Label gốc + render hết
menu (kể cả isVisible=false), per user Q2=b.
Verify:
- npm run build × fe-user pass
- npm run build × fe-admin pass (no regression)
Pending Chunk E: Docs S20 turn 7 + STATUS + HANDOFF
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@ -99,11 +99,19 @@ const USER_HIDDEN_KEYS = new Set([
|
||||
])
|
||||
|
||||
function filterForUser(nodes: MenuNode[]): MenuNode[] {
|
||||
// Filter 2 tầng: hardcode USER_HIDDEN_KEYS (system-level, structural never-show)
|
||||
// + dynamic isVisible (Mig 27 admin toggle qua MenuVisibilityPage). isVisible
|
||||
// mặc định true, admin set false → ẩn khỏi sidebar eOffice.
|
||||
return nodes
|
||||
.filter(n => !USER_HIDDEN_KEYS.has(n.key))
|
||||
.filter(n => !USER_HIDDEN_KEYS.has(n.key) && n.isVisible !== false)
|
||||
.map(n => ({ ...n, children: filterForUser(n.children) }))
|
||||
}
|
||||
|
||||
// Mig 27: ưu tiên displayLabel admin custom, fallback label gốc.
|
||||
function effectiveLabel(n: { label: string; displayLabel?: string | null }): string {
|
||||
return (n.displayLabel && n.displayLabel.trim()) || n.label
|
||||
}
|
||||
|
||||
// Accordion state cho groups Ct_<Code> (7 HĐ) + Pe_<Code> (2 phiếu) — mỗi
|
||||
// family mutex độc lập. Auto-expand theo URL `?type=X` (Ct_ dùng route
|
||||
// /contracts|/my-contracts|/inbox, Pe_ dùng /purchase-evaluations). Group
|
||||
@ -174,7 +182,7 @@ function MenuGroup({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
>
|
||||
<span className="flex items-center gap-2">
|
||||
<Icon className="h-4 w-4" />
|
||||
{node.label}
|
||||
{effectiveLabel(node)}
|
||||
</span>
|
||||
<ChevronDown className={cn('h-3.5 w-3.5 text-slate-400 transition', !open && '-rotate-90')} />
|
||||
</button>
|
||||
@ -236,7 +244,7 @@ function MenuLeaf({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
)}
|
||||
>
|
||||
<Icon className={cn(isDeep ? 'h-3.5 w-3.5' : 'h-4 w-4')} />
|
||||
{node.label}
|
||||
{effectiveLabel(node)}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
@ -244,7 +252,7 @@ function MenuLeaf({ node, depth }: { node: MenuNode; depth: number }) {
|
||||
// Static entries prepended to the dynamic menu tree — these are user-app
|
||||
// specific (inbox + quick create) not backed by MenuItems DB rows.
|
||||
const USER_FIXED_TOP: MenuNode[] = [
|
||||
{ key: '__inbox', label: 'Hộp thư', parentKey: null, order: 0, icon: 'Inbox', canRead: true, canCreate: true, canUpdate: true, canDelete: true, children: [] },
|
||||
{ key: '__inbox', label: 'Hộp thư', parentKey: null, order: 0, icon: 'Inbox', canRead: true, canCreate: true, canUpdate: true, canDelete: true, isVisible: true, displayLabel: null, children: [] },
|
||||
]
|
||||
|
||||
function staticResolvePath(key: string): string | null {
|
||||
@ -268,7 +276,7 @@ function StaticLeaf({ node }: { node: MenuNode }) {
|
||||
}
|
||||
>
|
||||
<Icon className="h-4 w-4" />
|
||||
{node.label}
|
||||
{effectiveLabel(node)}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
|
||||
@ -8,6 +8,8 @@ export type MenuNode = {
|
||||
canCreate: boolean
|
||||
canUpdate: boolean
|
||||
canDelete: boolean
|
||||
isVisible: boolean
|
||||
displayLabel: string | null
|
||||
children: MenuNode[]
|
||||
}
|
||||
|
||||
@ -17,6 +19,8 @@ export type MenuItem = {
|
||||
parentKey: string | null
|
||||
order: number
|
||||
icon: string | null
|
||||
isVisible: boolean
|
||||
displayLabel: string | null
|
||||
}
|
||||
|
||||
export type Role = {
|
||||
|
||||
Reference in New Issue
Block a user