project-standalo-sonic-cloud/docs/COMPONENTS.md

800 lines
18 KiB
Markdown

# Component Catalog
A comprehensive guide to all reusable React components in the Sonic Cloud platform.
---
## Audio & Playback
### AudioPlayer
**Purpose**: Fixed bottom audio player that displays the currently playing song with full playback controls
**Location**: `components/AudioPlayer.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| currentSong | Song | Currently playing song object |
| isPlaying | boolean | Playback state flag |
| onPlayPause | () => void | Toggle play/pause handler |
| onNext | () => void | Skip to next track handler |
| onPrevious | () => void | Go to previous track handler |
| progress | number | Playback progress (0-100) |
| volume | number | Volume level (0-100) |
| onSeek | (time: number) => void | Seek to specific time handler |
| onVolumeChange | (level: number) => void | Volume adjustment handler |
**Usage Example**:
```tsx
<AudioPlayer
currentSong={currentSong}
isPlaying={isPlaying}
onPlayPause={handlePlayPause}
onNext={handleNext}
onPrevious={handlePrevious}
progress={progress}
volume={volume}
onSeek={handleSeek}
onVolumeChange={setVolume}
/>
```
**Dependencies**: PlayerControls, WaveformDisplay
**Related**: PlayerControls, WaveformDisplay
</details>
---
### PlayerControls
**Purpose**: Reusable audio player control buttons for play/pause, skip, shuffle, and repeat
**Location**: `components/PlayerControls.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| isPlaying | boolean | Current playback state |
| onPlayPause | () => void | Toggle play/pause |
| onNext | () => void | Skip to next track |
| onPrevious | () => void | Go to previous track |
| shuffle | boolean | Shuffle mode enabled |
| repeat | 'none' \| 'all' \| 'one' | Repeat mode |
| onShuffleToggle | () => void | Toggle shuffle |
| onRepeatToggle | () => void | Cycle repeat modes |
| size? | 'sm' \| 'md' \| 'lg' | Button size variant |
**Usage Example**:
```tsx
<PlayerControls
isPlaying={isPlaying}
onPlayPause={togglePlayback}
onNext={skipNext}
onPrevious={skipPrevious}
shuffle={shuffleEnabled}
repeat={repeatMode}
onShuffleToggle={toggleShuffle}
onRepeatToggle={cycleRepeat}
size="md"
/>
```
**Dependencies**: None (presentational component)
**Related**: AudioPlayer
</details>
---
### WaveformDisplay
**Purpose**: Canvas-based audio waveform visualization with progress tracking and interactive seeking
**Location**: `components/WaveformDisplay.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| audioUrl | string | URL of audio file to visualize |
| progress | number | Current playback progress (0-100) |
| onSeek | (time: number) => void | Callback when user seeks |
| height? | number | Canvas height in pixels (default: 80) |
| barWidth? | number | Waveform bar width (default: 2) |
| barGap? | number | Space between bars (default: 1) |
| primaryColor? | string | Waveform color (default: theme primary) |
| progressColor? | string | Played portion color (default: accent) |
**Usage Example**:
```tsx
<WaveformDisplay
audioUrl={song.audioUrl}
progress={currentProgress}
onSeek={handleSeek}
height={100}
primaryColor="#3b82f6"
progressColor="#10b981"
/>
```
**Dependencies**: Canvas API, Web Audio API
**Related**: AudioPlayer
</details>
---
## Content Cards
### SongCard
**Purpose**: Displays song information with cover image, title, artist, and playback stats
**Location**: `components/SongCard.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| song | Song | Song object with metadata |
| onClick? | () => void | Click handler for card |
| onPlay? | () => void | Play button handler |
| showArtist? | boolean | Display artist name (default: true) |
| showPlayCount? | boolean | Display play count (default: true) |
| variant? | 'default' \| 'compact' | Card layout style |
**Usage Example**:
```tsx
<SongCard
song={song}
onClick={() => router.push(`/songs/${song.id}`)}
onPlay={playSong}
showArtist={true}
showPlayCount={true}
variant="default"
/>
```
**Dependencies**: Next.js Image, Link
**Related**: TrackList, AudioPlayer
</details>
---
### AlbumCard
**Purpose**: Displays album information with cover art, title, artist, release year, and track count
**Location**: `components/AlbumCard.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| album | Album | Album object with metadata |
| onClick? | () => void | Click handler for card |
| showArtist? | boolean | Display artist name (default: true) |
| showTrackCount? | boolean | Display track count (default: true) |
**Usage Example**:
```tsx
<AlbumCard
album={album}
onClick={() => router.push(`/album/${album.id}`)}
showArtist={true}
showTrackCount={true}
/>
```
**Dependencies**: Next.js Image, Link
**Related**: ArtistCard
</details>
---
### ArtistCard
**Purpose**: Displays artist profile with circular avatar, name, and verification badge
**Location**: `components/ArtistCard.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| artist | Artist | Artist profile object |
| onClick? | () => void | Click handler for card |
| showVerified? | boolean | Display verified badge (default: true) |
| showFollowers? | boolean | Display follower count (default: false) |
| size? | 'sm' \| 'md' \| 'lg' | Avatar size variant |
**Usage Example**:
```tsx
<ArtistCard
artist={artist}
onClick={() => router.push(`/artist/${artist.id}`)}
showVerified={true}
size="md"
/>
```
**Dependencies**: Next.js Image, Link
**Related**: AlbumCard, LabelCard
</details>
---
### PlaylistCard
**Purpose**: Displays playlist with cover image, title, song count, and privacy indicator
**Location**: `components/PlaylistCard.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| playlist | Playlist | Playlist object with metadata |
| onClick? | () => void | Click handler for card |
| showSongCount? | boolean | Display song count (default: true) |
| showPrivacy? | boolean | Display privacy badge (default: true) |
| editable? | boolean | Show edit actions (default: false) |
**Usage Example**:
```tsx
<PlaylistCard
playlist={playlist}
onClick={() => router.push(`/playlist/${playlist.id}`)}
showSongCount={true}
showPrivacy={true}
editable={isOwner}
/>
```
**Dependencies**: Next.js Image, Link
**Related**: TrackList
</details>
---
### LabelCard
**Purpose**: Displays record label information with logo, name, and artist roster count
**Location**: `components/LabelCard.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| label | Label | Label profile object |
| onClick? | () => void | Click handler for card |
| showArtistCount? | boolean | Display artist count (default: true) |
**Usage Example**:
```tsx
<LabelCard
label={label}
onClick={() => router.push(`/label/${label.id}`)}
showArtistCount={true}
/>
```
**Dependencies**: Next.js Image, Link
**Related**: ArtistCard
</details>
---
### GenreBadge
**Purpose**: Small clickable badge displaying music genre with color-coded styling
**Location**: `components/GenreBadge.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| genre | Genre | Genre object with name and color |
| onClick? | () => void | Click handler for badge |
| size? | 'xs' \| 'sm' \| 'md' | Badge size variant |
| removable? | boolean | Show remove button (default: false) |
| onRemove? | () => void | Remove button handler |
**Usage Example**:
```tsx
<GenreBadge
genre={genre}
onClick={() => filterByGenre(genre.slug)}
size="sm"
/>
```
**Dependencies**: None (presentational component)
**Related**: UploadForm
</details>
---
## Forms
### AuthForm
**Purpose**: Multi-mode authentication form for login, registration, and password reset flows
**Location**: `components/AuthForm.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| mode | 'login' \| 'register' \| 'forgot' \| 'reset' | Form mode |
| onSuccess? | () => void | Success callback |
| resetToken? | string | Password reset token (for reset mode) |
**Usage Example**:
```tsx
// Login mode
<AuthForm mode="login" onSuccess={handleLoginSuccess} />
// Register mode
<AuthForm mode="register" onSuccess={handleRegisterSuccess} />
// Password reset mode
<AuthForm mode="reset" resetToken={token} onSuccess={handleResetSuccess} />
```
**API Calls**:
- Login: `POST /api/auth/login`
- Register: `POST /api/auth/register`
- Forgot: `POST /api/auth/forgot-password`
- Reset: `POST /api/auth/reset-password`
**Dependencies**: React Hook Form, JWT storage
**Related**: UserMenu, Header
</details>
---
### UploadForm
**Purpose**: Multi-field form for uploading songs with audio file, metadata, and genre tagging
**Location**: `components/UploadForm.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| onSuccess? | (songId: string) => void | Success callback with created song ID |
| onCancel? | () => void | Cancel button handler |
| initialData? | Partial<Song> | Pre-fill form data (for editing) |
**Usage Example**:
```tsx
<UploadForm
onSuccess={(songId) => router.push(`/songs/${songId}`)}
onCancel={() => router.back()}
/>
```
**Form Fields**:
- Audio file upload (required)
- Title (required)
- Album selection (optional)
- Genre tags (multi-select)
- Cover image URL (optional)
- Lyrics (optional)
- Privacy toggle (public/private)
**API Calls**:
- Upload: `POST /api/songs/upload`
**Dependencies**: File upload handling, GenreBadge
**Related**: GenreBadge
</details>
---
### ProfileForm
**Purpose**: User profile editing form with username, display name, bio, and website fields
**Location**: `components/ProfileForm.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| user | User | Current user object |
| onSuccess? | () => void | Success callback |
| onCancel? | () => void | Cancel button handler |
**Usage Example**:
```tsx
<ProfileForm
user={currentUser}
onSuccess={() => toast.success('Profile updated')}
onCancel={() => router.back()}
/>
```
**Form Fields**:
- Username (unique, alphanumeric + underscore)
- Display name (optional)
- Bio (optional, max 500 chars)
- Website URL (optional, validated)
- Avatar URL (optional)
**API Calls**:
- Update: `PUT /api/users/me`
**Dependencies**: React Hook Form, URL validation
**Related**: UserMenu
</details>
---
### CreatePlaylistModal
**Purpose**: Modal dialog for creating new playlists with title, description, and privacy settings
**Location**: `components/CreatePlaylistModal.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| isOpen | boolean | Modal open state |
| onClose | () => void | Close modal handler |
| onSuccess? | (playlistId: string) => void | Success callback with playlist ID |
**Usage Example**:
```tsx
<CreatePlaylistModal
isOpen={isModalOpen}
onClose={() => setIsModalOpen(false)}
onSuccess={(id) => router.push(`/playlist/${id}`)}
/>
```
**Form Fields**:
- Playlist title (required)
- Description (optional)
- Privacy toggle (public/private)
**API Calls**:
- Create: `POST /api/playlists`
**Dependencies**: Modal backdrop, React Hook Form
**Related**: PlaylistCard
</details>
---
## Navigation
### Header
**Purpose**: Fixed top navigation header with logo, navigation links, search, and user menu
**Location**: `components/Header.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| transparent? | boolean | Transparent background (default: false) |
**Usage Example**:
```tsx
<Header transparent={false} />
```
**Features**:
- Logo with home link
- Primary navigation links (Discover, Artists, Genres, Labels)
- Search bar integration
- User menu with auth state
- Mobile hamburger menu
- Responsive breakpoints
**Dependencies**: NavLink, UserMenu, SearchBar
**Related**: NavLink, UserMenu, SearchBar
</details>
---
### NavLink
**Purpose**: Navigation link component with automatic active state highlighting
**Location**: `components/NavLink.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| href | string | Link destination path |
| children | ReactNode | Link text/content |
| exact? | boolean | Exact path match (default: false) |
| activeClassName? | string | Custom active state class |
**Usage Example**:
```tsx
<NavLink href="/discover" exact>
Discover
</NavLink>
<NavLink href="/artist" activeClassName="text-blue-500">
Artists
</NavLink>
```
**Dependencies**: Next.js usePathname, Link
**Related**: Header
</details>
---
### UserMenu
**Purpose**: Dropdown menu showing authentication state with profile, upload, playlists, and logout actions
**Location**: `components/UserMenu.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| user | User \| null | Current authenticated user |
**Usage Example**:
```tsx
<UserMenu user={currentUser} />
```
**Menu Items (Authenticated)**:
- Profile link
- Upload song link
- My playlists link
- Settings link (if applicable)
- Logout button
**Menu Items (Unauthenticated)**:
- Login link
- Register link
**Dependencies**: JWT auth context, Next.js Link
**Related**: Header, AuthForm
</details>
---
### SearchBar
**Purpose**: Search input with real-time suggestions dropdown, debouncing, and loading indicator
**Location**: `components/SearchBar.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| placeholder? | string | Input placeholder text |
| onSearch? | (query: string) => void | Search submit handler |
| debounceMs? | number | Debounce delay (default: 300ms) |
**Usage Example**:
```tsx
<SearchBar
placeholder="Search songs, artists, albums..."
onSearch={(query) => router.push(`/search?q=${query}`)}
debounceMs={300}
/>
```
**Features**:
- Real-time search as you type
- Suggestion dropdown with categorized results (Songs, Artists, Albums)
- Loading spinner during API calls
- Keyboard navigation (arrow keys, enter, escape)
- Click outside to close
**API Calls**:
- Search: `GET /api/search?q={query}`
**Dependencies**: Debounce hook, fetch API
**Related**: Header
</details>
---
## Sharing
### ShareButton
**Purpose**: Button that generates shareable links and opens the share modal
**Location**: `components/ShareButton.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| type | 'song' \| 'playlist' \| 'album' | Content type to share |
| id | string | Content ID |
| variant? | 'icon' \| 'button' | Display style (default: 'button') |
**Usage Example**:
```tsx
<ShareButton type="song" id={song.id} variant="button" />
<ShareButton type="playlist" id={playlist.id} variant="icon" />
```
**API Calls**:
- Generate link: `POST /api/share/{type}/{id}`
**Dependencies**: ShareModal
**Related**: ShareModal
</details>
---
### ShareModal
**Purpose**: Modal displaying shareable link with copy-to-clipboard and social media share buttons
**Location**: `components/ShareModal.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| isOpen | boolean | Modal open state |
| onClose | () => void | Close modal handler |
| shareUrl | string | Full shareable URL |
| title | string | Content title for social sharing |
**Usage Example**:
```tsx
<ShareModal
isOpen={isShareModalOpen}
onClose={() => setIsShareModalOpen(false)}
shareUrl={shareUrl}
title={song.title}
/>
```
**Features**:
- Copy link to clipboard button
- Social share buttons (Twitter, Facebook, WhatsApp)
- QR code generation (optional)
- Share analytics tracking
**Dependencies**: Clipboard API, social share URLs
**Related**: ShareButton
</details>
---
## Display
### TrackList
**Purpose**: Scrollable list of tracks with position numbers, song information, artist details, and duration
**Location**: `components/TrackList.tsx`
<details>
<summary>Props & Usage</summary>
**Props**:
| Prop | Type | Description |
|------|------|-------------|
| tracks | Song[] | Array of song objects |
| onTrackClick? | (song: Song, index: number) => void | Track selection handler |
| showArtist? | boolean | Display artist name (default: true) |
| showAlbum? | boolean | Display album name (default: false) |
| showDuration? | boolean | Display duration (default: true) |
| numbered? | boolean | Show track numbers (default: true) |
| currentTrackId? | string | ID of currently playing track |
**Usage Example**:
```tsx
<TrackList
tracks={albumTracks}
onTrackClick={(song, index) => playTrack(song, index)}
showArtist={true}
showAlbum={false}
numbered={true}
currentTrackId={currentSong?.id}
/>
```
**Features**:
- Hover states for interactivity
- Currently playing track highlight
- Click to play functionality
- Responsive column layout
**Dependencies**: SongCard (data structure)
**Related**: AudioPlayer, AlbumCard, PlaylistCard
</details>
---
## Component Dependencies Tree
```
AudioPlayer
├── PlayerControls
└── WaveformDisplay
Header
├── NavLink
├── UserMenu
│ └── AuthForm
└── SearchBar
ShareButton
└── ShareModal
UploadForm
└── GenreBadge
TrackList
└── (uses Song type)
Standalone Components:
- SongCard
- AlbumCard
- ArtistCard
- PlaylistCard
- LabelCard
- ProfileForm
- CreatePlaylistModal
```
---
## Styling Conventions
All components use **Tailwind CSS** for styling with the following patterns:
- **Color Palette**: Primary (blue), Accent (green), Neutral (gray)
- **Spacing**: Consistent use of `p-4`, `m-2`, `gap-4` units
- **Typography**: `text-sm`, `text-base`, `text-lg`, `font-semibold`
- **Responsive**: Mobile-first with `sm:`, `md:`, `lg:` breakpoints
- **Dark Mode**: Not currently implemented (future consideration)
---
## Type Definitions
All components use types defined in `/types`:
- `types/song.ts` - Song, Album, Artist entities
- `types/user.ts` - User, AuthState
- `types/playlist.ts` - Playlist, PlaylistSong
- `types/api.ts` - API request/response types