Files
app/frontend/src/routes/_layout.tsx
T
stroblme 113fdb5a26 Build dashboards you can actually look at and press
Widgets bind to a message name and read it live off the socket the editor
already had — lifted out of the flow editor so a dashboard route gets the
same values, which also gives the home page live data for free.

The input widgets close the loop the other way: a slider publishes into
the graph and whatever consumes that message runs. Verified end to end in
the running app — moving a slider set a flow input, and the stat bound to
what the flow computed from it followed.

View mode is plain CSS grid. A wall panel that only displays should not
download the code that lets someone drag things around, and it now does
not. Editing is a widget picker, a per-widget width control and a
settings card fed by the message catalog.

No new dependencies: the slider is a range input, the gauge is an arc,
and the markdown is a five-line subset. Charts are the one widget still
missing — they need a charting library and the chart tokens the design
guidelines reserved — so they are stored and validated but not offered.
2026-08-16 09:33:02 +02:00

50 lines
1.4 KiB
TypeScript

import { createFileRoute, Outlet, redirect } from "@tanstack/react-router"
import { Footer } from "@/components/Common/Footer"
import { useFlowSocket } from "@/components/Flow/useFlowSocket"
import AppSidebar from "@/components/Sidebar/AppSidebar"
import {
SidebarInset,
SidebarProvider,
SidebarTrigger,
} from "@/components/ui/sidebar"
import { isLoggedIn } from "@/hooks/useAuth"
export const Route = createFileRoute("/_layout")({
component: Layout,
beforeLoad: async () => {
if (!isLoggedIn()) {
throw redirect({
to: "/login",
})
}
},
})
function Layout() {
// Dashboards live in this shell and read live values, so the socket belongs
// here rather than only inside the editor.
useFlowSocket()
return (
<SidebarProvider className="bg-card">
<AppSidebar />
<SidebarInset className="bg-card">
{/* The sidebar carries its own collapse control; a phone has no
sidebar on screen to carry it. */}
<header className="sticky top-0 z-10 flex h-16 shrink-0 items-center gap-2 px-4 md:hidden">
<SidebarTrigger className="-ml-1 text-muted-foreground" />
</header>
<main className="flex-1 p-6 md:p-8">
<div className="mx-auto max-w-7xl">
<Outlet />
</div>
</main>
<Footer />
</SidebarInset>
</SidebarProvider>
)
}
export default Layout