Files
tabdeal-job-queue/frontend/src/layout/AppLayout.tsx

82 lines
2.6 KiB
TypeScript

import { BriefcaseBusiness, CalendarClock, LayoutDashboard, Moon, PanelLeftClose, PanelLeftOpen, Sun } from "lucide-react";
import { NavLink, Outlet } from "react-router-dom";
import { useState } from "react";
import { toast } from "sonner";
import logoUrl from "../assets/jobqueue-logo.svg";
import { useTheme } from "../hooks/useTheme";
const navItems = [
{ to: "/", label: "Dashboard", icon: LayoutDashboard },
{ to: "/jobs", label: "Jobs", icon: BriefcaseBusiness },
{ to: "/events", label: "Events", icon: CalendarClock }
];
export function AppLayout() {
const { theme, toggleTheme } = useTheme();
const [sidebarCollapsed, setSidebarCollapsed] = useState(() => window.localStorage.getItem("jobQueueSidebar") === "collapsed");
function toggleSidebarCollapsed() {
setSidebarCollapsed((current) => {
const next = !current;
window.localStorage.setItem("jobQueueSidebar", next ? "collapsed" : "expanded");
return next;
});
}
function handleThemeToggle() {
toggleTheme();
}
return (
<div className={`app-shell ${sidebarCollapsed ? "sidebar-collapsed" : ""}`}>
<header className="topbar">
<NavLink className="brand-lockup" to="/">
<img className="brand-logo" src={logoUrl} alt="JobQueue" />
<div>
<span className="eyebrow">Postgres queue</span>
<h1>JobQueue</h1>
</div>
</NavLink>
<div className="topbar-actions">
<button className="icon-button" type="button" aria-label="toggle dark mode" onClick={handleThemeToggle}>
{theme === "dark" ? <Sun size={18} /> : <Moon size={18} />}
</button>
</div>
</header>
<aside className="sidebar">
<div className="sidebar-header">
<span>Navigation</span>
<button
className="icon-button collapse-button"
type="button"
aria-label={sidebarCollapsed ? "expand sidebar" : "collapse sidebar"}
onClick={toggleSidebarCollapsed}
>
{sidebarCollapsed ? <PanelLeftOpen size={18} /> : <PanelLeftClose size={18} />}
</button>
</div>
<nav>
{navItems.map((item) => (
<NavLink
className={({ isActive }) => `nav-item ${isActive ? "active" : ""}`}
end={item.to === "/"}
key={item.to}
title={item.label}
to={item.to}
>
<item.icon size={18} />
<span>{item.label}</span>
</NavLink>
))}
</nav>
</aside>
<main className="content">
<Outlet />
</main>
</div>
);
}