src/components/elapsed-time.tsx
Linked repository file from this build.
"use client";
import { useEffect, useState } from "react";
export function ElapsedTime({
at,
label = "Last refreshed",
compact = false,
}: {
at: string | null;
label?: string;
compact?: boolean;
}) {
// Keep the server and first client render identical; advance to wall-clock time after mount.
const [now, setNow] = useState(() => (at ? new Date(at).getTime() : 0));
useEffect(() => {
if (!at) return;
const initial = now === new Date(at).getTime();
const elapsed = Math.max(0, Date.now() - new Date(at).getTime());
const delay = initial ? 0 : elapsed < 60_000 ? 1_000 : 60_000;
const timer = setTimeout(() => setNow(Date.now()), delay);
return () => clearTimeout(timer);
}, [at, now]);
if (!at)
return (
<span className="text-xs text-muted-foreground">{compact ? "—" : "Waiting for update…"}</span>
);
const seconds = Math.max(0, Math.floor((now - new Date(at).getTime()) / 1000));
const elapsed =
seconds < 60
? `${seconds}s ago`
: seconds < 3600
? `${Math.floor(seconds / 60)}m ago`
: `${Math.floor(seconds / 3600)}h ago`;
const tooltip = `${label.toLowerCase()} ${new Date(at).toLocaleTimeString("en-US", { hour: "2-digit", minute: "2-digit", second: "2-digit", hour12: true })}`;
return (
<span
tabIndex={0}
aria-label={`${label} ${elapsed}`}
data-tip={tooltip}
className="elapsed-time text-xs text-muted-foreground tabular-nums"
>
{compact ? elapsed : `${label} ${elapsed}`}
</span>
);
}