From 7cf94aa03f31fc1738ba9e9a19e965f279bc4e7c Mon Sep 17 00:00:00 2001 From: HunterSThompson Date: Thu, 14 May 2026 23:09:35 +0000 Subject: [PATCH] feat(web): show sidebar last updated status --- .../web/src/components/layout/Sidebar.tsx | 41 +++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/packages/web/src/components/layout/Sidebar.tsx b/packages/web/src/components/layout/Sidebar.tsx index 4615861..f4b6786 100644 --- a/packages/web/src/components/layout/Sidebar.tsx +++ b/packages/web/src/components/layout/Sidebar.tsx @@ -1,3 +1,4 @@ +import { useQueryClient } from "@tanstack/react-query"; import { Link, useMatchRoute } from "@tanstack/react-router"; import { AnimatePresence, motion } from "framer-motion"; import { @@ -39,6 +40,42 @@ const WORKSPACE_SECTIONS = [ { label: "Webhooks", icon: Webhook, section: "webhooks" }, ] as const; +function formatLastUpdated(value: number | null, now = Date.now()): string { + if (!value) return "Not updated yet"; + const elapsed = Math.max(0, now - value); + if (elapsed < 10_000) return "Updated just now"; + if (elapsed < 60_000) return `Updated ${Math.floor(elapsed / 1000)}s ago`; + if (elapsed < 3_600_000) return `Updated ${Math.floor(elapsed / 60_000)}m ago`; + return `Updated ${Math.floor(elapsed / 3_600_000)}h ago`; +} + +function useLastDataUpdate(): string { + const queryClient = useQueryClient(); + const [updatedAt, setUpdatedAt] = useState(null); + const [now, setNow] = useState(() => Date.now()); + + useEffect(() => { + function refresh() { + setNow(Date.now()); + const latest = queryClient + .getQueryCache() + .getAll() + .reduce((max, query) => Math.max(max, query.state.dataUpdatedAt || 0), 0); + setUpdatedAt(latest || null); + } + + refresh(); + const unsubscribe = queryClient.getQueryCache().subscribe(refresh); + const interval = window.setInterval(refresh, 30_000); + return () => { + unsubscribe(); + window.clearInterval(interval); + }; + }, [queryClient]); + + return formatLastUpdated(updatedAt, now); +} + export function Sidebar() { const matchRoute = useMatchRoute(); const { instances, active, activate } = useInstances(); @@ -46,6 +83,7 @@ export function Sidebar() { const { demo, toggle: toggleDemo, mask } = useDemo(); const { showMetadata, toggle: toggleMeta } = useMetadata(); const { data: health } = useHealthStatus(); + const lastUpdated = useLastDataUpdate(); const [switcherOpen, setSwitcherOpen] = useState(false); const switcherRef = useRef(null); @@ -121,6 +159,9 @@ export function Sidebar() {

{mask(active.baseUrl.replace(/^https?:\/\//, ""))}

+

+ {lastUpdated} +

{instances.length > 1 && (