"use client" import { useEffect, useState, useMemo } from "react" import { getApiUrl } from "@/lib/api-config" import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card" import { Input } from "@/components/ui/input" import { Button } from "@/components/ui/button" import Link from "next/link" import { MapPin, Search, Calendar, ArrowUpDown } from "lucide-react" interface Venue { id: number name: string slug?: string city: string state: string country: string show_count?: number } type SortOption = "name" | "city" | "shows" export default function VenuesPage() { const [venues, setVenues] = useState([]) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) const [searchQuery, setSearchQuery] = useState("") const [stateFilter, setStateFilter] = useState("") const [sortBy, setSortBy] = useState("name") useEffect(() => { async function fetchVenues() { try { setError(null) // Fetch venues const venuesRes = await fetch(`${getApiUrl()}/venues/`) 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 showsEnvelope = await showsRes.json() const showsData = showsEnvelope.data || [] // Count shows per venue const showCounts: Record = {} showsData.forEach((show: any) => { if (show.venue_id) { showCounts[show.venue_id] = (showCounts[show.venue_id] || 0) + 1 } }) // Merge counts into venues const venuesWithCounts = venuesData.map(v => ({ ...v, show_count: showCounts[v.id] || 0 })) setVenues(venuesWithCounts) } catch (error) { console.error("Failed to fetch venues:", error) setError("Failed to load venues. Please try again.") } finally { setLoading(false) } } fetchVenues() }, []) // Get unique states for filter dropdown const uniqueStates = useMemo(() => { const states = [...new Set(venues.map(v => v.state).filter(Boolean))] return states.sort() }, [venues]) // Filter and sort venues const filteredVenues = useMemo(() => { let result = venues // Search filter if (searchQuery) { const query = searchQuery.toLowerCase() result = result.filter(v => v.name.toLowerCase().includes(query) || v.city.toLowerCase().includes(query) ) } // State filter if (stateFilter) { result = result.filter(v => v.state === stateFilter) } // Sort switch (sortBy) { case "name": result = [...result].sort((a, b) => a.name.localeCompare(b.name)) break case "city": result = [...result].sort((a, b) => a.city.localeCompare(b.city)) break case "shows": result = [...result].sort((a, b) => (b.show_count || 0) - (a.show_count || 0)) break } return result }, [venues, searchQuery, stateFilter, sortBy]) if (error) { return (

Unable to Load Venues

{error}

) } if (loading) { return (
{[...Array(9)].map((_, i) => (
))}
) } return (
{/* Header */}

Venues

{venues.length} venues where the magic happens

{/* Search & Filters */}
setSearchQuery(e.target.value)} />
{/* Results count */} {(searchQuery || stateFilter) && (

Showing {filteredVenues.length} of {venues.length} venues

)} {/* Venue Grid */}
{filteredVenues.map((venue) => ( {venue.name}

{venue.city}{venue.state ? `, ${venue.state}` : ""}

{(venue.show_count || 0) > 0 && ( {venue.show_count} {venue.show_count === 1 ? "show" : "shows"} )}
))}
{filteredVenues.length === 0 && (

No venues found matching your search.

)}
) }