diff --git a/backend/routers/songs.py b/backend/routers/songs.py index 0156e7b..b995b47 100644 --- a/backend/routers/songs.py +++ b/backend/routers/songs.py @@ -4,7 +4,7 @@ from fastapi import APIRouter, Depends, HTTPException, Query from sqlmodel import Session, select, func from database import get_session from models import Song, User, Tag, EntityTag, Show, Performance, Rating -from schemas import SongCreate, SongRead, SongReadWithStats, SongUpdate, TagRead, PerformanceReadWithShow +from schemas import SongCreate, SongRead, SongReadWithStats, SongUpdate, TagRead, PerformanceReadWithShow, PaginatedResponse, PaginationMeta from auth import get_current_user from services.stats import get_song_stats @@ -18,7 +18,7 @@ def create_song(song: SongCreate, session: Session = Depends(get_session), curre session.refresh(db_song) return db_song -@router.get("/", response_model=List[SongRead]) +@router.get("/", response_model=PaginatedResponse[SongRead]) def read_songs( offset: int = 0, limit: int = Query(default=100, le=1000), @@ -34,13 +34,23 @@ def read_songs( if vertical_entity: query = query.where(Song.vertical_id == vertical_entity.id) else: - return [] + return PaginatedResponse(data=[], meta=PaginationMeta(total=0, limit=limit, offset=offset)) if sort == "times_played": - query = query.outerjoin(Performance).group_by(Song.id).order_by(func.count(Performance.id).desc()) + query = query.outerjoin(Performance).group_by(Song.id) + # Calculate total count before pagination + total = session.exec(select(func.count()).select_from(query.subquery())).one() + + if sort == "times_played": + query = query.order_by(func.count(Performance.id).desc()) + songs = session.exec(query.offset(offset).limit(limit)).all() - return songs + + return PaginatedResponse( + data=songs, + meta=PaginationMeta(total=total, limit=limit, offset=offset) + ) @router.get("/{slug}", response_model=SongReadWithStats) def read_song(slug: str, session: Session = Depends(get_session)): diff --git a/frontend/app/[vertical]/page.tsx b/frontend/app/[vertical]/page.tsx index 36a5775..da9d33a 100644 --- a/frontend/app/[vertical]/page.tsx +++ b/frontend/app/[vertical]/page.tsx @@ -35,7 +35,8 @@ async function getTopSongs(verticalSlug: string) { next: { revalidate: 60 } }) if (!res.ok) return [] - return res.json() + const data = await res.json() + return data.data || [] } catch { return [] } diff --git a/frontend/app/[vertical]/songs/page.tsx b/frontend/app/[vertical]/songs/page.tsx index f2c0a92..d1bebf9 100644 --- a/frontend/app/[vertical]/songs/page.tsx +++ b/frontend/app/[vertical]/songs/page.tsx @@ -18,7 +18,8 @@ async function getSongs(verticalSlug: string) { next: { revalidate: 60 } }) if (!res.ok) return [] - return res.json() + const data = await res.json() + return data.data || [] } catch { return [] } diff --git a/frontend/app/admin/sequences/page.tsx b/frontend/app/admin/sequences/page.tsx index 9a08692..ae203a6 100644 --- a/frontend/app/admin/sequences/page.tsx +++ b/frontend/app/admin/sequences/page.tsx @@ -198,7 +198,7 @@ export default function AdminSequencesPage() { }) if (res.ok) { const data = await res.json() - setAllSongs(data.songs || data) + setAllSongs(data.data || []) } } catch (e) { console.error("Failed to fetch songs", e) diff --git a/frontend/app/admin/songs/page.tsx b/frontend/app/admin/songs/page.tsx index 688eb4e..37efb7d 100644 --- a/frontend/app/admin/songs/page.tsx +++ b/frontend/app/admin/songs/page.tsx @@ -61,7 +61,7 @@ export default function AdminSongsPage() { }) if (res.ok) { const data = await res.json() - setSongs(data.songs || data) + setSongs(data.data || []) } } catch (e) { console.error("Failed to fetch songs", e) diff --git a/frontend/app/songs/page.tsx b/frontend/app/songs/page.tsx index 53df976..a769b3b 100644 --- a/frontend/app/songs/page.tsx +++ b/frontend/app/songs/page.tsx @@ -21,12 +21,10 @@ export default function SongsPage() { fetch(`${getApiUrl()}/songs/?limit=1000`) .then(res => res.json()) .then(data => { - if (!Array.isArray(data)) { - console.error("API Error: Expected array but got:", data) - return - } + // Handle envelope + const songData = data.data || [] // Sort alphabetically - const sorted = data.sort((a: Song, b: Song) => a.title.localeCompare(b.title)) + const sorted = songData.sort((a: Song, b: Song) => a.title.localeCompare(b.title)) setSongs(sorted) }) .catch(console.error)