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