diff --git a/src/components/dashboard/DashboardHeader.tsx b/src/components/dashboard/DashboardHeader.tsx new file mode 100644 index 0000000..6ce0ad7 --- /dev/null +++ b/src/components/dashboard/DashboardHeader.tsx @@ -0,0 +1,30 @@ +import { Button } from '@/components/ui/button' +import { RefreshCw } from 'lucide-react' + +interface DashboardHeaderProps { + onRefresh: () => void + isRefreshing: boolean +} + +export function DashboardHeader({ onRefresh, isRefreshing }: DashboardHeaderProps) { + return ( +
+
+

Dashboard

+

+ Overview of your soundboard and track statistics +

+
+ +
+ ) +} \ No newline at end of file diff --git a/src/components/dashboard/DashboardLoadingStates.tsx b/src/components/dashboard/DashboardLoadingStates.tsx new file mode 100644 index 0000000..9f35776 --- /dev/null +++ b/src/components/dashboard/DashboardLoadingStates.tsx @@ -0,0 +1,79 @@ +import { AppLayout } from '@/components/AppLayout' +import { DashboardHeader } from '@/components/dashboard/DashboardHeader' +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' + +interface LoadingSkeletonProps { + title: string + description: string +} + +export function LoadingSkeleton({ title, description }: LoadingSkeletonProps) { + return ( + +
+ {}} isRefreshing={false} /> +
+
+

+ Soundboard Statistics +

+
+ {[...Array(4)].map((_, i) => ( + + + + Loading... + + + +
---
+
+
+ ))} +
+
+
+

+ Track Statistics +

+
+ {[...Array(4)].map((_, i) => ( + + + + Loading... + + + +
---
+
+
+ ))} +
+
+
+
+
+ ) +} + +export function ErrorState({ error }: { error: string }) { + return ( + +
+ {}} isRefreshing={false} /> +
+

Error loading statistics: {error}

+
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/dashboard/StatisticCard.tsx b/src/components/dashboard/StatisticCard.tsx new file mode 100644 index 0000000..ce693eb --- /dev/null +++ b/src/components/dashboard/StatisticCard.tsx @@ -0,0 +1,25 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { LucideIcon } from 'lucide-react' +import { ReactNode } from 'react' + +interface StatisticCardProps { + title: string + icon: LucideIcon + value: ReactNode + description: string +} + +export function StatisticCard({ title, icon: Icon, value, description }: StatisticCardProps) { + return ( + + + {title} + + + +
{value}
+

{description}

+
+
+ ) +} \ No newline at end of file diff --git a/src/components/dashboard/StatisticsGrid.tsx b/src/components/dashboard/StatisticsGrid.tsx new file mode 100644 index 0000000..75fb24a --- /dev/null +++ b/src/components/dashboard/StatisticsGrid.tsx @@ -0,0 +1,114 @@ +import { StatisticCard } from '@/components/dashboard/StatisticCard' +import { NumberFlowDuration } from '@/components/ui/number-flow-duration' +import { NumberFlowSize } from '@/components/ui/number-flow-size' +import NumberFlow from '@number-flow/react' +import { Clock, HardDrive, Music, Play, Volume2 } from 'lucide-react' + +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 +} + +interface StatisticsGridProps { + soundboardStatistics: SoundboardStatistics + trackStatistics: TrackStatistics +} + +export function StatisticsGrid({ soundboardStatistics, trackStatistics }: StatisticsGridProps) { + return ( +
+
+

+ Soundboard Statistics +

+
+ } + description="Soundboard audio files" + /> + } + description="All-time play count" + /> + + } + description="Combined audio duration" + /> + + } + description="Original + normalized files" + /> +
+
+ +
+

+ Track Statistics +

