feat: add theme-based layouts
Each theme now has a unique layout: - Light: Standard fixed sidebar - Vibrant: Horizontal top navigation - Corporate: Compact sidebar (expands on hover) - Dark: Floating sidebar with glass effect Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -5,13 +5,37 @@ import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/com
|
||||
import { useThemeStore } from '@/stores/theme-store';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { themes, type ThemeName } from '@/themes';
|
||||
import { Check, Palette, User, Building } from 'lucide-react';
|
||||
import { Check, Palette, User, Building, Sidebar, PanelTop, Minimize2, Sparkles } from 'lucide-react';
|
||||
|
||||
const themeOptions: { name: ThemeName; label: string; description: string }[] = [
|
||||
{ name: 'light', label: 'Light', description: 'Tema claro profesional' },
|
||||
{ name: 'vibrant', label: 'Vibrant', description: 'Colores vivos y modernos' },
|
||||
{ name: 'corporate', label: 'Corporate', description: 'Diseño empresarial denso' },
|
||||
{ name: 'dark', label: 'Dark', description: 'Modo oscuro con acentos neón' },
|
||||
const themeOptions: { name: ThemeName; label: string; description: string; layoutDesc: string; layoutIcon: typeof Sidebar }[] = [
|
||||
{
|
||||
name: 'light',
|
||||
label: 'Light',
|
||||
description: 'Tema claro profesional',
|
||||
layoutDesc: 'Sidebar estándar fijo',
|
||||
layoutIcon: Sidebar,
|
||||
},
|
||||
{
|
||||
name: 'vibrant',
|
||||
label: 'Vibrant',
|
||||
description: 'Colores vivos y modernos',
|
||||
layoutDesc: 'Navegación horizontal superior',
|
||||
layoutIcon: PanelTop,
|
||||
},
|
||||
{
|
||||
name: 'corporate',
|
||||
label: 'Corporate',
|
||||
description: 'Diseño empresarial denso',
|
||||
layoutDesc: 'Sidebar compacto (expande al hover)',
|
||||
layoutIcon: Minimize2,
|
||||
},
|
||||
{
|
||||
name: 'dark',
|
||||
label: 'Dark',
|
||||
description: 'Modo oscuro con acentos neón',
|
||||
layoutDesc: 'Sidebar flotante con efecto glass',
|
||||
layoutIcon: Sparkles,
|
||||
},
|
||||
];
|
||||
|
||||
export default function ConfiguracionPage() {
|
||||
@@ -71,41 +95,72 @@ export default function ConfiguracionPage() {
|
||||
<CardHeader>
|
||||
<CardTitle className="flex items-center gap-2 text-base">
|
||||
<Palette className="h-4 w-4" />
|
||||
Tema Visual
|
||||
Tema Visual y Layout
|
||||
</CardTitle>
|
||||
<CardDescription>
|
||||
Elige el tema que mejor se adapte a tu preferencia
|
||||
Cada tema incluye colores y distribución de elementos únicos
|
||||
</CardDescription>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
|
||||
{themeOptions.map((option) => (
|
||||
<button
|
||||
key={option.name}
|
||||
onClick={() => setTheme(option.name)}
|
||||
className={`relative p-4 rounded-lg border-2 text-left transition-all ${
|
||||
theme === option.name
|
||||
? 'border-primary bg-primary/5'
|
||||
: 'border-border hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{theme === option.name && (
|
||||
<div className="absolute top-2 right-2">
|
||||
<Check className="h-4 w-4 text-primary" />
|
||||
<div className="grid gap-4 md:grid-cols-2">
|
||||
{themeOptions.map((option) => {
|
||||
const LayoutIcon = option.layoutIcon;
|
||||
return (
|
||||
<button
|
||||
key={option.name}
|
||||
onClick={() => setTheme(option.name)}
|
||||
className={`relative p-4 rounded-lg border-2 text-left transition-all ${
|
||||
theme === option.name
|
||||
? 'border-primary bg-primary/5 shadow-md'
|
||||
: 'border-border hover:border-primary/50'
|
||||
}`}
|
||||
>
|
||||
{theme === option.name && (
|
||||
<div className="absolute top-3 right-3">
|
||||
<div className="h-6 w-6 rounded-full bg-primary flex items-center justify-center">
|
||||
<Check className="h-4 w-4 text-primary-foreground" />
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{/* Color Preview */}
|
||||
<div className="flex gap-2 mb-4">
|
||||
<div
|
||||
className="h-12 w-12 rounded-lg shadow-inner"
|
||||
style={{
|
||||
background: `hsl(${themes[option.name].cssVars['--primary']})`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="h-12 w-8 rounded-lg shadow-inner"
|
||||
style={{
|
||||
background: `hsl(${themes[option.name].cssVars['--secondary']})`,
|
||||
}}
|
||||
/>
|
||||
<div
|
||||
className="h-12 w-8 rounded-lg shadow-inner"
|
||||
style={{
|
||||
background: `hsl(${themes[option.name].cssVars['--accent']})`,
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
)}
|
||||
<div
|
||||
className="h-20 rounded-md mb-3"
|
||||
style={{
|
||||
background: `hsl(${themes[option.name].cssVars['--primary']})`,
|
||||
}}
|
||||
/>
|
||||
<p className="font-medium">{option.label}</p>
|
||||
<p className="text-xs text-muted-foreground">
|
||||
{option.description}
|
||||
</p>
|
||||
</button>
|
||||
))}
|
||||
|
||||
{/* Theme Info */}
|
||||
<p className="font-semibold text-lg mb-1">{option.label}</p>
|
||||
<p className="text-sm text-muted-foreground mb-3">
|
||||
{option.description}
|
||||
</p>
|
||||
|
||||
{/* Layout Info */}
|
||||
<div className="flex items-center gap-2 pt-3 border-t">
|
||||
<LayoutIcon className="h-4 w-4 text-muted-foreground" />
|
||||
<span className="text-xs text-muted-foreground">
|
||||
{option.layoutDesc}
|
||||
</span>
|
||||
</div>
|
||||
</button>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
</CardContent>
|
||||
</Card>
|
||||
|
||||
@@ -3,7 +3,13 @@
|
||||
import { useEffect } from 'react';
|
||||
import { useRouter } from 'next/navigation';
|
||||
import { useAuthStore } from '@/stores/auth-store';
|
||||
import { useThemeStore } from '@/stores/theme-store';
|
||||
import { themes } from '@/themes';
|
||||
import { Sidebar } from '@/components/layouts/sidebar';
|
||||
import { TopNav } from '@/components/layouts/topnav';
|
||||
import { SidebarCompact } from '@/components/layouts/sidebar-compact';
|
||||
import { SidebarFloating } from '@/components/layouts/sidebar-floating';
|
||||
import { cn } from '@/lib/utils';
|
||||
|
||||
export default function DashboardLayout({
|
||||
children,
|
||||
@@ -12,6 +18,10 @@ export default function DashboardLayout({
|
||||
}) {
|
||||
const router = useRouter();
|
||||
const { isAuthenticated } = useAuthStore();
|
||||
const { theme } = useThemeStore();
|
||||
|
||||
const currentTheme = themes[theme];
|
||||
const layout = currentTheme.layout;
|
||||
|
||||
useEffect(() => {
|
||||
if (!isAuthenticated) {
|
||||
@@ -23,10 +33,42 @@ export default function DashboardLayout({
|
||||
return null;
|
||||
}
|
||||
|
||||
// Render layout based on theme
|
||||
const renderNavigation = () => {
|
||||
switch (layout) {
|
||||
case 'topnav':
|
||||
return <TopNav />;
|
||||
case 'sidebar-compact':
|
||||
return <SidebarCompact />;
|
||||
case 'sidebar-floating':
|
||||
return <SidebarFloating />;
|
||||
case 'sidebar-standard':
|
||||
default:
|
||||
return <Sidebar />;
|
||||
}
|
||||
};
|
||||
|
||||
const getContentClasses = () => {
|
||||
switch (layout) {
|
||||
case 'topnav':
|
||||
return 'pt-16'; // Top padding for fixed top nav
|
||||
case 'sidebar-compact':
|
||||
return 'pl-16'; // Small left padding for compact sidebar
|
||||
case 'sidebar-floating':
|
||||
return 'pl-72 pr-4 py-4'; // Padding for floating sidebar
|
||||
case 'sidebar-standard':
|
||||
default:
|
||||
return 'pl-64'; // Standard sidebar width
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-background">
|
||||
<Sidebar />
|
||||
<div className="pl-64">
|
||||
<div className={cn(
|
||||
'min-h-screen bg-background',
|
||||
layout === 'sidebar-floating' && 'bg-gradient-to-br from-background via-background to-muted/20'
|
||||
)}>
|
||||
{renderNavigation()}
|
||||
<div className={getContentClasses()}>
|
||||
{children}
|
||||
</div>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user