# Event Detail

> Full event detail view with organizer info, interactive map, policies, and ticket purchase. Fullwidth mode only.

A shadcn/ui block from the [@manifest](https://ui.manifest.build) registry, listed on [shadcn.exchange](https://shadcn.exchange/r/manifest/event-detail).

- Page, with a live preview: https://shadcn.exchange/r/manifest/event-detail
- Screenshot: https://abzjhwgwpdpz7ua5.public.blob.vercel-storage.com/shots/manifest/event-detail-light-04866b0b10449ab8.png
- Registry item: https://ui.manifest.build/r/event-detail.json
- Categories: events
- Listed: 2026-10-13, source last changed 2026-10-13

## Install

```bash
npx shadcn@latest add https://ui.manifest.build/r/event-detail.json
```

Run it in a project that has shadcn/ui set up. It adds the files below and installs what they depend on.

## Dependencies

- npm packages: `leaflet`, `lucide-react`, `react-leaflet`
- Registry items: `button`, `https://ui.manifest.build/r/manifest-types.json`, `https://ui.manifest.build/r/event-shared.json`

## Files

### `registry/events/event-detail.tsx`

```tsx
'use client'

import { Button } from '@/components/ui/button'
import { cn } from '@/lib/utils'
import {
  MapPin,
  Star,
  Share2,
  Heart,
  ChevronLeft,
  ChevronRight,
  CheckCircle,
  Car,
  Train,
  Bike,
  Footprints,
  Flag,
  BadgeCheck,
  Timer
} from 'lucide-react'
import { Suspense, useState } from 'react'
import type { EventDetails } from './types'
import {
  LazyLeafletMap,
  formatNumber,
  MapPlaceholder,
  EventSignalBadge
} from './shared'
import { demoEventDetails } from './demo/events'

// Format date for display
function formatEventDateTime(startDateTime: string, endDateTime?: string): string {
  const start = new Date(startDateTime)
  const now = new Date()
  const today = new Date(now.getFullYear(), now.getMonth(), now.getDate())
  const tomorrow = new Date(today.getTime() + 24 * 60 * 60 * 1000)
  const startDay = new Date(start.getFullYear(), start.getMonth(), start.getDate())

  const timeOptions: Intl.DateTimeFormatOptions = { hour: 'numeric', minute: '2-digit', hour12: true }
  const startTime = start.toLocaleTimeString('en-US', timeOptions)

  let datePrefix: string
  if (startDay.getTime() === today.getTime()) {
    datePrefix = 'Today'
  } else if (startDay.getTime() === tomorrow.getTime()) {
    datePrefix = 'Tomorrow'
  } else {
    datePrefix = start.toLocaleDateString('en-US', { month: 'short', day: 'numeric' })
  }

  if (endDateTime) {
    const end = new Date(endDateTime)
    const endTime = end.toLocaleTimeString('en-US', timeOptions)
    return `${datePrefix} · ${startTime} - ${endTime}`
  }

  return `${datePrefix} · ${startTime}`
}



/**
 * ═══════════════════════════════════════════════════════════════════════════
 * EventDetailProps
 * ═══════════════════════════════════════════════════════════════════════════
 *
 * Props for the EventDetail component. Displays comprehensive event information
 * including image carousel, location map, organizer details, and ticket actions.
 */
export interface EventDetailProps {
  data?: {
    /** The full event details object to display. */
    event?: EventDetails
  }
  actions?: {
    /** Called when "Get tickets" button is clicked. */
    onGetTickets?: (event: EventDetails) => void
    /** Called when share button is clicked. */
    onShare?: (event: EventDetails) => void
    /** Called when save/heart button is clicked. */
    onSave?: (event: EventDetails) => void
    /** Called when back navigation button is clicked. */
    onBack?: () => void
    /** Called when "Follow" organizer button is clicked. */
    onFollow?: (organizer: EventDetails['organizer']) => void
    /** Called when "Contact" organizer button is clicked. */
    onContact?: (organizer: EventDetails['organizer']) => void
  }
  appearance?: {
    /**
     * Whether to show the AI match explanation section.
     * @default true
     */
    showAiMatch?: boolean
    /**
     * Whether to show the interactive map.
     * @default true
     */
    showMap?: boolean
  }
}

export function EventDetail({ data, actions, appearance }: EventDetailProps) {
  const resolved: NonNullable<EventDetailProps['data']> = data ?? { event: demoEventDetails }
  const event = resolved.event
  const onGetTickets = actions?.onGetTickets
  const onShare = actions?.onShare
  const onSave = actions?.onSave
  const onBack = actions?.onBack
  const onFollow = actions?.onFollow
  const onContact = actions?.onContact
  const showAiMatch = appearance?.showAiMatch ?? true
  const showMap = appearance?.showMap ?? true

  const [currentImageIndex, setCurrentImageIndex] = useState(0)
  const [isSaved, setIsSaved] = useState(false)

  if (!event) {
    return null
  }

  const images = event.images?.length ? event.images : (event.image ? [event.image] : [])

  const handlePrevImage = () => {
    setCurrentImageIndex((prev) => (prev === 0 ? images.length - 1 : prev - 1))
  }

  const handleNextImage = () => {
    setCurrentImageIndex((prev) => (prev === images.length - 1 ? 0 : prev + 1))
  }

  const handleSave = () => {
    setIsSaved(!isSaved)
    onSave?.(event)
  }

  return (
    <div className="mx-auto max-w-lg bg-background">
      {/* Image Carousel */}
      {images.length > 0 && (
        <div className="relative aspect-[4/3] overflow-hidden bg-muted">
          <img
            src={images[currentImageIndex]}
            alt={event.title || 'Event image'}
            className="h-full w-full object-cover"
          />

          {/* Navigation overlay */}
          <div className="absolute inset-0 flex items-center justify-between p-2">
            {images.length > 1 && (
              <>
                <button
                  onClick={handlePrevImage}
                  aria-label="Previous image"
                  className="rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
                >
                  <ChevronLeft className="h-5 w-5" />
                </button>
                <button
                  onClick={handleNextImage}
                  aria-label="Next image"
                  className="rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
                >
                  <ChevronRight className="h-5 w-5" />
                </button>
              </>
            )}
          </div>

          {/* Top actions */}
          <div className="absolute top-3 right-3 flex gap-2">
            <button
              onClick={() => onShare?.(event)}
              aria-label="Share event"
              className="rounded-full bg-white/90 p-2 shadow-sm hover:bg-white"
            >
              <Share2 className="h-5 w-5" />
            </button>
            <button
              onClick={handleSave}
              aria-label={isSaved ? 'Remove from saved' : 'Save event'}
              className="rounded-full bg-white/90 p-2 shadow-sm hover:bg-white"
            >
              <Heart className={cn('h-5 w-5', isSaved && 'fill-red-500 text-red-500')} />
            </button>
          </div>

          {/* Back button */}
          {onBack && (
            <button
              onClick={onBack}
              aria-label="Go back"
              className="absolute top-3 left-3 rounded-full bg-black/50 p-2 text-white hover:bg-black/70"
            >
              <ChevronLeft className="h-5 w-5" />
            </button>
          )}

          {/* Image counter */}
          {images.length > 1 && (
            <div className="absolute bottom-3 right-3 rounded-full bg-black/60 px-2.5 py-1 text-xs text-white">
              {currentImageIndex + 1} / {images.length}
            </div>
          )}
        </div>
      )}

      <div className="space-y-6 p-4">
        {/* Signal Badge */}
        {event.eventSignal && (
          <div>
            <EventSignalBadge signal={event.eventSignal} />
          </div>
        )}

        {/* Category */}
        {event.category && (
          <span className="inline-block rounded-full bg-muted px-3 py-1 text-sm font-medium">
            {event.category}
          </span>
        )}

        {/* Title */}
        {event.title && (
          <h1 className="text-2xl font-bold leading-tight">{event.title}</h1>
        )}

        {/* Organizer + Rating */}
        {event.organizer && (
          <div className="flex items-center gap-2 text-sm">
            {event.organizer.verified && (
              <BadgeCheck className="h-4 w-4 text-blue-500" />
            )}
            {event.organizer.name && (
              <span className="font-medium">{event.organizer.name}</span>
            )}
            {(event.organizer.rating !== undefined || event.organizer.reviewCount !== undefined) && (
              <>
                <span className="text-muted-foreground">·</span>
                <span className="flex items-center gap-1">
                  <Star className="h-4 w-4 fill-current text-yellow-500" />
                  {event.organizer.rating !== undefined && event.organizer.rating}
                  {event.organizer.reviewCount !== undefined && ` (${formatNumber(event.organizer.reviewCount)})`}
                </span>
              </>
            )}
          </div>
        )}

        {/* Venue + Location */}
        {(event.venue_details?.name || event.venue || event.city) && (
          <div className="flex items-start gap-2 text-sm text-muted-foreground">
            <MapPin className="mt-0.5 h-4 w-4 shrink-0" />
            <span>
              {[event.venue_details?.name || event.venue, event.city].filter(Boolean).join(' · ')}
              {event.neighborhood && ` (${event.neighborhood})`}
            </span>
          </div>
        )}

        {/* Price + Attendees */}
        <div className="flex items-center gap-4">
          {event.priceRange && (
            <div>
              <div className="text-lg font-semibold">{event.priceRange}</div>
            </div>
          )}
          {event.attendeesCount !== undefined && (
            <div className="flex items-center gap-2">
              {event.friendsGoing && event.friendsGoing.length > 0 && (
                <div className="flex -space-x-2">
                  {event.friendsGoing.slice(0, 3).map((friend) => (
                    friend.avatar && (
                      <img
                        key={friend.name || friend.avatar}
                        src={friend.avatar}
                        alt={friend.name || 'Friend'}
                        className="h-6 w-6 rounded-full border-2 border-background"
                      />
                    )
                  ))}
                </div>
              )}
              <span className="text-sm text-muted-foreground">
                {event.friendsGoing && event.friendsGoing.length > 0 && `+ ${event.friendsGoing.length} friends · `}
                {event.attendeesCount} going
              </span>
            </div>
          )}
        </div>

        {/* Vibe Tags */}
        {event.vibeTags && event.vibeTags.length > 0 && (
          <div className="flex flex-wrap gap-2">
            {event.vibeTags.map((tag) => (
              <span
                key={tag}
                className="rounded-full border px-3 py-1 text-sm"
              >
                {tag}
              </span>
            ))}
          </div>
        )}

        {/* CTA Buttons */}
        <div className="flex gap-3">
          <Button
            className="flex-1 bg-primary hover:bg-primary/90"
            onClick={() => onGetTickets?.(event)}
          >
            Get tickets
          </Button>
          <Button variant="outline" className="flex-1">
            Invite friends
          </Button>
        </div>

        {/* AI Match */}
        {showAiMatch && event.aiSummary && (
          <div className="rounded-lg bg-muted/50 p-4">
            <h3 className="font-semibold">Why this matches your vibe</h3>
            <p className="mt-1 text-sm text-muted-foreground">{event.aiSummary}</p>
          </div>
        )}

        {/* About */}
        {event.description && (
          <div>
            <h2 className="text-lg font-semibold">About</h2>
            <p className="mt-2 text-sm text-muted-foreground">{event.description}</p>
          </div>
        )}

        {/* Lineup */}
        {event.lineup && event.lineup.length > 0 && (
          <div>
            <h3 className="text-sm font-medium text-muted-foreground">Lineup</h3>
            <div className="mt-2 flex flex-wrap gap-2">
              {event.lineup.map((artist) => (
                <span key={artist} className="rounded-full border px-3 py-1 text-sm">
                  {artist}
                </span>
              ))}
            </div>
          </div>
        )}

        {/* Good to Know */}
        {event.goodToKnow && (
          <div>
            <h2 className="text-lg font-semibold">Good to know</h2>
            <div className="mt-3 grid grid-cols-2 gap-4">
              <div className="rounded-lg bg-muted/50 p-3">
                <h4 className="text-sm font-medium">Highlights</h4>
                <div className="mt-2 space-y-1.5 text-sm text-muted-foreground">
                  {event.goodToKnow.duration && (
                    <div className="flex items-center gap-2">
                      <Timer className="h-4 w-4" />
                      {event.goodToKnow.duration}
                    </div>
                  )}
                  {event.locationType !== 'online' && (
                    <div className="flex items-center gap-2">
                      <MapPin className="h-4 w-4" />
                      In person
                    </div>
                  )}
                </div>
              </div>
              {event.policies?.refund && (
                <div className="rounded-lg bg-muted/50 p-3">
                  <h4 className="text-sm font-medium">Refund Policy</h4>
                  <p className="mt-2 text-sm text-muted-foreground">
                    {event.policies.refund}
                  </p>
                </div>
              )}
            </div>
          </div>
        )}

        {/* Location */}
        {event.venue_details && (
          <div>
            <h2 className="text-lg font-semibold">Location</h2>
            <div className="mt-3">
              {event.venue_details.name && (
                <p className="font-medium">{event.venue_details.name}</p>
              )}
              {event.venue_details.address && (
                <p className="text-sm text-muted-foreground">{event.venue_details.address}</p>
              )}
              {event.venue_details.city && (
                <p className="text-sm text-muted-foreground">{event.venue_details.city}</p>
              )}
            </div>

            {showMap && event.venue_details.coordinates && (
              <div className="mt-4 aspect-video overflow-hidden rounded-lg bg-muted">
                <Suspense fallback={<MapPlaceholder />}>
                  <LazyLeafletMap
                    center={[event.venue_details.coordinates.lat, event.venue_details.coordinates.lng]}
                    zoom={15}
                    scrollWheelZoom={false}
                    renderMarkers={({ Marker, L }) => {
                      const icon = L.divIcon({
                        className: '',
                        html: `<div style="
                          position: absolute;
                          left: 50%;
                          top: 50%;
                          transform: translate(-50%, -100%);
                          display: flex;
                          flex-direction: column;
                          align-items: center;
                        ">
                          <div style="
                            background-color: #18181b;
                            color: white;
                            padding: 6px 10px;
                            border-radius: 8px;
                            font-size: 12px;
                            font-weight: 600;
                            font-family: system-ui, -apple-system, sans-serif;
                            white-space: nowrap;
                            box-shadow: 0 2px 8px rgba(0,0,0,0.2);
                          ">${event.venue_details!.name}</div>
                          <div style="
                            width: 0;
                            height: 0;
                            border-left: 8px solid transparent;
                            border-right: 8px solid transparent;
                            border-top: 8px solid #18181b;
                            margin-top: -1px;
                          "></div>
                        </div>`,
                        iconSize: [100, 40],
                        iconAnchor: [50, 40]
                      })
                      return (
                        <Marker
                          position={[event.venue_details!.coordinates!.lat, event.venue_details!.coordinates!.lng]}
                          icon={icon}
                        />
                      )
                    }}
                  />
                </Suspense>
              </div>
            )}

            <div className="mt-4">
              <p className="text-sm font-medium">How do you want to get there?</p>
              <div className="mt-2 space-y-2">
                {[
                  { icon: Car, label: 'Driving' },
                  { icon: Train, label: 'Public transport' },
                  { icon: Bike, label: 'Biking' },
                  { icon: Footprints, label: 'Walking' }
                ].map(({ icon: Icon, label }) => (
                  <button key={label} className="flex w-full items-center gap-3 rounded-lg p-2 hover:bg-muted">
                    <Icon className="h-5 w-5 text-muted-foreground" />
                    <span className="text-sm">{label}</span>
                  </button>
                ))}
              </div>
            </div>
          </div>
        )}

        {/* Organizer */}
        {event.organizer && (
          <div>
            <h2 className="text-lg font-semibold">Organized by</h2>
            <div className="mt-3 rounded-lg border p-4">
              <div className="flex items-center gap-3">
                {event.organizer.image ? (
                  <img
                    src={event.organizer.image}
                    alt={event.organizer.name || 'Organizer'}
                    className="h-12 w-12 rounded-full object-cover"
                  />
                ) : event.organizer.name ? (
                  <div className="flex h-12 w-12 items-center justify-center rounded-full bg-muted text-lg font-medium">
                    {event.organizer.name.charAt(0)}
                  </div>
                ) : null}
                <div className="flex-1">
                  {event.organizer.name && (
                    <p className="font-medium">{event.organizer.name}</p>
                  )}
                  <div className="flex gap-4 text-xs text-muted-foreground">
                    {event.organizer.followers !== undefined && (
                      <span>Followers<br /><strong>{formatNumber(event.organizer.followers)}</strong></span>
                    )}
                    {event.organizer.eventsCount !== undefined && (
                      <span>Events<br /><strong>{event.organizer.eventsCount}</strong></span>
                    )}
                    {event.organizer.hostingYears !== undefined && (
                      <span>Hosting<br /><strong>{event.organizer.hostingYears} yrs</strong></span>
                    )}
                  </div>
                </div>
              </div>
              <div className="mt-3 flex gap-2">
                <Button variant="outline" size="sm" className="flex-1" onClick={() => onContact?.(event.organizer)}>
                  Contact
                </Button>
                <Button size="sm" className="flex-1 bg-primary hover:bg-primary/90" onClick={() => onFollow?.(event.organizer)}>
                  Follow
                </Button>
              </div>
              {(event.organizer.trackRecord || event.organizer.responseRate) && (
                <div className="mt-3 flex flex-wrap gap-2 text-xs">
                  {event.organizer.trackRecord === 'great' && (
                    <span className="flex items-center gap-1 text-green-600">
                      <CheckCircle className="h-3 w-3" /> Great track record
                    </span>
                  )}
                  {event.organizer.responseRate === 'very responsive' && (
                    <span className="flex items-center gap-1 text-orange-600">
                      <CheckCircle className="h-3 w-3" /> Very responsive
                    </span>
                  )}
                </div>
              )}
            </div>
          </div>
        )}

        {/* Policies & Info */}
        {event.policies && (
          <div>
            <h2 className="text-lg font-semibold">Policies & Info</h2>
            <div className="mt-3 flex flex-wrap gap-2">
              {event.goodToKnow?.ageRestriction && (
                <span className="rounded-full border px-3 py-1 text-sm">{event.goodToKnow.ageRestriction}</span>
              )}
              {event.policies.idRequired && (
                <span className="rounded-full border px-3 py-1 text-sm">ID checks</span>
              )}
              {event.policies.securityOnSite && (
                <span className="rounded-full border px-3 py-1 text-sm">Security on site</span>
              )}
            </div>
          </div>
        )}

        {/* FAQs */}
        {event.faq && event.faq.length > 0 && (
          <div>
            <h2 className="text-lg font-semibold">FAQs</h2>
            <div className="mt-3 space-y-2">
              {event.faq.map((item) => (
                <div key={item.question} className="text-sm">
                  <p><strong>{item.question.replace('?', '')}:</strong> {item.answer}</p>
                </div>
              ))}
            </div>
          </div>
        )}

        {/* Report */}
        <button className="flex items-center gap-2 text-sm text-blue-600 hover:underline">
          <Flag className="h-4 w-4" />
          Report this event
        </button>

        {/* Related Tags */}
        {event.relatedTags && event.relatedTags.length > 0 && (
          <div>
            <h2 className="text-lg font-semibold">Related to this event</h2>
            <div className="mt-3 flex flex-wrap gap-2">
              {event.relatedTags.map((tag) => (
                <span key={tag} className="rounded-full border px-3 py-1 text-sm hover:bg-muted cursor-pointer">
                  {tag}
                </span>
              ))}
            </div>
          </div>
        )}

        {/* Bottom CTA */}
        <div className="mt-6 rounded-lg border bg-muted/30 p-4">
          <div className="flex items-center justify-between gap-4">
            <div>
              {event.priceRange && <p className="font-semibold">{event.priceRange}</p>}
              {event.startDateTime && <p className="text-sm text-muted-foreground">{formatEventDateTime(event.startDateTime, event.endDateTime)}</p>}
            </div>
            <Button
              className="bg-primary hover:bg-primary/90"
              onClick={() => onGetTickets?.(event)}
            >
              Get tickets
            </Button>
          </div>
        </div>
      </div>
    </div>
  )
}
```

### `registry/events/demo/events.ts`

```ts
// Demo data for Events category components
// This file contains sample data used for component previews and documentation

import type { Event, EventDetails } from '../types'

// Helper to generate dates relative to today
function getDateAt(daysFromNow: number, hour: number): string {
  const date = new Date()
  date.setDate(date.getDate() + daysFromNow)
  date.setHours(hour, 0, 0, 0)
  return date.toISOString()
}

// Single event for EventCard default
export const demoEvent: Event = {
  title: 'NEON Vol. 9',
  category: 'Music',
  venue: 'Echoplex',
  neighborhood: 'Echo Park',
  city: 'Los Angeles',
  dateTime: 'Tonight 9:00 PM - 3:00 AM',
  priceRange: '$45 - $150',
  image: 'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800',
  vibeTags: ['High energy', 'Late night', 'Dressy'],
  vibeDescription:
    'Immersive electronic experience with world-class DJs and stunning visuals.',
  aiSummary:
    "Immersive electronic night with world-class DJs and stunning visuals at LA's top-rated venue.",
  lineup: ['DJ Shadow', 'Bonobo', 'Four Tet', 'Caribou'],
  ticketTiers: [
    'General Admission $45',
    'VIP Access $120',
    'Backstage Pass $150'
  ],
  eventSignal: 'going-fast',
  organizerRating: 4.8,
  reviewCount: 12453,
  venueRating: 4.8,
  ageRestriction: '21+',
  hasMultipleDates: true
}

// 15 events for EventList default
export const demoEvents: Event[] = [
  {
    title: 'NEON Vol. 9',
    category: 'Music',
    venue: 'Echoplex',
    neighborhood: 'Echo Park',
    city: 'Los Angeles',
    dateTime: 'Tonight 9:00 PM - 3:00 AM',
    priceRange: '$45 - $150',
    image: 'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800',
    coordinates: { lat: 34.0781, lng: -118.2606 },
    vibeTags: ['High energy', 'Late night'],
    eventSignal: 'going-fast',
    organizerRating: 4.8,
    reviewCount: 12453,
    ageRestriction: '21+'
  },
  {
    title: 'The Midnight Show',
    category: 'Comedy',
    venue: 'The Comedy Underground',
    neighborhood: 'Santa Monica',
    city: 'Los Angeles',
    dateTime: 'Tonight 10:00 PM - 12:00 AM',
    priceRange: '$15 - $35',
    image: 'https://images.unsplash.com/photo-1585699324551-f6c309eedeca?w=800',
    coordinates: { lat: 34.0195, lng: -118.4912 },
    vibeTags: ['Social', 'Late night'],
    eventSignal: 'popular',
    organizerRating: 4.7,
    reviewCount: 3241,
    discount: 'TONIGHT ONLY - 40% OFF'
  },
  {
    title: 'Salsa Sundays @ Echo Park',
    category: 'Classes',
    venue: 'Echo Park Lake',
    neighborhood: 'Echo Park',
    city: 'Los Angeles',
    dateTime: 'Saturday 6:00 PM - 10:00 PM',
    priceRange: 'Free',
    image: 'https://images.unsplash.com/photo-1504609813442-a8924e83f76e?w=800',
    coordinates: { lat: 34.0731, lng: -118.2608 },
    vibeTags: ['High energy', 'Social'],
    eventSignal: 'just-added',
    organizerRating: 4.9,
    reviewCount: 8764
  },
  {
    title: 'Dawn Flow: Griffith Park',
    category: 'Classes',
    venue: 'Griffith Park',
    neighborhood: 'Los Feliz',
    city: 'Los Angeles',
    dateTime: 'Tomorrow 6:00 AM - 8:00 AM',
    priceRange: 'Free',
    image: 'https://images.unsplash.com/photo-1545205597-3d9d02c29597?w=800',
    coordinates: { lat: 34.1365, lng: -118.2943 },
    vibeTags: ['Chill', 'Wellness', 'Outdoor'],
    organizerRating: 4.9,
    reviewCount: 8764,
    discount: 'FREE - First 50 Only'
  },
  {
    title: 'Lakers vs Celtics',
    category: 'Sports',
    venue: 'Crypto.com Arena',
    neighborhood: 'Downtown',
    city: 'Los Angeles',
    dateTime: 'Friday 7:30 PM - 10:30 PM',
    priceRange: '$125 - $850',
    image: 'https://images.unsplash.com/photo-1546519638-68e109498ffc?w=800',
    coordinates: { lat: 34.0430, lng: -118.2673 },
    vibeTags: ['High energy', 'Social', 'Premium'],
    eventSignal: 'sales-end-soon',
    organizerRating: 4.5,
    reviewCount: 2341
  },
  {
    title: 'Smorgasburg LA: Sunday Market',
    category: 'Food & Drink',
    venue: 'ROW DTLA',
    neighborhood: 'Arts District',
    city: 'Los Angeles',
    dateTime: 'Sunday 10:00 AM - 4:00 PM',
    priceRange: 'Free',
    image: 'https://images.unsplash.com/photo-1555939594-58d7cb561ad1?w=800',
    coordinates: { lat: 34.0341, lng: -118.2324 },
    vibeTags: ['Family-friendly', 'Outdoor', 'Social'],
    organizerRating: 4.8,
    reviewCount: 5632
  },
  {
    title: 'LACMA After Hours',
    category: 'Arts',
    venue: 'LACMA',
    neighborhood: 'Miracle Mile',
    city: 'Los Angeles',
    dateTime: 'Friday 7:00 PM - 11:00 PM',
    priceRange: '$35 - $75',
    image: 'https://images.unsplash.com/photo-1531243269054-5ebf6f34081e?w=800',
    coordinates: { lat: 34.0639, lng: -118.3592 },
    vibeTags: ['Chill', 'Date night', 'Sophisticated'],
    organizerRating: 4.7,
    reviewCount: 1234,
    ageRestriction: '21+',
    discount: 'MEMBER PRICE'
  },
  {
    title: 'Blue Note Under Stars',
    category: 'Music',
    venue: 'Hollywood Bowl',
    neighborhood: 'Hollywood Hills',
    city: 'Los Angeles',
    dateTime: 'Saturday 8:00 PM - 11:00 PM',
    priceRange: '$45 - $200',
    image: 'https://images.unsplash.com/photo-1514320291840-2e0a9bf2a9ae?w=800',
    coordinates: { lat: 34.1122, lng: -118.3391 },
    vibeTags: ['Chill', 'Date night', 'Outdoor'],
    lineup: ['Kamasi Washington', 'Thundercat', 'Terrace Martin'],
    organizerRating: 4.8,
    reviewCount: 12453
  },
  {
    title: 'Meraki: Seth Troxler',
    category: 'Nightlife',
    venue: 'Sound Nightclub',
    neighborhood: 'Hollywood',
    city: 'Los Angeles',
    dateTime: 'Saturday 10:00 PM - 4:00 AM',
    priceRange: '$35 - $65',
    image: 'https://images.unsplash.com/photo-1571266028243-e4733b0f0bb0?w=800',
    coordinates: { lat: 34.0928, lng: -118.3287 },
    vibeTags: ['High energy', 'Late night', 'Underground'],
    lineup: ['Amelie Lens', 'I Hate Models', 'FJAAK'],
    organizerRating: 4.6,
    reviewCount: 1876,
    ageRestriction: '21+'
  },
  {
    title: 'Whitney Cummings + Friends',
    category: 'Comedy',
    venue: 'The Laugh Factory',
    neighborhood: 'Hollywood',
    city: 'Los Angeles',
    dateTime: 'In 2 days 8:00 PM - 11:00 PM',
    priceRange: '$25 - $55',
    image: 'https://images.unsplash.com/photo-1527224538127-2104bb71c51b?w=800',
    coordinates: { lat: 34.0901, lng: -118.3615 },
    vibeTags: ['Chill', 'Social', 'Date night'],
    organizerRating: 4.7,
    reviewCount: 3241,
    ageRestriction: '18+'
  },
  {
    title: 'Venice Beach Drum Circle',
    category: 'Music',
    venue: 'Venice Beach Boardwalk',
    neighborhood: 'Venice',
    city: 'Los Angeles',
    dateTime: 'Sunday 4:00 PM - 8:00 PM',
    priceRange: 'Free',
    image: 'https://images.unsplash.com/photo-1506157786151-b8491531f063?w=800',
    coordinates: { lat: 33.9850, lng: -118.4695 },
    vibeTags: ['Outdoor', 'Social', 'Chill'],
    eventSignal: 'popular',
    organizerRating: 4.6,
    reviewCount: 2145
  },
  {
    title: 'Rooftop Cinema: Blade Runner',
    category: 'Film',
    venue: 'Rooftop Cinema Club',
    neighborhood: 'DTLA',
    city: 'Los Angeles',
    dateTime: 'Friday 8:30 PM - 11:00 PM',
    priceRange: '$25 - $45',
    image: 'https://images.unsplash.com/photo-1489599849927-2ee91cede3ba?w=800',
    coordinates: { lat: 34.0407, lng: -118.2468 },
    vibeTags: ['Date night', 'Views', 'Chill'],
    organizerRating: 4.8,
    reviewCount: 892
  },
  {
    title: 'Dodgers vs Giants',
    category: 'Sports',
    venue: 'Dodger Stadium',
    neighborhood: 'Elysian Park',
    city: 'Los Angeles',
    dateTime: 'Saturday 1:10 PM - 4:30 PM',
    priceRange: '$35 - $350',
    image: 'https://images.unsplash.com/photo-1566577739112-5180d4bf9390?w=800',
    coordinates: { lat: 34.0739, lng: -118.2400 },
    vibeTags: ['Family-friendly', 'Social', 'High energy'],
    eventSignal: 'few-tickets-left',
    organizerRating: 4.7,
    reviewCount: 15678
  },
  {
    title: 'Natural Wine Fair',
    category: 'Food & Drink',
    venue: 'Grand Central Market',
    neighborhood: 'Downtown',
    city: 'Los Angeles',
    dateTime: 'Sunday 12:00 PM - 6:00 PM',
    priceRange: '$45 - $85',
    image: 'https://images.unsplash.com/photo-1510812431401-41d2bd2722f3?w=800',
    coordinates: { lat: 34.0508, lng: -118.2490 },
    vibeTags: ['Tasting', 'Social', 'Sophisticated'],
    eventSignal: 'just-added',
    organizerRating: 4.5,
    reviewCount: 567,
    ageRestriction: '21+'
  },
  {
    title: 'Meditation in the Gardens',
    category: 'Wellness',
    venue: 'The Getty Center',
    neighborhood: 'Brentwood',
    city: 'Los Angeles',
    dateTime: 'Sunday 7:00 AM - 9:00 AM',
    priceRange: 'Free',
    image: 'https://images.unsplash.com/photo-1506126613408-eca07ce68773?w=800',
    coordinates: { lat: 34.0780, lng: -118.4741 },
    vibeTags: ['Wellness', 'Outdoor', 'Chill'],
    organizerRating: 4.9,
    reviewCount: 1234
  }
]

// Detailed event for EventDetail default
export const demoEventDetails: EventDetails = {
  title: 'Sunglasses at Night: Underground Techno',
  category: 'Nightlife',
  venue: 'The White Rabbit',
  neighborhood: 'The Woodlands',
  city: 'Houston, TX',
  startDateTime: getDateAt(2, 22),
  endDateTime: getDateAt(3, 4),
  priceRange: '$15 - $30',
  images: [
    'https://images.unsplash.com/photo-1470225620780-dba8ba36b745?w=800',
    'https://images.unsplash.com/photo-1514525253161-7a46d19cd819?w=800',
    'https://images.unsplash.com/photo-1533174072545-7a4b6ad7a6c3?w=800'
  ],
  vibeTags: ['High energy', 'Late night', 'Underground'],
  eventSignal: 'going-fast',
  aiSummary: 'Raw, unfiltered techno in an authentic warehouse setting.',
  description:
    'Experience the raw energy of underground techno. Industrial beats, immersive visuals, and a crowd that lives for the music.',
  lineup: ['Amelie Lens', 'I Hate Models', 'FJAAK'],
  attendeesCount: 537,
  friendsGoing: [
    { name: 'Alex', avatar: 'https://i.pravatar.cc/40?u=alex' },
    { name: 'Sam', avatar: 'https://i.pravatar.cc/40?u=sam' }
  ],
  organizer: {
    name: 'Midnight Lovers',
    image: 'https://i.pravatar.cc/80?u=midnight',
    rating: 4.5,
    reviewCount: 1067,
    verified: true,
    followers: 1200,
    eventsCount: 154,
    hostingYears: 8,
    trackRecord: 'great',
    responseRate: 'very responsive'
  },
  venue_details: {
    name: 'The White Rabbit',
    address: '8827 Nasher Ave',
    city: 'Houston TX',
    coordinates: { lat: 29.7604, lng: -95.3698 }
  },
  tiers: [
    { name: 'General Admission', price: 15, available: 50 },
    {
      name: 'VIP Access',
      price: 30,
      available: 20,
      benefits: ['Skip the line', 'Exclusive lounge']
    }
  ],
  goodToKnow: {
    duration: '2 hours',
    doorsOpen: '7:00 PM',
    showtime: '7:30 PM',
    ageRestriction: '21+',
    dressCode: 'Casual',
    parking: 'Limited, leave early to avoid long queues'
  },
  policies: {
    refund: 'No refunds. Tickets are transferable.',
    entry: 'Open 2 hours before event',
    idRequired: true,
    securityOnSite: true
  },
  faq: [
    {
      question: 'What is the refund policy?',
      answer: 'No refunds. Tickets are transferable.'
    },
    {
      question: 'When do doors open?',
      answer: 'Open 2 hours before event.'
    },
    {
      question: 'Is there parking?',
      answer: 'Limited, leave early to avoid long queues.'
    }
  ],
  relatedTags: ['Houston Events', 'Texas Nightlife', 'Techno Parties']
}

// Ticket tiers for TicketTierSelect
export const demoTicketTiers = [
  {
    id: '1',
    name: 'General Admission',
    price: 45,
    fee: 5,
    available: 100,
    maxPerOrder: 10,
  },
  {
    id: '2',
    name: 'VIP',
    price: 150,
    fee: 15,
    available: 20,
    maxPerOrder: 4,
    description: 'Includes backstage access',
  },
]

// Event confirmation data
export const demoEventConfirmation = {
  orderNumber: 'EVT-12345',
  eventTitle: 'Summer Music Festival',
  ticketCount: 2,
  recipientEmail: 'customer@example.com',
  eventDate: 'Jan 20, 2024',
  eventLocation: 'Central Park, New York',
  organizer: {
    name: 'Live Nation',
  },
}
```
