feat: add utility functions for formatting duration and file size

This commit is contained in:
JSC
2025-08-11 11:17:33 +02:00
parent 0897095942
commit dbbb9538dd
3 changed files with 264 additions and 4 deletions

View File

@@ -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<SoundboardStatistics | null>(null)
const [trackStatistics, setTrackStatistics] = useState<TrackStatistics | null>(null)
const [loading, setLoading] = useState(true)
const [error, setError] = useState<string | null>(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 (
<AppLayout
breadcrumb={{
items: [
{ label: 'Dashboard' }
]
}}
>
<div className="flex-1 rounded-xl bg-muted/30 p-4">
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
{[...Array(8)].map((_, i) => (
<Card key={i}>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Loading...</CardTitle>
</CardHeader>
<CardContent>
<div className="text-2xl font-bold animate-pulse">---</div>
</CardContent>
</Card>
))}
</div>
</div>
</AppLayout>
)
}
if (error || !soundboardStatistics || !trackStatistics) {
return (
<AppLayout
breadcrumb={{
items: [
{ label: 'Dashboard' }
]
}}
>
<div className="flex-1 rounded-xl bg-muted/30 p-4">
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
<div className="border-2 border-dashed border-destructive/25 rounded-lg p-4">
<p className="text-destructive">Error loading statistics: {error}</p>
</div>
</div>
</AppLayout>
)
}
return (
<AppLayout
breadcrumb={{
@@ -10,10 +109,123 @@ export function DashboardPage() {
}}
>
<div className="flex-1 rounded-xl bg-muted/30 p-4">
<h1 className="text-2xl font-bold mb-4">Dashboard</h1>
<div className="grid gap-4">
<div className="border-2 border-dashed border-muted-foreground/25 rounded-lg h-64 flex items-center justify-center">
<p className="text-muted-foreground">Dashboard content coming soon...</p>
<h1 className="text-2xl font-bold mb-6">Dashboard</h1>
<div className="space-y-6">
{/* Soundboard Statistics */}
<div>
<h2 className="text-lg font-semibold mb-3 text-muted-foreground">Soundboard Statistics</h2>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Sounds</CardTitle>
<Volume2 className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{soundboardStatistics.sound_count}</div>
<p className="text-xs text-muted-foreground">
Soundboard audio files
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Plays</CardTitle>
<Play className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{soundboardStatistics.total_play_count}</div>
<p className="text-xs text-muted-foreground">
All-time play count
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Duration</CardTitle>
<Clock className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{formatDuration(soundboardStatistics.total_duration)}</div>
<p className="text-xs text-muted-foreground">
Combined audio duration
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Size</CardTitle>
<HardDrive className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{formatFileSize(soundboardStatistics.total_size)}</div>
<p className="text-xs text-muted-foreground">
Original + normalized files
</p>
</CardContent>
</Card>
</div>
</div>
{/* Track Statistics */}
<div>
<h2 className="text-lg font-semibold mb-3 text-muted-foreground">Track Statistics</h2>
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-4">
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Tracks</CardTitle>
<Music className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{trackStatistics.track_count}</div>
<p className="text-xs text-muted-foreground">
Extracted audio tracks
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Plays</CardTitle>
<Play className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{trackStatistics.total_play_count}</div>
<p className="text-xs text-muted-foreground">
All-time play count
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Duration</CardTitle>
<Clock className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{formatDuration(trackStatistics.total_duration)}</div>
<p className="text-xs text-muted-foreground">
Combined track duration
</p>
</CardContent>
</Card>
<Card>
<CardHeader className="flex flex-row items-center justify-between space-y-0 pb-2">
<CardTitle className="text-sm font-medium">Total Size</CardTitle>
<HardDrive className="h-4 w-4 text-muted-foreground" />
</CardHeader>
<CardContent>
<div className="text-2xl font-bold">{formatFileSize(trackStatistics.total_size)}</div>
<p className="text-xs text-muted-foreground">
Original + normalized files
</p>
</CardContent>
</Card>
</div>
</div>
</div>
</div>