diff --git a/apps/web/app/(dashboard)/layout.tsx b/apps/web/app/(dashboard)/layout.tsx
new file mode 100644
index 0000000..97bbd3f
--- /dev/null
+++ b/apps/web/app/(dashboard)/layout.tsx
@@ -0,0 +1,34 @@
+'use client';
+
+import { useEffect } from 'react';
+import { useRouter } from 'next/navigation';
+import { useAuthStore } from '@/stores/auth-store';
+import { Sidebar } from '@/components/layouts/sidebar';
+
+export default function DashboardLayout({
+ children,
+}: {
+ children: React.ReactNode;
+}) {
+ const router = useRouter();
+ const { isAuthenticated } = useAuthStore();
+
+ useEffect(() => {
+ if (!isAuthenticated) {
+ router.push('/login');
+ }
+ }, [isAuthenticated, router]);
+
+ if (!isAuthenticated) {
+ return null;
+ }
+
+ return (
+
+ );
+}
diff --git a/apps/web/components/layouts/dashboard-shell.tsx b/apps/web/components/layouts/dashboard-shell.tsx
new file mode 100644
index 0000000..7c7f5ac
--- /dev/null
+++ b/apps/web/components/layouts/dashboard-shell.tsx
@@ -0,0 +1,19 @@
+import { Sidebar } from './sidebar';
+import { Header } from './header';
+
+interface DashboardShellProps {
+ children: React.ReactNode;
+ title: string;
+}
+
+export function DashboardShell({ children, title }: DashboardShellProps) {
+ return (
+
+ );
+}
diff --git a/apps/web/components/layouts/header.tsx b/apps/web/components/layouts/header.tsx
new file mode 100644
index 0000000..31690ea
--- /dev/null
+++ b/apps/web/components/layouts/header.tsx
@@ -0,0 +1,42 @@
+'use client';
+
+import { useThemeStore } from '@/stores/theme-store';
+import { themes, type ThemeName } from '@/themes';
+import { Button } from '@/components/ui/button';
+import { Sun, Moon, Palette } from 'lucide-react';
+
+const themeIcons: Record = {
+ light: ,
+ vibrant: ,
+ corporate: ,
+ dark: ,
+};
+
+const themeOrder: ThemeName[] = ['light', 'vibrant', 'corporate', 'dark'];
+
+export function Header({ title }: { title: string }) {
+ const { theme, setTheme } = useThemeStore();
+
+ const cycleTheme = () => {
+ const currentIndex = themeOrder.indexOf(theme);
+ const nextIndex = (currentIndex + 1) % themeOrder.length;
+ setTheme(themeOrder[nextIndex]);
+ };
+
+ return (
+
+ );
+}
diff --git a/apps/web/components/layouts/sidebar.tsx b/apps/web/components/layouts/sidebar.tsx
new file mode 100644
index 0000000..f037fa8
--- /dev/null
+++ b/apps/web/components/layouts/sidebar.tsx
@@ -0,0 +1,92 @@
+'use client';
+
+import Link from 'next/link';
+import { usePathname } from 'next/navigation';
+import { cn } from '@/lib/utils';
+import {
+ LayoutDashboard,
+ FileText,
+ Calculator,
+ Settings,
+ LogOut,
+} from 'lucide-react';
+import { useAuthStore } from '@/stores/auth-store';
+import { logout } from '@/lib/api/auth';
+import { useRouter } from 'next/navigation';
+
+const navigation = [
+ { name: 'Dashboard', href: '/dashboard', icon: LayoutDashboard },
+ { name: 'CFDI', href: '/cfdi', icon: FileText },
+ { name: 'Impuestos', href: '/impuestos', icon: Calculator },
+ { name: 'Configuracion', href: '/configuracion', icon: Settings },
+];
+
+export function Sidebar() {
+ const pathname = usePathname();
+ const router = useRouter();
+ const { user, logout: clearAuth } = useAuthStore();
+
+ const handleLogout = async () => {
+ try {
+ await logout();
+ } catch {
+ // Ignore errors
+ } finally {
+ clearAuth();
+ router.push('/login');
+ }
+ };
+
+ return (
+
+ );
+}