mirror of
https://github.com/idrainformatica/PecFlow.git
synced 2026-06-16 12:45:42 +02:00
fase 5
This commit is contained in:
@@ -0,0 +1,197 @@
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import {
|
||||
Inbox,
|
||||
Send,
|
||||
MailCheck,
|
||||
Users,
|
||||
Settings,
|
||||
LogOut,
|
||||
ChevronLeft,
|
||||
ChevronRight,
|
||||
Shield,
|
||||
} 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'
|
||||
|
||||
interface NavItem {
|
||||
to: string
|
||||
label: string
|
||||
icon: React.ElementType
|
||||
adminOnly?: boolean
|
||||
badge?: number
|
||||
}
|
||||
|
||||
const NAV_ITEMS: NavItem[] = [
|
||||
{ to: '/inbox', label: 'Posta in Arrivo', icon: Inbox },
|
||||
{ to: '/sent', label: 'Posta Inviata', icon: Send },
|
||||
{ to: '/compose', label: 'Nuova PEC', icon: MailCheck },
|
||||
]
|
||||
|
||||
const ADMIN_NAV_ITEMS: NavItem[] = [
|
||||
{ to: '/mailboxes', label: 'Caselle PEC', icon: MailCheck, adminOnly: true },
|
||||
{ to: '/users', label: 'Utenti', icon: Users, adminOnly: true },
|
||||
{ to: '/permissions', label: 'Permessi', icon: Shield, adminOnly: true },
|
||||
]
|
||||
|
||||
export function Sidebar() {
|
||||
const [collapsed, setCollapsed] = useState(false)
|
||||
const { user, isAdmin, logout } = useAuth()
|
||||
const unreadCount = useInboxStore((s) => s.unreadCount)
|
||||
|
||||
const handleLogout = async () => {
|
||||
try {
|
||||
await logout()
|
||||
toast.success('Disconnessione effettuata')
|
||||
} catch {
|
||||
toast.error('Errore durante il logout')
|
||||
}
|
||||
}
|
||||
|
||||
return (
|
||||
<aside
|
||||
className={cn(
|
||||
'flex flex-col h-screen bg-gray-900 text-white transition-all duration-300',
|
||||
collapsed ? 'w-16' : 'w-64',
|
||||
)}
|
||||
>
|
||||
{/* Logo + toggle */}
|
||||
<div className="flex items-center justify-between p-4 border-b border-gray-700">
|
||||
{!collapsed && (
|
||||
<div className="flex items-center gap-2">
|
||||
<div className="h-8 w-8 rounded-lg bg-blue-500 flex items-center justify-center text-white font-bold text-sm">
|
||||
PF
|
||||
</div>
|
||||
<span className="font-bold text-lg">PecFlow</span>
|
||||
</div>
|
||||
)}
|
||||
{collapsed && (
|
||||
<div className="mx-auto h-8 w-8 rounded-lg bg-blue-500 flex items-center justify-center text-white font-bold text-sm">
|
||||
PF
|
||||
</div>
|
||||
)}
|
||||
<button
|
||||
onClick={() => setCollapsed(!collapsed)}
|
||||
className={cn(
|
||||
'p-1 rounded hover:bg-gray-700 transition-colors text-gray-400',
|
||||
collapsed && 'mx-auto mt-0',
|
||||
)}
|
||||
>
|
||||
{collapsed ? <ChevronRight className="h-4 w-4" /> : <ChevronLeft className="h-4 w-4" />}
|
||||
</button>
|
||||
</div>
|
||||
|
||||
{/* Navigazione principale */}
|
||||
<nav className="flex-1 overflow-y-auto py-4">
|
||||
<div className="space-y-1 px-2">
|
||||
{NAV_ITEMS.map((item) => (
|
||||
<SidebarLink
|
||||
key={item.to}
|
||||
item={item}
|
||||
collapsed={collapsed}
|
||||
badge={item.to === '/inbox' ? unreadCount : undefined}
|
||||
/>
|
||||
))}
|
||||
</div>
|
||||
|
||||
{/* Sezione Admin */}
|
||||
{isAdmin && (
|
||||
<>
|
||||
<div className={cn('mt-6 px-4 mb-2', collapsed && 'hidden')}>
|
||||
<p className="text-xs font-semibold text-gray-500 uppercase tracking-wider">
|
||||
Amministrazione
|
||||
</p>
|
||||
</div>
|
||||
{!collapsed && <div className="border-t border-gray-700 mx-4 mb-2" />}
|
||||
<div className="space-y-1 px-2">
|
||||
{ADMIN_NAV_ITEMS.map((item) => (
|
||||
<SidebarLink key={item.to} item={item} collapsed={collapsed} />
|
||||
))}
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</nav>
|
||||
|
||||
{/* Profilo utente + logout */}
|
||||
<div className="border-t border-gray-700 p-3">
|
||||
{!collapsed ? (
|
||||
<div className="space-y-2">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-8 w-8 rounded-full bg-blue-600 flex items-center justify-center text-white text-sm font-medium flex-shrink-0">
|
||||
{user?.full_name?.[0]?.toUpperCase() || 'U'}
|
||||
</div>
|
||||
<div className="min-w-0">
|
||||
<p className="text-sm font-medium truncate">{user?.full_name}</p>
|
||||
<p className="text-xs text-gray-400 truncate">{user?.email}</p>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex gap-2">
|
||||
<NavLink
|
||||
to="/settings"
|
||||
className="flex-1 flex items-center gap-2 px-2 py-1.5 rounded text-xs text-gray-400 hover:text-white hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
<Settings className="h-3.5 w-3.5" />
|
||||
Impostazioni
|
||||
</NavLink>
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="flex-1 flex items-center gap-2 px-2 py-1.5 rounded text-xs text-gray-400 hover:text-red-400 hover:bg-gray-700 transition-colors"
|
||||
>
|
||||
<LogOut className="h-3.5 w-3.5" />
|
||||
Esci
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<button
|
||||
onClick={handleLogout}
|
||||
className="w-full flex justify-center p-2 rounded text-gray-400 hover:text-red-400 hover:bg-gray-700 transition-colors"
|
||||
title="Esci"
|
||||
>
|
||||
<LogOut className="h-4 w-4" />
|
||||
</button>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
)
|
||||
}
|
||||
|
||||
interface SidebarLinkProps {
|
||||
item: NavItem
|
||||
collapsed: boolean
|
||||
badge?: number
|
||||
}
|
||||
|
||||
function SidebarLink({ item, collapsed, badge }: SidebarLinkProps) {
|
||||
const Icon = item.icon
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
to={item.to}
|
||||
className={({ isActive }) =>
|
||||
cn(
|
||||
'flex items-center gap-3 px-3 py-2 rounded-lg text-sm font-medium transition-colors',
|
||||
isActive
|
||||
? 'bg-blue-600 text-white'
|
||||
: 'text-gray-300 hover:bg-gray-700 hover:text-white',
|
||||
collapsed && 'justify-center px-2',
|
||||
)
|
||||
}
|
||||
title={collapsed ? item.label : undefined}
|
||||
>
|
||||
<Icon className="h-5 w-5 flex-shrink-0" />
|
||||
{!collapsed && (
|
||||
<>
|
||||
<span className="flex-1">{item.label}</span>
|
||||
{badge !== undefined && badge > 0 && (
|
||||
<span className="inline-flex items-center justify-center h-5 w-5 rounded-full bg-blue-500 text-white text-xs font-bold">
|
||||
{badge > 99 ? '99+' : badge}
|
||||
</span>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
</NavLink>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user