282 lines
10 KiB
TypeScript
282 lines
10 KiB
TypeScript
"use client"
|
|
|
|
import { useState } from "react"
|
|
import Link from "next/link"
|
|
import { usePathname } from "next/navigation"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Sheet, SheetContent, SheetTrigger } from "@/components/ui/sheet"
|
|
import {
|
|
Home,
|
|
Settings,
|
|
Users,
|
|
Calendar,
|
|
BarChart3,
|
|
Vote,
|
|
LogOut,
|
|
Menu,
|
|
ChevronDown
|
|
} from "lucide-react"
|
|
import { useAuth } from "@/hooks/use-auth"
|
|
import {
|
|
DropdownMenu,
|
|
DropdownMenuContent,
|
|
DropdownMenuItem,
|
|
DropdownMenuSeparator,
|
|
DropdownMenuTrigger,
|
|
} from "@/components/ui/dropdown-menu"
|
|
|
|
interface DashboardHeaderProps {
|
|
title?: string
|
|
showStats?: boolean
|
|
stats?: {
|
|
totalEvents: number
|
|
activeEvents: number
|
|
}
|
|
}
|
|
|
|
export function DashboardHeader({ title = "E-Voting Platform", showStats = false, stats }: DashboardHeaderProps) {
|
|
const { user, isSuperAdmin, logout } = useAuth()
|
|
const pathname = usePathname()
|
|
const [isMobileMenuOpen, setIsMobileMenuOpen] = useState(false)
|
|
|
|
const navigationItems = [
|
|
{
|
|
name: "Home",
|
|
href: "/",
|
|
icon: Home,
|
|
show: true
|
|
},
|
|
{
|
|
name: "Admin Dashboard",
|
|
href: "/admin",
|
|
icon: Settings,
|
|
show: isSuperAdmin
|
|
},
|
|
{
|
|
name: "Events",
|
|
href: "/admin/events",
|
|
icon: Calendar,
|
|
show: isSuperAdmin
|
|
},
|
|
{
|
|
name: "Members",
|
|
href: "/admin/members",
|
|
icon: Users,
|
|
show: isSuperAdmin
|
|
},
|
|
{
|
|
name: "Results",
|
|
href: "/results",
|
|
icon: BarChart3,
|
|
show: isSuperAdmin
|
|
}
|
|
]
|
|
|
|
const isActive = (href: string) => {
|
|
if (href === "/") {
|
|
return pathname === "/"
|
|
}
|
|
return pathname.startsWith(href)
|
|
}
|
|
|
|
const NavItems = ({ mobile = false }) => (
|
|
<>
|
|
{navigationItems
|
|
.filter(item => item.show)
|
|
.map((item) => {
|
|
const Icon = item.icon
|
|
const active = isActive(item.href)
|
|
|
|
return (
|
|
<Link
|
|
key={item.name}
|
|
href={item.href}
|
|
className={`flex items-center gap-3 px-3 py-2 text-sm font-medium rounded-md transition-colors ${
|
|
active
|
|
? "bg-blue-100 text-blue-700"
|
|
: "text-gray-600 hover:text-gray-900 hover:bg-gray-100"
|
|
} ${mobile ? "w-full" : ""}`}
|
|
onClick={() => mobile && setIsMobileMenuOpen(false)}
|
|
>
|
|
<Icon className="h-4 w-4" />
|
|
{item.name}
|
|
</Link>
|
|
)
|
|
})}
|
|
</>
|
|
)
|
|
|
|
return (
|
|
<header className="sticky top-0 z-50 w-full border-b bg-white shadow-sm">
|
|
<div className="container mx-auto px-4 sm:px-6 lg:px-8">
|
|
<div className="flex h-16 items-center justify-between">
|
|
{/* Left side - Logo and Navigation */}
|
|
<div className="flex items-center gap-6">
|
|
{/* Logo */}
|
|
<Link href="/" className="flex items-center gap-3">
|
|
<img
|
|
src="/images/meti-logo.png"
|
|
alt="METI - New & Renewable Energy"
|
|
className="h-8 w-auto"
|
|
/>
|
|
<div className="hidden sm:block">
|
|
<div className="flex items-center gap-2">
|
|
<span className="text-lg font-bold text-gray-900">{title}</span>
|
|
<div className="w-1.5 h-1.5 bg-blue-600 rounded-full"></div>
|
|
</div>
|
|
</div>
|
|
</Link>
|
|
|
|
{/* Desktop Navigation */}
|
|
<nav className="hidden lg:flex items-center gap-1">
|
|
<NavItems />
|
|
</nav>
|
|
</div>
|
|
|
|
{/* Right side - Stats, User Menu, Mobile Menu */}
|
|
<div className="flex items-center gap-4">
|
|
{/* Stats (Desktop) */}
|
|
{showStats && stats && (
|
|
<div className="hidden md:flex items-center gap-4 text-sm">
|
|
<div className="flex items-center gap-2">
|
|
<Vote className="h-4 w-4 text-blue-600" />
|
|
<span className="text-gray-600">{stats.totalEvents} Events</span>
|
|
</div>
|
|
<div className="w-px h-4 bg-gray-300"></div>
|
|
<div className="flex items-center gap-2">
|
|
<div className="w-2 h-2 bg-green-500 rounded-full animate-pulse"></div>
|
|
<span className="text-gray-600">{stats.activeEvents} Active</span>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* User Menu */}
|
|
<DropdownMenu>
|
|
<DropdownMenuTrigger asChild>
|
|
<Button variant="ghost" className="flex items-center gap-2 h-9 px-3">
|
|
<div className="w-7 h-7 bg-blue-100 rounded-full flex items-center justify-center">
|
|
<span className="text-xs font-medium text-blue-700">
|
|
{user?.name?.[0] || user?.username?.[0] || "U"}
|
|
</span>
|
|
</div>
|
|
<div className="hidden sm:block text-left">
|
|
<div className="text-sm font-medium text-gray-900">
|
|
{user?.name || user?.username || "User"}
|
|
</div>
|
|
<div className="text-xs text-gray-500">
|
|
{isSuperAdmin ? "Super Admin" : "Member"}
|
|
</div>
|
|
</div>
|
|
<ChevronDown className="h-4 w-4 text-gray-500" />
|
|
</Button>
|
|
</DropdownMenuTrigger>
|
|
<DropdownMenuContent align="end" className="w-56">
|
|
<div className="flex items-center gap-2 p-2">
|
|
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
|
<span className="text-sm font-medium text-blue-700">
|
|
{user?.name?.[0] || user?.username?.[0] || "U"}
|
|
</span>
|
|
</div>
|
|
<div className="flex flex-col">
|
|
<span className="text-sm font-medium">{user?.name || user?.username}</span>
|
|
<span className="text-xs text-gray-500">{user?.email}</span>
|
|
</div>
|
|
</div>
|
|
<DropdownMenuSeparator />
|
|
{isSuperAdmin && (
|
|
<>
|
|
<DropdownMenuItem asChild>
|
|
<Link href="/admin" className="flex items-center gap-2">
|
|
<Settings className="h-4 w-4" />
|
|
Admin Dashboard
|
|
</Link>
|
|
</DropdownMenuItem>
|
|
<DropdownMenuSeparator />
|
|
</>
|
|
)}
|
|
<DropdownMenuItem onClick={logout} className="text-red-600 focus:text-red-600">
|
|
<LogOut className="h-4 w-4 mr-2" />
|
|
Logout
|
|
</DropdownMenuItem>
|
|
</DropdownMenuContent>
|
|
</DropdownMenu>
|
|
|
|
{/* Mobile Menu */}
|
|
<Sheet open={isMobileMenuOpen} onOpenChange={setIsMobileMenuOpen}>
|
|
<SheetTrigger asChild>
|
|
<Button variant="ghost" size="sm" className="lg:hidden">
|
|
<Menu className="h-5 w-5" />
|
|
</Button>
|
|
</SheetTrigger>
|
|
<SheetContent side="left" className="w-72">
|
|
<div className="flex flex-col h-full">
|
|
{/* Header */}
|
|
<div className="flex items-center gap-3 pb-4 border-b">
|
|
<img
|
|
src="/images/meti-logo.png"
|
|
alt="METI"
|
|
className="h-8 w-auto"
|
|
/>
|
|
<div>
|
|
<div className="font-bold text-gray-900">E-Voting Platform</div>
|
|
<div className="text-xs text-gray-500">METI</div>
|
|
</div>
|
|
</div>
|
|
|
|
{/* Stats (Mobile) */}
|
|
{showStats && stats && (
|
|
<div className="py-4 border-b">
|
|
<div className="grid grid-cols-2 gap-4 text-sm">
|
|
<div className="text-center">
|
|
<div className="text-lg font-bold text-blue-600">{stats.totalEvents}</div>
|
|
<div className="text-gray-600">Events</div>
|
|
</div>
|
|
<div className="text-center">
|
|
<div className="text-lg font-bold text-green-600">{stats.activeEvents}</div>
|
|
<div className="text-gray-600">Active</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Navigation */}
|
|
<nav className="flex flex-col gap-1 py-4 flex-1">
|
|
<NavItems mobile />
|
|
</nav>
|
|
|
|
{/* User Info */}
|
|
<div className="border-t pt-4">
|
|
<div className="flex items-center gap-3 mb-3">
|
|
<div className="w-8 h-8 bg-blue-100 rounded-full flex items-center justify-center">
|
|
<span className="text-sm font-medium text-blue-700">
|
|
{user?.name?.[0] || user?.username?.[0] || "U"}
|
|
</span>
|
|
</div>
|
|
<div>
|
|
<div className="text-sm font-medium text-gray-900">
|
|
{user?.name || user?.username}
|
|
</div>
|
|
<div className="text-xs text-gray-500">
|
|
{isSuperAdmin ? "Super Admin" : "Member"}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<Button
|
|
variant="outline"
|
|
size="sm"
|
|
onClick={logout}
|
|
className="w-full justify-start text-red-600 hover:text-red-700"
|
|
>
|
|
<LogOut className="h-4 w-4 mr-2" />
|
|
Logout
|
|
</Button>
|
|
</div>
|
|
</div>
|
|
</SheetContent>
|
|
</Sheet>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</header>
|
|
)
|
|
} |