diff --git a/src/app/globals.css b/src/app/globals.css index 8b6441d..cd8cbd1 100644 --- a/src/app/globals.css +++ b/src/app/globals.css @@ -1,27 +1,27 @@ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800;900&display=swap'); -:root { +:root, [data-theme="dark"] { --bg-primary: #0a0a0f; --bg-secondary: #12121a; --bg-card: #1a1a2e; --bg-card-hover: #22223a; --bg-elevated: #252540; - --border-color: rgba(255,255,255,0.06); + --border-color: rgba(255,255,255,0.09); --border-active: rgba(108,99,255,0.5); - --text-primary: #f0f0f5; - --text-secondary: #8888a0; - --text-muted: #55556a; + --text-primary: #f8fafc; + --text-secondary: #cbd5e1; + --text-muted: #94a3b8; --accent: #6C63FF; --accent-hover: #5a52e0; --accent-glow: rgba(108,99,255,0.25); --success: #22c55e; - --success-bg: rgba(34,197,94,0.1); + --success-bg: rgba(34,197,94,0.12); --warning: #f59e0b; - --warning-bg: rgba(245,158,11,0.1); + --warning-bg: rgba(245,158,11,0.12); --danger: #ef4444; - --danger-bg: rgba(239,68,68,0.1); + --danger-bg: rgba(239,68,68,0.12); --info: #3b82f6; - --info-bg: rgba(59,130,246,0.1); + --info-bg: rgba(59,130,246,0.12); --gradient-primary: linear-gradient(135deg, #6C63FF 0%, #a855f7 100%); --gradient-card: linear-gradient(145deg, rgba(26,26,46,0.8), rgba(18,18,26,0.9)); --shadow-sm: 0 2px 8px rgba(0,0,0,0.3); @@ -34,6 +34,36 @@ --radius-xl: 24px; } +[data-theme="light"] { + --bg-primary: #f8fafc; + --bg-secondary: #ffffff; + --bg-card: #ffffff; + --bg-card-hover: #f1f5f9; + --bg-elevated: #f1f5f9; + --border-color: #e2e8f0; + --border-active: #6C63FF; + --text-primary: #0f172a; + --text-secondary: #475569; + --text-muted: #64748b; + --accent: #6C63FF; + --accent-hover: #5a52e0; + --accent-glow: rgba(108,99,255,0.12); + --success: #16a34a; + --success-bg: rgba(22,163,74,0.1); + --warning: #d97706; + --warning-bg: rgba(217,119,6,0.1); + --danger: #dc2626; + --danger-bg: rgba(220,38,38,0.1); + --info: #2563eb; + --info-bg: rgba(37,99,235,0.1); + --gradient-primary: linear-gradient(135deg, #6C63FF 0%, #8b5cf6 100%); + --gradient-card: linear-gradient(145deg, #ffffff, #f8fafc); + --shadow-sm: 0 1px 3px rgba(0,0,0,0.08); + --shadow-md: 0 4px 12px rgba(0,0,0,0.08); + --shadow-lg: 0 10px 25px rgba(0,0,0,0.1); + --shadow-glow: 0 0 20px rgba(108,99,255,0.1); +} + * { margin: 0; padding: 0; box-sizing: border-box; } html, body { height: 100%; } @@ -1016,3 +1046,119 @@ tr:hover td { background: var(--bg-card-hover); } font-weight: 600; box-shadow: 0 2px 10px rgba(108, 99, 255, 0.4); } + +/* === RESPONSIVE BREAKPOINTS (PC, TABLET, MOBILE) === */ + +/* Desktop & Laptop (> 1024px) */ +@media (min-width: 1025px) { + .mobile-close-btn { display: none !important; } +} + +/* Tablet (769px - 1024px) */ +@media (max-width: 1024px) and (min-width: 769px) { + .main-content { margin-left: 240px; } + .main-content.collapsed { margin-left: 76px; } + .stats-grid { grid-template-columns: repeat(2, 1fr); } + .page-content { padding: 24px; } + .topbar { padding: 16px 24px; } +} + +/* Mobile (<= 768px) */ +@media (max-width: 768px) { + .sidebar { + position: fixed; + top: 0; + left: 0; + bottom: 0; + width: 280px !important; + z-index: 1000; + transform: translateX(-100%); + transition: transform 0.3s cubic-bezier(0.16, 1, 0.3, 1); + box-shadow: 0 0 40px rgba(0,0,0,0.5); + } + + .sidebar.open { + transform: translateX(0); + } + + .desktop-collapse-btn { display: none !important; } + .mobile-close-btn { display: flex !important; } + + .main-content, .main-content.collapsed { + margin-left: 0 !important; + width: 100% !important; + } + + .topbar { + padding: 14px 16px; + } + + .topbar-left h1 { + font-size: 18px; + } + + .search-box { + display: none; + } + + .page-content { + padding: 16px; + } + + .stats-grid { + grid-template-columns: 1fr; + } + + .grid-2, .grid-3 { + grid-template-columns: 1fr; + } + + .view-mode-selector { + width: 100%; + justify-content: space-between; + } + + .view-mode-btn { + flex: 1; + justify-content: center; + padding: 8px 6px; + font-size: 12px; + } +} + +/* === LIGHT THEME OVERRIDES === */ +[data-theme="light"] .sidebar { + background: #ffffff; + border-right: 1px solid #e2e8f0; +} + +[data-theme="light"] .topbar { + background: rgba(255, 255, 255, 0.9); + border-bottom: 1px solid #e2e8f0; +} + +[data-theme="light"] .nav-item { + color: #475569; +} + +[data-theme="light"] .nav-item:hover { + background: #f1f5f9; + color: #0f172a; +} + +[data-theme="light"] .nav-item.active { + background: rgba(108, 99, 255, 0.12); + color: #6C63FF; +} + +[data-theme="light"] .card { + background: #ffffff; + border-color: #e2e8f0; + box-shadow: 0 1px 4px rgba(0,0,0,0.05); +} + +[data-theme="light"] .form-input, [data-theme="light"] .form-select { + background: #f8fafc; + border-color: #cbd5e1; + color: #0f172a; +} diff --git a/src/components/Topbar.tsx b/src/components/Topbar.tsx index fc64df7..8828797 100644 --- a/src/components/Topbar.tsx +++ b/src/components/Topbar.tsx @@ -1,5 +1,5 @@ import { useState, useEffect, useRef } from 'react'; -import { Bell, Menu, Search, LogOut, User, CheckCircle, AlertCircle } from 'lucide-react'; +import { Bell, Menu, Search, LogOut, User, CheckCircle, AlertCircle, Sun, Moon } from 'lucide-react'; interface TopbarProps { title: string; @@ -18,6 +18,7 @@ export default function Topbar({ title, subtitle, onMenuClick, onNavigate }: Top const [avatarInitials, setAvatarInitials] = useState('AD'); const [tenantName, setTenantName] = useState('Administrador'); const [email, setEmail] = useState('admin@agendapro.com'); + const [theme, setTheme] = useState<'dark' | 'light'>('dark'); const [showNotifications, setShowNotifications] = useState(false); const [showProfile, setShowProfile] = useState(false); @@ -26,6 +27,19 @@ export default function Topbar({ title, subtitle, onMenuClick, onNavigate }: Top const notifRef = useRef(null); const profileRef = useRef(null); + useEffect(() => { + const savedTheme = (localStorage.getItem('agendapro_theme') as 'dark' | 'light') || 'dark'; + setTheme(savedTheme); + document.documentElement.setAttribute('data-theme', savedTheme); + }, []); + + const toggleTheme = () => { + const nextTheme = theme === 'dark' ? 'light' : 'dark'; + setTheme(nextTheme); + document.documentElement.setAttribute('data-theme', nextTheme); + localStorage.setItem('agendapro_theme', nextTheme); + }; + const fetchNotifications = async () => { const slug = localStorage.getItem('agendapro_tenant_slug') || 'barbearia-premium'; try { @@ -157,6 +171,16 @@ export default function Topbar({ title, subtitle, onMenuClick, onNavigate }: Top + +