feat: add full frontend — client portal and admin panel
Páginas cliente: login magic link, registro, validación de token, dashboard de autos, historial por auto y reserva de turnos con selector de servicio + slots disponibles. Páginas admin: login, dashboard de turnos del día con cambio de estado, historial de autos (buscar cliente → auto → ver/cargar registros), gestión de tipos de servicio y vista de clientes. Middleware JWT para proteger rutas /cliente/* y /admin/*. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,59 @@
|
||||
"use client";
|
||||
import Link from "next/link";
|
||||
import { usePathname, useRouter } from "next/navigation";
|
||||
|
||||
const navItems = [
|
||||
{ href: "/admin/dashboard", label: "Turnos del día", icon: "📅" },
|
||||
{ href: "/admin/historial", label: "Historial", icon: "📋" },
|
||||
{ href: "/admin/clientes", label: "Clientes", icon: "👥" },
|
||||
{ href: "/admin/servicios", label: "Servicios", icon: "⚙️" },
|
||||
];
|
||||
|
||||
export default function AdminLayout({ children }: { children: React.ReactNode }) {
|
||||
const pathname = usePathname();
|
||||
const router = useRouter();
|
||||
|
||||
async function cerrarSesion() {
|
||||
await fetch("/api/auth/admin-logout", { method: "POST" });
|
||||
router.push("/admin");
|
||||
}
|
||||
|
||||
if (pathname === "/admin") return <>{children}</>;
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex flex-col">
|
||||
<header className="bg-white border-b border-gray-200 sticky top-0 z-10">
|
||||
<div className="max-w-5xl mx-auto px-4 h-14 flex items-center justify-between">
|
||||
<span className="font-bold text-blue-600 text-lg">🔧 Taller — Admin</span>
|
||||
<button onClick={cerrarSesion} className="text-sm text-gray-500 hover:text-red-600 transition">
|
||||
Cerrar sesión
|
||||
</button>
|
||||
</div>
|
||||
</header>
|
||||
|
||||
<div className="max-w-5xl mx-auto w-full px-4 flex-1 flex flex-col md:flex-row gap-6 py-6">
|
||||
<nav className="md:w-48 shrink-0">
|
||||
<ul className="flex md:flex-col gap-2 flex-wrap">
|
||||
{navItems.map((item) => (
|
||||
<li key={item.href}>
|
||||
<Link
|
||||
href={item.href}
|
||||
className={`flex items-center gap-2 px-3 py-2 rounded-lg text-sm font-medium transition ${
|
||||
pathname.startsWith(item.href)
|
||||
? "bg-blue-600 text-white"
|
||||
: "text-gray-700 hover:bg-gray-100"
|
||||
}`}
|
||||
>
|
||||
<span>{item.icon}</span>
|
||||
{item.label}
|
||||
</Link>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
</nav>
|
||||
|
||||
<main className="flex-1">{children}</main>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user