Files
fintrack/components/dashboard/sidebar.tsx

222 lines
6.7 KiB
TypeScript

"use client";
import Link from "next/link";
import { usePathname, useRouter } from "next/navigation";
import { signOut } from "next-auth/react";
import { cn } from "@/lib/utils";
import { Button } from "@/components/ui/button";
import {
LayoutDashboard,
Wallet,
Tags,
BarChart3,
Upload,
ChevronLeft,
ChevronRight,
Settings,
Wand2,
LogOut,
} from "lucide-react";
import { toast } from "sonner";
import { Sheet, SheetContent, SheetTitle } from "@/components/ui/sheet";
import { useIsMobile } from "@/hooks/use-mobile";
import { useLocalStorage } from "@/hooks/use-local-storage";
const navItems = [
{ href: "/", label: "Tableau de bord", icon: LayoutDashboard },
{ href: "/accounts", label: "Comptes", icon: Wallet },
{ href: "/transactions", label: "Transactions", icon: Upload },
{ href: "/categories", label: "Catégories", icon: Tags },
{ href: "/rules", label: "Règles", icon: Wand2 },
{ href: "/statistics", label: "Statistiques", icon: BarChart3 },
];
interface SidebarContentProps {
collapsed?: boolean;
onNavigate?: () => void;
}
function SidebarContent({
collapsed = false,
onNavigate,
showHeader = false,
}: SidebarContentProps & { showHeader?: boolean }) {
const pathname = usePathname();
const router = useRouter();
const handleSignOut = async () => {
try {
await signOut({ redirect: false });
toast.success("Déconnexion réussie");
router.push("/login");
router.refresh();
} catch (error) {
console.error("Error signing out:", error);
toast.error("Erreur lors de la déconnexion");
}
};
const handleLinkClick = () => {
onNavigate?.();
};
return (
<>
{showHeader && (
<div className="flex items-center justify-between p-5 border-b border-border/50">
{!collapsed && (
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-xl bg-gradient-to-br from-primary to-primary/80 flex items-center justify-center shadow-lg shadow-primary/20">
<Wallet className="w-5 h-5 text-primary-foreground" />
</div>
<span className="font-bold text-lg text-foreground tracking-tight">
FinTrack
</span>
</div>
)}
</div>
)}
<nav className={cn("flex-1 space-y-2", collapsed ? "p-2" : "p-4")}>
{navItems.map((item) => {
const isActive = pathname === item.href;
return (
<Link key={item.href} href={item.href} onClick={handleLinkClick}>
<Button
variant={isActive ? "secondary" : "ghost"}
className={cn(
"w-full justify-start gap-4 h-12 rounded-2xl",
isActive &&
"bg-gradient-to-r from-primary/15 via-primary/10 to-primary/5 border-2 border-primary/30 shadow-lg shadow-primary/10 backdrop-blur-sm",
collapsed && "justify-center px-2 w-12 mx-auto"
)}
>
<item.icon
className={cn("w-5 h-5 shrink-0", isActive && "text-primary")}
/>
{!collapsed && (
<span
className={cn(
"font-semibold text-sm",
isActive && "text-primary font-bold"
)}
>
{item.label}
</span>
)}
</Button>
</Link>
);
})}
</nav>
<div
className={cn(
"border-t border-border/30 space-y-2",
collapsed ? "p-2" : "p-4"
)}
>
<Link href="/settings" onClick={handleLinkClick}>
<Button
variant="ghost"
className={cn(
"w-full justify-start gap-4 h-12 rounded-2xl",
collapsed && "justify-center px-2 w-12 mx-auto"
)}
>
<Settings className="w-5 h-5 shrink-0" />
{!collapsed && (
<span className="font-semibold text-sm">Paramètres</span>
)}
</Button>
</Link>
<Button
variant="ghost"
onClick={handleSignOut}
className={cn(
"w-full justify-start gap-4 h-12 rounded-2xl",
"text-destructive",
collapsed && "justify-center px-2 w-12 mx-auto"
)}
>
<LogOut className="w-5 h-5 shrink-0" />
{!collapsed && (
<span className="font-semibold text-sm">Déconnexion</span>
)}
</Button>
</div>
</>
);
}
interface SidebarProps {
open?: boolean;
onOpenChange?: (open: boolean) => void;
}
export function Sidebar({ open, onOpenChange }: SidebarProps) {
const [collapsed, setCollapsed] = useLocalStorage("sidebar-collapsed", false);
const isMobile = useIsMobile();
if (isMobile) {
return (
<Sheet open={open} onOpenChange={onOpenChange}>
<SheetContent
side="left"
className="w-64 p-0 bg-sidebar text-sidebar-foreground border-sidebar-border"
>
<SheetTitle className="sr-only">Navigation</SheetTitle>
<div className="flex flex-col h-full">
<SidebarContent
showHeader
onNavigate={() => onOpenChange?.(false)}
/>
</div>
</SheetContent>
</Sheet>
);
}
return (
<aside
className={cn(
"hidden md:flex flex-col h-screen bg-sidebar text-sidebar-foreground border-r border-sidebar-border",
"backdrop-blur-xl",
collapsed ? "w-16" : "w-64"
)}
>
<div
className={cn(
"flex items-center border-b border-border/30",
collapsed ? "justify-center p-4" : "justify-between p-6"
)}
>
{!collapsed && (
<div className="flex items-center gap-3">
<div className="w-12 h-12 rounded-2xl bg-gradient-to-br from-primary via-primary/90 to-primary/80 flex items-center justify-center shadow-xl shadow-primary/30 backdrop-blur-sm">
<Wallet className="w-6 h-6 text-primary-foreground" />
</div>
<span className="font-black text-xl text-foreground tracking-tight">
FinTrack
</span>
</div>
)}
<Button
variant="ghost"
size="icon"
onClick={() => setCollapsed(!collapsed)}
className={cn("rounded-xl", collapsed ? "" : "ml-auto")}
>
{collapsed ? (
<ChevronRight className="w-5 h-5" />
) : (
<ChevronLeft className="w-5 h-5" />
)}
</Button>
</div>
<SidebarContent collapsed={collapsed} showHeader={false} />
</aside>
);
}