From 3aaf35d43b5af5f6853c079efb3dbf85076e657f Mon Sep 17 00:00:00 2001 From: fullsizemalt <106900403+fullsizemalt@users.noreply.github.com> Date: Tue, 30 Dec 2025 20:35:59 -0800 Subject: [PATCH] refactor(api): standardize venues endpoint - Backend: /api/venues returns PaginatedResponse envelope - Frontend: Updated VenuesPage, AdminVenuesPage, VerticalVenuesPage to consume envelope --- backend/routers/venues.py | 12 +++++--- frontend/app/[vertical]/venues/page.tsx | 3 +- frontend/app/admin/venues/page.tsx | 40 ++++++++++++------------- frontend/app/venues/page.tsx | 6 ++-- 4 files changed, 34 insertions(+), 27 deletions(-) diff --git a/backend/routers/venues.py b/backend/routers/venues.py index 1a08aeb..38dda3c 100644 --- a/backend/routers/venues.py +++ b/backend/routers/venues.py @@ -1,9 +1,9 @@ from typing import List from fastapi import APIRouter, Depends, HTTPException, Query -from sqlmodel import Session, select +from sqlmodel import Session, select, func from database import get_session from models import Venue -from schemas import VenueCreate, VenueRead, VenueUpdate +from schemas import VenueCreate, VenueRead, VenueUpdate, PaginatedResponse, PaginationMeta from auth import get_current_user router = APIRouter(prefix="/venues", tags=["venues"]) @@ -16,10 +16,14 @@ def create_venue(venue: VenueCreate, session: Session = Depends(get_session), cu session.refresh(db_venue) return db_venue -@router.get("/", response_model=List[VenueRead]) +@router.get("/", response_model=PaginatedResponse[VenueRead]) def read_venues(offset: int = 0, limit: int = Query(default=1000, le=1000), session: Session = Depends(get_session)): + total = session.exec(select(func.count()).select_from(Venue)).one() venues = session.exec(select(Venue).offset(offset).limit(limit)).all() - return venues + return PaginatedResponse( + data=venues, + meta=PaginationMeta(total=total, limit=limit, offset=offset) + ) @router.get("/{slug}", response_model=VenueRead) def read_venue(slug: str, session: Session = Depends(get_session)): diff --git a/frontend/app/[vertical]/venues/page.tsx b/frontend/app/[vertical]/venues/page.tsx index b2c2b53..cbb21d4 100644 --- a/frontend/app/[vertical]/venues/page.tsx +++ b/frontend/app/[vertical]/venues/page.tsx @@ -18,7 +18,8 @@ async function getVenues(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/venues/page.tsx b/frontend/app/admin/venues/page.tsx index 1e47d6d..699a732 100644 --- a/frontend/app/admin/venues/page.tsx +++ b/frontend/app/admin/venues/page.tsx @@ -1,6 +1,6 @@ "use client" -import { useEffect, useState } from "react" +import { useEffect, useState, useCallback } from "react" import { useAuth } from "@/contexts/auth-context" import { useRouter } from "next/navigation" import { Card, CardContent } from "@/components/ui/card" @@ -38,6 +38,24 @@ export default function AdminVenuesPage() { const [editingVenue, setEditingVenue] = useState(null) const [saving, setSaving] = useState(false) + const fetchVenues = useCallback(async () => { + if (!token) return + + try { + const res = await fetch(`${getApiUrl()}/venues?limit=200`, { + headers: { Authorization: `Bearer ${token}` } + }) + if (res.ok) { + const data = await res.json() + setVenues(data.data || []) + } + } catch (e) { + console.error("Failed to fetch venues", e) + } finally { + setLoading(false) + } + }, [token]) + useEffect(() => { if (authLoading) return if (!user) { @@ -49,25 +67,7 @@ export default function AdminVenuesPage() { return } fetchVenues() - }, [user, router, authLoading]) - - const fetchVenues = async () => { - if (!token) return - - try { - const res = await fetch(`${getApiUrl()}/venues?limit=200`, { - headers: { Authorization: `Bearer ${token}` } - }) - if (res.ok) { - const data = await res.json() - setVenues(data.venues || data) - } - } catch (e) { - console.error("Failed to fetch venues", e) - } finally { - setLoading(false) - } - } + }, [user, router, authLoading, fetchVenues]) const updateVenue = async () => { if (!token || !editingVenue) return diff --git a/frontend/app/venues/page.tsx b/frontend/app/venues/page.tsx index 255601d..30a6c24 100644 --- a/frontend/app/venues/page.tsx +++ b/frontend/app/venues/page.tsx @@ -34,11 +34,13 @@ export default function VenuesPage() { setError(null) // Fetch venues const venuesRes = await fetch(`${getApiUrl()}/venues/`) - const venuesData: Venue[] = await venuesRes.json() + const venuesEnvelope = await venuesRes.json() + const venuesData: Venue[] = venuesEnvelope.data || [] // Fetch show counts for each venue (batch approach) const showsRes = await fetch(`${getApiUrl()}/shows/?limit=1000`) - const showsData = await showsRes.json() + const showsEnvelope = await showsRes.json() + const showsData = showsEnvelope.data || [] // Count shows per venue const showCounts: Record = {}