146 lines
5.2 KiB
TypeScript
146 lines
5.2 KiB
TypeScript
import { Navigate, NavLink, Route, Routes } from "react-router-dom";
|
|
import { useAuth } from "./auth/AuthContext";
|
|
|
|
import SalesDashboard from "./pages/aw/SalesDashboard";
|
|
import RepScores from "./pages/aw/RepScores";
|
|
import ProductDemand from "./pages/aw/ProductDemand";
|
|
import AnomalyDetection from "./pages/aw/AnomalyDetection";
|
|
import StockDashboard from "./pages/wwi/StockDashboard";
|
|
import SupplierScores from "./pages/wwi/SupplierScores";
|
|
import WhatIf from "./pages/wwi/WhatIf";
|
|
import BusinessEvents from "./pages/wwi/BusinessEvents";
|
|
import OperationsPage from "./pages/ops/OperationsPage";
|
|
import AuditPage from "./pages/ops/AuditPage";
|
|
import ExportsPage from "./pages/ops/ExportsPage";
|
|
|
|
function NavItem({ to, label }: { to: string; label: string }) {
|
|
return (
|
|
<NavLink
|
|
to={to}
|
|
className={({ isActive }) => `nav-link${isActive ? " nav-active" : ""}`}
|
|
>
|
|
{label}
|
|
</NavLink>
|
|
);
|
|
}
|
|
|
|
function CenteredShell({ children }: { children: React.ReactNode }) {
|
|
return (
|
|
<div className="min-h-screen grid place-items-center text-center p-4 text-[#d6e7ff]">
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export default function App() {
|
|
const auth = useAuth();
|
|
|
|
if (auth.loading) {
|
|
return <CenteredShell>Initializing OIDC session…</CenteredShell>;
|
|
}
|
|
|
|
if (auth.error) {
|
|
return <CenteredShell>Authentication error: {auth.error}</CenteredShell>;
|
|
}
|
|
|
|
if (auth.enabled && !auth.authenticated) {
|
|
return (
|
|
<CenteredShell>
|
|
<div className="flex flex-col items-center gap-4">
|
|
<p className="m-0 text-lg">Authentication required.</p>
|
|
<button className="btn-primary" onClick={() => void auth.login()} type="button">
|
|
Sign In with OIDC
|
|
</button>
|
|
</div>
|
|
</CenteredShell>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<div className="flex min-h-screen">
|
|
{/* Sidebar */}
|
|
<nav className="
|
|
w-[220px] max-[980px]:w-[180px]
|
|
shrink-0
|
|
bg-[rgba(8,16,28,0.92)]
|
|
border-r border-[rgba(186,212,255,0.22)]
|
|
flex flex-col
|
|
py-5 px-3
|
|
sticky top-0 h-screen overflow-y-auto
|
|
max-sm:w-full max-sm:h-auto max-sm:static
|
|
max-sm:flex-row max-sm:flex-wrap max-sm:gap-2 max-sm:p-3
|
|
">
|
|
<div className="flex items-center gap-2 font-bold text-[0.95rem] tracking-tight mb-6 px-[0.4rem] max-sm:mb-0">
|
|
<span className="text-[#57d4ff] text-[0.7rem]">●</span>
|
|
<span>OTel BI Platform</span>
|
|
</div>
|
|
|
|
<div className="mb-5">
|
|
<div className="text-[0.65rem] uppercase tracking-[0.12em] text-[rgba(233,244,255,0.7)] px-[0.4rem] mb-1">
|
|
AdventureWorks DW
|
|
</div>
|
|
<NavItem to="/aw/sales" label="Sales & Forecast" />
|
|
<NavItem to="/aw/reps" label="Rep Scores" />
|
|
<NavItem to="/aw/products" label="Product Demand" />
|
|
<NavItem to="/aw/anomalies" label="Anomaly Detection" />
|
|
</div>
|
|
|
|
<div className="mb-5">
|
|
<div className="text-[0.65rem] uppercase tracking-[0.12em] text-[rgba(233,244,255,0.7)] px-[0.4rem] mb-1">
|
|
WideWorldImporters DW
|
|
</div>
|
|
<NavItem to="/wwi/stock" label="Stock & Reorder" />
|
|
<NavItem to="/wwi/suppliers" label="Supplier Scores" />
|
|
<NavItem to="/wwi/whatif" label="What-if Scenarios" />
|
|
<NavItem to="/wwi/events" label="Business Events" />
|
|
</div>
|
|
|
|
<div className="mb-5">
|
|
<div className="text-[0.65rem] uppercase tracking-[0.12em] text-[rgba(233,244,255,0.7)] px-[0.4rem] mb-1">
|
|
Platform
|
|
</div>
|
|
<NavItem to="/ops/jobs" label="Operations" />
|
|
<NavItem to="/ops/audit" label="Audit Log" />
|
|
<NavItem to="/ops/exports" label="Export History" />
|
|
</div>
|
|
|
|
<div className="mt-auto pt-4 border-t border-[rgba(186,212,255,0.22)] max-sm:mt-0 max-sm:pt-0 max-sm:border-t-0">
|
|
{auth.subject && (
|
|
<p className="text-[0.78rem] text-[rgba(233,244,255,0.7)] m-0 mb-2 overflow-hidden text-ellipsis whitespace-nowrap">
|
|
{auth.subject}
|
|
</p>
|
|
)}
|
|
{auth.enabled && (
|
|
<button className="btn-ghost" onClick={() => void auth.logout()} type="button">
|
|
Sign Out
|
|
</button>
|
|
)}
|
|
</div>
|
|
</nav>
|
|
|
|
{/* Main content */}
|
|
<main className="flex-1 overflow-auto p-6">
|
|
<Routes>
|
|
<Route index element={<Navigate to="/aw/sales" replace />} />
|
|
|
|
<Route path="/aw/sales" element={<SalesDashboard />} />
|
|
<Route path="/aw/reps" element={<RepScores />} />
|
|
<Route path="/aw/products" element={<ProductDemand />} />
|
|
<Route path="/aw/anomalies" element={<AnomalyDetection />} />
|
|
|
|
<Route path="/wwi/stock" element={<StockDashboard />} />
|
|
<Route path="/wwi/suppliers" element={<SupplierScores />} />
|
|
<Route path="/wwi/whatif" element={<WhatIf />} />
|
|
<Route path="/wwi/events" element={<BusinessEvents />} />
|
|
|
|
<Route path="/ops/jobs" element={<OperationsPage />} />
|
|
<Route path="/ops/audit" element={<AuditPage />} />
|
|
<Route path="/ops/exports" element={<ExportsPage />} />
|
|
|
|
<Route path="*" element={<Navigate to="/aw/sales" replace />} />
|
|
</Routes>
|
|
</main>
|
|
</div>
|
|
);
|
|
}
|