@kotao/storefront 0.2.1 → 0.2.3

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 (58) hide show
  1. package/dist/chunk-4OQYNUGM.js +29 -0
  2. package/dist/chunk-4OQYNUGM.js.map +1 -0
  3. package/dist/{chunk-U5677AEA.js → chunk-4TEFGD42.js} +343 -40
  4. package/dist/chunk-4TEFGD42.js.map +1 -0
  5. package/dist/chunk-7MTCGXIG.js +144 -0
  6. package/dist/chunk-7MTCGXIG.js.map +1 -0
  7. package/dist/{chunk-GCQI6OKH.js → chunk-H3D7BAOL.js} +3 -26
  8. package/dist/chunk-H3D7BAOL.js.map +1 -0
  9. package/dist/chunk-IIOHENCG.js +239 -0
  10. package/dist/chunk-IIOHENCG.js.map +1 -0
  11. package/dist/chunk-M7BPQ54D.js +26 -0
  12. package/dist/chunk-M7BPQ54D.js.map +1 -0
  13. package/dist/{chunk-ISAFEOR2.js → chunk-PXVDVUFX.js} +3 -2
  14. package/dist/chunk-PXVDVUFX.js.map +1 -0
  15. package/dist/chunk-VV653S52.js +246 -0
  16. package/dist/chunk-VV653S52.js.map +1 -0
  17. package/dist/chunk-WG7HYVHJ.js +38 -0
  18. package/dist/chunk-WG7HYVHJ.js.map +1 -0
  19. package/dist/{client-D9YYA2w6.d.ts → client-D51WdvYR.d.ts} +55 -8
  20. package/dist/client-bsiNPHBi.d.ts +159 -0
  21. package/dist/createCartHandler-C4RrPwSG.d.ts +58 -0
  22. package/dist/editor.d.ts +1 -1
  23. package/dist/events-react.d.ts +68 -0
  24. package/dist/events-react.js +557 -0
  25. package/dist/events-react.js.map +1 -0
  26. package/dist/events.d.ts +18 -0
  27. package/dist/events.js +5 -0
  28. package/dist/events.js.map +1 -0
  29. package/dist/fetch-M3Y-fQrW.d.ts +47 -0
  30. package/dist/forms.d.ts +58 -0
  31. package/dist/forms.js +3 -0
  32. package/dist/forms.js.map +1 -0
  33. package/dist/http-types-CGh8WGHB.d.ts +11 -0
  34. package/dist/index.d.ts +75 -12
  35. package/dist/index.js +55 -8
  36. package/dist/index.js.map +1 -1
  37. package/dist/offer-token-C-nk_nj8.d.ts +16 -0
  38. package/dist/{pagination-CPgH3fAa.d.ts → pagination-DCrQk02Q.d.ts} +1 -1
  39. package/dist/product.d.ts +1 -1
  40. package/dist/react-router.d.ts +12 -6
  41. package/dist/react-router.js +4 -2
  42. package/dist/react-router.js.map +1 -1
  43. package/dist/react.d.ts +30 -7
  44. package/dist/react.js +275 -156
  45. package/dist/react.js.map +1 -1
  46. package/dist/{routing-BfUp49o8.d.ts → routing-sEonP93J.d.ts} +26 -59
  47. package/dist/seo.d.ts +4 -1
  48. package/dist/seo.js +1 -1
  49. package/dist/settings.d.ts +6 -39
  50. package/dist/settings.js +2 -1
  51. package/dist/{shop-DkQmro-Y.d.ts → shop-BJSvtmnV.d.ts} +2 -2
  52. package/dist/{store-DQK1nhob.d.ts → store-CtCWHI8H.d.ts} +1 -1
  53. package/dist/types-CSNXbGFW.d.ts +108 -0
  54. package/dist/{types-Df3bGbKU.d.ts → types-bcbQzx87.d.ts} +1 -1
  55. package/package.json +13 -1
  56. package/dist/chunk-GCQI6OKH.js.map +0 -1
  57. package/dist/chunk-ISAFEOR2.js.map +0 -1
  58. package/dist/chunk-U5677AEA.js.map +0 -1
