project-standalo-sonic-cloud/.workflow/versions/v001/design/design_document.yml

1822 lines
46 KiB
YAML

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