refactor(web): split AnalyticsView into analytics-view module
- 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>
2026-02-06 17:33:42 +00:00
|
|
|
import React from 'react';
|
|
|
|
|
import { Card } from '@/components/ui/card';
|
|
|
|
|
import { Globe } from 'lucide-react';
|
|
|
|
|
import { cn } from '@/lib/utils';
|
|
|
|
|
import type { TrafficSource } from './types';
|
|
|
|
|
|
|
|
|
|
interface AnalyticsViewOriginsProps {
|
|
|
|
|
sources: TrafficSource[];
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function AnalyticsViewOrigins({ sources }: AnalyticsViewOriginsProps) {
|
|
|
|
|
return (
|
|
|
|
|
<Card variant="glass" className="p-6 bg-black/40 border-white/5">
|
|
|
|
|
<h3 className="font-bold text-white mb-6 text-sm uppercase tracking-widest flex items-center gap-2">
|
|
|
|
|
<Globe className="w-4 h-4 text-magenta-500" /> Origins
|
|
|
|
|
</h3>
|
|
|
|
|
<div className="space-y-5">
|
|
|
|
|
{sources.map((src) => (
|
|
|
|
|
<div key={src.label} className="group cursor-default">
|
|
|
|
|
<div className="flex justify-between text-xs mb-2">
|
|
|
|
|
<span className="text-muted-foreground group-hover:text-white transition-colors uppercase font-mono">
|
|
|
|
|
{src.label}
|
|
|
|
|
</span>
|
|
|
|
|
<span className="text-white font-bold">{src.val}%</span>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="h-1.5 bg-white/5 rounded-full overflow-hidden">
|
|
|
|
|
<div
|
2026-02-07 08:44:52 +00:00
|
|
|
className={cn('h-full transition-all duration-1000', src.color.replace(/^bg-kodo-/, 'bg-'))}
|
refactor(web): split AnalyticsView into analytics-view module
- 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>
2026-02-06 17:33:42 +00:00
|
|
|
style={{ width: `${src.val}%` }}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
|
|
|
|
);
|
|
|
|
|
}
|