From 06e9f3eba786a96cf80c8458a3dcd423c95145d0 Mon Sep 17 00:00:00 2001 From: Consultoria AS Date: Thu, 22 Jan 2026 02:25:35 +0000 Subject: [PATCH] feat(web): add dashboard layout with sidebar and header Co-Authored-By: Claude Opus 4.5 --- apps/web/app/(dashboard)/layout.tsx | 34 +++++++ .../components/layouts/dashboard-shell.tsx | 19 ++++ apps/web/components/layouts/header.tsx | 42 +++++++++ apps/web/components/layouts/sidebar.tsx | 92 +++++++++++++++++++ 4 files changed, 187 insertions(+) create mode 100644 apps/web/app/(dashboard)/layout.tsx create mode 100644 apps/web/components/layouts/dashboard-shell.tsx create mode 100644 apps/web/components/layouts/header.tsx create mode 100644 apps/web/components/layouts/sidebar.tsx 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 ( +
+ +
+ {children} +
+
+ ); +} 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 ( +
+ +
+
+
{children}
+
+
+ ); +} 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 ( +
+

{title}

+ +
+ +
+
+ ); +} 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 ( + + ); +}