207 lines
11 KiB
TypeScript
207 lines
11 KiB
TypeScript
"use client"
|
|
|
|
// import { PublicProfileRead } from "@/lib/types" // We'll need to define this or import generic
|
|
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
|
|
import { Badge } from "@/components/ui/badge"
|
|
import { Button } from "@/components/ui/button"
|
|
import { Card } from "@/components/ui/card"
|
|
import { Separator } from "@/components/ui/separator"
|
|
import { MapPin, CalendarDays, Ticket, Music2, Share2 } from "lucide-react"
|
|
import Link from "next/link"
|
|
|
|
// Temporary type definition until we sync shared types
|
|
interface HeadlinerBand {
|
|
name: string
|
|
slug: string
|
|
tier: string
|
|
logo_url?: string | null
|
|
}
|
|
|
|
interface SocialHandles {
|
|
bluesky?: string | null
|
|
mastodon?: string | null
|
|
instagram?: string | null
|
|
}
|
|
|
|
interface ProfilePosterProps {
|
|
profile: any // Typing loosely first to get the visual structure up
|
|
}
|
|
|
|
export function ProfilePoster({ profile }: ProfilePosterProps) {
|
|
const { username, display_name, bio, avatar, avatar_bg_color, location, social_handles, headliners, supporting_acts, stats, joined_at } = profile
|
|
|
|
// Poster Date (Joined At)
|
|
const memberSince = new Date(joined_at).toLocaleDateString('en-US', { month: 'long', year: 'numeric' })
|
|
|
|
return (
|
|
<div className="max-w-4xl mx-auto space-y-8">
|
|
{/* THE POSTER */}
|
|
<div className="relative overflow-hidden rounded-xl border bg-card text-card-foreground shadow-2xl">
|
|
{/* Background Texture/Gradient */}
|
|
<div className="absolute inset-0 z-0 bg-gradient-to-br from-background via-background to-primary/5 opacity-50" />
|
|
<div className="absolute inset-0 z-0 bg-[url('/noise.png')] opacity-[0.03] mix-blend-overlay" />
|
|
|
|
<div className="relative z-10 p-8 md:p-12 flex flex-col md:flex-row gap-12 items-start md:items-center">
|
|
|
|
{/* LEFT: The "Lineup" (Typography Hierarchy) */}
|
|
<div className="flex-1 text-center md:text-left space-y-8 w-full">
|
|
|
|
{/* Top Billing: User Name */}
|
|
<div className="space-y-2">
|
|
<h1 className="text-5xl md:text-7xl font-black tracking-tighter uppercase leading-none bg-clip-text text-transparent bg-gradient-to-b from-foreground to-foreground/70 filter drop-shadow-sm">
|
|
{display_name}
|
|
</h1>
|
|
<p className="text-xl md:text-2xl text-muted-foreground font-light tracking-widest uppercase">
|
|
Presents
|
|
</p>
|
|
</div>
|
|
|
|
<Separator className="bg-primary/20" />
|
|
|
|
{/* Headliners */}
|
|
{headliners && headliners.length > 0 ? (
|
|
<div className="flex flex-wrap justify-center md:justify-start gap-x-6 gap-y-2">
|
|
{headliners.map((band: HeadlinerBand) => (
|
|
<Link key={band.slug} href={`/${band.slug}`} className="hover:scale-105 transition-transform">
|
|
<span className="text-3xl md:text-5xl font-bold tracking-tight text-primary hover:text-primary/80 uppercase">
|
|
{band.name}
|
|
</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
) : (
|
|
<div className="text-3xl font-bold text-muted-foreground/30 uppercase opacity-50 py-4">
|
|
No Headliners Announced
|
|
</div>
|
|
)}
|
|
|
|
{/* Supporting Acts */}
|
|
{supporting_acts && supporting_acts.length > 0 && (
|
|
<div className="flex flex-wrap justify-center md:justify-start gap-x-4 gap-y-1 pt-4">
|
|
{supporting_acts.map((band: HeadlinerBand) => (
|
|
<Link key={band.slug} href={`/${band.slug}`}>
|
|
<span className="text-lg md:text-2xl font-medium text-foreground/80 hover:text-foreground uppercase">
|
|
{band.name}
|
|
</span>
|
|
</Link>
|
|
))}
|
|
</div>
|
|
)}
|
|
|
|
{/* Location / Date Line */}
|
|
<div className="flex flex-wrap items-center justify-center md:justify-start gap-4 text-sm font-mono text-muted-foreground pt-6 uppercase tracking-wider">
|
|
{location && (
|
|
<span className="flex items-center gap-1">
|
|
<MapPin className="w-4 h-4" /> {location}
|
|
</span>
|
|
)}
|
|
<span className="flex items-center gap-1">
|
|
<CalendarDays className="w-4 h-4" /> Est. {memberSince}
|
|
</span>
|
|
</div>
|
|
|
|
</div>
|
|
|
|
{/* RIGHT: The "Pass" (Avatar) */}
|
|
<div className="flex-shrink-0 mx-auto md:mx-0">
|
|
<div className="relative group perspective-1000">
|
|
{/* Lanyard String Mockup */}
|
|
<div className="absolute -top-32 left-1/2 -translate-x-1/2 w-4 h-32 bg-primary/20 z-0 hidden md:block" />
|
|
|
|
<div className="relative w-64 h-80 bg-black rounded-xl border-4 border-primary/20 shadow-2xl overflow-hidden flex flex-col transform transition-transform group-hover:rotate-y-12 duration-500">
|
|
{/* Pass Header */}
|
|
<div className="h-12 bg-primary/90 flex items-center justify-center">
|
|
<span className="font-bold text-primary-foreground tracking-[0.2em] text-xs">ALL ACCESS</span>
|
|
</div>
|
|
|
|
{/* Pass Image */}
|
|
<div className="flex-1 relative bg-muted">
|
|
{avatar ? (
|
|
<img src={avatar} alt={username} className="w-full h-full object-cover" />
|
|
) : (
|
|
<div className="w-full h-full flex items-center justify-center bg-zinc-900 text-6xl font-bold text-zinc-700" style={{ backgroundColor: avatar_bg_color || '#333' }}>
|
|
{profile.avatar_text || username.substring(0, 2).toUpperCase()}
|
|
</div>
|
|
)}
|
|
</div>
|
|
|
|
{/* Pass Footer */}
|
|
<div className="h-16 bg-white/5 backdrop-blur-md p-2 flex flex-col items-center justify-center gap-1">
|
|
<span className="font-mono text-xs text-muted-foreground">@{username}</span>
|
|
<div className="flex gap-2 text-xs opacity-50">
|
|
<div className="w-16 h-4 bg-foreground/20 rounded-sm" /> {/* Barcode mockup */}
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
|
|
</div>
|
|
</div>
|
|
|
|
{/* LOWER BIO SECTION */}
|
|
<div className="grid grid-cols-1 md:grid-cols-3 gap-8">
|
|
{/* Stats Card */}
|
|
<Card className="p-6 space-y-4">
|
|
<h3 className="font-mono text-sm text-muted-foreground uppercase">Tour Stats</h3>
|
|
<div className="space-y-4">
|
|
<div className="flex items-center justify-between">
|
|
<span className="flex items-center gap-2"><Ticket className="w-4 h-4" /> Shows</span>
|
|
<span className="font-bold text-xl">{stats.shows_attended}</span>
|
|
</div>
|
|
<Separator />
|
|
<div className="flex items-center justify-between">
|
|
<span className="flex items-center gap-2"><MapPin className="w-4 h-4" /> Venues</span>
|
|
<span className="font-bold text-xl">{stats.venues_visited}</span>
|
|
</div>
|
|
<Separator />
|
|
<div className="flex items-center justify-between">
|
|
<span className="flex items-center gap-2"><Music2 className="w-4 h-4" /> Reviews</span>
|
|
<span className="font-bold text-xl">{stats.reviews_written}</span>
|
|
</div>
|
|
</div>
|
|
</Card>
|
|
|
|
{/* Bio & Socials */}
|
|
<Card className="md:col-span-2 p-6 flex flex-col justify-between gap-6">
|
|
<div className="space-y-4">
|
|
<h3 className="font-mono text-sm text-muted-foreground uppercase">About</h3>
|
|
<p className="text-lg leading-relaxed whitespace-pre-wrap">
|
|
{bio || "This fan hasn't written a bio yet. They're probably at a show."}
|
|
</p>
|
|
</div>
|
|
|
|
<div className="flex flex-wrap gap-3">
|
|
{social_handles.bluesky && (
|
|
<Button variant="outline" size="sm" asChild>
|
|
<a href={`https://bsky.app/profile/${social_handles.bluesky}`} target="_blank" rel="noopener noreferrer">
|
|
BlueSky
|
|
</a>
|
|
</Button>
|
|
)}
|
|
{social_handles.mastodon && (
|
|
<Button variant="outline" size="sm" asChild>
|
|
<a href={social_handles.mastodon} target="_blank" rel="noopener noreferrer">
|
|
Mastodon
|
|
</a>
|
|
</Button>
|
|
)}
|
|
{social_handles.instagram && (
|
|
<Button variant="outline" size="sm" asChild>
|
|
<a href={`https://instagram.com/${social_handles.instagram}`} target="_blank" rel="noopener noreferrer">
|
|
Instagram
|
|
</a>
|
|
</Button>
|
|
)}
|
|
|
|
<div className="flex-1" />
|
|
|
|
<Button variant="ghost" size="sm">
|
|
<Share2 className="w-4 h-4 mr-2" /> Share Profile
|
|
</Button>
|
|
</div>
|
|
</Card>
|
|
</div>
|
|
</div>
|
|
)
|
|
}
|