111 lines
3.6 KiB
TypeScript
111 lines
3.6 KiB
TypeScript
"use client";
|
|
|
|
import Link from "next/link";
|
|
import { usePathname, useRouter } from "next/navigation";
|
|
import type { ReactNode } from "react";
|
|
|
|
const NAV = [
|
|
{ href: "/", label: "Gutscheine" },
|
|
{ href: "/vouchers/new", label: "Neu" },
|
|
{ href: "/contacts", label: "Kontakte" },
|
|
{ href: "/settings", label: "Einstellungen" },
|
|
] as const;
|
|
|
|
function isActive(pathname: string, href: string) {
|
|
if (href === "/") return pathname === "/";
|
|
return pathname === href || pathname.startsWith(`${href}/`);
|
|
}
|
|
|
|
export default function Shell({ children }: { children: ReactNode }) {
|
|
const pathname = usePathname();
|
|
const router = useRouter();
|
|
|
|
async function logout() {
|
|
await fetch("/api/auth/logout", { method: "POST" }).catch(() => {});
|
|
router.push("/login");
|
|
router.refresh();
|
|
}
|
|
|
|
return (
|
|
<div className="min-h-dvh md:flex">
|
|
<aside className="hidden w-60 shrink-0 flex-col border-r border-line bg-white px-4 py-6 md:flex">
|
|
<p className="flex items-center gap-2.5 px-2 text-[15px] font-semibold tracking-tight">
|
|
<span
|
|
aria-hidden="true"
|
|
className="grid h-8 w-8 place-items-center rounded-[10px] bg-brand-100 text-[15px] font-bold text-brand-800"
|
|
>
|
|
G
|
|
</span>
|
|
Gutscheintracker
|
|
</p>
|
|
<nav aria-label="Hauptmenü" className="mt-7 flex flex-col gap-1">
|
|
{NAV.map((item) => (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
aria-current={isActive(pathname, item.href) ? "page" : undefined}
|
|
className={`rounded-[10px] px-3 py-2 text-sm font-medium transition-colors ${
|
|
isActive(pathname, item.href)
|
|
? "bg-brand-50 text-brand-800"
|
|
: "text-ink hover:bg-wash"
|
|
}`}
|
|
>
|
|
{item.label}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
<button
|
|
type="button"
|
|
onClick={logout}
|
|
className="mt-auto rounded-[10px] px-3 py-2 text-left text-sm font-medium text-muted transition-colors hover:bg-wash hover:text-ink"
|
|
>
|
|
Abmelden
|
|
</button>
|
|
</aside>
|
|
|
|
<div className="min-w-0 flex-1">
|
|
<header className="sticky top-0 flex items-center justify-between border-b border-line bg-white/95 px-4 py-3 backdrop-blur md:hidden">
|
|
<p className="flex items-center gap-2 text-sm font-semibold tracking-tight">
|
|
<span
|
|
aria-hidden="true"
|
|
className="grid h-7 w-7 place-items-center rounded-lg bg-brand-100 text-[13px] font-bold text-brand-800"
|
|
>
|
|
G
|
|
</span>
|
|
Gutscheintracker
|
|
</p>
|
|
<button
|
|
type="button"
|
|
onClick={logout}
|
|
className="rounded-lg px-3 py-1.5 text-sm font-medium text-muted hover:text-ink"
|
|
>
|
|
Abmelden
|
|
</button>
|
|
</header>
|
|
|
|
<div className="mx-auto w-full max-w-3xl px-4 pb-28 pt-6 md:px-6 md:pb-12">
|
|
{children}
|
|
</div>
|
|
|
|
<nav
|
|
aria-label="Hauptmenü"
|
|
className="fixed inset-x-0 bottom-0 grid grid-cols-4 border-t border-line bg-white/95 backdrop-blur md:hidden"
|
|
>
|
|
{NAV.map((item) => (
|
|
<Link
|
|
key={item.href}
|
|
href={item.href}
|
|
aria-current={isActive(pathname, item.href) ? "page" : undefined}
|
|
className={`px-1 py-2.5 text-center text-xs font-medium ${
|
|
isActive(pathname, item.href) ? "text-brand-700" : "text-muted"
|
|
}`}
|
|
>
|
|
{item.label}
|
|
</Link>
|
|
))}
|
|
</nav>
|
|
</div>
|
|
</div>
|
|
);
|
|
}
|