/** * Sidebar – navigazione principale di PEChub. * * Struttura visiva (sidebar espansa): * ┌────────────────────────────────┐ * │ [PF] PEChub [◀] │ * ├────────────────────────────────┤ * │ TUTTE LE CASELLE │ * │ 📥 Posta in Arrivo [badge] │ * │ 📤 Posta Inviata │ * │ ⭐ Preferiti │ * │ 📦 Archiviati │ * ├────────────────────────────────┤ * │ LE TUE CASELLE │ * │ ● gmgspa@pec.it [▼] │ * │ ├ 📥 In Arrivo │ * │ ├ 📤 Inviata │ * │ ├ ⭐ Preferiti │ * │ └ 📦 Archiviati │ * ├────────────────────────────────┤ * │ ✉ Nuova PEC │ * ├────────────────────────────────┤ * │ AMMINISTRAZIONE │ * │ 📬 Caselle PEC │ * │ 👥 Utenti │ * │ 🛡 Permessi │ * ├────────────────────────────────┤ * │ [avatar] Nome utente │ * │ Impostazioni | Esci │ * └────────────────────────────────┘ * * Quando collassata (w-16) mostra solo icone/avatar con tooltip. */ import { NavLink } from 'react-router-dom' import { Inbox, Send, MailCheck, Users, Settings, LogOut, ChevronLeft, ChevronRight, Shield, ChevronDown, Filter, Bell, Star, Archive, Building2, } from 'lucide-react' import { cn } from '@/lib/utils' import { useAuth } from '@/hooks/useAuth' import { useInboxStore } from '@/store/inbox.store' import { useState } from 'react' import toast from 'react-hot-toast' import { useQuery } from '@tanstack/react-query' import { mailboxesApi } from '@/api/mailboxes.api' import { virtualBoxesApi } from '@/api/virtual_boxes.api' import type { MailboxResponse, VirtualBoxResponse } from '@/types/api.types' // ─── Sidebar principale ─────────────────────────────────────────────────────── export function Sidebar() { const [collapsed, setCollapsed] = useState(false) /** * Set degli ID casella che l'utente ha esplicitamente chiuso. * Tutte le caselle sono espanse per default (nessuno nell'insieme). */ const [collapsedMailboxes, setCollapsedMailboxes] = useState>(new Set()) /** Set degli ID virtual box che l'utente ha esplicitamente chiuso. */ const [collapsedVboxes, setCollapsedVboxes] = useState>(new Set()) const { user, isAdmin, isSuperAdmin, logout } = useAuth() const unreadCount = useInboxStore((s) => s.unreadCount) // Le caselle PEC vengono caricate qui e condivise via React Query cache const { data: mailboxesData } = useQuery({ queryKey: ['mailboxes'], queryFn: () => mailboxesApi.list(), staleTime: 5 * 60 * 1000, }) const mailboxes = mailboxesData?.items ?? [] // Virtual Box assegnate all'utente corrente const { data: myVboxes = [] } = useQuery({ queryKey: ['virtual-boxes', 'my'], queryFn: () => virtualBoxesApi.myVirtualBoxes(), staleTime: 5 * 60 * 1000, }) const isMailboxExpanded = (id: string) => !collapsedMailboxes.has(id) const toggleMailbox = (id: string) => { setCollapsedMailboxes((prev) => { const next = new Set(prev) if (next.has(id)) { next.delete(id) } else { next.add(id) } return next }) } const isVboxExpanded = (id: string) => !collapsedVboxes.has(id) const toggleVbox = (id: string) => { setCollapsedVboxes((prev) => { const next = new Set(prev) if (next.has(id)) { next.delete(id) } else { next.add(id) } return next }) } const handleLogout = async () => { try { await logout() toast.success('Disconnessione effettuata') } catch { toast.error('Errore durante il logout') } } return ( ) } // ─── Voce di casella PEC nel sidebar ───────────────────────────────────────── interface MailboxNavItemProps { mailbox: MailboxResponse collapsed: boolean isExpanded: boolean onToggle: () => void } /** Colore del pallino di stato casella */ function statusDot(status: MailboxResponse['status']): string { switch (status) { case 'active': return 'bg-green-500' case 'paused': return 'bg-yellow-400' case 'error': return 'bg-red-500' case 'deleted': return 'bg-gray-600' default: return 'bg-gray-500' } } function MailboxNavItem({ mailbox, collapsed, isExpanded, onToggle }: MailboxNavItemProps) { const displayName = mailbox.display_name || mailbox.email_address const initial = displayName[0]?.toUpperCase() ?? '?' const dotClass = statusDot(mailbox.status) /* ── Modalità compressa: solo avatar/iniziale → link diretto all'inbox ── */ if (collapsed) { return ( cn( 'relative flex justify-center items-center w-full px-2 py-2 rounded-lg transition-colors', isActive ? 'bg-blue-600 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', ) } title={displayName} >
{initial}
) } /* ── Modalità espansa: sezione espandibile con In Arrivo, Inviata, Preferiti, Archiviati ── */ return (
{/* Intestazione casella (espandi/comprimi) */} {/* Sub-voci: In Arrivo, Inviata, Preferiti, Archiviati */} {isExpanded && (
cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-blue-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > In Arrivo cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-blue-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > Inviata cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-blue-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > Preferiti cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-blue-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > Archiviati
)}
) } // ─── Voce di Virtual Box nel sidebar ───────────────────────────────────────── interface VirtualBoxNavItemProps { vbox: VirtualBoxResponse collapsed: boolean isExpanded: boolean onToggle: () => void } function VirtualBoxNavItem({ vbox, collapsed, isExpanded, onToggle }: VirtualBoxNavItemProps) { const displayName = vbox.label || vbox.name const initial = displayName[0]?.toUpperCase() ?? '?' /* ── Modalità compressa: solo icona filtro → link diretto all'inbox ── */ if (collapsed) { return ( cn( 'relative flex justify-center items-center w-full px-2 py-2 rounded-lg transition-colors', isActive ? 'bg-purple-600 text-white' : 'text-gray-300 hover:bg-gray-700 hover:text-white', ) } title={displayName} >
{initial}
) } /* ── Modalità espansa: sezione espandibile ── */ return (
{/* Intestazione VBox (espandi/comprimi) */} {/* Sub-voci: In Arrivo, Inviata, Preferiti, Archiviati */} {isExpanded && (
cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-purple-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > In Arrivo cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-purple-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > Inviata cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-purple-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > Preferiti cn( 'flex items-center gap-2 px-2 py-1.5 rounded-md text-xs font-medium transition-colors', isActive ? 'bg-purple-600 text-white' : 'text-gray-400 hover:bg-gray-700 hover:text-white', ) } > Archiviati
)}
) }