diff --git a/app/share/[id]/page.tsx b/app/share/[id]/page.tsx
index 087f558..dae6587 100644
--- a/app/share/[id]/page.tsx
+++ b/app/share/[id]/page.tsx
@@ -3,7 +3,6 @@
import { useEffect, useState } from 'react'
import { useParams, useSearchParams, useRouter } from 'next/navigation'
import { getPublicDashboard, getPublicStats, getPublicDailyStats, getPublicRealtime, getPublicPerformanceByPage, type DashboardData, type Stats, type DailyStat, type PerformanceByPageStat } from '@/lib/api/stats'
-import { formatUpdatedAgo } from '@/lib/utils/format'
import { toast } from '@ciphera-net/ui'
import { getAuthErrorMessage } from '@/lib/utils/authErrors'
import { LoadingOverlay, Button } from '@ciphera-net/ui'
@@ -293,22 +292,15 @@ export default function PublicDashboardPage() {
- {/* Realtime Indicator & Polling - Desktop */}
-
-
-
-
-
-
-
- {realtime_visitors} current visitors
-
-
- {lastUpdatedAt !== null && (
-
- Updated {formatUpdatedAgo(lastUpdatedAt)}
-
- )}
+ {/* Realtime Indicator - Desktop */}
+
+
+
+
+
+
+ {realtime_visitors} current visitors
+
@@ -388,6 +380,7 @@ export default function PublicDashboardPage() {
setTodayInterval={setTodayInterval}
multiDayInterval={multiDayInterval}
setMultiDayInterval={setMultiDayInterval}
+ lastUpdatedAt={lastUpdatedAt}
/>
diff --git a/app/sites/[id]/page.tsx b/app/sites/[id]/page.tsx
index 676f9d4..ac9bd4c 100644
--- a/app/sites/[id]/page.tsx
+++ b/app/sites/[id]/page.tsx
@@ -6,7 +6,7 @@ import { useParams, useRouter } from 'next/navigation'
import { motion } from 'framer-motion'
import { getSite, type Site } from '@/lib/api/sites'
import { getStats, getRealtime, getDailyStats, getTopPages, getTopReferrers, getCountries, getCities, getRegions, getBrowsers, getOS, getDevices, getScreenResolutions, getEntryPages, getExitPages, getDashboard, getCampaigns, getPerformanceByPage, type Stats, type DailyStat, type PerformanceByPageStat } from '@/lib/api/stats'
-import { formatNumber, formatDuration, formatUpdatedAgo, getDateRange } from '@/lib/utils/format'
+import { formatNumber, formatDuration, getDateRange } from '@/lib/utils/format'
import { toast } from '@ciphera-net/ui'
import { getAuthErrorMessage } from '@/lib/utils/authErrors'
import { LoadingOverlay, Button } from '@ciphera-net/ui'
@@ -259,27 +259,19 @@ export default function SiteDashboardPage() {
-
- {/* Realtime Indicator */}
-
- {/* Polling indicator */}
- {lastUpdatedAt !== null && (
-
- Updated {formatUpdatedAgo(lastUpdatedAt)}
-
- )}
-
+ {/* Realtime Indicator */}
+
@@ -379,6 +371,7 @@ export default function SiteDashboardPage() {
setTodayInterval={setTodayInterval}
multiDayInterval={multiDayInterval}
setMultiDayInterval={setMultiDayInterval}
+ lastUpdatedAt={lastUpdatedAt}
/>
diff --git a/components/dashboard/Chart.tsx b/components/dashboard/Chart.tsx
index b0c74ff..b94e468 100644
--- a/components/dashboard/Chart.tsx
+++ b/components/dashboard/Chart.tsx
@@ -13,7 +13,7 @@ import {
ReferenceLine,
} from 'recharts'
import type { TooltipProps } from 'recharts'
-import { formatNumber, formatDuration } from '@/lib/utils/format'
+import { formatNumber, formatDuration, formatUpdatedAgo } from '@/lib/utils/format'
import { ArrowUpRightIcon, ArrowDownRightIcon, BarChartIcon, Select, Button, DownloadIcon } from '@ciphera-net/ui'
import { Checkbox } from '@ciphera-net/ui'
@@ -69,6 +69,8 @@ interface ChartProps {
setMultiDayInterval: (interval: 'hour' | 'day') => void
/** Optional: callback when user requests chart export (parent can open ExportModal or handle export) */
onExportChart?: () => void
+ /** Optional: timestamp of last data fetch for "Live · Xs ago" indicator */
+ lastUpdatedAt?: number | null
}
type MetricType = 'pageviews' | 'visitors' | 'bounce_rate' | 'avg_duration'
@@ -263,6 +265,7 @@ export default function Chart({
multiDayInterval,
setMultiDayInterval,
onExportChart,
+ lastUpdatedAt,
}: ChartProps) {
const [metric, setMetric] = useState('visitors')
const [showComparison, setShowComparison] = useState(false)
@@ -406,10 +409,19 @@ export default function Chart({
return (
+ {/* * Subtle live/updated indicator in bottom-right corner */}
+ {lastUpdatedAt != null && (
+
+ Live · {formatUpdatedAgo(lastUpdatedAt)}
+
+ )}
{/* Stats Header (Interactive Tabs) */}
{metrics.map((item) => (