@getmicdrop/venue-calendar 4.0.163 → 4.1.2
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/dist/{CartView-BkRTBDXZ.js → CartView-DYabQwHC.js} +9 -9
- package/dist/CartView-DYabQwHC.js.map +1 -0
- package/dist/{Checkout-C8kEhq77.js → Checkout-C9SOzGlj.js} +9 -9
- package/dist/Checkout-C9SOzGlj.js.map +1 -0
- package/dist/{Checkout-rSZjE7ZX.js → Checkout-DIVV0L0F.js} +7 -7
- package/dist/Checkout-DIVV0L0F.js.map +1 -0
- package/dist/{CheckoutTimer-CCoBtfvN.js → CheckoutTimer-B_SlgvFU.js} +5 -5
- package/dist/CheckoutTimer-B_SlgvFU.js.map +1 -0
- package/dist/{ClockIcon-TC9h_DgF.js → ClockIcon-PHXURS1M.js} +2 -2
- package/dist/ClockIcon-PHXURS1M.js.map +1 -0
- package/dist/{CollectionView-8095UIbe.js → CollectionView-BCA1R0PJ.js} +5 -5
- package/dist/CollectionView-BCA1R0PJ.js.map +1 -0
- package/dist/{Event-D5qSa4qP.js → Event-BErjd7aD.js} +183 -175
- package/dist/Event-BErjd7aD.js.map +1 -0
- package/dist/{EventPage-B0i9SYgi.js → EventPage-DV-MVPHg.js} +8 -8
- package/dist/EventPage-DV-MVPHg.js.map +1 -0
- package/dist/{Heading-BFbWZKav.js → Heading-Ci8L45Wr.js} +2 -2
- package/dist/Heading-Ci8L45Wr.js.map +1 -0
- package/dist/{ScarcityBadge-B1jjs4xL.js → ScarcityBadge-Cdwyg52F.js} +2 -2
- package/dist/ScarcityBadge-Cdwyg52F.js.map +1 -0
- package/dist/{SeriesPage-Q9vQLIEZ.js → SeriesPage-BoA70wuP.js} +5 -5
- package/dist/{SeriesPage-Q9vQLIEZ.js.map → SeriesPage-BoA70wuP.js.map} +1 -1
- package/dist/{Spinner-tiRQ2I2B.js → Spinner-jozFw0lx.js} +2 -2
- package/dist/Spinner-jozFw0lx.js.map +1 -0
- package/dist/{Success-CsIw4bxU.js → Success-HolOJLu3.js} +9 -9
- package/dist/Success-HolOJLu3.js.map +1 -0
- package/dist/{Text-KbMXo7g6.js → Text-BjAFX0Oh.js} +2 -2
- package/dist/Text-BjAFX0Oh.js.map +1 -0
- package/dist/{VenueCalendar-Czx0Kn4d.js → VenueCalendar-BDW-jkLN.js} +16 -15
- package/dist/VenueCalendar-BDW-jkLN.js.map +1 -0
- package/dist/{ViewTicketsEmbed-Bew7UA7B.js → ViewTicketsEmbed-DBkDPXp1.js} +5 -5
- package/dist/ViewTicketsEmbed-DBkDPXp1.js.map +1 -0
- package/dist/api/api/route-manifest.d.ts +35 -8
- package/dist/api/api.cjs.map +1 -1
- package/dist/api/api.mjs.map +1 -1
- package/dist/{es-Czctk5do.js → es-CltuJW7p.js} +2 -1
- package/dist/es-CltuJW7p.js.map +1 -0
- package/dist/{index-HngV5Tmp.js → index-EHtVTS-p.js} +3 -3
- package/dist/index-EHtVTS-p.js.map +1 -0
- package/dist/{modalManager.svelte-JZXVYH3m.js → modalManager.svelte-DmLABgNg.js} +2 -2
- package/dist/modalManager.svelte-DmLABgNg.js.map +1 -0
- package/dist/{promo-dsT5Iw68.js → promo-C9oFaeEp.js} +2 -2
- package/dist/{promo-dsT5Iw68.js.map → promo-C9oFaeEp.js.map} +1 -1
- package/dist/{serverTotals-D5dQ_beP.js → serverTotals-CJHVQkJo.js} +2 -2
- package/dist/{serverTotals-D5dQ_beP.js.map → serverTotals-CJHVQkJo.js.map} +1 -1
- package/dist/{shareCopyFeedback.svelte-CvE7h_Wn.js → shareCopyFeedback.svelte-BvXv8plI.js} +2 -2
- package/dist/shareCopyFeedback.svelte-BvXv8plI.js.map +1 -0
- package/dist/{showcase-state-options-DmmK-lzB.js → showcase-state-options-BZiy68Tm.js} +2 -2
- package/dist/{showcase-state-options-DmmK-lzB.js.map → showcase-state-options-BZiy68Tm.js.map} +1 -1
- package/dist/{transform-CpUt4pIr.js → transform-BH-qIBIv.js} +2 -2
- package/dist/transform-BH-qIBIv.js.map +1 -0
- package/dist/{utm-CfowKK2u.js → utm-B6GLtZhc.js} +2 -2
- package/dist/{utm-CfowKK2u.js.map → utm-B6GLtZhc.js.map} +1 -1
- package/dist/venue-calendar.css +1 -1
- package/dist/venue-calendar.es.js +2 -2
- package/dist/venue-calendar.iife.js +6 -6
- package/dist/venue-calendar.iife.js.map +1 -1
- package/dist/venue-calendar.umd.js +5 -5
- package/dist/venue-calendar.umd.js.map +1 -1
- package/package.json +2 -2
- package/dist/CartView-BkRTBDXZ.js.map +0 -1
- package/dist/Checkout-C8kEhq77.js.map +0 -1
- package/dist/Checkout-rSZjE7ZX.js.map +0 -1
- package/dist/CheckoutTimer-CCoBtfvN.js.map +0 -1
- package/dist/ClockIcon-TC9h_DgF.js.map +0 -1
- package/dist/CollectionView-8095UIbe.js.map +0 -1
- package/dist/Event-D5qSa4qP.js.map +0 -1
- package/dist/EventPage-B0i9SYgi.js.map +0 -1
- package/dist/Heading-BFbWZKav.js.map +0 -1
- package/dist/ScarcityBadge-B1jjs4xL.js.map +0 -1
- package/dist/Spinner-tiRQ2I2B.js.map +0 -1
- package/dist/Success-CsIw4bxU.js.map +0 -1
- package/dist/Text-KbMXo7g6.js.map +0 -1
- package/dist/VenueCalendar-Czx0Kn4d.js.map +0 -1
- package/dist/ViewTicketsEmbed-Bew7UA7B.js.map +0 -1
- package/dist/es-Czctk5do.js.map +0 -1
- package/dist/index-HngV5Tmp.js.map +0 -1
- package/dist/modalManager.svelte-JZXVYH3m.js.map +0 -1
- package/dist/shareCopyFeedback.svelte-CvE7h_Wn.js.map +0 -1
- package/dist/transform-CpUt4pIr.js.map +0 -1
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index-HngV5Tmp.js","sources":["../node_modules/@getmicdrop/svelte-components/dist/primitives/Icons/phosphor/CalendarIcon.svelte","../src/lib/api/cta.ts","../src/lib/public-calendar-flow/defaults.ts","../src/lib/public-calendar-flow/OrderSummary.svelte","../src/lib/public-calendar-flow/i18n/locales/es.js","../src/lib/public-calendar-flow/i18n/index.js"],"sourcesContent":["<script lang=\"ts\">\r\n /**\r\n * Calendar — Phosphor icon (canonical Micdrop icon family).\r\n * Outline by default; pass `weight=\"fill\"` for solid, or let a stateful parent\r\n * (NavItem / Tabs / BottomNav / status badge) drive it via icon-weight context.\r\n */\r\n import { useIconWeight, type IconWeight } from './iconWeight.js';\r\n\r\n interface Props {\r\n /** Pixel size (width + height). */\r\n size?: string;\r\n /** Icon color; defaults to `currentColor` (inherits text color). */\r\n color?: string;\r\n /** Extra classes. */\r\n class?: string;\r\n /** `regular` (outline) or `fill` (solid). Overrides ambient state weight. */\r\n weight?: IconWeight;\r\n }\r\n\r\n let { size = '24', color = 'currentColor', class: className = '', weight }: Props = $props();\r\n\r\n const ctx = useIconWeight();\r\n let resolved: IconWeight = $derived(weight ?? ctx?.weight ?? 'regular');\r\n</script>\r\n\r\n<svg\r\n class={className}\r\n aria-hidden=\"true\"\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 256 256\"\r\n fill=\"currentColor\"\r\n style:color={color === 'currentColor' ? undefined : color}\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n>\r\n {#if resolved === 'fill'}<path d=\"M208,32H184V24a8,8,0,0,0-16,0v8H88V24a8,8,0,0,0-16,0v8H48A16,16,0,0,0,32,48V208a16,16,0,0,0,16,16H208a16,16,0,0,0,16-16V48A16,16,0,0,0,208,32ZM112,184a8,8,0,0,1-16,0V132.94l-4.42,2.22a8,8,0,0,1-7.16-14.32l16-8A8,8,0,0,1,112,120Zm56-8a8,8,0,0,1,0,16H136a8,8,0,0,1-6.4-12.8l28.78-38.37A8,8,0,1,0,145.07,132a8,8,0,1,1-13.85-8A24,24,0,0,1,176,136a23.76,23.76,0,0,1-4.84,14.45L152,176ZM48,80V48H72v8a8,8,0,0,0,16,0V48h80v8a8,8,0,0,0,16,0V48h24V80Z\"/>{:else}<rect x=\"40\" y=\"40\" width=\"176\" height=\"176\" rx=\"8\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><line x1=\"176\" y1=\"24\" x2=\"176\" y2=\"56\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><line x1=\"80\" y1=\"24\" x2=\"80\" y2=\"56\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><line x1=\"40\" y1=\"88\" x2=\"216\" y2=\"88\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><polyline points=\"88 128 104 120 104 184\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><path d=\"M138.14,128a16,16,0,1,1,26.64,17.63L136,184h32\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/>{/if}\r\n</svg>\r\n","/**\r\n * CTA state computation.\r\n *\r\n * Pure function (no I/O) — derives the buy-button state from an event and\r\n * its ticket list. Lives in `api/` because it is the canonical, single\r\n * source of truth that replaces the duplicate implementations previously\r\n * scattered across utils/api.js, core/api-client.js, and a few components.\r\n */\r\n\r\n// Import from the specific subpath (not the bare entry) so the api/\r\n// bundle doesn't pull in the entire SC component graph (Accordion etc.)\r\n// and break `npm run build:api`. `formatLocal` is the sanctioned browser-local\r\n// (no-timezone) Intl label home — these \"On sale …\" countdown labels render in\r\n// the viewer's own clock, so no venue timezone applies. The datetime subpath is\r\n// leaf/component-free; only `formatLocal` (→ Intl) is pulled, no date-fns.\r\nimport {\r\n browserNow,\r\n compareInstants,\r\n formatLocal,\r\n toInstantOrNull,\r\n} from '@getmicdrop/svelte-components/datetime';\r\n// Hidden-ticket predicate from the `/transforms` leaf subpath (no component\r\n// graph, so `npm run build:api` stays clean). SC's copy was pinned FROM this\r\n// body (dedup #392) and additionally honours the backend-resolved\r\n// `effectiveSales*` window for relative-offset tickets (#388). Imported (not\r\n// just re-exported) so it stays in local scope for `computeCtaState` below.\r\nimport { isHiddenTicketPurchasable } from '@getmicdrop/svelte-components/transforms';\r\n\r\nexport interface CtaStateOptions {\r\n cancelled?: boolean;\r\n isRegistration?: boolean;\r\n waitlistEnabled?: boolean;\r\n}\r\n\r\nexport interface CtaState {\r\n text: string;\r\n disabled: boolean;\r\n reason:\r\n | 'cancelled'\r\n | 'event_past'\r\n | 'no_tickets'\r\n | 'available'\r\n | 'coming_soon'\r\n | 'sold_out_waitlist'\r\n | 'sold_out'\r\n | 'sales_ended'\r\n | 'hidden_only';\r\n}\r\n\r\ninterface CtaEvent {\r\n startDateTime?: string | null;\r\n endDateTime?: string | null;\r\n hasPurchasableHiddenTickets?: boolean;\r\n}\r\n\r\ninterface CtaTicket {\r\n salesChannel?: number;\r\n salesBegin?: string | null;\r\n salesStart?: string | null;\r\n saleBegin?: string | null;\r\n onSaleStart?: string | null;\r\n salesEnd?: string | null;\r\n saleEnd?: string | null;\r\n onSaleEnd?: string | null;\r\n remainingCapacity?: number | null;\r\n quantityRemaining?: number | null;\r\n quantity?: number | null;\r\n soldOut?: boolean;\r\n isHidden?: boolean;\r\n visibility?: number;\r\n}\r\n\r\n// A hidden ticket (visibility>=2 or isHidden) that's still on sale drives the\r\n// \"Sales ended\" / \"Sold out\" / \"No tickets\" → \"Get tickets\" override. The user\r\n// just needs the promo code to see it. Re-exported from the SC canonical so\r\n// existing consumers (cta.test.ts, computeCtaState) keep their import path while\r\n// the single-source body lives in SC (dedup #392).\r\nexport { isHiddenTicketPurchasable };\r\n\r\nexport function computeCtaState(\r\n event: CtaEvent,\r\n tickets: CtaTicket[] | undefined,\r\n options: CtaStateOptions = {}\r\n): CtaState {\r\n const {\r\n cancelled = false,\r\n isRegistration = false,\r\n waitlistEnabled = false,\r\n } = options;\r\n const now = browserNow();\r\n\r\n if (cancelled)\r\n return { text: 'Cancelled', disabled: true, reason: 'cancelled' };\r\n\r\n const eventEnd = toInstantOrNull(event.endDateTime || event.startDateTime);\r\n if (eventEnd && compareInstants(eventEnd, now) < 0) {\r\n return { text: 'Sales ended', disabled: true, reason: 'event_past' };\r\n }\r\n\r\n const hiddenAvailable =\r\n event?.hasPurchasableHiddenTickets === true ||\r\n (Array.isArray(tickets) && tickets.some(isHiddenTicketPurchasable));\r\n\r\n const publicTickets = (tickets || []).filter(\r\n t =>\r\n t.salesChannel !== 2 &&\r\n !t.isHidden &&\r\n !(typeof t.visibility === 'number' && t.visibility >= 2)\r\n );\r\n if (publicTickets.length === 0) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n return {\r\n text: 'No tickets available',\r\n disabled: true,\r\n reason: 'no_tickets',\r\n };\r\n }\r\n\r\n let hasPurchasable = false;\r\n let allSoldOut = true;\r\n let allComingSoon = true;\r\n let earliestSalesStart: Date | null = null;\r\n\r\n for (const ticket of publicTickets) {\r\n const salesBegin = toInstantOrNull(\r\n ticket.salesBegin ||\r\n ticket.salesStart ||\r\n ticket.saleBegin ||\r\n ticket.onSaleStart\r\n );\r\n const salesEnd = toInstantOrNull(\r\n ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd\r\n );\r\n const remaining =\r\n ticket.remainingCapacity ?? ticket.quantityRemaining ?? ticket.quantity;\r\n const isSoldOut =\r\n ticket.soldOut ||\r\n (remaining !== null && remaining !== undefined && remaining <= 0);\r\n\r\n const isScheduled = salesBegin\r\n ? compareInstants(salesBegin, now) > 0\r\n : false;\r\n const hasSalesEnded = salesEnd ? compareInstants(salesEnd, now) < 0 : false;\r\n const isPurchasable = !isSoldOut && !isScheduled && !hasSalesEnded;\r\n\r\n if (isPurchasable) hasPurchasable = true;\r\n if (!isSoldOut) allSoldOut = false;\r\n if (!isScheduled) allComingSoon = false;\r\n\r\n if (isScheduled && salesBegin && !isSoldOut && !hasSalesEnded) {\r\n if (\r\n !earliestSalesStart ||\r\n compareInstants(salesBegin, earliestSalesStart) < 0\r\n ) {\r\n earliestSalesStart = salesBegin;\r\n }\r\n }\r\n }\r\n\r\n if (hasPurchasable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'available',\r\n };\r\n }\r\n\r\n if (allComingSoon && earliestSalesStart) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n const diffMs = compareInstants(earliestSalesStart, now);\r\n const diffHours = diffMs / (1000 * 60 * 60);\r\n const diffDays = diffMs / (1000 * 60 * 60 * 24);\r\n // Viewer-local time label (\"8:00 PM\") — the shape shared by the <24h branch\r\n // and the >7d composite. Matches the deprecated `formatTime(d,{hour12:true})`\r\n // defaults-merge exactly.\r\n const timeLabel = (d: Date) =>\r\n formatLocal(\r\n d,\r\n { hour: 'numeric', minute: '2-digit', hour12: true },\r\n 'en-US'\r\n );\r\n let dateText: string;\r\n if (diffHours < 24) {\r\n dateText = timeLabel(earliestSalesStart);\r\n } else if (diffDays <= 7) {\r\n dateText = formatLocal(\r\n earliestSalesStart,\r\n { weekday: 'short', hour: 'numeric', minute: '2-digit', hour12: true },\r\n 'en-US'\r\n );\r\n } else {\r\n const d = formatLocal(\r\n earliestSalesStart,\r\n { month: 'short', day: 'numeric' },\r\n 'en-US'\r\n );\r\n dateText = `${d} ${timeLabel(earliestSalesStart)}`;\r\n }\r\n return {\r\n text: `On sale ${dateText}`,\r\n disabled: true,\r\n reason: 'coming_soon',\r\n };\r\n }\r\n\r\n if (allSoldOut) {\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n if (waitlistEnabled) {\r\n return {\r\n text: 'Join Waitlist',\r\n disabled: false,\r\n reason: 'sold_out_waitlist',\r\n };\r\n }\r\n return { text: 'Sold out', disabled: true, reason: 'sold_out' };\r\n }\r\n\r\n if (hiddenAvailable) {\r\n return {\r\n text: isRegistration ? 'Reserve a spot' : 'Get tickets',\r\n disabled: false,\r\n reason: 'hidden_only',\r\n };\r\n }\r\n\r\n return { text: 'Sales ended', disabled: true, reason: 'sales_ended' };\r\n}\r\n","// @currency-escape: feeCents / 100 in calculateServiceFee is arithmetic,\r\n// not display. Produces a dollar-magnitude number used in the fee formula\r\n// `price * (feePercentage / 100) + feeCents / 100`. Per Atlas\r\n// utility-currency convergence record; the no-handrolled-currency\r\n// ESLint rule's divideByHundred detector treats variable-name `Cents`\r\n// as drift but here it's real math.\r\n\r\n// Premium Ticket Experience — Constants & Defaults\r\n// Used by all design variants. DO NOT MODIFY per-variant.\r\n\r\n// This file is the canonical formatter module for public-calendar-flow: it\r\n// wraps + re-exports the timezone-aware production date/time formatters, plus\r\n// the shared sales-countdown formatter (formatSmartSalesDate) hoisted into\r\n// svelte-components (SC #471). No raw Intl date formatting lives here anymore.\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 { computeCtaState } from '$lib/api';\r\nimport {\r\n computeScarcityLevel,\r\n SCARCITY_THRESHOLDS,\r\n} from '$lib/constants/scarcity';\r\nimport type { Cents } from '@getmicdrop/svelte-components';\r\nimport { roundCurrency } from '@getmicdrop/svelte-components';\r\nimport {\r\n formatCurrency,\r\n formatSmartSalesDate,\r\n} from '@getmicdrop/svelte-components/utils/formatters';\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// Shared sales-countdown formatter, adopted from svelte-components (SC #471).\r\n// Kept as a re-export so public-calendar-flow consumers import it from this\r\n// module unchanged. English defaults match VC's prior copy; the clock time,\r\n// weekday, and \"tomorrow\" calendar-day identity all resolve in the viewer's\r\n// timezone, or in `labels.timeZone` when a caller pins one (SC #772).\r\n//\r\n// This module used to document the clock as UTC. That was the vc#446 bug, not\r\n// a decision: the clock hardcoded UTC while the weekday beside it rendered\r\n// viewer-local, so the pair could name a wall clock the buyer never had.\r\nexport { formatSmartSalesDate };\r\n\r\n// --- Scarcity Badge Rules ---\r\n// Threshold logic lives in the ONE canonical computeScarcityLevel\r\n// ($lib/constants/scarcity) — including the sliding floor/cap that keeps a\r\n// 1,000-seat show from flagging \"Selling fast\" at 200 left. This module only\r\n// adapts the canon's tier enum ('sold-out'/'plenty') to public-calendar-flow's\r\n// display enum ('sold_out'/'none') and renders the copy.\r\nexport function getScarcityLevel(\r\n remaining: number,\r\n total: number\r\n): ScarcityLevel {\r\n const level = computeScarcityLevel(remaining, total);\r\n if (level === 'sold-out') return 'sold_out';\r\n if (level === 'plenty') return 'none';\r\n return level; // 'urgent' | 'low' — identical between the two enums\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 // `isHidden` flows through so computeCtaState can apply the hidden-only\r\n // \"Sales ended\" → \"Get tickets\" override (MIC-1039).\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 isHidden: t.isHidden,\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 ? toFriendlyReason(result.reason) : undefined,\r\n };\r\n}\r\n\r\n/**\r\n * computeCtaState() returns machine-enum reasons ('event_past', 'sold_out',\r\n * 'coming_soon', etc) intended for branching, not display. The BookingWidget\r\n * and MobileCta render `reason` directly as supplementary copy below the\r\n * button text — which leaks raw enum strings to users.\r\n *\r\n * Map each enum to a friendly sentence, OR return undefined when the button\r\n * `text` already says everything (\"Sales ended\" / \"Sold out\" — no need for\r\n * a second line repeating it).\r\n */\r\nfunction toFriendlyReason(reason: string | undefined): string | undefined {\r\n if (!reason) return undefined;\r\n switch (reason) {\r\n case 'cancelled':\r\n return 'This event has been cancelled.';\r\n case 'event_past':\r\n return 'This event has already happened.';\r\n case 'no_tickets':\r\n return 'There are no tickets configured for this event.';\r\n case 'coming_soon':\r\n return undefined; // text already shows \"On sale <date>\"\r\n case 'sold_out':\r\n return undefined; // text already shows \"Sold out\"\r\n case 'sold_out_waitlist':\r\n return 'All tickets are claimed. Join the waitlist to be notified.';\r\n case 'sales_ended':\r\n return undefined; // text already shows \"Sales ended\"\r\n default:\r\n return undefined; // unknown enum — never leak it\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). Non-donation free tickets\r\n // outrank the paid options in the headline, matching the shared \"Free\" label\r\n // every other price surface uses. Returning null here left the widget blank.\r\n if (hasFree) return 'Free';\r\n\r\n // Money display goes through the SC formatCurrency canonical (drift-c13 money\r\n // convergence), never a hand-rolled dollar template literal. Fraction digits\r\n // 0-2 reproduce the bare-dollar headline this surface has always shown (\"$25\",\r\n // \"$25.99\"), while gaining canonical thousands separators for >= $1,000 prices.\r\n const price = formatCurrency(minPrice, {\r\n minimumFractionDigits: 0,\r\n maximumFractionDigits: 2,\r\n });\r\n\r\n // Donation + paid → \"From $X\" (donation is an alternative option)\r\n if (hasDonation) return `From ${price}`;\r\n\r\n // Single paid ticket type → \"$X\"\r\n if (pricedTickets.length === 1) return price;\r\n\r\n // Multiple paid ticket types → \"From $X\"\r\n return `From ${price}`;\r\n}\r\n\r\n// --- Fee Calculation ---\r\nexport function calculateFee(\r\n price: number,\r\n feePercentage: number,\r\n feeCents: Cents\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// --- 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// --- 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","<!-- @style-escape: widget-intentional. -->\n<script lang=\"ts\">\n import { defaultLabels } from '$lib/public-calendar-flow/i18n/labels.js';\n import { Text, formatCurrency, toUSD } from '@getmicdrop/svelte-components';\n import type { OrderTotals } from '$lib/public-calendar-flow/types';\n import { formatPriceOrFree } from '$lib/core';\n import { DURATION } from '$lib/public-calendar-flow/defaults';\n\n interface Props {\n orderTotals: OrderTotals;\n labels?: Record<string, string>;\n /**\n * Which section to render.\n * - `full` (default): ticket lines + totals stacked together.\n * - `lines` : ticket-line items only. Use when the parent scrolls\n * this section in a flex column.\n * - `totals` : subtotal/fees/taxes/promo/total only. Use in a fixed\n * footer alongside the action button.\n *\n * Splitting via `lines` / `totals` lets the sticky sidebars scroll\n * just the variable-length ticket list while keeping the summary\n * block and CTA always visible.\n */\n mode?: 'full' | 'lines' | 'totals';\n }\n\n let { orderTotals, labels = defaultLabels, mode = 'full' }: Props = $props();\n\n // A missing wire price renders an em-dash, never a coerced $0 (mf#2383 /\n // vc#619). A genuine free/zero value is a real number and formats normally.\n const PRICE_DASH = '—';\n function moneyOrDash(value: number | null): string {\n return value == null ? PRICE_DASH : formatCurrency(toUSD(value));\n }\n</script>\n\n<div class=\"space-y-2\" style=\"transition-duration: {DURATION.normal}ms;\">\n {#if mode !== 'totals'}\n <!-- Line items -->\n {#each orderTotals.lines as line (line.ticketId)}\n <div class=\"flex justify-between\">\n <Text size=\"sm\">{line.quantity}x {line.ticketName}</Text>\n <Text size=\"sm\"\n >{#if line.subtotal == null}{PRICE_DASH}{:else if line.isDonation}{formatCurrency(\n toUSD(line.subtotal)\n )}{:else}{formatPriceOrFree(line.subtotal, labels)}{/if}</Text\n >\n </div>\n {/each}\n {/if}\n\n {#if mode !== 'lines'}\n <!-- Subtotal. In `totals` mode the parent supplies the separator above\n\t\t this block (BookingWidget wraps the totals block in border-t); a\n\t\t second internal border would render as a double line. Only show\n\t\t the internal border-t in `full` mode where lines + totals stack\n\t\t in one container with no external separator. -->\n {#if mode === 'full' && orderTotals.lines.length > 1}\n <div class=\"flex justify-between pt-1 border-t border-default\">\n <Text size=\"sm\" color=\"muted\">{labels.subtotal || 'Subtotal'}</Text>\n <Text size=\"sm\" color=\"muted\">{moneyOrDash(orderTotals.subtotal)}</Text>\n </div>\n {:else if orderTotals.lines.length > 1}\n <div class=\"flex justify-between\">\n <Text size=\"sm\" color=\"muted\">{labels.subtotal || 'Subtotal'}</Text>\n <Text size=\"sm\" color=\"muted\">{moneyOrDash(orderTotals.subtotal)}</Text>\n </div>\n {/if}\n\n <!-- Promo discount — directly under the subtotal so the receipt reads as a\n running total: subtotal − discount, then fees + taxes on the reduced\n base. Placing it after taxes made the on-screen arithmetic look wrong. -->\n {#if orderTotals.promoDiscount > 0}\n <div class=\"flex justify-between\">\n <Text size=\"sm\" color=\"success\"\n >{labels.promoDiscount || 'Promo discount'}</Text\n >\n <Text size=\"sm\" color=\"success\"\n >-{formatCurrency(toUSD(orderTotals.promoDiscount))}</Text\n >\n </div>\n {/if}\n\n <!-- Fees -->\n {#if orderTotals.fees > 0}\n <div class=\"flex justify-between\">\n <Text size=\"sm\" color=\"muted\"\n >{labels.serviceFees || 'Service fees'}</Text\n >\n <Text size=\"sm\" color=\"muted\"\n >{formatCurrency(toUSD(orderTotals.fees))}</Text\n >\n </div>\n {/if}\n\n <!-- Taxes -->\n {#if orderTotals.taxes > 0}\n <div class=\"flex justify-between\">\n <Text size=\"sm\" color=\"muted\">{labels.taxes || 'Taxes'}</Text>\n <Text size=\"sm\" color=\"muted\"\n >{formatCurrency(toUSD(orderTotals.taxes))}</Text\n >\n </div>\n {/if}\n\n <!-- Gift card — deduction from the amount due; `total` already reflects it -->\n {#if orderTotals.giftCardAmount > 0}\n <div class=\"flex justify-between\">\n <Text size=\"sm\" color=\"success\">{labels.giftCard || 'Gift card'}</Text>\n <Text size=\"sm\" color=\"success\"\n >-{formatCurrency(toUSD(orderTotals.giftCardAmount))}</Text\n >\n </div>\n {/if}\n\n <!-- Total -->\n <div class=\"flex justify-between pt-2 border-t border-default\">\n <Text size=\"base\" weight=\"semibold\">{labels.total || 'Total'}</Text>\n <Text size=\"base\" weight=\"semibold\">{moneyOrDash(orderTotals.total)}</Text\n >\n </div>\n {/if}\n</div>\n","/**\r\n * Spanish (es) labels for the public-calendar-flow catalogue.\r\n *\r\n * Mirrors the key set of src/lib/public-calendar-flow/i18n/labels.js\r\n * (`defaultLabels`, the English source). Anything absent here falls back to\r\n * English via the shared mergeLabels() seam — the same merge the main\r\n * $lib/i18n catalogue uses. Terminology matches $lib/i18n/locales/es.js so the\r\n * two bundles read as one es-MX voice (\"tus boletos\", \"Agotado\", \"Gratis\",\r\n * \"Finalizar compra\", etc.).\r\n *\r\n * Section comments + key order follow labels.js exactly so a reviewer can diff\r\n * the two files side by side.\r\n */\r\nexport const es = {\r\n // ── Ticket quantity controls ──────────────────────────────────────\r\n // `quantityNoun` is the lowercase composing noun (per labels.js it is NOT\r\n // the main catalogue's `quantity`, whose value is the 'Cantidad:' prefix).\r\n decrease: 'Reducir',\r\n increase: 'Aumentar',\r\n quantityNoun: 'cantidad',\r\n\r\n // ── Ticket states ─────────────────────────────────────────────────\r\n soldOut: 'Agotado',\r\n free: 'Gratis',\r\n unavailable: 'No disponible',\r\n eventSoldOut: 'Este evento está agotado',\r\n\r\n // ── Event unavailable (canceled / postponed / sales closed) ───────\r\n eventPostponed: 'Evento aplazado',\r\n eventUnavailable: 'Evento no disponible',\r\n salesClosed: 'Ventas cerradas',\r\n eventPostponedBanner:\r\n 'Este evento se ha aplazado. La venta de boletos está pausada',\r\n eventCanceledBanner: 'Este evento ya no está disponible para su compra',\r\n\r\n // ── Price display ─────────────────────────────────────────────────\r\n includesFees: 'Incluye',\r\n fees: 'cargos',\r\n fee: 'cargo',\r\n\r\n // ── Checkout CTAs ─────────────────────────────────────────────────\r\n checkout: 'Finalizar compra',\r\n getTickets: 'Comprar boletos',\r\n selectTickets: 'Selecciona los boletos',\r\n selectTicketsBelow: 'Selecciona los boletos abajo',\r\n selectTicketsToContinue: 'Selecciona boletos para continuar',\r\n reserveASpot: 'Reservar un lugar',\r\n\r\n // ── Password modal / gate ─────────────────────────────────────────\r\n enterPassword: 'Introduce la contraseña',\r\n enterEventPassword: 'Introduce la contraseña del evento',\r\n passwordRequired: 'Se requiere contraseña',\r\n passwordRequiredShort: 'Se requiere contraseña',\r\n eventRequiresPassword:\r\n 'Este evento requiere una contraseña para comprar boletos',\r\n eventRequiresPasswordAccess:\r\n 'Este evento requiere una contraseña para acceder a los boletos',\r\n enterEventPasswordToView:\r\n 'Introduce la contraseña del evento para ver los boletos',\r\n enterEventPasswordToViewAvailable:\r\n 'Introduce la contraseña del evento para ver los boletos disponibles',\r\n eventPassword: 'Contraseña del evento',\r\n password: 'Contraseña',\r\n pleaseEnterPassword: 'Introduce la contraseña del evento',\r\n pleaseEnterAPassword: 'Introduce una contraseña',\r\n eventRequiresPasswordShort: 'Este evento requiere una contraseña',\r\n unlockTickets: 'Desbloquear boletos',\r\n cancel: 'Cancelar',\r\n continue: 'Continuar',\r\n submit: 'Enviar',\r\n\r\n // ── Order summary ─────────────────────────────────────────────────\r\n subtotal: 'Subtotal',\r\n serviceFees: 'Cargos por servicio',\r\n taxes: 'Impuestos',\r\n tax: 'Impuesto',\r\n promoDiscount: 'Descuento promocional',\r\n giftCard: 'Tarjeta de regalo',\r\n total: 'Total',\r\n priceUnavailable:\r\n 'No pudimos cargar el precio de este pedido — actualiza la página o comunícate con el lugar',\r\n orderSummary: 'Resumen del pedido',\r\n yourOrder: 'Tu pedido',\r\n\r\n // ── Ticket counts (used in CTA buttons) ───────────────────────────\r\n ticket: 'boleto',\r\n tickets: 'boletos',\r\n\r\n // ── Checkout form (public-calendar-flow) ───────────────────────────────\r\n yourDetails: 'Tus datos',\r\n attendeeDetails: 'Datos del asistente',\r\n attendee: 'Asistente',\r\n sameAsPurchaser: 'Igual que el comprador',\r\n payment: 'Pago',\r\n noPaymentRequired: 'No se requiere pago',\r\n freeEventMessage:\r\n 'Este es un evento gratuito. Completa el formulario de arriba para reservar tu lugar',\r\n firstName: 'Nombre',\r\n lastName: 'Apellido',\r\n emailAddress: 'Correo electrónico',\r\n email: 'Correo electrónico',\r\n emailPlaceholder: 'tu@ejemplo.com',\r\n attendeeEmailPlaceholder: 'asistente@ejemplo.com',\r\n cardNumber: 'Número de tarjeta',\r\n cardPlaceholder: '4242 4242 4242 4242',\r\n expiryDate: 'Fecha de vencimiento',\r\n expiryPlaceholder: 'MM / AA',\r\n cvc: 'CVC',\r\n cvcPlaceholder: '123',\r\n keepMeUpdated: 'Mantenme informado sobre este evento y eventos similares',\r\n agreeToTerms: 'Acepto los',\r\n termsAndConditionsLink: 'términos y condiciones',\r\n goBack: 'Volver',\r\n goBackToEventDetails: 'Volver a los detalles del evento',\r\n loadingPaymentForm: 'Cargando el formulario de pago seguro...',\r\n\r\n // ── Ended event ───────────────────────────────────────────────────\r\n thisEventHasEnded: 'Este evento ya terminó',\r\n ticketsNoLongerAvailable: 'Ya no hay boletos disponibles para este evento',\r\n\r\n // ── Checkout form validation ──────────────────────────────────────\r\n firstNameRequired: 'El nombre es obligatorio',\r\n lastNameRequired: 'El apellido es obligatorio',\r\n emailRequired: 'El correo electrónico es obligatorio',\r\n emailInvalid: 'Ingresa una dirección de correo electrónico válida',\r\n emailInvalidShort: 'Ingresa un correo electrónico válido',\r\n cardNumberRequired: 'El número de tarjeta es obligatorio',\r\n expiryRequired: 'La fecha de vencimiento es obligatoria',\r\n cvcRequired: 'El CVC es obligatorio',\r\n mustAgreeTerms: 'Debes aceptar los términos',\r\n\r\n // ── Place order CTAs ──────────────────────────────────────────────\r\n placeOrder: 'Confirmar pedido',\r\n completeOrder: 'Completar el pedido',\r\n placingOrder: 'Confirmando el pedido...',\r\n\r\n // ── Promo code ────────────────────────────────────────────────────\r\n havePromoCode: '¿Tienes un código promocional?',\r\n promoCode: 'Código promocional',\r\n enterCode: 'Ingresa el código',\r\n apply: 'Aplicar',\r\n remove: 'Quitar',\r\n removePromoCode: 'Quitar el código promocional',\r\n applyPromoCode: 'Aplicar el código promocional',\r\n checkingPromoCode: 'Comprobando el código promocional',\r\n checking: 'Comprobando...',\r\n promoApplied: 'Promoción aplicada',\r\n\r\n // ── Navigation tabs (app-segments) ────────────────────────────────\r\n about: 'Información',\r\n ticketsTab: 'Boletos',\r\n eventSections: 'Secciones del evento',\r\n\r\n // ── Event details ─────────────────────────────────────────────────\r\n eventDetails: 'Detalles del evento',\r\n performers: 'Artistas',\r\n faq: 'Preguntas frecuentes',\r\n frequentlyAskedQuestions: 'Preguntas frecuentes',\r\n doors: 'Puertas',\r\n doorsOpenAt: 'Las puertas abren a las',\r\n\r\n // ── Series occurrences ────────────────────────────────────────────\r\n upcomingDates: 'Próximas fechas',\r\n showtimeDifferentLocation: 'Lugar diferente:',\r\n\r\n // ── Close / dismiss ───────────────────────────────────────────────\r\n closeTicketSelection: 'Cerrar la selección de boletos',\r\n closeTicketSelector: 'Cerrar el selector de boletos',\r\n\r\n // ── Accessibility ─────────────────────────────────────────────────\r\n ticketSelection: 'selección de boletos',\r\n ticketsSelected: 'boletos seleccionados',\r\n view: 'Ver',\r\n\r\n // ── Success page (public-calendar-flow) ────────────────────────────────\r\n youreIn: 'Estás dentro',\r\n youreRegistered: 'Estás registrado',\r\n confirmed: 'confirmado',\r\n viewTickets: 'Ver boletos',\r\n order: 'Pedido',\r\n confirmationSentTo: 'Confirmación enviada a',\r\n addToCalendar: 'Agregar al calendario',\r\n googleCalendar: 'Google Calendar',\r\n appleCalendar: 'Apple Calendar (.ics)',\r\n share: 'Compartir',\r\n map: 'Mapa',\r\n backToEvent: 'Volver al evento',\r\n needHelp: '¿Necesitas ayuda?',\r\n linkCopied: 'Enlace copiado al portapapeles',\r\n copyLink: 'Copia este enlace:',\r\n addToAppleWallet: 'Agregar a Apple Wallet',\r\n saveToGoogleWallet: 'Guardar en Google Wallet',\r\n walletComingSoon:\r\n 'Los pases de wallet estarán disponibles pronto. Tu boleto está confirmado — revisa tu correo electrónico',\r\n\r\n // ── Session timer / expiration modals ─────────────────────────────\r\n reservationHeld: 'Reserva apartada',\r\n completeSoon: 'Completa pronto',\r\n sessionExpiringSoon: 'Tu sesión está por expirar',\r\n sessionExpiringSoonMessage:\r\n 'Tu sesión de pago expirará en {time}. Los boletos que seleccionaste se devolverán al inventario',\r\n extensionsRemaining: 'Te queda{plural} {count} ampliación',\r\n continueCheckout: 'Continuar con el pago',\r\n okIllHurry: 'De acuerdo, me apuro',\r\n maxExtensionsReached: 'Se alcanzó el máximo de ampliaciones',\r\n wantToKeepTickets: '¿Quieres conservar tus boletos?',\r\n sessionExpiredMessage:\r\n 'Tu sesión de pago ha expirado y los boletos que seleccionaste se han liberado',\r\n sessionExpired: 'Sesión expirada',\r\n reservationTimedOut:\r\n 'Tu reserva ha expirado. Selecciona tus boletos de nuevo para continuar',\r\n keepMyTickets: 'Conservar mis boletos',\r\n selectTicketsAgain: 'Seleccionar los boletos de nuevo',\r\n extendingSession: 'Ampliando...',\r\n\r\n // ── MobileCta ─────────────────────────────────────────────────────\r\n selectTicketsLabel: 'Selecciona los boletos',\r\n collapseOrderSummary: 'Contraer el resumen del pedido',\r\n expandOrderSummary: 'Expandir el resumen del pedido',\r\n\r\n // ── Waitlist (sold-out gating + tokenized unlock) ─────────────────\r\n joinWaitlist: 'Unirse a la lista de espera',\r\n getNotifiedWhenAvailable: 'Te avisamos cuando haya boletos disponibles',\r\n waitlistLinkInvalid:\r\n 'Este enlace no es válido. Sigues en la lista de espera — te avisaremos por correo si se liberan boletos',\r\n waitlistLinkExpired: 'Este enlace ha expirado. Sigues en la lista de espera',\r\n waitlistLinkUsed: 'Este enlace ya se ha usado. Sigues en la lista de espera',\r\n waitlistLinkEmailMismatch:\r\n 'Este enlace se envió a otra dirección de correo. Sigues en la lista de espera',\r\n waitlistLinkInventoryExhausted:\r\n 'Esos boletos acaban de reclamarse. Sigues en la lista de espera — te avisaremos por correo si se liberan más',\r\n dismiss: 'Descartar',\r\n};\r\n","/**\r\n * i18n module — the public-calendar-flow (checkout / event / success)\r\n * catalogue.\r\n *\r\n * Before vc#693 this catalogue had NO locale mechanism: its `defaultLabels`\r\n * were merged straight from English, so `locale=\"es\"` translated the main\r\n * calendar surface while the buyer's checkout stayed in English — a silently\r\n * half-applied translation. It now resolves through the SAME `createI18n`\r\n * mechanism as the main catalogue (`src/lib/i18n/`); this file is just this\r\n * catalogue's instance of it.\r\n *\r\n * Resolution order (highest priority first):\r\n * 1. user-supplied `labels` prop (per-render override)\r\n * 2. locale-specific bundle (`./locales/<locale>.js`)\r\n * 3. English defaults (`./labels.js`)\r\n *\r\n * Locales bundled: `en`, `es`. A locale this catalogue does not carry (e.g.\r\n * `fr`) resolves to `en` — the buyer sees an English checkout, never a blank\r\n * or a crash. Filling the remaining platform languages here is the drain that\r\n * follows the mechanism (see vc#709), not part of vc#693.\r\n */\r\n\r\nimport { defaultLabels } from './labels.js';\r\nimport { en } from './locales/en.js';\r\nimport { es } from './locales/es.js';\r\nimport { createI18n } from '../../i18n/createI18n.js';\r\n\r\nexport { defaultLabels } from './labels.js';\r\nexport { detectLocale } from '../../i18n/createI18n.js';\r\n\r\n/** All bundled locales. Keys are BCP-47 base tags, lowercase. */\r\nconst LOCALES = { en, es };\r\n\r\nconst flowI18n = createI18n({ defaults: defaultLabels, locales: LOCALES });\r\n\r\n/**\r\n * Resolve a locale tag to a bundled locale for this catalogue.\r\n * @type {(tag?: string) => { labels: object, locale: string }}\r\n */\r\nexport const resolveLocale = flowI18n.resolveLocale;\r\n\r\n/**\r\n * Build the final checkout-flow label set for a given locale + user override.\r\n * Equivalent to defaults < locale bundle < user labels.\r\n * @type {(locale?: string, userLabels?: object) => object}\r\n */\r\nexport const getLabelsForLocale = flowI18n.getLabelsForLocale;\r\n\r\n/** List of bundled locale tags. */\r\nexport const listLocales = flowI18n.listLocales;\r\n"],"names":["size","$","$$props","color","className","ctx","useIconWeight","resolved","svg","root_2","path","root","$$render","consequent","alternate","styles","computeCtaState","event","tickets","options","cancelled","isRegistration","waitlistEnabled","now","browserNow","eventEnd","toInstantOrNull","compareInstants","hiddenAvailable","isHiddenTicketPurchasable","publicTickets","t","hasPurchasable","allSoldOut","allComingSoon","earliestSalesStart","ticket","salesBegin","salesEnd","remaining","isSoldOut","isScheduled","hasSalesEnded","diffMs","diffHours","diffDays","timeLabel","d","formatLocal","dateText","getScarcityLevel","total","level","computeScarcityLevel","getScarcityText","context","SCARCITY_THRESHOLDS","getCtaConfig","apiTickets","result","toFriendlyReason","reason","getPriceDisplay","visibleTickets","hasDonation","paidTickets","hasFree","pricedTickets","minPrice","price","formatCurrency","calculateTax","amount","taxPercentage","roundCurrency","DURATION","createDefaultPromoState","labels","defaultLabels","mode","PRICE_DASH","moneyOrDash","value","toUSD","div","root_3","node_1","line","div_1","Text","node_2","text","node_3","$0","text_2","text_3","formatPriceOrFree","consequent_1","consequent_2","div_2","root_1","node_7","node_8","text_5","div_3","node_9","node_10","text_7","consequent_3","consequent_4","div_4","node_12","node_13","consequent_5","div_5","node_15","node_16","consequent_6","div_6","node_18","node_19","consequent_7","div_7","node_21","node_22","consequent_8","div_8","node_20","node_23","node_24","text_17","consequent_9","es","LOCALES","en","flowI18n","createI18n","getLabelsForLocale"],"mappings":"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","x_google_ignoreList":[0]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"modalManager.svelte-JZXVYH3m.js","sources":["../node_modules/@getmicdrop/svelte-components/dist/primitives/Modal/ModalHeader.svelte","../node_modules/@getmicdrop/svelte-components/dist/utils/modalManager.svelte.js"],"sourcesContent":["<script lang=\"ts\">\r\n import type { Snippet } from 'svelte';\r\n import { getModalContext } from './context.js';\r\n import { cn } from '../../utils/classNames.js';\r\n\r\n interface Props {\r\n class?: string;\r\n children?: Snippet;\r\n }\r\n\r\n let { class: className, children }: Props = $props();\r\n\r\n // Validates this component is inside ModalRoot\r\n getModalContext();\r\n</script>\r\n\r\n<div class={cn('shrink-0 px-6 pt-6 pb-3', className)}>\r\n {#if children}{@render children()}{/if}\r\n</div>\r\n","/**\n * behavior-modal-orchestration canonical (R-MODAL-1/3).\n *\n * Two rune primitives for modal open-state — pick by modal count:\n *\n * - `createModalState<T>()` — ONE modal. Replaces the dominant\n * `let showFooModal = $state(false)` + `let fooData = $state(null)` pair.\n * Use: `modal.isOpen`, `modal.data`, `modal.open(data?)`, `modal.close()`.\n *\n * - `createModalManager<Id>()` — a page with 3+ modals. Replaces N booleans\n * with one registry: `m.open(id, payload?)`, `m.close(id)`, `m.isOpen(id)`,\n * `m.payload(id)`, `m.active`, `m.closeAll()`. Coordinates exclusive-open\n * (opening one closes the others) and focus-return (focus goes back to the\n * element that was active before the first modal opened) in one place,\n * instead of the implicit-by-convention mutual exclusion seen across the\n * 4-modal accounts page and the 3-modal checkout hand-off chains.\n *\n * `createModalState` is lifted verbatim from micdrop-frontend's storeUtils\n * (zero consumers there); `createModalManager` is new — no modal coordinator\n * existed in any of the four repos.\n *\n * ## Binding to `ModalRoot` (`bind()`)\n * Both factories expose a `bind(...)` helper returning the props to spread onto a\n * `ModalRoot`, wiring its open-state to the manager:\n *\n * <ModalRoot {...modals.bind('edit')}>…</ModalRoot> // multi\n * <ModalRoot {...editModal.bind()}>…</ModalRoot> // single\n *\n * `bind()` returns a `{ get open, set open, oncancel }` object. The `open`\n * **getter** keeps `ModalRoot` reading the manager's live open-state (so opening\n * the modal anywhere re-renders it); `oncancel` closes the modal through the\n * manager. `ModalRoot`'s only self-close path (`resetModal`) always fires\n * `oncancel` before flipping its own local `open`, so escape / backdrop dismissal\n * routes back through the manager and the two never desync.\n *\n * This deliberately avoids the foot-gun it replaces: you can't write\n * `bind:open={modals.isOpen('edit')}` (a method call isn't an assignable lvalue),\n * and feeding `bind:open` a `$derived` is the read-only binding runtime bug. The\n * getter + `oncancel` spread sidesteps both — no per-page `$state` boolean, no\n * accidental derived binding. The `set open` setter completes the\n * getter/setter pair so the object also reads/writes cleanly when assigned to\n * programmatically. Scroll-lock + focus-trap stay in `ModalRoot`; the manager\n * owns only open-state, payload, exclusivity, and focus-return.\n */\n/**\n * Reactive open/close state for a single modal, with optional context data.\n *\n * @example\n * const editModal = createModalState<User>();\n * editModal.open(user); // editModal.isOpen === true, editModal.data === user\n * editModal.close(); // isOpen === false, data === null\n *\n * // Compose with ModalRoot by spreading the bind() props:\n * // <ModalRoot {...editModal.bind()}>…</ModalRoot>\n */\nexport function createModalState() {\n let isOpen = $state(false);\n let data = $state(null);\n return {\n get isOpen() {\n return isOpen;\n },\n get data() {\n return data;\n },\n open(newData) {\n data = newData ?? null;\n isOpen = true;\n },\n close() {\n isOpen = false;\n data = null;\n },\n toggle(newData) {\n if (isOpen) {\n isOpen = false;\n data = null;\n }\n else {\n data = newData ?? null;\n isOpen = true;\n }\n },\n bind() {\n return {\n get open() {\n return isOpen;\n },\n set open(value) {\n // Binding-driven open carries no data (data stays null); use the\n // explicit open(data) call when a payload is needed.\n if (value)\n isOpen = true;\n else {\n isOpen = false;\n data = null;\n }\n },\n oncancel() {\n isOpen = false;\n data = null;\n },\n };\n },\n };\n}\n/**\n * Coordinate a set of named modals on one page. See module docs for when to\n * reach for this over `createModalState`.\n *\n * @example\n * const modals = createModalManager<'create' | 'edit' | 'delete'>();\n * modals.open('edit', user);\n * modals.isOpen('edit'); // true\n * modals.payload<User>('edit');\n * modals.open('delete'); // 'edit' auto-closes (exclusive)\n * modals.closeAll(); // focus returns to the original trigger\n *\n * // Compose with ModalRoot by spreading the bind() props:\n * // <ModalRoot {...modals.bind('edit')}>…</ModalRoot>\n */\nexport function createModalManager(options = {}) {\n const exclusive = options.exclusive ?? true;\n const restoreFocus = options.restoreFocus ?? true;\n const modals = $state({});\n let returnFocusEl = null;\n function anyOpen() {\n return Object.values(modals).some((m) => m.open);\n }\n function isOpen(id) {\n return modals[id]?.open ?? false;\n }\n /** Remember the active element when transitioning from \"none open\" → \"open\". */\n function captureReturnFocus() {\n if (!restoreFocus || typeof document === 'undefined')\n return;\n if (!anyOpen()) {\n const el = document.activeElement;\n returnFocusEl = el instanceof HTMLElement ? el : null;\n }\n }\n /** Return focus to the captured trigger once everything is closed. */\n function restoreReturnFocus() {\n if (!restoreFocus)\n return;\n if (!anyOpen() && returnFocusEl) {\n returnFocusEl.focus();\n returnFocusEl = null;\n }\n }\n function open(id, payload) {\n captureReturnFocus();\n if (exclusive) {\n for (const key of Object.keys(modals)) {\n if (key !== id) {\n modals[key].open = false;\n modals[key].payload = null;\n }\n }\n }\n modals[id] = { open: true, payload: payload ?? null };\n }\n function close(id) {\n const entry = modals[id];\n if (entry) {\n entry.open = false;\n entry.payload = null;\n }\n restoreReturnFocus();\n }\n function toggle(id, payload) {\n if (isOpen(id))\n close(id);\n else\n open(id, payload);\n }\n function closeAll() {\n for (const key of Object.keys(modals)) {\n modals[key].open = false;\n modals[key].payload = null;\n }\n restoreReturnFocus();\n }\n return {\n isOpen,\n payload(id) {\n return modals[id]?.payload ?? null;\n },\n get active() {\n return Object.keys(modals).filter((key) => modals[key].open);\n },\n open,\n close,\n toggle,\n closeAll,\n bind(id) {\n return {\n get open() {\n return isOpen(id);\n },\n set open(value) {\n // Binding-driven open carries no payload; use open(id, payload) for that.\n if (value)\n open(id);\n else\n close(id);\n },\n oncancel() {\n close(id);\n },\n };\n },\n };\n}\n"],"names":["getModalContext","div","root","$0","$","cn","$$props","createModalState","isOpen","data","newData","value"],"mappings":";;;;iBAAA;iBAaEA,EAAe;MAGhBC,IAAGC,EAAA,eAAHD,CAAG;;;;;;;;;;UAAHA,CAAG,wCAAHA,GAAG,GAAAE,CAAA,GAAA,CAAA,MAAAC,EAAA,KAAQC,EAAG,2BAAyBC,EAAA,KAAA,CAAA,CAAA,CAAA,eAAvCL,CAAG;AAFI;ACyCD,SAASM,IAAmB;AAC/B,MAAIC,IAASJ,EAAA,MAAO,EAAK,GACrBK,IAAOL,EAAA,MAAO,IAAI;AACtB,SAAM;AAAA,IACF,IAAI,SAAS;AACT,mBAAOI,CAAM;AAAA,IACjB;AAAA,IACA,IAAI,OAAO;AACP,mBAAOC,CAAI;AAAA,IACf;AAAA,IACA,KAAKC,GAAS;YACVD,GAAOC,KAAW,MAAI,EAAA,GACtBN,EAAA,IAAAI,GAAS,EAAI;AAAA,IACjB;AAAA,IACA,QAAQ;AACJ,MAAAJ,EAAA,IAAAI,GAAS,EAAK,GACdJ,EAAA,IAAAK,GAAO,IAAI;AAAA,IACf;AAAA,IACA,OAAOC,GAAS;AACZ,MAAEN,EAAA,IAAEI,CAAM,KACNJ,EAAA,IAAAI,GAAS,EAAK,GACdJ,EAAA,IAAAK,GAAO,IAAI,YAGXA,GAAOC,KAAW,MAAI,EAAA,GACtBN,EAAA,IAAAI,GAAS,EAAI;AAAA,IAErB;AAAA,IACA,OAAO;AACH,aAAM;AAAA,QACF,IAAI,OAAO;AACP,uBAAOA,CAAM;AAAA,QACjB;AAAA,QACA,IAAI,KAAKG,GAAO;AAGZ,UAAIA,IAAKP,EAAA,IACLI,GAAS,EAAI,KAEbJ,EAAA,IAAAI,GAAS,EAAK,GACdJ,EAAA,IAAAK,GAAO,IAAI;AAAA,QAEnB;AAAA,QACA,WAAW;AACP,UAAAL,EAAA,IAAAI,GAAS,EAAK,GACdJ,EAAA,IAAAK,GAAO,IAAI;AAAA,QACf;AAAA;IAER;AAAA;AAER;","x_google_ignoreList":[0,1]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"shareCopyFeedback.svelte-CvE7h_Wn.js","sources":["../node_modules/@getmicdrop/svelte-components/dist/primitives/Icons/phosphor/ExportIcon.svelte","../node_modules/@getmicdrop/svelte-components/dist/primitives/Icons/phosphor/LocationIcon.svelte","../node_modules/@getmicdrop/svelte-components/dist/utils/copyToClipboard.js","../src/lib/public-calendar-flow/shareEventLink.ts","../src/lib/public-calendar-flow/shareCopyFeedback.svelte.ts"],"sourcesContent":["<script lang=\"ts\">\r\n /**\r\n * Export — Phosphor icon (canonical Micdrop icon family).\r\n * Outline by default; pass `weight=\"fill\"` for solid, or let a stateful parent\r\n * (NavItem / Tabs / BottomNav / status badge) drive it via icon-weight context.\r\n */\r\n import { useIconWeight, type IconWeight } from './iconWeight.js';\r\n\r\n interface Props {\r\n /** Pixel size (width + height). */\r\n size?: string;\r\n /** Icon color; defaults to `currentColor` (inherits text color). */\r\n color?: string;\r\n /** Extra classes. */\r\n class?: string;\r\n /** `regular` (outline) or `fill` (solid). Overrides ambient state weight. */\r\n weight?: IconWeight;\r\n }\r\n\r\n let { size = '24', color = 'currentColor', class: className = '', weight }: Props = $props();\r\n\r\n const ctx = useIconWeight();\r\n let resolved: IconWeight = $derived(weight ?? ctx?.weight ?? 'regular');\r\n</script>\r\n\r\n<svg\r\n class={className}\r\n aria-hidden=\"true\"\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 256 256\"\r\n fill=\"currentColor\"\r\n style:color={color === 'currentColor' ? undefined : color}\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n>\r\n {#if resolved === 'fill'}<path d=\"M216,112v96a16,16,0,0,1-16,16H56a16,16,0,0,1-16-16V112A16,16,0,0,1,56,96h64v48a8,8,0,0,0,16,0V96h64A16,16,0,0,1,216,112ZM136,43.31l26.34,26.35a8,8,0,0,0,11.32-11.32l-40-40a8,8,0,0,0-11.32,0l-40,40A8,8,0,0,0,93.66,69.66L120,43.31V96h16Z\"/>{:else}<path d=\"M176,104h24a8,8,0,0,1,8,8v96a8,8,0,0,1-8,8H56a8,8,0,0,1-8-8V112a8,8,0,0,1,8-8H80\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><polyline points=\"88 64 128 24 168 64\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><line x1=\"128\" y1=\"24\" x2=\"128\" y2=\"136\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/>{/if}\r\n</svg>\r\n","<script lang=\"ts\">\r\n /**\r\n * Location — Phosphor icon (canonical Micdrop icon family).\r\n * Outline by default; pass `weight=\"fill\"` for solid, or let a stateful parent\r\n * (NavItem / Tabs / BottomNav / status badge) drive it via icon-weight context.\r\n */\r\n import { useIconWeight, type IconWeight } from './iconWeight.js';\r\n\r\n interface Props {\r\n /** Pixel size (width + height). */\r\n size?: string;\r\n /** Icon color; defaults to `currentColor` (inherits text color). */\r\n color?: string;\r\n /** Extra classes. */\r\n class?: string;\r\n /** `regular` (outline) or `fill` (solid). Overrides ambient state weight. */\r\n weight?: IconWeight;\r\n }\r\n\r\n let { size = '24', color = 'currentColor', class: className = '', weight }: Props = $props();\r\n\r\n const ctx = useIconWeight();\r\n let resolved: IconWeight = $derived(weight ?? ctx?.weight ?? 'regular');\r\n</script>\r\n\r\n<svg\r\n class={className}\r\n aria-hidden=\"true\"\r\n width={size}\r\n height={size}\r\n viewBox=\"0 0 256 256\"\r\n fill=\"currentColor\"\r\n style:color={color === 'currentColor' ? undefined : color}\r\n xmlns=\"http://www.w3.org/2000/svg\"\r\n>\r\n {#if resolved === 'fill'}<path d=\"M128,16a88.1,88.1,0,0,0-88,88c0,75.3,80,132.17,83.41,134.55a8,8,0,0,0,9.18,0C136,236.17,216,179.3,216,104A88.1,88.1,0,0,0,128,16Zm0,56a32,32,0,1,1-32,32A32,32,0,0,1,128,72Z\"/>{:else}<circle cx=\"128\" cy=\"104\" r=\"32\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/><path d=\"M208,104c0,72-80,128-80,128S48,176,48,104a80,80,0,0,1,160,0Z\" fill=\"none\" stroke=\"currentColor\" stroke-linecap=\"round\" stroke-linejoin=\"round\" stroke-width=\"16\"/>{/if}\r\n</svg>\r\n","/**\n * Copy-to-clipboard utility — resolves `true` ONLY on a real successful write.\n *\n * The whole point of this canonical: callers fire their own \"Copied!\" toast\n * ONLY when this resolves `true`. It NEVER resolves `true` unless the text\n * actually landed on the clipboard, so a permission-denied / insecure-context /\n * no-API failure can no longer masquerade as success — killing the \"lying\n * toast\" bug where a button claimed it copied when nothing was copied.\n *\n * Strategy (best → fallback, never throws):\n * 1. Async Clipboard API (`navigator.clipboard.writeText`) when available\n * (secure context, granted permission) — AWAITED; resolves `true` only\n * when that promise resolves. On rejection it falls through (2) rather\n * than reporting success.\n * 2. Legacy `document.execCommand('copy')` for older / insecure (non-HTTPS)\n * contexts — returns its REAL success boolean.\n * 3. `false` on any failure (rejected write, no clipboard API, no DOM,\n * thrown exception).\n *\n * @param text - The text to place on the clipboard.\n * @returns Promise resolving `true` iff the write actually succeeded, else `false`.\n *\n * @example\n * if (await copyToClipboard(shareUrl)) toast.success('Link copied');\n * else toast.error(\"Couldn't copy — try selecting and copying manually\");\n */\nexport async function copyToClipboard(text) {\n // 1. Async Clipboard API — preferred; only present in secure contexts.\n if (typeof navigator !== 'undefined' &&\n navigator.clipboard &&\n typeof navigator.clipboard.writeText === 'function') {\n try {\n await navigator.clipboard.writeText(text);\n return true;\n }\n catch {\n // Permission denied / insecure context / transient failure — do NOT\n // report success. Fall through to the legacy path and let it decide.\n }\n }\n // 2. Legacy execCommand fallback — older browsers / insecure (non-HTTPS).\n return copyViaExecCommand(text);\n}\n/**\n * Legacy `document.execCommand('copy')` path. Returns its real success boolean\n * and never throws. Used when the async Clipboard API is unavailable or its\n * write rejected.\n *\n * @param text - The text to copy.\n * @returns `true` iff `execCommand('copy')` reported success.\n */\nfunction copyViaExecCommand(text) {\n if (typeof document === 'undefined' ||\n typeof document.execCommand !== 'function') {\n return false;\n }\n // Preserve what the user had selected/focused — selecting our textarea would\n // otherwise blow away their current selection (e.g. text they were copying by hand).\n const prevActive = document.activeElement;\n const selection = document.getSelection();\n const savedRanges = selection\n ? Array.from({ length: selection.rangeCount }, (_, i) => selection.getRangeAt(i).cloneRange())\n : [];\n const textarea = document.createElement('textarea');\n try {\n textarea.value = text;\n // Read-only + off-screen so it never flashes, scrolls, or steals focus.\n textarea.setAttribute('readonly', '');\n textarea.style.position = 'fixed';\n textarea.style.top = '-9999px';\n textarea.style.left = '-9999px';\n textarea.style.opacity = '0';\n document.body.appendChild(textarea);\n textarea.select();\n textarea.setSelectionRange(0, text.length);\n return document.execCommand('copy');\n }\n catch {\n return false;\n }\n finally {\n textarea.remove();\n // Restore the user's prior selection + focus, on success AND failure.\n if (selection) {\n selection.removeAllRanges();\n for (const range of savedRanges)\n selection.addRange(range);\n }\n prevActive?.focus?.();\n }\n}\n","import { copyToClipboard } from '@getmicdrop/svelte-components';\r\n\r\n/**\r\n * Outcome of a share attempt, mapped by the caller to its own inline feedback.\r\n *\r\n * - `shared` — the native share sheet handled it (or the user cancelled). No\r\n * inline feedback: the OS UI already spoke, and a cancel must stay quiet.\r\n * - `copied` — the link landed on the clipboard via the SC canonical.\r\n * - `failed` — the clipboard write was blocked; surface the URL for manual copy.\r\n */\r\nexport type ShareLinkResult = 'shared' | 'copied' | 'failed';\r\n\r\n/**\r\n * Share-link decision — the ONE copy of the native-share/clipboard logic that\r\n * used to be duplicated across the event header and the confirmation sidebar.\r\n *\r\n * 1. Native share sheet when the browser exposes `navigator.share`. A user\r\n * cancel (AbortError) ends quietly — returns `shared`, no fallback.\r\n * 2. Otherwise (no share API, or a non-cancel share failure) copy the URL via\r\n * the SC canonical `copyToClipboard`, which resolves `true` ONLY on a real\r\n * write and falls back to `execCommand` on the insecure (http embed) hosts\r\n * where the async Clipboard API is unavailable — the exact context the old\r\n * raw `navigator.clipboard.writeText` threw in.\r\n *\r\n * VC has no toasts (standalone embeddable widget, no Toaster mounted), so this\r\n * returns a result the caller renders inline rather than firing feedback here.\r\n *\r\n * @param url - The link to share.\r\n */\r\nexport async function shareEventLink(url: string): Promise<ShareLinkResult> {\r\n if (\r\n typeof navigator !== 'undefined' &&\r\n typeof navigator.share === 'function'\r\n ) {\r\n try {\r\n await navigator.share({ url });\r\n return 'shared';\r\n } catch (err: unknown) {\r\n // User dismissed the share sheet — leave it there, no fallback. Browsers\r\n // reject with a DOMException named 'AbortError', and DOMException is NOT\r\n // `instanceof Error`, so match on the name alone (not the prototype).\r\n if ((err as { name?: string } | null)?.name === 'AbortError') {\r\n return 'shared';\r\n }\r\n // Any other failure falls through to the clipboard fallback below.\r\n }\r\n }\r\n\r\n return (await copyToClipboard(url)) ? 'copied' : 'failed';\r\n}\r\n","import { shareEventLink } from '$lib/public-calendar-flow/shareEventLink';\r\n\r\n/**\r\n * Share-link copy feedback — the ONE copy of the inline state machine that used\r\n * to be hand-rolled identically in the event header and the confirmation\r\n * sidebar. It owns the reactive `copyState`/`copyFailedUrl` and the reset timer,\r\n * delegating the native-share/clipboard decision to `shareEventLink`.\r\n *\r\n * VC has no toasts, so feedback is inline: `copied` briefly swaps the button\r\n * label, `failed` reveals the URL for manual copy. Native share (or a user\r\n * cancel) leaves the state untouched — the OS UI already spoke.\r\n *\r\n * Call `dispose()` from the consumer's `onDestroy` to clear the pending timer.\r\n */\r\nexport function createShareCopyFeedback() {\r\n let copyState = $state<'idle' | 'copied' | 'failed'>('idle');\r\n let copyFailedUrl = $state('');\r\n let resetTimer: ReturnType<typeof setTimeout> | undefined;\r\n\r\n async function share(url: string) {\r\n const result = await shareEventLink(url);\r\n if (result === 'copied') {\r\n copyFailedUrl = '';\r\n copyState = 'copied';\r\n clearTimeout(resetTimer);\r\n resetTimer = setTimeout(() => (copyState = 'idle'), 2500);\r\n } else if (result === 'failed') {\r\n // Copy blocked — reveal the URL inline so the buyer can select it manually.\r\n clearTimeout(resetTimer);\r\n copyState = 'failed';\r\n copyFailedUrl = url;\r\n }\r\n // 'shared' → native sheet handled it (or user cancelled); no inline feedback.\r\n }\r\n\r\n function dispose() {\r\n clearTimeout(resetTimer);\r\n }\r\n\r\n return {\r\n get copyState() {\r\n return copyState;\r\n },\r\n get copyFailedUrl() {\r\n return copyFailedUrl;\r\n },\r\n share,\r\n dispose,\r\n };\r\n}\r\n"],"names":["size","$","$$props","color","className","ctx","useIconWeight","resolved","svg","root_2","path","root","$$render","consequent","alternate","styles","copyToClipboard","text","copyViaExecCommand","prevActive","selection","savedRanges","_","i","textarea","range","shareEventLink","url","err","createShareCopyFeedback","copyState","copyFailedUrl","resetTimer","share","result","dispose"],"mappings":"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","x_google_ignoreList":[0,1,2]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"transform-CpUt4pIr.js","sources":["../src/lib/api/transformers/address.ts","../src/lib/api/transformers/series.ts","../src/lib/api/transformers/collection.ts","../src/lib/public-calendar-flow/transform.ts"],"sourcesContent":["/**\r\n * Address helpers — shared between the event, series and collection page\r\n * transformers.\r\n *\r\n * Lifted verbatim from public-calendar-flow/transform.ts so the series and\r\n * collection canonicals (and the in-place transformApiEvent) read a single\r\n * source instead of each carrying a private copy. Behavior is byte-identical\r\n * to the previous module-private versions.\r\n */\r\n\r\n/**\r\n * Parse a single-line address string into display lines.\r\n * E.g. \"123 Main St, Los Angeles, CA 90012\" -> [\"123 Main St\", \"Los Angeles, CA 90012\"]\r\n */\r\nexport function parseAddress(addressStr: string): string[] {\r\n if (!addressStr) return [];\r\n const parts = addressStr.split(',').map(p => p.trim());\r\n if (parts.length < 2) return [addressStr];\r\n const lastPart = parts[parts.length - 1];\r\n const stateZipMatch = lastPart.match(/^([A-Z]{2})\\s+(\\d{5}(-\\d{4})?)$/);\r\n if (stateZipMatch && parts.length >= 3) {\r\n const state = stateZipMatch[1];\r\n const zip = stateZipMatch[2];\r\n const city = parts[parts.length - 2];\r\n const lines = parts.slice(0, -2);\r\n lines.push(`${city}, ${state} ${zip}`);\r\n return lines;\r\n }\r\n return [parts[0], parts.slice(1).join(', ')];\r\n}\r\n\r\n/**\r\n * Build a Google Maps search URL from an address string.\r\n */\r\nexport function buildGoogleMapsUrl(address: string): string {\r\n if (!address) return '';\r\n return `https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(address)}`;\r\n}\r\n","/**\r\n * Series Page Transformer\r\n *\r\n * Wire->domain boundary for the public series page. Maps a SeriesPageData\r\n * payload (from the series API) into the clean EventData shape consumed by\r\n * SeriesPage.svelte / EventExperience (full-width series mode, set via\r\n * eventSeriesId).\r\n *\r\n * Lifted byte-identically from public-calendar-flow/transform.ts's\r\n * transformSeriesPageData. Every ||/?? fallthrough is preserved exactly;\r\n * characterization tests pin the output. transformSeriesPageData now delegates\r\n * here.\r\n *\r\n * COMPOSITION NOTE — why this does NOT reuse parseVenue:\r\n * The api `parseVenue` returns the api-domain `Venue` (raw timezone string,\r\n * fee fields read from the payload). The series page needs the pcf `VenueData`\r\n * shape: timezone is the IANA-RESOLVED zone (getIANATimezone) and the fee\r\n * fields are hard-zeroed (the series payload carries no venue fees). Those\r\n * shapes are not byte-compatible, so the venue sub-shape stays local.\r\n */\r\n\r\nimport { getIANATimezone } from '@getmicdrop/svelte-components';\r\nimport { resolveImageUrl } from '@getmicdrop/svelte-components';\r\nimport { formatDateKey } from '$lib/utils/datetime.js';\r\nimport { parseAddress, buildGoogleMapsUrl } from './address.js';\r\nimport type { SeriesPageData } from '../types.js';\r\nimport type {\r\n EventData,\r\n EventStatus,\r\n PerformerData,\r\n FAQItem,\r\n SeriesOccurrence,\r\n} from '$lib/public-calendar-flow/types';\r\n\r\n/**\r\n * Transform SeriesPageData (from the series API) into our clean EventData interface.\r\n * Sets eventSeriesId so EventExperience renders in full-width series mode.\r\n */\r\nexport function parseSeries(series: SeriesPageData): EventData {\r\n // Mirror the event transform: raw series carry lowercase `timezone`\r\n // (some PascalCase `timeZone` / snake `time_zone`). Read all casings so a\r\n // non-UTC series doesn't silently fall back to UTC.\r\n const seriesAny = series as SeriesPageData & {\r\n timezone?: string;\r\n time_zone?: string;\r\n };\r\n const timezone = getIANATimezone(\r\n series.timeZone || seriesAny.timezone || seriesAny.time_zone || 'UTC'\r\n );\r\n\r\n // Build image URL — SC resolveImageUrl already passes absolute URLs through,\r\n // so no local startsWith('http') guard is needed.\r\n const imageUrl = resolveImageUrl(series.image || '', { cdn: true });\r\n\r\n // Map occurrences to SeriesOccurrence[].\r\n //\r\n // `occ.venue` is present ONLY for an occurrence the backend has determined is\r\n // no longer at the series master venue/room, so it is carried straight\r\n // through as the moved-showtime signal — no comparison is redone here. The\r\n // room name is folded into the display name (\"The Fillmore — Studio B\") so a\r\n // same-venue room move still reads as a change.\r\n const seriesOccurrences: SeriesOccurrence[] = (series.occurrences || []).map(\r\n occ => {\r\n const occVenue = occ.venue;\r\n const occVenueName = occVenue\r\n ? [occVenue.name, occVenue.stageName].filter(Boolean).join(' — ')\r\n : '';\r\n const occVenueAddress = occVenue\r\n ? occVenue.googleLocationNameCache ||\r\n [occVenue.address, occVenue.city, occVenue.state]\r\n .filter(Boolean)\r\n .join(', ')\r\n : '';\r\n return {\r\n id: occ.id,\r\n slug: occ.slug || '',\r\n startDateTime: occ.startDateTime,\r\n endDateTime: occ.endDateTime || '',\r\n status: (occ.ctaState?.reason === 'sold_out'\r\n ? 'sold_out'\r\n : 'on_sale') as EventStatus,\r\n ctaText: occ.ctaState?.text || 'Get Tickets',\r\n ctaDisabled: occ.ctaState?.disabled || false,\r\n ...(occVenueName ? { venueName: occVenueName } : {}),\r\n ...(occVenueAddress ? { venueAddress: occVenueAddress } : {}),\r\n };\r\n }\r\n );\r\n\r\n // Use first occurrence for date fields\r\n const firstOcc = series.occurrences?.[0];\r\n const date = firstOcc?.startDateTime\r\n ? formatDateKey(firstOcc.startDateTime, timezone)\r\n : '';\r\n\r\n // Map FAQs\r\n const rawFaq = series.venue?.faq;\r\n let faqs: FAQItem[] = [];\r\n if (Array.isArray(rawFaq)) {\r\n faqs = rawFaq.map(f => ({\r\n question: f.question || '',\r\n answer: f.answer || '',\r\n }));\r\n } else if (typeof rawFaq === 'string' && rawFaq) {\r\n faqs = [{ question: 'FAQ', answer: rawFaq }];\r\n }\r\n\r\n // Map performers\r\n const performers: PerformerData[] = (series.performers || []).map(\r\n (p, index) => ({\r\n id: index + 1,\r\n displayName:\r\n p.displayName || [p.firstName, p.lastName].filter(Boolean).join(' '),\r\n profileImage: p.profileImage || undefined,\r\n })\r\n );\r\n\r\n // Venue address\r\n const venueAddress =\r\n series.venue?.location || series.venue?.googleLocationNameCache || '';\r\n\r\n return {\r\n id: series.id,\r\n title: series.title || '',\r\n slug: '',\r\n description: series.description || series.eventSummary || '',\r\n date,\r\n startDateTime: firstOcc?.startDateTime || '',\r\n endDateTime: firstOcc?.endDateTime || '',\r\n doorsOpenTime: '',\r\n timezone,\r\n imageUrl,\r\n venue: {\r\n id: series.venue?.id || 0,\r\n name: series.venue?.name || '',\r\n address: venueAddress,\r\n timezone,\r\n serviceFeePercentage: 0,\r\n serviceFeeCents: 0,\r\n taxPercentage: 0,\r\n },\r\n performers,\r\n tickets: [],\r\n faqs,\r\n disclaimer: series.venue?.disclaimer || undefined,\r\n status: 'on_sale',\r\n eventSeriesId: series.id,\r\n seriesOccurrences,\r\n isPasswordProtected: false,\r\n isRegistrationEvent: false,\r\n totalTicketsRemaining: 0,\r\n totalTicketsCapacity: 0,\r\n venueAddressLines: parseAddress(venueAddress),\r\n googleMapsUrl: buildGoogleMapsUrl(venueAddress),\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias — the public-calendar-flow shaper name. Existing call\r\n * sites importing transformSeriesPageData continue to work unchanged.\r\n */\r\nexport const transformSeriesPageData = parseSeries;\r\n","/**\r\n * Collection Page Transformer\r\n *\r\n * Wire->domain boundary for the public collection page. Maps a\r\n * PublicCollectionData payload (from the collection API) into the clean\r\n * EventData shape consumed by CollectionView.svelte / EventExperience\r\n * (full-width collection mode, set via collectionId).\r\n *\r\n * Lifted byte-identically from public-calendar-flow/transform.ts's\r\n * transformCollectionData. Every ||/?? fallthrough is preserved exactly;\r\n * characterization tests pin the output. transformCollectionData now delegates\r\n * here.\r\n *\r\n * COMPOSITION NOTE — why this does NOT reuse parseVenue/parseEvent:\r\n * The collection payload has no venue and no timezone; the EventData it\r\n * produces hard-codes an empty venue and 'UTC'. The per-event mapping is a\r\n * tiny CollectionEvent card shape (id/name/slug/image/startDateTime/status),\r\n * not the full Event domain — reusing parseEvent would change the output.\r\n * Both stay local.\r\n */\r\n\r\nimport { resolveImageUrl } from '@getmicdrop/svelte-components';\r\nimport type { PublicCollectionData } from '../types.js';\r\nimport type {\r\n EventData,\r\n EventStatus,\r\n CollectionEvent,\r\n} from '$lib/public-calendar-flow/types';\r\n\r\n/**\r\n * Transform PublicCollectionData (from the collection API) into our clean EventData interface.\r\n * Sets collectionId so EventExperience renders in full-width collection mode.\r\n */\r\nexport function parseCollection(collection: PublicCollectionData): EventData {\r\n // Map collection events to CollectionEvent[]\r\n const collectionEvents: CollectionEvent[] = (collection.events || []).map(\r\n ev => {\r\n return {\r\n id: ev.id,\r\n name: ev.title || '',\r\n slug: ev.slug || '',\r\n // SC's resolveImageUrl passes absolute http(s)/data/blob URLs through\r\n // untouched, so no local startsWith('http') guard is needed.\r\n image: resolveImageUrl(ev.image || '', { cdn: true }),\r\n startDateTime: ev.startDateTime || '',\r\n status: (ev.status || 'on_sale') as EventStatus,\r\n };\r\n }\r\n );\r\n\r\n // Cover image — SC resolveImageUrl already passes absolute URLs through.\r\n const imageUrl = resolveImageUrl(collection.coverImage || '', { cdn: true });\r\n\r\n return {\r\n id: collection.id,\r\n title: collection.collectionTitle || '',\r\n slug: '',\r\n description: collection.description || collection.summary || '',\r\n date: '',\r\n startDateTime: '',\r\n endDateTime: '',\r\n doorsOpenTime: '',\r\n timezone: 'UTC',\r\n imageUrl,\r\n venue: {\r\n id: 0,\r\n name: '',\r\n address: '',\r\n timezone: 'UTC',\r\n serviceFeePercentage: 0,\r\n serviceFeeCents: 0,\r\n taxPercentage: 0,\r\n },\r\n performers: [],\r\n tickets: [],\r\n faqs: [],\r\n status: 'on_sale',\r\n collectionId: collection.id,\r\n collectionEvents,\r\n isPasswordProtected: false,\r\n isRegistrationEvent: false,\r\n totalTicketsRemaining: 0,\r\n totalTicketsCapacity: 0,\r\n };\r\n}\r\n\r\n/**\r\n * Back-compat alias — the public-calendar-flow shaper name. Existing call\r\n * sites importing transformCollectionData continue to work unchanged.\r\n */\r\nexport const transformCollectionData = parseCollection;\r\n","import { getIANATimezone } from '@getmicdrop/svelte-components';\r\nimport {\r\n browserNow,\r\n compareInstants,\r\n toInstantOrNull,\r\n} from '@getmicdrop/svelte-components/datetime';\r\nimport { formatDateKey } from '$lib/utils/datetime.js';\r\n// Premium Ticket Experience — API → EventData Transformer\r\n// Maps raw API event responses to our clean EventData interface.\r\n// Used when plugging in real API data. The showcase continues using mock-data.ts directly.\r\n\r\nimport { resolveImageUrl } from '@getmicdrop/svelte-components';\r\nimport {\r\n parseAddress,\r\n buildGoogleMapsUrl,\r\n} from '$lib/api/transformers/address';\r\n// Canonical wire→domain boundary — all event-level scalar reads go through it.\r\nimport { parseEvent } from '$lib/api/transformers/event';\r\nimport { parseSeries } from '$lib/api/transformers/series';\r\nimport { parseCollection } from '$lib/api/transformers/collection';\r\nimport type {\r\n EventData,\r\n EventStatus,\r\n TicketTypeData,\r\n TicketStatus,\r\n VenueData,\r\n PerformerData,\r\n FAQItem,\r\n SeriesOccurrence,\r\n} from './types';\r\nimport type { SeriesPageData, PublicCollectionData } from '$lib/api/types';\r\n\r\n/**\r\n * A wire price that isn't a finite number — missing (null/undefined) OR malformed\r\n * (NaN, a non-numeric string) — resolves to null, so the ticket + order lines\r\n * render an em-dash and checkout hard-blocks, rather than a coerced $0/\"Free\" or a\r\n * NaN that later throws in toUSD (mf#2383 / vc#619). A numeric string (\"25\") is\r\n * preserved as its number.\r\n */\r\nexport function finitePriceOrNull(value: unknown): number | null {\r\n if (value == null) return null;\r\n const n = Number(value);\r\n return Number.isFinite(n) ? n : null;\r\n}\r\n\r\n/**\r\n * Map a raw API ticket status/state to our TicketStatus union.\r\n */\r\nfunction mapTicketStatus(ticket: any): TicketStatus {\r\n const now = browserNow();\r\n const remaining =\r\n ticket.remainingCapacity ??\r\n ticket.quantityRemaining ??\r\n ticket.quantity ??\r\n 0;\r\n const salesBegin =\r\n ticket.salesBegin ||\r\n ticket.salesStart ||\r\n ticket.saleBegin ||\r\n ticket.onSaleStart;\r\n const salesEnd = ticket.salesEnd || ticket.saleEnd || ticket.onSaleEnd;\r\n\r\n if (remaining <= 0) return 'sold_out';\r\n const salesEndAt = toInstantOrNull(salesEnd);\r\n if (salesEndAt && compareInstants(salesEndAt, now) < 0) return 'sales_ended';\r\n const salesBeginAt = toInstantOrNull(salesBegin);\r\n if (salesBeginAt && compareInstants(salesBeginAt, now) > 0)\r\n return 'coming_soon';\r\n return 'on_sale';\r\n}\r\n\r\n/**\r\n * Map a raw API event status string to our EventStatus union.\r\n */\r\nfunction mapEventStatus(\r\n rawStatus: string,\r\n tickets: TicketTypeData[]\r\n): EventStatus {\r\n const s = (rawStatus || '').toLowerCase().replace(/\\s+/g, '_');\r\n\r\n if (s === 'cancelled' || s === 'canceled') return 'cancelled';\r\n\r\n // Check if event is past based on end time (handled by caller if needed)\r\n if (s === 'past' || s === 'ended') return 'past';\r\n\r\n // MIC-1039: if any hidden ticket is still on sale, the event is \"on_sale\"\r\n // regardless of visible-ticket states — the user just needs a promo code.\r\n // NOTE: this operates on the pcf DOMAIN ticket shape (post-transform, with a\r\n // computed `status` string), NOT the wire shape SC's\r\n // `isHiddenTicketPurchasable` reads (raw visibility/salesChannel/sale-window\r\n // fields). The sale-window math already happened in `mapTicketStatus`, so the\r\n // SC predicate can't honestly serve this site — kept deliberately local.\r\n const hasPurchasableHiddenTickets = tickets.some(\r\n t => t.isHidden && t.status === 'on_sale'\r\n );\r\n if (hasPurchasableHiddenTickets) return 'on_sale';\r\n\r\n // Derive from ticket states\r\n const publicTickets = tickets.filter(t => !t.isHidden);\r\n if (publicTickets.length > 0) {\r\n if (publicTickets.every(t => t.status === 'sold_out')) return 'sold_out';\r\n if (publicTickets.every(t => t.status === 'coming_soon'))\r\n return 'coming_soon';\r\n if (\r\n publicTickets.every(\r\n t => t.status === 'sales_ended' || t.status === 'sold_out'\r\n )\r\n )\r\n return 'sales_ended';\r\n }\r\n\r\n if (s === 'sold_out' || s === 'soldout') return 'sold_out';\r\n if (s === 'coming_soon' || s === 'comingsoon') return 'coming_soon';\r\n if (s === 'sales_ended' || s === 'salesended') return 'sales_ended';\r\n\r\n return 'on_sale';\r\n}\r\n\r\n/**\r\n * Transform a raw API event response into our clean EventData interface.\r\n * This is the bridge between the API and our premium ticket experience components.\r\n *\r\n * COMPOSITION NOTE (events convergence): the wire is parsed ONCE through the\r\n * canonical parseEvent ($lib/api/transformers/event); every event-level\r\n * scalar below projects off the domain Event. Three deliberate wire seams\r\n * remain, characterization-pinned in\r\n * utils/event-shapers.characterization.test.js:\r\n * 1. TICKETS — the TicketTypeData map uses `??` (nullish) semantics over\r\n * raw ticket fields (remainingCapacity/visibility/fee/…) that\r\n * parseTicket's `||`-folding AvailableTicket neither carries nor\r\n * preserves; routing through parseTicket would change rendered output.\r\n * 2. IMAGE — image→imageUrl→image_url precedence intentionally differs\r\n * from the canonical resolver's (imageUrl-first); see the note in\r\n * api/transformers/event.ts.\r\n * 3. VENUE/PERFORMERS — mapped from the raw passthrough objects\r\n * (domain.venue / domain.performers) with B-specific `??` fee/tax\r\n * semantics; the performer canonical lands in PR #260 and should\r\n * absorb the performer map then.\r\n */\r\nexport function transformApiEvent(rawEvent: any): EventData {\r\n // Wire→domain ONCE (see composition note).\r\n const domain = parseEvent(rawEvent);\r\n const rawVenue = domain.venue || {};\r\n // Event-level timeZone is an OPTIONAL per-event override; when absent/blank it\r\n // must inherit the venue timezone. Falling straight to 'UTC' here rendered\r\n // venue-local times (e.g. a Fri 10pm Central show) in UTC (\"Sat 3:00 AM\"),\r\n // which made already-ended events look like future ones.\r\n // (domain.timeZone is the verbatim PascalCase passthrough; domain.timezone\r\n // folds lowercase + snake_case — same chain as before.)\r\n const timezone = getIANATimezone(\r\n domain.timeZone ||\r\n domain.timezone ||\r\n rawVenue.timeZone ||\r\n rawVenue.timezone ||\r\n 'UTC'\r\n );\r\n\r\n // Transform tickets — TICKET SEAM: raw wire tickets by contract (see note).\r\n const rawTickets = rawEvent.availableTickets || [];\r\n const tickets: TicketTypeData[] = rawTickets.map((t: any) => ({\r\n id: t.id || t.ID,\r\n name: t.name || t.title || '',\r\n description: t.description || undefined,\r\n // A missing OR malformed wire price stays null → the ticket + order lines\r\n // render an em-dash and checkout hard-blocks, never a coerced $0/\"Free\" or a\r\n // NaN that throws in toUSD (mf#2383).\r\n price: finitePriceOrNull(t.price),\r\n // Legitimate-zero: a ticket with no per-ticket fee is a real 0, not a\r\n // missing price (mf#2234's separately-ruled class). A ternary keeps the 0\r\n // default without a `?? 0` money-field flag AND without a file-level escape\r\n // that would blind the price site above.\r\n fee: t.fee == null ? 0 : t.fee,\r\n quantityAvailable:\r\n t.remainingCapacity ?? t.quantityRemaining ?? t.quantity ?? 0,\r\n quantityTotal: t.totalCapacity ?? t.quantity ?? 0,\r\n minPerOrder: t.minPerOrder ?? t.minQuantity ?? 1,\r\n maxPerOrder: t.maxPerOrder ?? t.maxQuantity ?? 10,\r\n status: mapTicketStatus(t),\r\n salesStartDate:\r\n t.salesBegin || t.salesStart || t.saleBegin || t.onSaleStart || undefined,\r\n salesEndDate: t.salesEnd || t.saleEnd || t.onSaleEnd || undefined,\r\n isDonation: t.type === 2 || t.ticketType === 2 || t.isDonation === true,\r\n isHidden: t.visibility === 2 || t.isHidden === true,\r\n ticketType: t.type || t.ticketType || undefined,\r\n sectionName: t.sectionName || t.section || undefined,\r\n }));\r\n\r\n // Calculate totals from public tickets\r\n const publicTickets = tickets.filter(t => !t.isHidden && !t.isDonation);\r\n const totalRemaining = publicTickets.reduce(\r\n (sum, t) => sum + t.quantityAvailable,\r\n 0\r\n );\r\n const totalCapacity = publicTickets.reduce(\r\n (sum, t) => sum + t.quantityTotal,\r\n 0\r\n );\r\n\r\n // Determine event status\r\n const status = mapEventStatus(domain.status as string, tickets);\r\n\r\n // Build image URL — IMAGE SEAM: image-first precedence (see note).\r\n const rawImage =\r\n rawEvent.image || rawEvent.imageUrl || rawEvent.image_url || '';\r\n const imageUrl = rawImage.startsWith('http')\r\n ? rawImage\r\n : resolveImageUrl(rawImage, { cdn: true });\r\n\r\n // Venue\r\n const venue: VenueData = {\r\n id: rawVenue.id || rawVenue.ID || 0,\r\n name: rawVenue.name || rawVenue.googleLocationNameCache || '',\r\n address: rawVenue.address || rawVenue.googleLocationNameCache || '',\r\n timezone,\r\n serviceFeePercentage: rawVenue.serviceFeePercentage ?? rawVenue.fee ?? 0,\r\n // Legitimate-zero venue fee config (mf#2234's ruled-separate class): a venue\r\n // with no flat service fee is a real 0. Ternary keeps the default without a\r\n // `?? 0` money-field flag (this file also carries the price site above, so a\r\n // file-level escape would wrongly blind it).\r\n serviceFeeCents:\r\n rawVenue.serviceFeeCents == null ? 0 : rawVenue.serviceFeeCents,\r\n taxPercentage: rawVenue.taxPercentage ?? rawVenue.tax ?? 0,\r\n contactEmail: rawVenue.contactEmail || rawVenue.email || undefined,\r\n };\r\n\r\n // Performers — VENUE/PERFORMERS SEAM: raw passthrough map (see note).\r\n const rawPerformers = domain.performers || [];\r\n const performers: PerformerData[] = rawPerformers.map((p: any) => ({\r\n id: p.id || p.ID,\r\n displayName: p.displayName || p.name || '',\r\n profileImage: p.avatar || p.profileImage || undefined,\r\n role: p.role || undefined,\r\n }));\r\n\r\n // FAQs (may not be in API response)\r\n const faqs: FAQItem[] = (domain.faqs || []).map((f: any) => ({\r\n question: f.question || '',\r\n answer: f.answer || '',\r\n }));\r\n\r\n // Series showtimes → occurrences. A single series-event detail carries a\r\n // `showtimes` array; the v2 <ShowtimeDesigns> renders event.seriesOccurrences\r\n // under \"Upcoming Shows\". Without this map the section is always empty.\r\n const seriesOccurrences: SeriesOccurrence[] = (domain.showtimes || [])\r\n .filter((s: any) => s.published !== false)\r\n .map((s: any, i: number) => ({\r\n id: s.id ?? s.eventId ?? i + 1,\r\n slug: s.slug || '',\r\n startDateTime: s.startDateTime || s.startTime || '',\r\n endDateTime: s.endDateTime || s.endTime || '',\r\n status: (s.ctaState?.reason === 'sold_out'\r\n ? 'sold_out'\r\n : 'on_sale') as EventStatus,\r\n ctaText: s.ctaState?.text || 'Get tickets',\r\n ctaDisabled: s.ctaState?.disabled || false,\r\n }));\r\n\r\n // Date in venue timezone\r\n const date = domain.startDateTime\r\n ? formatDateKey(domain.startDateTime, timezone)\r\n : '';\r\n\r\n return {\r\n id: domain.id,\r\n title: domain.title || '',\r\n slug: domain.slug || '',\r\n description: domain.description || domain.eventSummary || '',\r\n date,\r\n startDateTime: domain.startDateTime || '',\r\n endDateTime: domain.endDateTime || '',\r\n doorsOpenTime: domain.doorsOpenTime || '',\r\n timezone,\r\n imageUrl,\r\n venue,\r\n performers,\r\n tickets,\r\n faqs,\r\n disclaimer: domain.disclaimer || undefined,\r\n status,\r\n eventSeriesId: domain.eventSeriesId || undefined,\r\n seriesOccurrences: seriesOccurrences.length ? seriesOccurrences : undefined,\r\n // Public API hides the actual password (json:\"-\") and exposes a boolean\r\n // `hasPassword` flag instead; fall back to a populated `password` for\r\n // admin-shaped payloads.\r\n isPasswordProtected: domain.hasPassword === true || !!domain.password,\r\n isRegistrationEvent:\r\n domain.ticketType === 1 || domain.eventTicketingType === 1,\r\n totalTicketsRemaining: totalRemaining,\r\n totalTicketsCapacity: totalCapacity,\r\n stageName: domain.stage?.name || domain.stageName || undefined,\r\n ageRestriction: domain.ageRestriction || undefined,\r\n displayAgeRestriction: domain.displayAgeRestriction ?? false,\r\n displayStartTime: domain.displayStartTime ?? true,\r\n displayEndTime: domain.displayEndTime ?? true,\r\n displayDoorsTime: domain.displayDoorsTime ?? true,\r\n collectionId: domain.collectionId || undefined, // domain folds collection_id\r\n venueAddressLines: parseAddress(\r\n rawVenue.address || rawVenue.googleLocationNameCache || ''\r\n ),\r\n googleMapsUrl: buildGoogleMapsUrl(\r\n rawVenue.address || rawVenue.googleLocationNameCache || ''\r\n ),\r\n };\r\n}\r\n\r\n/**\r\n * Transform SeriesPageData (from the series API) into our clean EventData interface.\r\n * Sets eventSeriesId so EventExperience renders in full-width series mode.\r\n */\r\nexport function transformSeriesPageData(series: SeriesPageData): EventData {\r\n // Implementation moved to the canonical transformer home\r\n // (`$lib/api/transformers/series.ts`). This export stays as a thin delegate\r\n // so SeriesPage.svelte and other consumers don't churn.\r\n return parseSeries(series);\r\n}\r\n\r\n/**\r\n * Transform PublicCollectionData (from the collection API) into our clean EventData interface.\r\n * Sets collectionId so EventExperience renders in full-width collection mode.\r\n */\r\nexport function transformCollectionData(\r\n collection: PublicCollectionData\r\n): EventData {\r\n // Implementation moved to the canonical transformer home\r\n // (`$lib/api/transformers/collection.ts`). This export stays as a thin\r\n // delegate so CollectionView.svelte and other consumers don't churn.\r\n return parseCollection(collection);\r\n}\r\n"],"names":["parseAddress","addressStr","parts","p","stateZipMatch","state","zip","city","lines","buildGoogleMapsUrl","address","parseSeries","series","seriesAny","timezone","getIANATimezone","imageUrl","resolveImageUrl","seriesOccurrences","occ","occVenue","occVenueName","occVenueAddress","firstOcc","date","formatDateKey","rawFaq","faqs","f","performers","index","venueAddress","parseCollection","collection","collectionEvents","ev","finitePriceOrNull","value","n","mapTicketStatus","ticket","now","browserNow","remaining","salesBegin","salesEnd","salesEndAt","toInstantOrNull","compareInstants","salesBeginAt","mapEventStatus","rawStatus","tickets","t","publicTickets","transformApiEvent","rawEvent","domain","parseEvent","rawVenue","totalRemaining","sum","totalCapacity","status","rawImage","venue","s","i","transformSeriesPageData","transformCollectionData"],"mappings":"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"}
|