File size: 4,761 Bytes
149698e
 
 
 
 
 
 
e85d815
149698e
 
 
efc415a
149698e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
e85d815
efc415a
149698e
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
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>
  );
}