refactor(api): standardize venues endpoint
Some checks failed
Deploy Fediversion / deploy (push) Failing after 1s

- Backend: /api/venues returns PaginatedResponse envelope
- Frontend: Updated VenuesPage, AdminVenuesPage, VerticalVenuesPage to consume envelope
This commit is contained in:
fullsizemalt 2025-12-30 20:35:59 -08:00
parent c0e3e2a7e2
commit 3aaf35d43b
4 changed files with 34 additions and 27 deletions

View file

@ -1,9 +1,9 @@
from typing import List from typing import List
from fastapi import APIRouter, Depends, HTTPException, Query from fastapi import APIRouter, Depends, HTTPException, Query
from sqlmodel import Session, select from sqlmodel import Session, select, func
from database import get_session from database import get_session
from models import Venue from models import Venue
from schemas import VenueCreate, VenueRead, VenueUpdate from schemas import VenueCreate, VenueRead, VenueUpdate, PaginatedResponse, PaginationMeta
from auth import get_current_user from auth import get_current_user
router = APIRouter(prefix="/venues", tags=["venues"]) 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) session.refresh(db_venue)
return 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)): 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() 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) @router.get("/{slug}", response_model=VenueRead)
def read_venue(slug: str, session: Session = Depends(get_session)): def read_venue(slug: str, session: Session = Depends(get_session)):

View file

@ -18,7 +18,8 @@ async function getVenues(verticalSlug: string) {
next: { revalidate: 60 } next: { revalidate: 60 }
}) })
if (!res.ok) return [] if (!res.ok) return []
return res.json() const data = await res.json()
return data.data || []
} catch { } catch {
return [] return []
} }

View file

@ -1,6 +1,6 @@
"use client" "use client"
import { useEffect, useState } from "react" import { useEffect, useState, useCallback } from "react"
import { useAuth } from "@/contexts/auth-context" import { useAuth } from "@/contexts/auth-context"
import { useRouter } from "next/navigation" import { useRouter } from "next/navigation"
import { Card, CardContent } from "@/components/ui/card" import { Card, CardContent } from "@/components/ui/card"
@ -38,6 +38,24 @@ export default function AdminVenuesPage() {
const [editingVenue, setEditingVenue] = useState<Venue | null>(null) const [editingVenue, setEditingVenue] = useState<Venue | null>(null)
const [saving, setSaving] = useState(false) 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(() => { useEffect(() => {
if (authLoading) return if (authLoading) return
if (!user) { if (!user) {
@ -49,25 +67,7 @@ export default function AdminVenuesPage() {
return return
} }
fetchVenues() fetchVenues()
}, [user, router, authLoading]) }, [user, router, authLoading, fetchVenues])
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)
}
}
const updateVenue = async () => { const updateVenue = async () => {
if (!token || !editingVenue) return if (!token || !editingVenue) return

View file

@ -34,11 +34,13 @@ export default function VenuesPage() {
setError(null) setError(null)
// Fetch venues // Fetch venues
const venuesRes = await fetch(`${getApiUrl()}/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) // Fetch show counts for each venue (batch approach)
const showsRes = await fetch(`${getApiUrl()}/shows/?limit=1000`) 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 // Count shows per venue
const showCounts: Record<number, number> = {} const showCounts: Record<number, number> = {}