From 213e33794001cbc5cac3b6e09b1b784f371aee92 Mon Sep 17 00:00:00 2001 From: Usman Baig Date: Thu, 12 Feb 2026 08:50:10 +0100 Subject: [PATCH] refactor: optimize data loading and auto-refresh logic in dashboard components, and enhance performance with useCallback for better efficiency --- app/share/[id]/page.tsx | 41 +++++++++++++++++----------------- app/sites/[id]/page.tsx | 37 ++++++++++-------------------- components/dashboard/Chart.tsx | 4 ++-- 3 files changed, 34 insertions(+), 48 deletions(-) diff --git a/app/share/[id]/page.tsx b/app/share/[id]/page.tsx index 4ea7408..912f158 100644 --- a/app/share/[id]/page.tsx +++ b/app/share/[id]/page.tsx @@ -1,6 +1,6 @@ 'use client' -import { useEffect, useState } from 'react' +import { useCallback, 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 { toast } from '@ciphera-net/ui' @@ -80,29 +80,13 @@ export default function PublicDashboardPage() { } } - // * Auto-refresh interval: chart, KPIs, and realtime count update every 30 seconds - useEffect(() => { - const interval = setInterval(() => { - if (data && !isPasswordProtected) { - loadDashboard(true) - loadRealtime() - } - }, 30000) - - return () => clearInterval(interval) - }, [data, isPasswordProtected, dateRange, todayInterval, multiDayInterval, password]) - // * Tick every 1s so "Live · Xs ago" counts in real time useEffect(() => { const interval = setInterval(() => setTick((t) => t + 1), 1000) return () => clearInterval(interval) }, []) - useEffect(() => { - loadDashboard() - }, [siteId, dateRange, todayInterval, multiDayInterval]) - - const loadRealtime = async () => { + const loadRealtime = useCallback(async () => { try { const auth = { password, @@ -122,9 +106,9 @@ export default function PublicDashboardPage() { } catch (error) { // Silently fail for realtime updates } - } + }, [siteId, password, captchaId, captchaSolution, captchaToken, data]) - const loadDashboard = async (silent = false) => { + const loadDashboard = useCallback(async (silent = false) => { try { if (!silent) setLoading(true) @@ -186,7 +170,22 @@ export default function PublicDashboardPage() { } finally { if (!silent) setLoading(false) } - } + }, [siteId, dateRange, todayInterval, multiDayInterval, password, captchaId, captchaSolution, captchaToken]) + + // * Auto-refresh interval: chart, KPIs, and realtime count update every 30 seconds + useEffect(() => { + if (data && !isPasswordProtected) { + const interval = setInterval(() => { + loadDashboard(true) + loadRealtime() + }, 30000) + return () => clearInterval(interval) + } + }, [data, isPasswordProtected, dateRange, todayInterval, multiDayInterval, password, loadDashboard, loadRealtime]) + + useEffect(() => { + loadDashboard() + }, [siteId, dateRange, todayInterval, multiDayInterval, loadDashboard]) const handlePasswordSubmit = (e: React.FormEvent) => { e.preventDefault() diff --git a/app/sites/[id]/page.tsx b/app/sites/[id]/page.tsx index bd191cf..85710cb 100644 --- a/app/sites/[id]/page.tsx +++ b/app/sites/[id]/page.tsx @@ -1,7 +1,7 @@ 'use client' import { useAuth } from '@/lib/auth/context' -import { useEffect, useState } from 'react' +import { useCallback, useEffect, useState } from 'react' import { useParams, useRouter } from 'next/navigation' import { motion } from 'framer-motion' import { getSite, type Site } from '@/lib/api/sites' @@ -128,15 +128,13 @@ export default function SiteDashboardPage() { }, [todayInterval, multiDayInterval, isSettingsLoaded]) // dateRange is handled in saveSettings/onChange useEffect(() => { - if (isSettingsLoaded) { - loadData() - } + if (isSettingsLoaded) loadData() const interval = setInterval(() => { loadData(true) loadRealtime() - }, 30000) // * Chart, KPIs, and realtime count update every 30 seconds + }, 30000) return () => clearInterval(interval) - }, [siteId, dateRange, todayInterval, multiDayInterval, isSettingsLoaded]) + }, [siteId, dateRange, todayInterval, multiDayInterval, isSettingsLoaded, loadData, loadRealtime]) // * Tick every 1s so "Live · Xs ago" counts in real time useEffect(() => { @@ -144,31 +142,20 @@ export default function SiteDashboardPage() { return () => clearInterval(interval) }, []) - const getPreviousDateRange = (start: string, end: string) => { + const getPreviousDateRange = useCallback((start: string, end: string) => { const startDate = new Date(start) const endDate = new Date(end) const duration = endDate.getTime() - startDate.getTime() - - // * If duration is 0 (Today), set previous range to yesterday if (duration === 0) { const prevEnd = new Date(startDate.getTime() - 24 * 60 * 60 * 1000) - const prevStart = prevEnd - return { - start: prevStart.toISOString().split('T')[0], - end: prevEnd.toISOString().split('T')[0] - } + return { start: prevEnd.toISOString().split('T')[0], end: prevEnd.toISOString().split('T')[0] } } - const prevEnd = new Date(startDate.getTime() - 24 * 60 * 60 * 1000) const prevStart = new Date(prevEnd.getTime() - duration) - - return { - start: prevStart.toISOString().split('T')[0], - end: prevEnd.toISOString().split('T')[0] - } - } + return { start: prevStart.toISOString().split('T')[0], end: prevEnd.toISOString().split('T')[0] } + }, []) - const loadData = async (silent = false) => { + const loadData = useCallback(async (silent = false) => { try { if (!silent) setLoading(true) const interval = dateRange.start === dateRange.end ? todayInterval : multiDayInterval @@ -217,16 +204,16 @@ export default function SiteDashboardPage() { } finally { if (!silent) setLoading(false) } - } + }, [siteId, dateRange, todayInterval, multiDayInterval]) - const loadRealtime = async () => { + const loadRealtime = useCallback(async () => { try { const data = await getRealtime(siteId) setRealtime(data.visitors) } catch (error) { // Silently fail for realtime updates } - } + }, [siteId]) if (loading) { return diff --git a/components/dashboard/Chart.tsx b/components/dashboard/Chart.tsx index 85c104b..89ff932 100644 --- a/components/dashboard/Chart.tsx +++ b/components/dashboard/Chart.tsx @@ -650,8 +650,8 @@ export default function Chart({ strokeDasharray="4 4" strokeOpacity={0.7} label={{ - value: `Avg: ${metric === 'bounce_rate' ? `${Math.round(avg)}%` : metric === 'avg_duration' ? formatDuration(avg) : formatAxisValue(avg)}`, - position: 'right', + value: `Avg: ${metric === 'bounce_rate' ? `${Math.round(avg)}%` : metric === 'avg_duration' ? formatAxisDuration(avg) : formatAxisValue(avg)}`, + position: 'insideTopRight', fill: colors.axis, fontSize: 11, }}