156 lines
6.8 KiB
TypeScript
156 lines
6.8 KiB
TypeScript
"use client"
|
|
|
|
import { useEffect, useState } from "react"
|
|
import Link from "next/link"
|
|
import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"
|
|
import { useAuth } from "@/contexts/auth-context"
|
|
import { getApiUrl } from "@/lib/api-config"
|
|
|
|
interface Vertical {
|
|
id: number
|
|
name: string
|
|
slug: string
|
|
description: string | null
|
|
}
|
|
|
|
interface Preference {
|
|
vertical_id: number
|
|
tier: string
|
|
}
|
|
|
|
export function TieredBandList({ initialVerticals }: { initialVerticals: Vertical[] }) {
|
|
const [verticals, setVerticals] = useState<Vertical[]>(initialVerticals)
|
|
const [preferences, setPreferences] = useState<Preference[]>([])
|
|
const { token, isAuthenticated } = useAuth()
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
// Default headliners for guests
|
|
const defaultHeadliners = ["phish", "grateful-dead", "dead-and-company", "billy-strings", "goose"]
|
|
|
|
useEffect(() => {
|
|
if (isAuthenticated && token) {
|
|
setLoading(true)
|
|
fetch(`${process.env.NEXT_PUBLIC_API_URL || "https://api.fediversion.org"}/verticals/preferences/me`, {
|
|
headers: { Authorization: `Bearer ${token}` }
|
|
})
|
|
.then(res => {
|
|
if (res.ok) return res.json()
|
|
return []
|
|
})
|
|
.then((data: any[]) => {
|
|
// Map response to preference format
|
|
const prefs = data.map(p => ({
|
|
vertical_id: p.vertical_id,
|
|
tier: p.tier || "main_stage"
|
|
}))
|
|
setPreferences(prefs)
|
|
})
|
|
.catch(err => console.error("Failed to fetch preferences", err))
|
|
.finally(() => setLoading(false))
|
|
}
|
|
}, [isAuthenticated, token])
|
|
|
|
// Determine tiers
|
|
let headliners: Vertical[] = []
|
|
let others: Vertical[] = []
|
|
|
|
if (isAuthenticated && preferences.length > 0) {
|
|
// User has preferences
|
|
const headlinerIds = preferences.filter(p => p.tier === "headliner").map(p => p.vertical_id)
|
|
const subscribedIds = preferences.map(p => p.vertical_id)
|
|
|
|
headliners = verticals.filter(v => headlinerIds.includes(v.id))
|
|
others = verticals.filter(v => !headlinerIds.includes(v.id))
|
|
|
|
// If user has NO headliners set but HAS preferences, maybe show their top priority?
|
|
// Or just defaults.
|
|
if (headliners.length === 0) {
|
|
// Fallback to top 3 priority?
|
|
// For now, let's mix in defaults if empty? No, respect user choice.
|
|
// If they selected bands but no headliners, all are "others" (Touring Acts).
|
|
}
|
|
|
|
// Optionally filter "others" to only show subscribed bands?
|
|
// The requirement says "Main Stage: Standard following".
|
|
// "Supporting: Hidden unless relevant".
|
|
// Let's show subscribed bands as "Your Bands" and others as "Discover".
|
|
// But for simple "Tiered Band Preferences" UI, let's keep it simple:
|
|
// Headliners = Tier 'headliner'
|
|
// Touring Acts = Everyone else (or just subscribed 'main_stage'?)
|
|
// Let's show All Bands but prioritize Headliners.
|
|
} else {
|
|
// Guest or no prefs
|
|
headliners = verticals.filter(v => defaultHeadliners.includes(v.slug))
|
|
others = verticals.filter(v => !defaultHeadliners.includes(v.slug))
|
|
}
|
|
|
|
return (
|
|
<section className="space-y-12 max-w-6xl mx-auto px-4">
|
|
{/* Headliners */}
|
|
{headliners.length > 0 && (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center gap-4">
|
|
<div className="h-px flex-1 bg-border" />
|
|
<h2 className="text-2xl font-bold tracking-tight text-muted-foreground uppercase text-sm">
|
|
{isAuthenticated && preferences.length > 0 ? "Your Headliners" : "Headliners"}
|
|
</h2>
|
|
<div className="h-px flex-1 bg-border" />
|
|
</div>
|
|
|
|
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-6">
|
|
{headliners.map((vertical) => (
|
|
<VerticalCard key={vertical.slug} vertical={vertical} featured />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
|
|
{/* Other Acts */}
|
|
{others.length > 0 && (
|
|
<div className="space-y-6">
|
|
<div className="flex items-center gap-4">
|
|
<div className="h-px flex-1 bg-border" />
|
|
<h2 className="text-2xl font-bold tracking-tight text-muted-foreground uppercase text-sm">Touring Acts</h2>
|
|
<div className="h-px flex-1 bg-border" />
|
|
</div>
|
|
<div className="grid grid-cols-2 md:grid-cols-3 lg:grid-cols-4 gap-4">
|
|
{others.map((vertical) => (
|
|
<VerticalCard key={vertical.slug} vertical={vertical} />
|
|
))}
|
|
</div>
|
|
</div>
|
|
)}
|
|
</section>
|
|
)
|
|
}
|
|
|
|
function VerticalCard({ vertical, featured = false }: { vertical: Vertical, featured?: boolean }) {
|
|
return (
|
|
<Link href={`/${vertical.slug}`} className="group block h-full">
|
|
<Card className={`h-full transition-all duration-300 hover:scale-[1.02] hover:shadow-xl border-muted/60 ${featured ? 'bg-card' : 'bg-muted/30 hover:bg-card'}`}>
|
|
<CardHeader>
|
|
<CardTitle className={`flex items-center justify-between ${featured ? 'text-2xl' : 'text-lg'}`}>
|
|
<span className="capitalize">{vertical.name}</span>
|
|
{featured && (
|
|
<span className="text-muted-foreground opacity-0 group-hover:opacity-100 transition-opacity">→</span>
|
|
)}
|
|
</CardTitle>
|
|
{featured && vertical.description && (
|
|
<CardDescription className="line-clamp-2 mt-2">
|
|
{vertical.description}
|
|
</CardDescription>
|
|
)}
|
|
</CardHeader>
|
|
{featured && (
|
|
<CardContent>
|
|
<div className="flex flex-wrap gap-2">
|
|
<span className="text-xs font-medium px-2 py-1 rounded-full bg-primary/10 text-primary">Shows</span>
|
|
<span className="text-xs font-medium px-2 py-1 rounded-full bg-primary/10 text-primary">Setlists</span>
|
|
<span className="text-xs font-medium px-2 py-1 rounded-full bg-primary/10 text-primary">Stats</span>
|
|
</div>
|
|
</CardContent>
|
|
)}
|
|
</Card>
|
|
</Link>
|
|
)
|
|
}
|