diff --git a/src/App.tsx b/src/App.tsx index f2fe08f..a5fee07 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -22,11 +22,8 @@ import { CheckCircle2, Timer, Sparkles, - LayoutDashboard, Activity, Target, - Menu, - X, } from 'lucide-react' // Types @@ -422,7 +419,6 @@ function App() { const [loading, setLoading] = useState(true) const [refreshing, setRefreshing] = useState(false) const [error, setError] = useState(null) - const [mobileMenuOpen, setMobileMenuOpen] = useState(false) const fetchData = useCallback(async () => { setRefreshing(true) @@ -459,7 +455,7 @@ function App() { if (loading) { return ( -
+
+
-
- {/* Header */} -
-
-
-
- - - -
-

Dashboard Descomplicar

-

Painel de Gestão

-
-
- - - -
- setMobileMenuOpen(!mobileMenuOpen)} - className="md:hidden p-2.5 rounded-xl bg-white/5 hover:bg-white/10 border border-white/10 transition-all" - > - {mobileMenuOpen ? : } - - - - -
-
-
-
-
- - {/* Mobile Navigation */} - - {mobileMenuOpen && ( - + {/* Header com refresh */} + ) } diff --git a/src/components/Layout.tsx b/src/components/Layout.tsx new file mode 100644 index 0000000..5303d50 --- /dev/null +++ b/src/components/Layout.tsx @@ -0,0 +1,269 @@ +import { useState, useEffect } from 'react' +import { NavLink, Outlet } from 'react-router-dom' +import { motion, AnimatePresence } from 'framer-motion' +import { + LayoutDashboard, + Activity, + CreditCard, + Zap, + ChevronLeft, + ChevronRight, + Menu, + X, +} from 'lucide-react' + +const SIDEBAR_KEY = 'dash-sidebar-collapsed' +const MOBILE_BREAKPOINT = 768 + +interface NavItem { + to: string + label: string + icon: React.ElementType +} + +const NAV_ITEMS: NavItem[] = [ + { to: '/', label: 'Dashboard', icon: LayoutDashboard }, + { to: '/monitor', label: 'Monitor', icon: Activity }, + { to: '/financial', label: 'Financeiro', icon: CreditCard }, +] + +function useIsMobile() { + const [isMobile, setIsMobile] = useState( + typeof window !== 'undefined' ? window.innerWidth < MOBILE_BREAKPOINT : false + ) + + useEffect(() => { + const onResize = () => setIsMobile(window.innerWidth < MOBILE_BREAKPOINT) + window.addEventListener('resize', onResize) + return () => window.removeEventListener('resize', onResize) + }, []) + + return isMobile +} + +function getInitialCollapsed(isMobile: boolean): boolean { + if (isMobile) return true + try { + const stored = localStorage.getItem(SIDEBAR_KEY) + if (stored !== null) return stored === 'true' + } catch { + // localStorage indisponivel + } + return false +} + +export default function Layout() { + const isMobile = useIsMobile() + const [collapsed, setCollapsed] = useState(() => getInitialCollapsed(isMobile)) + const [mobileOpen, setMobileOpen] = useState(false) + + // Colapsar automaticamente em mobile + useEffect(() => { + if (isMobile) { + setCollapsed(true) + setMobileOpen(false) + } + }, [isMobile]) + + // Persistir estado em localStorage (apenas desktop) + useEffect(() => { + if (!isMobile) { + try { + localStorage.setItem(SIDEBAR_KEY, String(collapsed)) + } catch { + // localStorage indisponivel + } + } + }, [collapsed, isMobile]) + + const toggleSidebar = () => { + if (isMobile) { + setMobileOpen(prev => !prev) + } else { + setCollapsed(prev => !prev) + } + } + + const closeMobileMenu = () => setMobileOpen(false) + + const sidebarWidth = collapsed ? 72 : 240 + + return ( +
+
+ + {/* Overlay mobile */} + + {isMobile && mobileOpen && ( + + )} + + + {/* Sidebar */} + + {(!isMobile || mobileOpen) && ( + + {/* Logo */} +
+ + + + + {(!collapsed || isMobile) && ( + +

Descomplicar

+

Painel de Gestao

+
+ )} +
+ + {/* Fechar em mobile */} + {isMobile && ( + + )} +
+ + {/* Navegacao */} + + + {/* Toggle (apenas desktop) */} + {!isMobile && ( +
+ +
+ )} +
+ )} +
+ + {/* Conteudo principal */} +
+ {/* Header mobile com botao de menu */} + {isMobile && ( +
+
+ +
+
+ +
+ Descomplicar +
+
+
+ )} + + {/* Outlet para as paginas */} +
+ +
+
+
+
+ ) +} diff --git a/src/main.tsx b/src/main.tsx index 5e47dfb..ceab3e4 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -6,6 +6,7 @@ import './index.css' import App from './App.tsx' import Monitor from './pages/Monitor.tsx' import Financial from './pages/Financial.tsx' +import Layout from './components/Layout.tsx' import { oidcConfig } from './auth/config.ts' import { AuthWrapper } from './auth/AuthWrapper.tsx' @@ -15,10 +16,12 @@ createRoot(document.getElementById('root')!).render( - } /> - } /> - } /> - } /> + }> + } /> + } /> + } /> + } /> + diff --git a/src/pages/Financial.tsx b/src/pages/Financial.tsx index 81ffc05..e4e3f3e 100644 --- a/src/pages/Financial.tsx +++ b/src/pages/Financial.tsx @@ -1,20 +1,15 @@ import { useState, useEffect, useCallback } from 'react' import { motion } from 'framer-motion' -import { Link } from 'react-router-dom' import { BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip, ResponsiveContainer, Legend, } from 'recharts' import { - Zap, RefreshCw, - ArrowLeft, TrendingUp, TrendingDown, DollarSign, Receipt, PiggyBank, - Activity, - LayoutDashboard, } from 'lucide-react' interface FinancialData { @@ -105,7 +100,7 @@ export default function Financial() { if (loading) { return ( -
+

A carregar dados financeiros...

@@ -127,59 +122,28 @@ export default function Financial() { })) return ( -
-
- {/* Header */} -
-
-
-
- - - -
-

Financeiro

-

Vendas e Despesas {new Date().getFullYear()}

-
-
- -
- - {lucroLabel}: {formatEUR(Math.abs(data.lucro_ano))} - - - - - - - Monitor - - - - Dashboard - -
+
+ {/* Header com resumo e refresh */} +
+
+

Financeiro

+

Vendas e Despesas {new Date().getFullYear()}

+
+
+ + {lucroLabel}: {formatEUR(Math.abs(data.lucro_ano))} + + + +
-
- - {/* Main Content */} -
{/* Summary Cards */}
@@ -280,8 +244,6 @@ export default function Financial() {
-
-
-
+
) } diff --git a/src/pages/Monitor.tsx b/src/pages/Monitor.tsx index cd963b8..b5913ad 100644 --- a/src/pages/Monitor.tsx +++ b/src/pages/Monitor.tsx @@ -1,6 +1,5 @@ import { useState, useEffect, useCallback } from 'react' import { motion } from 'framer-motion' -import { Link } from 'react-router-dom' import { Server, Wifi, @@ -11,11 +10,8 @@ import { RefreshCw, CheckCircle2, AlertTriangle, - Zap, - ArrowLeft, Activity, Clock, - TrendingUp, Wrench, Code, Network, @@ -363,7 +359,7 @@ export default function Monitor() { if (loading) { return ( -
+

A carregar monitorizacao...

@@ -392,59 +388,28 @@ export default function Monitor() { }[data.overall] return ( -
-
- {/* Header */} -
-
-
-
- - - -
-

Monitorizacao

-

Cluster Proxmox Descomplicar

-
-
- -
- - {overallLabel} - - - - - - - Financeiro - - - - Dashboard - -
+
+ {/* Header com status e refresh */} +
+
+

Monitorizacao

+

Cluster Proxmox Descomplicar

+
+
+ + {overallLabel} + + + +
-
- - {/* Main Content */} -
{/* Section 1: Cluster Overview */} @@ -661,9 +626,7 @@ export default function Monitor() { Auto-refresh: 60s
- -
-
+
) }