workflow_version: "v001" feature: "Music platform for musicians to upload songs" created_at: "2025-12-18T15:10:00Z" status: draft revision: 1 # ═══════════════════════════════════════════════════════════════ # LAYER 1: DATA MODELS # ═══════════════════════════════════════════════════════════════ data_models: - id: model_user name: User table_name: users description: Base user entity with authentication fields: - name: id type: uuid constraints: [primary_key] - name: email type: string constraints: [unique, not_null] - name: password_hash type: string constraints: [not_null] - name: name type: string constraints: [not_null] - name: role type: enum values: [musician, listener, label] constraints: [not_null] - name: email_verified type: boolean default: false - name: avatar_url type: string constraints: [nullable] - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: has_one to: model_artist foreign_key: user_id condition: "role = 'musician'" - type: has_one to: model_label foreign_key: user_id condition: "role = 'label'" - type: has_many to: model_playlist foreign_key: user_id indexes: - fields: [email] unique: true - fields: [role] timestamps: true - id: model_artist name: Artist table_name: artists description: Extended profile for musicians fields: - name: id type: uuid constraints: [primary_key] - name: user_id type: uuid constraints: [not_null, foreign_key] references: users.id - name: stage_name type: string constraints: [not_null] - name: bio type: text constraints: [nullable] - name: cover_image_url type: string constraints: [nullable] - name: social_links type: jsonb description: "JSON object with {twitter, instagram, facebook, website}" constraints: [nullable] - name: verified type: boolean default: false - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: belongs_to to: model_user foreign_key: user_id - type: has_many to: model_song foreign_key: artist_id - type: has_many to: model_album foreign_key: artist_id - type: belongs_to to: model_label foreign_key: label_id optional: true indexes: - fields: [user_id] unique: true - fields: [stage_name] timestamps: true - id: model_label name: Label table_name: labels description: Organization profile for labels fields: - name: id type: uuid constraints: [primary_key] - name: user_id type: uuid constraints: [not_null, foreign_key] references: users.id - name: label_name type: string constraints: [not_null] - name: description type: text constraints: [nullable] - name: logo_url type: string constraints: [nullable] - name: website type: string constraints: [nullable] - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: belongs_to to: model_user foreign_key: user_id - type: has_many to: model_artist foreign_key: label_id indexes: - fields: [user_id] unique: true timestamps: true - id: model_genre name: Genre table_name: genres description: Music category for discovery fields: - name: id type: uuid constraints: [primary_key] - name: name type: string constraints: [unique, not_null] - name: slug type: string constraints: [unique, not_null] - name: description type: text constraints: [nullable] - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: has_many to: model_song through: song_genres foreign_key: genre_id indexes: - fields: [slug] unique: true timestamps: true - id: model_album name: Album table_name: albums description: Collection of songs fields: - name: id type: uuid constraints: [primary_key] - name: artist_id type: uuid constraints: [not_null, foreign_key] references: artists.id - name: title type: string constraints: [not_null] - name: description type: text constraints: [nullable] - name: cover_art_url type: string constraints: [nullable] - name: release_date type: date constraints: [nullable] - name: album_type type: enum values: [album, ep, single] default: album - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: belongs_to to: model_artist foreign_key: artist_id - type: has_many to: model_song foreign_key: album_id indexes: - fields: [artist_id] - fields: [release_date] timestamps: true - id: model_song name: Song table_name: songs description: Audio track with metadata fields: - name: id type: uuid constraints: [primary_key] - name: artist_id type: uuid constraints: [not_null, foreign_key] references: artists.id - name: album_id type: uuid constraints: [nullable, foreign_key] references: albums.id - name: title type: string constraints: [not_null] - name: duration type: integer description: Duration in seconds constraints: [not_null] - name: file_url type: string description: Cloud storage URL for audio file constraints: [not_null] - name: file_format type: enum values: [mp3, wav] constraints: [not_null] - name: file_size type: integer description: File size in bytes constraints: [not_null] - name: waveform_data type: jsonb description: Waveform visualization data constraints: [nullable] - name: cover_art_url type: string constraints: [nullable] - name: release_date type: date constraints: [nullable] - name: play_count type: integer default: 0 - name: is_public type: boolean default: true - name: track_number type: integer description: Position in album constraints: [nullable] - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: belongs_to to: model_artist foreign_key: artist_id - type: belongs_to to: model_album foreign_key: album_id optional: true - type: has_many to: model_genre through: song_genres foreign_key: song_id - type: has_many to: model_playlist_song foreign_key: song_id indexes: - fields: [artist_id] - fields: [album_id] - fields: [release_date] - fields: [play_count] - fields: [is_public] timestamps: true - id: model_song_genre name: SongGenre table_name: song_genres description: Junction table for song-genre many-to-many fields: - name: song_id type: uuid constraints: [not_null, foreign_key] references: songs.id - name: genre_id type: uuid constraints: [not_null, foreign_key] references: genres.id relations: - type: belongs_to to: model_song foreign_key: song_id - type: belongs_to to: model_genre foreign_key: genre_id indexes: - fields: [song_id, genre_id] unique: true timestamps: false - id: model_playlist name: Playlist table_name: playlists description: User-created song collection fields: - name: id type: uuid constraints: [primary_key] - name: user_id type: uuid constraints: [not_null, foreign_key] references: users.id - name: name type: string constraints: [not_null] - name: description type: text constraints: [nullable] - name: cover_image_url type: string constraints: [nullable] - name: is_public type: boolean default: false - name: created_at type: timestamp constraints: [not_null] - name: updated_at type: timestamp constraints: [not_null] relations: - type: belongs_to to: model_user foreign_key: user_id - type: has_many to: model_playlist_song foreign_key: playlist_id indexes: - fields: [user_id] - fields: [is_public] timestamps: true - id: model_playlist_song name: PlaylistSong table_name: playlist_songs description: Junction table with ordering for playlists fields: - name: id type: uuid constraints: [primary_key] - name: playlist_id type: uuid constraints: [not_null, foreign_key] references: playlists.id - name: song_id type: uuid constraints: [not_null, foreign_key] references: songs.id - name: position type: integer constraints: [not_null] - name: added_at type: timestamp constraints: [not_null] relations: - type: belongs_to to: model_playlist foreign_key: playlist_id - type: belongs_to to: model_song foreign_key: song_id indexes: - fields: [playlist_id, position] unique: true - fields: [playlist_id, song_id] unique: true timestamps: false # ═══════════════════════════════════════════════════════════════ # LAYER 2: API ENDPOINTS # ═══════════════════════════════════════════════════════════════ api_endpoints: # ───────────────────────────────────────────────────────────── # AUTH ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_register method: POST path: /api/auth/register description: Register new user account request_body: email: string password: string name: string role: enum[musician, listener, label] responses: - status: 201 description: User created successfully schema: user: id: uuid email: string name: string role: string token: string - status: 400 description: Validation error schema: error: string - status: 409 description: Email already exists schema: error: string auth: required: false depends_on_models: [model_user] - id: api_login method: POST path: /api/auth/login description: Login with email and password request_body: email: string password: string responses: - status: 200 description: Login successful schema: user: id: uuid email: string name: string role: string token: string - status: 401 description: Invalid credentials schema: error: string auth: required: false depends_on_models: [model_user] - id: api_forgot_password method: POST path: /api/auth/forgot-password description: Request password reset email request_body: email: string responses: - status: 200 description: Reset email sent schema: message: string - status: 404 description: Email not found schema: error: string auth: required: false depends_on_models: [model_user] - id: api_reset_password method: POST path: /api/auth/reset-password description: Reset password with token request_body: token: string password: string responses: - status: 200 description: Password reset successful schema: message: string - status: 400 description: Invalid or expired token schema: error: string auth: required: false depends_on_models: [model_user] # ───────────────────────────────────────────────────────────── # USER ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_get_current_user method: GET path: /api/users/me description: Get current user profile responses: - status: 200 description: User profile schema: id: uuid email: string name: string role: string avatar_url: string auth: required: true depends_on_models: [model_user] - id: api_update_current_user method: PUT path: /api/users/me description: Update current user profile request_body: name: string avatar_url: string responses: - status: 200 description: User updated schema: id: uuid email: string name: string avatar_url: string auth: required: true depends_on_models: [model_user] # ───────────────────────────────────────────────────────────── # ARTIST ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_create_artist_profile method: POST path: /api/artists description: Create artist profile (musicians only) request_body: stage_name: string bio: string cover_image_url: string social_links: twitter: string instagram: string facebook: string website: string responses: - status: 201 description: Artist profile created schema: id: uuid stage_name: string bio: string cover_image_url: string - status: 403 description: User is not a musician schema: error: string auth: required: true roles: [musician] depends_on_models: [model_artist, model_user] - id: api_get_artist method: GET path: /api/artists/:id description: Get artist profile by ID responses: - status: 200 description: Artist profile schema: id: uuid stage_name: string bio: string cover_image_url: string social_links: object verified: boolean - status: 404 description: Artist not found schema: error: string auth: required: false depends_on_models: [model_artist] - id: api_update_artist method: PUT path: /api/artists/:id description: Update artist profile request_body: stage_name: string bio: string cover_image_url: string social_links: object responses: - status: 200 description: Artist updated schema: id: uuid stage_name: string bio: string - status: 403 description: Unauthorized schema: error: string auth: required: true owner_only: true depends_on_models: [model_artist] - id: api_get_artist_songs method: GET path: /api/artists/:id/songs description: Get all songs by artist responses: - status: 200 description: List of songs schema: songs: - id: uuid title: string duration: integer cover_art_url: string play_count: integer auth: required: false depends_on_models: [model_artist, model_song] - id: api_get_artist_albums method: GET path: /api/artists/:id/albums description: Get all albums by artist responses: - status: 200 description: List of albums schema: albums: - id: uuid title: string cover_art_url: string release_date: string album_type: string auth: required: false depends_on_models: [model_artist, model_album] # ───────────────────────────────────────────────────────────── # SONG ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_upload_song method: POST path: /api/songs/upload description: Upload new song (musicians only) request_body: file: binary title: string album_id: uuid genre_ids: array[uuid] release_date: string track_number: integer responses: - status: 201 description: Song uploaded successfully schema: id: uuid title: string file_url: string duration: integer - status: 400 description: Invalid file format or size schema: error: string - status: 403 description: User is not a musician schema: error: string auth: required: true roles: [musician] depends_on_models: [model_song, model_artist] - id: api_get_song method: GET path: /api/songs/:id description: Get song details responses: - status: 200 description: Song details schema: id: uuid title: string duration: integer file_url: string cover_art_url: string artist: id: uuid stage_name: string album: id: uuid title: string genres: array play_count: integer auth: required: false depends_on_models: [model_song, model_artist, model_album] - id: api_update_song method: PUT path: /api/songs/:id description: Update song metadata request_body: title: string album_id: uuid genre_ids: array[uuid] release_date: string is_public: boolean responses: - status: 200 description: Song updated schema: id: uuid title: string - status: 403 description: Unauthorized schema: error: string auth: required: true owner_only: true depends_on_models: [model_song] - id: api_delete_song method: DELETE path: /api/songs/:id description: Delete song responses: - status: 204 description: Song deleted - status: 403 description: Unauthorized schema: error: string auth: required: true owner_only: true depends_on_models: [model_song] - id: api_increment_play_count method: POST path: /api/songs/:id/play description: Increment play count request_body: null # Action endpoint - no body needed responses: - status: 200 description: Play count incremented schema: play_count: integer auth: required: false depends_on_models: [model_song] # ───────────────────────────────────────────────────────────── # ALBUM ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_create_album method: POST path: /api/albums description: Create new album request_body: title: string description: string cover_art_url: string release_date: string album_type: enum[album, ep, single] responses: - status: 201 description: Album created schema: id: uuid title: string cover_art_url: string auth: required: true roles: [musician] depends_on_models: [model_album, model_artist] - id: api_get_album method: GET path: /api/albums/:id description: Get album details with songs responses: - status: 200 description: Album details schema: id: uuid title: string description: string cover_art_url: string release_date: string artist: id: uuid stage_name: string songs: - id: uuid title: string duration: integer track_number: integer auth: required: false depends_on_models: [model_album, model_song, model_artist] - id: api_update_album method: PUT path: /api/albums/:id description: Update album metadata request_body: title: string description: string cover_art_url: string release_date: string responses: - status: 200 description: Album updated schema: id: uuid title: string auth: required: true owner_only: true depends_on_models: [model_album] - id: api_delete_album method: DELETE path: /api/albums/:id description: Delete album responses: - status: 204 description: Album deleted - status: 403 description: Unauthorized schema: error: string auth: required: true owner_only: true depends_on_models: [model_album] # ───────────────────────────────────────────────────────────── # PLAYLIST ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_create_playlist method: POST path: /api/playlists description: Create new playlist request_body: name: string description: string is_public: boolean responses: - status: 201 description: Playlist created schema: id: uuid name: string description: string auth: required: true depends_on_models: [model_playlist] - id: api_get_user_playlists method: GET path: /api/playlists description: Get current user's playlists responses: - status: 200 description: List of playlists schema: playlists: - id: uuid name: string cover_image_url: string song_count: integer auth: required: true depends_on_models: [model_playlist] - id: api_get_playlist method: GET path: /api/playlists/:id description: Get playlist details with songs responses: - status: 200 description: Playlist details schema: id: uuid name: string description: string songs: - id: uuid title: string artist: stage_name: string position: integer auth: required: false depends_on_models: [model_playlist, model_playlist_song] - id: api_update_playlist method: PUT path: /api/playlists/:id description: Update playlist metadata request_body: name: string description: string is_public: boolean responses: - status: 200 description: Playlist updated schema: id: uuid name: string auth: required: true owner_only: true depends_on_models: [model_playlist] - id: api_delete_playlist method: DELETE path: /api/playlists/:id description: Delete playlist responses: - status: 204 description: Playlist deleted auth: required: true owner_only: true depends_on_models: [model_playlist] - id: api_add_song_to_playlist method: POST path: /api/playlists/:id/songs description: Add song to playlist request_body: song_id: uuid position: integer responses: - status: 201 description: Song added to playlist schema: playlist_id: uuid song_id: uuid position: integer auth: required: true owner_only: true depends_on_models: [model_playlist, model_playlist_song] - id: api_remove_song_from_playlist method: DELETE path: /api/playlists/:playlistId/songs/:songId description: Remove song from playlist responses: - status: 204 description: Song removed from playlist auth: required: true owner_only: true depends_on_models: [model_playlist, model_playlist_song] - id: api_reorder_playlist_songs method: PUT path: /api/playlists/:id/reorder description: Reorder songs in playlist request_body: song_ids: array[uuid] responses: - status: 200 description: Playlist reordered schema: message: string auth: required: true owner_only: true depends_on_models: [model_playlist, model_playlist_song] # ───────────────────────────────────────────────────────────── # DISCOVERY ENDPOINTS # ───────────────────────────────────────────────────────────── - id: api_get_trending_songs method: GET path: /api/discover/trending description: Get trending songs query_params: limit: integer offset: integer responses: - status: 200 description: List of trending songs schema: songs: - id: uuid title: string artist: stage_name: string play_count: integer auth: required: false depends_on_models: [model_song, model_artist] - id: api_get_new_releases method: GET path: /api/discover/new-releases description: Get recently released songs query_params: limit: integer offset: integer responses: - status: 200 description: List of new releases schema: songs: - id: uuid title: string release_date: string auth: required: false depends_on_models: [model_song] - id: api_get_genres method: GET path: /api/discover/genres description: Get all genres responses: - status: 200 description: List of genres schema: genres: - id: uuid name: string slug: string auth: required: false depends_on_models: [model_genre] - id: api_get_songs_by_genre method: GET path: /api/discover/genres/:slug description: Get songs by genre query_params: limit: integer offset: integer responses: - status: 200 description: List of songs in genre schema: genre: name: string songs: array auth: required: false depends_on_models: [model_genre, model_song] - id: api_search method: GET path: /api/search description: Search songs, artists, and albums query_params: q: string type: enum[song, artist, album, all] limit: integer responses: - status: 200 description: Search results schema: songs: array artists: array albums: array auth: required: false depends_on_models: [model_song, model_artist, model_album] # ───────────────────────────────────────────────────────────── # LABEL ENDPOINTS (MVP) # ───────────────────────────────────────────────────────────── - id: api_create_label_profile method: POST path: /api/labels description: Create label profile request_body: label_name: string description: string logo_url: string website: string responses: - status: 201 description: Label created schema: id: uuid label_name: string auth: required: true roles: [label] depends_on_models: [model_label] - id: api_get_label_artists method: GET path: /api/labels/:id/artists description: Get artists under label responses: - status: 200 description: List of artists schema: artists: - id: uuid stage_name: string auth: required: false depends_on_models: [model_label, model_artist] # ═══════════════════════════════════════════════════════════════ # LAYER 3: PAGES # ═══════════════════════════════════════════════════════════════ pages: - id: page_login path: /login title: Login description: User login page data_needs: - api_id: api_login purpose: Authenticate user on_load: false components: - component_auth_form auth: required: false redirect_if_authenticated: / - id: page_register path: /register title: Register description: User registration page data_needs: - api_id: api_register purpose: Create new account on_load: false components: - component_auth_form auth: required: false redirect_if_authenticated: / - id: page_forgot_password path: /forgot-password title: Forgot Password description: Password reset request page data_needs: - api_id: api_forgot_password purpose: Request password reset on_load: false components: - component_auth_form auth: required: false - id: page_home path: / title: Discover Music description: Main discovery feed data_needs: - api_id: api_get_trending_songs purpose: Show trending songs on_load: true - api_id: api_get_new_releases purpose: Show new releases on_load: true - api_id: api_get_genres purpose: Genre navigation on_load: true components: - component_song_card - component_genre_badge - component_section_header auth: required: false - id: page_artist_profile path: /artist/:id title: Artist Profile description: Artist profile with songs and albums data_needs: - api_id: api_get_artist purpose: Artist details on_load: true - api_id: api_get_artist_songs purpose: Artist songs on_load: true - api_id: api_get_artist_albums purpose: Artist albums on_load: true components: - component_artist_header - component_song_card - component_album_card - component_social_links auth: required: false - id: page_album_detail path: /album/:id title: Album description: Album detail with track list data_needs: - api_id: api_get_album purpose: Album details and songs on_load: true components: - component_album_header - component_track_list - component_song_card auth: required: false - id: page_upload path: /upload title: Upload Music description: Song upload page (musicians only) data_needs: - api_id: api_upload_song purpose: Upload song file on_load: false - api_id: api_get_artist_albums purpose: Select album on_load: true - api_id: api_get_genres purpose: Select genres on_load: true components: - component_upload_form - component_waveform_display auth: required: true roles: [musician] redirect_if_unauthorized: /login - id: page_playlists path: /playlists title: My Playlists description: User's playlists data_needs: - api_id: api_get_user_playlists purpose: Load playlists on_load: true components: - component_playlist_card - component_create_playlist_modal auth: required: true redirect_if_unauthorized: /login - id: page_playlist_detail path: /playlist/:id title: Playlist description: Playlist detail with songs data_needs: - api_id: api_get_playlist purpose: Playlist details and songs on_load: true components: - component_playlist_header - component_track_list - component_song_card auth: required: false - id: page_profile path: /profile title: Profile Settings description: User profile settings data_needs: - api_id: api_get_current_user purpose: Load user data on_load: true - api_id: api_update_current_user purpose: Update profile on_load: false components: - component_profile_form - component_avatar_upload auth: required: true redirect_if_unauthorized: /login - id: page_search path: /search title: Search description: Search results page data_needs: - api_id: api_search purpose: Search songs, artists, albums on_load: false components: - component_search_bar - component_search_results - component_song_card - component_artist_card - component_album_card auth: required: false - id: page_genre_browse path: /genre/:slug title: Browse Genre description: Browse songs by genre data_needs: - api_id: api_get_songs_by_genre purpose: Load genre songs on_load: true components: - component_genre_header - component_song_card auth: required: false # ═══════════════════════════════════════════════════════════════ # LAYER 3: COMPONENTS # ═══════════════════════════════════════════════════════════════ components: - id: component_audio_player name: AudioPlayer description: Global audio player with full controls props: - name: currentSong type: Song required: false - name: queue type: array[Song] required: false - name: autoplay type: boolean default: false state: - name: isPlaying type: boolean - name: currentTime type: number - name: volume type: number - name: isShuffle type: boolean - name: repeatMode type: enum[off, one, all] events: - name: onPlay payload: songId: string - name: onPause payload: null - name: onSeek payload: time: number - name: onVolumeChange payload: volume: number - name: onNext payload: null - name: onPrevious payload: null - name: onShuffle payload: null - name: onRepeat payload: null uses_apis: - api_increment_play_count uses_components: - component_waveform_display - component_player_controls - id: component_player_controls name: PlayerControls description: Play/pause/seek controls props: - name: isPlaying type: boolean required: true - name: currentTime type: number required: true - name: duration type: number required: true events: - name: onPlay payload: null - name: onPause payload: null - name: onSeek payload: time: number uses_apis: [] uses_components: [] - id: component_song_card name: SongCard description: Song display card with play button props: - name: song type: Song required: true - name: showArtist type: boolean default: true - name: showAlbum type: boolean default: false events: - name: onPlay payload: songId: string - name: onAddToPlaylist payload: songId: string uses_apis: [] uses_components: [] - id: component_album_card name: AlbumCard description: Album display card props: - name: album type: Album required: true - name: showArtist type: boolean default: true events: - name: onClick payload: albumId: string uses_apis: [] uses_components: [] - id: component_artist_card name: ArtistCard description: Artist preview card props: - name: artist type: Artist required: true events: - name: onClick payload: artistId: string uses_apis: [] uses_components: [] - id: component_playlist_card name: PlaylistCard description: Playlist preview card props: - name: playlist type: Playlist required: true events: - name: onClick payload: playlistId: string uses_apis: [] uses_components: [] - id: component_upload_form name: UploadForm description: Song upload form with file input props: - name: albums type: array[Album] required: true - name: genres type: array[Genre] required: true state: - name: file type: File - name: title type: string - name: selectedAlbum type: string - name: selectedGenres type: array[string] - name: uploadProgress type: number events: - name: onUpload payload: file: File metadata: object - name: onCancel payload: null uses_apis: - api_upload_song uses_components: - component_waveform_display - id: component_waveform_display name: WaveformDisplay description: Audio waveform visualization props: - name: audioUrl type: string required: true - name: waveformData type: array[number] required: false - name: currentTime type: number required: false events: - name: onSeek payload: time: number uses_apis: [] uses_components: [] - id: component_genre_badge name: GenreBadge description: Genre tag display props: - name: genre type: Genre required: true - name: clickable type: boolean default: true events: - name: onClick payload: genreSlug: string uses_apis: [] uses_components: [] - id: component_track_list name: TrackList description: List of songs with track numbers props: - name: songs type: array[Song] required: true - name: showTrackNumber type: boolean default: true - name: reorderable type: boolean default: false events: - name: onPlay payload: songId: string - name: onReorder payload: songIds: array[string] uses_apis: [] uses_components: - component_song_card - id: component_artist_header name: ArtistHeader description: Artist profile header with cover image props: - name: artist type: Artist required: true events: [] uses_apis: [] uses_components: - component_social_links - id: component_album_header name: AlbumHeader description: Album detail header with cover art props: - name: album type: Album required: true - name: artist type: Artist required: true events: - name: onPlayAll payload: null uses_apis: [] uses_components: [] - id: component_playlist_header name: PlaylistHeader description: Playlist header with cover and controls props: - name: playlist type: Playlist required: true - name: isOwner type: boolean default: false events: - name: onPlayAll payload: null - name: onEdit payload: null - name: onDelete payload: null uses_apis: [] uses_components: [] - id: component_social_links name: SocialLinks description: Social media links display props: - name: links type: object required: true events: [] uses_apis: [] uses_components: [] - id: component_auth_form name: AuthForm description: Reusable authentication form props: - name: mode type: enum[login, register, forgot] required: true state: - name: email type: string - name: password type: string - name: name type: string - name: role type: string events: - name: onSubmit payload: object uses_apis: - api_login - api_register - api_forgot_password uses_components: [] - id: component_search_bar name: SearchBar description: Search input with autocomplete props: - name: placeholder type: string default: "Search songs, artists, albums..." state: - name: query type: string events: - name: onSearch payload: query: string uses_apis: - api_search uses_components: [] - id: component_search_results name: SearchResults description: Search results with filters props: - name: results type: object required: true events: [] uses_apis: [] uses_components: - component_song_card - component_artist_card - component_album_card - id: component_create_playlist_modal name: CreatePlaylistModal description: Modal for creating new playlist props: - name: isOpen type: boolean required: true state: - name: name type: string - name: description type: string - name: isPublic type: boolean events: - name: onCreate payload: name: string description: string isPublic: boolean - name: onClose payload: null uses_apis: - api_create_playlist uses_components: [] - id: component_profile_form name: ProfileForm description: User profile edit form props: - name: user type: User required: true state: - name: name type: string - name: avatarUrl type: string events: - name: onSave payload: name: string avatarUrl: string uses_apis: - api_update_current_user uses_components: - component_avatar_upload - id: component_avatar_upload name: AvatarUpload description: Avatar image upload component props: - name: currentAvatarUrl type: string required: false state: - name: file type: File - name: preview type: string events: - name: onUpload payload: file: File uses_apis: [] uses_components: [] - id: component_section_header name: SectionHeader description: Section title with optional action props: - name: title type: string required: true - name: actionLabel type: string required: false events: - name: onActionClick payload: null uses_apis: [] uses_components: [] - id: component_genre_header name: GenreHeader description: Genre browse page header props: - name: genre type: Genre required: true events: [] uses_apis: [] uses_components: [] # ═══════════════════════════════════════════════════════════════ # SUMMARY # ═══════════════════════════════════════════════════════════════ summary: data_models: 10 api_endpoints: 46 pages: 11 components: 24 total_entities: 91