Let a Home tile lead where its figure does, and drop the logo nobody saw

`Tile` takes an optional link: Nodes and Flows running to /flows, Runs
running to /runs, Failures down to the list further down the same page.
Queue backlog and loop lag have no screen of their own and stay plain.

`Logo`'s responsive variant carried a second `<img>` for the collapsed
rail, but AppSidebar hides the whole logo when collapsed, so an ancestor
`display: none` meant it never rendered in any state.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KYM38KSb4V4v2T71eifnZv
This commit is contained in:
2026-08-30 17:18:01 +02:00
co-authored by Claude Opus 5
parent 33d3e71b20
commit 671452e5eb
3 changed files with 49 additions and 37 deletions
+17 -32
View File
@@ -19,38 +19,23 @@ export function Logo({
const { resolvedTheme } = useTheme() const { resolvedTheme } = useTheme()
const src = resolvedTheme === "dark" ? logoDark : logo const src = resolvedTheme === "dark" ? logoDark : logo
// The mark and the wordmark live in the same SVG, so the collapsed sidebar // The mark and the wordmark live in the same SVG. The collapsed sidebar
// shows the same asset cropped to a square rather than a separate icon file. // hides the logo entirely (see AppSidebar), so "responsive" only needs to
const content = // hide itself the same way, not carry a second, cropped asset.
variant === "responsive" ? ( const content = (
<> <img
<img src={src}
src={src} alt="Fluksio"
alt="Fluksio" className={cn(
className={cn( variant === "full"
"h-6 w-auto group-data-[collapsible=icon]:hidden", ? "h-6 w-auto"
className, : variant === "responsive"
)} ? "h-6 w-auto group-data-[collapsible=icon]:hidden"
/> : "size-5 object-contain",
<img className,
src={src} )}
alt="Fluksio" />
className={cn( )
"size-5 object-contain hidden group-data-[collapsible=icon]:block",
className,
)}
/>
</>
) : (
<img
src={src}
alt="Fluksio"
className={cn(
variant === "full" ? "h-6 w-auto" : "size-5 object-contain",
className,
)}
/>
)
if (!asLink) { if (!asLink) {
return content return content
@@ -375,7 +375,8 @@ export function HealthActivity({ range }: { range: Range }) {
</div> </div>
</div> </div>
<div className="grid content-start gap-3"> {/* `id`: what the Failures tile on the overview above scrolls down to. */}
<div id="failures" className="grid content-start gap-3">
<ListHeader title="Failures" moment={failuresAt} /> <ListHeader title="Failures" moment={failuresAt} />
<div <div
className={cn( className={cn(
@@ -1,10 +1,11 @@
import { useQuery } from "@tanstack/react-query" import { useQuery } from "@tanstack/react-query"
import { Link } from "@tanstack/react-router"
import { type Range, RangePicker } from "@/components/Common/RangePicker" import { type Range, RangePicker } from "@/components/Common/RangePicker"
import { PANEL_SECTION } from "@/components/Flow/SidePanel" import { PANEL_SECTION } from "@/components/Flow/SidePanel"
import { runOverviewQueryOptions } from "@/components/Runs/queries" import { runOverviewQueryOptions } from "@/components/Runs/queries"
import { Badge } from "@/components/ui/badge" import { Badge } from "@/components/ui/badge"
import { dur, si } from "@/lib/utils" import { cn, dur, si } from "@/lib/utils"
import { import {
ago, ago,
CARD, CARD,
@@ -17,19 +18,35 @@ function Tile({
label, label,
value, value,
note, note,
to,
hash,
}: { }: {
label: string label: string
value: string value: string
note?: string note?: string
/** Where this figure is explained further, if anywhere obvious. */
to?: string
hash?: string
}) { }) {
return ( const body = (
<div className={CARD}> <>
<div className={PANEL_SECTION}>{label}</div> <div className={PANEL_SECTION}>{label}</div>
<div className="mt-1 text-2xl">{value}</div> <div className="mt-1 text-2xl">{value}</div>
{note ? ( {note ? (
<div className="text-xs text-muted-foreground">{note}</div> <div className="text-xs text-muted-foreground">{note}</div>
) : null} ) : null}
</div> </>
)
return to ? (
<Link
to={to}
hash={hash}
className={cn(CARD, "transition-colors hover:bg-accent/50")}
>
{body}
</Link>
) : (
<div className={CARD}>{body}</div>
) )
} }
@@ -91,10 +108,14 @@ export function HealthOverview({
? `${summary.nodes.error} failed to load` ? `${summary.nodes.error} failed to load`
: "all loaded" : "all loaded"
} }
// Nodes belong to flows, not a list of their own — the flows table
// is where a count and a load error are read node by node.
to="/flows"
/> />
<Tile <Tile
label="Flows running" label="Flows running"
value={`${summary?.flows.running ?? 0}/${summary?.flows.total ?? 0}`} value={`${summary?.flows.running ?? 0}/${summary?.flows.total ?? 0}`}
to="/flows"
// Worst first, but all of them: a quarantine used to hide the // Worst first, but all of them: a quarantine used to hide the
// flows validation blocks, so the tile disagreed with the count // flows validation blocks, so the tile disagreed with the count
// beside it. Zero and undefined both drop out of the filter. // beside it. Zero and undefined both drop out of the filter.
@@ -114,6 +135,7 @@ export function HealthOverview({
label="Runs running" label="Runs running"
value={String(running)} value={String(running)}
note={queued ? `${queued} queued` : "none queued"} note={queued ? `${queued} queued` : "none queued"}
to="/runs"
/> />
) : null} ) : null}
<Tile <Tile
@@ -128,6 +150,10 @@ export function HealthOverview({
? `latest ${ago(failures[0].ts)}` ? `latest ${ago(failures[0].ts)}`
: "nothing recorded" : "nothing recorded"
} }
// Down to the failures list further down this same page, rather
// than a route of its own.
to="/"
hash="failures"
/> />
{/* Backlog leads: what is waiting is what says the engine is {/* Backlog leads: what is waiting is what says the engine is
behind. `pending` is work already running, which reads as idle behind. `pending` is work already running, which reads as idle