diff --git a/frontend/src/components/repeater/repeaterPaneShared.tsx b/frontend/src/components/repeater/repeaterPaneShared.tsx
index 3fad438..4dc182d 100644
--- a/frontend/src/components/repeater/repeaterPaneShared.tsx
+++ b/frontend/src/components/repeater/repeaterPaneShared.tsx
@@ -80,6 +80,28 @@ export function formatAdvertInterval(val: string | null): string {
return `${trimmed}h`;
}
+function formatFetchedRelative(fetchedAt: number): string {
+ const elapsedSeconds = Math.max(0, Math.floor((Date.now() - fetchedAt) / 1000));
+
+ if (elapsedSeconds < 60) return 'Just now';
+
+ const elapsedMinutes = Math.floor(elapsedSeconds / 60);
+ if (elapsedMinutes < 60) {
+ return `${elapsedMinutes} minute${elapsedMinutes === 1 ? '' : 's'} ago`;
+ }
+
+ const elapsedHours = Math.floor(elapsedMinutes / 60);
+ return `${elapsedHours} hour${elapsedHours === 1 ? '' : 's'} ago`;
+}
+
+function formatFetchedTime(fetchedAt: number): string {
+ return new Date(fetchedAt).toLocaleTimeString([], {
+ hour: 'numeric',
+ minute: '2-digit',
+ second: '2-digit',
+ });
+}
+
// --- Generic Pane Wrapper ---
export function RepeaterPane({
@@ -99,10 +121,22 @@ export function RepeaterPane({
className?: string;
contentClassName?: string;
}) {
+ const fetchedAt = state.fetched_at ?? null;
+
return (
-
{title}
+
+
{title}
+ {fetchedAt && (
+
+ Fetched {formatFetchedTime(fetchedAt)} ({formatFetchedRelative(fetchedAt)})
+
+ )}
+
{onRefresh && (