@@ -0,0 +1,58 @@
1
+ import { S as StorefrontClient } from './client-D51WdvYR.js';
2
+ import { C as Cart, a as CartLineInput, b as CartAttribute, c as CartBuyerIdentityInput, d as CartMutationResult, e as CartLineUpdateInput, f as CartMetafield, g as CartMetafieldIdentifier, h as CartDeliveryAddress, i as CartSelectedDeliveryOption, j as CartFormAction } from './types-bcbQzx87.js';
3
+
4
+ interface CartCreateInput {
5
+ readonly lines?: readonly CartLineInput[];
6
+ readonly note?: string | null;
7
+ readonly attributes?: readonly CartAttribute[];
8
+ readonly buyerIdentity?: CartBuyerIdentityInput;
9
+ }
10
+ interface CartDispatchAction {
11
+ readonly action: CartFormAction;
12
+ readonly inputs: Record<string, unknown>;
13
+ }
14
+ interface CartHandler {
15
+ get(): Promise<Cart | null>;
16
+ getCart(): Promise<Cart | null>;
17
+ create(input?: CartCreateInput): Promise<CartMutationResult>;
18
+ addLines(lines: readonly CartLineInput[]): Promise<CartMutationResult>;
19
+ updateLines(lines: readonly CartLineUpdateInput[]): Promise<CartMutationResult>;
20
+ removeLines(lineIds: readonly string[]): Promise<CartMutationResult>;
21
+ updateBuyerIdentity(buyerIdentity: CartBuyerIdentityInput): Promise<CartMutationResult>;
22
+ updateDiscountCodes(discountCodes: readonly string[]): Promise<CartMutationResult>;
23
+ setCartId(cartId: string): Headers;
24
+ cartCreate(input?: CartCreateInput): Promise<CartMutationResult>;
25
+ linesAdd(lines: readonly CartLineInput[]): Promise<CartMutationResult>;
26
+ linesUpdate(lines: readonly CartLineUpdateInput[]): Promise<CartMutationResult>;
27
+ linesRemove(lineIds: readonly string[]): Promise<CartMutationResult>;
28
+ buyerIdentityUpdate(buyerIdentity: CartBuyerIdentityInput): Promise<CartMutationResult>;
29
+ discountCodesUpdate(discountCodes: readonly string[]): Promise<CartMutationResult>;
30
+ giftCardCodesAdd(giftCardCodes: readonly string[]): Promise<CartMutationResult>;
31
+ giftCardCodesRemove(giftCardCodes: readonly string[]): Promise<CartMutationResult>;
32
+ noteUpdate(note: string | null): Promise<CartMutationResult>;
33
+ attributesUpdate(attributes: readonly CartAttribute[]): Promise<CartMutationResult>;
34
+ metafieldsSet(metafields: readonly CartMetafield[]): Promise<CartMutationResult>;
35
+ metafieldsDelete(metafields: readonly CartMetafieldIdentifier[]): Promise<CartMutationResult>;
36
+ deliveryAddressesReplace(addresses: readonly CartDeliveryAddress[]): Promise<CartMutationResult>;
37
+ selectedDeliveryOptionsUpdate(selectedDeliveryOptions: readonly CartSelectedDeliveryOption[]): Promise<CartMutationResult>;
38
+ dispatch(action: CartDispatchAction): Promise<CartMutationResult>;
39
+ }
40
+ type MaybePromise<T> = T | Promise<T>;
41
+ type CartGetId = () => MaybePromise<string | null | undefined>;
42
+ type CartSetId = (cartId: string) => MaybePromise<string | Headers | null | undefined | void>;
43
+ interface RequestCartHandlerOptions {
44
+ request: Request;
45
+ storefront: StorefrontClient;
46
+ }
47
+ interface HydrogenCartHandlerOptions {
48
+ storefront: StorefrontClient;
49
+ customerAccount?: unknown;
50
+ getCartId: CartGetId;
51
+ setCartId: CartSetId;
52
+ }
53
+ type CreateCartHandlerOptions = RequestCartHandlerOptions | HydrogenCartHandlerOptions;
54
+ declare function cartGetIdDefault(headers: Headers): CartGetId;
55
+ declare function cartSetIdDefault(): CartSetId;
56
+ declare function createCartHandler(opts: CreateCartHandlerOptions): CartHandler;
57
+
58
+ export { type CartHandler as C, type HydrogenCartHandlerOptions as H, type RequestCartHandlerOptions as R, type CartDispatchAction as a, type CartGetId as b, type CartSetId as c, type CreateCartHandlerOptions as d, cartGetIdDefault as e, cartSetIdDefault as f, createCartHandler as g };
package/dist/editor.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { c as ThemeSettingsStore, b as ResolvedSettings, a as ResolvedSection, R as ResolvedBlock } from './store-DQK1nhob.js';
1
+ import { T as ThemeSettingsStore, R as ResolvedSettings, c as ResolvedSection, b as ResolvedBlock } from './store-CtCWHI8H.js';
2
2
 
