# 🏗️ Jukebox Project Architecture ## System Overview Diagram ``` ┌─────────────────────────────────────────────────────────────────────┐ │ JUKEBOX MUSIC APPLICATION │ └─────────────────────────────────────────────────────────────────────┘ ┌──────────────────────────────┐ ┌──────────────────────────────┐ │ 📱 MOBILE APP (EXPO) │ │ 🌐 WEB ADMIN PANEL │ │ React Native 0.81.5 │ │ React 19.2.0 + Vite │ └──────────────────────────────┘ └──────────────────────────────┘ │ │ │ HTTP/REST API │ │ (Bearer Token Auth) │ └────────────────┬────────────────┘ │ ┌──────▼──────┐ │ 🔌 LARAVEL │ │ API │ │ Backend │ └──────┬──────┘ │ ┌──────▼──────┐ │ 🗄️ MySQL │ │ Database │ └─────────────┘ ``` --- ## Mobile App Architecture (React Native) ``` App.js (Root) │ ├─ PlayerProvider Context │ └─ { currentTrack, isPlaying, ... } │ └─ LibraryProvider Context ├─ albums (state from mock data) ├─ likedTracks (computed) ├─ toggleLike() (function) │ └─ SafeAreaProvider │ └─ AppNavigator (Stack Navigator) │ ├─ HomeScreen │ ├─ Header (with icons) │ ├─ Liked Tracks Preview (top 5) │ └─ Albums Grid (2 columns) │ ├─ AlbumScreen │ ├─ Album Info (cover, title, artist) │ └─ TrackRow List │ └─ TrackRow Component (with heart) │ ├─ LikedTracksScreen │ ├─ Search Input │ ├─ Sort Controls │ └─ FlatList of Tracks │ ├─ LoginScreen │ ├─ Email Input │ ├─ Password Input │ └─ Navigation Links │ ├─ SignUpScreen ├─ PasswordResetScreen └─ SettingsScreen ├─ Notification Toggle ├─ Audio Quality Toggle └─ Logout Button MediaPlayer Component (Floating) └─ Shows current track + play controls ``` --- ## Data Flow (Current - Mock Data) ``` app.json (expo config) │ index.js → App.js │ ├─ PlayerProvider │ └─ { currentTrack, isPlaying } │ └─ LibraryProvider │ ├─ useState(initialLibrary) │ └─ src/data/library.js ◄─── MOCK DATA │ ├─ Album 1: Swans (26 tracks) │ └─ Album 2: Daft Punk (14 tracks) │ ├─ useMemo(likedTracks) │ └─ Filters albums.flatMap(album.tracks).filter(liked) │ └─ toggleLike(albumId, trackId) └─ Updates album.tracks[].liked status HomeScreen ──┐ AlbumScreen ├─► useLibrary() ──► Read-only access to: LikedTracks │ • albums LoginScreen ─┘ • likedTracks • Call toggleLike() ``` --- ## Data Flow (Future - With API Integration) ``` ┌─────────────────────────────────────────────┐ │ BACKEND API (Laravel) │ ├─────────────────────────────────────────────┤ │ GET /api/albums │ │ GET /api/albums/{id} │ │ GET /api/tracks │ │ POST /api/login │ │ POST /api/tracks/{id}/like │ │ GET /api/me/likes │ └─────────────────────────────────────────────┘ ▲ │ LibraryContext (FUTURE) ├─ useEffect(() => { │ fetch('/api/albums') │ .then(data => setAlbums(data)) │ }, []) │ ├─ toggleLike() → API call instead │ └─ All screens read from state (same as before, but data from API) ``` --- ## Component Hierarchy ``` App.js │ └─ AppNavigator (Stack) │ ├─ HomeScreen │ ├─ Header │ │ ├─ Title │ │ └─ Icon buttons (Person, Settings) │ │ │ ├─ Pressable (navigate to LikedTracks) │ │ └─ Section title │ │ │ ├─ Map (liked tracks) │ │ └─ TrackRow ✕ 5 │ │ │ └─ FlatList (albums grid) │ └─ Album Item ✕ N │ ├─ AlbumScreen │ ├─ Back Button │ ├─ Album Header │ │ ├─ Image (album cover) │ │ ├─ Title │ │ ├─ Artist │ │ └─ Meta (date, label, duration) │ │ │ └─ FlatList (tracks) │ └─ TrackRow ✕ N │ ├─ LikedTracksScreen │ ├─ Back Button │ ├─ Header │ ├─ TextInput (search) │ ├─ Sort Controls │ │ ├─ Dropdown (sortBy) │ │ └─ Toggle (direction) │ │ │ └─ FlatList │ └─ TrackRow ✕ N │ ├─ LoginScreen ├─ SignUpScreen ├─ PasswordResetScreen └─ SettingsScreen └─ MediaPlayer (Floating) ├─ Track info (text) ├─ Progress bar (Slider) │ └─ Time stamps │ └─ Play/Pause button ``` --- ## State Management Strategy ### Global State (Contexts) ```javascript // 1. PlayerProvider (App.js) { currentTrack: Track | null, isPlaying: boolean, setCurrentTrack: (track) => void, setIsPlaying: (bool) => void } // 2. LibraryProvider (LibraryContext.js) { albums: Album[], likedTracks: Track[] (computed), toggleLike: (albumId, trackId) => void } ``` ### Local State (Components) ```javascript // HomeScreen None (all from context) // AlbumScreen None (all from context & route params) // LikedTracksScreen { query: string (search), sortBy: 'dateAdded' | 'name' | 'length', sortDir: 'asc' | 'desc', showSortMenu: boolean } // LoginScreen { email: string, password: string } // SettingsScreen { notificationsEnabled: boolean, highQuality: boolean } // MediaPlayer { isPlaying: boolean, progress: number (0-100) } ``` --- ## Navigation Stack ``` ┌────────────────────────────────────┐ │ React Navigation Stack │ ├────────────────────────────────────┤ │ Navigator: createNativeStackNavigator() │ Options: headerShown = false │ ├─ Screen: "Home" → HomeScreen ├─ Screen: "LikedTracks" → LikedTracksScreen ├─ Screen: "Album" → AlbumScreen │ params: { album: Album } ├─ Screen: "SignUp" → SignUpScreen ├─ Screen: "Login" → LoginScreen ├─ Screen: "PasswordReset"→ PasswordResetScreen └─ Screen: "Settings" → SettingsScreen ``` ### Navigation Methods ```javascript // From any screen with useNavigation hook: navigation.navigate('Album', { album }) navigation.navigate('LikedTracks') navigation.navigate('Login') navigation.navigate('Settings') navigation.goBack() ``` --- ## Admin Panel Architecture (React Web) ``` admin_panel/ │ ├─ main.jsx │ └─ ReactDOM.createRoot() │ │ │ └─ BrowserRouter │ │ │ └─ App.jsx │ │ │ ├─ AuthProvider │ │ ├─ token (from localStorage) │ │ ├─ user (from localStorage) │ │ ├─ login() function │ │ └─ logout() function │ │ │ └─ Routes │ │ │ ├─ Route: "/login" │ │ └─ LoginPage │ │ │ └─ ProtectedRoute (requires token) │ │ │ ├─ Route: "/albums" │ │ └─ AlbumsPage (stub) │ │ │ ├─ Route: "/albums/:albumId/tracks" │ │ └─ AlbumTracksPage (stub) │ │ │ └─ Route: "/users" │ └─ UsersPage (stub) ``` --- ## API Integration Layer ### Admin Panel API Client (`services/api.js`) ```javascript const api = { // Albums (all return Promise) getAlbums(), // GET /albums getAlbumById(id), // GET /albums/{id} addAlbum(data), // POST /albums updateAlbum(id, data), // PUT /albums/{id} deleteAlbum(id), // DELETE /albums/{id} // Tracks addTrack(data), // POST /tracks updateTrack(id, data), // PUT /tracks/{id} deleteTrack(id), // DELETE /tracks/{id} reorderTracks(albumId, positions), // Upload uploadImage(file), // POST /upload/image uploadAudio(file), // POST /upload/audio // Artists & Genres getArtists(), addArtist(), getGenres(), addGenre(), // Users (admin only) getUsers(), updateUser(id, data), deleteUser(id) } ``` ### Authentication Flow ``` 1. User inputs email/password ↓ 2. POST /api/login ├─ Response: { token: "1|abc...", user: {...} } ↓ 3. Store in localStorage ├─ token: "1|abc..." ├─ user: {...} ↓ 4. Include in all requests └─ Header: "Authorization: Bearer 1|abc..." 5. On 401 response └─ Clear localStorage & redirect to /login ``` --- ## Database Schema (Simplified) ``` Users ├─ id (PK) ├─ name ├─ email (unique) ├─ password_hash ├─ role_id (FK) ├─ created_at └─ updated_at ├─ has_many: Likes (through likes table) └─ belongs_to: Role Albums ├─ id (PK) ├─ title ├─ cover_path ├─ release_date ├─ duration_seconds ├─ label_id (FK) └─ has_many: Tracks Tracks ├─ id (PK) ├─ title ├─ file_path ├─ duration_seconds ├─ album_id (FK) ├─ belongs_to_many: Artists ├─ belongs_to_many: Genres └─ belongs_to_many: Users (likes table) Artists ├─ id (PK) ├─ name ├─ label_id (FK) └─ belongs_to_many: Tracks Genres ├─ id (PK) ├─ name └─ belongs_to_many: Tracks Labels ├─ id (PK) ├─ name ├─ has_many: Artists └─ has_many: Albums Roles ├─ id (PK) ├─ name ('admin' or 'user') └─ has_many: Users ``` --- ## Dependency Tree ``` Mobile App (React Native) ├─ react 19.1.0 ├─ react-native 0.81.5 ├─ expo 54.0.33 │ ├─ expo-linear-gradient │ ├─ expo-vector-icons │ ├─ expo-status-bar │ ├─ expo-av (NOT YET INSTALLED - needed for audio) │ └─ ... ├─ @react-navigation/native 7.1.31 │ ├─ @react-navigation/native-stack 7.14.2 │ └─ @react-navigation/stack 7.8.2 ├─ react-native-reanimated 4.1.1 ├─ react-native-safe-area-context 5.6.0 ├─ rn-inkpad 1.1.0 └─ react-native-vector-icons 10.3.0 Admin Panel (React Web) ├─ react 19.2.0 ├─ react-dom 19.2.0 ├─ react-router-dom 7.13.1 ├─ vite 7.3.1 └─ tailwindcss 4.0.0 Backend (Laravel) ├─ laravel/framework ├─ laravel/sanctum (authentication) ├─ composer (PHP dependency manager) └─ mysql (database) ``` --- ## Performance Considerations ### Current Issues ``` ❌ No pagination - all albums/tracks loaded at once ❌ No caching - re-fetches on every mount ❌ No image optimization - full-res images ❌ No lazy loading - all screens bundled ❌ No code splitting - entire app in one JS bundle ``` ### Optimization Opportunities ``` ✅ Add pagination to API (limit: 20, offset) ✅ Implement React Query / SWR for caching ✅ Use Image.cache() or similar for images ✅ Code split screens with React.lazy() ✅ Use FlatList/VirtualizedList keyExtractor efficiently ✅ Memoize expensive computations (useMemo) ✅ Debounce search input ✅ Cache API responses in AsyncStorage ``` --- ## Deployment Architecture (Future) ``` ┌─────────────────────────────────────────┐ │ Apple App Store / Google Play │ │ (Built from React Native) │ └──────────────────┬──────────────────────┘ │ │ HTTPS API calls ▼ ┌─────────────────────────────────────────┐ │ Production API Server (Laravel) │ ├─────────────────────────────────────────┤ │ - Environment: Ubuntu/CentOS Linux │ │ - Server: Nginx + PHP-FPM │ │ - Database: MySQL 8.0 │ │ - Cache: Redis (optional) │ │ - SSL: Let's Encrypt HTTPS │ └────────────┬────────────────────────────┘ │ ▼ ┌────────────────┐ │ MySQL DB │ └────────────────┘ ``` --- ## Security Considerations ### Current ``` ✅ Bearer token authentication (Sanctum) ✅ Password hashing ✅ HTTPS ready (needs SSL cert in production) ``` ### Missing ``` ❌ CORS configuration (using Sanctum defaults) ❌ Rate limiting ❌ API request validation/sanitization ❌ SQL injection prevention (use Eloquent ORM - good!) ❌ XSS prevention headers ❌ Token refresh mechanism ❌ Logout from all devices feature ``` --- ## Integration Checklist ``` PHASE 1: API Integration [ ] Connect HomeScreen to GET /api/albums [ ] Connect AlbumScreen to GET /api/albums/{id} [ ] Connect LoginScreen to POST /api/login [ ] Store token & user in AsyncStorage [ ] Add error handling for API failures PHASE 2: Authentication [ ] Implement token refresh logic [ ] Add logout functionality [ ] Persist login state across app restart [ ] Redirect to login on 401 response PHASE 3: Audio Playback [ ] Install expo-av package [ ] Implement TrackPlayer service [ ] Connect MediaPlayer to real playback [ ] Handle audio focus on mobile PHASE 4: Admin Panel [ ] Implement AlbumsPage.jsx [ ] Implement AlbumTracksPage.jsx [ ] Implement UsersPage.jsx [ ] Add file upload handling PHASE 5: Polish [ ] Add loading indicators [ ] Add error boundaries [ ] Implement search/filter on backend [ ] Add pagination [ ] Add offline mode ``` --- **Architecture Version**: 1.0 **Last Updated**: May 12, 2026 **Status**: Pre-integration phase