diff --git a/src/lib/format.ts b/src/lib/format.ts new file mode 100644 index 0000000..c0d20c7 --- /dev/null +++ b/src/lib/format.ts @@ -0,0 +1,38 @@ +/** + * Utility functions for formatting data for display + */ + +export function formatDuration(durationMs: number): string { + if (!durationMs) return "0s" + + const totalSeconds = Math.floor(durationMs / 1000) + const hours = Math.floor(totalSeconds / 3600) + const minutes = Math.floor((totalSeconds % 3600) / 60) + const seconds = totalSeconds % 60 + + if (hours > 0) { + return `${hours}h ${minutes}m ${seconds}s` + } + if (minutes > 0) { + return `${minutes}m ${seconds}s` + } + return `${seconds}s` +} + +export function formatFileSize(sizeBytes: number): string { + if (!sizeBytes) return "0 B" + + const units = ["B", "KB", "MB", "GB", "TB"] + let size = sizeBytes + let unitIndex = 0 + + while (size >= 1024 && unitIndex < units.length - 1) { + size /= 1024 + unitIndex++ + } + + if (unitIndex === 0) { + return `${Math.floor(size)} ${units[unitIndex]}` + } + return `${size.toFixed(1)} ${units[unitIndex]}` +} \ No newline at end of file diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index e3bd207..02c7d27 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -1,6 +1,105 @@ +import { useEffect, useState } from 'react' import { AppLayout } from '@/components/AppLayout' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { Volume2, Play, Clock, HardDrive, Music } from 'lucide-react' +import { formatDuration, formatFileSize } from '@/lib/format' + +interface SoundboardStatistics { + sound_count: number + total_play_count: number + total_duration: number + total_size: number +} + +interface TrackStatistics { + track_count: number + total_play_count: number + total_duration: number + total_size: number +} export function DashboardPage() { + const [soundboardStatistics, setSoundboardStatistics] = useState(null) + const [trackStatistics, setTrackStatistics] = useState(null) + const [loading, setLoading] = useState(true) + const [error, setError] = useState(null) + + useEffect(() => { + const fetchStatistics = async () => { + try { + const [soundboardResponse, trackResponse] = await Promise.all([ + fetch('/api/v1/dashboard/soundboard-statistics', { credentials: 'include' }), + fetch('/api/v1/dashboard/track-statistics', { credentials: 'include' }) + ]) + + if (!soundboardResponse.ok || !trackResponse.ok) { + throw new Error('Failed to fetch statistics') + } + + const [soundboardData, trackData] = await Promise.all([ + soundboardResponse.json(), + trackResponse.json() + ]) + + setSoundboardStatistics(soundboardData) + setTrackStatistics(trackData) + } catch (err) { + setError(err instanceof Error ? err.message : 'An error occurred') + } finally { + setLoading(false) + } + } + + fetchStatistics() + }, []) + + if (loading) { + return ( + +
+

Dashboard

+
+ {[...Array(8)].map((_, i) => ( + + + Loading... + + +
---
+
+
+ ))} +
+
+
+ ) + } + + if (error || !soundboardStatistics || !trackStatistics) { + return ( + +
+

Dashboard

+
+

Error loading statistics: {error}

+
+
+
+ ) + } + return (
-

Dashboard

-
-
-

Dashboard content coming soon...

+

Dashboard

+ +
+ {/* Soundboard Statistics */} +
+

Soundboard Statistics

+
+ + + Total Sounds + + + +
{soundboardStatistics.sound_count}
+

+ Soundboard audio files +

+
+
+ + + + Total Plays + + + +
{soundboardStatistics.total_play_count}
+

+ All-time play count +

+
+
+ + + + Total Duration + + + +
{formatDuration(soundboardStatistics.total_duration)}
+

+ Combined audio duration +

+
+
+ + + + Total Size + + + +
{formatFileSize(soundboardStatistics.total_size)}
+

+ Original + normalized files +

+
+
+
+
+ + {/* Track Statistics */} +
+

Track Statistics

+
+ + + Total Tracks + + + +
{trackStatistics.track_count}
+

+ Extracted audio tracks +

+
+
+ + + + Total Plays + + + +
{trackStatistics.total_play_count}
+

+ All-time play count +

+
+
+ + + + Total Duration + + + +
{formatDuration(trackStatistics.total_duration)}
+

+ Combined track duration +

+
+
+ + + + Total Size + + + +
{formatFileSize(trackStatistics.total_size)}
+

+ Original + normalized files +

+
+
+
diff --git a/vite.config.ts b/vite.config.ts index 48bfd45..50f6e0b 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -11,6 +11,16 @@ export default defineConfig({ '@': path.resolve(__dirname, './src'), }, }, + // Development server configuration + server: { + port: 8001, + proxy: { + '/api': { + target: 'http://localhost:8000', + changeOrigin: true, + }, + }, + }, // Production build optimization build: { outDir: 'dist',