mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 06:42:12 +03:00
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
73 lines
2.1 KiB
JavaScript
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>
|
|
);
|
|
}
|