Changes:
- Add next-themes dependency for theme management
- Create ThemeProvider wrapper for app root layout
- Set dark mode as default theme
- Create ThemeToggle component with Sun/Moon icons
- Add theme toggle to home page navigation
- Add theme toggle to dashboard header
- App now starts in dark mode with ability to switch to light mode
Styling uses existing Tailwind dark mode variables configured in
tailwind.config.ts and globals.css. All existing components automatically
support dark theme.
🤖 Generated with Claude Code
Co-Authored-By: Claude <noreply@anthropic.com>
124 lines
4.3 KiB
TypeScript
124 lines
4.3 KiB
TypeScript
"use client"
|
||
|
||
import Link from "next/link"
|
||
import { useRouter } from "next/navigation"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Menu, LogOut, User as UserIcon } from "lucide-react"
|
||
import { useState } from "react"
|
||
import { useAuth } from "@/lib/auth-context"
|
||
import { ProtectedRoute } from "@/components/protected-route"
|
||
import { ThemeToggle } from "@/components/theme-toggle"
|
||
|
||
export default function DashboardLayout({
|
||
children,
|
||
}: {
|
||
children: React.ReactNode
|
||
}) {
|
||
const router = useRouter()
|
||
const { user, logout } = useAuth()
|
||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
||
|
||
const handleLogout = () => {
|
||
logout()
|
||
router.push("/auth/login")
|
||
}
|
||
|
||
const navItems = [
|
||
{ href: "/dashboard", label: "Tableau de Bord", icon: "📊" },
|
||
{ href: "/dashboard/votes/active", label: "Votes Actifs", icon: "🗳️" },
|
||
{ href: "/dashboard/votes/upcoming", label: "Votes à Venir", icon: "📅" },
|
||
{ href: "/dashboard/votes/history", label: "Historique", icon: "📜" },
|
||
{ href: "/dashboard/votes/archives", label: "Archives", icon: "🗂️" },
|
||
{ href: "/dashboard/blockchain", label: "Blockchain", icon: "⛓️" },
|
||
{ href: "/dashboard/profile", label: "Profil", icon: "👤" },
|
||
]
|
||
|
||
return (
|
||
<div className="min-h-screen bg-background flex">
|
||
{/* Sidebar */}
|
||
<aside
|
||
className={`fixed inset-y-0 left-0 z-40 w-64 bg-card border-r border-border transition-transform duration-300 ${
|
||
sidebarOpen ? "translate-x-0" : "-translate-x-full"
|
||
} lg:static lg:translate-x-0`}
|
||
>
|
||
<div className="flex flex-col h-full">
|
||
{/* Logo */}
|
||
<div className="flex items-center gap-2 p-6 border-b border-border">
|
||
<span className="text-2xl">🗳️</span>
|
||
<span className="font-bold text-lg text-accent">E-Voting</span>
|
||
</div>
|
||
|
||
{/* Navigation */}
|
||
<nav className="flex-1 p-4 space-y-2">
|
||
{navItems.map((item) => (
|
||
<Link
|
||
key={item.href}
|
||
href={item.href}
|
||
className="flex items-center gap-3 px-4 py-3 rounded-lg hover:bg-muted transition-colors text-foreground hover:text-accent"
|
||
onClick={() => setSidebarOpen(false)}
|
||
>
|
||
<span className="text-xl">{item.icon}</span>
|
||
<span className="text-sm font-medium">{item.label}</span>
|
||
</Link>
|
||
))}
|
||
</nav>
|
||
|
||
{/* Footer */}
|
||
<div className="p-4 border-t border-border space-y-2">
|
||
<Link href="/dashboard/profile">
|
||
<Button variant="ghost" className="w-full justify-start gap-2">
|
||
<UserIcon className="w-4 h-4" />
|
||
Mon Profil
|
||
</Button>
|
||
</Link>
|
||
<Button
|
||
onClick={handleLogout}
|
||
variant="ghost"
|
||
className="w-full justify-start gap-2 text-destructive hover:text-destructive"
|
||
>
|
||
<LogOut className="w-4 h-4" />
|
||
Déconnexion
|
||
</Button>
|
||
</div>
|
||
</div>
|
||
</aside>
|
||
|
||
{/* Main Content */}
|
||
<div className="flex-1 flex flex-col">
|
||
{/* Top Bar */}
|
||
<header className="sticky top-0 z-30 border-b border-border bg-card/50 backdrop-blur-sm">
|
||
<div className="flex items-center justify-between px-6 py-4">
|
||
<button
|
||
onClick={() => setSidebarOpen(!sidebarOpen)}
|
||
className="lg:hidden p-2 hover:bg-muted rounded-lg"
|
||
>
|
||
<Menu className="w-5 h-5" />
|
||
</button>
|
||
<div className="ml-auto flex items-center gap-4">
|
||
{user && (
|
||
<span className="text-sm text-muted-foreground">
|
||
Bienvenue, {user.first_name} {user.last_name}
|
||
</span>
|
||
)}
|
||
<ThemeToggle />
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
{/* Content Area */}
|
||
<main className="flex-1 overflow-auto p-6 max-w-7xl w-full mx-auto">
|
||
<ProtectedRoute>{children}</ProtectedRoute>
|
||
</main>
|
||
</div>
|
||
|
||
{/* Mobile Overlay */}
|
||
{sidebarOpen && (
|
||
<div
|
||
className="fixed inset-0 bg-black/50 z-30 lg:hidden"
|
||
onClick={() => setSidebarOpen(false)}
|
||
/>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|