Nye-TeeOff/frontend/src/components/AdminMobileMenu.tsx
2026-04-14 16:31:28 +02:00

141 lines
4.7 KiB
TypeScript
Executable file
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"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)}
className="btn btn-md btn-primary"
>
<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)}
className="btn-icon border-white/20 bg-white/10 text-xl text-white hover:bg-white/20"
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}
className="btn btn-md btn-danger w-full justify-start text-left"
>
Logg ut
</button>
</div>
</div>
</div>
)}
</>
);
}