2026-01-07 09:31:02 +00:00
|
|
|
import React from 'react';
|
|
|
|
|
import { Card } from '../ui/card';
|
|
|
|
|
import { Button } from '../ui/button';
|
|
|
|
|
import { StatCard } from '../dashboard/StatCard';
|
2026-01-13 18:47:57 +00:00
|
|
|
import {
|
|
|
|
|
Play,
|
|
|
|
|
SkipForward,
|
|
|
|
|
Clock,
|
|
|
|
|
Users,
|
|
|
|
|
Map,
|
|
|
|
|
ArrowLeft,
|
|
|
|
|
Download,
|
|
|
|
|
} from 'lucide-react';
|
2026-01-26 13:12:17 +00:00
|
|
|
import { useToast } from '../../components/feedback/ToastProvider';
|
2026-01-07 09:31:02 +00:00
|
|
|
|
|
|
|
|
interface TrackAnalyticsViewProps {
|
2026-01-13 18:47:57 +00:00
|
|
|
trackId: string;
|
|
|
|
|
onBack: () => void;
|
2026-01-07 09:31:02 +00:00
|
|
|
}
|
|
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
export const TrackAnalyticsView: React.FC<TrackAnalyticsViewProps> = ({
|
|
|
|
|
trackId: _trackId,
|
|
|
|
|
onBack,
|
|
|
|
|
}) => {
|
2026-01-07 09:31:02 +00:00
|
|
|
const { addToast } = useToast();
|
|
|
|
|
|
|
|
|
|
// Mock Track Data
|
|
|
|
|
const trackData = {
|
2026-01-13 18:47:57 +00:00
|
|
|
title: 'Neon Nights',
|
|
|
|
|
artist: 'Cyber_Producer',
|
|
|
|
|
plays: 15420,
|
|
|
|
|
skips: 320,
|
|
|
|
|
avgListen: '2:45', // vs 3:45 total
|
|
|
|
|
completion: 78,
|
|
|
|
|
demographics: { '18-24': 45, '25-34': 30, '35+': 25 },
|
|
|
|
|
geo: [
|
|
|
|
|
{ country: 'USA', percent: 40 },
|
|
|
|
|
{ country: 'Japan', percent: 25 },
|
|
|
|
|
{ country: 'Germany', percent: 15 },
|
|
|
|
|
{ country: 'UK', percent: 10 },
|
|
|
|
|
],
|
2026-01-07 09:31:02 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-8 animate-fadeIn pb-20">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div className="flex items-center justify-between mb-6">
|
|
|
|
|
<div className="flex items-center gap-4">
|
|
|
|
|
<Button variant="ghost" size="icon" onClick={onBack}>
|
|
|
|
|
<ArrowLeft className="w-5 h-5" />
|
|
|
|
|
</Button>
|
|
|
|
|
<div>
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<h2 className="text-2xl font-bold text-foreground">{trackData.title}</h2>
|
ui(tokens): migrate text-kodo-content-dim to text-muted-foreground (35 files, 160 instances)
Replace legacy hardcoded `text-kodo-content-dim` (Gray-400, theme-unaware)
with semantic `text-muted-foreground` across 35 user-facing components.
This ensures all secondary/muted text adapts correctly to light/dark theme
changes instead of staying fixed at a single gray value.
Covers: SearchBar, PlaylistsView, NotificationBell, TrackAnalyticsView,
LiveStreamDetailView, LicenceCard, FilePreviewCard, PasswordStrengthIndicator,
NotificationItem, TrackList, CourseCard, GroupCard, AchievementCard, XPBar,
EquipmentCard, SellerDashboardView, APIPlaygroundView, DeveloperDashboardView,
CreatorModal, AddToPlaylistModal, LicenceDetailsModal, QuizModal,
CertificateModal, FlashSaleModal, CreateAPIKeyModal, LyricsEditorModal,
WatermarkSettingsModal, ProfileXPView, LeaderboardView, PostCard,
ExploreView, FeedView, MessageSearch, TypingIndicator, PlaylistTrackItem.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 23:03:33 +00:00
|
|
|
<p className="text-muted-foreground text-sm">Analytics Report</p>
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
<Button
|
|
|
|
|
variant="secondary"
|
|
|
|
|
icon={<Download className="w-4 h-4" />}
|
|
|
|
|
onClick={() => addToast('Report downloaded')}
|
|
|
|
|
>
|
|
|
|
|
Export CSV
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
{/* Key Metrics */}
|
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6">
|
|
|
|
|
<StatCard
|
|
|
|
|
label="Total Plays"
|
|
|
|
|
value={trackData.plays.toLocaleString()}
|
|
|
|
|
icon={<Play className="w-5 h-5" />}
|
|
|
|
|
color="cyan"
|
|
|
|
|
trend={12}
|
|
|
|
|
sparklineData={[10, 15, 12, 20, 25, 30, 28, 35, 40]}
|
|
|
|
|
/>
|
|
|
|
|
<StatCard
|
|
|
|
|
label="Completion Rate"
|
|
|
|
|
value={`${trackData.completion}%`}
|
|
|
|
|
icon={<Clock className="w-5 h-5" />}
|
|
|
|
|
color="lime"
|
|
|
|
|
trend={2.5}
|
|
|
|
|
/>
|
|
|
|
|
<StatCard
|
|
|
|
|
label="Skip Rate"
|
|
|
|
|
value={`${((trackData.skips / trackData.plays) * 100).toFixed(1)}%`}
|
|
|
|
|
icon={<SkipForward className="w-5 h-5" />}
|
|
|
|
|
color="red"
|
|
|
|
|
trend={-0.5} // Negative is good for skip rate, logic in StatCard handles green/red based on +/-. might need tweak for inverse metrics
|
|
|
|
|
/>
|
|
|
|
|
<StatCard
|
|
|
|
|
label="Avg Listen Time"
|
|
|
|
|
value={trackData.avgListen}
|
|
|
|
|
icon={<Users className="w-5 h-5" />}
|
|
|
|
|
color="gold"
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
<div className="grid grid-cols-1 lg:grid-cols-2 gap-8">
|
|
|
|
|
{/* Plays Over Time Graph Placeholder */}
|
|
|
|
|
<Card variant="default">
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<h3 className="font-bold text-foreground mb-6">
|
2026-01-13 18:47:57 +00:00
|
|
|
Plays Over Time (30 Days)
|
|
|
|
|
</h3>
|
|
|
|
|
<div className="h-64 flex items-end gap-2 px-4 pb-4">
|
|
|
|
|
{Array.from({ length: 30 }).map((_, i) => {
|
|
|
|
|
const h = Math.random() * 100;
|
|
|
|
|
return (
|
|
|
|
|
<div
|
|
|
|
|
key={i}
|
2026-02-08 23:08:42 +00:00
|
|
|
className="flex-1 bg-muted/20 hover:bg-muted/50 transition-colors rounded-t relative group"
|
2026-01-13 18:47:57 +00:00
|
|
|
style={{ height: `${h}%` }}
|
|
|
|
|
>
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<div className="absolute bottom-full left-1/2 -translate-x-1/2 mb-2 bg-background text-foreground text-xs px-2 py-1 rounded opacity-0 group-hover:opacity-100 pointer-events-none">
|
2026-01-13 18:47:57 +00:00
|
|
|
{Math.floor(h * 10)} plays
|
|
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
);
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
2026-01-07 09:31:02 +00:00
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
{/* Demographics & Geo */}
|
|
|
|
|
<div className="space-y-6">
|
|
|
|
|
<Card variant="default">
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<h3 className="font-bold text-foreground mb-4 flex items-center gap-2">
|
refactor: Phase 3a — Global color class migration to SUMI semantics
- Replace all kodo-* color classes across ~100 TSX files:
kodo-void → background, kodo-ink → card, kodo-graphite → muted,
kodo-steel → muted-foreground, kodo-cyan → primary, kodo-magenta → destructive,
kodo-lime → success, kodo-red → destructive, kodo-gold → warning
- Replace cyan-500, magenta-500, lime-500 default Tailwind colors with
semantic equivalents (primary, destructive, success)
- Fix WaveformVisualizer hardcoded hex colors to SUMI values
- Delete global-effects.css (conflicting, redundant with index.css)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 00:51:49 +00:00
|
|
|
<Map className="w-4 h-4 text-destructive" /> Top Locations
|
2026-01-13 18:47:57 +00:00
|
|
|
</h3>
|
aesthetic-improvements: align spacing to 8px grid (Action 11.2.1.3)
- Created automated script (scripts/align-8px-grid.py) to align all spacing to 8px grid
- Replaced non-8px-aligned spacing: gap-3/p-3/m-3 (12px) → gap-4/p-4/m-4 (16px), gap-5/p-5/m-5 (20px) → gap-6/p-6/m-6 (24px), gap-10/p-10/m-10 (40px) → gap-12/p-12/m-12 (48px), gap-20/p-20/m-20 (80px) → gap-24/p-24/m-24 (96px)
- Preserved: 4px values (gap-1, p-1, m-1) as they may be intentional fine-tuning, responsive breakpoints (sm:, md:, lg:), test files, documentation
- Modified files across all components to ensure consistent 8px grid alignment
- Action 11.2.1.3: Align all elements to 8px grid - COMPLETE
2026-01-16 10:50:46 +00:00
|
|
|
<div className="space-y-4">
|
2026-01-13 18:47:57 +00:00
|
|
|
{trackData.geo.map((g) => (
|
|
|
|
|
<div key={g.country} className="flex items-center gap-4">
|
ui(tokens): migrate text-kodo-content-dim to text-muted-foreground (35 files, 160 instances)
Replace legacy hardcoded `text-kodo-content-dim` (Gray-400, theme-unaware)
with semantic `text-muted-foreground` across 35 user-facing components.
This ensures all secondary/muted text adapts correctly to light/dark theme
changes instead of staying fixed at a single gray value.
Covers: SearchBar, PlaylistsView, NotificationBell, TrackAnalyticsView,
LiveStreamDetailView, LicenceCard, FilePreviewCard, PasswordStrengthIndicator,
NotificationItem, TrackList, CourseCard, GroupCard, AchievementCard, XPBar,
EquipmentCard, SellerDashboardView, APIPlaygroundView, DeveloperDashboardView,
CreatorModal, AddToPlaylistModal, LicenceDetailsModal, QuizModal,
CertificateModal, FlashSaleModal, CreateAPIKeyModal, LyricsEditorModal,
WatermarkSettingsModal, ProfileXPView, LeaderboardView, PostCard,
ExploreView, FeedView, MessageSearch, TypingIndicator, PlaylistTrackItem.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 23:03:33 +00:00
|
|
|
<div className="w-16 text-sm text-muted-foreground">{g.country}</div>
|
2026-02-08 23:08:42 +00:00
|
|
|
<div className="flex-1 h-2 bg-muted rounded-full overflow-hidden">
|
2026-01-13 18:47:57 +00:00
|
|
|
<div
|
refactor: Phase 3a — Global color class migration to SUMI semantics
- Replace all kodo-* color classes across ~100 TSX files:
kodo-void → background, kodo-ink → card, kodo-graphite → muted,
kodo-steel → muted-foreground, kodo-cyan → primary, kodo-magenta → destructive,
kodo-lime → success, kodo-red → destructive, kodo-gold → warning
- Replace cyan-500, magenta-500, lime-500 default Tailwind colors with
semantic equivalents (primary, destructive, success)
- Fix WaveformVisualizer hardcoded hex colors to SUMI values
- Delete global-effects.css (conflicting, redundant with index.css)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-12 00:51:49 +00:00
|
|
|
className="h-full bg-destructive"
|
2026-01-13 18:47:57 +00:00
|
|
|
style={{ width: `${g.percent}%` }}
|
|
|
|
|
></div>
|
|
|
|
|
</div>
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<div className="w-12 text-right text-sm font-bold text-foreground">
|
2026-01-13 18:47:57 +00:00
|
|
|
{g.percent}%
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
</Card>
|
2026-01-07 09:31:02 +00:00
|
|
|
|
2026-01-13 18:47:57 +00:00
|
|
|
<Card variant="default">
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<h3 className="font-bold text-foreground mb-4 flex items-center gap-2">
|
2026-02-08 23:20:32 +00:00
|
|
|
<Users className="w-4 h-4 text-warning" /> Listeners Age
|
2026-01-13 18:47:57 +00:00
|
|
|
</h3>
|
|
|
|
|
<div className="flex gap-2 h-8">
|
|
|
|
|
{Object.entries(trackData.demographics).map(([range, val]) => (
|
|
|
|
|
<div
|
|
|
|
|
key={range}
|
|
|
|
|
className="h-full first:rounded-l last:rounded-r relative group"
|
|
|
|
|
style={{
|
|
|
|
|
width: `${val}%`,
|
|
|
|
|
backgroundColor:
|
|
|
|
|
range === '18-24'
|
2026-04-27 03:07:24 +00:00
|
|
|
? 'var(--sumi-viz-indigo)'
|
2026-01-13 18:47:57 +00:00
|
|
|
: range === '25-34'
|
2026-04-27 03:07:24 +00:00
|
|
|
? 'var(--sumi-viz-sage)'
|
|
|
|
|
: 'var(--sumi-bg-hover)',
|
2026-01-13 18:47:57 +00:00
|
|
|
}}
|
|
|
|
|
>
|
refactor(tokens): complete design token migration to semantic system
Sprint 3.1 — Default colors → semantic (~15 files, ~99 replacements):
- lime-500 → success, red-500 → destructive, cyan-500 → primary
Sprint 3.2 — Hardcoded colors → semantic (~13 files, ~99 replacements):
- text-white → text-foreground, bg-black → bg-background, bg-white → bg-card
Sprint 3.3 — Legacy kodo-* → semantic (~27 files, ~122 replacements):
- bg-kodo-ink → bg-card, bg-kodo-void → bg-background, text-kodo-steel → text-muted-foreground
- Preserved kodo-cyan/magenta/lime/gold palette accents and gradients
Sprint 3.4 — Arbitrary values → Tailwind scale (5 replacements):
- min-h-[600px] → min-h-layout-page, min-h-[400px] → min-h-layout-page-sm
- left-[50%] → left-1/2, min-h-[80px] → min-h-20, min-h-[40px] → min-h-10
Sprint 3.5 — Border-radius standardization (4 replacements):
- Modal/dialog skeletons: rounded-lg → rounded-xl (convention)
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-09 22:05:09 +00:00
|
|
|
<div className="absolute inset-0 flex items-center justify-center text-xs font-bold text-foreground opacity-0 group-hover:opacity-100 transition-opacity">
|
2026-01-13 18:47:57 +00:00
|
|
|
{range}
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
ui(tokens): migrate text-kodo-content-dim to text-muted-foreground (35 files, 160 instances)
Replace legacy hardcoded `text-kodo-content-dim` (Gray-400, theme-unaware)
with semantic `text-muted-foreground` across 35 user-facing components.
This ensures all secondary/muted text adapts correctly to light/dark theme
changes instead of staying fixed at a single gray value.
Covers: SearchBar, PlaylistsView, NotificationBell, TrackAnalyticsView,
LiveStreamDetailView, LicenceCard, FilePreviewCard, PasswordStrengthIndicator,
NotificationItem, TrackList, CourseCard, GroupCard, AchievementCard, XPBar,
EquipmentCard, SellerDashboardView, APIPlaygroundView, DeveloperDashboardView,
CreatorModal, AddToPlaylistModal, LicenceDetailsModal, QuizModal,
CertificateModal, FlashSaleModal, CreateAPIKeyModal, LyricsEditorModal,
WatermarkSettingsModal, ProfileXPView, LeaderboardView, PostCard,
ExploreView, FeedView, MessageSearch, TypingIndicator, PlaylistTrackItem.
Co-authored-by: Cursor <cursoragent@cursor.com>
2026-02-08 23:03:33 +00:00
|
|
|
<div className="flex justify-between text-xs text-muted-foreground mt-2">
|
2026-01-13 18:47:57 +00:00
|
|
|
{Object.entries(trackData.demographics).map(([range, val]) => (
|
|
|
|
|
<span key={range}>
|
|
|
|
|
{range}: {val}%
|
|
|
|
|
</span>
|
|
|
|
|
))}
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
</Card>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
2026-01-13 18:47:57 +00:00
|
|
|
</div>
|
2026-01-07 09:31:02 +00:00
|
|
|
</div>
|
|
|
|
|
);
|
|
|
|
|
};
|