'use client' import { useEffect, useState } from 'react' import { useParams, useSearchParams, useRouter } from 'next/navigation' import { getPublicDashboard, type DashboardData } from '@/lib/api/stats' import { toast } from 'sonner' import LoadingOverlay from '@/components/LoadingOverlay' import Chart from '@/components/dashboard/Chart' import TopPages from '@/components/dashboard/ContentStats' import TopReferrers from '@/components/dashboard/TopReferrers' import Locations from '@/components/dashboard/Locations' import TechSpecs from '@/components/dashboard/TechSpecs' import PerformanceStats from '@/components/dashboard/PerformanceStats' import Select from '@/components/ui/Select' import { LightningBoltIcon } from '@radix-ui/react-icons' import DatePickerModal from '@/components/ui/DatePicker' // Helper to get date ranges const getDateRange = (days: number) => { const end = new Date() const start = new Date() start.setDate(end.getDate() - (days - 1)) // -1 because today counts as 1 day return { start: start.toISOString().split('T')[0], end: end.toISOString().split('T')[0] } } export default function PublicDashboardPage() { const params = useParams() const searchParams = useSearchParams() const router = useRouter() const siteId = params.id as string const passwordParam = searchParams.get('password') || undefined const [loading, setLoading] = useState(true) const [data, setData] = useState(null) const [password, setPassword] = useState(passwordParam || '') const [isPasswordProtected, setIsPasswordProtected] = useState(false) // Date range state const [dateRange, setDateRange] = useState(getDateRange(30)) const [isDatePickerOpen, setIsDatePickerOpen] = useState(false) const [todayInterval, setTodayInterval] = useState<'minute' | 'hour'>('hour') const [multiDayInterval, setMultiDayInterval] = useState<'hour' | 'day'>('day') // Auto-refresh interval (for realtime) useEffect(() => { const interval = setInterval(() => { // Only refresh realtime count if we have data if (data && !isPasswordProtected) { loadDashboard(true) } }, 10000) // 10 seconds return () => clearInterval(interval) }, [data, isPasswordProtected, dateRange, password]) useEffect(() => { loadDashboard() }, [siteId, dateRange, todayInterval, multiDayInterval]) 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, password ) setData(dashboardData) setIsPasswordProtected(false) } catch (error: any) { if ((error.status === 401 || error.response?.status === 401) && (error.data?.is_protected || error.response?.data?.is_protected)) { setIsPasswordProtected(true) if (password) { toast.error('Invalid password') } } else if (error.status === 404 || error.response?.status === 404) { toast.error('Site not found') } else if (!silent) { toast.error('Failed to load dashboard: ' + (error.message || 'Unknown error')) } } finally { if (!silent) setLoading(false) } } const handlePasswordSubmit = (e: React.FormEvent) => { e.preventDefault() loadDashboard() } if (loading && !data && !isPasswordProtected) { return } if (isPasswordProtected && !data) { return (

Protected Dashboard

This dashboard is password protected. Please enter the password to view stats.

setPassword(e.target.value)} placeholder="Enter password" className="w-full px-4 py-2 border border-neutral-300 dark:border-neutral-700 rounded-lg bg-white dark:bg-neutral-800 text-neutral-900 dark:text-white focus:ring-2 focus:ring-brand-orange focus:border-transparent" autoFocus />
) } if (!data) return null const { site, stats, daily_stats, top_pages, entry_pages, exit_pages, top_referrers, countries, cities, regions, browsers, os, devices, screen_resolutions, performance, performance_by_page, realtime_visitors } = data // Provide defaults for potentially undefined data const safeDailyStats = daily_stats || [] const safeStats = stats || { pageviews: 0, visitors: 0, bounce_rate: 0, avg_duration: 0 } const safeTopPages = top_pages || [] const safeEntryPages = entry_pages || [] const safeExitPages = exit_pages || [] const safeTopReferrers = top_referrers || [] const safeCountries = countries || [] const safeCities = cities || [] const safeRegions = regions || [] const safeBrowsers = browsers || [] const safeOS = os || [] const safeDevices = devices || [] const safeScreenResolutions = screen_resolutions || [] return (
{/* Header */}
Public Dashboard

{site.name} { (e.target as HTMLImageElement).src = '/globe.svg' }} /> {site.domain}

{/* Realtime Indicator - Desktop */}
{realtime_visitors} current visitors
setTodayInterval(value as 'minute' | 'hour')} options={[ { value: 'minute', label: '1 min' }, { value: 'hour', label: '1 hour' }, ]} className="min-w-[100px]" /> )} {dateRange.start !== dateRange.end && (