Nye-TeeOff/frontend/src/components/AdminMobileMenu.tsx

142 lines
4.7 KiB
TypeScript
Raw Normal View History

2026-04-11 09:54:54 +02:00
"use client";
import { useEffect, useState } from 'react';
import Link from 'next/link';
import { usePathname } from 'next/navigation';
import { API_URL } from "@/config/constants";
type AdminMobileMenuProps = {
onOpenTwoFactor?: () => void;
};
const NAV_ITEMS = [
{ href: '/admin', label: 'Kontrollpanel', match: (pathname: string) => pathname === '/admin' },
{ href: '/admin/medlemskap', label: 'Medlemskap', match: (pathname: string) => pathname.startsWith('/admin/medlemskap') },
{ href: '/admin/greenfee', label: 'Greenfee', match: (pathname: string) => pathname.startsWith('/admin/greenfee') },
{ href: '/admin/vtg', label: 'VTG', match: (pathname: string) => pathname.startsWith('/admin/vtg') },
];
export default function AdminMobileMenu({ onOpenTwoFactor }: AdminMobileMenuProps) {
const pathname = usePathname();
const [isOpen, setIsOpen] = useState(false);
useEffect(() => {
if (!isOpen) return;
const handleKeyDown = (event: KeyboardEvent) => {
if (event.key === 'Escape') {
setIsOpen(false);
}
};
window.addEventListener('keydown', handleKeyDown);
return () => window.removeEventListener('keydown', handleKeyDown);
}, [isOpen]);
const handleLogout = async () => {
try {
await fetch(`${API_URL}/auth/logout`, {
method: 'POST',
credentials: 'include'
});
} finally {
window.location.href = '/';
}
};
return (
<>
<div className="mb-6 flex md:hidden">
<button
onClick={() => setIsOpen(true)}
2026-04-14 16:31:28 +02:00
className="btn btn-md btn-primary"
2026-04-11 09:54:54 +02:00
>
<span className="text-lg leading-none"></span>
Adminmeny
</button>
</div>
{isOpen && (
<div
className="fixed inset-0 z-50 bg-black/50 md:hidden"
onMouseDown={(event) => {
if (event.target === event.currentTarget) {
setIsOpen(false);
}
}}
>
<div className="h-full w-[88%] max-w-[22rem] bg-[#11280f] p-6 text-white shadow-2xl">
<div className="flex items-center justify-between border-b border-white/10 pb-5">
<div>
<p className="text-[10px] font-black uppercase tracking-[0.2em] text-[#7ca982]">Adminmeny</p>
<h3 className="mt-2 text-2xl font-black tracking-tight">TeeOff Admin</h3>
</div>
<button
onClick={() => setIsOpen(false)}
2026-04-14 16:31:28 +02:00
className="btn-icon border-white/20 bg-white/10 text-xl text-white hover:bg-white/20"
2026-04-11 09:54:54 +02:00
aria-label="Lukk adminmeny"
>
×
</button>
</div>
<nav className="mt-6 space-y-6 text-[11px] font-black uppercase tracking-[0.2em] text-[#7ca982]">
<div className="space-y-2">
{NAV_ITEMS.map((item) => {
const isActive = item.match(pathname);
return (
<Link
key={item.href}
href={item.href}
onClick={() => setIsOpen(false)}
className={`block rounded-2xl px-4 py-4 transition-colors ${
isActive
? 'border border-[#8bc34a]/30 bg-white/5 text-white'
: 'hover:bg-white/5 hover:text-white'
}`}
>
{item.label}
</Link>
);
})}
</div>
<div className="space-y-2">
<div className="text-[9px] font-bold uppercase tracking-widest text-gray-500">Konto</div>
{onOpenTwoFactor ? (
<button
onClick={() => {
setIsOpen(false);
onOpenTwoFactor();
}}
className="block w-full rounded-2xl px-4 py-3 text-left hover:bg-white/5 hover:text-white"
>
2FA / 1Password
</button>
) : (
<Link
href="/admin"
onClick={() => setIsOpen(false)}
className="block rounded-2xl px-4 py-3 hover:bg-white/5 hover:text-white"
>
2FA / 1Password
</Link>
)}
</div>
</nav>
<div className="mt-8 border-t border-white/10 pt-6">
<button
onClick={handleLogout}
2026-04-14 16:31:28 +02:00
className="btn btn-md btn-danger w-full justify-start text-left"
2026-04-11 09:54:54 +02:00
>
Logg ut
</button>
</div>
</div>
</div>
)}
</>
);
}