124 lines
5.2 KiB
TypeScript
124 lines
5.2 KiB
TypeScript
|
|
"use client"
|
|
|
|
import Link from "next/link"
|
|
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { Calendar, MapPin, Youtube, Music2 } from "lucide-react"
|
|
import { Button } from "@/components/ui/button"
|
|
|
|
interface Show {
|
|
id: number
|
|
slug?: string
|
|
date: string
|
|
youtube_link?: string
|
|
vertical?: {
|
|
name: string
|
|
slug: string
|
|
}
|
|
venue: {
|
|
id: number
|
|
name: string
|
|
city: string
|
|
state: string
|
|
}
|
|
}
|
|
|
|
interface DateGroupedListProps {
|
|
shows: Show[]
|
|
}
|
|
|
|
export function DateGroupedList({ shows }: DateGroupedListProps) {
|
|
if (shows.length === 0) {
|
|
return (
|
|
<div className="text-center py-12 text-muted-foreground">
|
|
No shows found matching your filters.
|
|
</div>
|
|
)
|
|
}
|
|
|
|
// Group shows by date
|
|
const groupedShows: Record<string, Show[]> = {}
|
|
|
|
shows.forEach(show => {
|
|
// Use local date string to avoid timezone shifts if possible, or usually just split T
|
|
const dateKey = new Date(show.date).toLocaleDateString(undefined, {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})
|
|
|
|
if (!groupedShows[dateKey]) {
|
|
groupedShows[dateKey] = []
|
|
}
|
|
groupedShows[dateKey].push(show)
|
|
})
|
|
|
|
// Sort dates (descending) - assuming API returned sorted, but safe to re-sort keys if list order isn't guaranteed
|
|
// Actually relying on input order is safer if we trust the API to sort by date desc.
|
|
// Let's just iterate over the unique keys in the order they appear (which will be desc if input is desc)
|
|
const uniqueDates = Array.from(new Set(shows.map(s =>
|
|
new Date(s.date).toLocaleDateString(undefined, {
|
|
weekday: 'long',
|
|
year: 'numeric',
|
|
month: 'long',
|
|
day: 'numeric'
|
|
})
|
|
)))
|
|
|
|
return (
|
|
<div className="space-y-8">
|
|
{uniqueDates.map(dateStr => (
|
|
<div key={dateStr} className="space-y-4">
|
|
<div className="sticky top-16 z-10 bg-background/95 backdrop-blur py-2 border-b">
|
|
<h2 className="text-xl font-bold flex items-center gap-2">
|
|
<Calendar className="h-5 w-5 text-primary" />
|
|
{dateStr}
|
|
</h2>
|
|
</div>
|
|
|
|
<div className="grid gap-4 md:grid-cols-2 lg:grid-cols-3">
|
|
{groupedShows[dateStr].map(show => (
|
|
<Link key={show.id} href={`/shows/${show.slug}`} className="block group">
|
|
<Card className="h-full transition-all duration-300 hover:scale-[1.02] hover:shadow-lg group-hover:border-primary/50 relative overflow-hidden">
|
|
{/* Vertical Stripe/Badge */}
|
|
{show.vertical && (
|
|
<div className="absolute top-0 left-0 w-1 h-full bg-primary/50 group-hover:bg-primary transition-colors" />
|
|
)}
|
|
|
|
{show.youtube_link && (
|
|
<div className="absolute top-2 right-2 bg-red-500/10 text-red-500 p-1.5 rounded-full" title="Full show video available">
|
|
<Youtube className="h-4 w-4" />
|
|
</div>
|
|
)}
|
|
|
|
<CardHeader className="pl-5 pb-2"> {/* Added padding-left for stripe */}
|
|
<CardTitle className="text-lg flex flex-col gap-1">
|
|
{show.vertical && (
|
|
<span className="text-xs font-medium text-primary uppercase tracking-wider flex items-center gap-1">
|
|
<Music2 className="h-3 w-3" />
|
|
{show.vertical.name}
|
|
</span>
|
|
)}
|
|
<span className="group-hover:text-primary transition-colors">
|
|
{show.venue?.name}
|
|
</span>
|
|
</CardTitle>
|
|
</CardHeader>
|
|
<CardContent className="pl-5 pt-0">
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground group-hover:text-foreground transition-colors">
|
|
<MapPin className="h-3.5 w-3.5" />
|
|
<span>
|
|
{show.venue?.city}, {show.venue?.state}
|
|
</span>
|
|
</div>
|
|
</CardContent>
|
|
</Card>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
</div>
|
|
))}
|
|
</div>
|
|
)
|
|
}
|