136 lines
5.6 KiB
TypeScript
136 lines
5.6 KiB
TypeScript
import React, { useMemo } from 'react';
|
|
import { Link, useLocation, useNavigate } from 'react-router-dom';
|
|
import { LogOut, Menu, X } from 'lucide-react';
|
|
import { Button } from '../../common/components/ui/button';
|
|
import { NAV_CONFIG } from "../../common/config/navigation";
|
|
|
|
interface SidebarProps {
|
|
sidebarOpen: boolean;
|
|
setSidebarOpen: (open: boolean) => void;
|
|
user: {
|
|
name?: string;
|
|
role?: string;
|
|
} | null;
|
|
onLogout: () => void;
|
|
}
|
|
|
|
const Sidebar: React.FC<SidebarProps> = ({
|
|
sidebarOpen,
|
|
setSidebarOpen,
|
|
user,
|
|
onLogout
|
|
}) => {
|
|
const location = useLocation();
|
|
const navigate = useNavigate();
|
|
|
|
/**
|
|
* Handle logout with navigation
|
|
* Ensures user is redirected to login page after logout
|
|
*/
|
|
const handleLogoutClick = async () => {
|
|
onLogout();
|
|
// Small delay to allow logout to complete before navigation
|
|
setTimeout(() => {
|
|
navigate('/login', { replace: true });
|
|
}, 100);
|
|
};
|
|
|
|
// Filter navigation based on user role
|
|
const filteredNav = useMemo(() => {
|
|
const userRoleRaw = (user?.role || 'Client') as string;
|
|
const userRole = userRoleRaw.toLowerCase();
|
|
|
|
return NAV_CONFIG.map(group => {
|
|
const visibleItems = group.items.filter(item =>
|
|
item.allowedRoles.some(r => r.toLowerCase() === userRole)
|
|
);
|
|
return {
|
|
...group,
|
|
items: visibleItems
|
|
};
|
|
}).filter(group => group.items.length > 0);
|
|
}, [user?.role]);
|
|
|
|
return (
|
|
<aside
|
|
className={`${sidebarOpen ? 'w-64' : 'w-20'
|
|
} bg-white border-r border-slate-200 transition-all duration-300 flex flex-col z-20`}
|
|
>
|
|
<div className="h-16 flex items-center justify-between px-4 border-b border-slate-100 flex-shrink-0">
|
|
{sidebarOpen ? (
|
|
<span className="text-xl font-bold text-primary">KROW</span>
|
|
) : (
|
|
<span className="text-xl font-bold text-primary mx-auto">K</span>
|
|
)}
|
|
<button
|
|
onClick={() => setSidebarOpen(!sidebarOpen)}
|
|
className="p-1 rounded-md hover:bg-slate-100 text-secondary-text"
|
|
>
|
|
{sidebarOpen ? <X size={20} /> : <Menu size={20} />}
|
|
</button>
|
|
</div>
|
|
|
|
<nav className="flex-1 py-6 px-3 space-y-6 overflow-y-auto">
|
|
{filteredNav.map((group) => (
|
|
<div key={group.title}>
|
|
{sidebarOpen && (
|
|
<h3 className="px-3 mb-2 text-xs font-semibold text-muted-text uppercase tracking-wider">
|
|
{group.title}
|
|
</h3>
|
|
)}
|
|
<div className="space-y-1">
|
|
{group.items.map((item) => (
|
|
<Link
|
|
key={item.path}
|
|
to={item.path}
|
|
className={`flex items-center px-3 py-2.5 rounded-lg transition-colors group ${location.pathname.startsWith(item.path)
|
|
? 'bg-primary/10 text-primary font-medium'
|
|
: 'text-secondary-text hover:bg-slate-50 hover:text-primary-text'
|
|
}`}
|
|
title={!sidebarOpen ? item.label : undefined}
|
|
>
|
|
<item.icon
|
|
size={20}
|
|
className={`flex-shrink-0 ${location.pathname.startsWith(item.path)
|
|
? 'text-primary'
|
|
: 'text-muted-text group-hover:text-secondary-text'
|
|
}`}
|
|
/>
|
|
{sidebarOpen && <span className="ml-3 truncate">{item.label}</span>}
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</nav>
|
|
|
|
<div className="p-4 border-t border-slate-100 flex-shrink-0">
|
|
<div className={`flex items-center ${sidebarOpen ? 'justify-between' : 'justify-center'}`}>
|
|
{sidebarOpen && (
|
|
<div className="flex items-center overflow-hidden">
|
|
<div className="w-8 h-8 rounded-full bg-primary/10 flex items-center justify-center text-primary font-bold text-xs flex-shrink-0">
|
|
{user?.name?.charAt(0) || 'U'}
|
|
</div>
|
|
<div className="ml-3 overflow-hidden">
|
|
<p className="text-sm font-medium text-primary-text truncate w-32">{user?.name}</p>
|
|
<p className="text-xs text-secondary-text truncate">{user?.role}</p>
|
|
</div>
|
|
</div>
|
|
)}
|
|
<Button
|
|
variant="ghost"
|
|
size="icon"
|
|
onClick={handleLogoutClick}
|
|
title="Logout"
|
|
className="text-secondary-text hover:text-destructive hover:bg-destructive/10"
|
|
>
|
|
<LogOut size={18} />
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</aside>
|
|
);
|
|
};
|
|
|
|
export default Sidebar;
|