Fork Horse repository, listed by its owner. Publisher snapshot.
packages/forkhorse-dashboard/src/freshness.tsx
Snapshot at 34e3175
1import type { ReactNode } from "react";
2import { Button, Tooltip } from "@cloudflare/kumo";
3import type { ViewModel } from "./model.ts";
4import { relativeTime } from "./time.ts";
5
6export function snapshotAge(view: ViewModel, now = Date.now()): string {
7 const age = view.updatedAt && relativeTime(view.updatedAt, now);
8 return age === "Just now" ? "just now" : age || "an unknown time";
9}
10
11export function staleReason(view: ViewModel): string | undefined {
12 return view.stale ? `Refresh first. This data is from ${snapshotAge(view)}.` : undefined;
13}
14
15/** Disabled controls still have a focusable tooltip target, including in open dialogs. */
16export function WriteHint({ reason, children }: { reason?: string; children: ReactNode }) {
17 return reason ? (
18 <Tooltip content={reason} render={<span className="write-hint" tabIndex={0} />}>
19 {children}
20 </Tooltip>
21 ) : <>{children}</>;
22}
23
24export function FreshnessStamp({ view, retry }: { view: ViewModel; retry?: () => Promise<void> }) {
25 if (!view.state || !view.updatedAt) return null;
26 const time = <Tooltip content={new Date(view.updatedAt).toLocaleString()} render={<time dateTime={view.updatedAt} />}>
27 {snapshotAge(view)}
28 </Tooltip>;
29 // A retry of a failed read stays stale, with its time visible, until it succeeds.
30 return <div className="freshness-stamp caption muted" data-freshness="stamp">
31 {view.stale ? <>
32 <span role="status">Couldn't refresh. Last updated {time}.</span>
33 {retry && <Button variant="ghost" size="sm" disabled={view.refreshing || view.busy}
34 onClick={() => void retry()}>Try again</Button>}
35 </> : view.refreshing ? <Tooltip content={`Updated ${snapshotAge(view)} · ${new Date(view.updatedAt).toLocaleString()}`} render={<span />}>
36 Refreshing…
37 </Tooltip> : <span>Updated {time}</span>}
38 </div>;
39}
40