+ {/* Title section */}
+
+
{title}
+ {children}
+
-
-
-
-
-
+ {/* Actions section */}
+
+
+
+
+
+
+
+
);
diff --git a/apps/web/components/layouts/mobile-nav.tsx b/apps/web/components/layouts/mobile-nav.tsx
new file mode 100644
index 0000000..b7714c8
--- /dev/null
+++ b/apps/web/components/layouts/mobile-nav.tsx
@@ -0,0 +1,181 @@
+'use client';
+
+import Link from 'next/link';
+import Image from 'next/image';
+import { usePathname } from 'next/navigation';
+import { useState } from 'react';
+import { cn, Sheet, SheetContent, SheetHeader, SheetTitle, SheetTrigger } from '@horux/shared-ui';
+import {
+ LayoutDashboard,
+ FileText,
+ Calculator,
+ Settings,
+ LogOut,
+ BarChart3,
+ Calendar,
+ Bell,
+ Users,
+ Building2,
+ Scale,
+ Send,
+ ListChecks,
+ FileCheck,
+ ClipboardList,
+ CreditCard,
+ CheckSquare2,
+ UserCog,
+ Shield,
+ FileWarning,
+ Rocket,
+ Menu,
+} from 'lucide-react';
+import { useAuthStore } from '@/stores/auth-store';
+import { logout } from '@/lib/api/auth';
+import { useNavGate } from '@/lib/hooks/use-nav-gate';
+import { useRouter } from 'next/navigation';
+import { hasDespachoFeature, isGlobalAdminRfc, type DespachoPlan } from '@horux/shared';
+
+interface NavItem {
+ name: string;
+ href: string;
+ icon: typeof LayoutDashboard;
+ feature?: string;
+ roles?: string[];
+}
+
+const navigation: NavItem[] = [
+ { name: 'Despacho', href: '/despachos', icon: ListChecks, roles: ['owner', 'cfo', 'contador', 'auxiliar', 'supervisor'] },
+ { name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard, roles: ['owner', 'cfo', 'contador', 'auxiliar', 'supervisor', 'cliente'] },
+ { name: 'CFDI', href: '/cfdi', icon: FileText },
+ { name: 'Impuestos', href: '/impuestos', icon: Calculator },
+ { name: 'Reportes', href: '/reportes', icon: BarChart3, feature: 'reportes', roles: ['owner', 'cfo', 'supervisor', 'cliente'] },
+ { name: 'Conciliacion', href: '/conciliacion', icon: Scale, feature: 'conciliacion' },
+ { name: 'Calendario', href: '/calendario', icon: Calendar, feature: 'calendario' },
+ { name: 'Alertas', href: '/alertas', icon: Bell, feature: 'alertas' },
+ { name: 'Facturación', href: '/facturacion', icon: Send, roles: ['owner', 'cfo', 'contador', 'auxiliar', 'supervisor'] },
+ { name: 'Documentos', href: '/documentos', icon: FileCheck, feature: 'documentos' },
+ { name: 'Carteras', href: '/carteras', icon: ClipboardList, roles: ['supervisor', 'auxiliar'] },
+ { name: 'Contribuyentes', href: '/contribuyentes', icon: Building2, roles: ['owner', 'cfo', 'supervisor', 'contador', 'auxiliar'] },
+ { name: 'Usuarios', href: '/usuarios', icon: Users, roles: ['owner', 'cfo', 'supervisor', 'auxiliar'] },
+ { name: 'Tareas', href: '/tareas', icon: CheckSquare2, roles: ['owner', 'cfo', 'contador', 'auxiliar', 'supervisor'] },
+ { name: 'Planes', href: '/configuracion/planes-despacho', icon: CreditCard, roles: ['owner', 'cfo'] },
+ { name: 'Configuracion', href: '/configuracion', icon: Settings, roles: ['owner', 'cfo', 'supervisor', 'auxiliar', 'cliente'] },
+];
+
+const adminNavigation: NavItem[] = [
+ { name: 'Clientes', href: '/clientes', icon: Building2 },
+ { name: 'Admin Usuarios', href: '/admin/usuarios', icon: UserCog },
+ { name: 'Staff', href: '/admin/staff', icon: Shield },
+ { name: 'Audit Log', href: '/admin/audit-log', icon: FileWarning },
+];
+
+export function MobileNav() {
+ const pathname = usePathname();
+ const router = useRouter();
+ const { user, logout: clearAuth } = useAuthStore();
+ const [open, setOpen] = useState(false);
+
+ const handleLogout = async () => {
+ try {
+ await logout();
+ } catch {
+ // Ignore errors
+ } finally {
+ clearAuth();
+ router.push('/login');
+ }
+ };
+
+ const plan = (user?.plan || 'trial') as DespachoPlan;
+ const role = user?.role || 'visor';
+ const navGate = useNavGate();
+ const filteredNav = navigation.filter((item) => {
+ if (item.feature && !hasDespachoFeature(plan, item.feature)) return false;
+ if (item.roles && !item.roles.includes(role)) return false;
+ if (!navGate.isAllowed(item.href)) return false;
+ return true;
+ });
+
+ const isGlobalAdmin = isGlobalAdminRfc(user?.tenantRfc, role, user?.platformRoles);
+ const allNavigation = isGlobalAdmin
+ ? [...filteredNav.slice(0, -1), ...adminNavigation, filteredNav[filteredNav.length - 1]]
+ : filteredNav;
+
+ const handleLinkClick = () => {
+ setOpen(false);
+ };
+
+ return (
+
+
+
+
+
+ {/* Logo */}
+
+
+
+
+
+ Horux
+ Despachos
+
+
+
+
+
+ {/* Navigation */}
+
+
+ {/* User & Logout */}
+
+ {!isGlobalAdmin && (
+
+
{user?.nombre}
+
{user?.email}
+
+ )}
+
+
+
+
+ );
+}
diff --git a/apps/web/components/membership-switcher.tsx b/apps/web/components/membership-switcher.tsx
index ae8a389..7f2c649 100644
--- a/apps/web/components/membership-switcher.tsx
+++ b/apps/web/components/membership-switcher.tsx
@@ -65,10 +65,10 @@ export function MembershipSwitcher() {