@getmicdrop/venue-calendar 4.0.5 → 4.0.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +700 -700
- package/dist/{AirbnbSplitMain-BVNtTJc-.js → AirbnbSplitMain-QRE-W2ZL.js} +4 -4
- package/dist/AirbnbSplitMain-QRE-W2ZL.js.map +1 -0
- package/dist/{CarouselView.legacy-B6HtvF8d.js → CarouselView.legacy-DtUAYloL.js} +3 -3
- package/dist/CarouselView.legacy-DtUAYloL.js.map +1 -0
- package/dist/{CartView-DrOhr4Qi.js → CartView-Be1-gajf.js} +158 -149
- package/dist/CartView-Be1-gajf.js.map +1 -0
- package/dist/{Checkout-DB1Aocl-.js → Checkout-DET04TDs.js} +4 -4
- package/dist/Checkout-DET04TDs.js.map +1 -0
- package/dist/{Checkout.legacy-DpcjjHQu.js → Checkout.legacy-Dg8qlXGF.js} +4 -4
- package/dist/Checkout.legacy-Dg8qlXGF.js.map +1 -0
- package/dist/{CheckoutMain-D0_1xfR8.js → CheckoutMain-CddDRMZ6.js} +4 -4
- package/dist/CheckoutMain-CddDRMZ6.js.map +1 -0
- package/dist/{CheckoutTimer-r1HaN6b6.js → CheckoutTimer-12XARKCb.js} +2 -2
- package/dist/CheckoutTimer-12XARKCb.js.map +1 -0
- package/dist/{CollectionView-5OzqEVQg.js → CollectionView-DsNE9b9o.js} +5 -5
- package/dist/CollectionView-DsNE9b9o.js.map +1 -0
- package/dist/{CollectionView.legacy-CabEI40i.js → CollectionView.legacy-C0EfF6bO.js} +3 -3
- package/dist/CollectionView.legacy-C0EfF6bO.js.map +1 -0
- package/dist/{EventDetailsView-CiGPGPz5.js → EventDetailsView-CKpXTip_.js} +2 -2
- package/dist/EventDetailsView-CKpXTip_.js.map +1 -0
- package/dist/{FeaturedView.legacy-tTPbRxSf.js → FeaturedView.legacy-CKANudQ1.js} +2 -2
- package/dist/FeaturedView.legacy-CKANudQ1.js.map +1 -0
- package/dist/{GalleryCard-CBMPmdrM.js → GalleryCard-X_ux5Ig_.js} +2 -2
- package/dist/GalleryCard-X_ux5Ig_.js.map +1 -0
- package/dist/{GalleryView.legacy-4QPpX22O.js → GalleryView.legacy-DiJEEg4a.js} +3 -3
- package/dist/GalleryView.legacy-DiJEEg4a.js.map +1 -0
- package/dist/{GroupedListView.legacy-CPJRLePS.js → GroupedListView.legacy-BlY6IUF7.js} +2 -2
- package/dist/GroupedListView.legacy-BlY6IUF7.js.map +1 -0
- package/dist/OrderSummarySkeleton-dDKUH741.js.map +1 -1
- package/dist/{SeriesPage-IZWQ53xg.js → SeriesPage-AHd2GQvo.js} +5 -5
- package/dist/SeriesPage-AHd2GQvo.js.map +1 -0
- package/dist/{SeriesPage.legacy-4jSF2g-n.js → SeriesPage.legacy-DRoCeh7t.js} +2 -2
- package/dist/SeriesPage.legacy-DRoCeh7t.js.map +1 -0
- package/dist/{Success-DTSn26bg.js → Success-BH-b9NQ-.js} +5 -5
- package/dist/Success-BH-b9NQ-.js.map +1 -0
- package/dist/{Success.legacy--ABrtyII.js → Success.legacy-BuElevkX.js} +2 -2
- package/dist/Success.legacy-BuElevkX.js.map +1 -0
- package/dist/{VenueCalendar-DpZ70okH.js → VenueCalendar-D3g_j17B.js} +18 -15
- package/dist/{VenueCalendar-DpZ70okH.js.map → VenueCalendar-D3g_j17B.js.map} +1 -1
- package/dist/{ViewTicketsEmbed-CgHcn-qh.js → ViewTicketsEmbed-BUIrA7s5.js} +2 -2
- package/dist/ViewTicketsEmbed-BUIrA7s5.js.map +1 -0
- package/dist/api/api.cjs +1 -1
- package/dist/api/api.cjs.map +1 -1
- package/dist/api/api.mjs +4 -1
- package/dist/api/api.mjs.map +1 -1
- package/dist/api/types.d.ts +357 -353
- package/dist/colors-BZoMuXdh.js.map +1 -1
- package/dist/{labels-ClcIxHAF.js → labels-CJIz5Cek.js} +2 -2
- package/dist/labels-CJIz5Cek.js.map +1 -0
- package/dist/seo/seo.cjs.map +1 -1
- package/dist/seo/seo.mjs.map +1 -1
- package/dist/seo/types.d.ts +137 -137
- package/dist/{transform-DW3JR-t5.js → transform-CXAZf_M1.js} +2 -2
- package/dist/transform-CXAZf_M1.js.map +1 -0
- package/dist/types/index.d.ts +387 -387
- package/dist/venue-calendar.es.js +1 -1
- package/dist/venue-calendar.iife.js +13 -13
- package/dist/venue-calendar.iife.js.map +1 -1
- package/dist/venue-calendar.umd.js +13 -13
- package/dist/venue-calendar.umd.js.map +1 -1
- package/package.json +1 -1
- package/src/lib/theme.js +222 -222
- package/dist/AirbnbSplitMain-BVNtTJc-.js.map +0 -1
- package/dist/CarouselView.legacy-B6HtvF8d.js.map +0 -1
- package/dist/CartView-DrOhr4Qi.js.map +0 -1
- package/dist/Checkout-DB1Aocl-.js.map +0 -1
- package/dist/Checkout.legacy-DpcjjHQu.js.map +0 -1
- package/dist/CheckoutMain-D0_1xfR8.js.map +0 -1
- package/dist/CheckoutTimer-r1HaN6b6.js.map +0 -1
- package/dist/CollectionView-5OzqEVQg.js.map +0 -1
- package/dist/CollectionView.legacy-CabEI40i.js.map +0 -1
- package/dist/EventDetailsView-CiGPGPz5.js.map +0 -1
- package/dist/FeaturedView.legacy-tTPbRxSf.js.map +0 -1
- package/dist/GalleryCard-CBMPmdrM.js.map +0 -1
- package/dist/GalleryView.legacy-4QPpX22O.js.map +0 -1
- package/dist/GroupedListView.legacy-CPJRLePS.js.map +0 -1
- package/dist/SeriesPage-IZWQ53xg.js.map +0 -1
- package/dist/SeriesPage.legacy-4jSF2g-n.js.map +0 -1
- package/dist/Success-DTSn26bg.js.map +0 -1
- package/dist/Success.legacy--ABrtyII.js.map +0 -1
- package/dist/ViewTicketsEmbed-CgHcn-qh.js.map +0 -1
- package/dist/labels-ClcIxHAF.js.map +0 -1
- package/dist/transform-DW3JR-t5.js.map +0 -1
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"colors-BZoMuXdh.js","sources":["../src/lib/constants/colors.ts"],"sourcesContent":["/**\n * Centralized color constants for venue-calendar-npm.\n *\n * Single source of truth for all colors used across the calendar package.\n * This enables consistent theming and makes global color changes trivial.\n *\n * Usage:\n * import { COLORS, GRAY, BRAND } from '$lib/constants/colors';\n *\n * IMPORTANT: Never hardcode hex values in components.\n * Always import from this file.\n */\n\n/**\n * Brand colors - Micdrop identity\n */\nexport const BRAND = {\n\t/** Primary brand blue - used for primary actions, links */\n\tprimary: '#0284fe',\n\t/** Secondary brand blue (Tailwind blue-700) - calendar events, highlights */\n\tsecondary: '#1d4ed8',\n\t/** Tertiary brand blue (Tailwind blue-600) */\n\ttertiary: '#2563eb'\n} as const;\n\n/**\n * Gray scale - Tailwind gray palette\n * Maps directly to Tailwind's gray color scale\n */\nexport const GRAY = {\n\t50: '#f9fafb',\n\t100: '#f3f4f6',\n\t200: '#e5e7eb',\n\t300: '#d1d5db',\n\t400: '#9ca3af',\n\t500: '#6b7280',\n\t600: '#4b5563',\n\t700: '#374151',\n\t800: '#1f2937',\n\t900: '#111827'\n} as const;\n\n/**\n * Semantic colors - for specific UI purposes\n */\nexport const SEMANTIC = {\n\t// Text colors\n\ttext: {\n\t\tprimary: GRAY[900],\n\t\tsecondary: GRAY[500],\n\t\tmuted: GRAY[400],\n\t\tinverted: '#ffffff'\n\t},\n\t// Background colors\n\tbackground: {\n\t\tprimary: '#ffffff',\n\t\tsecondary: GRAY[50],\n\t\ttertiary: GRAY[100],\n\t\tdark: GRAY[800],\n\t\tdarker: GRAY[900]\n\t},\n\t// Border colors\n\tborder: {\n\t\tdefault: GRAY[200],\n\t\tstrong: GRAY[300],\n\t\tmuted: GRAY[100],\n\t\tlight: '#e0e0e0'\n\t},\n\t// Status colors\n\tsuccess: '#16a34a', // green-600\n\terror: '#ef4444', // red-500\n\twarning: '#f59e0b', // amber-500\n\tinfo: '#3b82f6' // blue-500\n} as const;\n\n/**\n * Accent colors - Tailwind and custom palette\n */\nexport const ACCENT = {\n\tblue: {\n\t\t200: '#bfdbfe',\n\t\t500: '#3b82f6',\n\t\t600: '#2563eb',\n\t\t700: '#1d4ed8'\n\t},\n\tred: {\n\t\t500: '#ef4444',\n\t\t600: '#dc2626'\n\t},\n\tgreen: {\n\t\t500: '#22c55e',\n\t\t600: '#16a34a',\n\t\temerald: '#10b981',\n\t\temeraldDark: '#059669'\n\t},\n\tamber: {\n\t\t500: '#f59e0b'\n\t},\n\torange: {\n\t\t500: '#f97316'\n\t},\n\tviolet: {\n\t\t500: '#8b5cf6'\n\t},\n\tcyan: {\n\t\t400: '#22d3ee',\n\t\t500: '#06b6d4',\n\t\t600: '#0891b2'\n\t},\n\tteal: {\n\t\t400: '#2dd4bf',\n\t\t500: '#14b8a6'\n\t},\n\t// Custom event/calendar colors\n\tsky: {\n\t\t400: '#45b7d1',\n\t\tlight: '#4ecdc4'\n\t},\n\tmint: {\n\t\t500: '#98d8c8',\n\t\t600: '#96ceb4'\n\t},\n\tplum: '#dda0dd',\n\t// Warm palette colors\n\tyellow: {\n\t\t500: '#f7dc6f',\n\t\t300: '#ffeaa7'\n\t},\n\tcoral: '#ff6b6b'\n} as const;\n\n/**\n * Google Pay brand colors\n * Used in payment section\n */\nexport const GOOGLE_PAY = {\n\tblue: '#4285f4',\n\tgreen: '#34a853',\n\tyellow: '#fbbc05',\n\tred: '#ea4335'\n} as const;\n\n/**\n * Dark mode colors\n * Used for calendar container and other dark sections\n */\nexport const DARK = {\n\tnavy: {\n\t\t900: '#0d1526',\n\t\t800: '#1a202c',\n\t\t700: '#2d3748',\n\t\t600: '#252b37'\n\t}\n} as const;\n\n/**\n * Combined colors object for easy access\n */\nexport const COLORS = {\n\tbrand: BRAND,\n\tgray: GRAY,\n\tsemantic: SEMANTIC,\n\taccent: ACCENT,\n\tgooglePay: GOOGLE_PAY,\n\tdark: DARK\n} as const;\n\n/**\n * Hex to Tailwind class mappings for common colors.\n * Used by migration scripts and for documentation.\n */\nexport const HEX_TO_TAILWIND: Record<string, string> = {\n\t// Gray scale\n\t'#f9fafb': 'gray-50',\n\t'#F9FAFB': 'gray-50',\n\t'#f3f4f6': 'gray-100',\n\t'#e5e7eb': 'gray-200',\n\t'#d1d5db': 'gray-300',\n\t'#9ca3af': 'gray-400',\n\t'#9CA3AF': 'gray-400',\n\t'#6b7280': 'gray-500',\n\t'#6B7280': 'gray-500',\n\t'#4b5563': 'gray-600',\n\t'#374151': 'gray-700',\n\t'#1f2937': 'gray-800',\n\t'#111827': 'gray-900',\n\n\t// Blues\n\t'#bfdbfe': 'blue-200',\n\t'#BFDBFE': 'blue-200',\n\t'#3b82f6': 'blue-500',\n\t'#3B82F6': 'blue-500',\n\t'#2563eb': 'blue-600',\n\t'#1d4ed8': 'blue-700',\n\t'#0284fe': 'brand-primary',\n\n\t// Reds\n\t'#ef4444': 'red-500',\n\t'#dc2626': 'red-600',\n\n\t// Greens\n\t'#22c55e': 'green-500',\n\t'#16a34a': 'green-600',\n\t'#10b981': 'emerald-500',\n\t'#10B981': 'emerald-500',\n\t'#059669': 'emerald-600',\n\n\t// Ambers/Oranges\n\t'#f59e0b': 'amber-500',\n\t'#F59E0B': 'amber-500',\n\t'#f97316': 'orange-500',\n\t'#F97316': 'orange-500',\n\n\t// Violets/Purples\n\t'#8b5cf6': 'violet-500',\n\t'#8B5CF6': 'violet-500',\n\n\t// Cyans\n\t'#22d3ee': 'cyan-400',\n\t'#06b6d4': 'cyan-500',\n\t'#06B6D4': 'cyan-500',\n\t'#0891b2': 'cyan-600',\n\n\t// Teals\n\t'#2dd4bf': 'teal-400',\n\t'#14b8a6': 'teal-500',\n\n\t// Custom colors\n\t'#45b7d1': 'sky-400',\n\t'#45B7D1': 'sky-400',\n\t'#4ecdc4': 'sky-light',\n\t'#4ECDC4': 'sky-light',\n\t'#98d8c8': 'mint-500',\n\t'#98D8C8': 'mint-500',\n\t'#96ceb4': 'mint-600',\n\t'#96CEB4': 'mint-600',\n\t'#dda0dd': 'plum',\n\t'#DDA0DD': 'plum',\n\n\t// Warm palette\n\t'#f7dc6f': 'yellow-500',\n\t'#F7DC6F': 'yellow-500',\n\t'#ffeaa7': 'yellow-300',\n\t'#FFEAA7': 'yellow-300',\n\t'#ff6b6b': 'coral',\n\t'#FF6B6B': 'coral',\n\n\t// Dark navy\n\t'#0d1526': 'navy-900',\n\t'#0D1526': 'navy-900',\n\t'#1a202c': 'navy-800',\n\t'#1A202C': 'navy-800',\n\t'#2d3748': 'navy-700',\n\t'#2D3748': 'navy-700',\n\t'#252b37': 'navy-600',\n\t'#252B37': 'navy-600',\n\n\t// Google Pay colors\n\t'#4285f4': 'google-blue',\n\t'#4285F4': 'google-blue',\n\t'#34a853': 'google-green',\n\t'#34A853': 'google-green',\n\t'#fbbc05': 'google-yellow',\n\t'#FBBC05': 'google-yellow',\n\t'#ea4335': 'google-red',\n\t'#EA4335': 'google-red',\n\n\t// Other\n\t'#e0e0e0': 'border-light',\n\t'#ffffff': 'white',\n\t'#FFFFFF': 'white',\n\t'#fff': 'white',\n\t'#000000': 'black'\n};\n\n/**\n * CSS custom property names for use in style blocks.\n * These map to the theme-config.js CSS variables.\n */\nexport const CSS_VARS = {\n\t// Calendar\n\tcalDayHeader: '--cal-day-header',\n\tcalDayHeaderBG: '--cal-day-header-bg',\n\tcalCellBG: '--cal-cell-bg',\n\tcalCellBorder: '--cal-cell-border',\n\tcalDateText: '--cal-date-text',\n\tcalEventDot: '--cal-event-dot',\n\tcalNavText: '--cal-nav-text',\n\tcalNavBG: '--cal-nav-bg',\n\n\t// Event Cards\n\tecCardBG: '--ec-card-bg',\n\tecCardBorder: '--ec-card-border',\n\tecTitle: '--ec-title',\n\tecDate: '--ec-date',\n\tecPrice: '--ec-price',\n\tecImageBG: '--ec-image-bg',\n\n\t// Event Page\n\tepTitle: '--ep-title',\n\tepSectionHeader: '--ep-section-header',\n\tepBodyText: '--ep-body-text',\n\tepVenue: '--ep-venue',\n\tepLink: '--ep-link',\n\tepCardBG: '--ep-card-bg',\n\tepCardBorder: '--ep-card-border',\n\tepSidebarBG: '--ep-sidebar-bg',\n\tepSidebarBorder: '--ep-sidebar-border',\n\tepSidebarLabel: '--ep-sidebar-label',\n\tepSidebarPrice: '--ep-sidebar-price',\n\tepCtaBG: '--ep-cta-bg',\n\tepCtaText: '--ep-cta-text',\n\tepTrust: '--ep-trust',\n\n\t// Ticket Selection\n\ttsHeaderBG: '--ts-header-bg',\n\ttsHeaderBorder: '--ts-header-border',\n\ttsHeaderTitle: '--ts-header-title',\n\ttsHeaderText: '--ts-header-text',\n\ttsCardBG: '--ts-card-bg',\n\ttsCardBorder: '--ts-card-border',\n\ttsCardBorderHover: '--ts-card-border-hover',\n\ttsTicketName: '--ts-ticket-name',\n\ttsTicketDesc: '--ts-ticket-desc',\n\ttsTicketPrice: '--ts-ticket-price',\n\ttsTicketFee: '--ts-ticket-fee',\n\ttsScarcity: '--ts-scarcity',\n\ttsQtyInactive: '--ts-qty-inactive',\n\ttsQtyActive: '--ts-qty-active',\n\ttsQtyValue: '--ts-qty-value',\n\ttsQtyBorder: '--ts-qty-border',\n\ttsSummaryBG: '--ts-summary-bg',\n\ttsSummaryBorder: '--ts-summary-border',\n\ttsSummaryTitle: '--ts-summary-title',\n\ttsSummaryRow: '--ts-summary-row',\n\ttsSummaryTotal: '--ts-summary-total',\n\ttsCheckoutBG: '--ts-checkout-bg',\n\ttsCheckoutText: '--ts-checkout-text',\n\n\t// Checkout\n\tcoHeaderBG: '--co-header-bg',\n\tcoHeaderBorder: '--co-header-border',\n\tcoTitle: '--co-title',\n\tcoTimer: '--co-timer',\n\tcoClose: '--co-close',\n\tcoCardBG: '--co-card-bg',\n\tcoCardBorder: '--co-card-border',\n\tcoSectionTitle: '--co-section-title',\n\tcoFormLabel: '--co-form-label',\n\tcoInputBG: '--co-input-bg',\n\tcoInputBorder: '--co-input-border',\n\tcoInputFocus: '--co-input-focus',\n\tcoInputText: '--co-input-text',\n\tcoPlaceholder: '--co-placeholder',\n\tcoCheckboxBorder: '--co-checkbox-border',\n\tcoRadioActive: '--co-radio-active',\n\tcoSecondaryText: '--co-secondary-text',\n\tcoError: '--co-error',\n\tcoSuccess: '--co-success',\n\tcoOrderBG: '--co-order-bg',\n\tcoOrderText: '--co-order-text',\n\tcoTrust: '--co-trust',\n\n\t// Status Badges\n\tstatusOnSale: '--status-on-sale',\n\tstatusSellingFast: '--status-selling-fast',\n\tstatusSoldOut: '--status-sold-out'\n} as const;\n\n/**\n * Get Tailwind color class from hex value.\n * Useful for debugging and migration.\n */\nexport function getTailwindFromHex(hex: string): string | undefined {\n\treturn HEX_TO_TAILWIND[hex.toLowerCase()] || HEX_TO_TAILWIND[hex.toUpperCase()];\n}\n\n/**\n * Type guard to check if a string is a valid hex color.\n */\nexport function isHexColor(value: string): boolean {\n\treturn /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value);\n}\n\n/**\n * Convert hex color to HSL string (without hsl() wrapper)\n * @param hex - Hex color string (e.g., \"#2563EB\")\n * @returns HSL values as string (e.g., \"217 91% 60%\")\n */\nexport function hexToHSL(hex: string): string | null {\n\tif (!hex || typeof hex !== 'string') return null;\n\n\thex = hex.replace('#', '');\n\tif (hex.length !== 6 && hex.length !== 3) return null;\n\n\t// Expand 3-digit hex to 6-digit\n\tif (hex.length === 3) {\n\t\thex = hex\n\t\t\t.split('')\n\t\t\t.map((char) => char + char)\n\t\t\t.join('');\n\t}\n\n\tconst r = parseInt(hex.substr(0, 2), 16) / 255;\n\tconst g = parseInt(hex.substr(2, 2), 16) / 255;\n\tconst b = parseInt(hex.substr(4, 2), 16) / 255;\n\n\tconst max = Math.max(r, g, b);\n\tconst min = Math.min(r, g, b);\n\tlet h,\n\t\ts,\n\t\tl = (max + min) / 2;\n\n\tif (max === min) {\n\t\th = s = 0;\n\t} else {\n\t\tconst d = max - min;\n\t\ts = l > 0.5 ? d / (2 - max - min) : d / (max + min);\n\t\tswitch (max) {\n\t\t\tcase r:\n\t\t\t\th = ((g - b) / d + (g < b ? 6 : 0)) / 6;\n\t\t\t\tbreak;\n\t\t\tcase g:\n\t\t\t\th = ((b - r) / d + 2) / 6;\n\t\t\t\tbreak;\n\t\t\tcase b:\n\t\t\t\th = ((r - g) / d + 4) / 6;\n\t\t\t\tbreak;\n\t\t}\n\t}\n\n\treturn `${Math.round(h! * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;\n}\n\n/**\n * Convert HSL string to hex color\n * @param hsl - HSL values as string (e.g., \"217 91% 60%\")\n * @returns Hex color string (e.g., \"#2563EB\")\n */\nexport function hslToHex(hsl: string): string | null {\n\tif (!hsl || typeof hsl !== 'string') return null;\n\n\tconst parts = hsl.match(/(\\d+)\\s+(\\d+)%\\s+(\\d+)%/);\n\tif (!parts) return null;\n\n\tconst h = parseInt(parts[1]) / 360;\n\tconst s = parseInt(parts[2]) / 100;\n\tconst l = parseInt(parts[3]) / 100;\n\n\tlet r, g, b;\n\n\tif (s === 0) {\n\t\tr = g = b = l;\n\t} else {\n\t\tconst hue2rgb = (p: number, q: number, t: number) => {\n\t\t\tif (t < 0) t += 1;\n\t\t\tif (t > 1) t -= 1;\n\t\t\tif (t < 1 / 6) return p + (q - p) * 6 * t;\n\t\t\tif (t < 1 / 2) return q;\n\t\t\tif (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\n\t\t\treturn p;\n\t\t};\n\n\t\tconst q = l < 0.5 ? l * (1 + s) : l + s - l * s;\n\t\tconst p = 2 * l - q;\n\t\tr = hue2rgb(p, q, h + 1 / 3);\n\t\tg = hue2rgb(p, q, h);\n\t\tb = hue2rgb(p, q, h - 1 / 3);\n\t}\n\n\tconst toHex = (x: number) => {\n\t\tconst hex = Math.round(x * 255).toString(16);\n\t\treturn hex.length === 1 ? '0' + hex : hex;\n\t};\n\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\n}\n"],"names":["ACCENT","GOOGLE_PAY"],"mappings":"AA8EO,MAAMA,IAAS;AAAA,EACrB,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,KAAK;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,OAAO;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AAAA,IACL,SAAS;AAAA,IACT,aAAa;AAAA,EAAA;AAAA,EAEd,OAAO;AAAA,IACN,KAAK;AAAA,EAAA;AAAA,EAEN,QAAQ;AAAA,IACP,KAAK;AAAA,EAAA;AAAA,EAEN,QAAQ;AAAA,IACP,KAAK;AAAA,EAAA;AAAA,EAEN,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA;AAAA,EAGN,KAAK;AAAA,IACJ,KAAK;AAAA,IACL,OAAO;AAAA,EAAA;AAAA,EAER,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,MAAM;AAAA;AAAA,EAEN,QAAQ;AAAA,IACP,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,OAAO;AACR,GAMaC,IAAa;AAAA,EACzB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AACN;"}
|
|
1
|
+
{"version":3,"file":"colors-BZoMuXdh.js","sources":["../src/lib/constants/colors.ts"],"sourcesContent":["/**\r\n * Centralized color constants for venue-calendar-npm.\r\n *\r\n * Single source of truth for all colors used across the calendar package.\r\n * This enables consistent theming and makes global color changes trivial.\r\n *\r\n * Usage:\r\n * import { COLORS, GRAY, BRAND } from '$lib/constants/colors';\r\n *\r\n * IMPORTANT: Never hardcode hex values in components.\r\n * Always import from this file.\r\n */\r\n\r\n/**\r\n * Brand colors - Micdrop identity\r\n */\r\nexport const BRAND = {\r\n\t/** Primary brand blue - used for primary actions, links */\r\n\tprimary: '#0284fe',\r\n\t/** Secondary brand blue (Tailwind blue-700) - calendar events, highlights */\r\n\tsecondary: '#1d4ed8',\r\n\t/** Tertiary brand blue (Tailwind blue-600) */\r\n\ttertiary: '#2563eb'\r\n} as const;\r\n\r\n/**\r\n * Gray scale - Tailwind gray palette\r\n * Maps directly to Tailwind's gray color scale\r\n */\r\nexport const GRAY = {\r\n\t50: '#f9fafb',\r\n\t100: '#f3f4f6',\r\n\t200: '#e5e7eb',\r\n\t300: '#d1d5db',\r\n\t400: '#9ca3af',\r\n\t500: '#6b7280',\r\n\t600: '#4b5563',\r\n\t700: '#374151',\r\n\t800: '#1f2937',\r\n\t900: '#111827'\r\n} as const;\r\n\r\n/**\r\n * Semantic colors - for specific UI purposes\r\n */\r\nexport const SEMANTIC = {\r\n\t// Text colors\r\n\ttext: {\r\n\t\tprimary: GRAY[900],\r\n\t\tsecondary: GRAY[500],\r\n\t\tmuted: GRAY[400],\r\n\t\tinverted: '#ffffff'\r\n\t},\r\n\t// Background colors\r\n\tbackground: {\r\n\t\tprimary: '#ffffff',\r\n\t\tsecondary: GRAY[50],\r\n\t\ttertiary: GRAY[100],\r\n\t\tdark: GRAY[800],\r\n\t\tdarker: GRAY[900]\r\n\t},\r\n\t// Border colors\r\n\tborder: {\r\n\t\tdefault: GRAY[200],\r\n\t\tstrong: GRAY[300],\r\n\t\tmuted: GRAY[100],\r\n\t\tlight: '#e0e0e0'\r\n\t},\r\n\t// Status colors\r\n\tsuccess: '#16a34a', // green-600\r\n\terror: '#ef4444', // red-500\r\n\twarning: '#f59e0b', // amber-500\r\n\tinfo: '#3b82f6' // blue-500\r\n} as const;\r\n\r\n/**\r\n * Accent colors - Tailwind and custom palette\r\n */\r\nexport const ACCENT = {\r\n\tblue: {\r\n\t\t200: '#bfdbfe',\r\n\t\t500: '#3b82f6',\r\n\t\t600: '#2563eb',\r\n\t\t700: '#1d4ed8'\r\n\t},\r\n\tred: {\r\n\t\t500: '#ef4444',\r\n\t\t600: '#dc2626'\r\n\t},\r\n\tgreen: {\r\n\t\t500: '#22c55e',\r\n\t\t600: '#16a34a',\r\n\t\temerald: '#10b981',\r\n\t\temeraldDark: '#059669'\r\n\t},\r\n\tamber: {\r\n\t\t500: '#f59e0b'\r\n\t},\r\n\torange: {\r\n\t\t500: '#f97316'\r\n\t},\r\n\tviolet: {\r\n\t\t500: '#8b5cf6'\r\n\t},\r\n\tcyan: {\r\n\t\t400: '#22d3ee',\r\n\t\t500: '#06b6d4',\r\n\t\t600: '#0891b2'\r\n\t},\r\n\tteal: {\r\n\t\t400: '#2dd4bf',\r\n\t\t500: '#14b8a6'\r\n\t},\r\n\t// Custom event/calendar colors\r\n\tsky: {\r\n\t\t400: '#45b7d1',\r\n\t\tlight: '#4ecdc4'\r\n\t},\r\n\tmint: {\r\n\t\t500: '#98d8c8',\r\n\t\t600: '#96ceb4'\r\n\t},\r\n\tplum: '#dda0dd',\r\n\t// Warm palette colors\r\n\tyellow: {\r\n\t\t500: '#f7dc6f',\r\n\t\t300: '#ffeaa7'\r\n\t},\r\n\tcoral: '#ff6b6b'\r\n} as const;\r\n\r\n/**\r\n * Google Pay brand colors\r\n * Used in payment section\r\n */\r\nexport const GOOGLE_PAY = {\r\n\tblue: '#4285f4',\r\n\tgreen: '#34a853',\r\n\tyellow: '#fbbc05',\r\n\tred: '#ea4335'\r\n} as const;\r\n\r\n/**\r\n * Dark mode colors\r\n * Used for calendar container and other dark sections\r\n */\r\nexport const DARK = {\r\n\tnavy: {\r\n\t\t900: '#0d1526',\r\n\t\t800: '#1a202c',\r\n\t\t700: '#2d3748',\r\n\t\t600: '#252b37'\r\n\t}\r\n} as const;\r\n\r\n/**\r\n * Combined colors object for easy access\r\n */\r\nexport const COLORS = {\r\n\tbrand: BRAND,\r\n\tgray: GRAY,\r\n\tsemantic: SEMANTIC,\r\n\taccent: ACCENT,\r\n\tgooglePay: GOOGLE_PAY,\r\n\tdark: DARK\r\n} as const;\r\n\r\n/**\r\n * Hex to Tailwind class mappings for common colors.\r\n * Used by migration scripts and for documentation.\r\n */\r\nexport const HEX_TO_TAILWIND: Record<string, string> = {\r\n\t// Gray scale\r\n\t'#f9fafb': 'gray-50',\r\n\t'#F9FAFB': 'gray-50',\r\n\t'#f3f4f6': 'gray-100',\r\n\t'#e5e7eb': 'gray-200',\r\n\t'#d1d5db': 'gray-300',\r\n\t'#9ca3af': 'gray-400',\r\n\t'#9CA3AF': 'gray-400',\r\n\t'#6b7280': 'gray-500',\r\n\t'#6B7280': 'gray-500',\r\n\t'#4b5563': 'gray-600',\r\n\t'#374151': 'gray-700',\r\n\t'#1f2937': 'gray-800',\r\n\t'#111827': 'gray-900',\r\n\r\n\t// Blues\r\n\t'#bfdbfe': 'blue-200',\r\n\t'#BFDBFE': 'blue-200',\r\n\t'#3b82f6': 'blue-500',\r\n\t'#3B82F6': 'blue-500',\r\n\t'#2563eb': 'blue-600',\r\n\t'#1d4ed8': 'blue-700',\r\n\t'#0284fe': 'brand-primary',\r\n\r\n\t// Reds\r\n\t'#ef4444': 'red-500',\r\n\t'#dc2626': 'red-600',\r\n\r\n\t// Greens\r\n\t'#22c55e': 'green-500',\r\n\t'#16a34a': 'green-600',\r\n\t'#10b981': 'emerald-500',\r\n\t'#10B981': 'emerald-500',\r\n\t'#059669': 'emerald-600',\r\n\r\n\t// Ambers/Oranges\r\n\t'#f59e0b': 'amber-500',\r\n\t'#F59E0B': 'amber-500',\r\n\t'#f97316': 'orange-500',\r\n\t'#F97316': 'orange-500',\r\n\r\n\t// Violets/Purples\r\n\t'#8b5cf6': 'violet-500',\r\n\t'#8B5CF6': 'violet-500',\r\n\r\n\t// Cyans\r\n\t'#22d3ee': 'cyan-400',\r\n\t'#06b6d4': 'cyan-500',\r\n\t'#06B6D4': 'cyan-500',\r\n\t'#0891b2': 'cyan-600',\r\n\r\n\t// Teals\r\n\t'#2dd4bf': 'teal-400',\r\n\t'#14b8a6': 'teal-500',\r\n\r\n\t// Custom colors\r\n\t'#45b7d1': 'sky-400',\r\n\t'#45B7D1': 'sky-400',\r\n\t'#4ecdc4': 'sky-light',\r\n\t'#4ECDC4': 'sky-light',\r\n\t'#98d8c8': 'mint-500',\r\n\t'#98D8C8': 'mint-500',\r\n\t'#96ceb4': 'mint-600',\r\n\t'#96CEB4': 'mint-600',\r\n\t'#dda0dd': 'plum',\r\n\t'#DDA0DD': 'plum',\r\n\r\n\t// Warm palette\r\n\t'#f7dc6f': 'yellow-500',\r\n\t'#F7DC6F': 'yellow-500',\r\n\t'#ffeaa7': 'yellow-300',\r\n\t'#FFEAA7': 'yellow-300',\r\n\t'#ff6b6b': 'coral',\r\n\t'#FF6B6B': 'coral',\r\n\r\n\t// Dark navy\r\n\t'#0d1526': 'navy-900',\r\n\t'#0D1526': 'navy-900',\r\n\t'#1a202c': 'navy-800',\r\n\t'#1A202C': 'navy-800',\r\n\t'#2d3748': 'navy-700',\r\n\t'#2D3748': 'navy-700',\r\n\t'#252b37': 'navy-600',\r\n\t'#252B37': 'navy-600',\r\n\r\n\t// Google Pay colors\r\n\t'#4285f4': 'google-blue',\r\n\t'#4285F4': 'google-blue',\r\n\t'#34a853': 'google-green',\r\n\t'#34A853': 'google-green',\r\n\t'#fbbc05': 'google-yellow',\r\n\t'#FBBC05': 'google-yellow',\r\n\t'#ea4335': 'google-red',\r\n\t'#EA4335': 'google-red',\r\n\r\n\t// Other\r\n\t'#e0e0e0': 'border-light',\r\n\t'#ffffff': 'white',\r\n\t'#FFFFFF': 'white',\r\n\t'#fff': 'white',\r\n\t'#000000': 'black'\r\n};\r\n\r\n/**\r\n * CSS custom property names for use in style blocks.\r\n * These map to the theme-config.js CSS variables.\r\n */\r\nexport const CSS_VARS = {\r\n\t// Calendar\r\n\tcalDayHeader: '--cal-day-header',\r\n\tcalDayHeaderBG: '--cal-day-header-bg',\r\n\tcalCellBG: '--cal-cell-bg',\r\n\tcalCellBorder: '--cal-cell-border',\r\n\tcalDateText: '--cal-date-text',\r\n\tcalEventDot: '--cal-event-dot',\r\n\tcalNavText: '--cal-nav-text',\r\n\tcalNavBG: '--cal-nav-bg',\r\n\r\n\t// Event Cards\r\n\tecCardBG: '--ec-card-bg',\r\n\tecCardBorder: '--ec-card-border',\r\n\tecTitle: '--ec-title',\r\n\tecDate: '--ec-date',\r\n\tecPrice: '--ec-price',\r\n\tecImageBG: '--ec-image-bg',\r\n\r\n\t// Event Page\r\n\tepTitle: '--ep-title',\r\n\tepSectionHeader: '--ep-section-header',\r\n\tepBodyText: '--ep-body-text',\r\n\tepVenue: '--ep-venue',\r\n\tepLink: '--ep-link',\r\n\tepCardBG: '--ep-card-bg',\r\n\tepCardBorder: '--ep-card-border',\r\n\tepSidebarBG: '--ep-sidebar-bg',\r\n\tepSidebarBorder: '--ep-sidebar-border',\r\n\tepSidebarLabel: '--ep-sidebar-label',\r\n\tepSidebarPrice: '--ep-sidebar-price',\r\n\tepCtaBG: '--ep-cta-bg',\r\n\tepCtaText: '--ep-cta-text',\r\n\tepTrust: '--ep-trust',\r\n\r\n\t// Ticket Selection\r\n\ttsHeaderBG: '--ts-header-bg',\r\n\ttsHeaderBorder: '--ts-header-border',\r\n\ttsHeaderTitle: '--ts-header-title',\r\n\ttsHeaderText: '--ts-header-text',\r\n\ttsCardBG: '--ts-card-bg',\r\n\ttsCardBorder: '--ts-card-border',\r\n\ttsCardBorderHover: '--ts-card-border-hover',\r\n\ttsTicketName: '--ts-ticket-name',\r\n\ttsTicketDesc: '--ts-ticket-desc',\r\n\ttsTicketPrice: '--ts-ticket-price',\r\n\ttsTicketFee: '--ts-ticket-fee',\r\n\ttsScarcity: '--ts-scarcity',\r\n\ttsQtyInactive: '--ts-qty-inactive',\r\n\ttsQtyActive: '--ts-qty-active',\r\n\ttsQtyValue: '--ts-qty-value',\r\n\ttsQtyBorder: '--ts-qty-border',\r\n\ttsSummaryBG: '--ts-summary-bg',\r\n\ttsSummaryBorder: '--ts-summary-border',\r\n\ttsSummaryTitle: '--ts-summary-title',\r\n\ttsSummaryRow: '--ts-summary-row',\r\n\ttsSummaryTotal: '--ts-summary-total',\r\n\ttsCheckoutBG: '--ts-checkout-bg',\r\n\ttsCheckoutText: '--ts-checkout-text',\r\n\r\n\t// Checkout\r\n\tcoHeaderBG: '--co-header-bg',\r\n\tcoHeaderBorder: '--co-header-border',\r\n\tcoTitle: '--co-title',\r\n\tcoTimer: '--co-timer',\r\n\tcoClose: '--co-close',\r\n\tcoCardBG: '--co-card-bg',\r\n\tcoCardBorder: '--co-card-border',\r\n\tcoSectionTitle: '--co-section-title',\r\n\tcoFormLabel: '--co-form-label',\r\n\tcoInputBG: '--co-input-bg',\r\n\tcoInputBorder: '--co-input-border',\r\n\tcoInputFocus: '--co-input-focus',\r\n\tcoInputText: '--co-input-text',\r\n\tcoPlaceholder: '--co-placeholder',\r\n\tcoCheckboxBorder: '--co-checkbox-border',\r\n\tcoRadioActive: '--co-radio-active',\r\n\tcoSecondaryText: '--co-secondary-text',\r\n\tcoError: '--co-error',\r\n\tcoSuccess: '--co-success',\r\n\tcoOrderBG: '--co-order-bg',\r\n\tcoOrderText: '--co-order-text',\r\n\tcoTrust: '--co-trust',\r\n\r\n\t// Status Badges\r\n\tstatusOnSale: '--status-on-sale',\r\n\tstatusSellingFast: '--status-selling-fast',\r\n\tstatusSoldOut: '--status-sold-out'\r\n} as const;\r\n\r\n/**\r\n * Get Tailwind color class from hex value.\r\n * Useful for debugging and migration.\r\n */\r\nexport function getTailwindFromHex(hex: string): string | undefined {\r\n\treturn HEX_TO_TAILWIND[hex.toLowerCase()] || HEX_TO_TAILWIND[hex.toUpperCase()];\r\n}\r\n\r\n/**\r\n * Type guard to check if a string is a valid hex color.\r\n */\r\nexport function isHexColor(value: string): boolean {\r\n\treturn /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/.test(value);\r\n}\r\n\r\n/**\r\n * Convert hex color to HSL string (without hsl() wrapper)\r\n * @param hex - Hex color string (e.g., \"#2563EB\")\r\n * @returns HSL values as string (e.g., \"217 91% 60%\")\r\n */\r\nexport function hexToHSL(hex: string): string | null {\r\n\tif (!hex || typeof hex !== 'string') return null;\r\n\r\n\thex = hex.replace('#', '');\r\n\tif (hex.length !== 6 && hex.length !== 3) return null;\r\n\r\n\t// Expand 3-digit hex to 6-digit\r\n\tif (hex.length === 3) {\r\n\t\thex = hex\r\n\t\t\t.split('')\r\n\t\t\t.map((char) => char + char)\r\n\t\t\t.join('');\r\n\t}\r\n\r\n\tconst r = parseInt(hex.substr(0, 2), 16) / 255;\r\n\tconst g = parseInt(hex.substr(2, 2), 16) / 255;\r\n\tconst b = parseInt(hex.substr(4, 2), 16) / 255;\r\n\r\n\tconst max = Math.max(r, g, b);\r\n\tconst min = Math.min(r, g, b);\r\n\tlet h,\r\n\t\ts,\r\n\t\tl = (max + min) / 2;\r\n\r\n\tif (max === min) {\r\n\t\th = s = 0;\r\n\t} else {\r\n\t\tconst d = max - min;\r\n\t\ts = l > 0.5 ? d / (2 - max - min) : d / (max + min);\r\n\t\tswitch (max) {\r\n\t\t\tcase r:\r\n\t\t\t\th = ((g - b) / d + (g < b ? 6 : 0)) / 6;\r\n\t\t\t\tbreak;\r\n\t\t\tcase g:\r\n\t\t\t\th = ((b - r) / d + 2) / 6;\r\n\t\t\t\tbreak;\r\n\t\t\tcase b:\r\n\t\t\t\th = ((r - g) / d + 4) / 6;\r\n\t\t\t\tbreak;\r\n\t\t}\r\n\t}\r\n\r\n\treturn `${Math.round(h! * 360)} ${Math.round(s * 100)}% ${Math.round(l * 100)}%`;\r\n}\r\n\r\n/**\r\n * Convert HSL string to hex color\r\n * @param hsl - HSL values as string (e.g., \"217 91% 60%\")\r\n * @returns Hex color string (e.g., \"#2563EB\")\r\n */\r\nexport function hslToHex(hsl: string): string | null {\r\n\tif (!hsl || typeof hsl !== 'string') return null;\r\n\r\n\tconst parts = hsl.match(/(\\d+)\\s+(\\d+)%\\s+(\\d+)%/);\r\n\tif (!parts) return null;\r\n\r\n\tconst h = parseInt(parts[1]) / 360;\r\n\tconst s = parseInt(parts[2]) / 100;\r\n\tconst l = parseInt(parts[3]) / 100;\r\n\r\n\tlet r, g, b;\r\n\r\n\tif (s === 0) {\r\n\t\tr = g = b = l;\r\n\t} else {\r\n\t\tconst hue2rgb = (p: number, q: number, t: number) => {\r\n\t\t\tif (t < 0) t += 1;\r\n\t\t\tif (t > 1) t -= 1;\r\n\t\t\tif (t < 1 / 6) return p + (q - p) * 6 * t;\r\n\t\t\tif (t < 1 / 2) return q;\r\n\t\t\tif (t < 2 / 3) return p + (q - p) * (2 / 3 - t) * 6;\r\n\t\t\treturn p;\r\n\t\t};\r\n\r\n\t\tconst q = l < 0.5 ? l * (1 + s) : l + s - l * s;\r\n\t\tconst p = 2 * l - q;\r\n\t\tr = hue2rgb(p, q, h + 1 / 3);\r\n\t\tg = hue2rgb(p, q, h);\r\n\t\tb = hue2rgb(p, q, h - 1 / 3);\r\n\t}\r\n\r\n\tconst toHex = (x: number) => {\r\n\t\tconst hex = Math.round(x * 255).toString(16);\r\n\t\treturn hex.length === 1 ? '0' + hex : hex;\r\n\t};\r\n\r\n\treturn `#${toHex(r)}${toHex(g)}${toHex(b)}`.toUpperCase();\r\n}\r\n"],"names":["ACCENT","GOOGLE_PAY"],"mappings":"AA8EO,MAAMA,IAAS;AAAA,EACrB,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,KAAK;AAAA,IACJ,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,OAAO;AAAA,IACN,KAAK;AAAA,IACL,KAAK;AAAA,IACL,SAAS;AAAA,IACT,aAAa;AAAA,EAAA;AAAA,EAEd,OAAO;AAAA,IACN,KAAK;AAAA,EAAA;AAAA,EAEN,QAAQ;AAAA,IACP,KAAK;AAAA,EAAA;AAAA,EAEN,QAAQ;AAAA,IACP,KAAK;AAAA,EAAA;AAAA,EAEN,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA;AAAA,EAGN,KAAK;AAAA,IACJ,KAAK;AAAA,IACL,OAAO;AAAA,EAAA;AAAA,EAER,MAAM;AAAA,IACL,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,MAAM;AAAA;AAAA,EAEN,QAAQ;AAAA,IACP,KAAK;AAAA,IACL,KAAK;AAAA,EAAA;AAAA,EAEN,OAAO;AACR,GAMaC,IAAa;AAAA,EACzB,MAAM;AAAA,EACN,OAAO;AAAA,EACP,QAAQ;AAAA,EACR,KAAK;AACN;"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { af as C, u as O, v as $, f as _ } from "./VenueCalendar-
|
|
1
|
+
import { af as C, u as O, v as $, f as _ } from "./VenueCalendar-D3g_j17B.js";
|
|
2
2
|
import "svelte/internal/disclose-version";
|
|
3
3
|
import * as e from "svelte/internal/client";
|
|
4
4
|
import "svelte/internal/flags/legacy";
|
|
@@ -1348,4 +1348,4 @@ export {
|
|
|
1348
1348
|
ee as u,
|
|
1349
1349
|
Q as v
|
|
1350
1350
|
};
|
|
1351
|
-
//# sourceMappingURL=labels-
|
|
1351
|
+
//# sourceMappingURL=labels-CJIz5Cek.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"labels-CJIz5Cek.js","sources":["../src/lib/api/cta.ts","../src/lib/premium-ticket-experience/mock-data.ts","../src/lib/premium-ticket-experience/defaults.ts","../src/lib/premium-ticket-experience/variants/v3-airbnb-split/OrderSummary.svelte","../src/lib/premium-ticket-experience/i18n/labels.js"],"sourcesContent":["/**\n * CTA state computation.\n *\n * Pure function (no I/O) — derives the buy-button state from an event and\n * its ticket list. Lives in `api/` because it is the canonical, single\n * source of truth that replaces the duplicate implementations previously\n * scattered across utils/api.js, core/api-client.js, and a few components.\n */\n\n// Import from the specific subpath (not the bare entry) so the api/\n// bundle doesn't pull in the entire SC component graph (Accordion etc.)\n// and break `npm run build:api`.\nimport { formatDateRaw } from '@getmicdrop/svelte-components/utils/formatters';\n\nexport interface CtaStateOptions {\n cancelled?: boolean;\n isRegistration?: boolean;\n waitlistEnabled?: boolean;\n}\n\nexport interface CtaState {\n text: string;\n disabled: boolean;\n reason:\n | 'cancelled'\n | 'event_past'\n | 'no_tickets'\n | 'available'\n | 'coming_soon'\n | 'sold_out_waitlist'\n | 'sold_out'\n | 'sales_ended';\n}\n\ninterface CtaEvent {\n startDateTime?: string | null;\n endDateTime?: string | null;\n}\n\ninterface CtaTicket {\n salesChannel?: number;\n salesBegin?: string | null;\n salesStart?: string | null;\n saleBegin?: string | null;\n onSaleStart?: string | null;\n salesEnd?: string | null;\n saleEnd?: string | null;\n onSaleEnd?: string | null;\n remainingCapacity?: number | null;\n quantityRemaining?: number | null;\n quantity?: number | null;\n soldOut?: boolean;\n}\n\nexport function computeCtaState(\n event: CtaEvent,\n tickets: CtaTicket[] | undefined,\n options: CtaStateOptions = {}\n): CtaState {\n const {\n cancelled = false,\n isRegistration = false,\n waitlistEnabled = false,\n } = options;\n const now = new Date();\n\n if (cancelled)\n return { text: 'Cancelled', disabled: true, reason: 'cancelled' };\n\n const eventEnd = event.endDateTime || event.startDateTime;\n if (eventEnd && new Date(eventEnd) < now) {\n return { text: 'Sales ended', disabled: true, reason: 'event_past' };\n }\n\n const publicTickets = (tickets || []).filter(t => t.salesChannel !== 2);\n if (publicTickets.length === 0) {\n return {\n text: 'No tickets available',\n disabled: true,\n reason: 'no_tickets',\n };\n }\n\n let hasPurchasable = false;\n let allSoldOut = true;\n let allComingSoon = true;\n let earliestSalesStart: Date | null = null;\n\n for (const ticket of publicTickets) {\n const salesBegin =\n ticket.salesBegin ||\n ticket.salesStart ||\n ticket.saleBegin ||\n ticket.onSaleStart;\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\n const remaining =\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\n const isSoldOut =\n ticket.soldOut ||\n (remaining !== null && remaining !== undefined && remaining <= 0);\n\n const isScheduled = salesBegin ? new Date(salesBegin) > now : false;\n const hasSalesEnded = salesEnd ? new Date(salesEnd) < now : false;\n const isPurchasable = !isSoldOut && !isScheduled && !hasSalesEnded;\n\n if (isPurchasable) hasPurchasable = true;\n if (!isSoldOut) allSoldOut = false;\n if (!isScheduled) allComingSoon = false;\n\n if (isScheduled && salesBegin && !isSoldOut && !hasSalesEnded) {\n const startDate = new Date(salesBegin);\n if (!earliestSalesStart || startDate < earliestSalesStart) {\n earliestSalesStart = startDate;\n }\n }\n }\n\n if (hasPurchasable) {\n return {\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\n disabled: false,\n reason: 'available',\n };\n }\n\n if (allComingSoon && earliestSalesStart) {\n const diffMs = earliestSalesStart.getTime() - now.getTime();\n const diffHours = diffMs / (1000 * 60 * 60);\n const diffDays = diffMs / (1000 * 60 * 60 * 24);\n let dateText: string;\n if (diffHours < 24) {\n dateText = earliestSalesStart.toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n });\n } else if (diffDays <= 7) {\n dateText = formatDateRaw(\n earliestSalesStart,\n { weekday: 'short', hour: 'numeric', minute: '2-digit', hour12: true },\n 'en-US'\n );\n } else {\n const d = formatDateRaw(\n earliestSalesStart,\n { month: 'short', day: 'numeric' },\n 'en-US'\n );\n const t = earliestSalesStart.toLocaleTimeString('en-US', {\n hour: 'numeric',\n minute: '2-digit',\n hour12: true,\n });\n dateText = `${d} ${t}`;\n }\n return {\n text: `On sale ${dateText}`,\n disabled: true,\n reason: 'coming_soon',\n };\n }\n\n if (allSoldOut) {\n if (waitlistEnabled) {\n return {\n text: 'Join Waitlist',\n disabled: false,\n reason: 'sold_out_waitlist',\n };\n }\n return { text: 'Sold out', disabled: true, reason: 'sold_out' };\n }\n\n return { text: 'Sales ended', disabled: true, reason: 'sales_ended' };\n}\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components';\r\n// Premium Ticket Experience — Mock Data\r\n// Realistic event data for all design variants. DO NOT MODIFY per-variant.\r\n\r\nimport type { EventData, PerformerData, FAQItem, VenueData, TicketTypeData } from './types';\r\n\r\n// --- Venue ---\r\nexport const mockVenue: VenueData = {\r\n\tid: 101,\r\n\tname: 'The Laugh Factory',\r\n\taddress: '8001 Sunset Blvd, Los Angeles, CA 90046',\r\n\ttimezone: getDefaultTimezone(),\r\n\tserviceFeePercentage: 10,\r\n\tserviceFeeCents: 0,\r\n\ttaxPercentage: 9.5,\r\n\tcontactEmail: 'help@thelaughfactory.com'\r\n};\r\n\r\n// --- Performers ---\r\nexport const mockPerformers: PerformerData[] = [\r\n\t{\r\n\t\tid: 1,\r\n\t\tdisplayName: 'Dave Martinez',\r\n\t\tprofileImage: 'https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=200&h=200&fit=crop'\r\n\t},\r\n\t{\r\n\t\tid: 2,\r\n\t\tdisplayName: 'Sarah Chen',\r\n\t\tprofileImage: 'https://images.unsplash.com/photo-1494790108377-be9c29b29330?w=200&h=200&fit=crop'\r\n\t},\r\n\t{\r\n\t\tid: 3,\r\n\t\tdisplayName: 'Mike Thompson',\r\n\t\tprofileImage: 'https://images.unsplash.com/photo-1507003211169-0a1dd7228f2d?w=200&h=200&fit=crop'\r\n\t}\r\n];\r\n\r\n// --- FAQs ---\r\nexport const mockFaqs: FAQItem[] = [\r\n\t{\r\n\t\tquestion: 'What time do doors open?',\r\n\t\tanswer: 'Doors open at 7:00 PM, one hour before showtime. We recommend arriving early to get the best seats.'\r\n\t},\r\n\t{\r\n\t\tquestion: 'Is there a drink minimum?',\r\n\t\tanswer: 'Yes, there is a 2-drink minimum per person. Both alcoholic and non-alcoholic beverages count toward the minimum.'\r\n\t},\r\n\t{\r\n\t\tquestion: 'Can I get a refund?',\r\n\t\tanswer: 'Tickets are non-refundable but can be transferred to another person up to 24 hours before the show. Contact us for transfers.'\r\n\t},\r\n\t{\r\n\t\tquestion: 'Is the venue accessible?',\r\n\t\tanswer: 'Yes, the venue is fully wheelchair accessible with ADA-compliant seating available. Please contact us to reserve accessible seating.'\r\n\t}\r\n];\r\n\r\n// --- Ticket Types ---\r\nexport const mockTickets: TicketTypeData[] = [\r\n\t{\r\n\t\tid: 101,\r\n\t\tname: 'General Admission',\r\n\t\tdescription: 'Standard seating in the main room',\r\n\t\tprice: 25,\r\n\t\tfee: 2.5,\r\n\t\tquantityAvailable: 12,\r\n\t\tquantityTotal: 150,\r\n\t\tminPerOrder: 1,\r\n\t\tmaxPerOrder: 8,\r\n\t\tstatus: 'on_sale',\r\n\t\tsalesEndDate: '2026-02-20T20:00:00-08:00',\r\n\t\tisDonation: false,\r\n\t\tisHidden: false\r\n\t},\r\n\t{\r\n\t\tid: 102,\r\n\t\tname: 'VIP Front Row',\r\n\t\tdescription: 'Premium seats in the first two rows with a complimentary drink',\r\n\t\tprice: 55,\r\n\t\tfee: 5.5,\r\n\t\tquantityAvailable: 3,\r\n\t\tquantityTotal: 20,\r\n\t\tminPerOrder: 1,\r\n\t\tmaxPerOrder: 4,\r\n\t\tstatus: 'on_sale',\r\n\t\tsalesEndDate: '2026-02-20T20:00:00-08:00',\r\n\t\tisDonation: false,\r\n\t\tisHidden: false\r\n\t},\r\n\t{\r\n\t\tid: 103,\r\n\t\tname: 'Table for 4',\r\n\t\tdescription: 'Reserved table for four guests with bottle service',\r\n\t\tprice: 180,\r\n\t\tfee: 18,\r\n\t\tquantityAvailable: 8,\r\n\t\tquantityTotal: 15,\r\n\t\tminPerOrder: 1,\r\n\t\tmaxPerOrder: 3,\r\n\t\tstatus: 'on_sale',\r\n\t\tisDonation: false,\r\n\t\tisHidden: false\r\n\t},\r\n\t{\r\n\t\tid: 104,\r\n\t\tname: 'Student Discount',\r\n\t\tdescription: 'Valid student ID required at the door',\r\n\t\tprice: 15,\r\n\t\tfee: 1.5,\r\n\t\tquantityAvailable: 45,\r\n\t\tquantityTotal: 50,\r\n\t\tminPerOrder: 1,\r\n\t\tmaxPerOrder: 2,\r\n\t\tstatus: 'on_sale',\r\n\t\tisDonation: false,\r\n\t\tisHidden: false\r\n\t},\r\n\t{\r\n\t\tid: 105,\r\n\t\tname: 'Early Bird Special',\r\n\t\tdescription: 'Limited early bird pricing — first come, first served',\r\n\t\tprice: 20,\r\n\t\tfee: 2,\r\n\t\tquantityAvailable: 0,\r\n\t\tquantityTotal: 30,\r\n\t\tminPerOrder: 1,\r\n\t\tmaxPerOrder: 4,\r\n\t\tstatus: 'sold_out',\r\n\t\tisDonation: false,\r\n\t\tisHidden: false\r\n\t}\r\n];\r\n\r\n// --- Main Event (on sale, multiple ticket types, scarcity on VIP) ---\r\nexport const mockEvent: EventData = {\r\n\tid: 2405,\r\n\ttitle: 'Friday Night Comedy Throwdown',\r\n\tslug: 'friday-night-comedy-throwdown',\r\n\tdescription: `Get ready for the funniest night of the week! The Friday Night Comedy Throwdown brings together three of LA's hottest stand-up comics for two hours of non-stop laughs.\r\n\r\nHeadliner Dave Martinez (as seen on Netflix's \"Comedy Knockout\") brings his signature high-energy style, while Sarah Chen delivers her razor-sharp observational humor that's earned her a devoted following. Host Mike Thompson keeps the energy up between sets with crowd work that'll have you crying laughing.\r\n\r\nWhether you're a comedy regular or a first-timer, this show delivers. Grab your crew, grab some drinks, and prepare to laugh until your abs hurt.`,\r\n\tdate: '2026-02-20',\r\n\tstartDateTime: '2026-02-20T20:00:00-08:00',\r\n\tendDateTime: '2026-02-20T22:00:00-08:00',\r\n\tdoorsOpenTime: '2026-02-20T19:00:00-08:00',\r\n\ttimezone: getDefaultTimezone(),\r\n\timageUrl: '/posters/Friday Night Showdown.jpg', // wide landscape 800×400\r\n\tvenue: mockVenue,\r\n\tperformers: mockPerformers,\r\n\ttickets: mockTickets,\r\n\tfaqs: mockFaqs,\r\n\tdisclaimer: '',\r\n\tstatus: 'on_sale',\r\n\tisPasswordProtected: false,\r\n\tisRegistrationEvent: false,\r\n\ttotalTicketsRemaining: 68,\r\n\ttotalTicketsCapacity: 265,\r\n\tdisplayStartTime: true,\r\n\tdisplayEndTime: true,\r\n\tdisplayDoorsTime: true\r\n};\r\n\r\n// --- Sold Out Event (no end time shown) ---\r\nexport const mockSoldOutEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2406,\r\n\ttitle: 'Saturday Night Special',\r\n\tslug: 'saturday-night-special',\r\n\timageUrl: '/posters/the-duel-square.webp', // square 3000×3000\r\n\tdate: '2026-02-21',\r\n\tstartDateTime: '2026-02-21T21:00:00-08:00',\r\n\tendDateTime: '2026-02-21T23:00:00-08:00',\r\n\tdisplayEndTime: false,\r\n\tstatus: 'sold_out',\r\n\ttotalTicketsRemaining: 0,\r\n\ttickets: mockTickets.map((t) => ({ ...t, quantityAvailable: 0, status: 'sold_out' as const }))\r\n};\r\n\r\n// --- Free Event (no doors time shown) ---\r\nexport const mockFreeEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2407,\r\n\ttitle: 'Open Mic Night',\r\n\tslug: 'open-mic-night',\r\n\timageUrl: '/posters/OpenMic-Twitter.jpg', // wide landscape 2127×1075\r\n\tdate: '2026-02-18',\r\n\tstartDateTime: '2026-02-18T19:00:00-08:00',\r\n\tendDateTime: '2026-02-18T21:00:00-08:00',\r\n\tdisplayDoorsTime: false,\r\n\tdescription: 'Free open mic night! Sign up at the door. All skill levels welcome.',\r\n\tstatus: 'on_sale',\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 201,\r\n\t\t\tname: 'Free Entry',\r\n\t\t\tdescription: 'Sign up at the door for a 5-minute set',\r\n\t\t\tprice: 0,\r\n\t\t\tfee: 0,\r\n\t\t\tquantityAvailable: 40,\r\n\t\t\tquantityTotal: 50,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 2,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 40,\r\n\ttotalTicketsCapacity: 50\r\n};\r\n\r\n// --- Series Event (with occurrences) ---\r\nexport const mockSeriesEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2410,\r\n\ttitle: 'Improv Throwdown',\r\n\tslug: 'improv-throwdown',\r\n\timageUrl: '/posters/laughlab-tall.webp', // tall portrait 2550×3300\r\n\teventSeriesId: 42,\r\n\tdescription: 'Weekly improv comedy show featuring rotating guest performers and audience participation games.',\r\n\tseriesOccurrences: [\r\n\t\t// Same-day shows (Fri Feb 20 — 7pm, 8pm, 9:30pm)\r\n\t\t{\r\n\t\t\tid: 2410,\r\n\t\t\tslug: 'improv-throwdown-feb-20-7pm',\r\n\t\t\tstartDateTime: '2026-02-20T19:00:00-08:00',\r\n\t\t\tendDateTime: '2026-02-20T20:30:00-08:00',\r\n\t\t\tstatus: 'sold_out',\r\n\t\t\tctaText: 'Sold out',\r\n\t\t\tctaDisabled: true\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2414,\r\n\t\t\tslug: 'improv-throwdown-feb-20-8pm',\r\n\t\t\tstartDateTime: '2026-02-20T20:00:00-08:00',\r\n\t\t\tendDateTime: '2026-02-20T21:30:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tctaText: 'Get tickets',\r\n\t\t\tctaDisabled: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2415,\r\n\t\t\tslug: 'improv-throwdown-feb-20-930pm',\r\n\t\t\tstartDateTime: '2026-02-20T21:30:00-08:00',\r\n\t\t\tendDateTime: '2026-02-20T23:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tctaText: 'Get tickets',\r\n\t\t\tctaDisabled: false\r\n\t\t},\r\n\t\t// Single show days\r\n\t\t{\r\n\t\t\tid: 2411,\r\n\t\t\tslug: 'improv-throwdown-feb-27',\r\n\t\t\tstartDateTime: '2026-02-27T20:00:00-08:00',\r\n\t\t\tendDateTime: '2026-02-27T22:00:00-08:00',\r\n\t\t\tstatus: 'sold_out',\r\n\t\t\tctaText: 'Sold out',\r\n\t\t\tctaDisabled: true\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2412,\r\n\t\t\tslug: 'improv-throwdown-mar-06',\r\n\t\t\tstartDateTime: '2026-03-06T20:00:00-08:00',\r\n\t\t\tendDateTime: '2026-03-06T22:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tctaText: 'Get tickets',\r\n\t\t\tctaDisabled: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2413,\r\n\t\t\tslug: 'improv-throwdown-mar-13',\r\n\t\t\tstartDateTime: '2026-03-13T20:00:00-08:00',\r\n\t\t\tendDateTime: '2026-03-13T22:00:00-08:00',\r\n\t\t\tstatus: 'coming_soon',\r\n\t\t\tctaText: 'Coming soon',\r\n\t\t\tctaDisabled: true\r\n\t\t}\r\n\t]\r\n};\r\n\r\n// --- Series Instance (one specific occurrence of the series — regular event page with series link) ---\r\nexport const mockSeriesInstanceEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2414,\r\n\ttitle: 'Improv Throwdown',\r\n\tslug: 'improv-throwdown-feb-20-8pm',\r\n\timageUrl: '/posters/laughlab-tall.webp',\r\n\teventSeriesId: 42,\r\n\tstartDateTime: '2026-02-20T20:00:00-08:00',\r\n\tendDateTime: '2026-02-20T21:30:00-08:00',\r\n\tdescription: 'Weekly improv comedy show featuring rotating guest performers and audience participation games.',\r\n\t// No seriesOccurrences — this is a single instance, not the series home\r\n};\r\n\r\n// --- Collection Home (loose grouping of different events with widgets) ---\r\nexport const mockCollectionEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 3000,\r\n\ttitle: 'February Comedy Festival',\r\n\tslug: 'february-comedy-festival',\r\n\timageUrl: '/posters/Friday Night Showdown.jpg',\r\n\tdescription: 'A curated collection of the best comedy shows this February. From improv to stand-up, sketch to open mic — there\\'s something for every comedy fan.',\r\n\tcollectionId: 7,\r\n\tcollectionEvents: [\r\n\t\t{\r\n\t\t\tid: 2401,\r\n\t\t\tname: 'Friday Night Comedy Throwdown',\r\n\t\t\tslug: 'friday-night-comedy-throwdown',\r\n\t\t\timage: '/posters/Friday Night Showdown.jpg',\r\n\t\t\tstartDateTime: '2026-02-20T20:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tticketsRemaining: 42,\r\n\t\t\tticketsTotal: 200,\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2402,\r\n\t\t\tname: 'Saturday Night Special',\r\n\t\t\tslug: 'saturday-night-special',\r\n\t\t\timage: '/posters/the-duel-square.webp',\r\n\t\t\tstartDateTime: '2026-02-21T21:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tticketsRemaining: 8,\r\n\t\t\tticketsTotal: 150,\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2403,\r\n\t\t\tname: 'Stand-Up Comedy Workshop',\r\n\t\t\tslug: 'standup-comedy-workshop',\r\n\t\t\timage: '/posters/stand up 101.png',\r\n\t\t\tstartDateTime: '2026-02-22T14:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tticketsRemaining: 15,\r\n\t\t\tticketsTotal: 30,\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2410,\r\n\t\t\tname: 'Improv Throwdown',\r\n\t\t\tslug: 'improv-throwdown',\r\n\t\t\timage: '/posters/laughlab-tall.webp',\r\n\t\t\tstartDateTime: '2026-02-27T20:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tticketsRemaining: 65,\r\n\t\t\tticketsTotal: 200,\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2404,\r\n\t\t\tname: 'Open Mic Night',\r\n\t\t\tslug: 'open-mic-night',\r\n\t\t\timage: '/posters/OpenMic-Twitter.jpg',\r\n\t\t\tstartDateTime: '2026-02-28T19:00:00-08:00',\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tticketsRemaining: 100,\r\n\t\t\tticketsTotal: 100,\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 2405,\r\n\t\t\tname: 'Late Night Comedy Underground',\r\n\t\t\tslug: 'late-night-comedy-underground',\r\n\t\t\timage: '/posters/standup-portrait.webp',\r\n\t\t\tstartDateTime: '2026-02-28T22:00:00-08:00',\r\n\t\t\tstatus: 'sold_out',\r\n\t\t\tticketsRemaining: 0,\r\n\t\t\tticketsTotal: 80,\r\n\t\t}\r\n\t]\r\n};\r\n\r\n// --- Registration Event (workshop — no time shown, just date + doors) ---\r\nexport const mockRegistrationEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2408,\r\n\ttitle: 'Stand-Up Comedy Workshop',\r\n\tslug: 'standup-comedy-workshop',\r\n\timageUrl: '/posters/stand up 101.png', // 16:9 landscape 1920×1080\r\n\tdescription: 'Learn the fundamentals of stand-up comedy in this 3-hour intensive workshop. Write, refine, and perform your first 5-minute set.',\r\n\tisRegistrationEvent: true,\r\n\tdisplayStartTime: false,\r\n\tdisplayEndTime: false,\r\n\tstatus: 'on_sale',\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 301,\r\n\t\t\tname: 'Workshop Seat',\r\n\t\t\tdescription: 'Includes materials and performance slot',\r\n\t\t\tprice: 75,\r\n\t\t\tfee: 7.5,\r\n\t\t\tquantityAvailable: 8,\r\n\t\t\tquantityTotal: 12,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 1,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 8,\r\n\ttotalTicketsCapacity: 12\r\n};\r\n\r\n// --- Donation Event ---\r\nexport const mockDonationEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2414,\r\n\ttitle: 'Charity Comedy Benefit',\r\n\tslug: 'charity-comedy-benefit',\r\n\timageUrl: '/posters/Friday Night Showdown.jpg',\r\n\tdescription: 'All proceeds go to the LA Comedy Relief Fund. Choose your own donation amount or grab a standard ticket.',\r\n\tstatus: 'on_sale',\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 401,\r\n\t\t\tname: 'Donation',\r\n\t\t\tdescription: 'Support the cause — choose your amount',\r\n\t\t\tprice: 0,\r\n\t\t\tfee: 0,\r\n\t\t\tquantityAvailable: 200,\r\n\t\t\tquantityTotal: 200,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 10,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: true,\r\n\t\t\tisHidden: false,\r\n\t\t\tticketType: 2\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 402,\r\n\t\t\tname: 'Standard Admission',\r\n\t\t\tdescription: 'Includes one drink ticket',\r\n\t\t\tprice: 30,\r\n\t\t\tfee: 3,\r\n\t\t\tquantityAvailable: 80,\r\n\t\t\tquantityTotal: 100,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 6,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 280,\r\n\ttotalTicketsCapacity: 300\r\n};\r\n\r\n// --- Mixed Event (ALL ticket states in one event) ---\r\nexport const mockMixedEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2415,\r\n\ttitle: 'The Everything Show',\r\n\tslug: 'the-everything-show',\r\n\timageUrl: '/posters/the-duel-square.webp',\r\n\tdescription: 'A showcase event with every ticket state possible — for testing all edge cases.',\r\n\tstatus: 'on_sale',\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 501,\r\n\t\t\tname: 'General Admission',\r\n\t\t\tdescription: 'Standard seating in the main room',\r\n\t\t\tprice: 25,\r\n\t\t\tfee: 2.5,\r\n\t\t\tquantityAvailable: 45,\r\n\t\t\tquantityTotal: 150,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 8,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 502,\r\n\t\t\tname: 'Basic Entry',\r\n\t\t\tprice: 15,\r\n\t\t\tfee: 1.5,\r\n\t\t\tquantityAvailable: 60,\r\n\t\t\tquantityTotal: 80,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 503,\r\n\t\t\tname: 'VIP Front Row',\r\n\t\t\tdescription: 'Premium seats with complimentary drink',\r\n\t\t\tprice: 75,\r\n\t\t\tfee: 7.5,\r\n\t\t\tquantityAvailable: 2,\r\n\t\t\tquantityTotal: 20,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 504,\r\n\t\t\tname: 'Early Bird',\r\n\t\t\tdescription: 'Limited early bird pricing',\r\n\t\t\tprice: 20,\r\n\t\t\tfee: 2,\r\n\t\t\tquantityAvailable: 0,\r\n\t\t\tquantityTotal: 30,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'sold_out',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 505,\r\n\t\t\tname: 'Late Night Add-On',\r\n\t\t\tdescription: 'Access to the after-party at midnight',\r\n\t\t\tprice: 40,\r\n\t\t\tfee: 4,\r\n\t\t\tquantityAvailable: 50,\r\n\t\t\tquantityTotal: 50,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 2,\r\n\t\t\tstatus: 'coming_soon',\r\n\t\t\tsalesStartDate: '2026-03-01T10:00:00-08:00',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 506,\r\n\t\t\tname: 'Weekend Bundle',\r\n\t\t\tdescription: 'Includes Saturday + Sunday shows',\r\n\t\t\tprice: 45,\r\n\t\t\tfee: 4.5,\r\n\t\t\tquantityAvailable: 0,\r\n\t\t\tquantityTotal: 25,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'sales_ended',\r\n\t\t\tsalesEndDate: '2026-02-10T23:59:00-08:00',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 507,\r\n\t\t\tname: 'Donation',\r\n\t\t\tdescription: 'Support the performers directly',\r\n\t\t\tprice: 0,\r\n\t\t\tfee: 0,\r\n\t\t\tquantityAvailable: 100,\r\n\t\t\tquantityTotal: 100,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 5,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: true,\r\n\t\t\tisHidden: false,\r\n\t\t\tticketType: 2\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 508,\r\n\t\t\tname: 'Secret VIP Lounge',\r\n\t\t\tdescription: 'Backstage access with meet & greet',\r\n\t\t\tprice: 150,\r\n\t\t\tfee: 15,\r\n\t\t\tquantityAvailable: 10,\r\n\t\t\tquantityTotal: 10,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 2,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: true\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 509,\r\n\t\t\tname: 'Industry Comp',\r\n\t\t\tdescription: 'Complimentary ticket for industry professionals',\r\n\t\t\tprice: 0,\r\n\t\t\tfee: 0,\r\n\t\t\tquantityAvailable: 20,\r\n\t\t\tquantityTotal: 20,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 1,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: true\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 287,\r\n\ttotalTicketsCapacity: 485\r\n};\r\n\r\n// --- Sectioned Event (tickets grouped by section) ---\r\nexport const mockSectionedEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2416,\r\n\ttitle: 'Multi-Stage Festival',\r\n\tslug: 'multi-stage-festival',\r\n\timageUrl: '/posters/OpenMic-Twitter.jpg',\r\n\tdescription: 'Three stages, one incredible night. Choose your zone and enjoy performances across the venue.',\r\n\tstatus: 'on_sale',\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 601,\r\n\t\t\tname: 'Floor Standing',\r\n\t\t\tdescription: 'Standing room on the main floor',\r\n\t\t\tprice: 35,\r\n\t\t\tfee: 3.5,\r\n\t\t\tquantityAvailable: 100,\r\n\t\t\tquantityTotal: 200,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 8,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false,\r\n\t\t\tsectionName: 'Floor'\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 602,\r\n\t\t\tname: 'Floor Reserved',\r\n\t\t\tdescription: 'Reserved seat on the main floor',\r\n\t\t\tprice: 50,\r\n\t\t\tfee: 5,\r\n\t\t\tquantityAvailable: 30,\r\n\t\t\tquantityTotal: 80,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 6,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false,\r\n\t\t\tsectionName: 'Floor'\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 603,\r\n\t\t\tname: 'Balcony Standard',\r\n\t\t\tdescription: 'Standard balcony seating with great views',\r\n\t\t\tprice: 40,\r\n\t\t\tfee: 4,\r\n\t\t\tquantityAvailable: 50,\r\n\t\t\tquantityTotal: 60,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 6,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false,\r\n\t\t\tsectionName: 'Balcony'\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 604,\r\n\t\t\tname: 'Balcony Premium',\r\n\t\t\tdescription: 'Front-row balcony seats',\r\n\t\t\tprice: 60,\r\n\t\t\tfee: 6,\r\n\t\t\tquantityAvailable: 5,\r\n\t\t\tquantityTotal: 20,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false,\r\n\t\t\tsectionName: 'Balcony'\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 605,\r\n\t\t\tname: 'VIP Lounge Table',\r\n\t\t\tdescription: 'Private table for 4 with bottle service and backstage access',\r\n\t\t\tprice: 250,\r\n\t\t\tfee: 25,\r\n\t\t\tquantityAvailable: 3,\r\n\t\t\tquantityTotal: 8,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 2,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false,\r\n\t\t\tsectionName: 'VIP Lounge'\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 188,\r\n\ttotalTicketsCapacity: 368\r\n};\r\n\r\n// --- Promo Event (hidden tickets for promo reveal testing) ---\r\nexport const mockPromoEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2417,\r\n\ttitle: 'Secret Show',\r\n\tslug: 'secret-show',\r\n\timageUrl: '/posters/luke-null-tall.webp',\r\n\tdescription: 'An intimate comedy experience. Some tickets are only available with a promo code.',\r\n\tstatus: 'on_sale',\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 701,\r\n\t\t\tname: 'General Admission',\r\n\t\t\tprice: 30,\r\n\t\t\tfee: 3,\r\n\t\t\tquantityAvailable: 40,\r\n\t\t\tquantityTotal: 60,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 6,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 702,\r\n\t\t\tname: 'Premium Seat',\r\n\t\t\tdescription: 'Reserved seating in rows 1-5',\r\n\t\t\tprice: 50,\r\n\t\t\tfee: 5,\r\n\t\t\tquantityAvailable: 15,\r\n\t\t\tquantityTotal: 30,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 703,\r\n\t\t\tname: 'Artist Meet & Greet',\r\n\t\t\tdescription: 'Backstage pass with photo opportunity — promo code required',\r\n\t\t\tprice: 100,\r\n\t\t\tfee: 10,\r\n\t\t\tquantityAvailable: 8,\r\n\t\t\tquantityTotal: 10,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 2,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: true\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 704,\r\n\t\t\tname: 'Industry Comp',\r\n\t\t\tdescription: 'Complimentary entry for industry professionals',\r\n\t\t\tprice: 0,\r\n\t\t\tfee: 0,\r\n\t\t\tquantityAvailable: 10,\r\n\t\t\tquantityTotal: 10,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 1,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: true\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 73,\r\n\ttotalTicketsCapacity: 110\r\n};\r\n\r\n// --- Multi-Stage Event (stage name + age restriction) ---\r\nexport const mockMultiStageEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2418,\r\n\ttitle: 'Late Night Comedy Underground',\r\n\tslug: 'late-night-comedy-underground',\r\n\timageUrl: '/posters/laughlab-tall.webp',\r\n\tdescription: 'The raunchiest, most unfiltered comedy night in LA. Side stage, no holds barred.',\r\n\tstatus: 'on_sale',\r\n\tstageName: 'Side Stage',\r\n\tageRestriction: 21,\r\n\tdisplayAgeRestriction: true,\r\n\ttickets: [\r\n\t\t{\r\n\t\t\tid: 801,\r\n\t\t\tname: 'General Admission',\r\n\t\t\tprice: 20,\r\n\t\t\tfee: 2,\r\n\t\t\tquantityAvailable: 25,\r\n\t\t\tquantityTotal: 40,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 4,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 802,\r\n\t\t\tname: 'VIP Booth',\r\n\t\t\tdescription: 'Reserved booth for 2 with drinks included',\r\n\t\t\tprice: 80,\r\n\t\t\tfee: 8,\r\n\t\t\tquantityAvailable: 4,\r\n\t\t\tquantityTotal: 6,\r\n\t\t\tminPerOrder: 1,\r\n\t\t\tmaxPerOrder: 2,\r\n\t\t\tstatus: 'on_sale',\r\n\t\t\tisDonation: false,\r\n\t\t\tisHidden: false\r\n\t\t}\r\n\t],\r\n\ttotalTicketsRemaining: 29,\r\n\ttotalTicketsCapacity: 46\r\n};\r\n\r\n// --- Long Name Event (Laugh Factory-style: all performers in the title) ---\r\nexport const mockLongNameEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2420,\r\n\ttitle: 'The Friday Night All-Star Comedy Throwdown featuring Dave Martinez, Sarah Chen, Mike Thompson, Bobby Lee, Andrew Santino, Hasan Minhaj, and Very Special Guests from Netflix, Comedy Central, HBO, and Amazon Prime',\r\n\tslug: 'the-friday-night-all-star-comedy-throwdown',\r\n\timageUrl: '/posters/laughlab-tall.webp',\r\n\tdescription: `The biggest lineup of the year! Seven of the hottest comics in LA on one stage for one unforgettable night. This is the show comedy fans have been waiting for — a stacked bill from top to bottom with headliners you've seen on every major streaming platform.\r\n\r\nDave Martinez (Netflix's \"Comedy Knockout\") headlines with his signature high-energy style. Sarah Chen brings her razor-sharp observational humor. Mike Thompson keeps the energy sky-high between sets. Bobby Lee delivers his unhinged brilliance. Andrew Santino roasts everything in sight. Hasan Minhaj brings the political heat. Plus surprise guests you won't want to miss.\r\n\r\nTwo-drink minimum. No photography or recording. Arrive early — seating is first come, first served.`,\r\n\tageRestriction: 18,\r\n\tdisplayAgeRestriction: true,\r\n\tdisplayStartTime: true,\r\n\tdisplayEndTime: true,\r\n\tdisplayDoorsTime: true,\r\n\tperformers: [\r\n\t\t...mockPerformers,\r\n\t\t{\r\n\t\t\tid: 4,\r\n\t\t\tdisplayName: 'Bobby Lee',\r\n\t\t\tprofileImage: 'https://images.unsplash.com/photo-1472099645785-5658abf4ff4e?w=200&h=200&fit=crop'\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 5,\r\n\t\t\tdisplayName: 'Andrew Santino',\r\n\t\t\tprofileImage: 'https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=200&h=200&fit=crop'\r\n\t\t},\r\n\t\t{\r\n\t\t\tid: 6,\r\n\t\t\tdisplayName: 'Hasan Minhaj',\r\n\t\t\tprofileImage: 'https://images.unsplash.com/photo-1519085360753-af0119f7cbe7?w=200&h=200&fit=crop'\r\n\t\t}\r\n\t]\r\n};\r\n\r\n// --- Wide/Panoramic Image Event (tests min-height behavior) ---\r\nexport const mockWideImageEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2421,\r\n\ttitle: 'Sunset Sessions: Rooftop Jazz & Cocktails',\r\n\tslug: 'sunset-sessions-rooftop-jazz',\r\n\timageUrl: '/posters/Wide.jpg',\r\n\tdescription: `Join us on the rooftop for an evening of smooth jazz, craft cocktails, and stunning sunset views over the city. Featuring a rotating cast of LA's finest jazz musicians in an intimate outdoor setting.\r\n\r\nFull bar available with specialty cocktails curated for the evening. Light bites and small plates from our kitchen. Dress code: smart casual.`,\r\n\tdisplayStartTime: true,\r\n\tdisplayEndTime: true,\r\n\tdisplayDoorsTime: true\r\n};\r\n\r\n// --- Password Protected Event ---\r\nexport const mockPasswordEvent: EventData = {\r\n\t...mockEvent,\r\n\tid: 2409,\r\n\ttitle: 'Secret Comedy Club',\r\n\tslug: 'secret-comedy-club',\r\n\timageUrl: '/posters/luke-null-tall.webp', // tall portrait 625×937\r\n\tdescription: 'An exclusive underground comedy experience. Password required.',\r\n\tisPasswordProtected: true,\r\n\tstatus: 'on_sale'\r\n};\r\n","// Premium Ticket Experience — Constants & Defaults\r\n// Used by all design variants. DO NOT MODIFY per-variant.\r\n\r\nimport type {\r\n EventData,\r\n TicketTypeData,\r\n ScarcityLevel,\r\n CtaConfig,\r\n PromoCodeState,\r\n} from './types';\r\nimport { roundCurrency } from '$lib/utils/currency.js';\r\nimport { computeCtaState } from '$lib/api';\r\nimport {\r\n formatDate as scFormatDate,\r\n formatTime as scFormatTime,\r\n} from '@getmicdrop/svelte-components';\r\nimport {\r\n formatFullDate,\r\n formatFullDateCompact,\r\n formatEventTime,\r\n formatTimeRange,\r\n formatDoorsTime,\r\n formatSalesDate,\r\n formatBrowseDate,\r\n formatEventDate,\r\n formatCleanTimeRange,\r\n getDateParts,\r\n} from '$lib/utils/datetime.js';\r\n\r\n// --- Re-export production date/time formatters (timezone-aware) ---\r\nexport {\r\n formatFullDate,\r\n formatFullDateCompact,\r\n formatEventTime,\r\n formatTimeRange,\r\n formatDoorsTime,\r\n formatSalesDate,\r\n formatBrowseDate,\r\n formatEventDate,\r\n formatCleanTimeRange,\r\n getDateParts,\r\n};\r\n\r\n// --- Scarcity Badge Rules ---\r\nexport const SCARCITY_THRESHOLDS = {\r\n SOLD_OUT: 0,\r\n URGENT: 0.1, // ≤10% remaining → urgent (\"Only X left\")\r\n LOW: 0.2, // ≤20% remaining (80%+ sold) → \"Selling fast\"\r\n MAX_EXACT_COUNT: 20, // Don't show exact count above 20\r\n} as const;\r\n\r\nexport function getScarcityLevel(\r\n remaining: number,\r\n total: number\r\n): ScarcityLevel {\r\n if (total === 0) return 'none';\r\n if (remaining <= 0) return 'sold_out';\r\n const pct = remaining / total;\r\n if (pct <= SCARCITY_THRESHOLDS.URGENT) return 'urgent';\r\n if (pct <= SCARCITY_THRESHOLDS.LOW) return 'low';\r\n return 'none';\r\n}\r\n\r\nexport function getScarcityText(\r\n level: ScarcityLevel,\r\n remaining: number,\r\n context: 'browse' | 'event' | 'ticket',\r\n options?: { isFree?: boolean }\r\n): string | null {\r\n if (level === 'none') return null;\r\n if (level === 'sold_out') return 'Sold out';\r\n // No scarcity badges on free events/tickets — nothing to lose = no urgency\r\n if (options?.isFree) return null;\r\n if (\r\n level === 'urgent' &&\r\n context !== 'browse' &&\r\n remaining <= SCARCITY_THRESHOLDS.MAX_EXACT_COUNT\r\n ) {\r\n return `Only ${remaining} left`;\r\n }\r\n if (level === 'low' || level === 'urgent') return 'Selling fast';\r\n return null;\r\n}\r\n\r\n// --- CTA Rules ---\r\n// Delegates to production computeCtaState() but adapts our EventData shape.\r\n// Components keep calling getCtaConfig(event) — internal logic is production-aligned.\r\nexport function getCtaConfig(event: EventData): CtaConfig {\r\n // Map our EventData tickets to the shape computeCtaState expects\r\n const apiTickets = event.tickets.map(t => ({\r\n salesBegin: t.salesStartDate,\r\n salesEnd: t.salesEndDate,\r\n remainingCapacity: t.quantityAvailable,\r\n totalCapacity: t.quantityTotal,\r\n soldOut: t.status === 'sold_out',\r\n salesChannel: 1, // All our tickets are online (not \"at the door only\")\r\n }));\r\n\r\n // Handle statuses that computeCtaState doesn't compute from tickets\r\n if (event.status === 'cancelled') {\r\n return {\r\n text: 'Cancelled',\r\n disabled: true,\r\n reason: 'This event has been cancelled',\r\n };\r\n }\r\n if (event.status === 'past') {\r\n return {\r\n text: 'Event ended',\r\n disabled: true,\r\n reason: 'This event has ended',\r\n };\r\n }\r\n\r\n const result = computeCtaState(\r\n { startDateTime: event.startDateTime, endDateTime: event.endDateTime },\r\n apiTickets,\r\n {\r\n cancelled: false,\r\n isRegistration: event.isRegistrationEvent,\r\n waitlistEnabled: false,\r\n }\r\n );\r\n\r\n return {\r\n text: result.text,\r\n disabled: result.disabled,\r\n reason: result.disabled ? result.reason : undefined,\r\n };\r\n}\r\n\r\n// --- Price Display Rules (from DECISIONS.md #1) ---\r\nexport function getPriceDisplay(event: EventData): string | null {\r\n const visibleTickets = event.tickets.filter(\r\n t => t.status === 'on_sale' && !t.isHidden\r\n );\r\n if (visibleTickets.length === 0) return null;\r\n\r\n const hasDonation = visibleTickets.some(\r\n t => t.isDonation || t.ticketType === 2\r\n );\r\n const paidTickets = visibleTickets.filter(\r\n t => !t.isDonation && t.ticketType !== 2\r\n );\r\n const hasFree = paidTickets.some(t => t.price === 0);\r\n const pricedTickets = paidTickets.filter(t => t.price > 0);\r\n\r\n // Only donation tickets → \"Donation\"\r\n if (pricedTickets.length === 0 && !hasFree && hasDonation) return 'Donation';\r\n\r\n // Only free tickets (no paid, no donation) → null (BookingWidget shows \"Free\" via isFreeEvent)\r\n if (pricedTickets.length === 0 && hasFree) return null;\r\n\r\n const minPrice = Math.min(...pricedTickets.map(t => t.price));\r\n\r\n // Free + paid → \"Free\" (free dominates the display)\r\n if (hasFree) return null;\r\n\r\n // Donation + paid → \"From $X\" (donation is an alternative option)\r\n if (hasDonation) return `From $${minPrice}`;\r\n\r\n // Single paid ticket type → \"$X\"\r\n if (pricedTickets.length === 1) return `$${minPrice}`;\r\n\r\n // Multiple paid ticket types → \"From $X\"\r\n return `From $${minPrice}`;\r\n}\r\n\r\n// --- Fee Calculation ---\r\nexport function calculateFee(\r\n price: number,\r\n feePercentage: number,\r\n feeCents: number\r\n): number {\r\n return roundCurrency(price * (feePercentage / 100) + feeCents / 100);\r\n}\r\n\r\nexport function calculateTax(amount: number, taxPercentage: number): number {\r\n return roundCurrency(amount * (taxPercentage / 100));\r\n}\r\n\r\n// --- Date Formatting ---\r\n// PREFERRED: Use the timezone-aware production formatters re-exported above:\r\n// formatBrowseDate(iso, tz) → \"Fri, Feb 20\"\r\n// formatFullDateCompact(iso, tz) → \"Friday, February 20\"\r\n// formatEventTime(iso, tz) → \"8:00 PM\"\r\n// formatTimeRange(start, end, tz) → \"8:00 PM - 10:00 PM\"\r\n// formatDoorsTime(doors, tz) → \"7:00 PM\"\r\n//\r\n// DEPRECATED: The functions below are backward-compatible wrappers for variants\r\n// not yet migrated to timezone-aware formatters. They use browser-local timezone.\r\n// Migrate to the production formatters above when updating each variant.\r\n\r\n/** @deprecated Use formatBrowseDate(dateStr, timezone) instead */\r\nexport function formatDate(dateStr: string): string {\r\n return scFormatDate(dateStr, { weekday: 'short', year: undefined });\r\n}\r\n\r\n/** @deprecated Use formatEventTime(dateStr, timezone) instead */\r\nexport function formatTime(dateStr: string): string {\r\n return scFormatTime(dateStr);\r\n}\r\n\r\n/** @deprecated Use formatBrowseDate + formatTimeRange instead */\r\nexport function formatDateRange(start: string, end: string): string {\r\n const tz = Intl.DateTimeFormat().resolvedOptions().timeZone;\r\n return `${formatBrowseDate(start, tz)} · ${formatTimeRange(start, end, tz)}`;\r\n}\r\n\r\n// --- Animation Durations ---\r\nexport const DURATION = {\r\n instant: 100,\r\n micro: 150,\r\n fast: 200,\r\n normal: 300,\r\n slow: 400,\r\n elaborate: 600,\r\n} as const;\r\n\r\n// --- Smart Sales Date Formatting ---\r\n// Ported from production TicketBlock.svelte — smart thresholds for human-readable dates\r\nexport function formatSmartSalesDate(dateStr: string): string {\r\n if (!dateStr) return '';\r\n const date = new Date(dateStr);\r\n const now = new Date();\r\n const diffMs = date.getTime() - now.getTime();\r\n const diffMinutes = Math.round(diffMs / (1000 * 60));\r\n const diffHours = diffMs / (1000 * 60 * 60);\r\n\r\n // < 1 hour: \"in X minutes\"\r\n if (diffMinutes > 0 && diffMinutes < 60) {\r\n const mins = Math.max(1, diffMinutes);\r\n return `in ${mins} minute${mins !== 1 ? 's' : ''}`;\r\n }\r\n\r\n // 1-24 hours: \"in X hours\"\r\n if (diffHours > 0 && diffHours < 24) {\r\n const hours = Math.max(1, Math.round(diffHours));\r\n return `in ${hours} hour${hours !== 1 ? 's' : ''}`;\r\n }\r\n\r\n // Tomorrow: \"tomorrow at X PM\"\r\n const tomorrow = new Date(now);\r\n tomorrow.setDate(tomorrow.getDate() + 1);\r\n if (date.toDateString() === tomorrow.toDateString()) {\r\n const timeStr = date.toLocaleTimeString('en-US', {\r\n hour: 'numeric',\r\n minute: '2-digit',\r\n });\r\n return `tomorrow at ${timeStr}`;\r\n }\r\n\r\n // 2-7 days: \"on Friday at X PM\"\r\n const diffDays = diffMs / (1000 * 60 * 60 * 24);\r\n if (diffDays > 0 && diffDays <= 7) {\r\n const dayName = new Intl.DateTimeFormat('en-US', {\r\n weekday: 'long',\r\n }).format(date);\r\n const timeStr = date.toLocaleTimeString('en-US', {\r\n hour: 'numeric',\r\n minute: '2-digit',\r\n });\r\n return `on ${dayName} at ${timeStr}`;\r\n }\r\n\r\n // > 7 days: \"on Mar 15\"\r\n const dateFormatted = new Intl.DateTimeFormat('en-US', {\r\n month: 'short',\r\n day: 'numeric',\r\n }).format(date);\r\n return `on ${dateFormatted}`;\r\n}\r\n\r\n// --- Promo Code State Factory ---\r\nexport function createDefaultPromoState(): PromoCodeState {\r\n return {\r\n code: '',\r\n isApplied: false,\r\n isValidating: false,\r\n error: '',\r\n hint: '',\r\n discountAmount: 0,\r\n discountType: '',\r\n discountTicketIds: [],\r\n revealHiddenTickets: false,\r\n revealTicketIds: [],\r\n };\r\n}\r\n\r\n// --- Ticket helpers ---\r\nexport function getAvailableTickets(\r\n tickets: TicketTypeData[]\r\n): TicketTypeData[] {\r\n return tickets.filter(t => !t.isHidden && t.status === 'on_sale');\r\n}\r\n","<script lang=\"ts\">\r\n\timport type { USD } from '@getmicdrop/svelte-components';\r\nimport { Text, formatCurrency } from '@getmicdrop/svelte-components';\r\n\timport type { OrderTotals } from '$lib/premium-ticket-experience/types';\r\n\timport { DURATION } from '$lib/premium-ticket-experience/defaults';\r\n\r\n\tinterface Props {\r\n\t\torderTotals: OrderTotals;\r\n\t\t\tlabels?: Record<string, string>;\r\n\t}\r\n\r\n\tlet { orderTotals, labels = {}, }: Props = $props();\r\n</script>\r\n\r\n<div\r\n\tclass=\"space-y-2\"\r\n\tstyle=\"transition-duration: {DURATION.normal}ms;\"\r\n>\r\n\t<!-- Line items -->\r\n\t{#each orderTotals.lines as line (line.ticketId)}\r\n\t\t<div class=\"flex justify-between\">\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{line.quantity}x {line.ticketName}</Text>\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{formatCurrency(line.subtotal as USD /* FIXME(canonical-cleanup:toFixed): this cast bypasses the brand boundary; replace with toCents/toUSD at the API-response-transform layer. */)}</Text>\r\n\t\t</div>\r\n\t{/each}\r\n\r\n\t<!-- Subtotal -->\r\n\t{#if orderTotals.lines.length > 1}\r\n\t\t<div class=\"flex justify-between pt-1 border-t border-default\">\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{labels.subtotal || 'Subtotal'}</Text>\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{formatCurrency(orderTotals.subtotal as USD /* FIXME(canonical-cleanup:toFixed): this cast bypasses the brand boundary; replace with toCents/toUSD at the API-response-transform layer. */)}</Text>\r\n\t\t</div>\r\n\t{/if}\r\n\r\n\t<!-- Fees -->\r\n\t{#if orderTotals.fees > 0}\r\n\t\t<div class=\"flex justify-between\">\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{labels.serviceFees || 'Service fees'}</Text>\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{formatCurrency(orderTotals.fees as USD /* FIXME(canonical-cleanup:toFixed): this cast bypasses the brand boundary; replace with toCents/toUSD at the API-response-transform layer. */)}</Text>\r\n\t\t</div>\r\n\t{/if}\r\n\r\n\t<!-- Taxes -->\r\n\t{#if orderTotals.taxes > 0}\r\n\t\t<div class=\"flex justify-between\">\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{labels.taxes || 'Taxes'}</Text>\r\n\t\t\t<Text size=\"xs\" color=\"muted\">{formatCurrency(orderTotals.taxes as USD /* FIXME(canonical-cleanup:toFixed): this cast bypasses the brand boundary; replace with toCents/toUSD at the API-response-transform layer. */)}</Text>\r\n\t\t</div>\r\n\t{/if}\r\n\r\n\t<!-- Promo discount -->\r\n\t{#if orderTotals.promoDiscount > 0}\r\n\t\t<div class=\"flex justify-between\">\r\n\t\t\t<Text size=\"xs\" color=\"success\">{labels.promoDiscount || 'Promo discount'}</Text>\r\n\t\t\t<Text size=\"xs\" color=\"success\">-{formatCurrency(orderTotals.promoDiscount as USD /* FIXME(canonical-cleanup:toFixed): this cast bypasses the brand boundary; replace with toCents/toUSD at the API-response-transform layer. */)}</Text>\r\n\t\t</div>\r\n\t{/if}\r\n\r\n\t<!-- Total -->\r\n\t<div class=\"flex justify-between pt-2 border-t border-default\">\r\n\t\t<Text size=\"sm\" class=\"font-semibold\">{labels.total || 'Total'}</Text>\r\n\t\t<Text size=\"sm\" class=\"font-semibold\">{formatCurrency(orderTotals.total as USD /* FIXME(canonical-cleanup:toFixed): this cast bypasses the brand boundary; replace with toCents/toUSD at the API-response-transform layer. */)}</Text>\r\n\t</div>\r\n</div>\r\n","/**\r\n * Default English labels for premium-ticket-experience components.\r\n * Consumers override via the `labels` prop on any *Main component.\r\n *\r\n * Label keys use flat camelCase. For composed strings (e.g. aria-labels),\r\n * components build them from atomic label parts.\r\n */\r\nexport const defaultLabels = {\r\n // ── Ticket quantity controls ──────────────────────────────────────\r\n decrease: 'Decrease',\r\n increase: 'Increase',\r\n quantity: 'quantity',\r\n\r\n // ── Ticket states ─────────────────────────────────────────────────\r\n soldOut: 'Sold out',\r\n free: 'Free',\r\n unavailable: 'Unavailable',\r\n eventSoldOut: 'This event is sold out',\r\n\r\n // ── Price display ─────────────────────────────────────────────────\r\n includesFees: 'Includes',\r\n fees: 'fees',\r\n fee: 'fee',\r\n\r\n // ── Checkout CTAs ─────────────────────────────────────────────────\r\n checkout: 'Checkout',\r\n getTickets: 'Get tickets',\r\n selectTickets: 'Select tickets',\r\n selectTicketsBelow: 'Select tickets below',\r\n selectTicketsToContinue: 'Select tickets to continue',\r\n reserveASpot: 'Reserve a spot',\r\n\r\n // ── Password modal / gate ─────────────────────────────────────────\r\n enterPassword: 'Enter Password',\r\n enterEventPassword: 'Enter event password',\r\n passwordRequired: 'Password Required',\r\n passwordRequiredShort: 'Password required',\r\n eventRequiresPassword: 'This event requires a password to purchase tickets.',\r\n eventRequiresPasswordAccess: 'This event requires a password to access tickets.',\r\n enterEventPasswordToView: 'Enter the event password to view tickets.',\r\n enterEventPasswordToViewAvailable: 'Enter the event password to view available tickets.',\r\n eventPassword: 'Event password',\r\n password: 'Password',\r\n pleaseEnterPassword: 'Please enter the event password',\r\n pleaseEnterAPassword: 'Please enter a password',\r\n eventRequiresPasswordShort: 'This event requires a password',\r\n unlockTickets: 'Unlock tickets',\r\n cancel: 'Cancel',\r\n continue: 'Continue',\r\n submit: 'Submit',\r\n\r\n // ── Order summary ─────────────────────────────────────────────────\r\n subtotal: 'Subtotal',\r\n serviceFees: 'Service fees',\r\n taxes: 'Taxes',\r\n tax: 'Tax',\r\n promoDiscount: 'Promo discount',\r\n giftCard: 'Gift card',\r\n total: 'Total',\r\n orderSummary: 'Order summary',\r\n yourOrder: 'Your Order',\r\n\r\n // ── Ticket counts (used in CTA buttons) ───────────────────────────\r\n ticket: 'ticket',\r\n tickets: 'tickets',\r\n\r\n // ── Checkout form (v3-airbnb-split) ───────────────────────────────\r\n yourDetails: 'Your details',\r\n attendeeDetails: 'Attendee details',\r\n attendee: 'Attendee',\r\n sameAsPurchaser: 'Same as purchaser',\r\n payment: 'Payment',\r\n noPaymentRequired: 'No payment required',\r\n freeEventMessage: 'This is a free event. Complete the form above to reserve your spot.',\r\n firstName: 'First name',\r\n lastName: 'Last name',\r\n emailAddress: 'Email address',\r\n email: 'Email',\r\n emailPlaceholder: 'you@example.com',\r\n attendeeEmailPlaceholder: 'attendee@example.com',\r\n cardNumber: 'Card number',\r\n cardPlaceholder: '4242 4242 4242 4242',\r\n expiryDate: 'Expiry date',\r\n expiryPlaceholder: 'MM / YY',\r\n cvc: 'CVC',\r\n cvcPlaceholder: '123',\r\n keepMeUpdated: 'Keep me updated about this event and similar events',\r\n agreeToTerms: 'I agree to the terms and conditions',\r\n goBack: 'Go back',\r\n loadingPaymentForm: 'Loading secure payment form...',\r\n\r\n // ── Checkout form validation ──────────────────────────────────────\r\n firstNameRequired: 'First name is required',\r\n lastNameRequired: 'Last name is required',\r\n emailRequired: 'Email is required',\r\n emailInvalid: 'Enter a valid email address',\r\n emailInvalidShort: 'Enter a valid email',\r\n cardNumberRequired: 'Card number is required',\r\n expiryRequired: 'Expiry date is required',\r\n cvcRequired: 'CVC is required',\r\n mustAgreeTerms: 'You must agree to the terms',\r\n\r\n // ── Place order CTAs ──────────────────────────────────────────────\r\n placeOrder: 'Place order',\r\n completeOrder: 'Complete order',\r\n placingOrder: 'Placing order...',\r\n\r\n // ── Promo code ────────────────────────────────────────────────────\r\n havePromoCode: 'Have a promo code?',\r\n promoCode: 'Promo code',\r\n enterCode: 'Enter code',\r\n apply: 'Apply',\r\n remove: 'Remove',\r\n removePromoCode: 'Remove promo code',\r\n applyPromoCode: 'Apply promo code',\r\n checkingPromoCode: 'Checking promo code',\r\n checking: 'Checking...',\r\n promoApplied: 'Promo applied',\r\n\r\n // ── Navigation tabs (app-segments) ────────────────────────────────\r\n about: 'About',\r\n ticketsTab: 'Tickets',\r\n eventSections: 'Event sections',\r\n\r\n // ── Event details ─────────────────────────────────────────────────\r\n eventDetails: 'Event details',\r\n performers: 'Performers',\r\n faq: 'FAQ',\r\n frequentlyAskedQuestions: 'Frequently asked questions',\r\n doors: 'Doors',\r\n doorsOpenAt: 'Doors open at',\r\n\r\n // ── Series occurrences ────────────────────────────────────────────\r\n upcomingDates: 'Upcoming Dates',\r\n\r\n // ── Close / dismiss ───────────────────────────────────────────────\r\n closeTicketSelection: 'Close ticket selection',\r\n closeTicketSelector: 'Close ticket selector',\r\n\r\n // ── Accessibility ─────────────────────────────────────────────────\r\n ticketSelection: 'ticket selection',\r\n ticketsSelected: 'tickets selected',\r\n view: 'View',\r\n\r\n // ── Success page (v3-airbnb-split) ────────────────────────────────\r\n youreIn: \"You're in!\",\r\n youreRegistered: \"You're registered!\",\r\n confirmed: 'confirmed',\r\n viewTickets: 'View tickets',\r\n order: 'Order',\r\n confirmationSentTo: 'Confirmation sent to',\r\n addToCalendar: 'Add to Calendar',\r\n googleCalendar: 'Google Calendar',\r\n appleCalendar: 'Apple Calendar (.ics)',\r\n share: 'Share',\r\n map: 'Map',\r\n backToEvent: 'Back to event',\r\n needHelp: 'Need help?',\r\n linkCopied: 'Link copied to clipboard!',\r\n copyLink: 'Copy this link:',\r\n addToAppleWallet: 'Add to Apple Wallet',\r\n saveToGoogleWallet: 'Save to Google Wallet',\r\n\r\n // ── Session timer / expiration modals ─────────────────────────────\r\n sessionExpiringSoon: 'Session expiring soon',\r\n sessionExpiringSoonMessage: 'Your checkout session will expire in {time}. Your selected tickets will be released back to inventory.',\r\n extensionsRemaining: '{count} extension{plural} remaining',\r\n continueCheckout: 'Continue checkout',\r\n okIllHurry: \"OK, I'll hurry\",\r\n maxExtensionsReached: 'Maximum extensions reached',\r\n wantToKeepTickets: 'Want to keep your tickets?',\r\n sessionExpiredMessage: 'Your checkout session has expired and your selected tickets have been released.',\r\n sessionExpired: 'Session expired',\r\n reservationTimedOut: 'Your reservation has timed out. Please select your tickets again to continue.',\r\n keepMyTickets: 'Keep my tickets',\r\n selectTicketsAgain: 'Select tickets again',\r\n extendingSession: 'Extending...',\r\n\r\n // ── MobileCta ─────────────────────────────────────────────────────\r\n selectTicketsLabel: 'Select Tickets',\r\n};\r\n\r\n/**\r\n * Merge user-provided label overrides with the defaults.\r\n * Shallow merge — any key provided by the consumer wins.\r\n *\r\n * @param {Record<string, string>} [userLabels] - Partial label overrides\r\n * @returns {Record<string, string>} Merged labels object\r\n */\r\nexport function mergeLabels(userLabels = {}) {\r\n return { ...defaultLabels, ...userLabels };\r\n}\r\n"],"names":["computeCtaState","event","tickets","options","cancelled","isRegistration","waitlistEnabled","now","eventEnd","publicTickets","t","hasPurchasable","allSoldOut","allComingSoon","earliestSalesStart","ticket","salesBegin","salesEnd","remaining","isSoldOut","isScheduled","hasSalesEnded","startDate","diffMs","diffHours","diffDays","dateText","formatDateRaw","d","mockVenue","getDefaultTimezone","mockPerformers","mockFaqs","mockTickets","mockEvent","mockSoldOutEvent","mockFreeEvent","mockSeriesEvent","mockSeriesInstanceEvent","mockCollectionEvent","mockRegistrationEvent","mockDonationEvent","mockMixedEvent","mockSectionedEvent","mockPromoEvent","mockMultiStageEvent","mockLongNameEvent","mockWideImageEvent","SCARCITY_THRESHOLDS","getScarcityLevel","total","pct","getScarcityText","level","context","getCtaConfig","apiTickets","result","getPriceDisplay","visibleTickets","hasDonation","paidTickets","hasFree","pricedTickets","minPrice","calculateTax","amount","taxPercentage","roundCurrency","DURATION","formatSmartSalesDate","dateStr","date","diffMinutes","mins","hours","tomorrow","dayName","timeStr","createDefaultPromoState","labels","$","$$props","div","root","node","line","div_1","root_1","Text","node_1","text","node_2","formatCurrency","div_2","root_4","node_4","node_5","$0","text_3","$$render","consequent","div_3","root_7","node_7","node_8","text_5","consequent_1","div_4","root_10","node_10","node_11","text_7","consequent_2","div_5","root_13","node_13","node_14","text_9","consequent_3","div_6","node_12","node_15","node_16","text_11","defaultLabels","mergeLabels","userLabels"],"mappings":"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"}
|
package/dist/seo/seo.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"seo.cjs","sources":["../../node_modules/@getmicdrop/svelte-components/dist/utils/timezones.js","../../src/lib/seo/helpers.ts","../../src/lib/seo/buildEventJsonLd.ts","../../src/lib/seo/buildSeriesJsonLd.ts","../../src/lib/seo/buildCollectionJsonLd.ts","../../src/lib/utils/textUtils.ts","../../src/lib/seo/HostSeoController.ts"],"sourcesContent":["/**\n * Timezone utility helpers.\n * Canonical source for timezone defaults and validation.\n */\n/**\n * Returns the default timezone for Micdrop, branded as `IANATimezone`.\n * Used when no explicit timezone is provided to date formatters. Bypass\n * call sites that pass `'America/Los_Angeles'` literally are red-squiggle\n * type errors at canonicals that take `IANATimezone` — the fix is to\n * thread `getDefaultTimezone()` through.\n *\n * @returns Branded IANA timezone (`'America/Los_Angeles'`)\n *\n * @example\n * formatDate(new Date(), {}, 'en-US', getDefaultTimezone())\n */\nexport function getDefaultTimezone() {\n return 'America/Los_Angeles';\n}\n/**\n * Validates an IANA timezone string.\n * Uses Intl.DateTimeFormat to test if the timezone is recognized.\n *\n * @param tz - Timezone string to validate\n * @returns true if valid IANA timezone, false otherwise\n *\n * @example\n * isValidTimezone('America/Los_Angeles') // true\n * isValidTimezone('Invalid/Zone') // false\n */\nexport function isValidTimezone(tz) {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz });\n return true;\n }\n catch {\n return false;\n }\n}\n","/**\n * Shared helper utilities for JSON-LD builders.\n *\n * All functions are pure (no DOM, no Svelte, no side effects).\n * Extracted verbatim from EventStructuredData.svelte.\n */\n\nimport type { EventTicketInput, EventPerformerInput } from './types';\n\n/**\n * Format datetime to ISO-8601 with timezone offset.\n * Google requires: \"2025-07-21T19:00:00-05:00\"\n *\n * Extracted from EventStructuredData.svelte lines 25-68.\n */\nexport function formatDateTimeWithOffset(\n isoString: string | null | undefined,\n tz: string,\n): string | null {\n if (!isoString) return null;\n\n const date = new Date(isoString);\n if (isNaN(date.getTime())) return null;\n\n // Get the offset in the target timezone\n const formatter = new Intl.DateTimeFormat('en-US', {\n timeZone: tz,\n year: 'numeric',\n month: '2-digit',\n day: '2-digit',\n hour: '2-digit',\n minute: '2-digit',\n second: '2-digit',\n hour12: false,\n timeZoneName: 'shortOffset',\n });\n\n const parts = formatter.formatToParts(date);\n const get = (type: string) => parts.find((p) => p.type === type)?.value || '';\n\n const year = get('year');\n const month = get('month');\n const day = get('day');\n const hour = get('hour');\n const minute = get('minute');\n const second = get('second');\n const tzOffset = get('timeZoneName'); // e.g., \"GMT-5\" or \"GMT+2\"\n\n // Convert \"GMT-5\" to \"-05:00\" format\n let offset = '+00:00';\n if (tzOffset) {\n const match = tzOffset.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\n if (match) {\n const sign = match[1];\n const hours = match[2].padStart(2, '0');\n const mins = match[3] || '00';\n offset = `${sign}${hours}:${mins}`;\n }\n }\n\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;\n}\n\n/**\n * Parse address string into PostalAddress components.\n * Input: \"7702 Santa Monica Blvd, West Hollywood, CA 90046\"\n * Output: { @type, streetAddress, addressLocality, addressRegion, postalCode, addressCountry }\n *\n * Extracted from EventStructuredData.svelte lines 75-111.\n */\nexport function parseAddressToPostal(\n addressStr: string | null | undefined,\n): Record<string, string> | null {\n if (!addressStr) return null;\n\n const parts = addressStr.split(',').map((p) => p.trim());\n\n if (parts.length < 2) {\n return {\n '@type': 'PostalAddress',\n streetAddress: addressStr,\n addressCountry: 'US',\n };\n }\n\n // Check if last part is \"State ZIP\" (e.g., \"CA 90046\")\n const lastPart = parts[parts.length - 1];\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(?:-\\d{4})?)$/);\n\n if (stateZipMatch && parts.length >= 3) {\n // Standard US address format\n return {\n '@type': 'PostalAddress',\n streetAddress: parts.slice(0, -2).join(', '),\n addressLocality: parts[parts.length - 2],\n addressRegion: stateZipMatch[1],\n postalCode: stateZipMatch[2],\n addressCountry: 'US',\n };\n }\n\n // Fallback for non-standard formats\n return {\n '@type': 'PostalAddress',\n streetAddress: parts[0],\n addressLocality: parts.slice(1).join(', '),\n addressCountry: 'US',\n };\n}\n\n/**\n * Map event status to schema.org EventStatusType.\n *\n * Extracted from EventStructuredData.svelte lines 116-132.\n */\nexport function getEventStatus(rawStatus: string | null | undefined): string {\n if (!rawStatus) return 'https://schema.org/EventScheduled';\n\n const status = rawStatus.toLowerCase();\n\n if (status === 'cancelled' || status === 'canceled') {\n return 'https://schema.org/EventCancelled';\n }\n if (status === 'postponed') {\n return 'https://schema.org/EventPostponed';\n }\n if (status === 'rescheduled') {\n return 'https://schema.org/EventRescheduled';\n }\n\n return 'https://schema.org/EventScheduled';\n}\n\n/**\n * Map ticket availability to schema.org ItemAvailability.\n *\n * Extracted from EventStructuredData.svelte lines 137-157.\n */\nexport function getTicketAvailability(ticket: EventTicketInput): string {\n const remaining =\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\n\n if (remaining === 0 || ticket.soldOut) {\n return 'https://schema.org/SoldOut';\n }\n\n const now = new Date();\n const salesBegin =\n ticket.salesBegin || ticket.salesStart || ticket.saleBegin || ticket.onSaleStart;\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\n\n if (salesBegin && new Date(salesBegin) > now) {\n return 'https://schema.org/PreOrder';\n }\n\n if (salesEnd && new Date(salesEnd) < now) {\n return 'https://schema.org/SoldOut';\n }\n\n return 'https://schema.org/InStock';\n}\n\n/**\n * Build offers from available tickets.\n *\n * Extracted from EventStructuredData.svelte lines 162-208.\n */\nexport function buildOffers(\n tickets: EventTicketInput[] | null | undefined,\n url?: string,\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter to public tickets only (exclude door-only and hidden)\n const publicTickets = tickets.filter((t) => {\n const visibility = t.visibility ?? 0;\n const isVisible = visibility === 0 || visibility === 1;\n const isPublic = t.salesChannel !== 2;\n return isVisible && isPublic;\n });\n\n if (publicTickets.length === 0) return null;\n\n // Find lowest and highest price\n const prices = publicTickets\n .map((t) => parseFloat(String(t.price ?? 0)))\n .filter((p) => !isNaN(p));\n\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n // Use AggregateOffer when there are multiple price points\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice: lowPrice,\n highPrice: highPrice,\n priceCurrency: 'USD',\n availability: getTicketAvailability(publicTickets[0]),\n url: url || undefined,\n validFrom:\n publicTickets[0]?.salesBegin || publicTickets[0]?.salesStart || undefined,\n };\n }\n\n // Single price point - use Offer\n const ticket = publicTickets[0];\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: getTicketAvailability(ticket),\n url: url || undefined,\n validFrom: ticket?.salesBegin || ticket?.salesStart || undefined,\n };\n}\n\n/**\n * Build performer array from event performers.\n *\n * Extracted from EventStructuredData.svelte lines 213-237.\n */\nexport function buildPerformers(\n performers: EventPerformerInput[] | null | undefined,\n): Array<Record<string, string>> | null {\n if (!performers || performers.length === 0) return null;\n\n // Filter to confirmed, visible performers\n const confirmed = performers.filter((p) => {\n const isConfirmed = p.acceptedState === 2;\n const isNotHidden = !p.shouldBeHidden;\n const profile = p.RosterPerformer?.User?.performerProfile;\n const hasName = profile?.firstName || profile?.lastName || profile?.displayName;\n return isConfirmed && isNotHidden && hasName;\n });\n\n if (confirmed.length === 0) return null;\n\n return confirmed.map((p) => {\n const profile = p.RosterPerformer?.User?.performerProfile;\n const name =\n profile?.displayName ||\n [profile?.firstName, profile?.lastName].filter(Boolean).join(' ');\n\n return {\n '@type': 'PerformingGroup',\n name: name,\n };\n });\n}\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\n/**\n * Pure function that builds Event JSON-LD structured data for Google Events rich results.\n *\n * Extracted from EventStructuredData.svelte (lines 242-313).\n * Produces identical output to the original $derived.by block.\n */\n\nimport type { EventJsonLdInput, VenueInput, OrganizerInput } from './types';\nimport {\n formatDateTimeWithOffset,\n parseAddressToPostal,\n getEventStatus,\n buildOffers,\n buildPerformers,\n} from './helpers';\n\nexport interface BuildEventJsonLdOptions {\n event: EventJsonLdInput;\n venue: VenueInput;\n venueTimeZone?: string;\n eventUrl?: string;\n organizer?: OrganizerInput;\n}\n\nexport function buildEventJsonLd(\n options: BuildEventJsonLdOptions,\n): Record<string, unknown> | null {\n const {\n event,\n venue,\n venueTimeZone = getDefaultTimezone(),\n eventUrl,\n organizer,\n } = options;\n\n if (!event) return null;\n\n const address = venue?.address || venue?.googleLocationNameCache || '';\n const startDate = formatDateTimeWithOffset(event.startDateTime, venueTimeZone);\n\n // Skip if we don't have required fields\n if (!event.name || !startDate || !venue?.name) {\n return null;\n }\n\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Event',\n name: event.name,\n startDate: startDate,\n eventStatus: getEventStatus(event.status),\n eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',\n location: {\n '@type': 'Place',\n name: venue.name,\n address: parseAddressToPostal(address),\n },\n };\n\n // Add endDate if available\n const endDate = formatDateTimeWithOffset(event.endDateTime, venueTimeZone);\n if (endDate) {\n data.endDate = endDate;\n }\n\n // Add description\n const description = event.description || event.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Add image - Google recommends multiple aspect ratios\n const imageUrl = event.image || event.coverImage || event.ShowImage;\n if (imageUrl) {\n data.image = [imageUrl];\n }\n\n // Add offers from tickets\n const offers = buildOffers(event.availableTickets, eventUrl);\n if (offers) {\n data.offers = offers;\n }\n\n // Add performers\n const performers = buildPerformers(event.performers);\n if (performers && performers.length > 0) {\n data.performer = performers;\n }\n\n // Add organizer if provided\n if (organizer?.name) {\n const org: Record<string, string> = {\n '@type': 'Organization',\n name: organizer.name,\n };\n if (organizer.url) {\n org.url = organizer.url;\n }\n data.organizer = org;\n }\n\n // Add event URL if provided\n if (eventUrl) {\n data.url = eventUrl;\n }\n\n return data;\n}\n","/**\n * Pure function that builds EventSeries JSON-LD structured data.\n *\n * Series use schema.org EventSeries type with subEvent array containing\n * individual Event blocks for each occurrence. Each subEvent has its own\n * offers built from the simplified series ticket DTOs (different shape\n * from the event builder's tickets).\n *\n * Google Rich Results Test does NOT support EventSeries directly, but\n * the individual subEvent Event blocks can qualify for rich results.\n */\n\nimport type { SeriesJsonLdInput } from './types';\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\n\nexport interface BuildSeriesJsonLdOptions {\n series: SeriesJsonLdInput;\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\" -- used to build subEvent @id URLs\n}\n\n/**\n * Map a series ticket status string to schema.org ItemAvailability.\n *\n * Series tickets use a simplified { name, price, status } shape from the\n * backend (SeriesTicketDTO), unlike event tickets which have visibility,\n * salesChannel, remainingCapacity, etc.\n */\nfunction mapSeriesTicketAvailability(status: string): string {\n switch (status) {\n case 'available':\n return 'https://schema.org/InStock';\n case 'sold_out':\n return 'https://schema.org/SoldOut';\n case 'coming_soon':\n return 'https://schema.org/PreOrder';\n case 'ended':\n return 'https://schema.org/SoldOut';\n default:\n return 'https://schema.org/InStock';\n }\n}\n\n/**\n * Build offers from series occurrence tickets.\n *\n * Series tickets have a simpler shape than event tickets: { name, price, status }.\n * Uses AggregateOffer for multiple price points, Offer for single price.\n */\nfunction buildSeriesOccurrenceOffers(\n tickets: Array<{ name: string; price: number; status: string }> | undefined,\n occurrenceStatus: string | undefined,\n): Record<string, unknown> | null {\n if (!tickets || tickets.length === 0) return null;\n\n // Filter out sold_out and ended tickets for offer building\n const activeTickets = tickets.filter(\n (t) => t.status !== 'sold_out' && t.status !== 'ended',\n );\n\n // If all tickets are sold out / ended, report SoldOut with the price range\n if (activeTickets.length === 0) {\n const prices = tickets.map((t) => t.price).filter((p) => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: 'https://schema.org/SoldOut',\n };\n }\n\n const prices = activeTickets.map((t) => t.price).filter((p) => !isNaN(p));\n if (prices.length === 0) return null;\n\n const lowPrice = Math.min(...prices);\n const highPrice = Math.max(...prices);\n\n if (lowPrice !== highPrice) {\n return {\n '@type': 'AggregateOffer',\n lowPrice,\n highPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n }\n\n return {\n '@type': 'Offer',\n price: lowPrice,\n priceCurrency: 'USD',\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\n };\n}\n\nexport function buildSeriesJsonLd(\n options: BuildSeriesJsonLdOptions,\n): Record<string, unknown> | null {\n const { series, baseEventUrl } = options;\n\n // Required fields\n if (!series.title) return null;\n if (!series.occurrences || series.occurrences.length === 0) return null;\n if (!series.venue?.name) return null;\n\n // Build location\n const address = parseAddressToPostal(series.venue.googleLocationNameCache || '');\n const location: Record<string, unknown> = {\n '@type': 'Place',\n name: series.venue.name,\n };\n if (address) {\n location.address = address;\n }\n\n // Build parent EventSeries\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'EventSeries',\n name: series.title,\n location,\n };\n\n // Description: prefer description, fall back to eventSummary\n const description = series.description || series.eventSummary;\n if (description) {\n data.description = description;\n }\n\n // Image\n if (series.image) {\n data.image = [series.image];\n }\n\n // Performers: flat displayName array (different from event builder's nested shape)\n const performers = series.performers?.filter((p) => p.displayName);\n if (performers && performers.length > 0) {\n data.performer = performers.map((p) => ({\n '@type': 'PerformingGroup',\n name: p.displayName,\n }));\n }\n\n // SubEvents: build Event blocks for each occurrence\n const subEvents: Record<string, unknown>[] = [];\n\n for (const occ of series.occurrences) {\n const startDate = formatDateTimeWithOffset(occ.startDateTime, series.timeZone);\n if (!startDate) continue; // Skip occurrences without valid startDate\n\n const subEvent: Record<string, unknown> = {\n '@type': 'Event',\n name: occ.title,\n startDate,\n eventStatus: getEventStatus(occ.status),\n location,\n };\n\n // @id from baseEventUrl\n if (baseEventUrl) {\n subEvent['@id'] = `${baseEventUrl}/${occ.id}-${occ.slug}`;\n }\n\n // endDate\n const endDate = formatDateTimeWithOffset(occ.endDateTime, series.timeZone);\n if (endDate) {\n subEvent.endDate = endDate;\n }\n\n // Offers from simplified ticket DTOs\n const offers = buildSeriesOccurrenceOffers(occ.tickets, occ.status);\n if (offers) {\n subEvent.offers = offers;\n }\n\n subEvents.push(subEvent);\n }\n\n // If all occurrences were skipped, return null\n if (subEvents.length === 0) return null;\n\n data.subEvent = subEvents;\n\n return data;\n}\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\n/**\n * Pure function that builds Festival JSON-LD structured data for event collections.\n *\n * Collections use schema.org Festival type with subEvent array containing\n * individual Event blocks. Location is included only when all events share\n * the same venue. Date range is included only when dateRange is not null.\n *\n * Google Rich Results Test does NOT support Festival directly, but\n * the individual subEvent Event blocks can qualify for rich results.\n */\n\nimport type { CollectionJsonLdInput } from './types';\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\n\nexport interface BuildCollectionJsonLdOptions {\n collection: CollectionJsonLdInput;\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\"\n defaultTimeZone?: string; // fallback timezone, defaults to getDefaultTimezone()\n}\n\nexport function buildCollectionJsonLd(\n options: BuildCollectionJsonLdOptions,\n): Record<string, unknown> | null {\n const {\n collection,\n baseEventUrl,\n defaultTimeZone = getDefaultTimezone(),\n } = options;\n\n // Required fields\n if (!collection.collectionTitle) return null;\n if (!collection.events || collection.events.length === 0) return null;\n\n // Build parent Festival\n const data: Record<string, unknown> = {\n '@context': 'https://schema.org',\n '@type': 'Festival',\n name: collection.collectionTitle,\n };\n\n // Description: prefer description, fall back to summary\n const description = collection.description || collection.summary;\n if (description) {\n data.description = description;\n }\n\n // Cover image\n if (collection.coverImage) {\n data.image = [collection.coverImage];\n }\n\n // Date range: include only when dateRange is not null and has values\n if (collection.dateRange) {\n const startDate = formatDateTimeWithOffset(\n collection.dateRange.startDate,\n defaultTimeZone,\n );\n const endDate = formatDateTimeWithOffset(\n collection.dateRange.endDate,\n defaultTimeZone,\n );\n if (startDate) {\n data.startDate = startDate;\n }\n if (endDate) {\n data.endDate = endDate;\n }\n }\n\n // Location: include only when all events share the same venue\n const uniqueVenueIds = new Set(\n collection.events\n .map((e) => e.venueId)\n .filter((id): id is number => id !== undefined && id !== 0),\n );\n\n if (uniqueVenueIds.size === 1) {\n // All events share the same venue; use the first event's venueName\n const venueEvent = collection.events.find(\n (e) => e.venueId && e.venueId !== 0 && e.venueName,\n );\n if (venueEvent?.venueName) {\n data.location = {\n '@type': 'Place',\n name: venueEvent.venueName,\n };\n }\n }\n // If multiple venueIds or zero venueIds: OMIT location entirely\n\n // SubEvents: build Event blocks for each collection event\n const subEvents: Record<string, unknown>[] = [];\n\n for (const event of collection.events) {\n const startDate = formatDateTimeWithOffset(event.startDateTime, defaultTimeZone);\n if (!startDate) continue; // Skip events without valid startDate\n\n const subEvent: Record<string, unknown> = {\n '@type': 'Event',\n name: event.title,\n startDate,\n eventStatus: getEventStatus(event.status),\n };\n\n // Location: include venue name and address if available\n if (event.venueName) {\n const subLocation: Record<string, unknown> = {\n '@type': 'Place',\n name: event.venueName,\n };\n if (event.venueAddress) {\n const address = parseAddressToPostal(event.venueAddress);\n if (address) {\n subLocation.address = address;\n }\n }\n subEvent.location = subLocation;\n }\n\n // @id from baseEventUrl\n if (baseEventUrl && event.slug) {\n subEvent['@id'] = `${baseEventUrl}/${event.id}-${event.slug}`;\n }\n\n // endDate\n const endDate = formatDateTimeWithOffset(event.endDateTime, defaultTimeZone);\n if (endDate) {\n subEvent.endDate = endDate;\n }\n\n // Image\n if (event.image) {\n subEvent.image = [event.image];\n }\n\n subEvents.push(subEvent);\n }\n\n // If all events were skipped, return null\n if (subEvents.length === 0) return null;\n\n data.subEvent = subEvents;\n\n return data;\n}\n","/**\n * Text utility functions: generateSlug, validateNegativeNumber\n *\n * classNames and truncateTitle re-exported from @getmicdrop/svelte-components\n * via ./utils.js for backward compatibility with existing consumers.\n */\n\n/**\n * Generate a URL-friendly slug from a string\n */\nexport function generateSlug(text: string): string {\n if (!text) return '';\n\n return text\n .toLowerCase()\n .trim()\n .replace(/[^\\w\\s-]/g, '')\n .replace(/\\s+/g, '-')\n .replace(/--+/g, '-')\n .replace(/^-+|-+$/g, '');\n}\n\n/**\n * Validate that a value is a positive number\n * Returns an error message string, or empty string if valid\n */\nexport function validateNegativeNumber(\n value: string | number | null | undefined\n): string {\n if (value === null || value === undefined || value === '') {\n return 'Must be greater than zero.';\n }\n\n const stringValue = String(value).trim();\n\n if (\n stringValue.includes('--') ||\n stringValue.includes('++') ||\n stringValue.match(/^-+$/) ||\n stringValue.match(/^\\++$/)\n ) {\n return 'Please enter a valid number';\n }\n\n if (stringValue.match(/^-?\\d+-$/)) {\n return 'Please enter a valid number';\n }\n\n if ((stringValue.match(/\\./g) || []).length > 1) {\n return 'Please enter a valid number';\n }\n\n const numValue = Number(value);\n if (isNaN(numValue)) {\n return 'Please enter a valid number';\n }\n\n if (numValue < 0 || numValue === 0) {\n return 'Price must be greater than zero.';\n }\n\n return '';\n}\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\n/**\n * HostSeoController -- manages JSON-LD structured data, canonical URLs,\n * OG tags, meta descriptions, and page title injection into venue pages'\n * <head> when the Micdrop calendar widget is embedded.\n *\n * Follows a save-replace-restore pattern to be a \"good citizen\" on venue pages:\n * idle -> listing -> entity -> listing -> ... -> destroyed\n *\n * Uses textContent (never innerHTML) for JSON-LD script tags to prevent XSS.\n */\n\nimport type { HostSeoControllerOptions } from './types';\nimport { buildEventJsonLd } from './buildEventJsonLd';\nimport { buildSeriesJsonLd } from './buildSeriesJsonLd';\nimport { buildCollectionJsonLd } from './buildCollectionJsonLd';\nimport { generateSlug } from '$lib/utils/textUtils';\n\ntype ControllerState = 'idle' | 'listing' | 'entity' | 'destroyed';\n\ninterface SavedOriginals {\n title: string;\n canonicalHref: string | null;\n ogTitle: string | null;\n ogDescription: string | null;\n ogImage: string | null;\n ogUrl: string | null;\n ogType: string | null;\n metaDescription: string | null;\n}\n\n// Minimal event shape for listing mode\ninterface ListingEvent {\n id: number | string;\n name: string;\n slug?: string;\n startDateTime?: string;\n image?: string;\n}\n\n// Venue data passed at runtime\ninterface VenueData {\n name: string;\n address?: string;\n googleLocationNameCache?: string;\n timeZone?: string;\n}\n\nexport class HostSeoController {\n /**\n * Single active instance. A venue page that mounts >1 calendar embed\n * (e.g. \"upcoming shows\" + \"this weekend\") will see the *later*\n * controller take over `<head>` — the earlier one is destroyed.\n *\n * KNOWN LIMITATION: multi-embed pages cannot run two SEO controllers\n * in parallel because they would both fight to own `<title>`/OG/etc.\n * Pass `data-inject-seo=\"false\"` on all but one embed to opt out.\n */\n static _activeInstance: HostSeoController | null = null;\n\n private _state: ControllerState = 'idle';\n private _venueName: string;\n private _venueAddress: string | undefined;\n private _venueTimeZone: string;\n private _baseUrl: string;\n private _originals: SavedOriginals | null = null;\n private _originalsSaved = false;\n private _events: ListingEvent[] = [];\n\n constructor(options: HostSeoControllerOptions) {\n // SSR guard\n if (typeof document === 'undefined') return;\n\n // If another instance exists, destroy it first.\n if (\n HostSeoController._activeInstance &&\n HostSeoController._activeInstance !== this\n ) {\n HostSeoController._activeInstance.destroy();\n }\n\n this._venueName = options.venueName;\n this._venueAddress = options.venueAddress;\n this._venueTimeZone = options.venueTimeZone || getDefaultTimezone();\n this._baseUrl =\n options.baseUrl || window.location.origin + window.location.pathname;\n\n this._saveOriginals();\n\n HostSeoController._activeInstance = this;\n }\n\n // ---------------------------------------------------------------------------\n // Private helpers\n // ---------------------------------------------------------------------------\n\n private _saveOriginals(): void {\n if (this._originalsSaved) return;\n\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\n const ogTitle = document.querySelector('meta[property=\"og:title\"]') as HTMLMetaElement | null;\n const ogDescription = document.querySelector('meta[property=\"og:description\"]') as HTMLMetaElement | null;\n const ogImage = document.querySelector('meta[property=\"og:image\"]') as HTMLMetaElement | null;\n const ogUrl = document.querySelector('meta[property=\"og:url\"]') as HTMLMetaElement | null;\n const ogType = document.querySelector('meta[property=\"og:type\"]') as HTMLMetaElement | null;\n const metaDescription = document.querySelector('meta[name=\"description\"]') as HTMLMetaElement | null;\n\n this._originals = {\n title: document.title,\n canonicalHref: canonical?.getAttribute('href') ?? null,\n ogTitle: ogTitle?.getAttribute('content') ?? null,\n ogDescription: ogDescription?.getAttribute('content') ?? null,\n ogImage: ogImage?.getAttribute('content') ?? null,\n ogUrl: ogUrl?.getAttribute('content') ?? null,\n ogType: ogType?.getAttribute('content') ?? null,\n metaDescription: metaDescription?.getAttribute('content') ?? null,\n };\n\n this._originalsSaved = true;\n }\n\n private _injectJsonLd(data: Record<string, unknown>): void {\n if (typeof document === 'undefined') return;\n\n // Remove any existing micdrop JSON-LD\n document.querySelectorAll('script[data-micdrop=\"jsonld\"]').forEach((el) => el.remove());\n\n const script = document.createElement('script');\n script.setAttribute('type', 'application/ld+json');\n script.setAttribute('data-micdrop', 'jsonld');\n // Use textContent (NOT innerHTML) to prevent XSS\n script.textContent = JSON.stringify(data);\n document.head.appendChild(script);\n }\n\n private _setMeta(property: string, content: string): void {\n if (typeof document === 'undefined') return;\n\n if (property.startsWith('og:')) {\n // OG tags use property attribute\n let el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\n if (el) {\n el.setAttribute('content', content);\n } else {\n el = document.createElement('meta');\n el.setAttribute('property', property);\n el.setAttribute('content', content);\n el.setAttribute('data-micdrop', 'og');\n document.head.appendChild(el);\n }\n } else {\n // Standard meta tags use name attribute (e.g., description)\n let el = document.querySelector(`meta[name=\"${property}\"]`) as HTMLMetaElement | null;\n if (el) {\n el.setAttribute('content', content);\n } else {\n el = document.createElement('meta');\n el.setAttribute('name', property);\n el.setAttribute('content', content);\n el.setAttribute('data-micdrop', 'meta');\n document.head.appendChild(el);\n }\n }\n }\n\n private _setCanonical(url: string): void {\n if (typeof document === 'undefined') return;\n\n let el = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\n if (el) {\n el.setAttribute('href', url);\n } else {\n el = document.createElement('link');\n el.setAttribute('rel', 'canonical');\n el.setAttribute('href', url);\n el.setAttribute('data-micdrop', 'canonical');\n document.head.appendChild(el);\n }\n }\n\n private _removeAllInjected(): void {\n if (typeof document === 'undefined') return;\n document.querySelectorAll('[data-micdrop]').forEach((el) => el.remove());\n }\n\n private _restoreOriginals(): void {\n if (typeof document === 'undefined' || !this._originals) return;\n\n // Restore title\n document.title = this._originals.title;\n\n // Restore canonical\n if (this._originals.canonicalHref !== null) {\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\n if (canonical) {\n canonical.setAttribute('href', this._originals.canonicalHref);\n }\n }\n\n // Restore OG tags\n this._restoreMetaProperty('og:title', this._originals.ogTitle);\n this._restoreMetaProperty('og:description', this._originals.ogDescription);\n this._restoreMetaProperty('og:image', this._originals.ogImage);\n this._restoreMetaProperty('og:url', this._originals.ogUrl);\n this._restoreMetaProperty('og:type', this._originals.ogType);\n\n // Restore meta description\n this._restoreMetaName('description', this._originals.metaDescription);\n\n this._originalsSaved = false;\n }\n\n private _restoreMetaProperty(property: string, originalContent: string | null): void {\n const el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\n if (originalContent !== null && el) {\n // Had original value -- restore it\n el.setAttribute('content', originalContent);\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\n // We created it -- remove it\n el.remove();\n }\n }\n\n private _restoreMetaName(name: string, originalContent: string | null): void {\n const el = document.querySelector(`meta[name=\"${name}\"]`) as HTMLMetaElement | null;\n if (originalContent !== null && el) {\n el.setAttribute('content', originalContent);\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\n el.remove();\n }\n }\n\n private _buildItemList(events: ListingEvent[]): Record<string, unknown> {\n return {\n '@context': 'https://schema.org',\n '@type': 'ItemList',\n itemListElement: events.map((event, i) => {\n const slug = event.slug || generateSlug(event.name);\n return {\n '@type': 'ListItem',\n position: i + 1,\n url: `${this._baseUrl}?e=${event.id}-${slug}`,\n };\n }),\n };\n }\n\n private _buildEntityUrl(type: string, id: string | number, slug: string): string {\n const prefix = type === 'event' ? 'e' : type === 'series' ? 's' : 'c';\n return `${this._baseUrl}?${prefix}=${id}-${slug}`;\n }\n\n // ---------------------------------------------------------------------------\n // Public API\n // ---------------------------------------------------------------------------\n\n /**\n * Called when events finish loading from the API.\n * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title.\n */\n onEventsLoaded(\n events: ListingEvent[],\n venue?: VenueData,\n ): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n // Update venue info from parameter if provided\n if (venue) {\n this._venueName = venue.name || this._venueName;\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\n if (venue.timeZone) {\n this._venueTimeZone = venue.timeZone;\n }\n }\n\n // Store events for later use (re-injection on back to listing)\n this._events = events;\n\n // Build and inject ItemList JSON-LD\n const itemList = this._buildItemList(events);\n this._injectJsonLd(itemList);\n\n this._state = 'listing';\n }\n\n /**\n * Called when the user selects an event, series, or collection.\n * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates\n * all head tags (canonical, OG, meta description, title).\n */\n onEntitySelected(\n type: 'event' | 'series' | 'collection',\n data: any,\n venue?: VenueData,\n ): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n // Save originals if not already saved\n this._saveOriginals();\n\n // Update venue info from parameter if provided\n if (venue) {\n this._venueName = venue.name || this._venueName;\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\n if (venue.timeZone) {\n this._venueTimeZone = venue.timeZone;\n }\n }\n\n let jsonLd: Record<string, unknown> | null = null;\n let entityName = '';\n let entityDescription = '';\n let entityImage = '';\n let entityUrl = '';\n\n if (type === 'event') {\n const slug = data.slug || generateSlug(data.name || '');\n entityUrl = this._buildEntityUrl('event', data.id || data.eventID || '', slug);\n\n jsonLd = buildEventJsonLd({\n event: data,\n venue: {\n name: this._venueName,\n googleLocationNameCache: this._venueAddress,\n },\n venueTimeZone: this._venueTimeZone,\n eventUrl: entityUrl,\n });\n\n entityName = data.name || '';\n entityDescription = data.description || data.eventSummary || '';\n entityImage = data.image || data.coverImage || data.ShowImage || '';\n } else if (type === 'series') {\n const slug = data.slug || generateSlug(data.title || '');\n entityUrl = this._buildEntityUrl('series', data.id || '', slug);\n\n jsonLd = buildSeriesJsonLd({\n series: {\n ...data,\n venue: {\n name: this._venueName,\n googleLocationNameCache: this._venueAddress,\n },\n timeZone: this._venueTimeZone,\n },\n baseEventUrl: entityUrl,\n });\n\n entityName = data.title || '';\n entityDescription = data.description || data.eventSummary || '';\n entityImage = data.image || '';\n } else if (type === 'collection') {\n const slug = data.slug || generateSlug(data.collectionTitle || '');\n entityUrl = this._buildEntityUrl('collection', data.id || '', slug);\n\n jsonLd = buildCollectionJsonLd({\n collection: data,\n baseEventUrl: entityUrl,\n defaultTimeZone: this._venueTimeZone,\n });\n\n entityName = data.collectionTitle || '';\n entityDescription = data.description || data.summary || '';\n entityImage = data.coverImage || '';\n }\n\n // Inject JSON-LD (replaces existing micdrop JSON-LD)\n if (jsonLd) {\n this._injectJsonLd(jsonLd);\n }\n\n // Update canonical URL\n if (entityUrl) {\n this._setCanonical(entityUrl);\n }\n\n // Update OG tags\n if (entityName) {\n this._setMeta('og:title', entityName);\n }\n if (entityDescription) {\n this._setMeta('og:description', entityDescription);\n }\n if (entityImage) {\n this._setMeta('og:image', entityImage);\n }\n if (entityUrl) {\n this._setMeta('og:url', entityUrl);\n }\n this._setMeta('og:type', 'website');\n\n // Twitter Card: large image preview\n this._setMeta('twitter:card', 'summary_large_image');\n\n // Update meta description\n if (entityDescription) {\n this._setMeta('description', entityDescription);\n }\n\n // Update page title\n if (entityName) {\n document.title = `${entityName} | ${this._venueName}`;\n }\n\n this._state = 'entity';\n }\n\n /**\n * Called when the user navigates back to the event listing.\n * Restores original head elements and re-injects ItemList JSON-LD.\n */\n onBackToListing(): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n // Restore all originals\n this._restoreOriginals();\n\n // Remove any remaining injected elements\n this._removeAllInjected();\n\n // Re-inject ItemList JSON-LD if events are available\n if (this._events.length > 0) {\n const itemList = this._buildItemList(this._events);\n this._injectJsonLd(itemList);\n }\n\n // Re-save originals since we just restored them\n this._saveOriginals();\n\n this._state = 'listing';\n }\n\n /**\n * Called when the widget is destroyed. Removes all injected elements\n * and restores originals. Clears the static active instance.\n */\n destroy(): void {\n if (typeof document === 'undefined') return;\n if (this._state === 'destroyed') return;\n\n this._removeAllInjected();\n this._restoreOriginals();\n\n if (HostSeoController._activeInstance === this) {\n HostSeoController._activeInstance = null;\n }\n\n this._state = 'destroyed';\n }\n}\n"],"names":["getDefaultTimezone","formatDateTimeWithOffset","isoString","tz","date","parts","get","type","p","year","month","day","hour","minute","second","tzOffset","offset","match","sign","hours","mins","parseAddressToPostal","addressStr","stateZipMatch","getEventStatus","rawStatus","status","getTicketAvailability","ticket","now","salesBegin","salesEnd","buildOffers","tickets","url","publicTickets","t","visibility","isVisible","isPublic","prices","lowPrice","highPrice","buildPerformers","performers","confirmed","isConfirmed","isNotHidden","profile","hasName","buildEventJsonLd","options","event","venue","venueTimeZone","eventUrl","organizer","address","startDate","data","endDate","description","imageUrl","offers","org","mapSeriesTicketAvailability","buildSeriesOccurrenceOffers","occurrenceStatus","activeTickets","buildSeriesJsonLd","series","baseEventUrl","location","subEvents","occ","subEvent","buildCollectionJsonLd","collection","defaultTimeZone","e","id","venueEvent","subLocation","generateSlug","text","_HostSeoController","canonical","ogTitle","ogDescription","ogImage","ogUrl","ogType","metaDescription","el","script","property","content","originalContent","name","events","i","slug","prefix","itemList","jsonLd","entityName","entityDescription","entityImage","entityUrl","HostSeoController"],"mappings":"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","x_google_ignoreList":[0]}
|
|
1
|
+
{"version":3,"file":"seo.cjs","sources":["../../node_modules/@getmicdrop/svelte-components/dist/utils/timezones.js","../../src/lib/seo/helpers.ts","../../src/lib/seo/buildEventJsonLd.ts","../../src/lib/seo/buildSeriesJsonLd.ts","../../src/lib/seo/buildCollectionJsonLd.ts","../../src/lib/utils/textUtils.ts","../../src/lib/seo/HostSeoController.ts"],"sourcesContent":["/**\n * Timezone utility helpers.\n * Canonical source for timezone defaults and validation.\n */\n/**\n * Returns the default timezone for Micdrop, branded as `IANATimezone`.\n * Used when no explicit timezone is provided to date formatters. Bypass\n * call sites that pass `'America/Los_Angeles'` literally are red-squiggle\n * type errors at canonicals that take `IANATimezone` — the fix is to\n * thread `getDefaultTimezone()` through.\n *\n * @returns Branded IANA timezone (`'America/Los_Angeles'`)\n *\n * @example\n * formatDate(new Date(), {}, 'en-US', getDefaultTimezone())\n */\nexport function getDefaultTimezone() {\n return 'America/Los_Angeles';\n}\n/**\n * Validates an IANA timezone string.\n * Uses Intl.DateTimeFormat to test if the timezone is recognized.\n *\n * @param tz - Timezone string to validate\n * @returns true if valid IANA timezone, false otherwise\n *\n * @example\n * isValidTimezone('America/Los_Angeles') // true\n * isValidTimezone('Invalid/Zone') // false\n */\nexport function isValidTimezone(tz) {\n try {\n new Intl.DateTimeFormat('en-US', { timeZone: tz });\n return true;\n }\n catch {\n return false;\n }\n}\n","/**\r\n * Shared helper utilities for JSON-LD builders.\r\n *\r\n * All functions are pure (no DOM, no Svelte, no side effects).\r\n * Extracted verbatim from EventStructuredData.svelte.\r\n */\r\n\r\nimport type { EventTicketInput, EventPerformerInput } from './types';\r\n\r\n/**\r\n * Format datetime to ISO-8601 with timezone offset.\r\n * Google requires: \"2025-07-21T19:00:00-05:00\"\r\n *\r\n * Extracted from EventStructuredData.svelte lines 25-68.\r\n */\r\nexport function formatDateTimeWithOffset(\r\n isoString: string | null | undefined,\r\n tz: string,\r\n): string | null {\r\n if (!isoString) return null;\r\n\r\n const date = new Date(isoString);\r\n if (isNaN(date.getTime())) return null;\r\n\r\n // Get the offset in the target timezone\r\n const formatter = new Intl.DateTimeFormat('en-US', {\r\n timeZone: tz,\r\n year: 'numeric',\r\n month: '2-digit',\r\n day: '2-digit',\r\n hour: '2-digit',\r\n minute: '2-digit',\r\n second: '2-digit',\r\n hour12: false,\r\n timeZoneName: 'shortOffset',\r\n });\r\n\r\n const parts = formatter.formatToParts(date);\r\n const get = (type: string) => parts.find((p) => p.type === type)?.value || '';\r\n\r\n const year = get('year');\r\n const month = get('month');\r\n const day = get('day');\r\n const hour = get('hour');\r\n const minute = get('minute');\r\n const second = get('second');\r\n const tzOffset = get('timeZoneName'); // e.g., \"GMT-5\" or \"GMT+2\"\r\n\r\n // Convert \"GMT-5\" to \"-05:00\" format\r\n let offset = '+00:00';\r\n if (tzOffset) {\r\n const match = tzOffset.match(/GMT([+-])(\\d+)(?::(\\d+))?/);\r\n if (match) {\r\n const sign = match[1];\r\n const hours = match[2].padStart(2, '0');\r\n const mins = match[3] || '00';\r\n offset = `${sign}${hours}:${mins}`;\r\n }\r\n }\r\n\r\n return `${year}-${month}-${day}T${hour}:${minute}:${second}${offset}`;\r\n}\r\n\r\n/**\r\n * Parse address string into PostalAddress components.\r\n * Input: \"7702 Santa Monica Blvd, West Hollywood, CA 90046\"\r\n * Output: { @type, streetAddress, addressLocality, addressRegion, postalCode, addressCountry }\r\n *\r\n * Extracted from EventStructuredData.svelte lines 75-111.\r\n */\r\nexport function parseAddressToPostal(\r\n addressStr: string | null | undefined,\r\n): Record<string, string> | null {\r\n if (!addressStr) return null;\r\n\r\n const parts = addressStr.split(',').map((p) => p.trim());\r\n\r\n if (parts.length < 2) {\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: addressStr,\r\n addressCountry: 'US',\r\n };\r\n }\r\n\r\n // Check if last part is \"State ZIP\" (e.g., \"CA 90046\")\r\n const lastPart = parts[parts.length - 1];\r\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(?:-\\d{4})?)$/);\r\n\r\n if (stateZipMatch && parts.length >= 3) {\r\n // Standard US address format\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: parts.slice(0, -2).join(', '),\r\n addressLocality: parts[parts.length - 2],\r\n addressRegion: stateZipMatch[1],\r\n postalCode: stateZipMatch[2],\r\n addressCountry: 'US',\r\n };\r\n }\r\n\r\n // Fallback for non-standard formats\r\n return {\r\n '@type': 'PostalAddress',\r\n streetAddress: parts[0],\r\n addressLocality: parts.slice(1).join(', '),\r\n addressCountry: 'US',\r\n };\r\n}\r\n\r\n/**\r\n * Map event status to schema.org EventStatusType.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 116-132.\r\n */\r\nexport function getEventStatus(rawStatus: string | null | undefined): string {\r\n if (!rawStatus) return 'https://schema.org/EventScheduled';\r\n\r\n const status = rawStatus.toLowerCase();\r\n\r\n if (status === 'cancelled' || status === 'canceled') {\r\n return 'https://schema.org/EventCancelled';\r\n }\r\n if (status === 'postponed') {\r\n return 'https://schema.org/EventPostponed';\r\n }\r\n if (status === 'rescheduled') {\r\n return 'https://schema.org/EventRescheduled';\r\n }\r\n\r\n return 'https://schema.org/EventScheduled';\r\n}\r\n\r\n/**\r\n * Map ticket availability to schema.org ItemAvailability.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 137-157.\r\n */\r\nexport function getTicketAvailability(ticket: EventTicketInput): string {\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n\r\n if (remaining === 0 || ticket.soldOut) {\r\n return 'https://schema.org/SoldOut';\r\n }\r\n\r\n const now = new Date();\r\n const salesBegin =\r\n ticket.salesBegin || ticket.salesStart || ticket.saleBegin || ticket.onSaleStart;\r\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n\r\n if (salesBegin && new Date(salesBegin) > now) {\r\n return 'https://schema.org/PreOrder';\r\n }\r\n\r\n if (salesEnd && new Date(salesEnd) < now) {\r\n return 'https://schema.org/SoldOut';\r\n }\r\n\r\n return 'https://schema.org/InStock';\r\n}\r\n\r\n/**\r\n * Build offers from available tickets.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 162-208.\r\n */\r\nexport function buildOffers(\r\n tickets: EventTicketInput[] | null | undefined,\r\n url?: string,\r\n): Record<string, unknown> | null {\r\n if (!tickets || tickets.length === 0) return null;\r\n\r\n // Filter to public tickets only (exclude door-only and hidden)\r\n const publicTickets = tickets.filter((t) => {\r\n const visibility = t.visibility ?? 0;\r\n const isVisible = visibility === 0 || visibility === 1;\r\n const isPublic = t.salesChannel !== 2;\r\n return isVisible && isPublic;\r\n });\r\n\r\n if (publicTickets.length === 0) return null;\r\n\r\n // Find lowest and highest price\r\n const prices = publicTickets\r\n .map((t) => parseFloat(String(t.price ?? 0)))\r\n .filter((p) => !isNaN(p));\r\n\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n // Use AggregateOffer when there are multiple price points\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice: lowPrice,\r\n highPrice: highPrice,\r\n priceCurrency: 'USD',\r\n availability: getTicketAvailability(publicTickets[0]),\r\n url: url || undefined,\r\n validFrom:\r\n publicTickets[0]?.salesBegin || publicTickets[0]?.salesStart || undefined,\r\n };\r\n }\r\n\r\n // Single price point - use Offer\r\n const ticket = publicTickets[0];\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: getTicketAvailability(ticket),\r\n url: url || undefined,\r\n validFrom: ticket?.salesBegin || ticket?.salesStart || undefined,\r\n };\r\n}\r\n\r\n/**\r\n * Build performer array from event performers.\r\n *\r\n * Extracted from EventStructuredData.svelte lines 213-237.\r\n */\r\nexport function buildPerformers(\r\n performers: EventPerformerInput[] | null | undefined,\r\n): Array<Record<string, string>> | null {\r\n if (!performers || performers.length === 0) return null;\r\n\r\n // Filter to confirmed, visible performers\r\n const confirmed = performers.filter((p) => {\r\n const isConfirmed = p.acceptedState === 2;\r\n const isNotHidden = !p.shouldBeHidden;\r\n const profile = p.RosterPerformer?.User?.performerProfile;\r\n const hasName = profile?.firstName || profile?.lastName || profile?.displayName;\r\n return isConfirmed && isNotHidden && hasName;\r\n });\r\n\r\n if (confirmed.length === 0) return null;\r\n\r\n return confirmed.map((p) => {\r\n const profile = p.RosterPerformer?.User?.performerProfile;\r\n const name =\r\n profile?.displayName ||\r\n [profile?.firstName, profile?.lastName].filter(Boolean).join(' ');\r\n\r\n return {\r\n '@type': 'PerformingGroup',\r\n name: name,\r\n };\r\n });\r\n}\r\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\r\n/**\r\n * Pure function that builds Event JSON-LD structured data for Google Events rich results.\r\n *\r\n * Extracted from EventStructuredData.svelte (lines 242-313).\r\n * Produces identical output to the original $derived.by block.\r\n */\r\n\r\nimport type { EventJsonLdInput, VenueInput, OrganizerInput } from './types';\r\nimport {\r\n formatDateTimeWithOffset,\r\n parseAddressToPostal,\r\n getEventStatus,\r\n buildOffers,\r\n buildPerformers,\r\n} from './helpers';\r\n\r\nexport interface BuildEventJsonLdOptions {\r\n event: EventJsonLdInput;\r\n venue: VenueInput;\r\n venueTimeZone?: string;\r\n eventUrl?: string;\r\n organizer?: OrganizerInput;\r\n}\r\n\r\nexport function buildEventJsonLd(\r\n options: BuildEventJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const {\r\n event,\r\n venue,\r\n venueTimeZone = getDefaultTimezone(),\r\n eventUrl,\r\n organizer,\r\n } = options;\r\n\r\n if (!event) return null;\r\n\r\n const address = venue?.address || venue?.googleLocationNameCache || '';\r\n const startDate = formatDateTimeWithOffset(event.startDateTime, venueTimeZone);\r\n\r\n // Skip if we don't have required fields\r\n if (!event.name || !startDate || !venue?.name) {\r\n return null;\r\n }\r\n\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'Event',\r\n name: event.name,\r\n startDate: startDate,\r\n eventStatus: getEventStatus(event.status),\r\n eventAttendanceMode: 'https://schema.org/OfflineEventAttendanceMode',\r\n location: {\r\n '@type': 'Place',\r\n name: venue.name,\r\n address: parseAddressToPostal(address),\r\n },\r\n };\r\n\r\n // Add endDate if available\r\n const endDate = formatDateTimeWithOffset(event.endDateTime, venueTimeZone);\r\n if (endDate) {\r\n data.endDate = endDate;\r\n }\r\n\r\n // Add description\r\n const description = event.description || event.eventSummary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Add image - Google recommends multiple aspect ratios\r\n const imageUrl = event.image || event.coverImage || event.ShowImage;\r\n if (imageUrl) {\r\n data.image = [imageUrl];\r\n }\r\n\r\n // Add offers from tickets\r\n const offers = buildOffers(event.availableTickets, eventUrl);\r\n if (offers) {\r\n data.offers = offers;\r\n }\r\n\r\n // Add performers\r\n const performers = buildPerformers(event.performers);\r\n if (performers && performers.length > 0) {\r\n data.performer = performers;\r\n }\r\n\r\n // Add organizer if provided\r\n if (organizer?.name) {\r\n const org: Record<string, string> = {\r\n '@type': 'Organization',\r\n name: organizer.name,\r\n };\r\n if (organizer.url) {\r\n org.url = organizer.url;\r\n }\r\n data.organizer = org;\r\n }\r\n\r\n // Add event URL if provided\r\n if (eventUrl) {\r\n data.url = eventUrl;\r\n }\r\n\r\n return data;\r\n}\r\n","/**\r\n * Pure function that builds EventSeries JSON-LD structured data.\r\n *\r\n * Series use schema.org EventSeries type with subEvent array containing\r\n * individual Event blocks for each occurrence. Each subEvent has its own\r\n * offers built from the simplified series ticket DTOs (different shape\r\n * from the event builder's tickets).\r\n *\r\n * Google Rich Results Test does NOT support EventSeries directly, but\r\n * the individual subEvent Event blocks can qualify for rich results.\r\n */\r\n\r\nimport type { SeriesJsonLdInput } from './types';\r\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\r\n\r\nexport interface BuildSeriesJsonLdOptions {\r\n series: SeriesJsonLdInput;\r\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\" -- used to build subEvent @id URLs\r\n}\r\n\r\n/**\r\n * Map a series ticket status string to schema.org ItemAvailability.\r\n *\r\n * Series tickets use a simplified { name, price, status } shape from the\r\n * backend (SeriesTicketDTO), unlike event tickets which have visibility,\r\n * salesChannel, remainingCapacity, etc.\r\n */\r\nfunction mapSeriesTicketAvailability(status: string): string {\r\n switch (status) {\r\n case 'available':\r\n return 'https://schema.org/InStock';\r\n case 'sold_out':\r\n return 'https://schema.org/SoldOut';\r\n case 'coming_soon':\r\n return 'https://schema.org/PreOrder';\r\n case 'ended':\r\n return 'https://schema.org/SoldOut';\r\n default:\r\n return 'https://schema.org/InStock';\r\n }\r\n}\r\n\r\n/**\r\n * Build offers from series occurrence tickets.\r\n *\r\n * Series tickets have a simpler shape than event tickets: { name, price, status }.\r\n * Uses AggregateOffer for multiple price points, Offer for single price.\r\n */\r\nfunction buildSeriesOccurrenceOffers(\r\n tickets: Array<{ name: string; price: number; status: string }> | undefined,\r\n occurrenceStatus: string | undefined,\r\n): Record<string, unknown> | null {\r\n if (!tickets || tickets.length === 0) return null;\r\n\r\n // Filter out sold_out and ended tickets for offer building\r\n const activeTickets = tickets.filter(\r\n (t) => t.status !== 'sold_out' && t.status !== 'ended',\r\n );\r\n\r\n // If all tickets are sold out / ended, report SoldOut with the price range\r\n if (activeTickets.length === 0) {\r\n const prices = tickets.map((t) => t.price).filter((p) => !isNaN(p));\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice,\r\n highPrice,\r\n priceCurrency: 'USD',\r\n availability: 'https://schema.org/SoldOut',\r\n };\r\n }\r\n\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: 'https://schema.org/SoldOut',\r\n };\r\n }\r\n\r\n const prices = activeTickets.map((t) => t.price).filter((p) => !isNaN(p));\r\n if (prices.length === 0) return null;\r\n\r\n const lowPrice = Math.min(...prices);\r\n const highPrice = Math.max(...prices);\r\n\r\n if (lowPrice !== highPrice) {\r\n return {\r\n '@type': 'AggregateOffer',\r\n lowPrice,\r\n highPrice,\r\n priceCurrency: 'USD',\r\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\r\n };\r\n }\r\n\r\n return {\r\n '@type': 'Offer',\r\n price: lowPrice,\r\n priceCurrency: 'USD',\r\n availability: mapSeriesTicketAvailability(activeTickets[0].status),\r\n };\r\n}\r\n\r\nexport function buildSeriesJsonLd(\r\n options: BuildSeriesJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const { series, baseEventUrl } = options;\r\n\r\n // Required fields\r\n if (!series.title) return null;\r\n if (!series.occurrences || series.occurrences.length === 0) return null;\r\n if (!series.venue?.name) return null;\r\n\r\n // Build location\r\n const address = parseAddressToPostal(series.venue.googleLocationNameCache || '');\r\n const location: Record<string, unknown> = {\r\n '@type': 'Place',\r\n name: series.venue.name,\r\n };\r\n if (address) {\r\n location.address = address;\r\n }\r\n\r\n // Build parent EventSeries\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'EventSeries',\r\n name: series.title,\r\n location,\r\n };\r\n\r\n // Description: prefer description, fall back to eventSummary\r\n const description = series.description || series.eventSummary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Image\r\n if (series.image) {\r\n data.image = [series.image];\r\n }\r\n\r\n // Performers: flat displayName array (different from event builder's nested shape)\r\n const performers = series.performers?.filter((p) => p.displayName);\r\n if (performers && performers.length > 0) {\r\n data.performer = performers.map((p) => ({\r\n '@type': 'PerformingGroup',\r\n name: p.displayName,\r\n }));\r\n }\r\n\r\n // SubEvents: build Event blocks for each occurrence\r\n const subEvents: Record<string, unknown>[] = [];\r\n\r\n for (const occ of series.occurrences) {\r\n const startDate = formatDateTimeWithOffset(occ.startDateTime, series.timeZone);\r\n if (!startDate) continue; // Skip occurrences without valid startDate\r\n\r\n const subEvent: Record<string, unknown> = {\r\n '@type': 'Event',\r\n name: occ.title,\r\n startDate,\r\n eventStatus: getEventStatus(occ.status),\r\n location,\r\n };\r\n\r\n // @id from baseEventUrl\r\n if (baseEventUrl) {\r\n subEvent['@id'] = `${baseEventUrl}/${occ.id}-${occ.slug}`;\r\n }\r\n\r\n // endDate\r\n const endDate = formatDateTimeWithOffset(occ.endDateTime, series.timeZone);\r\n if (endDate) {\r\n subEvent.endDate = endDate;\r\n }\r\n\r\n // Offers from simplified ticket DTOs\r\n const offers = buildSeriesOccurrenceOffers(occ.tickets, occ.status);\r\n if (offers) {\r\n subEvent.offers = offers;\r\n }\r\n\r\n subEvents.push(subEvent);\r\n }\r\n\r\n // If all occurrences were skipped, return null\r\n if (subEvents.length === 0) return null;\r\n\r\n data.subEvent = subEvents;\r\n\r\n return data;\r\n}\r\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\r\n/**\r\n * Pure function that builds Festival JSON-LD structured data for event collections.\r\n *\r\n * Collections use schema.org Festival type with subEvent array containing\r\n * individual Event blocks. Location is included only when all events share\r\n * the same venue. Date range is included only when dateRange is not null.\r\n *\r\n * Google Rich Results Test does NOT support Festival directly, but\r\n * the individual subEvent Event blocks can qualify for rich results.\r\n */\r\n\r\nimport type { CollectionJsonLdInput } from './types';\r\nimport { formatDateTimeWithOffset, parseAddressToPostal, getEventStatus } from './helpers';\r\n\r\nexport interface BuildCollectionJsonLdOptions {\r\n collection: CollectionJsonLdInput;\r\n baseEventUrl?: string; // e.g., \"https://get-micdrop.com/e\"\r\n defaultTimeZone?: string; // fallback timezone, defaults to getDefaultTimezone()\r\n}\r\n\r\nexport function buildCollectionJsonLd(\r\n options: BuildCollectionJsonLdOptions,\r\n): Record<string, unknown> | null {\r\n const {\r\n collection,\r\n baseEventUrl,\r\n defaultTimeZone = getDefaultTimezone(),\r\n } = options;\r\n\r\n // Required fields\r\n if (!collection.collectionTitle) return null;\r\n if (!collection.events || collection.events.length === 0) return null;\r\n\r\n // Build parent Festival\r\n const data: Record<string, unknown> = {\r\n '@context': 'https://schema.org',\r\n '@type': 'Festival',\r\n name: collection.collectionTitle,\r\n };\r\n\r\n // Description: prefer description, fall back to summary\r\n const description = collection.description || collection.summary;\r\n if (description) {\r\n data.description = description;\r\n }\r\n\r\n // Cover image\r\n if (collection.coverImage) {\r\n data.image = [collection.coverImage];\r\n }\r\n\r\n // Date range: include only when dateRange is not null and has values\r\n if (collection.dateRange) {\r\n const startDate = formatDateTimeWithOffset(\r\n collection.dateRange.startDate,\r\n defaultTimeZone,\r\n );\r\n const endDate = formatDateTimeWithOffset(\r\n collection.dateRange.endDate,\r\n defaultTimeZone,\r\n );\r\n if (startDate) {\r\n data.startDate = startDate;\r\n }\r\n if (endDate) {\r\n data.endDate = endDate;\r\n }\r\n }\r\n\r\n // Location: include only when all events share the same venue\r\n const uniqueVenueIds = new Set(\r\n collection.events\r\n .map((e) => e.venueId)\r\n .filter((id): id is number => id !== undefined && id !== 0),\r\n );\r\n\r\n if (uniqueVenueIds.size === 1) {\r\n // All events share the same venue; use the first event's venueName\r\n const venueEvent = collection.events.find(\r\n (e) => e.venueId && e.venueId !== 0 && e.venueName,\r\n );\r\n if (venueEvent?.venueName) {\r\n data.location = {\r\n '@type': 'Place',\r\n name: venueEvent.venueName,\r\n };\r\n }\r\n }\r\n // If multiple venueIds or zero venueIds: OMIT location entirely\r\n\r\n // SubEvents: build Event blocks for each collection event\r\n const subEvents: Record<string, unknown>[] = [];\r\n\r\n for (const event of collection.events) {\r\n const startDate = formatDateTimeWithOffset(event.startDateTime, defaultTimeZone);\r\n if (!startDate) continue; // Skip events without valid startDate\r\n\r\n const subEvent: Record<string, unknown> = {\r\n '@type': 'Event',\r\n name: event.title,\r\n startDate,\r\n eventStatus: getEventStatus(event.status),\r\n };\r\n\r\n // Location: include venue name and address if available\r\n if (event.venueName) {\r\n const subLocation: Record<string, unknown> = {\r\n '@type': 'Place',\r\n name: event.venueName,\r\n };\r\n if (event.venueAddress) {\r\n const address = parseAddressToPostal(event.venueAddress);\r\n if (address) {\r\n subLocation.address = address;\r\n }\r\n }\r\n subEvent.location = subLocation;\r\n }\r\n\r\n // @id from baseEventUrl\r\n if (baseEventUrl && event.slug) {\r\n subEvent['@id'] = `${baseEventUrl}/${event.id}-${event.slug}`;\r\n }\r\n\r\n // endDate\r\n const endDate = formatDateTimeWithOffset(event.endDateTime, defaultTimeZone);\r\n if (endDate) {\r\n subEvent.endDate = endDate;\r\n }\r\n\r\n // Image\r\n if (event.image) {\r\n subEvent.image = [event.image];\r\n }\r\n\r\n subEvents.push(subEvent);\r\n }\r\n\r\n // If all events were skipped, return null\r\n if (subEvents.length === 0) return null;\r\n\r\n data.subEvent = subEvents;\r\n\r\n return data;\r\n}\r\n","/**\r\n * Text utility functions: generateSlug, validateNegativeNumber\r\n *\r\n * classNames and truncateTitle re-exported from @getmicdrop/svelte-components\r\n * via ./utils.js for backward compatibility with existing consumers.\r\n */\r\n\r\n/**\r\n * Generate a URL-friendly slug from a string\r\n */\r\nexport function generateSlug(text: string): string {\r\n if (!text) return '';\r\n\r\n return text\r\n .toLowerCase()\r\n .trim()\r\n .replace(/[^\\w\\s-]/g, '')\r\n .replace(/\\s+/g, '-')\r\n .replace(/--+/g, '-')\r\n .replace(/^-+|-+$/g, '');\r\n}\r\n\r\n/**\r\n * Validate that a value is a positive number\r\n * Returns an error message string, or empty string if valid\r\n */\r\nexport function validateNegativeNumber(\r\n value: string | number | null | undefined\r\n): string {\r\n if (value === null || value === undefined || value === '') {\r\n return 'Must be greater than zero.';\r\n }\r\n\r\n const stringValue = String(value).trim();\r\n\r\n if (\r\n stringValue.includes('--') ||\r\n stringValue.includes('++') ||\r\n stringValue.match(/^-+$/) ||\r\n stringValue.match(/^\\++$/)\r\n ) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if (stringValue.match(/^-?\\d+-$/)) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if ((stringValue.match(/\\./g) || []).length > 1) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n const numValue = Number(value);\r\n if (isNaN(numValue)) {\r\n return 'Please enter a valid number';\r\n }\r\n\r\n if (numValue < 0 || numValue === 0) {\r\n return 'Price must be greater than zero.';\r\n }\r\n\r\n return '';\r\n}\r\n","import { getDefaultTimezone } from '@getmicdrop/svelte-components/utils/timezones';\r\n/**\r\n * HostSeoController -- manages JSON-LD structured data, canonical URLs,\r\n * OG tags, meta descriptions, and page title injection into venue pages'\r\n * <head> when the Micdrop calendar widget is embedded.\r\n *\r\n * Follows a save-replace-restore pattern to be a \"good citizen\" on venue pages:\r\n * idle -> listing -> entity -> listing -> ... -> destroyed\r\n *\r\n * Uses textContent (never innerHTML) for JSON-LD script tags to prevent XSS.\r\n */\r\n\r\nimport type { HostSeoControllerOptions } from './types';\r\nimport { buildEventJsonLd } from './buildEventJsonLd';\r\nimport { buildSeriesJsonLd } from './buildSeriesJsonLd';\r\nimport { buildCollectionJsonLd } from './buildCollectionJsonLd';\r\nimport { generateSlug } from '$lib/utils/textUtils';\r\n\r\ntype ControllerState = 'idle' | 'listing' | 'entity' | 'destroyed';\r\n\r\ninterface SavedOriginals {\r\n title: string;\r\n canonicalHref: string | null;\r\n ogTitle: string | null;\r\n ogDescription: string | null;\r\n ogImage: string | null;\r\n ogUrl: string | null;\r\n ogType: string | null;\r\n metaDescription: string | null;\r\n}\r\n\r\n// Minimal event shape for listing mode\r\ninterface ListingEvent {\r\n id: number | string;\r\n name: string;\r\n slug?: string;\r\n startDateTime?: string;\r\n image?: string;\r\n}\r\n\r\n// Venue data passed at runtime\r\ninterface VenueData {\r\n name: string;\r\n address?: string;\r\n googleLocationNameCache?: string;\r\n timeZone?: string;\r\n}\r\n\r\nexport class HostSeoController {\r\n /**\r\n * Single active instance. A venue page that mounts >1 calendar embed\r\n * (e.g. \"upcoming shows\" + \"this weekend\") will see the *later*\r\n * controller take over `<head>` — the earlier one is destroyed.\r\n *\r\n * KNOWN LIMITATION: multi-embed pages cannot run two SEO controllers\r\n * in parallel because they would both fight to own `<title>`/OG/etc.\r\n * Pass `data-inject-seo=\"false\"` on all but one embed to opt out.\r\n */\r\n static _activeInstance: HostSeoController | null = null;\r\n\r\n private _state: ControllerState = 'idle';\r\n private _venueName: string;\r\n private _venueAddress: string | undefined;\r\n private _venueTimeZone: string;\r\n private _baseUrl: string;\r\n private _originals: SavedOriginals | null = null;\r\n private _originalsSaved = false;\r\n private _events: ListingEvent[] = [];\r\n\r\n constructor(options: HostSeoControllerOptions) {\r\n // SSR guard\r\n if (typeof document === 'undefined') return;\r\n\r\n // If another instance exists, destroy it first.\r\n if (\r\n HostSeoController._activeInstance &&\r\n HostSeoController._activeInstance !== this\r\n ) {\r\n HostSeoController._activeInstance.destroy();\r\n }\r\n\r\n this._venueName = options.venueName;\r\n this._venueAddress = options.venueAddress;\r\n this._venueTimeZone = options.venueTimeZone || getDefaultTimezone();\r\n this._baseUrl =\r\n options.baseUrl || window.location.origin + window.location.pathname;\r\n\r\n this._saveOriginals();\r\n\r\n HostSeoController._activeInstance = this;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Private helpers\r\n // ---------------------------------------------------------------------------\r\n\r\n private _saveOriginals(): void {\r\n if (this._originalsSaved) return;\r\n\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n const ogTitle = document.querySelector('meta[property=\"og:title\"]') as HTMLMetaElement | null;\r\n const ogDescription = document.querySelector('meta[property=\"og:description\"]') as HTMLMetaElement | null;\r\n const ogImage = document.querySelector('meta[property=\"og:image\"]') as HTMLMetaElement | null;\r\n const ogUrl = document.querySelector('meta[property=\"og:url\"]') as HTMLMetaElement | null;\r\n const ogType = document.querySelector('meta[property=\"og:type\"]') as HTMLMetaElement | null;\r\n const metaDescription = document.querySelector('meta[name=\"description\"]') as HTMLMetaElement | null;\r\n\r\n this._originals = {\r\n title: document.title,\r\n canonicalHref: canonical?.getAttribute('href') ?? null,\r\n ogTitle: ogTitle?.getAttribute('content') ?? null,\r\n ogDescription: ogDescription?.getAttribute('content') ?? null,\r\n ogImage: ogImage?.getAttribute('content') ?? null,\r\n ogUrl: ogUrl?.getAttribute('content') ?? null,\r\n ogType: ogType?.getAttribute('content') ?? null,\r\n metaDescription: metaDescription?.getAttribute('content') ?? null,\r\n };\r\n\r\n this._originalsSaved = true;\r\n }\r\n\r\n private _injectJsonLd(data: Record<string, unknown>): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n // Remove any existing micdrop JSON-LD\r\n document.querySelectorAll('script[data-micdrop=\"jsonld\"]').forEach((el) => el.remove());\r\n\r\n const script = document.createElement('script');\r\n script.setAttribute('type', 'application/ld+json');\r\n script.setAttribute('data-micdrop', 'jsonld');\r\n // Use textContent (NOT innerHTML) to prevent XSS\r\n script.textContent = JSON.stringify(data);\r\n document.head.appendChild(script);\r\n }\r\n\r\n private _setMeta(property: string, content: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n if (property.startsWith('og:')) {\r\n // OG tags use property attribute\r\n let el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('property', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'og');\r\n document.head.appendChild(el);\r\n }\r\n } else {\r\n // Standard meta tags use name attribute (e.g., description)\r\n let el = document.querySelector(`meta[name=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (el) {\r\n el.setAttribute('content', content);\r\n } else {\r\n el = document.createElement('meta');\r\n el.setAttribute('name', property);\r\n el.setAttribute('content', content);\r\n el.setAttribute('data-micdrop', 'meta');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n }\r\n\r\n private _setCanonical(url: string): void {\r\n if (typeof document === 'undefined') return;\r\n\r\n let el = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (el) {\r\n el.setAttribute('href', url);\r\n } else {\r\n el = document.createElement('link');\r\n el.setAttribute('rel', 'canonical');\r\n el.setAttribute('href', url);\r\n el.setAttribute('data-micdrop', 'canonical');\r\n document.head.appendChild(el);\r\n }\r\n }\r\n\r\n private _removeAllInjected(): void {\r\n if (typeof document === 'undefined') return;\r\n document.querySelectorAll('[data-micdrop]').forEach((el) => el.remove());\r\n }\r\n\r\n private _restoreOriginals(): void {\r\n if (typeof document === 'undefined' || !this._originals) return;\r\n\r\n // Restore title\r\n document.title = this._originals.title;\r\n\r\n // Restore canonical\r\n if (this._originals.canonicalHref !== null) {\r\n const canonical = document.querySelector('link[rel=\"canonical\"]') as HTMLLinkElement | null;\r\n if (canonical) {\r\n canonical.setAttribute('href', this._originals.canonicalHref);\r\n }\r\n }\r\n\r\n // Restore OG tags\r\n this._restoreMetaProperty('og:title', this._originals.ogTitle);\r\n this._restoreMetaProperty('og:description', this._originals.ogDescription);\r\n this._restoreMetaProperty('og:image', this._originals.ogImage);\r\n this._restoreMetaProperty('og:url', this._originals.ogUrl);\r\n this._restoreMetaProperty('og:type', this._originals.ogType);\r\n\r\n // Restore meta description\r\n this._restoreMetaName('description', this._originals.metaDescription);\r\n\r\n this._originalsSaved = false;\r\n }\r\n\r\n private _restoreMetaProperty(property: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[property=\"${property}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n // Had original value -- restore it\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n // We created it -- remove it\r\n el.remove();\r\n }\r\n }\r\n\r\n private _restoreMetaName(name: string, originalContent: string | null): void {\r\n const el = document.querySelector(`meta[name=\"${name}\"]`) as HTMLMetaElement | null;\r\n if (originalContent !== null && el) {\r\n el.setAttribute('content', originalContent);\r\n } else if (originalContent === null && el?.hasAttribute('data-micdrop')) {\r\n el.remove();\r\n }\r\n }\r\n\r\n private _buildItemList(events: ListingEvent[]): Record<string, unknown> {\r\n return {\r\n '@context': 'https://schema.org',\r\n '@type': 'ItemList',\r\n itemListElement: events.map((event, i) => {\r\n const slug = event.slug || generateSlug(event.name);\r\n return {\r\n '@type': 'ListItem',\r\n position: i + 1,\r\n url: `${this._baseUrl}?e=${event.id}-${slug}`,\r\n };\r\n }),\r\n };\r\n }\r\n\r\n private _buildEntityUrl(type: string, id: string | number, slug: string): string {\r\n const prefix = type === 'event' ? 'e' : type === 'series' ? 's' : 'c';\r\n return `${this._baseUrl}?${prefix}=${id}-${slug}`;\r\n }\r\n\r\n // ---------------------------------------------------------------------------\r\n // Public API\r\n // ---------------------------------------------------------------------------\r\n\r\n /**\r\n * Called when events finish loading from the API.\r\n * Injects an ItemList JSON-LD for the listing. Does not modify OG/canonical/title.\r\n */\r\n onEventsLoaded(\r\n events: ListingEvent[],\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n if (venue.timeZone) {\r\n this._venueTimeZone = venue.timeZone;\r\n }\r\n }\r\n\r\n // Store events for later use (re-injection on back to listing)\r\n this._events = events;\r\n\r\n // Build and inject ItemList JSON-LD\r\n const itemList = this._buildItemList(events);\r\n this._injectJsonLd(itemList);\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the user selects an event, series, or collection.\r\n * Replaces ItemList JSON-LD with entity-specific JSON-LD and updates\r\n * all head tags (canonical, OG, meta description, title).\r\n */\r\n onEntitySelected(\r\n type: 'event' | 'series' | 'collection',\r\n data: any,\r\n venue?: VenueData,\r\n ): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Save originals if not already saved\r\n this._saveOriginals();\r\n\r\n // Update venue info from parameter if provided\r\n if (venue) {\r\n this._venueName = venue.name || this._venueName;\r\n this._venueAddress = venue.address || venue.googleLocationNameCache || this._venueAddress;\r\n if (venue.timeZone) {\r\n this._venueTimeZone = venue.timeZone;\r\n }\r\n }\r\n\r\n let jsonLd: Record<string, unknown> | null = null;\r\n let entityName = '';\r\n let entityDescription = '';\r\n let entityImage = '';\r\n let entityUrl = '';\r\n\r\n if (type === 'event') {\r\n const slug = data.slug || generateSlug(data.name || '');\r\n entityUrl = this._buildEntityUrl('event', data.id || data.eventID || '', slug);\r\n\r\n jsonLd = buildEventJsonLd({\r\n event: data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n venueTimeZone: this._venueTimeZone,\r\n eventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.name || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || data.coverImage || data.ShowImage || '';\r\n } else if (type === 'series') {\r\n const slug = data.slug || generateSlug(data.title || '');\r\n entityUrl = this._buildEntityUrl('series', data.id || '', slug);\r\n\r\n jsonLd = buildSeriesJsonLd({\r\n series: {\r\n ...data,\r\n venue: {\r\n name: this._venueName,\r\n googleLocationNameCache: this._venueAddress,\r\n },\r\n timeZone: this._venueTimeZone,\r\n },\r\n baseEventUrl: entityUrl,\r\n });\r\n\r\n entityName = data.title || '';\r\n entityDescription = data.description || data.eventSummary || '';\r\n entityImage = data.image || '';\r\n } else if (type === 'collection') {\r\n const slug = data.slug || generateSlug(data.collectionTitle || '');\r\n entityUrl = this._buildEntityUrl('collection', data.id || '', slug);\r\n\r\n jsonLd = buildCollectionJsonLd({\r\n collection: data,\r\n baseEventUrl: entityUrl,\r\n defaultTimeZone: this._venueTimeZone,\r\n });\r\n\r\n entityName = data.collectionTitle || '';\r\n entityDescription = data.description || data.summary || '';\r\n entityImage = data.coverImage || '';\r\n }\r\n\r\n // Inject JSON-LD (replaces existing micdrop JSON-LD)\r\n if (jsonLd) {\r\n this._injectJsonLd(jsonLd);\r\n }\r\n\r\n // Update canonical URL\r\n if (entityUrl) {\r\n this._setCanonical(entityUrl);\r\n }\r\n\r\n // Update OG tags\r\n if (entityName) {\r\n this._setMeta('og:title', entityName);\r\n }\r\n if (entityDescription) {\r\n this._setMeta('og:description', entityDescription);\r\n }\r\n if (entityImage) {\r\n this._setMeta('og:image', entityImage);\r\n }\r\n if (entityUrl) {\r\n this._setMeta('og:url', entityUrl);\r\n }\r\n this._setMeta('og:type', 'website');\r\n\r\n // Twitter Card: large image preview\r\n this._setMeta('twitter:card', 'summary_large_image');\r\n\r\n // Update meta description\r\n if (entityDescription) {\r\n this._setMeta('description', entityDescription);\r\n }\r\n\r\n // Update page title\r\n if (entityName) {\r\n document.title = `${entityName} | ${this._venueName}`;\r\n }\r\n\r\n this._state = 'entity';\r\n }\r\n\r\n /**\r\n * Called when the user navigates back to the event listing.\r\n * Restores original head elements and re-injects ItemList JSON-LD.\r\n */\r\n onBackToListing(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n // Restore all originals\r\n this._restoreOriginals();\r\n\r\n // Remove any remaining injected elements\r\n this._removeAllInjected();\r\n\r\n // Re-inject ItemList JSON-LD if events are available\r\n if (this._events.length > 0) {\r\n const itemList = this._buildItemList(this._events);\r\n this._injectJsonLd(itemList);\r\n }\r\n\r\n // Re-save originals since we just restored them\r\n this._saveOriginals();\r\n\r\n this._state = 'listing';\r\n }\r\n\r\n /**\r\n * Called when the widget is destroyed. Removes all injected elements\r\n * and restores originals. Clears the static active instance.\r\n */\r\n destroy(): void {\r\n if (typeof document === 'undefined') return;\r\n if (this._state === 'destroyed') return;\r\n\r\n this._removeAllInjected();\r\n this._restoreOriginals();\r\n\r\n if (HostSeoController._activeInstance === this) {\r\n HostSeoController._activeInstance = null;\r\n }\r\n\r\n this._state = 'destroyed';\r\n }\r\n}\r\n"],"names":["getDefaultTimezone","formatDateTimeWithOffset","isoString","tz","date","parts","get","type","p","year","month","day","hour","minute","second","tzOffset","offset","match","sign","hours","mins","parseAddressToPostal","addressStr","stateZipMatch","getEventStatus","rawStatus","status","getTicketAvailability","ticket","now","salesBegin","salesEnd","buildOffers","tickets","url","publicTickets","t","visibility","isVisible","isPublic","prices","lowPrice","highPrice","buildPerformers","performers","confirmed","isConfirmed","isNotHidden","profile","hasName","buildEventJsonLd","options","event","venue","venueTimeZone","eventUrl","organizer","address","startDate","data","endDate","description","imageUrl","offers","org","mapSeriesTicketAvailability","buildSeriesOccurrenceOffers","occurrenceStatus","activeTickets","buildSeriesJsonLd","series","baseEventUrl","location","subEvents","occ","subEvent","buildCollectionJsonLd","collection","defaultTimeZone","e","id","venueEvent","subLocation","generateSlug","text","_HostSeoController","canonical","ogTitle","ogDescription","ogImage","ogUrl","ogType","metaDescription","el","script","property","content","originalContent","name","events","i","slug","prefix","itemList","jsonLd","entityName","entityDescription","entityImage","entityUrl","HostSeoController"],"mappings":"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","x_google_ignoreList":[0]}
|