diff --git a/apps/web/app/(admin)/dashboard/page.tsx b/apps/web/app/(admin)/dashboard/page.tsx
new file mode 100644
index 0000000..b84e4c8
--- /dev/null
+++ b/apps/web/app/(admin)/dashboard/page.tsx
@@ -0,0 +1,10 @@
+export default function DashboardPage() {
+ return (
+
+
Dashboard
+
+ Bienvenido al panel de administración de Padel Pro.
+
+
+ );
+}
diff --git a/apps/web/app/(admin)/layout.tsx b/apps/web/app/(admin)/layout.tsx
new file mode 100644
index 0000000..7da2c19
--- /dev/null
+++ b/apps/web/app/(admin)/layout.tsx
@@ -0,0 +1,21 @@
+import { AuthProvider } from '@/components/providers/auth-provider';
+import { Sidebar } from '@/components/layout/sidebar';
+import { Header } from '@/components/layout/header';
+
+export default function AdminLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ return (
+
+
+
+ );
+}
diff --git a/apps/web/components/layout/header.tsx b/apps/web/components/layout/header.tsx
new file mode 100644
index 0000000..05bec0e
--- /dev/null
+++ b/apps/web/components/layout/header.tsx
@@ -0,0 +1,40 @@
+'use client';
+
+import { useSession, signOut } from 'next-auth/react';
+import { LogOut } from 'lucide-react';
+import { Button } from '@/components/ui/button';
+import { SiteSwitcher } from './site-switcher';
+
+export function Header() {
+ const { data: session } = useSession();
+
+ const handleLogout = () => {
+ signOut({ callbackUrl: '/login' });
+ };
+
+ const userRole = session?.user?.role || '';
+ const displayRole = userRole
+ .replace(/_/g, ' ')
+ .toLowerCase()
+ .replace(/\b\w/g, (l) => l.toUpperCase());
+
+ return (
+
+
+
+
+
+
+
+
{session?.user?.name || 'Usuario'}
+
{displayRole}
+
+
+
+
+ );
+}
+
+export default Header;
diff --git a/apps/web/components/layout/sidebar.tsx b/apps/web/components/layout/sidebar.tsx
new file mode 100644
index 0000000..69d2c9b
--- /dev/null
+++ b/apps/web/components/layout/sidebar.tsx
@@ -0,0 +1,74 @@
+'use client';
+
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+import {
+ LayoutDashboard,
+ Calendar,
+ Trophy,
+ ShoppingCart,
+ Users,
+ CreditCard,
+ BarChart3,
+ Settings,
+} from 'lucide-react';
+import { cn } from '@/lib/utils';
+
+interface NavItem {
+ label: string;
+ href: string;
+ icon: React.ComponentType<{ className?: string }>;
+}
+
+const navItems: NavItem[] = [
+ { label: 'Dashboard', href: '/admin/dashboard', icon: LayoutDashboard },
+ { label: 'Reservas', href: '/admin/bookings', icon: Calendar },
+ { label: 'Torneos', href: '/admin/tournaments', icon: Trophy },
+ { label: 'Ventas', href: '/admin/pos', icon: ShoppingCart },
+ { label: 'Clientes', href: '/admin/clients', icon: Users },
+ { label: 'Membresías', href: '/admin/memberships', icon: CreditCard },
+ { label: 'Reportes', href: '/admin/reports', icon: BarChart3 },
+ { label: 'Configuración', href: '/admin/settings', icon: Settings },
+];
+
+export function Sidebar() {
+ const pathname = usePathname();
+
+ return (
+
+ );
+}
+
+export default Sidebar;
diff --git a/apps/web/components/layout/site-switcher.tsx b/apps/web/components/layout/site-switcher.tsx
new file mode 100644
index 0000000..f18e3b7
--- /dev/null
+++ b/apps/web/components/layout/site-switcher.tsx
@@ -0,0 +1,119 @@
+'use client';
+
+import { useState, useEffect, useRef } from 'react';
+import { useSession } from 'next-auth/react';
+import { MapPin, ChevronDown, Check } from 'lucide-react';
+import { cn } from '@/lib/utils';
+
+interface Site {
+ id: string;
+ name: string;
+}
+
+export function SiteSwitcher() {
+ const { data: session } = useSession();
+ const [sites, setSites] = useState([]);
+ const [selectedSiteId, setSelectedSiteId] = useState(null);
+ const [isOpen, setIsOpen] = useState(false);
+ const [isLoading, setIsLoading] = useState(true);
+ const dropdownRef = useRef(null);
+
+ const isSuperAdmin = session?.user?.role === 'SUPER_ADMIN';
+
+ useEffect(() => {
+ async function fetchSites() {
+ try {
+ const response = await fetch('/api/sites');
+ if (response.ok) {
+ const data = await response.json();
+ setSites(data.data || []);
+ }
+ } catch (error) {
+ console.error('Failed to fetch sites:', error);
+ } finally {
+ setIsLoading(false);
+ }
+ }
+
+ fetchSites();
+ }, []);
+
+ useEffect(() => {
+ function handleClickOutside(event: MouseEvent) {
+ if (dropdownRef.current && !dropdownRef.current.contains(event.target as Node)) {
+ setIsOpen(false);
+ }
+ }
+
+ document.addEventListener('mousedown', handleClickOutside);
+ return () => document.removeEventListener('mousedown', handleClickOutside);
+ }, []);
+
+ const selectedSite = sites.find((site) => site.id === selectedSiteId);
+ const displayName = selectedSiteId ? selectedSite?.name : 'Todas las sedes';
+
+ // For non-SUPER_ADMIN users, just show their assigned site
+ if (!isSuperAdmin) {
+ const userSiteName = sites.length > 0 ? sites[0]?.name : 'Cargando...';
+ return (
+
+
+ {isLoading ? 'Cargando...' : userSiteName}
+
+ );
+ }
+
+ // For SUPER_ADMIN, show dropdown to select site
+ return (
+
+
+
+ {isOpen && (
+
+
+
+ {sites.map((site) => (
+
+ ))}
+
+ )}
+
+ );
+}
+
+export default SiteSwitcher;