feat: improve sound restoration in PlaylistEditPage by fetching complete sound data
This commit is contained in:
@@ -1,7 +1,8 @@
|
||||
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 { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from '@/components/ui/select'
|
||||
import { Volume2, Play, Clock, HardDrive, Music, Trophy, Loader2 } from 'lucide-react'
|
||||
import { formatDuration, formatFileSize } from '@/lib/format'
|
||||
|
||||
interface SoundboardStatistics {
|
||||
@@ -18,11 +19,48 @@ interface TrackStatistics {
|
||||
total_size: number
|
||||
}
|
||||
|
||||
interface TopSound {
|
||||
id: number
|
||||
name: string
|
||||
type: string
|
||||
play_count: number
|
||||
duration: number | null
|
||||
created_at: string | null
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
// Top sounds state
|
||||
const [topSounds, setTopSounds] = useState<TopSound[]>([])
|
||||
const [topSoundsLoading, setTopSoundsLoading] = useState(false)
|
||||
const [soundType, setSoundType] = useState('SDB')
|
||||
const [period, setPeriod] = useState('all_time')
|
||||
const [limit, setLimit] = useState(5)
|
||||
|
||||
const fetchTopSounds = async () => {
|
||||
try {
|
||||
setTopSoundsLoading(true)
|
||||
const response = await fetch(
|
||||
`/api/v1/dashboard/top-sounds?sound_type=${soundType}&period=${period}&limit=${limit}`,
|
||||
{ credentials: 'include' }
|
||||
)
|
||||
|
||||
if (!response.ok) {
|
||||
throw new Error('Failed to fetch top sounds')
|
||||
}
|
||||
|
||||
const data = await response.json()
|
||||
setTopSounds(data)
|
||||
} catch (err) {
|
||||
console.error('Failed to fetch top sounds:', err)
|
||||
} finally {
|
||||
setTopSoundsLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
const fetchStatistics = async () => {
|
||||
@@ -52,6 +90,10 @@ export function DashboardPage() {
|
||||
|
||||
fetchStatistics()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
fetchTopSounds()
|
||||
}, [soundType, period, limit])
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
@@ -268,6 +310,108 @@ export function DashboardPage() {
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Top Sounds Section */}
|
||||
<div className="mt-8">
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-2">
|
||||
<Trophy className="h-5 w-5" />
|
||||
<CardTitle>Top Sounds</CardTitle>
|
||||
</div>
|
||||
<div className="flex items-center gap-4">
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium">Type:</span>
|
||||
<Select value={soundType} onValueChange={setSoundType}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="all">All</SelectItem>
|
||||
<SelectItem value="SDB">Soundboard</SelectItem>
|
||||
<SelectItem value="EXT">Tracks</SelectItem>
|
||||
<SelectItem value="TTS">TTS</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium">Period:</span>
|
||||
<Select value={period} onValueChange={setPeriod}>
|
||||
<SelectTrigger className="w-32">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="today">Today</SelectItem>
|
||||
<SelectItem value="1_day">1 Day</SelectItem>
|
||||
<SelectItem value="1_week">1 Week</SelectItem>
|
||||
<SelectItem value="1_month">1 Month</SelectItem>
|
||||
<SelectItem value="1_year">1 Year</SelectItem>
|
||||
<SelectItem value="all_time">All Time</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<span className="text-sm font-medium">Count:</span>
|
||||
<Select value={limit.toString()} onValueChange={(value) => setLimit(parseInt(value))}>
|
||||
<SelectTrigger className="w-20">
|
||||
<SelectValue />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
<SelectItem value="5">5</SelectItem>
|
||||
<SelectItem value="10">10</SelectItem>
|
||||
<SelectItem value="25">25</SelectItem>
|
||||
<SelectItem value="50">50</SelectItem>
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
<CardContent>
|
||||
{topSoundsLoading ? (
|
||||
<div className="flex items-center justify-center py-8">
|
||||
<Loader2 className="h-6 w-6 animate-spin mr-2" />
|
||||
Loading top sounds...
|
||||
</div>
|
||||
) : topSounds.length === 0 ? (
|
||||
<div className="text-center py-8 text-muted-foreground">
|
||||
<Music className="h-8 w-8 mx-auto mb-2 opacity-50" />
|
||||
<p>No sounds found for the selected criteria</p>
|
||||
</div>
|
||||
) : (
|
||||
<div className="space-y-3">
|
||||
{topSounds.map((sound, index) => (
|
||||
<div key={sound.id} className="flex items-center gap-4 p-3 bg-muted/30 rounded-lg">
|
||||
<div className="flex items-center justify-center w-8 h-8 bg-primary text-primary-foreground rounded-full font-bold text-sm">
|
||||
{index + 1}
|
||||
</div>
|
||||
<Music className="h-4 w-4 text-muted-foreground flex-shrink-0" />
|
||||
<div className="flex-1 min-w-0">
|
||||
<div className="font-medium truncate">{sound.name}</div>
|
||||
<div className="flex items-center gap-4 text-xs text-muted-foreground mt-1">
|
||||
<span className="flex items-center gap-1">
|
||||
<Play className="h-3 w-3" />
|
||||
{sound.play_count} plays
|
||||
</span>
|
||||
{sound.duration && (
|
||||
<span className="flex items-center gap-1">
|
||||
<Clock className="h-3 w-3" />
|
||||
{formatDuration(sound.duration)}
|
||||
</span>
|
||||
)}
|
||||
<span className="px-1.5 py-0.5 bg-secondary rounded text-xs">
|
||||
{sound.type}
|
||||
</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</CardContent>
|
||||
</Card>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</AppLayout>
|
||||
|
||||
@@ -606,25 +606,17 @@ export function PlaylistEditPage() {
|
||||
|
||||
// If it's an EXT sound and we're in add mode, add it back to available sounds
|
||||
if (isAddMode && removedSound.type === 'EXT') {
|
||||
// Convert PlaylistSound back to Sound format for available list
|
||||
const soundToAddBack = {
|
||||
id: removedSound.id,
|
||||
name: removedSound.name,
|
||||
filename: removedSound.filename,
|
||||
duration: removedSound.duration,
|
||||
size: removedSound.size,
|
||||
hash: removedSound.hash,
|
||||
type: removedSound.type,
|
||||
play_count: removedSound.play_count,
|
||||
is_normalized: removedSound.is_normalized,
|
||||
normalized_filename: removedSound.normalized_filename,
|
||||
normalized_duration: removedSound.normalized_duration,
|
||||
normalized_size: removedSound.normalized_size,
|
||||
normalized_hash: removedSound.normalized_hash,
|
||||
created_at: removedSound.created_at,
|
||||
updated_at: removedSound.updated_at
|
||||
// Fetch the complete sound data to add back to available sounds
|
||||
try {
|
||||
const allExtSounds = await soundsService.getSoundsByType('EXT')
|
||||
const soundToAddBack = allExtSounds.find(s => s.id === soundId)
|
||||
if (soundToAddBack) {
|
||||
setAvailableSounds(prev => [...prev, soundToAddBack].sort((a, b) => a.name.localeCompare(b.name)))
|
||||
}
|
||||
} catch {
|
||||
// If we can't fetch the sound data, just refresh the available sounds
|
||||
await fetchAvailableSounds()
|
||||
}
|
||||
setAvailableSounds(prev => [...prev, soundToAddBack].sort((a, b) => a.name.localeCompare(b.name)))
|
||||
}
|
||||
|
||||
// Optimistically update playlist stats
|
||||
|
||||
Reference in New Issue
Block a user