Files
OmniRoute/src/shared/components/layouts/DashboardLayout.js
diegosouzapw 034f1a7e1d feat(pages): Phase 8 — Missing Flows & Pages
8.1 — 403 Forbidden Page
  - New: src/app/forbidden/page.js
  - Gradient code + Access Denied message + Dashboard link
  - Consistent design with not-found.js

8.2 — Password Recovery Flow
  - New: src/app/forgot-password/page.js
  - Two methods: CLI reset + manual database reset
  - Added 'Forgot password?' link to login page

8.3 — Health/Status Dashboard
  - New: src/app/(dashboard)/dashboard/health/page.js
  - New: src/app/api/monitoring/health/route.js
  - Cards: Uptime, Version, Memory, Provider count
  - Provider health (circuit breaker states with color coding)
  - Rate limit status table
  - Active lockouts list
  - Auto-refresh every 15s
  - Added Health nav link to Sidebar

8.4 — Maintenance Banner
  - New: src/shared/components/MaintenanceBanner.js
  - Auto-detects server health issues every 10s
  - Shows/hides automatically, dismissible
  - Wired into DashboardLayout

8.5 — Empty States
  - Verified EmptyState component applied in 6+ pages
  - Providers page has its own empty handling

Bonus:
  - Fixed stale GitHub link in Sidebar (decolua → diegosouzapw)

Tests: 295 pass | Build: success
2026-02-15 10:46:11 -03:00

73 lines
2.1 KiB
JavaScript

"use client";
import { useState } from "react";
import Sidebar from "../Sidebar";
import Header from "../Header";
import Breadcrumbs from "../Breadcrumbs";
import NotificationToast from "../NotificationToast";
import MaintenanceBanner from "../MaintenanceBanner";
const SIDEBAR_COLLAPSED_KEY = "sidebar-collapsed";
export default function DashboardLayout({ children }) {
const [sidebarOpen, setSidebarOpen] = useState(false);
const [collapsed, setCollapsed] = useState(() => {
if (typeof window === "undefined") return false;
try {
return localStorage.getItem(SIDEBAR_COLLAPSED_KEY) === "true";
} catch {
return false;
}
});
const handleToggleCollapse = () => {
const next = !collapsed;
setCollapsed(next);
localStorage.setItem(SIDEBAR_COLLAPSED_KEY, String(next));
};
return (
<div className="flex h-screen w-full overflow-hidden bg-bg">
{/* Mobile sidebar overlay */}
{sidebarOpen && (
<div
className="fixed inset-0 z-40 bg-black/20 lg:hidden"
onClick={() => setSidebarOpen(false)}
/>
)}
{/* Sidebar - Desktop */}
<div className="hidden lg:flex">
<Sidebar collapsed={collapsed} onToggleCollapse={handleToggleCollapse} />
</div>
{/* Sidebar - Mobile */}
<div
className={`fixed inset-y-0 left-0 z-50 transform lg:hidden transition-transform duration-300 ease-in-out ${
sidebarOpen ? "translate-x-0" : "-translate-x-full"
}`}
>
<Sidebar onClose={() => setSidebarOpen(false)} />
</div>
{/* Main content */}
<main
id="main-content"
className="flex flex-col flex-1 h-full min-w-0 relative transition-colors duration-300"
>
<Header onMenuClick={() => setSidebarOpen(true)} />
<MaintenanceBanner />
<div className="flex-1 overflow-y-auto custom-scrollbar p-6 lg:p-10">
<div className="max-w-7xl mx-auto">
<Breadcrumbs />
{children}
</div>
</div>
</main>
{/* Global notification toast system */}
<NotificationToast />
</div>
);
}