108 lines
4.9 KiB
TypeScript
108 lines
4.9 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import Link from "next/link"
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { getApiUrl } from "@/lib/api-config"
|
|
import { Loader2, ChevronRight, Music, Calendar, MapPin, ListMusic } from "lucide-react"
|
|
|
|
interface Vertical {
|
|
id: number
|
|
name: string
|
|
slug: string
|
|
description: string | null
|
|
color: string | null
|
|
show_count?: number
|
|
song_count?: number
|
|
venue_count?: number
|
|
}
|
|
|
|
export function BandsGrid() {
|
|
const [verticals, setVerticals] = useState<Vertical[]>([])
|
|
const [loading, setLoading] = useState(true)
|
|
|
|
useEffect(() => {
|
|
async function fetchVerticals() {
|
|
try {
|
|
const res = await fetch(`${getApiUrl()}/verticals`)
|
|
if (res.ok) {
|
|
const data = await res.json()
|
|
setVerticals(data)
|
|
}
|
|
} catch (error) {
|
|
console.error("Failed to fetch verticals", error)
|
|
} finally {
|
|
setLoading(false)
|
|
}
|
|
}
|
|
fetchVerticals()
|
|
}, [])
|
|
|
|
if (loading) {
|
|
return (
|
|
<div className="flex items-center justify-center min-h-[300px]">
|
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
</div>
|
|
)
|
|
}
|
|
|
|
return (
|
|
<div className="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{verticals.map((vertical) => (
|
|
<Link key={vertical.id} href={`/${vertical.slug}`} className="group">
|
|
<Card className="h-full transition-all duration-200 hover:scale-[1.02] hover:shadow-lg hover:border-primary/50">
|
|
<CardHeader className="pb-2">
|
|
<div className="flex items-center justify-between">
|
|
<div className="flex items-center gap-3">
|
|
<div
|
|
className="w-12 h-12 rounded-lg flex items-center justify-center text-white font-bold text-lg"
|
|
style={{ backgroundColor: vertical.color || '#3B82F6' }}
|
|
>
|
|
{vertical.name.substring(0, 2).toUpperCase()}
|
|
</div>
|
|
<CardTitle className="text-xl group-hover:text-primary transition-colors">
|
|
{vertical.name}
|
|
</CardTitle>
|
|
</div>
|
|
<ChevronRight className="h-5 w-5 text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity" />
|
|
</div>
|
|
</CardHeader>
|
|
<CardContent>
|
|
{vertical.description && (
|
|
<p className="text-sm text-muted-foreground line-clamp-2 mb-4">
|
|
{vertical.description}
|
|
</p>
|
|
)}
|
|
<div className="flex flex-wrap gap-3 text-xs text-muted-foreground">
|
|
<Link
|
|
href={`/${vertical.slug}/shows`}
|
|
className="flex items-center gap-1 px-2 py-1 rounded-full bg-muted hover:bg-primary/10 hover:text-primary transition-colors"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<Calendar className="h-3 w-3" />
|
|
Shows
|
|
</Link>
|
|
<Link
|
|
href={`/${vertical.slug}/songs`}
|
|
className="flex items-center gap-1 px-2 py-1 rounded-full bg-muted hover:bg-primary/10 hover:text-primary transition-colors"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<ListMusic className="h-3 w-3" />
|
|
Songs
|
|
</Link>
|
|
<Link
|
|
href={`/${vertical.slug}/venues`}
|
|
className="flex items-center gap-1 px-2 py-1 rounded-full bg-muted hover:bg-primary/10 hover:text-primary transition-colors"
|
|
onClick={(e) => e.stopPropagation()}
|
|
>
|
|
<MapPin className="h-3 w-3" />
|
|
Venues
|
|
</Link>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|