MichaelEdou
feat: add Email Senders page, replace Reports with Coming Soon, remove AI references
efc415a
import { useLocation, Link, useNavigate } from 'react-router';
import { useTranslation } from 'react-i18next';
import {
LayoutDashboard,
ArrowLeftRight,
BarChart3,
Store,
ScrollText,
Settings,
LogOut,
Wallet,
Mail,
} from 'lucide-react';
import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar';
import { cn } from '@/lib/utils';
import { useAuthStore } from '@/stores/authStore';
interface NavItem {
icon: React.ElementType;
labelKey: string;
href: string;
}
const navItems: NavItem[] = [
{ icon: LayoutDashboard, labelKey: 'nav.dashboard', href: '/dashboard' },
{ icon: ArrowLeftRight, labelKey: 'nav.transactions', href: '/transactions' },
{ icon: BarChart3, labelKey: 'nav.reports', href: '/reports' },
{ icon: Store, labelKey: 'nav.branches', href: '/branches' },
{ icon: ScrollText, labelKey: 'nav.journal', href: '/journal' },
{ icon: Mail, labelKey: 'nav.emailSenders', href: '/email-senders' },
{ icon: Settings, labelKey: 'nav.settings', href: '/settings' },
];
function getInitials(name?: string | null): string {
if (!name) return '?';
const parts = name.trim().split(/\s+/);
if (parts.length >= 2) return (parts[0][0] + parts[parts.length - 1][0]).toUpperCase();
return name.slice(0, 2).toUpperCase();
}
function getDisplayName(name?: string | null): string {
if (!name) return 'Utilisateur';
const parts = name.trim().split(/\s+/);
if (parts.length >= 2) return `${parts[0]} ${parts[parts.length - 1][0]}.`;
return name;
}
const ROLE_LABELS: Record<string, string> = {
admin: 'Administrateur',
editor: 'Éditeur',
viewer: 'Lecteur',
};
export default function Sidebar() {
const { t } = useTranslation();
const location = useLocation();
const navigate = useNavigate();
const user = useAuthStore((s) => s.user);
const handleLogout = async () => {
try {
await fetch('/api/auth/logout', { method: 'POST', credentials: 'include' });
} catch {
// Ignore errors — clear cookies and redirect regardless
}
useAuthStore.getState().logout();
navigate('/login');
};
return (
<aside className="flex w-72 flex-col border-r border-border bg-card transition-all duration-300">
{/* Logo */}
<div className="flex items-center gap-3 px-6 py-8">
<div className="flex h-10 w-10 items-center justify-center rounded-xl bg-primary text-white shadow-sm">
<Wallet className="h-5 w-5" />
</div>
<div className="flex flex-col">
<h1 className="text-slate-900 text-base font-bold leading-tight tracking-tight">
ICC Interac
</h1>
<p className="text-slate-400 text-xs font-medium">Manager v2.4</p>
</div>
</div>
{/* Navigation */}
<nav className="flex flex-1 flex-col gap-1 px-4 py-4">
{navItems.map((item) => {
const isActive = location.pathname === item.href;
return (
<Link
key={item.href}
to={item.href}
className={cn(
'flex items-center gap-3 rounded-lg px-4 py-3 transition-colors',
isActive
? 'bg-blue-50 text-primary font-semibold'
: 'text-slate-500 hover:bg-slate-50 hover:text-slate-900'
)}
>
<item.icon className="h-5 w-5" />
<span className={cn('text-sm', isActive ? 'font-semibold' : 'font-medium')}>
{t(item.labelKey)}
</span>
</Link>
);
})}
</nav>
{/* Footer */}
<div className="p-4 border-t border-border/50">
<button
onClick={handleLogout}
className="flex w-full items-center gap-3 rounded-lg px-4 py-2.5 text-slate-500 hover:text-slate-900 transition-colors group mb-4"
>
<LogOut className="h-5 w-5 group-hover:text-red-500 transition-colors" />
<span className="text-sm font-medium">{t('nav.logout')}</span>
</button>
<div className="flex items-center gap-3 rounded-xl border border-border p-3 bg-white">
<Avatar className="h-10 w-10">
<AvatarImage src={user?.avatarUrl ?? ''} alt={user?.name ?? 'User'} />
<AvatarFallback className="bg-slate-100 text-sm font-semibold text-slate-600">
{getInitials(user?.name)}
</AvatarFallback>
</Avatar>
<div className="flex flex-col min-w-0">
<span className="text-sm font-semibold text-slate-900">{getDisplayName(user?.name)}</span>
<span className="text-xs text-slate-500 truncate">{user?.email ?? ROLE_LABELS[user?.role ?? ''] ?? ''}</span>
</div>
</div>
</div>
</aside>
);
}