3
3
  interface StorefrontEditorMessageTarget {
4
4
  postMessage(message: unknown, targetOrigin: string): void;
@@ -0,0 +1,68 @@
1
+ import * as react from 'react';
2
+ import { ReactNode } from 'react';
3
+ import { O as OfferTokenLocation, a as OfferTokenHistory } from './offer-token-C-nk_nj8.js';
4
+ import { E as EventsClient, a as EventCheckoutSession, b as EventApprovalOfferResolution, P as PublicEvent, c as EventRegistrationSubmission } from './client-bsiNPHBi.js';
5
+ import './types-CSNXbGFW.js';
6
+ import './http-types-CGh8WGHB.js';
7
+
8
+ type EventRegistrationState = 'idle' | 'submitting' | 'awaiting_approval' | 'waitlisted' | 'checkout_ready' | 'request_error';
9
+ declare function useEventRegistration(input: {
10
+ readonly client: EventsClient;
11
+ readonly onCheckoutSession?: (session: EventCheckoutSession) => void;
12
+ }): {
13
+ readonly state: EventRegistrationState;
14
+ readonly submit: (submission: EventRegistrationSubmission) => Promise<void>;
15
+ readonly reset: () => void;
16
+ };
17
+ interface EventRegistrationLabels {
18
+ readonly submit: string;
19
+ readonly submitting: string;
20
+ readonly awaiting_approval: string;
21
+ readonly waitlisted: string;
22
+ readonly checkout_ready: string;
23
+ readonly request_error: string;
24
+ readonly email: string;
25
+ readonly quantity: string;
26
+ readonly ticket: string;
27
+ }
28
+ interface EventRegistrationFormProps {
29
+ readonly event: PublicEvent;
30
+ readonly locale: string;
31
+ readonly client: EventsClient;
32
+ readonly cartToken?: string;
33
+ readonly fallbackLocale?: string;
34
+ readonly onCheckoutSession?: (session: EventCheckoutSession) => void;
35
+ readonly labels?: Partial<EventRegistrationLabels>;
36
+ readonly className?: string;
37
+ }
38
+ declare function EventRegistrationForm({ event, locale, client, cartToken, fallbackLocale, onCheckoutSession, labels, className, }: EventRegistrationFormProps): react.JSX.Element;
39
+ type EventOfferState = 'resolving' | 'active' | 'claiming' | 'claimed' | 'expired' | 'unavailable' | 'request_error';
40
+ interface EventApprovalOfferLabels {
41
+ readonly resolving: ReactNode;
42
+ readonly claim: ReactNode;
43
+ readonly resume: ReactNode;
44
+ readonly claiming: ReactNode;
45
+ readonly claimed: ReactNode;
46
+ readonly expired: ReactNode;
47
+ readonly unavailable: ReactNode;
48
+ readonly requestError: ReactNode;
49
+ readonly retry?: ReactNode;
50
+ readonly expires: ReactNode;
51
+ }
52
+ interface EventApprovalOfferProps {
53
+ readonly client: EventsClient;
54
+ readonly offerId: string;
55
+ readonly cartToken: string;
56
+ readonly locale: string;
57
+ readonly idempotencyKey?: string;
58
+ readonly location?: OfferTokenLocation;
59
+ readonly history?: OfferTokenHistory;
60
+ readonly labels?: Partial<EventApprovalOfferLabels>;
61
+ readonly onCheckoutSession?: (session: EventCheckoutSession) => void;
62
+ readonly initialResolution?: EventApprovalOfferResolution;
63
+ readonly initialState?: Exclude<EventOfferState, 'resolving' | 'active' | 'claiming'>;
64
+ readonly className?: string;
65
+ }
66
+ declare function EventApprovalOffer({ client, offerId, cartToken, locale, idempotencyKey, location, history, labels, onCheckoutSession, initialResolution, initialState, className, }: EventApprovalOfferProps): react.JSX.Element;
67
+
68
+ export { EventApprovalOffer, type EventApprovalOfferLabels, type EventApprovalOfferProps, type EventOfferState, EventRegistrationForm, type EventRegistrationFormProps, type EventRegistrationLabels, type EventRegistrationState, useEventRegistration };
@@ -0,0 +1,557 @@
1
+ import { StorefrontFormFields } from './chunk-VV653S52.js';
2
+ import { offerTokenHrefMarker, consumeOfferTokenFragment } from './chunk-WG7HYVHJ.js';
3
+ import { StorefrontEventsRequestError } from './chunk-7MTCGXIG.js';
4
+ import './chunk-IIOHENCG.js';
5
+ import { useState, useRef, useEffect, useCallback, useId, useMemo, useLayoutEffect } from 'react';
6
+ import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
7
+
8
+ function useEventRegistration(input) {
9
+ const [state, setState] = useState("idle");
10
+ const generation = useRef(0);
11
+ const logicalPayload = useRef(null);
12
+ const idempotencyKey = useRef(null);
13
+ const activeClient = useRef(input.client);
14
+ const activeCallback = useRef(input.onCheckoutSession);
15
+ useEffect(() => {
16
+ if (activeClient.current !== input.client || activeCallback.current !== input.onCheckoutSession) {
17
+ activeClient.current = input.client;
18
+ activeCallback.current = input.onCheckoutSession;
19
+ generation.current += 1;
20
+ setState("idle");
21
+ }
22
+ }, [input.client, input.onCheckoutSession]);
23
+ useEffect(
24
+ () => () => {
25
+ generation.current += 1;
26
+ },
27
+ []
28
+ );
29
+ const reset = useCallback(() => {
30
+ generation.current += 1;
31
+ setState("idle");
32
+ }, []);
33
+ async function submit(submission) {
34
+ const attempt = ++generation.current;
35
+ const prepared = registrationSubmission(submission, logicalPayload, idempotencyKey);
36
+ setState("submitting");
37
+ try {
38
+ const result = await input.client.submitRegistration(prepared);
39
+ if (attempt !== generation.current) return;
40
+ if (result.kind === "checkout") {
41
+ input.onCheckoutSession?.(result.checkoutSession);
42
+ setState("checkout_ready");
43
+ } else if (result.kind === "approval") {
44
+ setState("awaiting_approval");
45
+ } else {
46
+ setState("waitlisted");
47
+ }
48
+ } catch {
49
+ if (attempt !== generation.current) return;
50
+ setState("request_error");
51
+ }
52
+ }
53
+ return { state, submit, reset };
54
+ }
55
+ function registrationKey(mode) {
56
+ const kind = mode === "approval_then_payment" ? "application" : "checkout";
57
+ return `events-${kind}-${Date.now()}-${Math.random().toString(36).slice(2)}`;
58
+ }
59
+ function stablePayload(value) {
60
+ return JSON.stringify(value, (_key, item) => {
61
+ if (item === null || typeof item !== "object" || Array.isArray(item)) return item;
62
+ return Object.fromEntries(
63
+ Object.entries(item).sort(
64
+ ([left], [right]) => left.localeCompare(right)
65
+ )
66
+ );
67
+ });
68
+ }
69
+ function registrationSubmission(submission, previousPayload, previousKey) {
70
+ if (submission.registrationMode === "waitlist_only") return submission;
71
+ if (submission.idempotencyKey.trim() !== "") return submission;
72
+ const payload = {
73
+ registrationMode: submission.registrationMode,
74
+ handle: submission.handle,
75
+ input: submission.input
76
+ };
77
+ const identity = stablePayload(payload);
78
+ if (identity !== previousPayload.current || previousKey.current === null) {
79
+ previousPayload.current = identity;
80
+ previousKey.current = registrationKey(submission.registrationMode);
81
+ }
82
+ return { ...submission, idempotencyKey: previousKey.current };
83
+ }
84
+ function eventFormForRenderer(form) {
85
+ return {
86
+ definition: {
87
+ id: form.definitionId,
88
+ handle: form.definitionId,
89
+ title: form.title
90
+ },
91
+ version: {
92
+ id: form.versionId,
93
+ definitionId: form.definitionId,
94
+ schema: form.schema
95
+ }
96
+ };
97
+ }
98
+ var REGISTRATION_COPY = {
99
+ en: {
100
+ submit: "Register",
101
+ submitting: "Submitting\u2026",
102
+ awaiting_approval: "Application received. Awaiting approval.",
103
+ waitlisted: "You joined the waitlist.",
104
+ checkout_ready: "Registration ready for checkout.",
105
+ request_error: "Something went wrong. Please try again.",
106
+ email: "Email",
107
+ quantity: "Quantity",
108
+ ticket: "Ticket"
109
+ },
110
+ de: {
111
+ submit: "Anmelden",
112
+ submitting: "Wird gesendet\u2026",
113
+ awaiting_approval: "Anmeldung erhalten. Sie wartet auf Freigabe.",
114
+ waitlisted: "Du stehst auf der Warteliste.",
115
+ checkout_ready: "Die Anmeldung kann bezahlt werden.",
116
+ request_error: "Etwas ist schiefgelaufen. Bitte versuche es erneut.",
117
+ email: "E-Mail",
118
+ quantity: "Anzahl",
119
+ ticket: "Ticket"
120
+ }
121
+ };
122
+ function registrationCopy(locale) {
123
+ return locale.toLowerCase().startsWith("de") ? REGISTRATION_COPY.de : REGISTRATION_COPY.en;
124
+ }
125
+ function selectable(ticket) {
126
+ return ticket.salesState === "on_sale" && ticket.maxPerOrder >= ticket.minPerOrder && (ticket.available === null || ticket.available >= ticket.minPerOrder);
127
+ }
128
+ function EventRegistrationForm({
129
+ event,
130
+ locale,
131
+ client,
132
+ cartToken,
133
+ fallbackLocale,
134
+ onCheckoutSession,
135
+ labels,
136
+ className
137
+ }) {
138
+ const tickets = event.occurrences.flatMap((occurrence) => occurrence.ticketTypes);
139
+ const firstTicket = tickets.find(selectable);
140
+ const [ticketTypeId, setTicketTypeId] = useState(firstTicket?.id ?? "");
141
+ const ticket = tickets.find((candidate) => candidate.id === ticketTypeId);
142
+ const initialQuantity = ticket?.minPerOrder ?? 1;
143
+ const [quantity, setQuantity] = useState(initialQuantity);
144
+ const [email, setEmail] = useState("");
145
+ const [preflightError, setPreflightError] = useState(false);
146
+ const [buyerAnswers, setBuyerAnswers] = useState({});
147
+ const [participantAnswers, setParticipantAnswers] = useState(
148
+ () => Array.from({ length: initialQuantity }, () => ({}))
149
+ );
150
+ const generatedId = useId();
151
+ const instanceId = `event-registration-${generatedId.replaceAll(":", "")}`;
152
+ const selectedProjectionIdentity = stablePayload({
153
+ buyerForm: ticket?.buyerForm ?? null,
154
+ participantForm: ticket?.participantForm ?? null
155
+ });
156
+ const previousProjection = useRef(selectedProjectionIdentity);
157
+ const [projectionGeneration, setProjectionGeneration] = useState(0);
158
+ const registration = useEventRegistration({
159
+ client,
160
+ ...onCheckoutSession ? { onCheckoutSession } : {}
161
+ });
162
+ const copy = { ...registrationCopy(locale), ...labels };
163
+ const previousEvent = useRef(`${event.id}:${event.handle}:${event.registrationMode}:${locale}`);
164
+ const previousCartToken = useRef(cartToken);
165
+ useEffect(() => {
166
+ const eventIdentity = `${event.id}:${event.handle}:${event.registrationMode}:${locale}`;
167
+ if (previousEvent.current !== eventIdentity) {
168
+ previousEvent.current = eventIdentity;
169
+ const nextTicket = tickets.find(selectable);
170
+ setTicketTypeId(nextTicket?.id ?? "");
171
+ setQuantity(nextTicket?.minPerOrder ?? 1);
172
+ setEmail("");
173
+ setBuyerAnswers({});
174
+ setParticipantAnswers(Array.from({ length: nextTicket?.minPerOrder ?? 1 }, () => ({})));
175
+ previousProjection.current = selectedProjectionIdentity;
176
+ setProjectionGeneration((current) => current + 1);
177
+ setPreflightError(false);
178
+ registration.reset();
179
+ } else if (previousProjection.current !== selectedProjectionIdentity) {
180
+ previousProjection.current = selectedProjectionIdentity;
181
+ setBuyerAnswers({});
182
+ setParticipantAnswers(Array.from({ length: quantity }, () => ({})));
183
+ setProjectionGeneration((current) => current + 1);
184
+ setPreflightError(false);
185
+ registration.reset();
186
+ }
187
+ if (previousCartToken.current !== cartToken) {
188
+ previousCartToken.current = cartToken;
189
+ setPreflightError(false);
190
+ registration.reset();
191
+ }
192
+ }, [
193
+ cartToken,
194
+ event.handle,
195
+ event.id,
196
+ event.registrationMode,
197
+ locale,
198
+ quantity,
199
+ selectedProjectionIdentity
200
+ ]);
201
+ function selectTicket(nextId) {
202
+ const next = tickets.find((candidate) => candidate.id === nextId);
203
+ if (!next || !selectable(next)) return;
204
+ setTicketTypeId(nextId);
205
+ setQuantity(next.minPerOrder);
206
+ setBuyerAnswers({});
207
+ setParticipantAnswers(Array.from({ length: next.minPerOrder }, () => ({})));
208
+ registration.reset();
209
+ setPreflightError(false);
210
+ }
211
+ function updateQuantity(next) {
212
+ if (!ticket) return;
213
+ const maximum2 = Math.min(ticket.maxPerOrder, ticket.available ?? ticket.maxPerOrder);
214
+ const normalized = Number.isFinite(next) ? Math.trunc(next) : ticket.minPerOrder;
215
+ const bounded = Math.max(ticket.minPerOrder, Math.min(maximum2, normalized));
216
+ setQuantity(bounded);
217
+ setParticipantAnswers(
218
+ (current) => Array.from({ length: bounded }, (_, index) => current[index] ?? {})
219
+ );
220
+ registration.reset();
221
+ setPreflightError(false);
222
+ }
223
+ async function handleSubmit(submitEvent) {
224
+ submitEvent.preventDefault();
225
+ if (!ticket || !selectable(ticket)) {
226
+ setPreflightError(true);
227
+ return;
228
+ }
229
+ setPreflightError(false);
230
+ const buyer = ticket.buyerForm ? { versionId: ticket.buyerForm.versionId, answers: buyerAnswers } : void 0;
231
+ const participants = ticket.participantForm ? participantAnswers.map((answers) => ({
232
+ versionId: ticket.participantForm.versionId,
233
+ answers
234
+ })) : void 0;
235
+ let submission;
236
+ if (event.registrationMode === "instant") {
237
+ if (!cartToken) {
238
+ setPreflightError(true);
239
+ return;
240
+ }
241
+ submission = {
242
+ registrationMode: "instant",
243
+ handle: event.handle,
244
+ idempotencyKey: "",
245
+ input: {
246
+ ticketTypeId: ticket.id,
247
+ quantity,
248
+ cartToken,
249
+ locale,
250
+ ...buyer ? { buyer } : {},
251
+ ...participants ? { participants } : {}
252
+ }
253
+ };
254
+ } else if (event.registrationMode === "approval_then_payment") {
255
+ submission = {
256
+ registrationMode: "approval_then_payment",
257
+ handle: event.handle,
258
+ idempotencyKey: "",
259
+ input: {
260
+ ticketTypeId: ticket.id,
261
+ quantity,
262
+ contactEmail: email,
263
+ locale,
264
+ ...buyer ? { buyer } : {},
265
+ ...participants ? { participants } : {}
266
+ }
267
+ };
268
+ } else {
269
+ submission = {
270
+ registrationMode: "waitlist_only",
271
+ handle: event.handle,
272
+ input: { ticketTypeId: ticket.id, quantity, email }
273
+ };
274
+ }
275
+ await registration.submit(submission);
276
+ }
277
+ const maximum = ticket ? Math.min(ticket.maxPerOrder, ticket.available ?? ticket.maxPerOrder) : void 0;
278
+ const renderedState = preflightError ? "request_error" : registration.state;
279
+ const status = renderedState === "idle" ? "" : copy[renderedState];
280
+ return /* @__PURE__ */ jsxs("form", { className, "data-kotao-event-registration": true, onSubmit: handleSubmit, children: [
281
+ /* @__PURE__ */ jsxs("label", { children: [
282
+ /* @__PURE__ */ jsx("span", { children: copy.ticket }),
283
+ /* @__PURE__ */ jsx(
284
+ "select",
285
+ {
286
+ "data-kotao-event-ticket": true,
287
+ value: ticketTypeId,
288
+ onChange: (changeEvent) => selectTicket(changeEvent.currentTarget.value),
289
+ children: tickets.map((candidate) => /* @__PURE__ */ jsx("option", { value: candidate.id, disabled: !selectable(candidate), children: candidate.title }, candidate.id))
290
+ }
291
+ )
292
+ ] }),
293
+ /* @__PURE__ */ jsxs("label", { children: [
294
+ /* @__PURE__ */ jsx("span", { children: copy.quantity }),
295
+ /* @__PURE__ */ jsx(
296
+ "input",
297
+ {
298
+ name: "quantity",
299
+ type: "number",
300
+ value: quantity,
301
+ min: ticket?.minPerOrder,
302
+ max: maximum,
303
+ required: true,
304
+ step: 1,
305
+ onChange: (changeEvent) => updateQuantity(changeEvent.currentTarget.valueAsNumber)
306
+ }
307
+ )
308
+ ] }),
309
+ event.registrationMode !== "instant" ? /* @__PURE__ */ jsxs("label", { children: [
310
+ /* @__PURE__ */ jsx("span", { children: copy.email }),
311
+ /* @__PURE__ */ jsx(
312
+ "input",
313
+ {
314
+ type: "email",
315
+ required: true,
316
+ value: email,
317
+ onChange: (changeEvent) => {
318
+ setEmail(changeEvent.currentTarget.value);
319
+ setPreflightError(false);
320
+ registration.reset();
321
+ }
322
+ }
323
+ )
324
+ ] }) : null,
325
+ ticket?.buyerForm ? /* @__PURE__ */ jsx("div", { "data-kotao-event-buyer-form": true, children: /* @__PURE__ */ jsx(
326
+ StorefrontFormFields,
327
+ {
328
+ form: eventFormForRenderer(ticket.buyerForm),
329
+ locale,
330
+ ...fallbackLocale === void 0 ? {} : { fallbackLocale },
331
+ answers: buyerAnswers,
332
+ idPrefix: `${instanceId}-event-${event.id}-ticket-${ticket.id}-buyer`,
333
+ onAnswersChange: (next) => {
334
+ setBuyerAnswers(next);
335
+ setPreflightError(false);
336
+ registration.reset();
337
+ }
338
+ },
339
+ `${projectionGeneration}:${ticket.id}:${ticket.buyerForm.versionId}`
340
+ ) }) : null,
341
+ ticket?.participantForm ? participantAnswers.map((answers, index) => /* @__PURE__ */ jsx(
342
+ "div",
343
+ {
344
+ "data-kotao-event-participant-form": true,
345
+ children: /* @__PURE__ */ jsx(
346
+ StorefrontFormFields,
347
+ {
348
+ form: eventFormForRenderer(ticket.participantForm),
349
+ locale,
350
+ ...fallbackLocale === void 0 ? {} : { fallbackLocale },
351
+ answers,
352
+ idPrefix: `${instanceId}-event-${event.id}-ticket-${ticket.id}-participant-${index}`,
353
+ onAnswersChange: (next) => {
354
+ setParticipantAnswers(
355
+ (current) => current.map(
356
+ (value, participantIndex) => participantIndex === index ? next : value
357
+ )
358
+ );
359
+ setPreflightError(false);
360
+ registration.reset();
361
+ }
362
+ }
363
+ )
364
+ },
365
+ `${projectionGeneration}:${ticket.id}:${ticket.participantForm.versionId}:${index}`
366
+ )) : null,
367
+ /* @__PURE__ */ jsx("button", { type: "submit", disabled: registration.state === "submitting", children: copy.submit }),
368
+ /* @__PURE__ */ jsx("p", { role: "status", "aria-live": "polite", "aria-atomic": "true", "data-kotao-event-state": renderedState, children: status })
369
+ ] });
370
+ }
371
+ var OFFER_LABELS = {
372
+ resolving: "Loading offer\u2026",
373
+ claim: "Claim offer",
374
+ resume: "Continue to checkout",
375
+ claiming: "Starting checkout\u2026",
376
+ claimed: "Offer claimed. Continue to checkout.",
377
+ expired: "This offer has expired.",
378
+ unavailable: "This offer is not available.",
379
+ requestError: "Something went wrong. Please try again.",
380
+ retry: "Try again",
381
+ expires: "Offer expires at"
382
+ };
383
+ function browserLocation() {
384
+ return typeof window === "undefined" ? void 0 : window.location;
385
+ }
386
+ function browserHistory() {
387
+ return typeof window === "undefined" ? void 0 : window.history;
388
+ }
389
+ function EventApprovalOffer({
390
+ client,
391
+ offerId,
392
+ cartToken,
393
+ locale,
394
+ idempotencyKey,
395
+ location,
396
+ history,
397
+ labels,
398
+ onCheckoutSession,
399
+ initialResolution,
400
+ initialState,
401
+ className
402
+ }) {
403
+ const copy = { ...OFFER_LABELS, ...labels };
404
+ const token = useRef(null);
405
+ const consumedHrefMarkers = useRef(/* @__PURE__ */ new Set());
406
+ const offerGeneration = useRef(0);
407
+ const resolvedGeneration = useRef(initialResolution ? 0 : null);
408
+ const mounted = useRef(false);
409
+ const strictCleanupGeneration = useRef(null);
410
+ const activeDependencies = useRef({ client, cartToken, locale, onCheckoutSession });
411
+ const claimIdempotencyKey = useMemo(
412
+ () => idempotencyKey ?? `events-claim-${Date.now()}-${Math.random().toString(36).slice(2)}`,
413
+ [idempotencyKey, offerId]
414
+ );
415
+ const resolveIdempotencyKey = useMemo(
416
+ () => `events-resolve-${Date.now()}-${Math.random().toString(36).slice(2)}`,
417
+ [offerId]
418
+ );
419
+ const [canClaim, setCanClaim] = useState(initialResolution?.state === "active");
420
+ const [resolution, setResolution] = useState(
421
+ initialResolution ?? null
422
+ );
423
+ const [state, setState] = useState(
424
+ initialResolution ? initialResolution.state === "claimed" ? "claimed" : "active" : initialState ?? "resolving"
425
+ );
426
+ async function resolveCapability(generation, claimToken) {
427
+ try {
428
+ const offer = await client.resolveOffer(offerId, claimToken, resolveIdempotencyKey);
429
+ if (!mounted.current || generation !== offerGeneration.current) return;
430
+ resolvedGeneration.current = generation;
431
+ setResolution(offer);
432
+ if (offer.state === "claimed") setState("claimed");
433
+ else if (new Date(offer.expiresAt).getTime() <= Date.now()) setState("expired");
434
+ else setState("active");
435
+ } catch (error) {
436
+ if (!mounted.current || generation !== offerGeneration.current) return;
437
+ setState(
438
+ error instanceof StorefrontEventsRequestError && error.status === 409 ? "unavailable" : "request_error"
439
+ );
440
+ }
441
+ }
442
+ useLayoutEffect(() => {
443
+ if (strictCleanupGeneration.current !== null && offerGeneration.current === strictCleanupGeneration.current + 1) {
444
+ offerGeneration.current = strictCleanupGeneration.current;
445
+ }
446
+ strictCleanupGeneration.current = null;
447
+ mounted.current = true;
448
+ return () => {
449
+ mounted.current = false;
450
+ strictCleanupGeneration.current = offerGeneration.current;
451
+ offerGeneration.current += 1;
452
+ };
453
+ }, []);
454
+ useLayoutEffect(() => {
455
+ const previous = activeDependencies.current;
456
+ if (previous.client === client && previous.cartToken === cartToken && previous.locale === locale && previous.onCheckoutSession === onCheckoutSession) {
457
+ return;
458
+ }
459
+ activeDependencies.current = { client, cartToken, locale, onCheckoutSession };
460
+ offerGeneration.current += 1;
461
+ resolvedGeneration.current = null;
462
+ token.current = null;
463
+ setCanClaim(false);
464
+ setResolution(null);
465
+ setState("unavailable");
466
+ }, [cartToken, client, locale, onCheckoutSession]);
467
+ useLayoutEffect(() => {
468
+ if (initialState !== void 0) return;
469
+ const currentLocation = location ?? browserLocation();
470
+ const currentHistory = history ?? browserHistory();
471
+ if (currentLocation === void 0 || currentHistory === void 0) {
472
+ if (initialResolution === void 0) setState("unavailable");
473
+ return;
474
+ }
475
+ const hrefMarker = offerTokenHrefMarker(currentLocation.href);
476
+ if (consumedHrefMarkers.current.has(hrefMarker)) return;
477
+ consumedHrefMarkers.current.add(hrefMarker);
478
+ offerGeneration.current += 1;
479
+ const generation = offerGeneration.current;
480
+ resolvedGeneration.current = null;
481
+ token.current = null;
482
+ setCanClaim(false);
483
+ setResolution(null);
484
+ setState("resolving");
485
+ const claimToken = consumeOfferTokenFragment(currentLocation, currentHistory);
486
+ consumedHrefMarkers.current.add(offerTokenHrefMarker(currentLocation.href));
487
+ token.current = claimToken;
488
+ setCanClaim(claimToken !== null);
489
+ if (claimToken === null) {
490
+ if (initialResolution === void 0) setState("unavailable");
491
+ return;
492
+ }
493
+ void resolveCapability(generation, claimToken);
494
+ });
495
+ function retryResolve() {
496
+ const claimToken = token.current;
497
+ if (claimToken === null) {
498
+ setState("unavailable");
499
+ return;
500
+ }
501
+ const generation = offerGeneration.current;
502
+ setState("resolving");
503
+ void resolveCapability(generation, claimToken);
504
+ }
505
+ async function claim() {
506
+ const claimToken = token.current;
507
+ const generation = offerGeneration.current;
508
+ if (claimToken === null || resolvedGeneration.current !== generation || resolution?.offerId !== offerId) {
509
+ setState("unavailable");
510
+ return;
511
+ }
512
+ setState("claiming");
513
+ try {
514
+ const session = await client.claimOffer(
515
+ offerId,
516
+ { claimToken, cartToken, locale },
517
+ claimIdempotencyKey
518
+ );
519
+ if (!mounted.current || generation !== offerGeneration.current) return;
520
+ token.current = null;
521
+ setCanClaim(false);
522
+ setState("claimed");
523
+ onCheckoutSession?.(session);
524
+ } catch (error) {
525
+ if (!mounted.current || generation !== offerGeneration.current) return;
526
+ setState(
527
+ error instanceof StorefrontEventsRequestError && error.status === 409 ? "unavailable" : "request_error"
528
+ );
529
+ }
530
+ }
531
+ const status = state === "resolving" ? copy.resolving : state === "claiming" ? copy.claiming : state === "claimed" ? copy.claimed : state === "expired" ? copy.expired : state === "unavailable" ? copy.unavailable : state === "request_error" ? copy.requestError : null;
532
+ return /* @__PURE__ */ jsxs("section", { className, "data-kotao-event-approval-offer": true, children: [
533
+ resolution ? /* @__PURE__ */ jsxs("header", { children: [
534
+ /* @__PURE__ */ jsx("h1", { children: resolution.eventTitle }),
535
+ /* @__PURE__ */ jsxs("p", { children: [
536
+ resolution.ticketTypeTitle,
537
+ " \xB7 ",
538
+ resolution.quantity
539
+ ] }),
540
+ /* @__PURE__ */ jsx("time", { dateTime: resolution.occurrenceStartsAt, children: resolution.occurrenceStartsAt })
541
+ ] }) : null,
542
+ (state === "active" || state === "claimed" && canClaim || state === "request_error" && canClaim) && resolution ? /* @__PURE__ */ jsxs(Fragment, { children: [
543
+ /* @__PURE__ */ jsxs("p", { children: [
544
+ copy.expires,
545
+ ": ",
546
+ /* @__PURE__ */ jsx("time", { dateTime: resolution.expiresAt, children: resolution.expiresAt })
547
+ ] }),
548
+ /* @__PURE__ */ jsx("button", { type: "button", onClick: claim, children: state === "claimed" ? copy.resume : copy.claim })
549
+ ] }) : null,
550
+ state === "request_error" && canClaim && resolution === null ? /* @__PURE__ */ jsx("button", { type: "button", onClick: retryResolve, children: copy.retry }) : null,
551
+ /* @__PURE__ */ jsx("p", { role: "status", "aria-live": "polite", "aria-atomic": "true", children: status })
552
+ ] });
553
+ }
554
+
555
+ export { EventApprovalOffer, EventRegistrationForm, useEventRegistration };
556
+ //# sourceMappingURL=events-react.js.map
557
+ //# sourceMappingURL=events-react.js.map