feat: Add 1 min and 1 hour interval selection for Today view in analytics dashboard
This commit is contained in:
@@ -41,6 +41,7 @@ export default function PublicDashboardPage() {
|
||||
// Date range state
|
||||
const [dateRange, setDateRange] = useState(getDateRange(30))
|
||||
const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
|
||||
const [todayInterval, setTodayInterval] = useState<'minute' | 'hour'>('hour')
|
||||
|
||||
// Auto-refresh interval (for realtime)
|
||||
useEffect(() => {
|
||||
@@ -56,7 +57,7 @@ export default function PublicDashboardPage() {
|
||||
|
||||
useEffect(() => {
|
||||
loadDashboard()
|
||||
}, [siteId, dateRange])
|
||||
}, [siteId, dateRange, todayInterval])
|
||||
|
||||
const loadDashboard = async (silent = false) => {
|
||||
try {
|
||||
@@ -67,7 +68,7 @@ export default function PublicDashboardPage() {
|
||||
dateRange.start,
|
||||
dateRange.end,
|
||||
10,
|
||||
dateRange.start === dateRange.end ? 'hour' : 'day',
|
||||
dateRange.start === dateRange.end ? todayInterval : 'day',
|
||||
password
|
||||
)
|
||||
|
||||
@@ -224,6 +225,17 @@ export default function PublicDashboardPage() {
|
||||
{ value: 'custom', label: 'Custom' },
|
||||
]}
|
||||
/>
|
||||
{dateRange.start === new Date().toISOString().split('T')[0] && dateRange.end === new Date().toISOString().split('T')[0] && (
|
||||
<Select
|
||||
value={todayInterval}
|
||||
onChange={(value) => setTodayInterval(value as 'minute' | 'hour')}
|
||||
options={[
|
||||
{ value: 'minute', label: '1 min' },
|
||||
{ value: 'hour', label: '1 hour' },
|
||||
]}
|
||||
className="min-w-[100px]"
|
||||
/>
|
||||
)}
|
||||
{/* Powered by Ciphera Badge */}
|
||||
<a
|
||||
href="https://ciphera.net"
|
||||
@@ -255,7 +267,7 @@ export default function PublicDashboardPage() {
|
||||
data={safeDailyStats}
|
||||
prevData={[]}
|
||||
stats={safeStats}
|
||||
interval={dateRange.start === dateRange.end ? 'hour' : 'day'}
|
||||
interval={dateRange.start === dateRange.end ? todayInterval : 'day'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -42,6 +42,7 @@ export default function SiteDashboardPage() {
|
||||
const [performance, setPerformance] = useState<{ lcp: number, cls: number, inp: number }>({ lcp: 0, cls: 0, inp: 0 })
|
||||
const [dateRange, setDateRange] = useState(getDateRange(30))
|
||||
const [isDatePickerOpen, setIsDatePickerOpen] = useState(false)
|
||||
const [todayInterval, setTodayInterval] = useState<'minute' | 'hour'>('hour')
|
||||
|
||||
useEffect(() => {
|
||||
loadData()
|
||||
@@ -49,7 +50,7 @@ export default function SiteDashboardPage() {
|
||||
loadRealtime()
|
||||
}, 30000) // Update every 30 seconds
|
||||
return () => clearInterval(interval)
|
||||
}, [siteId, dateRange])
|
||||
}, [siteId, dateRange, todayInterval])
|
||||
|
||||
const getPreviousDateRange = (start: string, end: string) => {
|
||||
const startDate = new Date(start)
|
||||
@@ -78,7 +79,7 @@ export default function SiteDashboardPage() {
|
||||
const loadData = async () => {
|
||||
try {
|
||||
setLoading(true)
|
||||
const interval = dateRange.start === dateRange.end ? 'hour' : 'day'
|
||||
const interval = dateRange.start === dateRange.end ? todayInterval : 'day'
|
||||
|
||||
const [data, prevStatsData, prevDailyStatsData] = await Promise.all([
|
||||
getDashboard(siteId, dateRange.start, dateRange.end, 10, interval),
|
||||
@@ -195,6 +196,17 @@ export default function SiteDashboardPage() {
|
||||
{ value: 'custom', label: 'Custom' },
|
||||
]}
|
||||
/>
|
||||
{dateRange.start === new Date().toISOString().split('T')[0] && dateRange.end === new Date().toISOString().split('T')[0] && (
|
||||
<Select
|
||||
value={todayInterval}
|
||||
onChange={(value) => setTodayInterval(value as 'minute' | 'hour')}
|
||||
options={[
|
||||
{ value: 'minute', label: '1 min' },
|
||||
{ value: 'hour', label: '1 hour' },
|
||||
]}
|
||||
className="min-w-[100px]"
|
||||
/>
|
||||
)}
|
||||
<button
|
||||
onClick={() => router.push(`/sites/${siteId}/settings`)}
|
||||
className="btn-secondary text-sm"
|
||||
@@ -212,7 +224,7 @@ export default function SiteDashboardPage() {
|
||||
prevData={prevDailyStats}
|
||||
stats={stats}
|
||||
prevStats={prevStats}
|
||||
interval={dateRange.start === dateRange.end ? 'hour' : 'day'}
|
||||
interval={dateRange.start === dateRange.end ? todayInterval : 'day'}
|
||||
/>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -33,7 +33,7 @@ interface ChartProps {
|
||||
prevData?: DailyStat[]
|
||||
stats: Stats
|
||||
prevStats?: Stats
|
||||
interval: 'hour' | 'day' | 'month'
|
||||
interval: 'minute' | 'hour' | 'day' | 'month'
|
||||
}
|
||||
|
||||
type MetricType = 'pageviews' | 'visitors' | 'bounce_rate' | 'avg_duration'
|
||||
@@ -47,8 +47,18 @@ export default function Chart({ data, prevData, stats, prevStats, interval }: Ch
|
||||
// * For more robustness, we could match by relative index
|
||||
const prevItem = prevData && prevData[i]
|
||||
|
||||
// * Format date based on interval
|
||||
let formattedDate: string
|
||||
if (interval === 'minute') {
|
||||
formattedDate = new Date(item.date).toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' })
|
||||
} else if (interval === 'hour') {
|
||||
formattedDate = new Date(item.date).toLocaleTimeString('en-US', { hour: 'numeric', minute: 'numeric' })
|
||||
} else {
|
||||
formattedDate = new Date(item.date).toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
|
||||
}
|
||||
|
||||
return {
|
||||
date: new Date(item.date).toLocaleDateString('en-US', interval === 'hour' ? { hour: 'numeric', minute: 'numeric' } : { month: 'short', day: 'numeric' }),
|
||||
date: formattedDate,
|
||||
originalDate: item.date,
|
||||
pageviews: item.pageviews,
|
||||
visitors: item.visitors,
|
||||
|
||||
Reference in New Issue
Block a user