feat: add previous period statistics and enhance data loading in PublicDashboardPage

This commit is contained in:
Usman Baig
2026-01-27 21:23:11 +01:00
parent 5dd785d2be
commit d9c3294eeb
2 changed files with 156 additions and 17 deletions

View File

@@ -2,7 +2,7 @@
import { useEffect, useState } from 'react'
import { useParams, useSearchParams, useRouter } from 'next/navigation'
import { getPublicDashboard, type DashboardData } from '@/lib/api/stats'
import { getPublicDashboard, getPublicStats, getPublicDailyStats, getPublicRealtime, getPublicPerformanceByPage, type DashboardData, type Stats, type DailyStat, type PerformanceByPageStat } from '@/lib/api/stats'
import { toast } from 'sonner'
import { LoadingOverlay } from '@ciphera-net/ui'
import Chart from '@/components/dashboard/Chart'
@@ -48,14 +48,42 @@ export default function PublicDashboardPage() {
const [todayInterval, setTodayInterval] = useState<'minute' | 'hour'>('hour')
const [multiDayInterval, setMultiDayInterval] = useState<'hour' | 'day'>('day')
// Previous period data
const [prevStats, setPrevStats] = useState<Stats | undefined>(undefined)
const [prevDailyStats, setPrevDailyStats] = useState<DailyStat[] | undefined>(undefined)
const getPreviousDateRange = (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]
}
}
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]
}
}
// Auto-refresh interval (for realtime)
useEffect(() => {
const interval = setInterval(() => {
// Only refresh realtime count if we have data
if (data && !isPasswordProtected) {
loadDashboard(true)
loadRealtime()
}
}, 10000) // 10 seconds
}, 30000) // 30 seconds
return () => clearInterval(interval)
}, [data, isPasswordProtected, dateRange, password])
@@ -64,25 +92,66 @@ export default function PublicDashboardPage() {
loadDashboard()
}, [siteId, dateRange, todayInterval, multiDayInterval])
const loadRealtime = async () => {
try {
const auth = {
password,
captcha: {
captcha_id: captchaId,
captcha_solution: captchaSolution,
captcha_token: captchaToken
}
}
const realtimeData = await getPublicRealtime(siteId, auth)
if (data) {
setData({
...data,
realtime_visitors: realtimeData.visitors
})
}
} catch (error) {
// Silently fail for realtime updates
}
}
const loadDashboard = async (silent = false) => {
try {
if (!silent) setLoading(true)
const dashboardData = await getPublicDashboard(
siteId,
dateRange.start,
dateRange.end,
10,
dateRange.start === dateRange.end ? todayInterval : multiDayInterval,
const interval = dateRange.start === dateRange.end ? todayInterval : multiDayInterval
const auth = {
password,
{
captcha: {
captcha_id: captchaId,
captcha_solution: captchaSolution,
captcha_token: captchaToken
}
)
}
const [dashboardData, prevStatsData, prevDailyStatsData] = await Promise.all([
getPublicDashboard(
siteId,
dateRange.start,
dateRange.end,
10,
interval,
password,
auth.captcha
),
(async () => {
const prevRange = getPreviousDateRange(dateRange.start, dateRange.end)
return getPublicStats(siteId, prevRange.start, prevRange.end, auth)
})(),
(async () => {
const prevRange = getPreviousDateRange(dateRange.start, dateRange.end)
return getPublicDailyStats(siteId, prevRange.start, prevRange.end, interval, auth)
})()
])
setData(dashboardData)
setPrevStats(prevStatsData)
setPrevDailyStats(prevDailyStatsData)
setIsPasswordProtected(false)
// Reset captcha
setCaptchaId('')
@@ -304,8 +373,9 @@ export default function PublicDashboardPage() {
<div className="mb-8">
<Chart
data={safeDailyStats}
prevData={[]}
prevData={prevDailyStats}
stats={safeStats}
prevStats={prevStats}
interval={dateRange.start === dateRange.end ? todayInterval : multiDayInterval}
/>
</div>
@@ -313,7 +383,23 @@ export default function PublicDashboardPage() {
{/* Performance Stats - Only show if enabled */}
{performance && data.site?.enable_performance_insights && (
<div className="mb-8">
<PerformanceStats stats={performance} performanceByPage={performance_by_page} />
<PerformanceStats
stats={performance}
performanceByPage={performance_by_page}
siteId={siteId}
startDate={dateRange.start}
endDate={dateRange.end}
getPerformanceByPage={(siteId, startDate, endDate, opts) => {
return getPublicPerformanceByPage(siteId, startDate, endDate, opts, {
password,
captcha: {
captcha_id: captchaId,
captcha_solution: captchaSolution,
captcha_token: captchaToken
}
})
}}
/>
</div>
)}

View File

@@ -82,6 +82,18 @@ export interface RealtimeStats {
visitors: number
}
export interface AuthParams {
password?: string
captcha?: { captcha_id?: string, captcha_solution?: string, captcha_token?: string }
}
function appendAuthParams(params: URLSearchParams, auth?: AuthParams) {
if (auth?.password) params.append('password', auth.password)
if (auth?.captcha?.captcha_id) params.append('captcha_id', auth.captcha.captcha_id)
if (auth?.captcha?.captcha_solution) params.append('captcha_solution', auth.captcha.captcha_solution)
if (auth?.captcha?.captcha_token) params.append('captcha_token', auth.captcha.captcha_token)
}
export async function getStats(siteId: string, startDate?: string, endDate?: string): Promise<Stats> {
const params = new URLSearchParams()
if (startDate) params.append('start_date', startDate)
@@ -90,10 +102,25 @@ export async function getStats(siteId: string, startDate?: string, endDate?: str
return apiRequest<Stats>(`/sites/${siteId}/stats${query ? `?${query}` : ''}`)
}
export async function getPublicStats(siteId: string, startDate?: string, endDate?: string, auth?: AuthParams): Promise<Stats> {
const params = new URLSearchParams()
if (startDate) params.append('start_date', startDate)
if (endDate) params.append('end_date', endDate)
appendAuthParams(params, auth)
const query = params.toString()
return apiRequest<Stats>(`/public/sites/${siteId}/stats${query ? `?${query}` : ''}`)
}
export async function getRealtime(siteId: string): Promise<RealtimeStats> {
return apiRequest<RealtimeStats>(`/sites/${siteId}/realtime`)
}
export async function getPublicRealtime(siteId: string, auth?: AuthParams): Promise<RealtimeStats> {
const params = new URLSearchParams()
appendAuthParams(params, auth)
return apiRequest<RealtimeStats>(`/public/sites/${siteId}/realtime?${params.toString()}`)
}
export async function getTopPages(siteId: string, startDate?: string, endDate?: string, limit = 10): Promise<TopPage[]> {
const params = new URLSearchParams()
if (startDate) params.append('start_date', startDate)
@@ -166,6 +193,15 @@ export async function getDailyStats(siteId: string, startDate?: string, endDate?
return apiRequest<{ stats: DailyStat[] }>(`/sites/${siteId}/daily?${params.toString()}`).then(r => r?.stats || [])
}
export async function getPublicDailyStats(siteId: string, startDate?: string, endDate?: string, interval?: string, auth?: AuthParams): Promise<DailyStat[]> {
const params = new URLSearchParams()
if (startDate) params.append('start_date', startDate)
if (endDate) params.append('end_date', endDate)
if (interval) params.append('interval', interval)
appendAuthParams(params, auth)
return apiRequest<{ stats: DailyStat[] }>(`/public/sites/${siteId}/daily?${params.toString()}`).then(r => r?.stats || [])
}
export async function getEntryPages(siteId: string, startDate?: string, endDate?: string, limit = 10): Promise<TopPage[]> {
const params = new URLSearchParams()
if (startDate) params.append('start_date', startDate)
@@ -207,6 +243,25 @@ export async function getPerformanceByPage(
return res?.performance_by_page ?? []
}
export async function getPublicPerformanceByPage(
siteId: string,
startDate?: string,
endDate?: string,
opts?: { limit?: number; sort?: 'lcp' | 'cls' | 'inp' },
auth?: AuthParams
): Promise<PerformanceByPageStat[]> {
const params = new URLSearchParams()
if (startDate) params.append('start_date', startDate)
if (endDate) params.append('end_date', endDate)
if (opts?.limit != null) params.append('limit', String(opts.limit))
if (opts?.sort) params.append('sort', opts.sort)
appendAuthParams(params, auth)
const res = await apiRequest<{ performance_by_page: PerformanceByPageStat[] }>(
`/public/sites/${siteId}/performance-by-page?${params.toString()}`
)
return res?.performance_by_page ?? []
}
export interface DashboardData {
site: Site
stats: Stats
@@ -249,10 +304,8 @@ export async function getPublicDashboard(
if (startDate) params.append('start_date', startDate)
if (endDate) params.append('end_date', endDate)
if (interval) params.append('interval', interval)
if (password) params.append('password', password)
if (captcha?.captcha_id) params.append('captcha_id', captcha.captcha_id)
if (captcha?.captcha_solution) params.append('captcha_solution', captcha.captcha_solution)
if (captcha?.captcha_token) params.append('captcha_token', captcha.captcha_token)
appendAuthParams(params, { password, captcha })
params.append('limit', limit.toString())
return apiRequest<DashboardData>(`/public/sites/${siteId}/dashboard?${params.toString()}`)