@getmicdrop/venue-calendar 4.2.2 → 4.2.4

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.
Files changed (101) hide show
  1. package/dist/{ArrowLeftIcon-DQ-h5PcH.js → ArrowLeftIcon-BGd-X_Qm.js} +1 -1
  2. package/dist/{Badge-M6qZyfTF.js → Badge-Bv_MT-Vt.js} +10 -9
  3. package/dist/BlockPlaceholder-Wl39H9Z7.js +52 -0
  4. package/dist/Button-C4uFgl7L.js +2141 -0
  5. package/dist/{Calendar-BX22svpz.js → Calendar-DafPX3k_.js} +292 -290
  6. package/dist/CalendarFoundryView-COkX5oQx.js +22292 -0
  7. package/dist/{CalendarIcon-DR197Ioz.js → CalendarIcon-CeQ1_FWq.js} +1 -1
  8. package/dist/{CartView-B2VtVvZM.js → CartView-nvVA8vWk.js} +651 -644
  9. package/dist/Checkout-BphC7gak.js +1807 -0
  10. package/dist/{Checkout-BTqfjvDp.js → Checkout-Ow2x8cNL.js} +92 -91
  11. package/dist/{CheckoutTimer-Cng5tcGO.js → CheckoutTimer-DJlt5M2b.js} +5 -5
  12. package/dist/{ChevronDownIcon-C_RiK-BW.js → ChevronDownIcon-y7oNyepp.js} +1 -1
  13. package/dist/{ClockIcon-BOJENuPq.js → ClockIcon-BGTz8dak.js} +1 -1
  14. package/dist/{CloseIcon-Rc7tpX2a.js → CloseIcon-C9SHeORJ.js} +1 -1
  15. package/dist/CloseOutline-qpVa62mu.js +13 -0
  16. package/dist/{CollectionView-A1zP8yz5.js → CollectionView-BAIlcuqO.js} +13 -12
  17. package/dist/CropImage-B0tj2_uN.js +404 -0
  18. package/dist/Event-CTaXLze_.js +3034 -0
  19. package/dist/{EventPage-hxrPKPhm.js → EventPage-LuA-aEYZ.js} +168 -167
  20. package/dist/{Heading-B7VYbbfJ.js → Heading-Dfo3bjyn.js} +4 -3
  21. package/dist/Image-B3cEndLZ.js +87 -0
  22. package/dist/Input-iXqece97.js +487 -0
  23. package/dist/{MailIcon-rxvK4Wga.js → MailIcon-xtNog-7h.js} +1 -1
  24. package/dist/{Modal-BVD2O8hd.js → Modal-CqzRblrN.js} +2 -2
  25. package/dist/{ModalFooter-CfboEe1M.js → ModalFooter-DHhBLDbO.js} +59 -57
  26. package/dist/{PasswordInput-BAlq2j97.js → PasswordInput-Czm5Pe_p.js} +4 -4
  27. package/dist/{ScarcityBadge-h7ygRD_C.js → ScarcityBadge-CdgQmt1D.js} +4 -4
  28. package/dist/{SeriesPage-D-cwUNZM.js → SeriesPage-DfCyV1ka.js} +5 -5
  29. package/dist/{Spinner-ChTN7t75.js → Spinner-BwiDys7C.js} +3 -3
  30. package/dist/{Success-CIAX5zmz.js → Success-CsYxRkLE.js} +81 -80
  31. package/dist/{TagIcon-V0qMu9wS.js → TagIcon-DJSTnymB.js} +1 -1
  32. package/dist/{ViewTicketsEmbed-By-IcUJZ.js → ViewTicketsEmbed-CKucKt-W.js} +67 -66
  33. package/dist/{WarningCircleIcon-D32h6VDr.js → WarningCircleIcon-BKSQbu7p.js} +1 -1
  34. package/dist/{WarningTriIcon-BPDrCiKU.js → WarningTriIcon-DgwTwlxe.js} +1 -1
  35. package/dist/{__SKIP_PATHS__-DirqE5Xe.js → __SKIP_PATHS__-BTbGfN80.js} +1 -1
  36. package/dist/api/api.cjs +1 -1
  37. package/dist/api/api.mjs +187 -181
  38. package/dist/api/client.d.ts +16 -0
  39. package/dist/api/types.d.ts +528 -528
  40. package/dist/{browser-25hnscei.js → browser-Um8Y6K99.js} +7 -7
  41. package/dist/{colors-5ifWyP6H.js → colors-DeJAzg_q.js} +60 -59
  42. package/dist/{constants-Dcj3kgZE.js → constants-OTS1wmXZ.js} +76 -73
  43. package/dist/{copyToClipboard-CNxFW3ni.js → copyToClipboard-CRDdh3r8.js} +1 -1
  44. package/dist/{createI18n-08XGDh1_.js → createI18n-DBUVdZra.js} +1 -1
  45. package/dist/{event-Cp5D4ctZ.js → event-BrptLro8.js} +1 -1
  46. package/dist/{event-transform-DSuEpMxr.js → event-transform-BaJ7M1DL.js} +4 -4
  47. package/dist/{eventStatusStream-CIZVH5Lp.js → eventStatusStream-sq6uPnMe.js} +6 -6
  48. package/dist/{allowlist-BiUmz2AF.js → href-DJeJpcO9.js} +21 -4
  49. package/dist/{i18n-CEeBd5Hi.js → i18n-GzfxX0Ad.js} +11 -11
  50. package/dist/imageOptimizer-CB3wKJU8.js +47 -0
  51. package/dist/{labels-CtyfffZI.js → labels-CqodQmuY.js} +1 -1
  52. package/dist/{modalManager.svelte-BH-QJm9M.js → modalManager.svelte-OSPs3dSW.js} +2 -2
  53. package/dist/{order-totals-hcdvbJvw.js → order-totals-BAKL9cOB.js} +15 -13
  54. package/dist/{orders-tzTe2hNM.js → orders-NZP81zYT.js} +210 -198
  55. package/dist/{Input-W-QE9nWi.js → phoneUtils-BJeAIGYo.js} +694 -1132
  56. package/dist/{pixels-7kl8Fcjl.js → pixels-BBruZizp.js} +8 -7
  57. package/dist/{promo-BrMcoTYh.js → promo-DtLZRskK.js} +3 -3
  58. package/dist/{serverTotals-CpRaEe9K.js → serverTotals-BO126TTd.js} +1 -1
  59. package/dist/{shareCopyFeedback.svelte-DsyhrfiP.js → shareCopyFeedback.svelte-BRInK1hH.js} +25 -25
  60. package/dist/{showcase-state-options-B2XfybZq.js → showcase-state-options-BoJxiEsg.js} +1 -1
  61. package/dist/sortable.esm--ZtTTTPp.js +1451 -0
  62. package/dist/timezones-C5l0SSHK.js +2030 -0
  63. package/dist/{to-event-card-props-VPne8W4I.js → to-event-card-props-CO3aPBdH.js} +24 -23
  64. package/dist/{utils-DJVlBMbw.js → utils-C8GgxJMv.js} +30 -30
  65. package/dist/venue-calendar.css +1 -1
  66. package/dist/venue-calendar.es.js +1291 -1151
  67. package/dist/venue-calendar.iife.js +63 -62
  68. package/dist/venue-calendar.umd.js +60 -59
  69. package/dist/z-index-B0Vk_Mp1.js +27 -0
  70. package/package.json +220 -220
  71. package/dist/BlockPlaceholder-IOcqxTx2.js +0 -62
  72. package/dist/CalendarFoundryView-Ct39xn0Q.js +0 -23561
  73. package/dist/Checkout-CXcjQfbc.js +0 -1805
  74. package/dist/Event-C_0lUZvV.js +0 -3040
  75. package/dist/Image-DBJAfLYe.js +0 -81
  76. package/dist/cancel-Cq2Q8qW7.js +0 -59
  77. package/dist/timezones-Cq1mLtfM.js +0 -4170
  78. /package/dist/locales/{4.2.2 → 4.2.4}/flow/de.js +0 -0
  79. /package/dist/locales/{4.2.2 → 4.2.4}/flow/es.js +0 -0
  80. /package/dist/locales/{4.2.2 → 4.2.4}/flow/fr.js +0 -0
  81. /package/dist/locales/{4.2.2 → 4.2.4}/flow/id.js +0 -0
  82. /package/dist/locales/{4.2.2 → 4.2.4}/flow/it.js +0 -0
  83. /package/dist/locales/{4.2.2 → 4.2.4}/flow/ja.js +0 -0
  84. /package/dist/locales/{4.2.2 → 4.2.4}/flow/ko.js +0 -0
  85. /package/dist/locales/{4.2.2 → 4.2.4}/flow/nl.js +0 -0
  86. /package/dist/locales/{4.2.2 → 4.2.4}/flow/pl.js +0 -0
  87. /package/dist/locales/{4.2.2 → 4.2.4}/flow/pt-br.js +0 -0
  88. /package/dist/locales/{4.2.2 → 4.2.4}/flow/tr.js +0 -0
  89. /package/dist/locales/{4.2.2 → 4.2.4}/flow/zh.js +0 -0
  90. /package/dist/locales/{4.2.2 → 4.2.4}/main/de.js +0 -0
  91. /package/dist/locales/{4.2.2 → 4.2.4}/main/es.js +0 -0
  92. /package/dist/locales/{4.2.2 → 4.2.4}/main/fr.js +0 -0
  93. /package/dist/locales/{4.2.2 → 4.2.4}/main/id.js +0 -0
  94. /package/dist/locales/{4.2.2 → 4.2.4}/main/it.js +0 -0
  95. /package/dist/locales/{4.2.2 → 4.2.4}/main/ja.js +0 -0
  96. /package/dist/locales/{4.2.2 → 4.2.4}/main/ko.js +0 -0
  97. /package/dist/locales/{4.2.2 → 4.2.4}/main/nl.js +0 -0
  98. /package/dist/locales/{4.2.2 → 4.2.4}/main/pl.js +0 -0
  99. /package/dist/locales/{4.2.2 → 4.2.4}/main/pt-br.js +0 -0
  100. /package/dist/locales/{4.2.2 → 4.2.4}/main/tr.js +0 -0
  101. /package/dist/locales/{4.2.2 → 4.2.4}/main/zh.js +0 -0
