Implementar modo oscuro con toggle en sidebar
This commit is contained in:
+10
-1
@@ -12,8 +12,9 @@ import { NuevoIngreso } from '@/sections/NuevoIngreso';
|
||||
import { EditIngreso } from '@/sections/EditIngreso';
|
||||
import { Spinner } from '@/components/ui/spinner';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { ThemeProvider } from '@/contexts/ThemeContext';
|
||||
|
||||
function App() {
|
||||
function AppContent() {
|
||||
const store = useHospitalStore();
|
||||
|
||||
if (!store.isLoaded) {
|
||||
@@ -207,4 +208,12 @@ case 'nuevoingreso':
|
||||
);
|
||||
}
|
||||
|
||||
function App() {
|
||||
return (
|
||||
<ThemeProvider>
|
||||
<AppContent />
|
||||
</ThemeProvider>
|
||||
);
|
||||
}
|
||||
|
||||
export default App;
|
||||
|
||||
@@ -0,0 +1,40 @@
|
||||
import { createContext, useContext, useEffect, useState, type ReactNode } from 'react';
|
||||
|
||||
interface ThemeContextType {
|
||||
isDarkMode: boolean;
|
||||
toggleDarkMode: () => void;
|
||||
}
|
||||
|
||||
const ThemeContext = createContext<ThemeContextType | undefined>(undefined);
|
||||
|
||||
export function ThemeProvider({ children }: { children: ReactNode }) {
|
||||
const [isDarkMode, setIsDarkMode] = useState(() => {
|
||||
const saved = localStorage.getItem('darkMode');
|
||||
return saved ? JSON.parse(saved) : false;
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
localStorage.setItem('darkMode', JSON.stringify(isDarkMode));
|
||||
if (isDarkMode) {
|
||||
document.documentElement.classList.add('dark');
|
||||
} else {
|
||||
document.documentElement.classList.remove('dark');
|
||||
}
|
||||
}, [isDarkMode]);
|
||||
|
||||
const toggleDarkMode = () => setIsDarkMode((prev: boolean) => !prev);
|
||||
|
||||
return (
|
||||
<ThemeContext.Provider value={{ isDarkMode, toggleDarkMode }}>
|
||||
{children}
|
||||
</ThemeContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useTheme() {
|
||||
const context = useContext(ThemeContext);
|
||||
if (!context) {
|
||||
throw new Error('useTheme must be used within ThemeProvider');
|
||||
}
|
||||
return context;
|
||||
}
|
||||
+43
-14
@@ -7,10 +7,13 @@ import {
|
||||
FlaskConical,
|
||||
Activity,
|
||||
Microscope,
|
||||
Menu
|
||||
Menu,
|
||||
Sun,
|
||||
Moon
|
||||
} from 'lucide-react';
|
||||
import { Button } from '@/components/ui/button';
|
||||
import { Sheet, SheetContent, SheetTrigger } from '@/components/ui/sheet';
|
||||
import { useTheme } from '@/contexts/ThemeContext';
|
||||
import type { Vista } from '@/types';
|
||||
|
||||
interface LayoutProps {
|
||||
@@ -31,6 +34,8 @@ const menuItems: { vista: Vista; label: string; icon: React.ElementType }[] = [
|
||||
];
|
||||
|
||||
export function Layout({ children, vistaActual, onCambiarVista }: LayoutProps) {
|
||||
const { isDarkMode, toggleDarkMode } = useTheme();
|
||||
|
||||
const NavContent = () => (
|
||||
<nav className="flex flex-col gap-2">
|
||||
{menuItems.map((item) => {
|
||||
@@ -40,7 +45,7 @@ export function Layout({ children, vistaActual, onCambiarVista }: LayoutProps) {
|
||||
<Button
|
||||
key={item.vista}
|
||||
variant={isActive ? 'default' : 'ghost'}
|
||||
className={`justify-start gap-3 ${isActive ? 'bg-blue-600 hover:bg-blue-700' : 'hover:bg-gray-100'}`}
|
||||
className={`justify-start gap-3 ${isActive ? 'bg-blue-600 hover:bg-blue-700 text-white' : 'hover:bg-gray-100 dark:hover:bg-gray-700 dark:text-gray-200'}`}
|
||||
onClick={() => onCambiarVista(item.vista)}
|
||||
>
|
||||
<Icon className="h-5 w-5" />
|
||||
@@ -52,51 +57,75 @@ export function Layout({ children, vistaActual, onCambiarVista }: LayoutProps) {
|
||||
);
|
||||
|
||||
return (
|
||||
<div className="min-h-screen bg-gray-50 flex">
|
||||
<div className="min-h-screen bg-gray-50 dark:bg-gray-900 flex">
|
||||
{/* Sidebar Desktop */}
|
||||
<aside className="hidden lg:flex w-64 flex-col bg-white border-r border-gray-200 fixed h-full">
|
||||
<div className="p-6 border-b border-gray-200">
|
||||
<aside className="hidden lg:flex w-64 flex-col bg-white dark:bg-gray-800 border-r border-gray-200 dark:border-gray-700 fixed h-full">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-10 w-10 bg-blue-600 rounded-lg flex items-center justify-center">
|
||||
<Activity className="h-6 w-6 text-white" />
|
||||
</div>
|
||||
<div>
|
||||
<h1 className="font-bold text-lg text-gray-900 leading-tight">Historia Clinica Electrónica</h1>
|
||||
<p className="text-xs text-gray-500">Servicio Clínica Médica - Hospital Santojanni</p>
|
||||
<h1 className="font-bold text-lg text-gray-900 dark:text-white leading-tight">Historia Clinica Electrónica</h1>
|
||||
<p className="text-xs text-gray-500 dark:text-gray-400">Servicio Clínica Médica - Hospital Santojanni</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex-1 p-4 overflow-auto">
|
||||
<NavContent />
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-200 text-xs text-gray-400 text-center">
|
||||
<div className="p-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-center gap-2">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={toggleDarkMode}
|
||||
className="flex items-center gap-2"
|
||||
>
|
||||
{isDarkMode ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||||
<span className="text-xs">{isDarkMode ? 'Modo Claro' : 'Modo Oscuro'}</span>
|
||||
</Button>
|
||||
</div>
|
||||
<div className="text-xs text-gray-400 dark:text-gray-500 text-center mt-2">
|
||||
v1.0.0
|
||||
</div>
|
||||
</div>
|
||||
</aside>
|
||||
|
||||
{/* Header Mobile */}
|
||||
<header className="lg:hidden fixed top-0 left-0 right-0 h-16 bg-white border-b border-gray-200 z-50 flex items-center justify-between px-4">
|
||||
<header className="lg:hidden fixed top-0 left-0 right-0 h-16 bg-white dark:bg-gray-800 border-b border-gray-200 dark:border-gray-700 z-50 flex items-center justify-between px-4">
|
||||
<div className="flex items-center gap-3">
|
||||
<div className="h-8 w-8 bg-blue-600 rounded-lg flex items-center justify-center">
|
||||
<Activity className="h-5 w-5 text-white" />
|
||||
</div>
|
||||
<span className="font-bold text-gray-900">Gestion Historia Clinica Electronica</span>
|
||||
<span className="font-bold text-gray-900 dark:text-white">Gestion Historia Clinica Electronica</span>
|
||||
</div>
|
||||
<Sheet>
|
||||
<SheetTrigger asChild>
|
||||
<Button variant="ghost" size="icon">
|
||||
<Button variant="ghost" size="icon" className="dark:text-white">
|
||||
<Menu className="h-6 w-6" />
|
||||
</Button>
|
||||
</SheetTrigger>
|
||||
<SheetContent side="right" className="w-64 p-0">
|
||||
<div className="p-6 border-b border-gray-200">
|
||||
<SheetContent side="right" className="w-64 p-0 dark:bg-gray-800">
|
||||
<div className="p-6 border-b border-gray-200 dark:border-gray-700">
|
||||
<div className="flex items-center justify-between">
|
||||
<span className="font-bold text-lg">Menú</span>
|
||||
<span className="font-bold text-lg dark:text-white">Menú</span>
|
||||
</div>
|
||||
</div>
|
||||
<div className="p-4">
|
||||
<NavContent />
|
||||
</div>
|
||||
<div className="p-4 border-t border-gray-200 dark:border-gray-700">
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
onClick={toggleDarkMode}
|
||||
className="w-full flex items-center justify-center gap-2 dark:text-white"
|
||||
>
|
||||
{isDarkMode ? <Sun className="h-4 w-4" /> : <Moon className="h-4 w-4" />}
|
||||
<span className="text-sm">{isDarkMode ? 'Modo Claro' : 'Modo Oscuro'}</span>
|
||||
</Button>
|
||||
</div>
|
||||
</SheetContent>
|
||||
</Sheet>
|
||||
</header>
|
||||
|
||||
Reference in New Issue
Block a user