fix(sidebar): deduplicate menu items for multi-role users
Deploy banu-front / deploy (push) Successful in 1m50s

When a user has both student and missionary roles, common items
(dashboard, education, ticket) appeared twice. Added dedupeKey to
each item; buildMergedMenuItems now skips items whose key was already
added by an earlier role — the primary role's version always wins.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
madresebanoo
2026-09-05 16:08:59 +04:30
parent 259bc55c86
commit a967479c9f
+19 -4
View File
@@ -11,6 +11,7 @@ export const buildStudentMenuItems = (route) => [
icon: 'monitor', icon: 'monitor',
to: { name: 'student-dashboard' }, to: { name: 'student-dashboard' },
active: route.name === 'student-dashboard', active: route.name === 'student-dashboard',
dedupeKey: 'dashboard',
}, },
{ {
title: 'آموزش', title: 'آموزش',
@@ -25,6 +26,7 @@ export const buildStudentMenuItems = (route) => [
'student-courses', 'student-courses',
'student-course-details', 'student-course-details',
].includes(route.name), ].includes(route.name),
dedupeKey: 'education',
}, },
{ {
title: 'خدمات', title: 'خدمات',
@@ -55,6 +57,7 @@ export const buildStudentMenuItems = (route) => [
icon: 'envelope-open', icon: 'envelope-open',
to: { name: 'student-tickets' }, to: { name: 'student-tickets' },
active: route.name === 'student-tickets', active: route.name === 'student-tickets',
dedupeKey: 'ticket',
}, },
] ]
@@ -64,6 +67,7 @@ export const buildMissionaryMenuItems = (route) => [
icon: 'menu', icon: 'menu',
to: { name: 'missionary-dashboard' }, to: { name: 'missionary-dashboard' },
active: route.name === 'missionary-dashboard', active: route.name === 'missionary-dashboard',
dedupeKey: 'dashboard',
}, },
{ {
title: 'سامانه اعزام', title: 'سامانه اعزام',
@@ -76,12 +80,14 @@ export const buildMissionaryMenuItems = (route) => [
icon: 'graduation-cap', icon: 'graduation-cap',
to: { name: 'missionary-education' }, to: { name: 'missionary-education' },
active: route.name?.startsWith('missionary-education'), active: route.name?.startsWith('missionary-education'),
dedupeKey: 'education',
}, },
{ {
title: 'تیکت', title: 'تیکت',
icon: 'envelope-open', icon: 'envelope-open',
to: { name: 'missionary-tickets' }, to: { name: 'missionary-tickets' },
active: route.name === 'missionary-tickets', active: route.name === 'missionary-tickets',
dedupeKey: 'ticket',
}, },
] ]
@@ -91,12 +97,14 @@ export const buildCounselorMenuItems = (route) => [
icon: 'menu', icon: 'menu',
to: { name: 'counselor-dashboard' }, to: { name: 'counselor-dashboard' },
active: route.name === 'counselor-dashboard', active: route.name === 'counselor-dashboard',
dedupeKey: 'dashboard',
}, },
{ {
title: 'مشاوره', title: 'مشاوره',
icon: 'chat', icon: 'chat',
to: { name: 'counselor-consultations' }, to: { name: 'counselor-consultations' },
active: route.name === 'counselor-consultations', active: route.name === 'counselor-consultations',
dedupeKey: 'consultation',
}, },
] ]
@@ -110,15 +118,22 @@ const MENU_BUILDERS = {
* Union of every panel section unlocked by `roles`, in role order (roles * Union of every panel section unlocked by `roles`, in role order (roles
* come back from the API ordered by role id, so a user's base role's * come back from the API ordered by role id, so a user's base role's
* sections lead and each additional role's sections are appended after). * sections lead and each additional role's sections are appended after).
* Items sharing a dedupeKey across roles are merged — only the first
* occurrence is kept (the primary role's version wins).
*/ */
export const buildMergedMenuItems = (roles, route) => { export const buildMergedMenuItems = (roles, route) => {
const seen = new Set() const seenRoles = new Set()
const seenKeys = new Set()
const items = [] const items = []
for (const roleName of roles ?? []) { for (const roleName of roles ?? []) {
const builder = MENU_BUILDERS[roleName] const builder = MENU_BUILDERS[roleName]
if (!builder || seen.has(roleName)) continue if (!builder || seenRoles.has(roleName)) continue
seen.add(roleName) seenRoles.add(roleName)
items.push(...builder(route)) for (const item of builder(route)) {
if (item.dedupeKey && seenKeys.has(item.dedupeKey)) continue
if (item.dedupeKey) seenKeys.add(item.dedupeKey)
items.push(item)
}
} }
return items return items
} }