singsalot/docs/platforms/web/PRD-web.md

759 lines
25 KiB
Markdown

# Web Platform Implementation Guide
> **Platform:** Web (React + Ionic + TypeScript)
> **Core Business Logic:** See `../../PRD.md` for platform-agnostic requirements
This document contains **web-specific implementation details** for the Karaoke App. All business logic, data models, and core requirements are defined in the main PRD.
---
## Quick Reference
| Section | Purpose |
|---------|---------|
| [UI/UX Behavior](#uiux-behavior) | Web-specific UI patterns and interactions |
| [Component Architecture](#component-architecture) | React/Ionic component structure |
| [State Management](#state-management) | Redux Toolkit implementation |
| [Development Setup](#development-setup) | Web project configuration |
| [Design Assets](#design-assets) | Web-specific visual references |
| [Toolset Choices](#toolset-choices) | Web technology rationale |
---
## UI/UX Behavior
### **Web-Specific Requirements:**
#### **Responsive Design:**
- **Mobile-first approach** with touch-friendly interface
- **Progressive Web App (PWA)** capabilities for app-like experience
- **Responsive breakpoints** for tablet and desktop views
- **Touch gestures** for queue reordering and navigation
#### **Ionic React Components:**
- **IonApp** as root container with proper theming
- **IonRouterOutlet** for navigation between pages
- **IonList** and **IonItem** for song and queue displays
- **IonButton** with proper touch targets (44px minimum)
- **IonSearchbar** for song search with debounced input
- **IonModal** for singer selection and settings
- **IonToast** for user feedback and notifications
#### **Navigation Patterns:**
- **Tab-based navigation** for main sections (Queue, Search, History, etc.)
- **Stack navigation** for detail views (song info, artist pages)
- **Modal overlays** for quick actions (singer selection, settings)
- **Deep linking** support for direct page access
#### **Real-time Updates:**
- **Live queue updates** with visual indicators for changes
- **Playback state synchronization** across all connected devices
- **User presence indicators** showing who's currently active
- **Optimistic UI updates** with rollback on errors
---
## Codebase Organization & File Structure
### **Web Project Structure:**
```
src/
├── components/ # Reusable UI components
│ ├── Auth/ # Authentication components
│ ├── common/ # Shared components (SongItem, ActionButton, etc.)
│ ├── Layout/ # Layout and navigation components
│ └── Navigation/ # Navigation components
├── features/ # Feature-specific components
│ ├── Artists/ # Artist browsing feature
│ ├── Favorites/ # Favorites management
│ ├── History/ # Play history
│ ├── NewSongs/ # New songs feature
│ ├── Queue/ # Queue management
│ ├── Search/ # Song search
│ ├── Settings/ # Settings and admin features
│ ├── Singers/ # Singer management
│ ├── SongLists/ # Song lists feature
│ └── TopPlayed/ # Top played songs
├── firebase/ # Firebase configuration and services
│ ├── config.ts # Firebase configuration
│ ├── services.ts # Firebase service layer
│ └── useFirebase.ts # Firebase hooks
├── hooks/ # Custom React hooks
│ ├── useQueue.ts # Queue management hooks
│ ├── useSearch.ts # Search functionality hooks
│ ├── useFavorites.ts # Favorites management hooks
│ └── ... # Other feature hooks
├── redux/ # State management
│ ├── store.ts # Redux store configuration
│ ├── slices/ # Redux slices
│ ├── selectors.ts # Memoized selectors
│ └── hooks.ts # Redux hooks
├── types/ # TypeScript type definitions
└── utils/ # Utility functions
```
### **File Organization Rules:**
| Folder | Purpose | Key Files | Import Pattern |
|--------|---------|-----------|----------------|
| `/components` | Reusable UI components | `SongItem.tsx`, `ActionButton.tsx` | `import { SongItem } from '../components/common'` |
| `/features` | Feature-specific pages | `Queue.tsx`, `Search.tsx` | `import { Queue } from '../features/Queue'` |
| `/firebase` | Firebase integration | `services.ts`, `config.ts` | `import { queueService } from '../firebase/services'` |
| `/hooks` | Custom business logic | `useQueue.ts`, `useSearch.ts` | `import { useQueue } from '../hooks/useQueue'` |
| `/redux` | State management | `controllerSlice.ts`, `authSlice.ts`, `selectors.ts` | `import { useAppDispatch, selectQueue } from '../redux'` |
| `/types` | TypeScript definitions | `index.ts` (extends docs/types.ts) | `import type { Song, QueueItem } from '../types'` |
### **Import Patterns:**
#### **1. Redux Imports:**
```typescript
// Always import from the main redux index
import { useAppDispatch, useAppSelector } from '../redux';
import { selectQueue, selectSongs } from '../redux';
import { setController, updateQueue } from '../redux';
// ❌ Don't import directly from slice files
// ❌ import { selectQueue } from '../redux/controllerSlice';
// ✅ Always use the main redux index
// ✅ import { selectQueue } from '../redux';
```
#### **2. Firebase Service Imports:**
```typescript
// Import services from the main services file
import { queueService, searchService } from '../firebase/services';
// ❌ Don't import directly from individual service files
// ❌ import { addToQueue } from '../firebase/queueService';
// ✅ Always use the main services file
// ✅ import { queueService } from '../firebase/services';
```
#### **3. Type Imports:**
```typescript
// Always use type imports for TypeScript interfaces
import type { Song, QueueItem, Singer } from '../types';
// ❌ Don't import types from individual files
// ❌ import { Song } from '../types/Song';
// ✅ Always use the main types index
// ✅ import type { Song } from '../types';
```
#### **4. Hook Imports:**
```typescript
// Import hooks from their specific files
import { useQueue } from '../hooks/useQueue';
import { useSearch } from '../hooks/useSearch';
import { useFavorites } from '../hooks/useFavorites';
// ❌ Don't import from a general hooks index
// ❌ import { useQueue } from '../hooks';
// ✅ Import from specific hook files
// ✅ import { useQueue } from '../hooks/useQueue';
```
#### **5. Component Imports:**
```typescript
// Import components from their specific folders
import { SongItem } from '../components/common/SongItem';
import { ActionButton } from '../components/common/ActionButton';
import { Layout } from '../components/Layout/Layout';
// ❌ Don't import from general component indices
// ❌ import { SongItem } from '../components';
// ✅ Import from specific component files
// ✅ import { SongItem } from '../components/common/SongItem';
```
### **Architecture Flow:**
```
UI Components → Custom Hooks → Redux State → Firebase Services → Firebase Database
```
### **Separation of Concerns:**
- **Components:** Only handle UI presentation and user interactions
- **Hooks:** Contain business logic and data management
- **Redux:** Manage global application state
- **Services:** Handle Firebase operations and data persistence
- **Types:** Define data structures and interfaces
## Component Architecture
### **React Component Structure:**
#### **Page Components:**
```typescript
// src/features/Queue/Queue.tsx
interface QueueProps {
// Props interface
}
const Queue: React.FC<QueueProps> = () => {
// Component implementation
}
```
#### **Common Components:**
```typescript
// src/components/common/SongItem.tsx
interface SongItemProps {
song: Song;
onAddToQueue: (song: Song) => void;
onAddToFavorites: (song: Song) => void;
showAddButton?: boolean;
showFavoriteButton?: boolean;
}
```
#### **Layout Components:**
```typescript
// src/components/Layout/Layout.tsx
interface LayoutProps {
children: React.ReactNode;
title?: string;
showBackButton?: boolean;
}
```
### **Ionic Integration:**
- **IonPage** wrapper for all page components
- **IonHeader** with dynamic titles and action buttons
- **IonContent** with proper scrolling behavior
- **IonFooter** for player controls and queue management
- **IonLoading** for async operations and data fetching
---
## State Management
### **Redux Architecture Pattern:**
The web implementation uses Redux Toolkit for predictable state management with excellent TypeScript support.
#### **Store Structure:**
```typescript
// src/redux/store.ts
export const store = configureStore({
reducer: {
auth: authSlice,
player: playerSlice,
queue: queueSlice,
controller: controllerSlice,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST'],
},
}),
});
```
#### **Slice Patterns:**
```typescript
// src/redux/controllerSlice.ts
const controllerSlice = createSlice({
name: 'controller',
initialState,
reducers: {
setController: (state, action) => {
state.data = action.payload;
},
updateQueue: (state, action) => {
state.data.player.queue = action.payload;
},
},
});
```
#### **Selectors:**
```typescript
// src/redux/selectors.ts
export const selectQueue = (state: RootState) => state.controller.data?.player?.queue || {};
export const selectQueueLength = createSelector(
[selectQueue],
(queue) => Object.keys(queue).length
);
```
### **Redux Toolkit Implementation:**
#### **Store Structure:**
```typescript
// src/redux/store.ts
export const store = configureStore({
reducer: {
auth: authSlice,
player: playerSlice,
queue: queueSlice,
controller: controllerSlice,
},
middleware: (getDefaultMiddleware) =>
getDefaultMiddleware({
serializableCheck: {
ignoredActions: ['persist/PERSIST'],
},
}),
});
```
#### **Slice Patterns:**
```typescript
// src/redux/queueSlice.ts
const queueSlice = createSlice({
name: 'queue',
initialState,
reducers: {
addToQueue: (state, action) => {
// Implementation
},
removeFromQueue: (state, action) => {
// Implementation
},
},
});
```
#### **Selectors:**
```typescript
// src/redux/selectors.ts
export const selectQueue = (state: RootState) => state.queue.items;
export const selectQueueLength = createSelector(
[selectQueue],
(queue) => queue.length
);
```
---
## Development Setup
### **Project Configuration:**
#### **Package.json Dependencies:**
```json
{
"dependencies": {
"@ionic/react": "^7.0.0",
"@reduxjs/toolkit": "^1.9.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.0.0",
"firebase": "^9.0.0"
},
"devDependencies": {
"@types/react": "^18.0.0",
"@types/react-dom": "^18.0.0",
"typescript": "^4.9.0",
"vite": "^4.0.0"
}
}
```
#### **Vite Configuration:**
```typescript
// vite.config.ts
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': path.resolve(__dirname, './src'),
},
},
build: {
outDir: 'dist',
sourcemap: true,
},
});
```
#### **TypeScript Configuration:**
```json
// tsconfig.json
{
"compilerOptions": {
"target": "ES2020",
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
"skipLibCheck": true,
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true,
"noEmit": true,
"jsx": "react-jsx",
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
}
}
```
### **Environment Setup:**
```bash
# .env.local
VITE_FIREBASE_API_KEY=your_api_key
VITE_FIREBASE_AUTH_DOMAIN=your_project.firebaseapp.com
VITE_FIREBASE_PROJECT_ID=your_project_id
VITE_FIREBASE_STORAGE_BUCKET=your_project.appspot.com
VITE_FIREBASE_MESSAGING_SENDER_ID=your_sender_id
VITE_FIREBASE_APP_ID=your_app_id
VITE_FIREBASE_DATABASE_URL=https://your_project-default-rtdb.firebaseio.com
```
---
## Design Assets
### **Web-Specific Mockups:**
Located in `design/` with 30+ mockups covering all features:
#### **Core Navigation & Layout:**
- `00-web-layout.JPG` - Overall web layout structure
- `01-Login.png` - Login screen design
- `02-menu.jpeg`, `02a-menu.jpeg`, `02b-menu.png`, `02c-menu.jpeg` - Navigation menu variations
#### **Queue Management:**
- `02-queue.png` - Main queue view
- `02-queue-delete.png` - Queue with delete functionality
- `02-queue-drag.png` - Queue reordering with drag and drop
- `02-queue-sorting.png` - Queue sorting interface
#### **Search & Discovery:**
- `04-search.png` - Main search interface
- `04-search typing .png` - Search with typing interaction
- `04-search-song info.png` - Search results with song information
#### **User Management:**
- `05-singers.png` - Singer list view
- `05-singers add.png` - Singer management interface
#### **Content Browsing:**
- `06-artists .png` - Artist browse interface
- `06-artists (not admin).png` - Non-admin artist view
- `06-artists search.png` - Artist search functionality
- `06-artists songs.png` - Artist songs list
#### **User Features:**
- `07-favorites.png` - Favorites management
- `08-history.png` - Play history view
- `09-songs list.png` - Main song lists view
- `09-song lists - songs.png` - Song lists with song details
- `09- song lists songs expand.png` - Song lists with expandable sections
#### **Admin Features:**
- `10-Settings.png` - Settings interface
- `11-top 100.png` - Top played songs
- `12-favorites .png` - Favorites view
- `12-favorite lists.png` - Favorite lists management
#### **Menu States:**
- `03-menu.png` - General menu layout
- `03-menu current page and non-admin.png` - Navigation with current page indicators
- `03-menu playing (admin).png` - Admin view during playback
### **Design System:**
- **Ionic Design System** for consistent UI components
- **Custom CSS variables** for theming and branding
- **Responsive grid system** for layout consistency
- **Touch-friendly spacing** and sizing guidelines
---
## Toolset Choices
### **Current Web Implementation Toolset:**
#### **Core Framework: React 18 + TypeScript**
- **Why:** Chosen for its component-based architecture, strong ecosystem, and excellent TypeScript support
- **Migration Note:** Can be replaced with any other component-based framework (e.g., Vue, Svelte)
#### **State Management: Redux Toolkit**
- **Why:** Provides predictable state management with excellent TypeScript support and dev tools
- **Migration Note:** Can be replaced with Context API, Zustand, or other state management solutions
#### **UI Framework: Ionic React**
- **Why:** Provides mobile-first UI components with excellent touch support and PWA capabilities
- **Migration Note:** Can be replaced with Material-UI, Chakra UI, or custom components
#### **Build Tool: Vite**
- **Why:** Fast development server and optimized builds with excellent TypeScript support
- **Migration Note:** Can be replaced with Webpack, Parcel, or other build tools
#### **Backend: Firebase Realtime Database**
- **Why:** Real-time synchronization, simple setup, and excellent React integration
- **Migration Note:** Can be replaced with any real-time database (Supabase, AWS AppSync, etc.)
### **Development Tools:**
- **TypeScript:** Type safety and better developer experience
- **ESLint:** Code quality and consistency
- **Prettier:** Code formatting
- **React DevTools:** Component debugging
- **Redux DevTools:** State management debugging
---
## Web-Specific Implementation Details
### **Authentication & Session Management:**
- **Admin Access:** Triggered by URL parameter (`?admin=true`), removed after login
- **Session Persistence:** Lost on browser reload (no persistent storage)
- **Admin Mode:** Pre-fills singer name as "Admin" for convenience
- **URL Handling:** Admin parameter automatically removed from URL after authentication
### **Search Implementation:**
- **Pagination:** Uses Ionic InfiniteScrollList component for efficient loading
- **Debouncing:** Search input debounced to prevent excessive API calls
- **Real-time Results:** Instant search results as user types
- **Context Actions:** Add to queue, favorite, and other actions available per song
### **Queue Management:**
- **UI Controls:** Reordering and deletion exposed via UI controls only visible to admins
- **Drag & Drop:** Uses Ionic drag handles and swipe actions for reordering
- **Visual Feedback:** Clear indicators for admin-only actions
- **State Synchronization:** Real-time updates across all connected clients
### **Favorites Implementation:**
- **Infinite Scroll:** Uses Ionic InfiniteScrollList for pagination
- **Real-time Sync:** Shared favorites list synchronized across all clients
- **Duplicate Prevention:** Prevents duplicates using song path field
- **User Actions:** Anyone can add/remove favorites
### **New Songs Implementation:**
- **Infinite Scroll:** Uses Ionic InfiniteScrollList for pagination
- **Real-time Updates:** Shows recently added songs from newSongs node
- **Automatic Loading:** Progressive loading as user scrolls
### **Artists Implementation:**
- **Modal Views:** Uses Ionic modals for artist song lists
- **Infinite Scroll:** Uses Ionic InfiniteScrollList for pagination
- **Search Integration:** Artist search functionality included
- **Song Counts:** Displays song count per artist
### **Song Lists Implementation:**
- **Modal Interface:** Uses Ionic modals for viewing list contents
- **Infinite Scroll:** Uses Ionic InfiniteScrollList for pagination
- **Expandable Views:** Available versions shown with expandable sections
- **Availability Status:** Shows which songs are available in catalog
### **History Implementation:**
- **Infinite Scroll:** Uses Ionic InfiniteScrollList for pagination
- **Automatic Tracking:** Songs automatically added when played
- **Timestamp Display:** Shows when each song was last played
- **Append-only:** History is append-only, shared across all clients
### **Top Played Implementation:**
- **Infinite Scroll:** Uses Ionic InfiniteScrollList for pagination
- **Play Count Display:** Shows play count for each song
- **Real-time Updates:** Popular songs generated by backend
- **Backend Integration:** Based on history data
### **Singer Management:**
- **Admin-only UI:** Singer management available only to admins via settings page
- **Unique Names:** Singer names must be unique and non-empty
- **Auto-addition:** Singers automatically added when they join
- **Last Join Tracking:** Tracks when each singer last joined
### **Playback Control:**
- **Admin-only Controls:** Player controls only rendered for admins
- **State-based UI:** Play/pause/stop buttons shown/hidden based on current state
- **Queue Validation:** Play button disabled when queue is empty
- **Real-time Sync:** Playback state synchronized across all clients
### **Error Handling & Sync:**
- **Toast Notifications:** Uses web-specific toast notifications for user feedback
- **Error Boundaries:** React error boundaries for component-level error handling
- **Retry Patterns:** Automatic retry for failed Firebase operations
- **Connection Monitoring:** Real-time connection status tracking
### **Disabled Songs:**
- **Modal Management:** Disabled songs managed via modal dialog
- **Search & Filter:** Search and filter capabilities in management interface
- **Hash Storage:** Disabled songs stored using hash of song path
- **Admin-only Access:** Only admins can disable or enable songs
### **Settings Implementation:**
- **Debug Logging:** Web-only feature, toggled via settings page
- **Admin-only Access:** Only admins can change player settings
- **Real-time Updates:** Settings changes synchronized across all clients
- **UI Controls:** Settings interface with toggle switches and controls
### **Navigation Implementation:**
- **Admin-based Navigation:** Navigation adapts based on admin status
- **Hidden Pages:** Settings page only visible to admins
- **Role-based UI:** Different navigation options for different user roles
- **Dynamic Menus:** Menu items shown/hidden based on permissions
### **UI/UX Implementation:**
- **Ionic React:** Uses Ionic React and Tailwind CSS for UI
- **Component Library:** Infinite scroll, swipe actions, modals, and toasts using Ionic components
- **Responsive Design:** Mobile-first approach with touch-friendly interface
- **Theme Support:** Light/dark mode support with consistent styling
## Implementation Notes
### **Critical Web-Specific Patterns:**
#### **Firebase Integration:**
```typescript
// src/firebase/services.ts
export const queueService = {
addToQueue: async (song: Song, singerId: string) => {
const newKey = await getNextKey('queue');
await set(ref(database, `queue/${newKey}`), {
songId: song.id,
singerId,
timestamp: Date.now(),
key: newKey,
});
},
};
```
#### **Real-time Listeners:**
```typescript
// src/hooks/useQueue.ts
useEffect(() => {
const queueRef = ref(database, 'queue');
const unsubscribe = onValue(queueRef, (snapshot) => {
const data = snapshot.val();
if (data) {
const queueItems = Object.values(data) as QueueItem[];
dispatch(setQueue(queueItems));
}
});
return () => unsubscribe();
}, [dispatch]);
```
#### **Error Handling:**
```typescript
// src/hooks/useFirebaseSync.ts
const retryOperation = async (operation: () => Promise<void>, maxRetries = 3) => {
for (let i = 0; i < maxRetries; i++) {
try {
await operation();
return;
} catch (error) {
if (i === maxRetries - 1) throw error;
await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
}
}
};
```
### **Hook Implementation Patterns:**
#### **Custom Hook Structure:**
```typescript
// src/hooks/useQueue.ts
export const useQueue = () => {
const dispatch = useAppDispatch();
const queue = useAppSelector(selectQueue);
const [loading, setLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const addToQueue = useCallback(async (song: Song, singerId: string) => {
try {
setLoading(true);
setError(null);
await queueService.addToQueue(song, singerId);
} catch (err) {
setError(err instanceof Error ? err.message : 'Failed to add to queue');
} finally {
setLoading(false);
}
}, []);
return {
queue,
loading,
error,
addToQueue,
};
};
```
#### **Hook Dependencies:**
- **Hooks depend on Redux state** and services
- **Business logic** implemented in hooks, not components
- **Error handling** and loading states managed in hooks
- **Memoization** used for expensive operations
#### **Hook Usage in Components:**
```typescript
// src/features/Queue/Queue.tsx
const Queue: React.FC = () => {
const { queue, loading, error, addToQueue } = useQueue();
const { songs } = useSongs();
const handleAddToQueue = useCallback((song: Song) => {
addToQueue(song, currentSingerId);
}, [addToQueue, currentSingerId]);
if (loading) return <LoadingSpinner />;
if (error) return <ErrorMessage message={error} />;
return (
<IonList>
{Object.values(queue).map((item) => (
<SongItem
key={item.key}
song={songs[item.songId]}
onAddToQueue={handleAddToQueue}
/>
))}
</IonList>
);
};
```
### **Performance Optimizations:**
- **React.memo** for expensive components
- **useMemo** and **useCallback** for expensive calculations
- **Redux selectors** with memoization
- **Lazy loading** for route-based code splitting
- **Service worker** for PWA caching
### **Critical Implementation Rules:**
- **Never import directly from slice files** - always use the main redux index
- **Always use type imports** for TypeScript interfaces
- **Implement business logic in hooks**, not components
- **Use memoization** for expensive operations
- **Handle loading and error states** in all async operations
---
## Migration Guide
### **To Other Web Frameworks:**
#### **Vue.js Migration:**
- Replace React components with Vue components
- Replace Redux with Pinia or Vuex
- Replace Ionic React with Ionic Vue
- Keep all business logic and Firebase integration
#### **Svelte Migration:**
- Replace React components with Svelte components
- Replace Redux with Svelte stores
- Replace Ionic React with Ionic Svelte
- Keep all business logic and Firebase integration
### **To Mobile Platforms:**
#### **React Native Migration:**
- Replace Ionic components with React Native components
- Replace web-specific APIs with React Native APIs
- Keep Redux state management
- Keep all business logic and Firebase integration
#### **Native iOS/Android Migration:**
- Replace React components with native UI components
- Replace Redux with native state management
- Keep all business logic and Firebase integration
- Adapt UI patterns to platform conventions
---
_This document contains web-specific implementation details. For core business logic and platform-agnostic requirements, see the main `../../PRD.md`._