diff --git a/backend/routers/tours.py b/backend/routers/tours.py index 38e2f5d..33b3250 100644 --- a/backend/routers/tours.py +++ b/backend/routers/tours.py @@ -1,8 +1,8 @@ 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 Tour, User +from models import Tour, Show, User from schemas import TourCreate, TourRead, TourUpdate from auth import get_current_user @@ -20,18 +20,46 @@ def create_tour( session.refresh(db_tour) return db_tour -@router.get("/", response_model=List[TourRead]) +@router.get("/") def read_tours( offset: int = 0, - limit: int = Query(default=100, le=100), + limit: int = Query(default=100, le=500), session: Session = Depends(get_session) ): + """Get all tours with show counts""" tours = session.exec(select(Tour).offset(offset).limit(limit)).all() - return tours + + # Get show counts per tour + result = [] + for tour in tours: + show_count = session.exec( + select(func.count(Show.id)).where(Show.tour_id == tour.id) + ).one() + tour_dict = tour.model_dump() + tour_dict["show_count"] = show_count + result.append(tour_dict) + + return result -@router.get("/{slug}", response_model=TourRead) +@router.get("/{slug}") def read_tour(slug: str, session: Session = Depends(get_session)): - tour = session.exec(select(Tour).where(Tour.slug == slug)).first() + """Get tour by slug or ID""" + # Try as ID first (for backwards compatibility) + try: + tour_id = int(slug) + tour = session.get(Tour, tour_id) + except ValueError: + tour = session.exec(select(Tour).where(Tour.slug == slug)).first() + if not tour: raise HTTPException(status_code=404, detail="Tour not found") - return tour + + # Add show count + show_count = session.exec( + select(func.count(Show.id)).where(Show.tour_id == tour.id) + ).one() + tour_dict = tour.model_dump() + tour_dict["show_count"] = show_count + + return tour_dict + diff --git a/frontend/app/tours/[slug]/page.tsx b/frontend/app/tours/[slug]/page.tsx index bceab0d..1abe9f8 100644 --- a/frontend/app/tours/[slug]/page.tsx +++ b/frontend/app/tours/[slug]/page.tsx @@ -1,6 +1,7 @@ import { Button } from "@/components/ui/button" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" -import { ArrowLeft, Calendar, Music2 } from "lucide-react" +import { Badge } from "@/components/ui/badge" +import { ArrowLeft, Calendar, MapPin, Hash } from "lucide-react" import Link from "next/link" import { notFound } from "next/navigation" import { getApiUrl } from "@/lib/api-config" @@ -9,6 +10,17 @@ import { EntityRating } from "@/components/social/entity-rating" import { EntityReviews } from "@/components/reviews/entity-reviews" import { SocialWrapper } from "@/components/social/social-wrapper" +interface ShowWithVenue { + id: number + slug: string + date: string + venue?: { + name: string + city?: string + state?: string + } +} + async function getTour(id: string) { try { const res = await fetch(`${getApiUrl()}/tours/${id}`, { cache: 'no-store' }) @@ -41,11 +53,15 @@ export default async function TourDetailPage({ params }: { params: Promise<{ slu const shows = await getTourShows(tour.id) + // Calculate stats + const uniqueStates = [...new Set(shows.filter((s: ShowWithVenue) => s.venue?.state).map((s: ShowWithVenue) => s.venue!.state))] + const uniqueCities = [...new Set(shows.filter((s: ShowWithVenue) => s.venue?.city).map((s: ShowWithVenue) => s.venue!.city))] + return (
{tour.notes}
- )} +{tour.notes}
+- {new Date(tour.start_date).toLocaleDateString()} - {new Date(tour.end_date).toLocaleDateString()} -
-
+