Files
Calbook/components/admin/admin-nav.tsx

143 lines
7.0 KiB
TypeScript

"use client";
import Link from "next/link";
import { usePathname } from "next/navigation";
import {
CalendarDays,
Database,
FileText,
Globe,
LayoutDashboard,
Mail,
Megaphone,
Menu,
Palette,
Settings,
Shield,
Users,
X
} from "lucide-react";
import { cn } from "@/lib/utils";
import { useState } from "react";
import { signOut } from "next-auth/react";
import { AnimatedPage } from "@/components/layout/animated-page";
const NAV_ITEMS = [
{ href: "/admin/uebersicht", label: "Dashboard", icon: LayoutDashboard },
{ href: "/admin/termine", label: "Termine", icon: CalendarDays },
{ href: "/admin/kalender", label: "Kalender", icon: Users },
{ href: "/admin/email-templates", label: "E-Mails", icon: Mail },
{ href: "/admin/branding", label: "Branding", icon: Palette },
{ href: "/admin/rechtliches", label: "Rechtliches", icon: Shield },
{ href: "/admin/instant-meeting", label: "Instant Meeting", icon: Megaphone },
{ href: "/admin/backup", label: "Backup", icon: Database },
{ href: "/admin/einstellungen", label: "Einstellungen", icon: Settings }
];
export function AdminNav() {
const pathname = usePathname();
return (
<nav className="flex-1 py-4 px-3 space-y-1">
{NAV_ITEMS.map((item) => {
const isActive = pathname === item.href || pathname.startsWith(`${item.href}/`);
const Icon = item.icon;
return (
<Link
key={item.href}
href={item.href}
className={cn(
"flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-bold transition-all",
isActive
? "bg-indigo-50 text-indigo-600"
: "text-slate-500 hover:bg-slate-50 hover:text-slate-900"
)}
>
<Icon className={cn("h-4 w-4", isActive ? "text-indigo-600" : "text-slate-400")} />
{item.label}
</Link>
);
})}
</nav>
);
}
export function AdminLayoutClientShell({ children }: { children: React.ReactNode }) {
const [mobileMenuOpen, setMobileMenuOpen] = useState(false);
const pathname = usePathname();
return (
<div className="min-h-screen bg-slate-50 font-sans">
<aside className="fixed inset-y-0 left-0 z-30 hidden w-60 flex-col border-r border-slate-200 bg-white lg:flex">
<Link href="/admin/uebersicht" className="h-16 flex items-center gap-2 px-6 border-b border-slate-100">
<div className="w-7 h-7 rounded-lg flex items-center justify-center" style={{ backgroundColor: "var(--accent)" }}>
<svg xmlns="http://www.w3.org/2000/svg" className="h-4 w-4 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</div>
<h1 className="text-lg font-black text-slate-900 tracking-tight">admin<span className="text-indigo-600">.</span></h1>
</Link>
<AdminNav />
<div className="p-3 border-t border-slate-100 space-y-2">
<Link href="/buchen" className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-bold text-slate-500 hover:text-slate-900 hover:bg-slate-50 transition-all">
<Globe className="h-4 w-4" /> Buchung
</Link>
<button onClick={() => signOut({ callbackUrl: "/anmelden" })} className="flex w-full items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-bold text-slate-400 hover:text-red-600 hover:bg-red-50 transition-all">
<FileText className="h-4 w-4" /> Abmelden
</button>
</div>
</aside>
<div className="lg:hidden sticky top-0 z-30 flex items-center gap-3 border-b border-slate-200 bg-white px-4 h-14">
<button onClick={() => setMobileMenuOpen(!mobileMenuOpen)} className="rounded-lg p-1.5 text-slate-500 hover:bg-slate-100">
{mobileMenuOpen ? <X className="h-5 w-5" /> : <Menu className="h-5 w-5" />}
</button>
<Link href="/admin/uebersicht" className="flex items-center gap-2">
<div className="w-6 h-6 rounded-md flex items-center justify-center" style={{ backgroundColor: "var(--accent)" }}>
<svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</div>
<h1 className="text-sm font-black text-slate-900">admin<span className="text-indigo-600">.</span></h1>
</Link>
<span className="text-xs font-bold text-slate-400 ml-auto">
{NAV_ITEMS.find((item) => pathname === item.href || pathname.startsWith(`${item.href}/`))?.label ?? "Admin"}
</span>
</div>
{mobileMenuOpen && (
<div className="lg:hidden fixed inset-0 z-20">
<div className="absolute inset-0 bg-slate-950/40" onClick={() => setMobileMenuOpen(false)} />
<div className="absolute left-0 top-0 bottom-0 w-64 bg-white border-r border-slate-200 shadow-2xl animate-in slide-in-from-left duration-200">
<div className="h-14 flex items-center border-b border-slate-100 px-4">
<Link href="/admin/uebersicht" className="flex items-center gap-2" onClick={() => setMobileMenuOpen(false)}>
<div className="w-6 h-6 rounded-md flex items-center justify-center" style={{ backgroundColor: "var(--accent)" }}>
<svg xmlns="http://www.w3.org/2000/svg" className="h-3.5 w-3.5 text-white" fill="none" viewBox="0 0 24 24" stroke="currentColor">
<path strokeLinecap="round" strokeLinejoin="round" strokeWidth="2" d="M8 7V3m8 4V3m-9 8h10M5 21h14a2 2 0 002-2V7a2 2 0 00-2-2H5a2 2 0 00-2 2v12a2 2 0 002 2z" />
</svg>
</div>
<h1 className="text-sm font-black text-slate-900">admin<span className="text-indigo-600">.</span></h1>
</Link>
</div>
<AdminNav />
<div className="p-3 border-t border-slate-100 space-y-2">
<Link href="/buchen" onClick={() => setMobileMenuOpen(false)} className="flex items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-bold text-slate-500 hover:text-slate-900 hover:bg-slate-50">
<Globe className="h-4 w-4" /> Buchung
</Link>
<button onClick={() => signOut({ callbackUrl: "/anmelden" })} className="flex w-full items-center gap-3 px-3 py-2.5 rounded-xl text-sm font-bold text-slate-400 hover:text-red-600 hover:bg-red-50">
<FileText className="h-4 w-4" /> Abmelden
</button>
</div>
</div>
</div>
)}
<main className="min-h-screen lg:pl-60">
<div className="w-full max-w-6xl mx-auto p-4 lg:p-8">
<AnimatedPage key={pathname}>{children}</AnimatedPage>
</div>
</main>
</div>
);
}