feat: enhance error handling in DashboardPage and add retry functionality
This commit is contained in:
@@ -145,7 +145,7 @@ function App() {
|
||||
<AuthProvider>
|
||||
<SocketProvider>
|
||||
<AppRoutes />
|
||||
<Toaster richColors />
|
||||
<Toaster richColors position='top-right' />
|
||||
</SocketProvider>
|
||||
</AuthProvider>
|
||||
</ThemeProvider>
|
||||
|
||||
@@ -56,7 +56,7 @@ export function LoadingSkeleton() {
|
||||
)
|
||||
}
|
||||
|
||||
export function ErrorState({ error }: { error: string }) {
|
||||
export function ErrorState({ error, onRetry }: { error: string; onRetry: () => void }) {
|
||||
return (
|
||||
<AppLayout
|
||||
breadcrumb={{
|
||||
@@ -64,7 +64,7 @@ export function ErrorState({ error }: { error: string }) {
|
||||
}}
|
||||
>
|
||||
<div className="flex-1 rounded-xl bg-muted/50 p-4">
|
||||
<DashboardHeader onRefresh={() => {}} isRefreshing={false} />
|
||||
<DashboardHeader onRefresh={onRetry} isRefreshing={false} />
|
||||
<div className="border-2 border-dashed border-destructive/25 rounded-lg p-4">
|
||||
<p className="text-destructive">Error loading statistics: {error}</p>
|
||||
</div>
|
||||
|
||||
@@ -46,26 +46,38 @@ export function DashboardPage() {
|
||||
|
||||
const fetchStatistics = useCallback(async () => {
|
||||
try {
|
||||
const [soundboardResponse, trackResponse] = await Promise.all([
|
||||
fetch('/api/v1/dashboard/soundboard-statistics', {
|
||||
credentials: 'include',
|
||||
}),
|
||||
fetch('/api/v1/dashboard/track-statistics', { credentials: 'include' }),
|
||||
])
|
||||
setError(null) // Clear previous errors
|
||||
|
||||
if (!soundboardResponse.ok || !trackResponse.ok) {
|
||||
throw new Error('Failed to fetch statistics')
|
||||
// Fetch soundboard statistics
|
||||
const soundboardResponse = await fetch('/api/v1/dashboard/soundboard-statistics', {
|
||||
credentials: 'include',
|
||||
})
|
||||
|
||||
if (!soundboardResponse.ok) {
|
||||
const errorText = await soundboardResponse.text()
|
||||
throw new Error(`Failed to fetch soundboard statistics: ${errorText}`)
|
||||
}
|
||||
|
||||
const [soundboardData, trackData] = await Promise.all([
|
||||
soundboardResponse.json(),
|
||||
trackResponse.json(),
|
||||
])
|
||||
|
||||
const soundboardData = await soundboardResponse.json()
|
||||
setSoundboardStatistics(soundboardData)
|
||||
|
||||
// Fetch track statistics separately to avoid Promise.all failures
|
||||
const trackResponse = await fetch('/api/v1/dashboard/track-statistics', {
|
||||
credentials: 'include'
|
||||
})
|
||||
|
||||
if (!trackResponse.ok) {
|
||||
const errorText = await trackResponse.text()
|
||||
throw new Error(`Failed to fetch track statistics: ${errorText}`)
|
||||
}
|
||||
|
||||
const trackData = await trackResponse.json()
|
||||
setTrackStatistics(trackData)
|
||||
|
||||
} catch (err) {
|
||||
console.error('Dashboard statistics error:', err)
|
||||
setError(err instanceof Error ? err.message : 'An error occurred')
|
||||
// Don't reset the statistics to null on error, keep previous data if available
|
||||
}
|
||||
}, [])
|
||||
|
||||
@@ -125,12 +137,26 @@ export function DashboardPage() {
|
||||
const refreshAll = useCallback(async () => {
|
||||
setRefreshing(true)
|
||||
try {
|
||||
await Promise.all([fetchStatistics(), fetchTopSounds()])
|
||||
// Fetch statistics and top sounds sequentially to avoid Promise.all issues
|
||||
await fetchStatistics()
|
||||
await fetchTopSounds()
|
||||
} catch (err) {
|
||||
console.error('Error during refresh:', err)
|
||||
} finally {
|
||||
setRefreshing(false)
|
||||
}
|
||||
}, [fetchStatistics, fetchTopSounds])
|
||||
|
||||
const retryFromError = useCallback(async () => {
|
||||
setLoading(true)
|
||||
setError(null)
|
||||
try {
|
||||
await fetchStatistics()
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [fetchStatistics])
|
||||
|
||||
useEffect(() => {
|
||||
const loadInitialData = async () => {
|
||||
setLoading(true)
|
||||
@@ -144,14 +170,17 @@ export function DashboardPage() {
|
||||
loadInitialData()
|
||||
}, [])
|
||||
|
||||
// Auto-refresh every 5 seconds
|
||||
// Auto-refresh every 30 seconds (reduced frequency to avoid issues)
|
||||
useEffect(() => {
|
||||
const interval = setInterval(() => {
|
||||
refreshAll()
|
||||
}, 5000)
|
||||
// Only auto-refresh if not currently loading or in error state
|
||||
if (!loading && !refreshing && (!error || (soundboardStatistics && trackStatistics))) {
|
||||
refreshAll()
|
||||
}
|
||||
}, 30000) // Increased to 30 seconds
|
||||
|
||||
return () => clearInterval(interval)
|
||||
}, [refreshAll])
|
||||
}, [refreshAll, loading, refreshing, error, soundboardStatistics, trackStatistics])
|
||||
|
||||
useEffect(() => {
|
||||
fetchTopSounds(true) // Show loading on initial load and filter changes
|
||||
@@ -161,8 +190,8 @@ export function DashboardPage() {
|
||||
return <LoadingSkeleton />
|
||||
}
|
||||
|
||||
if (error || !soundboardStatistics || !trackStatistics) {
|
||||
return <ErrorState error={error || 'Failed to load statistics'} />
|
||||
if (error && (!soundboardStatistics || !trackStatistics)) {
|
||||
return <ErrorState error={error} onRetry={retryFromError} />
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -175,10 +204,12 @@ export function DashboardPage() {
|
||||
<DashboardHeader onRefresh={refreshAll} isRefreshing={refreshing} />
|
||||
|
||||
<div className="space-y-6">
|
||||
<StatisticsGrid
|
||||
soundboardStatistics={soundboardStatistics}
|
||||
trackStatistics={trackStatistics}
|
||||
/>
|
||||
{soundboardStatistics && trackStatistics && (
|
||||
<StatisticsGrid
|
||||
soundboardStatistics={soundboardStatistics}
|
||||
trackStatistics={trackStatistics}
|
||||
/>
|
||||
)}
|
||||
|
||||
<TopSoundsSection
|
||||
topSounds={topSounds}
|
||||
|
||||
Reference in New Issue
Block a user