- Migrate from React CRA to Next.js 15 with modern architecture - Implement comprehensive shadcn/ui component library - Create complete dashboard system with layouts and navigation - Build authentication pages (login, register) with proper forms - Implement vote management pages (active, upcoming, history, archives) - Add user profile management with security settings - Configure Tailwind CSS with custom dark theme (accent: #e8704b) - Setup TypeScript with strict type checking - Backup old React-based frontend to .backups/frontend-old - All pages compile successfully and build passes linting Pages created: - Home page with hero section and features - Authentication (login/register) - Dashboard with stats and vote cards - Vote management (active, upcoming, history, archives) - User profile with form validation 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
105 lines
3.7 KiB
TypeScript
105 lines
3.7 KiB
TypeScript
"use client"
|
||
|
||
import Link from "next/link"
|
||
import { Button } from "@/components/ui/button"
|
||
import { Menu, LogOut, User as UserIcon } from "lucide-react"
|
||
import { useState } from "react"
|
||
|
||
export default function DashboardLayout({
|
||
children,
|
||
}: {
|
||
children: React.ReactNode
|
||
}) {
|
||
const [sidebarOpen, setSidebarOpen] = useState(false)
|
||
|
||
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/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 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">
|
||
<span className="text-sm text-muted-foreground">
|
||
Bienvenue, Utilisateur
|
||
</span>
|
||
</div>
|
||
</div>
|
||
</header>
|
||
|
||
{/* Content Area */}
|
||
<main className="flex-1 overflow-auto p-6 max-w-7xl w-full mx-auto">
|
||
{children}
|
||
</main>
|
||
</div>
|
||
|
||
{/* Mobile Overlay */}
|
||
{sidebarOpen && (
|
||
<div
|
||
className="fixed inset-0 bg-black/50 z-30 lg:hidden"
|
||
onClick={() => setSidebarOpen(false)}
|
||
/>
|
||
)}
|
||
</div>
|
||
)
|
||
}
|