- types: AnalyticsViewProps, DateRangeKey, GlobalStats, TopTrackRow, TrafficSource, DeviceStats, ChartHoverData - useAnalyticsView: dateRange, stats, topTracks, trafficSources, deviceStats, loading, hoveredData, handleExport - AnalyticsViewHeader, AnalyticsViewKpiGrid, AnalyticsViewChart, AnalyticsViewOrigins, AnalyticsViewPlatforms, AnalyticsViewTopTracks, AnalyticsViewSkeleton - Data via analyticsService; loading renders Skeleton - text-[10px] -> text-xs, tracking-[0.2em] -> tracking-wide - Stories: Default, Loading (Skeleton); decorator min-h-layout-page - Re-export from AnalyticsView.tsx Co-authored-by: Cursor <cursoragent@cursor.com>
70 lines
2.5 KiB
TypeScript
70 lines
2.5 KiB
TypeScript
import { useState, useEffect, useCallback } from 'react';
|
|
import { useToast } from '@/components/feedback/ToastProvider';
|
|
import { analyticsService } from '@/services/analyticsService';
|
|
import { logger } from '@/utils/logger';
|
|
import type { DateRangeKey } from './types';
|
|
import type { GlobalStats, TopTrackRow, TrafficSource, DeviceStats, ChartHoverData } from './types';
|
|
|
|
export function useAnalyticsView(dateRangeInitial: DateRangeKey = '30d') {
|
|
const { addToast } = useToast();
|
|
const [dateRange, setDateRange] = useState<DateRangeKey>(dateRangeInitial);
|
|
const [stats, setStats] = useState<GlobalStats>({});
|
|
const [topTracks, setTopTracks] = useState<TopTrackRow[]>([]);
|
|
const [trafficSources, setTrafficSources] = useState<TrafficSource[]>([]);
|
|
const [deviceStats, setDeviceStats] = useState<DeviceStats>({ mobile: 0, desktop: 0 });
|
|
const [loading, setLoading] = useState(true);
|
|
const [hoveredData, setHoveredData] = useState<ChartHoverData | null>(null);
|
|
|
|
const fetchData = useCallback(async () => {
|
|
setLoading(true);
|
|
try {
|
|
const [global, tracks, sources, devices] = await Promise.all([
|
|
analyticsService.getGlobalStats(dateRange),
|
|
analyticsService.getTopTracks(dateRange),
|
|
analyticsService.getTrafficSources(),
|
|
analyticsService.getDeviceBreakdown(),
|
|
]);
|
|
setStats(global as GlobalStats);
|
|
setTopTracks((tracks as TopTrackRow[]) ?? []);
|
|
setTrafficSources((sources as TrafficSource[]) ?? []);
|
|
setDeviceStats((devices as DeviceStats) ?? { mobile: 0, desktop: 0 });
|
|
} catch (e) {
|
|
logger.error('Error loading analytics', { error: e });
|
|
} finally {
|
|
setLoading(false);
|
|
}
|
|
}, [dateRange]);
|
|
|
|
useEffect(() => {
|
|
fetchData();
|
|
}, [fetchData]);
|
|
|
|
const handleExport = useCallback(
|
|
(format: 'csv' | 'json') => {
|
|
addToast(`Building ${format.toUpperCase()} archive...`, 'info');
|
|
setTimeout(() => {
|
|
const blob = new Blob([JSON.stringify(stats, null, 2)], { type: 'application/json' });
|
|
const url = URL.createObjectURL(blob);
|
|
const a = document.createElement('a');
|
|
a.href = url;
|
|
a.download = `veza-analytics-${dateRange}-${new Date().toISOString().split('T')[0]}.${format}`;
|
|
a.click();
|
|
addToast('Data packet exported successfully', 'success');
|
|
}, 1500);
|
|
},
|
|
[stats, dateRange, addToast]
|
|
);
|
|
|
|
return {
|
|
dateRange,
|
|
setDateRange,
|
|
stats,
|
|
topTracks,
|
|
trafficSources,
|
|
deviceStats,
|
|
loading,
|
|
hoveredData,
|
|
setHoveredData,
|
|
handleExport,
|
|
};
|
|
}
|