+
+ } + description="Extracted audio tracks" + /> + } + description="All-time play count" + /> + + } + description="Combined track duration" + /> + + } + description="Original + normalized files" + /> +
+
+
+ ) +} \ No newline at end of file diff --git a/src/components/dashboard/TopSoundsSection.tsx b/src/components/dashboard/TopSoundsSection.tsx new file mode 100644 index 0000000..21085d5 --- /dev/null +++ b/src/components/dashboard/TopSoundsSection.tsx @@ -0,0 +1,156 @@ +import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' +import { NumberFlowDuration } from '@/components/ui/number-flow-duration' +import { + Select, + SelectContent, + SelectItem, + SelectTrigger, + SelectValue, +} from '@/components/ui/select' +import NumberFlow from '@number-flow/react' +import { Clock, Loader2, Music, Trophy } from 'lucide-react' + +interface TopSound { + id: number + name: string + type: string + play_count: number + duration: number | null + created_at: string | null +} + +interface TopSoundsSectionProps { + topSounds: TopSound[] + loading: boolean + soundType: string + period: string + limit: number + onSoundTypeChange: (value: string) => void + onPeriodChange: (value: string) => void + onLimitChange: (value: number) => void +} + +export function TopSoundsSection({ + topSounds, + loading, + soundType, + period, + limit, + onSoundTypeChange, + onPeriodChange, + onLimitChange, +}: TopSoundsSectionProps) { + return ( +
+ + +
+
+ + Top Sounds +
+
+
+ Type: + +
+
+ Period: + +
+
+ Count: + +
+
+
+
+ + {loading ? ( +
+ + Loading top sounds... +
+ ) : topSounds.length === 0 ? ( +
+ +

No sounds found for the selected criteria

+
+ ) : ( +
+ {topSounds.map((sound, index) => ( +
+
+ {index + 1} +
+ +
+
{sound.name}
+
+ {sound.duration && ( + + + + + )} + + {sound.type} + +
+
+
+
+ +
+
plays
+
+
+ ))} +
+ )} +
+
+
+ ) +} \ No newline at end of file diff --git a/src/pages/DashboardPage.tsx b/src/pages/DashboardPage.tsx index fcf954f..5685b86 100644 --- a/src/pages/DashboardPage.tsx +++ b/src/pages/DashboardPage.tsx @@ -1,26 +1,8 @@ import { AppLayout } from '@/components/AppLayout' -import { Button } from '@/components/ui/button' -import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card' -import { NumberFlowDuration } from '@/components/ui/number-flow-duration' -import { NumberFlowSize } from '@/components/ui/number-flow-size' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import NumberFlow from '@number-flow/react' -import { - Clock, - HardDrive, - Loader2, - Music, - Play, - RefreshCw, - Trophy, - Volume2, -} from 'lucide-react' +import { DashboardHeader } from '@/components/dashboard/DashboardHeader' +import { ErrorState, LoadingSkeleton } from '@/components/dashboard/DashboardLoadingStates' +import { StatisticsGrid } from '@/components/dashboard/StatisticsGrid' +import { TopSoundsSection } from '@/components/dashboard/TopSoundsSection' import { useCallback, useEffect, useState } from 'react' interface SoundboardStatistics { @@ -176,94 +158,11 @@ export function DashboardPage() { }, [fetchTopSounds]) if (loading) { - return ( - -
-
-
-

Dashboard

-

- Overview of your soundboard and track statistics -

-
-
-
-
-

- Soundboard Statistics -

-
- {[...Array(4)].map((_, i) => ( - - - - Loading... - - - -
- --- -
-
-
- ))} -
-
-
-

- Track Statistics -

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

Dashboard

-

- Overview of your soundboard and track statistics -

-
-
-
-

- Error loading statistics: {error} -

-
-
-
- ) + return } return ( @@ -273,306 +172,24 @@ export function DashboardPage() { }} >
-
-
-

Dashboard

-

- Overview of your soundboard and track statistics -

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

- Soundboard Statistics -

-
- - - - Total Sounds - - - - -
- -
-

- Soundboard audio files -

-
-
- - - - - Total Plays - - - - -
- -
-

- All-time play count -

-
-
- - - - - Total Duration - - - - -
- -
-

- Combined audio duration -

-
-
- - - - - Total Size - - - - -
- -
-

- Original + normalized files -

-
-
-
-
- - {/* Track Statistics */} -
-

- Track Statistics -

-
- - - - Total Tracks - - - - -
- -
-

- Extracted audio tracks -

-
-
- - - - - Total Plays - - - - -
- -
-

- All-time play count -

-
-
- - - - - Total Duration - - - - -
- -
-

- Combined track duration -

-
-
- - - - - Total Size - - - - -
- -
-

- Original + normalized files -

-
-
-
-
- - {/* Top Sounds Section */} -
- - -
-
- - Top Sounds -
-
-
- Type: - -
-
- Period: - -
-
- Count: - -
-
-
-
- - {topSoundsLoading ? ( -
- - Loading top sounds... -
- ) : topSounds.length === 0 ? ( -
- -

No sounds found for the selected criteria

-
- ) : ( -
- {topSounds.map((sound, index) => ( -
-
- {index + 1} -
- -
-
- {sound.name} -
-
- {sound.duration && ( - - - - - )} - - {sound.type} - -
-
-
-
- -
-
- plays -
-
-
- ))} -
- )} -
-
-
+ + +