@@ -1,528 +1,528 @@
1
- /**
2
- * API Types for MicDrop Public Checkout API
3
- *
4
- * These types define the shape of API requests and responses
5
- * for the public checkout flow (no authentication required).
6
- */
7
-
8
- import type { EventId, SeriesId } from '@getmicdrop/svelte-components';
9
-
10
- // ============================================================================
11
- // Payment & Orders
12
- // ============================================================================
13
-
14
- export interface PaymentIntentRequest {
15
- IP?: string;
16
- productQuantities: Record<string | number, number>;
17
- }
18
-
19
- export interface PaymentIntentResponse {
20
- client_secret: string;
21
- // The residual the buyer is charged via Stripe, in int cents (net of any
22
- // applied gift card). This is the field the checkout Total keys off. The
23
- // orders-service response has NO `amount` key — an earlier `amount` here was
24
- // never populated, so Total rendered $0.00 on every paid order.
25
- stripe_amount?: number;
26
- amount_total: number;
27
- subtotal?: number;
28
- tax?: number;
29
- tax_amount_exclusive?: number;
30
- service_fee?: number;
31
- // The buyer's SECOND fee rail (MIC-1760) — card processing, grossed up and
32
- // passed through at cost, in int cents. orders-service reports it separately
33
- // from `service_fee` and folds it into `stripe_amount`. The PUBLIC receipt
34
- // merges the two into one "Service fees" row (Gus, 2026-08-16), which is also
35
- // what the pre-server estimate (`estimateBuyerFee`) has always shown; leaving
36
- // it unread made the priced total exceed the rows on screen by this amount.
37
- card_processing?: number;
38
- discount?: number;
39
- gift_card_amount?: number;
40
- gift_card_code?: string;
41
- stripe_publishable_key?: string;
42
- // ISO-4217 currency (Stripe returns it lowercase). Feeds the checkout totals.
43
- currency?: string;
44
- }
45
-
46
- export interface CompleteReservationResponse {
47
- success: boolean;
48
- message?: string;
49
- error?: string;
50
- }
51
-
52
- export interface CancelReservationResponse {
53
- success: boolean;
54
- message?: string;
55
- error?: string;
56
- }
57
-
58
- export interface CreateOrderRequest {
59
- eventId: string | number;
60
- promoCode?: string;
61
- }
62
-
63
- export interface CreateOrderResponse {
64
- uuid: string;
65
- }
66
-
67
- export interface ValidatePaymentRequest {
68
- id: string;
69
- paymentIntentId: string;
70
- tickets: Record<string | number, number>;
71
- firstName: string;
72
- lastName: string;
73
- email: string;
74
- /** Buyer phone collected at checkout (MIC-1705). Persisted on the order. */
75
- phone?: string;
76
- /** Answer to the venue's custom checkout question (MIC-1705). */
77
- questionAnswer?: string;
78
- paymentMethod: string;
79
- mailingList?: boolean;
80
- saleType?: string;
81
- attendees?: AttendeeInfo[];
82
- }
83
-
84
- export interface AttendeeInfo {
85
- ticketId: string | number;
86
- firstName: string;
87
- lastName: string;
88
- email: string;
89
- }
90
-
91
- export interface ValidatePaymentResponse {
92
- success: boolean;
93
- status: string;
94
- orderUUID?: string;
95
- error?: string;
96
- /**
97
- * Fail-closed FLOOR fields. When the event is canceled/postponed between
98
- * cart-creation and finalize, the backend gate refuses to capture the charge
99
- * and sets one of these (alongside `success: false`). Either flag means the
100
- * buyer was NOT charged; the checkout raises the charge-aware
101
- * EventUnavailableModal instead of navigating to success. Optional because
102
- * they are only present on the rejected-finalize path.
103
- */
104
- eventCanceled?: boolean;
105
- eventPostponed?: boolean;
106
- }
107
-
108
- // ============================================================================
109
- // Session Management
110
- // ============================================================================
111
-
112
- export interface ExtendSessionRequest {
113
- orderUuid: string;
114
- }
115
-
116
- export interface ExtendSessionResponse {
117
- success: boolean;
118
- newExpiryTime?: string;
119
- remainingExtensions?: number;
120
- error?: string;
121
- /**
122
- * HTTP status code from the backend on failure. Populated on non-OK
123
- * responses so callers can distinguish "session already expired"
124
- * (typically 410 Gone) from "max extensions reached" or generic 5xx.
125
- * Undefined on network errors and on success.
126
- */
127
- statusCode?: number;
128
- }
129
-
130
- export interface SessionStatus {
131
- expiresAt?: string;
132
- extensionCount?: number;
133
- remainingExtensions?: number;
134
- canExtend?: boolean;
135
- reservationCount?: number;
136
- error?: string;
137
- // 404 from /orders/session/{uuid}: the cart's reservations are
138
- // gone (expired or never existed). Callers polling on an interval
139
- // MUST treat this as terminal and stop — see CartView.svelte.
140
- notFound?: boolean;
141
- // Live event status the session heartbeat used to piggyback so the public
142
- // checkout could flip the buy button on cancel/postpone. As of MIC-1408 the
143
- // checkout rides a dedicated per-event SSE stream (subscribeEventStatus)
144
- // instead, flipping instantly rather than within ~30s — this field is no
145
- // longer consumed there. One of the auth-service Event statuses
146
- // (draft|live|started|ended|canceled|postponed). Absent if the lookup failed.
147
- eventStatus?: string;
148
- }
149
-
150
- // ============================================================================
151
- // Promo Codes
152
- // ============================================================================
153
-
154
- export interface PromoValidationResponse {
155
- valid: boolean;
156
- revealHiddenTickets?: boolean;
157
- revealTicketIds?: number[];
158
- provideDiscount?: boolean;
159
- discountType?: 'percentage' | 'fixed';
160
- amount?: number;
161
- code?: string;
162
- /**
163
- * The code's ticket scope, exactly as the server sends it — `false` here plus
164
- * a non-empty `applyToTicketIds` means the code covers only those types.
165
- * Dropped by validatePromoCode until MIC-2575, which is why a code scoped to
166
- * one type discounted every type. Read through promoTicketIdsFromValidation,
167
- * never field-by-field at a call site.
168
- */
169
- appliesToAllTickets?: boolean;
170
- applyToTicketIds?: number[];
171
- error?: string;
172
- }
173
-
174
- /**
175
- * RAW body of GET /api/v2/public/promo-codes/validate/{eventId}/{code}.
176
- *
177
- * The live endpoint reports the discount magnitude as `discount` and the code
178
- * as `name` — NOT `amount`/`code`. validatePromoCode normalises this into the
179
- * client-facing PromoValidationResponse (amount/code) so callers read one shape.
180
- * The legacy `amount`/`code` keys stay optional here so an older payload (or a
181
- * mocked one) still maps through the same fallback. Verified 2026-07-11 against
182
- * two live promos (seeded GUARD20PCT + pre-existing EARLY20).
183
- */
184
- export interface RawPromoValidationBody {
185
- valid?: boolean;
186
- revealHiddenTickets?: boolean;
187
- revealTicketIds?: number[];
188
- provideDiscount?: boolean;
189
- discountType?: 'percentage' | 'fixed';
190
- /** Discount magnitude — real server field. */
191
- discount?: number;
192
- /** Legacy/mocked magnitude field — fallback only. */
193
- amount?: number;
194
- /** Promo code — real server field. */
195
- name?: string;
196
- /** Legacy/mocked code field — fallback only. */
197
- code?: string;
198
- appliesToAllTickets?: boolean;
199
- applyToTicketIds?: number[];
200
- isGlobal?: boolean;
201
- ticketLimit?: number;
202
- error?: string;
203
- }
204
-
205
- export interface HasPromoCodesResponse {
206
- hasPromoCodes: boolean;
207
- }
208
-
209
- // ============================================================================
210
- // Orders
211
- // ============================================================================
212
-
213
- export interface Order {
214
- uuid: string;
215
- id?: number;
216
- customerEmail: string;
217
- customerFirstName?: string;
218
- customerLastName?: string;
219
- status: string;
220
- totalAmount: number;
221
- subtotal?: number;
222
- serviceFeesAmount: number;
223
- taxAmount: number;
224
- discount?: number;
225
- paymentIntentId?: string;
226
- paymentMethod?: string;
227
- purchasedTickets: PurchasedTicket[];
228
- createdAt?: string;
229
- updatedAt?: string;
230
- }
231
-
232
- export interface PurchasedTicket {
233
- uuid: string;
234
- id?: number;
235
- ticketNumber?: string;
236
- orderId?: string | number;
237
- attendeeFirstName?: string;
238
- attendeeLastName?: string;
239
- attendeeEmail?: string;
240
- ticketName: string;
241
- ticketTypeId?: number;
242
- purchasePrice: number;
243
- status?: string;
244
- checkedIn?: boolean;
245
- checkedInAt?: string;
246
- }
247
-
248
- // ============================================================================
249
- // Events
250
- // ============================================================================
251
-
252
- export interface Event {
253
- eventID: number;
254
- id?: number;
255
- name: string;
256
- title?: string;
257
- slug?: string;
258
- description?: string;
259
- date: string;
260
- startDateTime?: string;
261
- endDateTime?: string;
262
- doorsOpenTime?: string;
263
- timezone?: string;
264
- venueId?: number;
265
- venueName?: string;
266
- venueAddress?: string;
267
- location?: string;
268
- imageUrl?: string;
269
- imageURL?: string;
270
- status?: string;
271
- isPublished?: boolean;
272
- isCancelled?: boolean;
273
- availableTickets?: AvailableTicket[];
274
- ticketsAvailable?: number;
275
- ticketsSold?: number;
276
- minPrice?: number;
277
- maxPrice?: number;
278
- ctaText?: string;
279
- ctaState?: 'available' | 'sold_out' | 'coming_soon' | 'ended';
280
- showPerformers?: boolean;
281
- eventSeriesId?: number;
282
- seriesInstanceNumber?: number;
283
- // ── Passthrough seams (events convergence) ─────────────────────────────
284
- // Carried verbatim from the wire by parseEvent so view-shape projections
285
- // (utils/event-transform.js, public-calendar-flow/transform.ts) compose the
286
- // canonical instead of reading wire fields directly. The object-valued
287
- // fields are intentionally raw (see composition note in
288
- // api/transformers/event.ts) — their consumers own the wire variance.
289
- timeZone?: string;
290
- /**
291
- * The operator's sales dial — effective on public payloads (MIC-2455).
292
- * `computeCtaState` refuses on `paused` / `ended` / `sold_out` and leaves
293
- * `not_started` to the sale-window logic.
294
- */
295
- salesStatus?: string;
296
- eventSummary?: string;
297
- password?: string;
298
- hasPassword?: boolean;
299
- disclaimer?: string;
300
- ticketType?: number;
301
- eventTicketingType?: number;
302
- ageRestriction?: number;
303
- displayAgeRestriction?: boolean;
304
- displayStartTime?: boolean;
305
- displayEndTime?: boolean;
306
- displayDoorsTime?: boolean;
307
- collectionId?: number;
308
- ticketsRemaining?: number;
309
- ticketsTotal?: number;
310
- hasHiddenTickets?: boolean;
311
- stage?: { name?: string } | null;
312
- stageName?: string;
313
- // Widget-filter dimensions on the public month feeds (saved-widget engine):
314
- // stage/category/performer/collection data the embed adapts onto the SC
315
- // FilterableEvent view (lib/widget-filters.js).
316
- stageId?: number;
317
- eventCategoryTypes?: number[] | null;
318
- publicPerformers?: Array<{
319
- id?: number;
320
- displayName?: string;
321
- image?: string;
322
- }> | null;
323
- collectionIds?: number[] | null;
324
- venue?: Record<string, any>;
325
- performers?: Record<string, any>[];
326
- faqs?: Record<string, any>[];
327
- showtimes?: Record<string, any>[];
328
- purchasedTickets?: Record<string, any>[];
329
- }
330
-
331
- export interface AvailableTicket {
332
- id: number;
333
- name: string;
334
- description?: string;
335
- price: number;
336
- quantity: number;
337
- quantitySold?: number;
338
- quantityAvailable?: number;
339
- minPerOrder?: number;
340
- maxPerOrder?: number;
341
- saleStartDate?: string;
342
- saleEndDate?: string;
343
- isHidden?: boolean;
344
- revealWithPromoCode?: boolean;
345
- ticketType?: number; // 0 = GA, 1 = assigned
346
- sectionId?: number;
347
- sortOrder?: number;
348
- }
349
-
350
- export interface EventPerformersResponse {
351
- performers: Performer[];
352
- showPerformers: boolean;
353
- }
354
-
355
- export interface Performer {
356
- id: number;
357
- displayName: string;
358
- avatar?: string;
359
- order?: number;
360
- }
361
-
362
- // ============================================================================
363
- // Venues
364
- // ============================================================================
365
-
366
- export interface Venue {
367
- id: number;
368
- name: string;
369
- slug?: string;
370
- address?: string;
371
- googleLocationNameCache?: string;
372
- city?: string;
373
- state?: string;
374
- zipCode?: string;
375
- country?: string;
376
- timezone?: string;
377
- logoUrl?: string;
378
- serviceFeePercentage?: number;
379
- serviceFeeCents?: number;
380
- /** True = the venue absorbs the service fee; false/absent = the buyer pays it. */
381
- absorbServiceFee?: boolean;
382
- taxPercentage?: number;
383
- organizationId?: number;
384
- }
385
-
386
- // ============================================================================
387
- // Series
388
- // ============================================================================
389
-
390
- export interface SeriesOccurrence {
391
- eventId: EventId;
392
- date: string;
393
- startDateTime: string;
394
- endDateTime?: string;
395
- instanceNumber: number;
396
- status: string;
397
- ticketsAvailable?: number;
398
- ctaState?: string;
399
- }
400
-
401
- export interface SeriesOccurrencesResponse {
402
- seriesId: SeriesId;
403
- occurrences: SeriesOccurrence[];
404
- }
405
-
406
- // ============================================================================
407
- // Series Page
408
- // ============================================================================
409
-
410
- export interface SeriesPageData {
411
- id: number;
412
- title: string;
413
- description: string;
414
- eventSummary?: string;
415
- image?: string;
416
- timeZone: string;
417
- venue: {
418
- id: number;
419
- name: string;
420
- location?: string;
421
- googleLocationNameCache?: string;
422
- faq?: Array<{ question: string; answer: string }> | string;
423
- disclaimer?: string;
424
- };
425
- performers: Array<{
426
- displayName: string;
427
- firstName?: string;
428
- lastName?: string;
429
- profileImage?: string;
430
- }>;
431
- occurrences: Array<{
432
- id: number;
433
- slug: string;
434
- startDateTime: string;
435
- endDateTime: string;
436
- ctaState: {
437
- text: string;
438
- disabled: boolean;
439
- reason?: string;
440
- };
441
- /**
442
- * The occurrence's OWN location. The backend emits this ONLY when the
443
- * occurrence has been moved off the series master venue/stage, so
444
- * presence === "this showtime is not where the rest of the series is".
445
- * Absent for every ordinary occurrence.
446
- */
447
- venue?: {
448
- id: number;
449
- name: string;
450
- address?: string;
451
- city?: string;
452
- state?: string;
453
- googleLocationNameCache?: string;
454
- stageName?: string;
455
- };
456
- }>;
457
- }
458
-
459
- // ============================================================================
460
- // Entity resolution (type-agnostic public id → concrete entity)
461
- // ============================================================================
462
-
463
- /**
464
- * Response shape of `/api/v2/public/resolve/{id}`. The embed deep-link is
465
- * type-agnostic (`#{id}-{slug}`), so the backend resolves which kind of entity
466
- * the id points at; `data` is the corresponding event / series / collection
467
- * payload (same shape the dedicated fetchers return for that type).
468
- */
469
- export interface ResolvedEntity {
470
- type: 'event' | 'series' | 'collection';
471
- id: string | number;
472
- title?: string;
473
- data: any;
474
- }
475
-
476
- // ============================================================================
477
- // Public Collection
478
- // ============================================================================
479
-
480
- export interface PublicCollectionData {
481
- id: number;
482
- collectionTitle: string;
483
- summary?: string;
484
- description?: string;
485
- coverImage?: string;
486
- events: Array<{
487
- id: number;
488
- title: string;
489
- slug?: string;
490
- startDateTime?: string;
491
- endDateTime?: string;
492
- image?: string;
493
- status?: string;
494
- venue?: { name: string };
495
- minPrice?: number;
496
- }>;
497
- // Password-gate variant: a protected collection responds with a gate signal
498
- // instead of the collection body (either `passwordRequired: true` or an
499
- // `error.code === 'PASSWORD_REQUIRED'`). Modelled here as optional wire
500
- // fields so the view can read them without an `as any` escape.
501
- passwordRequired?: boolean;
502
- error?: { code?: string };
503
- }
504
-
505
- // ============================================================================
506
- // API Configuration
507
- // ============================================================================
508
-
509
- export interface ApiConfig {
510
- baseUrl?: string;
511
- timeout?: number;
512
- /** Number of additional attempts after the first try (default: 2 = 3 total tries). */
513
- retries?: number;
514
- /** Base delay before the first retry; doubles each attempt (default: 500ms). */
515
- retryDelay?: number;
516
- onError?: (_error: Error) => void;
517
- }
518
-
519
- // ============================================================================
520
- // Generic API Response
521
- // ============================================================================
522
-
523
- export interface ApiResponse<T> {
524
- success: boolean;
525
- data?: T;
526
- error?: string;
527
- statusCode?: number;
528
- }
1
+ /**
2
+ * API Types for MicDrop Public Checkout API
3
+ *
4
+ * These types define the shape of API requests and responses
5
+ * for the public checkout flow (no authentication required).
6
+ */
7
+
8
+ import type { EventId, SeriesId } from '@getmicdrop/svelte-components';
9
+
10
+ // ============================================================================
11
+ // Payment & Orders
12
+ // ============================================================================
13
+
14
+ export interface PaymentIntentRequest {
15
+ IP?: string;
16
+ productQuantities: Record<string | number, number>;
17
+ }
18
+
19
+ export interface PaymentIntentResponse {
20
+ client_secret: string;
21
+ // The residual the buyer is charged via Stripe, in int cents (net of any
22
+ // applied gift card). This is the field the checkout Total keys off. The
23
+ // orders-service response has NO `amount` key — an earlier `amount` here was
24
+ // never populated, so Total rendered $0.00 on every paid order.
25
+ stripe_amount?: number;
26
+ amount_total: number;
27
+ subtotal?: number;
28
+ tax?: number;
29
+ tax_amount_exclusive?: number;
30
+ service_fee?: number;
31
+ // The buyer's SECOND fee rail (MIC-1760) — card processing, grossed up and
32
+ // passed through at cost, in int cents. orders-service reports it separately
33
+ // from `service_fee` and folds it into `stripe_amount`. The PUBLIC receipt
34
+ // merges the two into one "Service fees" row (Gus, 2026-08-16), which is also
35
+ // what the pre-server estimate (`estimateBuyerFee`) has always shown; leaving
36
+ // it unread made the priced total exceed the rows on screen by this amount.
37
+ card_processing?: number;
38
+ discount?: number;
39
+ gift_card_amount?: number;
40
+ gift_card_code?: string;
41
+ stripe_publishable_key?: string;
42
+ // ISO-4217 currency (Stripe returns it lowercase). Feeds the checkout totals.
43
+ currency?: string;
44
+ }
45
+
46
+ export interface CompleteReservationResponse {
47
+ success: boolean;
48
+ message?: string;
49
+ error?: string;
50
+ }
51
+
52
+ export interface CancelReservationResponse {
53
+ success: boolean;
54
+ message?: string;
55
+ error?: string;
56
+ }
57
+
58
+ export interface CreateOrderRequest {
59
+ eventId: string | number;
60
+ promoCode?: string;
61
+ }
62
+
63
+ export interface CreateOrderResponse {
64
+ uuid: string;
65
+ }
66
+
67
+ export interface ValidatePaymentRequest {
68
+ id: string;
69
+ paymentIntentId: string;
70
+ tickets: Record<string | number, number>;
71
+ firstName: string;
72
+ lastName: string;
73
+ email: string;
74
+ /** Buyer phone collected at checkout (MIC-1705). Persisted on the order. */
75
+ phone?: string;
76
+ /** Answer to the venue's custom checkout question (MIC-1705). */
77
+ questionAnswer?: string;
78
+ paymentMethod: string;
79
+ mailingList?: boolean;
80
+ saleType?: string;
81
+ attendees?: AttendeeInfo[];
82
+ }
83
+
84
+ export interface AttendeeInfo {
85
+ ticketId: string | number;
86
+ firstName: string;
87
+ lastName: string;
88
+ email: string;
89
+ }
90
+
91
+ export interface ValidatePaymentResponse {
92
+ success: boolean;
93
+ status: string;
94
+ orderUUID?: string;
95
+ error?: string;
96
+ /**
97
+ * Fail-closed FLOOR fields. When the event is canceled/postponed between
98
+ * cart-creation and finalize, the backend gate refuses to capture the charge
99
+ * and sets one of these (alongside `success: false`). Either flag means the
100
+ * buyer was NOT charged; the checkout raises the charge-aware
101
+ * EventUnavailableModal instead of navigating to success. Optional because
102
+ * they are only present on the rejected-finalize path.
103
+ */
104
+ eventCanceled?: boolean;
105
+ eventPostponed?: boolean;
106
+ }
107
+
108
+ // ============================================================================
109
+ // Session Management
110
+ // ============================================================================
111
+
112
+ export interface ExtendSessionRequest {
113
+ orderUuid: string;
114
+ }
115
+
116
+ export interface ExtendSessionResponse {
117
+ success: boolean;
118
+ newExpiryTime?: string;
119
+ remainingExtensions?: number;
120
+ error?: string;
121
+ /**
122
+ * HTTP status code from the backend on failure. Populated on non-OK
123
+ * responses so callers can distinguish "session already expired"
124
+ * (typically 410 Gone) from "max extensions reached" or generic 5xx.
125
+ * Undefined on network errors and on success.
126
+ */
127
+ statusCode?: number;
128
+ }
129
+
130
+ export interface SessionStatus {
131
+ expiresAt?: string;
132
+ extensionCount?: number;
133
+ remainingExtensions?: number;
134
+ canExtend?: boolean;
135
+ reservationCount?: number;
136
+ error?: string;
137
+ // 404 from /orders/session/{uuid}: the cart's reservations are
138
+ // gone (expired or never existed). Callers polling on an interval
139
+ // MUST treat this as terminal and stop — see CartView.svelte.
140
+ notFound?: boolean;
141
+ // Live event status the session heartbeat used to piggyback so the public
142
+ // checkout could flip the buy button on cancel/postpone. As of MIC-1408 the
143
+ // checkout rides a dedicated per-event SSE stream (subscribeEventStatus)
144
+ // instead, flipping instantly rather than within ~30s — this field is no
145
+ // longer consumed there. One of the auth-service Event statuses
146
+ // (draft|live|started|ended|canceled|postponed). Absent if the lookup failed.
147
+ eventStatus?: string;
148
+ }
149
+
150
+ // ============================================================================
151
+ // Promo Codes
152
+ // ============================================================================
153
+
154
+ export interface PromoValidationResponse {
155
+ valid: boolean;
156
+ revealHiddenTickets?: boolean;
157
+ revealTicketIds?: number[];
158
+ provideDiscount?: boolean;
159
+ discountType?: 'percentage' | 'fixed';
160
+ amount?: number;
161
+ code?: string;
162
+ /**
163
+ * The code's ticket scope, exactly as the server sends it — `false` here plus
164
+ * a non-empty `applyToTicketIds` means the code covers only those types.
165
+ * Dropped by validatePromoCode until MIC-2575, which is why a code scoped to
166
+ * one type discounted every type. Read through promoTicketIdsFromValidation,
167
+ * never field-by-field at a call site.
168
+ */
169
+ appliesToAllTickets?: boolean;
170
+ applyToTicketIds?: number[];
171
+ error?: string;
172
+ }
173
+
174
+ /**
175
+ * RAW body of GET /api/v2/public/promo-codes/validate/{eventId}/{code}.
176
+ *
177
+ * The live endpoint reports the discount magnitude as `discount` and the code
178
+ * as `name` — NOT `amount`/`code`. validatePromoCode normalises this into the
179
+ * client-facing PromoValidationResponse (amount/code) so callers read one shape.
180
+ * The legacy `amount`/`code` keys stay optional here so an older payload (or a
181
+ * mocked one) still maps through the same fallback. Verified 2026-07-11 against
182
+ * two live promos (seeded GUARD20PCT + pre-existing EARLY20).
183
+ */
184
+ export interface RawPromoValidationBody {
185
+ valid?: boolean;
186
+ revealHiddenTickets?: boolean;
187
+ revealTicketIds?: number[];
188
+ provideDiscount?: boolean;
189
+ discountType?: 'percentage' | 'fixed';
190
+ /** Discount magnitude — real server field. */
191
+ discount?: number;
192
+ /** Legacy/mocked magnitude field — fallback only. */
193
+ amount?: number;
194
+ /** Promo code — real server field. */
195
+ name?: string;
196
+ /** Legacy/mocked code field — fallback only. */
197
+ code?: string;
198
+ appliesToAllTickets?: boolean;
199
+ applyToTicketIds?: number[];
200
+ isGlobal?: boolean;
201
+ ticketLimit?: number;
202
+ error?: string;
203
+ }
204
+
205
+ export interface HasPromoCodesResponse {
206
+ hasPromoCodes: boolean;
207
+ }
208
+
209
+ // ============================================================================
210
+ // Orders
211
+ // ============================================================================
212
+
213
+ export interface Order {
214
+ uuid: string;
215
+ id?: number;
216
+ customerEmail: string;
217
+ customerFirstName?: string;
218
+ customerLastName?: string;
219
+ status: string;
220
+ totalAmount: number;
221
+ subtotal?: number;
222
+ serviceFeesAmount: number;
223
+ taxAmount: number;
224
+ discount?: number;
225
+ paymentIntentId?: string;
226
+ paymentMethod?: string;
227
+ purchasedTickets: PurchasedTicket[];
228
+ createdAt?: string;
229
+ updatedAt?: string;
230
+ }
231
+
232
+ export interface PurchasedTicket {
233
+ uuid: string;
234
+ id?: number;
235
+ ticketNumber?: string;
236
+ orderId?: string | number;
237
+ attendeeFirstName?: string;
238
+ attendeeLastName?: string;
239
+ attendeeEmail?: string;
240
+ ticketName: string;
241
+ ticketTypeId?: number;
242
+ purchasePrice: number;
243
+ status?: string;
244
+ checkedIn?: boolean;
245
+ checkedInAt?: string;
246
+ }
247
+
248
+ // ============================================================================
249
+ // Events
250
+ // ============================================================================
251
+
252
+ export interface Event {
253
+ eventID: number;
254
+ id?: number;
255
+ name: string;
256
+ title?: string;
257
+ slug?: string;
258
+ description?: string;
259
+ date: string;
260
+ startDateTime?: string;
261
+ endDateTime?: string;
262
+ doorsOpenTime?: string;
263
+ timezone?: string;
264
+ venueId?: number;
265
+ venueName?: string;
266
+ venueAddress?: string;
267
+ location?: string;
268
+ imageUrl?: string;
269
+ imageURL?: string;
270
+ status?: string;
271
+ isPublished?: boolean;
272
+ isCancelled?: boolean;
273
+ availableTickets?: AvailableTicket[];
274
+ ticketsAvailable?: number;
275
+ ticketsSold?: number;
276
+ minPrice?: number;
277
+ maxPrice?: number;
278
+ ctaText?: string;
279
+ ctaState?: 'available' | 'sold_out' | 'coming_soon' | 'ended';
280
+ showPerformers?: boolean;
281
+ eventSeriesId?: number;
282
+ seriesInstanceNumber?: number;
283
+ // ── Passthrough seams (events convergence) ─────────────────────────────
284
+ // Carried verbatim from the wire by parseEvent so view-shape projections
285
+ // (utils/event-transform.js, public-calendar-flow/transform.ts) compose the
286
+ // canonical instead of reading wire fields directly. The object-valued
287
+ // fields are intentionally raw (see composition note in
288
+ // api/transformers/event.ts) — their consumers own the wire variance.
289
+ timeZone?: string;
290
+ /**
291
+ * The operator's sales dial — effective on public payloads (MIC-2455).
292
+ * `computeCtaState` refuses on `paused` / `ended` / `sold_out` and leaves
293
+ * `not_started` to the sale-window logic.
294
+ */
295
+ salesStatus?: string;
296
+ eventSummary?: string;
297
+ password?: string;
298
+ hasPassword?: boolean;
299
+ disclaimer?: string;
300
+ ticketType?: number;
301
+ eventTicketingType?: number;
302
+ ageRestriction?: number;
303
+ displayAgeRestriction?: boolean;
304
+ displayStartTime?: boolean;
305
+ displayEndTime?: boolean;
306
+ displayDoorsTime?: boolean;
307
+ collectionId?: number;
308
+ ticketsRemaining?: number;
309
+ ticketsTotal?: number;
310
+ hasHiddenTickets?: boolean;
311
+ stage?: { name?: string } | null;
312
+ stageName?: string;
313
+ // Widget-filter dimensions on the public month feeds (saved-widget engine):
314
+ // stage/category/performer/collection data the embed adapts onto the SC
315
+ // FilterableEvent view (lib/widget-filters.js).
316
+ stageId?: number;
317
+ eventCategoryTypes?: number[] | null;
318
+ publicPerformers?: Array<{
319
+ id?: number;
320
+ displayName?: string;
321
+ image?: string;
322
+ }> | null;
323
+ collectionIds?: number[] | null;
324
+ venue?: Record<string, any>;
325
+ performers?: Record<string, any>[];
326
+ faqs?: Record<string, any>[];
327
+ showtimes?: Record<string, any>[];
328
+ purchasedTickets?: Record<string, any>[];
329
+ }
330
+
331
+ export interface AvailableTicket {
332
+ id: number;
333
+ name: string;
334
+ description?: string;
335
+ price: number;
336
+ quantity: number;
337
+ quantitySold?: number;
338
+ quantityAvailable?: number;
339
+ minPerOrder?: number;
340
+ maxPerOrder?: number;
341
+ saleStartDate?: string;
342
+ saleEndDate?: string;
343
+ isHidden?: boolean;
344
+ revealWithPromoCode?: boolean;
345
+ ticketType?: number; // 0 = GA, 1 = assigned
346
+ sectionId?: number;
347
+ sortOrder?: number;
348
+ }
349
+
350
+ export interface EventPerformersResponse {
351
+ performers: Performer[];
352
+ showPerformers: boolean;
353
+ }
354
+
355
+ export interface Performer {
356
+ id: number;
357
+ displayName: string;
358
+ avatar?: string;
359
+ order?: number;
360
+ }
361
+
362
+ // ============================================================================
363
+ // Venues
364
+ // ============================================================================
365
+
366
+ export interface Venue {
367
+ id: number;
368
+ name: string;
369
+ slug?: string;
370
+ address?: string;
371
+ googleLocationNameCache?: string;
372
+ city?: string;
373
+ state?: string;
374
+ zipCode?: string;
375
+ country?: string;
376
+ timezone?: string;
377
+ logoUrl?: string;
378
+ serviceFeePercentage?: number;
379
+ serviceFeeCents?: number;
380
+ /** True = the venue absorbs the service fee; false/absent = the buyer pays it. */
381
+ absorbServiceFee?: boolean;
382
+ taxPercentage?: number;
383
+ organizationId?: number;
384
+ }
385
+
386
+ // ============================================================================
387
+ // Series
388
+ // ============================================================================
389
+
390
+ export interface SeriesOccurrence {
391
+ eventId: EventId;
392
+ date: string;
393
+ startDateTime: string;
394
+ endDateTime?: string;
395
+ instanceNumber: number;
396
+ status: string;
397
+ ticketsAvailable?: number;
398
+ ctaState?: string;
399
+ }
400
+
401
+ export interface SeriesOccurrencesResponse {
402
+ seriesId: SeriesId;
403
+ occurrences: SeriesOccurrence[];
404
+ }
405
+
406
+ // ============================================================================
407
+ // Series Page
408
+ // ============================================================================
409
+
410
+ export interface SeriesPageData {
411
+ id: number;
412
+ title: string;
413
+ description: string;
414
+ eventSummary?: string;
415
+ image?: string;
416
+ timeZone: string;
417
+ venue: {
418
+ id: number;
419
+ name: string;
420
+ location?: string;
421
+ googleLocationNameCache?: string;
422
+ faq?: Array<{ question: string; answer: string }> | string;
423
+ disclaimer?: string;
424
+ };
425
+ performers: Array<{
426
+ displayName: string;
427
+ firstName?: string;
428
+ lastName?: string;
429
+ profileImage?: string;
430
+ }>;
431
+ occurrences: Array<{
432
+ id: number;
433
+ slug: string;
434
+ startDateTime: string;
435
+ endDateTime: string;
436
+ ctaState: {
437
+ text: string;
438
+ disabled: boolean;
439
+ reason?: string;
440
+ };
441
+ /**
442
+ * The occurrence's OWN location. The backend emits this ONLY when the
443
+ * occurrence has been moved off the series master venue/stage, so
444
+ * presence === "this showtime is not where the rest of the series is".
445
+ * Absent for every ordinary occurrence.
446
+ */
447
+ venue?: {
448
+ id: number;
449
+ name: string;
450
+ address?: string;
451
+ city?: string;
452
+ state?: string;
453
+ googleLocationNameCache?: string;
454
+ stageName?: string;
455
+ };
456
+ }>;
457
+ }
458
+
459
+ // ============================================================================
460
+ // Entity resolution (type-agnostic public id → concrete entity)
461
+ // ============================================================================
462
+
463
+ /**
464
+ * Response shape of `/api/v2/public/resolve/{id}`. The embed deep-link is
465
+ * type-agnostic (`#{id}-{slug}`), so the backend resolves which kind of entity
466
+ * the id points at; `data` is the corresponding event / series / collection
467
+ * payload (same shape the dedicated fetchers return for that type).
468
+ */
469
+ export interface ResolvedEntity {
470
+ type: 'event' | 'series' | 'collection';
471
+ id: string | number;
472
+ title?: string;
473
+ data: any;
474
+ }
475
+
476
+ // ============================================================================
477
+ // Public Collection
478
+ // ============================================================================
479
+
480
+ export interface PublicCollectionData {
481
+ id: number;
482
+ collectionTitle: string;
483
+ summary?: string;
484
+ description?: string;
485
+ coverImage?: string;
486
+ events: Array<{
487
+ id: number;
488
+ title: string;
489
+ slug?: string;
490
+ startDateTime?: string;
491
+ endDateTime?: string;
492
+ image?: string;
493
+ status?: string;
494
+ venue?: { name: string };
495
+ minPrice?: number;
496
+ }>;
497
+ // Password-gate variant: a protected collection responds with a gate signal
498
+ // instead of the collection body (either `passwordRequired: true` or an
499
+ // `error.code === 'PASSWORD_REQUIRED'`). Modelled here as optional wire
500
+ // fields so the view can read them without an `as any` escape.
501
+ passwordRequired?: boolean;
502
+ error?: { code?: string };
503
+ }
504
+
505
+ // ============================================================================
506
+ // API Configuration
507
+ // ============================================================================
508
+
509
+ export interface ApiConfig {
510
+ baseUrl?: string;
511
+ timeout?: number;
512
+ /** Number of additional attempts after the first try (default: 2 = 3 total tries). */
513
+ retries?: number;
514
+ /** Base delay before the first retry; doubles each attempt (default: 500ms). */
515
+ retryDelay?: number;
516
+ onError?: (_error: Error) => void;
517
+ }
518
+
519
+ // ============================================================================
520
+ // Generic API Response
521
+ // ============================================================================
522
+
523
+ export interface ApiResponse<T> {
524
+ success: boolean;
525
+ data?: T;
526
+ error?: string;
527
+ statusCode?: number;
528
+ }