From b41602b35c8d18643d0631e9b837f6562cdefc7d Mon Sep 17 00:00:00 2001 From: soufiane Date: Mon, 8 Dec 2025 15:55:59 +0100 Subject: [PATCH] refactor: extract DashboardSidebar component to reduce code duplication MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Create shared DashboardSidebar component in components/ui/ - Refactor employe/layout.tsx to use shared component - Refactor admin/Sidebar.tsx to use shared component - Reduces duplicated lines from ~60% to minimal 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- app/employe/layout.tsx | 123 ++++--------------- components/admin/Sidebar.tsx | 182 +++++++---------------------- components/ui/DashboardSidebar.tsx | 117 +++++++++++++++++++ 3 files changed, 184 insertions(+), 238 deletions(-) create mode 100644 components/ui/DashboardSidebar.tsx diff --git a/app/employe/layout.tsx b/app/employe/layout.tsx index bf37252..84e8e98 100644 --- a/app/employe/layout.tsx +++ b/app/employe/layout.tsx @@ -2,15 +2,28 @@ import { useAuth } from "@/contexts/AuthContext"; import { useRouter } from "next/navigation"; -import { useEffect, useState } from "react"; +import { useEffect } from "react"; import { Loading } from "@/components/ui/Loading"; import toast from "react-hot-toast"; -import { Ticket, BarChart3, Menu, X } from "lucide-react"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; -import Logo from "@/components/Logo"; +import { Ticket, BarChart3 } from "lucide-react"; +import DashboardSidebar from "@/components/ui/DashboardSidebar"; import UserDropdown from "@/components/UserDropdown"; +const navItems = [ + { + label: "Dashboard", + href: "/employe/dashboard", + icon: , + color: "green", + }, + { + label: "Validation des Tickets", + href: "/employe/verification", + icon: , + color: "emerald", + }, +]; + export default function EmployeLayout({ children, }: { @@ -18,8 +31,6 @@ export default function EmployeLayout({ }) { const { user, isAuthenticated, isLoading, logout } = useAuth(); const router = useRouter(); - const pathname = usePathname(); - const [isOpen, setIsOpen] = useState(false); useEffect(() => { if (!isLoading && !isAuthenticated) { @@ -51,102 +62,15 @@ export default function EmployeLayout({ return null; } - const navItems = [ - { - label: "Dashboard", - href: "/employe/dashboard", - icon: , - color: "green", - }, - { - label: "Validation des Tickets", - href: "/employe/verification", - icon: , - color: "emerald", - }, - ]; - - const isActive = (href: string) => pathname === href; - - const getActiveStyles = (color: string) => { - const styles: Record = { - green: "bg-gradient-to-r from-green-600 to-green-700 text-white shadow-lg shadow-green-500/30", - emerald: "bg-gradient-to-r from-emerald-600 to-teal-600 text-white shadow-lg shadow-emerald-500/30", - }; - return styles[color] || styles.green; - }; - return (
- {/* Mobile menu button */} - + - {/* Overlay for mobile */} - {isOpen && ( -
setIsOpen(false)} - /> - )} - - {/* Sidebar */} - - - {/* Main Content */}
- {/* Header */}
- {/* Page Content */}
{children}
diff --git a/components/admin/Sidebar.tsx b/components/admin/Sidebar.tsx index a3a288c..916847a 100644 --- a/components/admin/Sidebar.tsx +++ b/components/admin/Sidebar.tsx @@ -1,154 +1,60 @@ "use client"; -import Link from "next/link"; -import { usePathname } from "next/navigation"; import { LayoutDashboard, Users, Ticket, BarChart3, Gift, - Menu, - X, Trophy, } from "lucide-react"; -import { useState } from "react"; -import Logo from "@/components/Logo"; +import DashboardSidebar from "@/components/ui/DashboardSidebar"; -interface NavItem { - label: string; - href: string; - icon: React.ReactNode; - color: string; -} +const navItems = [ + { + label: "Dashboard", + href: "/admin/dashboard", + icon: , + color: "darkblue", + }, + { + label: "Utilisateurs", + href: "/admin/utilisateurs", + icon: , + color: "blue", + }, + { + label: "Tickets", + href: "/admin/tickets", + icon: , + color: "emerald", + }, + { + label: "Lots & Prix", + href: "/admin/lots", + icon: , + color: "purple", + }, + { + label: "Données Marketing", + href: "/admin/marketing-data", + icon: , + color: "indigo", + }, + { + label: "Tirages", + href: "/admin/tirages", + icon: , + color: "amber", + }, +]; export default function Sidebar() { - const pathname = usePathname(); - const [isOpen, setIsOpen] = useState(false); - - const navItems: NavItem[] = [ - { - label: "Dashboard", - href: "/admin/dashboard", - icon: , - color: "darkblue", - }, - { - label: "Utilisateurs", - href: "/admin/utilisateurs", - icon: , - color: "blue", - }, - { - label: "Tickets", - href: "/admin/tickets", - icon: , - color: "emerald", - }, - { - label: "Lots & Prix", - href: "/admin/lots", - icon: , - color: "purple", - }, - { - label: "Données Marketing", - href: "/admin/marketing-data", - icon: , - color: "indigo", - }, - { - label: "Tirages", - href: "/admin/tirages", - icon: , - color: "amber", - }, - ]; - - const isActive = (href: string) => { - return pathname === href; - }; - - const getActiveStyles = (color: string) => { - const styles: Record = { - darkblue: "bg-gradient-to-r from-[#1e3a5f] to-[#2d5a8f] text-white shadow-lg shadow-blue-900/30", - blue: "bg-gradient-to-r from-blue-600 to-indigo-600 text-white shadow-lg shadow-blue-500/30", - emerald: "bg-gradient-to-r from-emerald-600 to-teal-600 text-white shadow-lg shadow-emerald-500/30", - purple: "bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg shadow-purple-500/30", - indigo: "bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg shadow-indigo-500/30", - amber: "bg-gradient-to-r from-amber-500 to-orange-600 text-white shadow-lg shadow-amber-500/30", - }; - return styles[color] || styles.blue; - }; - return ( - <> - {/* Mobile menu button */} - - - {/* Overlay for mobile */} - {isOpen && ( -
setIsOpen(false)} - /> - )} - - {/* Sidebar */} - - + ); } diff --git a/components/ui/DashboardSidebar.tsx b/components/ui/DashboardSidebar.tsx new file mode 100644 index 0000000..b5ca5ca --- /dev/null +++ b/components/ui/DashboardSidebar.tsx @@ -0,0 +1,117 @@ +"use client"; + +import Link from "next/link"; +import { usePathname } from "next/navigation"; +import { Menu, X } from "lucide-react"; +import { useState } from "react"; +import Logo from "@/components/Logo"; + +export interface NavItem { + label: string; + href: string; + icon: React.ReactNode; + color: string; +} + +interface DashboardSidebarProps { + navItems: NavItem[]; + title: string; + subtitle: string; + colorStyles?: Record; +} + +const defaultColorStyles: Record = { + darkblue: "bg-gradient-to-r from-[#1e3a5f] to-[#2d5a8f] text-white shadow-lg shadow-blue-900/30", + blue: "bg-gradient-to-r from-blue-600 to-indigo-600 text-white shadow-lg shadow-blue-500/30", + green: "bg-gradient-to-r from-green-600 to-green-700 text-white shadow-lg shadow-green-500/30", + emerald: "bg-gradient-to-r from-emerald-600 to-teal-600 text-white shadow-lg shadow-emerald-500/30", + purple: "bg-gradient-to-r from-purple-600 to-pink-600 text-white shadow-lg shadow-purple-500/30", + indigo: "bg-gradient-to-r from-indigo-600 to-purple-600 text-white shadow-lg shadow-indigo-500/30", + amber: "bg-gradient-to-r from-amber-500 to-orange-600 text-white shadow-lg shadow-amber-500/30", +}; + +export default function DashboardSidebar({ + navItems, + title, + subtitle, + colorStyles = defaultColorStyles, +}: DashboardSidebarProps) { + const pathname = usePathname(); + const [isOpen, setIsOpen] = useState(false); + + const isActive = (href: string) => pathname === href; + + const getActiveStyles = (color: string) => { + return colorStyles[color] || colorStyles.blue || defaultColorStyles.blue; + }; + + return ( + <> + {/* Mobile menu button */} + + + {/* Overlay for mobile */} + {isOpen && ( +
setIsOpen(false)} + /> + )} + + {/* Sidebar */} + + + ); +}