feat: refactor navigation structure in AppSidebar to use permissions for dynamic item rendering

This commit is contained in:
Yoga Pangestu 2026-08-31 16:34:31 +07:00
parent 6834d47e2b
commit 4350f7d9ea

View File

@ -64,63 +64,65 @@ const STAFF_ROLES = ['developer', 'staff-admin', 'staff-keuangan', 'kaprodi'];
function buildNavMain({ function buildNavMain({
isMahasiswa, isMahasiswa,
isDosen, isDosen,
canViewLogs, permissions,
canViewRoles,
}: { }: {
isMahasiswa: boolean; isMahasiswa: boolean;
isDosen: boolean; isDosen: boolean;
canViewLogs: boolean; permissions: string[];
canViewRoles: boolean;
}): (NavGroup | NavItem)[] { }): (NavGroup | NavItem)[] {
return [ const can = (permission: string) => permissions.includes(permission);
{
name: 'Dasbor', const penggunaItems: NavItem[] = [
url: '#', ...(can('view-administrators')
icon: IconGridDots, ? [
},
{
label: 'Pengguna',
items: [
{ {
name: 'Administrator', name: 'Administrator',
url: administratorsRoute.url(), url: administratorsRoute.url(),
icon: User, icon: User,
}, },
{ ]
name: 'Dosen', : []),
url: lecturersRoute.url(), ...(can('view-lecturers')
icon: Users, ? [{ name: 'Dosen', url: lecturersRoute.url(), icon: Users }]
}, : []),
...(can('view-students')
? [
{ {
name: 'Mahasiswa', name: 'Mahasiswa',
url: studentsRoute.url(), url: studentsRoute.url(),
icon: GraduationCap, icon: GraduationCap,
}, },
], ]
}, : []),
{ ];
label: 'Master',
items: [ const masterItems: NavItem[] = [
...(can('view-academic-terms')
? [
{ {
name: 'Periode Akademik', name: 'Periode Akademik',
url: academicTerm.url(), url: academicTerm.url(),
icon: Calendar, icon: Calendar,
}, },
]
: []),
...(can('view-departments')
? [
{ {
name: 'Jurusan', name: 'Jurusan',
url: departmentsRoute.url(), url: departmentsRoute.url(),
icon: Building2, icon: Building2,
}, },
{ ]
name: 'Mata Kuliah', : []),
url: coursesRoute.url(), ...(can('view-courses')
icon: BookOpen, ? [{ name: 'Mata Kuliah', url: coursesRoute.url(), icon: BookOpen }]
}, : []),
], ];
},
{ const kelolaItems: NavItem[] = [
label: 'Kelola', ...(isMahasiswa || can('view-course-registrations')
items: [ ? [
{ {
name: 'Registrasi KRS', name: 'Registrasi KRS',
url: isMahasiswa url: isMahasiswa
@ -128,56 +130,76 @@ function buildNavMain({
: courseRegistrationsRoute.url(), : courseRegistrationsRoute.url(),
icon: FileCheck2, icon: FileCheck2,
}, },
]
: []),
...(can('view-course-classes')
? [
{ {
name: 'Kelas Mata Kuliah', name: 'Kelas Mata Kuliah',
url: courseClassesRoute.url(), url: courseClassesRoute.url(),
icon: School, icon: School,
}, },
]
: []),
...(can('view-announcements')
? [
{ {
name: 'Pengumuman', name: 'Pengumuman',
url: announcementsRoute.url(), url: announcementsRoute.url(),
icon: Megaphone, icon: Megaphone,
}, },
], ]
}, : []),
{ ];
label: 'Kelas',
items: [ const kelasItems: NavItem[] = [
{ ...(can('view-materials')
name: 'Materi', ? [{ name: 'Materi', url: materialsRoute.url(), icon: FileText }]
url: materialsRoute.url(), : []),
icon: FileText, ...(can('view-assignments')
}, ? [
{ {
name: 'Tugas', name: 'Tugas',
url: assignmentsRoute.url(), url: assignmentsRoute.url(),
icon: ClipboardList, icon: ClipboardList,
}, },
]
: []),
...(can('view-schedules')
? [
{ {
name: 'Jadwal', name: 'Jadwal',
url: schedulesRoute.url(), url: schedulesRoute.url(),
icon: CalendarClock, icon: CalendarClock,
}, },
]
: []),
...(can('view-attendances')
? [
{ {
name: 'Kehadiran', name: 'Kehadiran',
url: attendancesRoute.url(), url: attendancesRoute.url(),
icon: ClipboardCheck, icon: ClipboardCheck,
}, },
], ]
}, : []),
{ ];
label: 'Keuangan',
items: [ const keuanganItems: NavItem[] = [
...(can('view-tuition-invoices')
? [
{ {
name: 'Tagihan', name: 'Tagihan',
url: tuitionInvoicesRoute.url(), url: tuitionInvoicesRoute.url(),
icon: Receipt, icon: Receipt,
}, },
], ]
}, : []),
{ ];
label: 'Layanan',
items: [ const layananItems: NavItem[] = [
...(isMahasiswa || can('view-letter-requests')
? [
{ {
name: 'Surat Permohonan', name: 'Surat Permohonan',
url: isMahasiswa url: isMahasiswa
@ -185,6 +207,10 @@ function buildNavMain({
: letterRequestsRoute.url(), : letterRequestsRoute.url(),
icon: Mail, icon: Mail,
}, },
]
: []),
...(isDosen || can('view-academic-advising-logs')
? [
{ {
name: 'Bimbingan Akademik', name: 'Bimbingan Akademik',
url: isDosen url: isDosen
@ -192,23 +218,15 @@ function buildNavMain({
: academicAdvisingLogsRoute.url(), : academicAdvisingLogsRoute.url(),
icon: MessageCircle, icon: MessageCircle,
}, },
],
},
...(canViewLogs || canViewRoles
? [
{
label: 'Pengembang',
items: [
...(canViewLogs
? [
{
name: 'Logs',
url: logsRoute.url(),
icon: ScrollText,
},
] ]
: []), : []),
...(canViewRoles ];
const pengembangItems: NavItem[] = [
...(can('view-logs')
? [{ name: 'Logs', url: logsRoute.url(), icon: ScrollText }]
: []),
...(can('view-roles')
? [ ? [
{ {
name: 'Role & Permission', name: 'Role & Permission',
@ -217,11 +235,45 @@ function buildNavMain({
}, },
] ]
: []), : []),
],
},
]
: []),
]; ];
const groups: (NavGroup | NavItem)[] = [
{
name: 'Dasbor',
url: '#',
icon: IconGridDots,
},
];
if (penggunaItems.length > 0) {
groups.push({ label: 'Pengguna', items: penggunaItems });
}
if (masterItems.length > 0) {
groups.push({ label: 'Master', items: masterItems });
}
if (kelolaItems.length > 0) {
groups.push({ label: 'Kelola', items: kelolaItems });
}
if (kelasItems.length > 0) {
groups.push({ label: 'Kelas', items: kelasItems });
}
if (keuanganItems.length > 0) {
groups.push({ label: 'Keuangan', items: keuanganItems });
}
if (layananItems.length > 0) {
groups.push({ label: 'Layanan', items: layananItems });
}
if (pengembangItems.length > 0) {
groups.push({ label: 'Pengembang', items: pengembangItems });
}
return groups;
} }
function buildNavSecondary( function buildNavSecondary(
@ -249,13 +301,11 @@ export function AppSidebar({ ...props }: React.ComponentProps<typeof Sidebar>) {
const isStaff = roleNames.some((role) => STAFF_ROLES.includes(role)); const isStaff = roleNames.some((role) => STAFF_ROLES.includes(role));
const isMahasiswa = !isStaff && roleNames.includes('mahasiswa'); const isMahasiswa = !isStaff && roleNames.includes('mahasiswa');
const isDosen = !isStaff && roleNames.includes('dosen'); const isDosen = !isStaff && roleNames.includes('dosen');
const canViewLogs = (auth?.permissions ?? []).includes('view-logs'); const permissions = auth?.permissions ?? [];
const canViewRoles = (auth?.permissions ?? []).includes('view-roles');
const navMain = buildNavMain({ const navMain = buildNavMain({
isMahasiswa, isMahasiswa,
isDosen, isDosen,
canViewLogs, permissions,
canViewRoles,
}); });
return ( return (