152 lines
9.1 KiB
TypeScript
152 lines
9.1 KiB
TypeScript
import { usePage, Link } from '@inertiajs/react';
|
|
import { PageProps } from '@/types';
|
|
|
|
export function Sidebar({ theme: _theme }: { theme: string }) {
|
|
const props = usePage<PageProps>().props as any;
|
|
const { auth } = props;
|
|
const { user, permissions } = auth;
|
|
const path = window.location.pathname;
|
|
|
|
const canAccess = (ability: string | null) => {
|
|
if (!ability) return true;
|
|
return permissions.includes(ability);
|
|
};
|
|
|
|
const initials = `${user.first_name?.charAt(0) || ''}${user.last_name?.charAt(0) || ''}`.toUpperCase();
|
|
|
|
const allGroups = [
|
|
{
|
|
label: 'Governance',
|
|
items: [
|
|
{
|
|
href: '/dashboard',
|
|
label: 'Dashboard',
|
|
ability: null,
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M3 12l2-2m0 0l7-7 7 7M5 10v10a1 1 0 001 1h3m10-11l2 2m-2-2v10a1 1 0 01-1 1h-3m-6 0a1 1 0 001-1v-4a1 1 0 011-1h2a1 1 0 011 1v4a1 1 0 001 1m-6 0h6" /></svg>
|
|
},
|
|
{
|
|
href: '/notifications',
|
|
label: 'Notifications',
|
|
ability: 'role.manage',
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M15 17h5l-1.405-1.405A2.032 2.032 0 0118 14.158V11a6.002 6.002 0 00-4-5.659V5a2 2 0 10-4 0v.341C7.67 6.165 6 8.388 6 11v3.159c0 .538-.214 1.055-.595 1.436L4 17h5m6 0v1a3 3 0 11-6 0v-1m6 0H9" /></svg>
|
|
},
|
|
]
|
|
},
|
|
{
|
|
label: 'Management',
|
|
items: [
|
|
{
|
|
href: '/users',
|
|
label: 'Users',
|
|
ability: 'user.view',
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M12 4.354a4 4 0 110 5.292M15 21H3v-1a6 6 0 0112 0v1zm0 0h6v-1a6 6 0 00-9-5.197M13 7a4 4 0 11-8 0 4 4 0 018 0z" /></svg>
|
|
},
|
|
{
|
|
href: '/roles',
|
|
label: 'Roles',
|
|
ability: 'role.view',
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M9 12l2 2 4-4m5.618-4.016A11.955 11.955 0 0112 2.944a11.955 11.955 0 01-8.618 3.04A12.02 12.02 0 003 9c0 5.591 3.824 10.29 9 11.622 5.176-1.332 9-6.03 9-11.622 0-1.042-.133-2.052-.382-3.016z" /></svg>
|
|
},
|
|
{
|
|
href: '/activity-logs',
|
|
label: 'Activity Logs',
|
|
ability: 'user.view',
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path d="M12 8v4l3 3m6-3a9 9 0 11-18 0 9 9 0 0118 0z" /></svg>
|
|
},
|
|
]
|
|
},
|
|
{
|
|
label: 'Account',
|
|
items: [
|
|
{
|
|
href: '/settings',
|
|
label: 'Settings',
|
|
ability: null,
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M16 7a4 4 0 11-8 0 4 4 0 018 0zM12 14a7 7 0 00-7 7h14a7 7 0 00-7-7z" /></svg>
|
|
},
|
|
]
|
|
},
|
|
{
|
|
label: 'Systems',
|
|
adminOnly: true,
|
|
items: [
|
|
{
|
|
href: '/system-settings',
|
|
label: 'System Settings',
|
|
ability: null,
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M10.325 4.317c.426-1.756 2.924-1.756 3.35 0a1.724 1.724 0 002.573 1.066c1.543-.94 3.31.826 2.37 2.37a1.724 1.724 0 001.065 2.572c1.756.426 1.756 2.924 0 3.35a1.724 1.724 0 00-1.066 2.573c.94 1.543-.826 3.31-2.37 2.37a1.724 1.724 0 00-2.572 1.065c-.426 1.756-2.924 1.756-3.35 0a1.724 1.724 0 00-2.573-1.066c-1.543.94-3.31-.826-2.37-2.37a1.724 1.724 0 00-1.065-2.572c-1.756-.426-1.756-2.924 0-3.35a1.724 1.724 0 001.066-2.573c-.94-1.543.826-3.31 2.37-2.37.996.608 2.296.07 2.572-1.065z" /><path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" /></svg>
|
|
},
|
|
{
|
|
href: '/documentation',
|
|
label: 'Dokumentasi',
|
|
ability: null,
|
|
icon: <svg className="w-4 h-4" fill="none" viewBox="0 0 24 24" stroke="currentColor" strokeWidth={2.5}><path strokeLinecap="round" strokeLinejoin="round" d="M12 6.253v13m0-13C10.832 5.477 9.246 5 7.5 5S4.168 5.477 3 6.253v13C4.168 18.477 5.754 18 7.5 18s3.332.477 4.5 1.253m0-13C13.168 5.477 14.754 5 16.5 5c1.747 0 3.332.477 4.5 1.253v13C19.832 18.477 18.247 18 16.5 18c-1.746 0-3.332.477-4.5 1.253" /></svg>
|
|
},
|
|
]
|
|
},
|
|
];
|
|
|
|
const canManageSettings = permissions.includes('settings.manage');
|
|
|
|
return (
|
|
<aside className="w-[260px] h-screen p-4 shrink-0 relative z-20 flex flex-col anim-left">
|
|
<div className="bg-[#3D4E4B] flex-1 rounded-[2rem] flex flex-col relative">
|
|
|
|
{/* Profile Section */}
|
|
<div className="p-8 flex flex-col items-center text-center">
|
|
<div className="relative mb-4">
|
|
<div className="w-16 h-16 rounded-full border border-[#D4A017] p-1 flex items-center justify-center">
|
|
<div className="w-full h-full rounded-full bg-[#2D3A38] overflow-hidden flex items-center justify-center font-bold text-white/40 text-lg">
|
|
{user.avatar_url ? (
|
|
<img src={user.avatar_url} className="w-full h-full object-cover" />
|
|
) : initials}
|
|
</div>
|
|
</div>
|
|
<div className="absolute bottom-0 right-0 w-4 h-4 bg-[#21A59F] border-2 border-[#3D4E4B] rounded-full"></div>
|
|
</div>
|
|
<h3 className="text-white text-sm font-bold tracking-tight">{user.first_name} {user.last_name}</h3>
|
|
<p className="text-[#E3EBE8]/40 text-sm font-medium mt-0.5">{user.email}</p>
|
|
</div>
|
|
|
|
{/* Nav Section with Grouping */}
|
|
<nav className="flex-1 px-3 space-y-6 overflow-y-auto custom-scrollbar pb-10">
|
|
{allGroups.map((group) => {
|
|
if (group.adminOnly && !canManageSettings) return null;
|
|
const items = group.items.filter(i => canAccess(i.ability));
|
|
if (items.length === 0) return null;
|
|
|
|
return (
|
|
<div key={group.label} className="space-y-1.5">
|
|
<div className="px-5 text-xs font-semibold text-[#E3EBE8]/30 tracking-widest uppercase mb-2">{group.label}</div>
|
|
{items.map((item) => {
|
|
const isActive = path === item.href || path.startsWith(item.href + '/');
|
|
return (
|
|
<Link key={item.href} href={item.href}
|
|
className={`flex items-center gap-4 py-3 transition-[color,background-color,box-shadow,opacity] duration-200 relative group
|
|
${isActive
|
|
? 'text-[#3D4E4B] bg-[#E3EBE8] dark:bg-[#1A2120] dark:text-white -mr-3 pr-6 pl-5 rounded-l-[15px] w-[calc(100%+0.75rem)] shadow-sm'
|
|
: 'text-[#E3EBE8]/60 hover:text-white hover:bg-white/5 px-5 rounded-[15px]'}`}>
|
|
<span className={`shrink-0 z-10 transition-colors duration-300 ${isActive ? 'text-[#D4A017]' : 'group-hover:text-[#D4A017]'}`}>
|
|
{item.icon}
|
|
</span>
|
|
<span className="text-sm font-semibold tracking-tight z-10">{item.label}</span>
|
|
</Link>
|
|
);
|
|
})}
|
|
</div>
|
|
);
|
|
})}
|
|
</nav>
|
|
|
|
{/* Footer Section */}
|
|
<div className="p-6 pb-8">
|
|
<div className="px-4 py-3 rounded-2xl bg-white/5 border border-white/5 flex items-center justify-between group hover:bg-white/10 transition-colors cursor-default">
|
|
<span className="text-sm font-semibold tracking-tight text-[#D4A017]">biiproject kit</span>
|
|
<span className="text-sm font-bold text-white">v2</span>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
);
|
|
}
|