@uptrademedia/site-kit 1.0.34 → 1.0.36

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.
@@ -1,6 +1,12 @@
1
1
  import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
2
2
 
3
3
  // src/images/ManagedFavicon.tsx
4
+ function toAbsoluteFaviconUrl(url, apiBase) {
5
+ if (!url) return void 0;
6
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
7
+ const base = apiBase.replace(/\/$/, "");
8
+ return url.startsWith("/") ? `${base}${url}` : `${base}/${url}`;
9
+ }
4
10
  async function fetchFaviconData(apiUrl, apiKey) {
5
11
  try {
6
12
  const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {
@@ -17,8 +23,10 @@ async function fetchFaviconData(apiUrl, apiKey) {
17
23
  }
18
24
  const data = await res.json();
19
25
  if (data.image && !data.is_placeholder) {
26
+ const raw = data.image.public_url || data.image.external_url;
27
+ const public_url = toAbsoluteFaviconUrl(raw, apiUrl);
20
28
  return {
21
- public_url: data.image.public_url || data.image.external_url,
29
+ public_url: public_url || void 0,
22
30
  mime_type: data.image.file?.mime_type,
23
31
  is_placeholder: false
24
32
  };
@@ -56,5 +64,5 @@ async function ManagedFavicon({
56
64
  }
57
65
 
58
66
  export { ManagedFavicon, fetchFaviconData, getManagedFaviconUrl };
59
- //# sourceMappingURL=chunk-WUIDGCAI.mjs.map
60
- //# sourceMappingURL=chunk-WUIDGCAI.mjs.map
67
+ //# sourceMappingURL=chunk-CSB2IVNY.mjs.map
68
+ //# sourceMappingURL=chunk-CSB2IVNY.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/images/ManagedFavicon.tsx"],"names":[],"mappings":"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","file":"chunk-CSB2IVNY.mjs","sourcesContent":["/**\n * ManagedFavicon Component\n * \n * Server component that renders favicon link tags using the project's logo.\n * When a logo is uploaded in Project Settings, it's automatically synced\n * to the 'favicon' slot in site_managed_images.\n * \n * Usage:\n * ```tsx\n * import { ManagedFavicon } from '@uptrade/site-kit/images'\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <head>\n * <ManagedFavicon />\n * </head>\n * <body>{children}</body>\n * </html>\n * )\n * }\n * ```\n * \n * Supports:\n * - SVG favicons (best for modern browsers, scales perfectly)\n * - PNG favicons with multiple sizes\n * - Apple touch icons\n * - Theme color for mobile browsers\n */\n\nimport React from 'react'\n\nexport interface ManagedFaviconProps {\n /**\n * API key for Portal API authentication\n * Defaults to NEXT_PUBLIC_UPTRADE_API_KEY env var\n */\n apiKey?: string\n \n /**\n * API URL (defaults to https://api.uptrademedia.com)\n */\n apiUrl?: string\n \n /**\n * Fallback favicon URL if no managed favicon is set\n */\n fallback?: string\n \n /**\n * Theme color for mobile browser chrome\n * Defaults to #4bbf39 (Uptrade brand primary)\n */\n themeColor?: string\n}\n\ninterface FaviconData {\n public_url?: string\n mime_type?: string\n is_placeholder?: boolean\n}\n\n/** If url is relative (e.g. /uploads/... from API), resolve against base so production can load it. */\nfunction toAbsoluteFaviconUrl(url: string | undefined, apiBase: string): string | undefined {\n if (!url) return undefined\n if (url.startsWith('http://') || url.startsWith('https://')) return url\n const base = apiBase.replace(/\\/$/, '')\n return url.startsWith('/') ? `${base}${url}` : `${base}/${url}`\n}\n\n/**\n * Server-side fetch of favicon data.\n * Exported for use in generateMetadata (single source for favicon URL).\n */\nexport async function fetchFaviconData(apiUrl: string, apiKey: string): Promise<FaviconData | null> {\n try {\n const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n next: { revalidate: 3600 }, // Cache 1hr – favicon rarely changes, allows static generation\n })\n\n if (!res.ok) {\n console.warn('[ManagedFavicon] Failed to fetch:', res.status)\n return null\n }\n\n const data = await res.json()\n if (data.image && !data.is_placeholder) {\n const raw = data.image.public_url || data.image.external_url\n const public_url = toAbsoluteFaviconUrl(raw, apiUrl)\n return {\n public_url: public_url || undefined,\n mime_type: data.image.file?.mime_type,\n is_placeholder: false,\n }\n }\n return null\n } catch (err) {\n console.error('[ManagedFavicon] Error fetching favicon:', err)\n return null\n }\n}\n\n/**\n * Return the managed favicon URL for the project (from API key).\n * Use in generateMetadata to set metadata.icons so favicon is consistent across all sites.\n */\nexport async function getManagedFaviconUrl(\n apiKey?: string,\n apiUrl: string = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com'\n): Promise<string | null> {\n const key = apiKey ?? process.env.NEXT_PUBLIC_UPTRADE_API_KEY ?? process.env.UPTRADE_API_KEY\n if (!key) return null\n const data = await fetchFaviconData(apiUrl, key)\n return data?.public_url ?? null\n}\n\nexport async function ManagedFavicon({\n apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,\n apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com',\n fallback = '/favicon.ico',\n themeColor = '#4bbf39',\n}: ManagedFaviconProps) {\n // Fetch favicon data during SSR\n const faviconData = apiKey && apiUrl ? await fetchFaviconData(apiUrl, apiKey) : null\n \n const faviconUrl = faviconData?.public_url || fallback\n const mimeType = faviconData?.mime_type || 'image/x-icon'\n const isSvg = mimeType === 'image/svg+xml' || faviconUrl.endsWith('.svg')\n\n return (\n <>\n {/* Primary favicon */}\n {isSvg ? (\n <link rel=\"icon\" type=\"image/svg+xml\" href={faviconUrl} />\n ) : (\n <>\n <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href={faviconUrl} />\n <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href={faviconUrl} />\n </>\n )}\n \n {/* Apple touch icon (for iOS home screen) */}\n <link rel=\"apple-touch-icon\" sizes=\"180x180\" href={faviconUrl} />\n \n {/* Theme color for mobile browsers */}\n <meta name=\"theme-color\" content={themeColor} />\n </>\n )\n}"]}
@@ -3,6 +3,12 @@
3
3
  var jsxRuntime = require('react/jsx-runtime');
4
4
 
5
5
  // src/images/ManagedFavicon.tsx
6
+ function toAbsoluteFaviconUrl(url, apiBase) {
7
+ if (!url) return void 0;
8
+ if (url.startsWith("http://") || url.startsWith("https://")) return url;
9
+ const base = apiBase.replace(/\/$/, "");
10
+ return url.startsWith("/") ? `${base}${url}` : `${base}/${url}`;
11
+ }
6
12
  async function fetchFaviconData(apiUrl, apiKey) {
7
13
  try {
8
14
  const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {
@@ -19,8 +25,10 @@ async function fetchFaviconData(apiUrl, apiKey) {
19
25
  }
20
26
  const data = await res.json();
21
27
  if (data.image && !data.is_placeholder) {
28
+ const raw = data.image.public_url || data.image.external_url;
29
+ const public_url = toAbsoluteFaviconUrl(raw, apiUrl);
22
30
  return {
23
- public_url: data.image.public_url || data.image.external_url,
31
+ public_url: public_url || void 0,
24
32
  mime_type: data.image.file?.mime_type,
25
33
  is_placeholder: false
26
34
  };
@@ -60,5 +68,5 @@ async function ManagedFavicon({
60
68
  exports.ManagedFavicon = ManagedFavicon;
61
69
  exports.fetchFaviconData = fetchFaviconData;
62
70
  exports.getManagedFaviconUrl = getManagedFaviconUrl;
63
- //# sourceMappingURL=chunk-77PEDSV5.js.map
64
- //# sourceMappingURL=chunk-77PEDSV5.js.map
71
+ //# sourceMappingURL=chunk-KHP4AK3O.js.map
72
+ //# sourceMappingURL=chunk-KHP4AK3O.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/images/ManagedFavicon.tsx"],"names":["jsxs","Fragment","jsx"],"mappings":"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","file":"chunk-KHP4AK3O.js","sourcesContent":["/**\n * ManagedFavicon Component\n * \n * Server component that renders favicon link tags using the project's logo.\n * When a logo is uploaded in Project Settings, it's automatically synced\n * to the 'favicon' slot in site_managed_images.\n * \n * Usage:\n * ```tsx\n * import { ManagedFavicon } from '@uptrade/site-kit/images'\n * \n * export default function RootLayout({ children }) {\n * return (\n * <html>\n * <head>\n * <ManagedFavicon />\n * </head>\n * <body>{children}</body>\n * </html>\n * )\n * }\n * ```\n * \n * Supports:\n * - SVG favicons (best for modern browsers, scales perfectly)\n * - PNG favicons with multiple sizes\n * - Apple touch icons\n * - Theme color for mobile browsers\n */\n\nimport React from 'react'\n\nexport interface ManagedFaviconProps {\n /**\n * API key for Portal API authentication\n * Defaults to NEXT_PUBLIC_UPTRADE_API_KEY env var\n */\n apiKey?: string\n \n /**\n * API URL (defaults to https://api.uptrademedia.com)\n */\n apiUrl?: string\n \n /**\n * Fallback favicon URL if no managed favicon is set\n */\n fallback?: string\n \n /**\n * Theme color for mobile browser chrome\n * Defaults to #4bbf39 (Uptrade brand primary)\n */\n themeColor?: string\n}\n\ninterface FaviconData {\n public_url?: string\n mime_type?: string\n is_placeholder?: boolean\n}\n\n/** If url is relative (e.g. /uploads/... from API), resolve against base so production can load it. */\nfunction toAbsoluteFaviconUrl(url: string | undefined, apiBase: string): string | undefined {\n if (!url) return undefined\n if (url.startsWith('http://') || url.startsWith('https://')) return url\n const base = apiBase.replace(/\\/$/, '')\n return url.startsWith('/') ? `${base}${url}` : `${base}/${url}`\n}\n\n/**\n * Server-side fetch of favicon data.\n * Exported for use in generateMetadata (single source for favicon URL).\n */\nexport async function fetchFaviconData(apiUrl: string, apiKey: string): Promise<FaviconData | null> {\n try {\n const res = await fetch(`${apiUrl}/public/images/slot/favicon`, {\n headers: {\n 'Content-Type': 'application/json',\n 'x-api-key': apiKey,\n },\n next: { revalidate: 3600 }, // Cache 1hr – favicon rarely changes, allows static generation\n })\n\n if (!res.ok) {\n console.warn('[ManagedFavicon] Failed to fetch:', res.status)\n return null\n }\n\n const data = await res.json()\n if (data.image && !data.is_placeholder) {\n const raw = data.image.public_url || data.image.external_url\n const public_url = toAbsoluteFaviconUrl(raw, apiUrl)\n return {\n public_url: public_url || undefined,\n mime_type: data.image.file?.mime_type,\n is_placeholder: false,\n }\n }\n return null\n } catch (err) {\n console.error('[ManagedFavicon] Error fetching favicon:', err)\n return null\n }\n}\n\n/**\n * Return the managed favicon URL for the project (from API key).\n * Use in generateMetadata to set metadata.icons so favicon is consistent across all sites.\n */\nexport async function getManagedFaviconUrl(\n apiKey?: string,\n apiUrl: string = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com'\n): Promise<string | null> {\n const key = apiKey ?? process.env.NEXT_PUBLIC_UPTRADE_API_KEY ?? process.env.UPTRADE_API_KEY\n if (!key) return null\n const data = await fetchFaviconData(apiUrl, key)\n return data?.public_url ?? null\n}\n\nexport async function ManagedFavicon({\n apiKey = process.env.NEXT_PUBLIC_UPTRADE_API_KEY,\n apiUrl = process.env.NEXT_PUBLIC_UPTRADE_API_URL || 'https://api.uptrademedia.com',\n fallback = '/favicon.ico',\n themeColor = '#4bbf39',\n}: ManagedFaviconProps) {\n // Fetch favicon data during SSR\n const faviconData = apiKey && apiUrl ? await fetchFaviconData(apiUrl, apiKey) : null\n \n const faviconUrl = faviconData?.public_url || fallback\n const mimeType = faviconData?.mime_type || 'image/x-icon'\n const isSvg = mimeType === 'image/svg+xml' || faviconUrl.endsWith('.svg')\n\n return (\n <>\n {/* Primary favicon */}\n {isSvg ? (\n <link rel=\"icon\" type=\"image/svg+xml\" href={faviconUrl} />\n ) : (\n <>\n <link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href={faviconUrl} />\n <link rel=\"icon\" type=\"image/png\" sizes=\"16x16\" href={faviconUrl} />\n </>\n )}\n \n {/* Apple touch icon (for iOS home screen) */}\n <link rel=\"apple-touch-icon\" sizes=\"180x180\" href={faviconUrl} />\n \n {/* Theme color for mobile browsers */}\n <meta name=\"theme-color\" content={themeColor} />\n </>\n )\n}"]}
@@ -1,5 +1,5 @@
1
1
  import { jsxs, jsx, Fragment } from 'react/jsx-runtime';
2
- import React5, { useState, useEffect, useMemo, useCallback } from 'react';
2
+ import React5, { useState, useEffect, useMemo, useRef, useCallback } from 'react';
3
3
 
4
4
  // src/commerce/utils.ts
5
5
  function formatPrice(amount, currency = "USD") {
@@ -248,6 +248,24 @@ async function registerForEvent(eventId, scheduleId, customer) {
248
248
  return { success: false, error: "Network error. Please try again." };
249
249
  }
250
250
  }
251
+ async function fetchProcessorConfig() {
252
+ const { apiUrl, apiKey } = getApiConfig();
253
+ if (!apiKey) return null;
254
+ try {
255
+ const response = await fetch(`${apiUrl}/api/public/commerce/processor-config`, {
256
+ method: "POST",
257
+ headers: {
258
+ "Content-Type": "application/json",
259
+ "x-api-key": apiKey
260
+ },
261
+ body: JSON.stringify({})
262
+ });
263
+ if (!response.ok) return null;
264
+ return await response.json();
265
+ } catch {
266
+ return null;
267
+ }
268
+ }
251
269
  async function createCheckoutSession(optionsOrOfferingId, legacyOptions) {
252
270
  const { apiUrl, apiKey } = getApiConfig();
253
271
  const options = typeof optionsOrOfferingId === "string" ? { offeringId: optionsOrOfferingId, ...legacyOptions } : optionsOrOfferingId;
@@ -268,8 +286,12 @@ async function createCheckoutSession(optionsOrOfferingId, legacyOptions) {
268
286
  })
269
287
  });
270
288
  if (!response.ok) {
271
- const error = await response.json();
272
- return { success: false, error: error.message || "Checkout failed" };
289
+ const error = await response.json().catch(() => ({}));
290
+ const message = error?.message || (typeof error?.error === "string" ? error.error : null) || "Checkout failed";
291
+ if (typeof console !== "undefined" && console.error) {
292
+ console.error("[Commerce] Checkout failed:", response.status, message, error);
293
+ }
294
+ return { success: false, error: message };
273
295
  }
274
296
  const result = await response.json();
275
297
  return {
@@ -2805,6 +2827,17 @@ function CalendarView({
2805
2827
  }, children: "Loading..." })
2806
2828
  ] });
2807
2829
  }
2830
+ function loadSquareSDK(environment) {
2831
+ return new Promise((resolve, reject) => {
2832
+ if (typeof window === "undefined") return reject(new Error("No window"));
2833
+ if (window.Square) return resolve();
2834
+ const script = document.createElement("script");
2835
+ script.src = environment === "sandbox" ? "https://sandbox.web.squarecdn.com/v1/square.js" : "https://web.squarecdn.com/v1/square.js";
2836
+ script.onload = () => resolve();
2837
+ script.onerror = () => reject(new Error("Failed to load Square SDK"));
2838
+ document.head.appendChild(script);
2839
+ });
2840
+ }
2808
2841
  function EventModal({
2809
2842
  event,
2810
2843
  schedule: propSchedule,
@@ -2828,7 +2861,56 @@ function EventModal({
2828
2861
  phone: ""
2829
2862
  });
2830
2863
  const [additionalData, setAdditionalData] = useState({});
2864
+ const [processor, setProcessor] = useState(null);
2865
+ const [processorConfig, setProcessorConfig] = useState(null);
2866
+ const [squareCard, setSquareCard] = useState(null);
2867
+ const [cardReady, setCardReady] = useState(false);
2868
+ const cardContainerRef = useRef(null);
2831
2869
  const schedule = propSchedule || event?.schedules?.[0] || event?.next_schedule;
2870
+ useEffect(() => {
2871
+ if (!isOpen) return;
2872
+ fetchProcessorConfig().then(async (config) => {
2873
+ setProcessorConfig(config);
2874
+ if (config?.processor) setProcessor(config.processor);
2875
+ if (config?.processor === "square" && config.squareAppId && config.squareLocationId) {
2876
+ try {
2877
+ await loadSquareSDK(config.squareEnvironment || "production");
2878
+ const Square = window.Square;
2879
+ const payments = Square.payments(config.squareAppId, config.squareLocationId);
2880
+ const card = await payments.card({
2881
+ style: {
2882
+ ".input-container": {
2883
+ borderColor: "#d1d5db",
2884
+ borderRadius: "6px"
2885
+ },
2886
+ ".input-container.is-focus": {
2887
+ borderColor: "#2563eb"
2888
+ },
2889
+ ".input-container.is-error": {
2890
+ borderColor: "#dc2626"
2891
+ }
2892
+ }
2893
+ });
2894
+ await card.attach("#sq-card-container");
2895
+ setSquareCard(card);
2896
+ setCardReady(true);
2897
+ } catch (err) {
2898
+ console.error("[Commerce] Square card init failed:", err);
2899
+ }
2900
+ }
2901
+ });
2902
+ return () => {
2903
+ setSquareCard((prev) => {
2904
+ if (prev) {
2905
+ prev.destroy?.().catch(() => {
2906
+ });
2907
+ }
2908
+ return null;
2909
+ });
2910
+ setCardReady(false);
2911
+ setProcessorConfig(null);
2912
+ };
2913
+ }, [isOpen]);
2832
2914
  useEffect(() => {
2833
2915
  if (isOpen && event) {
2834
2916
  setError(null);
@@ -2889,17 +2971,34 @@ function EventModal({
2889
2971
  onError?.(result.error || "Registration failed");
2890
2972
  }
2891
2973
  } else {
2892
- const result = await createCheckoutSession(event.id, {
2974
+ let sourceId;
2975
+ if (squareCard && cardReady) {
2976
+ const tokenResult = await squareCard.tokenize();
2977
+ if (tokenResult.status !== "OK") {
2978
+ const msg = tokenResult.errors?.[0]?.message || "Card verification failed. Please check your card details.";
2979
+ setError(msg);
2980
+ onError?.(msg);
2981
+ setLoading(false);
2982
+ return;
2983
+ }
2984
+ sourceId = tokenResult.token;
2985
+ }
2986
+ const result = await createCheckoutSession({
2987
+ offeringId: event.id,
2893
2988
  scheduleId: schedule.id,
2894
2989
  quantity,
2895
2990
  customer: {
2896
2991
  ...customer,
2897
2992
  ...additionalData
2898
2993
  },
2994
+ sourceId,
2899
2995
  successUrl: window.location.href + "?registration=success",
2900
2996
  cancelUrl: window.location.href
2901
2997
  });
2902
- if (result.success && result.payment_url) {
2998
+ if (result.success && !result.payment_url) {
2999
+ setSuccess(true);
3000
+ onSuccess?.(result);
3001
+ } else if (result.success && result.payment_url) {
2903
3002
  window.location.href = result.payment_url;
2904
3003
  } else {
2905
3004
  setError(result.error || "Checkout failed");
@@ -3223,6 +3322,30 @@ function EventModal({
3223
3322
  )
3224
3323
  ] }, field.name))
3225
3324
  ] }),
3325
+ !isFree && processor === "square" && /* @__PURE__ */ jsxs("div", { style: { marginTop: "1rem" }, children: [
3326
+ /* @__PURE__ */ jsx("label", { style: {
3327
+ display: "block",
3328
+ fontSize: "0.875rem",
3329
+ fontWeight: 500,
3330
+ color: "#374151",
3331
+ marginBottom: "0.375rem"
3332
+ }, children: "Card Details *" }),
3333
+ /* @__PURE__ */ jsx(
3334
+ "div",
3335
+ {
3336
+ id: "sq-card-container",
3337
+ ref: cardContainerRef,
3338
+ style: {
3339
+ minHeight: "42px",
3340
+ border: "1px solid #d1d5db",
3341
+ borderRadius: "6px",
3342
+ padding: "0.5rem 0.75rem",
3343
+ background: "#fff"
3344
+ }
3345
+ }
3346
+ ),
3347
+ !cardReady && /* @__PURE__ */ jsx("p", { style: { fontSize: "0.75rem", color: "#9ca3af", marginTop: "0.25rem" }, children: "Loading card form..." })
3348
+ ] }),
3226
3349
  error && /* @__PURE__ */ jsx("div", { style: {
3227
3350
  marginTop: "1rem",
3228
3351
  padding: "0.75rem",
@@ -3236,7 +3359,7 @@ function EventModal({
3236
3359
  "button",
3237
3360
  {
3238
3361
  type: "submit",
3239
- disabled: loading,
3362
+ disabled: loading || !isFree && processor === "square" && !cardReady,
3240
3363
  style: {
3241
3364
  width: "100%",
3242
3365
  marginTop: "1.25rem",
@@ -3245,19 +3368,26 @@ function EventModal({
3245
3368
  fontWeight: 600,
3246
3369
  borderRadius: "8px",
3247
3370
  border: "none",
3248
- background: loading ? "#93c5fd" : "#2563eb",
3371
+ background: loading || !isFree && processor === "square" && !cardReady ? "#93c5fd" : "#2563eb",
3249
3372
  color: "white",
3250
- cursor: loading ? "not-allowed" : "pointer"
3373
+ cursor: loading || !isFree && processor === "square" && !cardReady ? "not-allowed" : "pointer"
3251
3374
  },
3252
3375
  children: loading ? "Processing..." : isFree ? "Register Free" : `Pay ${formatPrice(total, event.currency)}`
3253
3376
  }
3254
3377
  ),
3255
- !isFree && /* @__PURE__ */ jsx("p", { style: {
3378
+ !isFree && /* @__PURE__ */ jsxs("p", { style: {
3256
3379
  textAlign: "center",
3257
3380
  fontSize: "0.75rem",
3258
3381
  color: "#666",
3259
3382
  margin: "0.75rem 0 0"
3260
- }, children: "\u{1F512} Secure checkout via Stripe" })
3383
+ }, children: [
3384
+ "\u{1F512} Secure checkout via ",
3385
+ processor === "square" ? "Square" : processor === "stripe" ? "Stripe" : "secure payment"
3386
+ ] }),
3387
+ !isFree && processor && /* @__PURE__ */ jsxs("p", { style: { fontSize: "0.625rem", color: "#999", margin: "0.25rem 0 0", textAlign: "center" }, children: [
3388
+ "Powered by ",
3389
+ processor === "stripe" ? "Stripe" : "Square"
3390
+ ] })
3261
3391
  ] })
3262
3392
  ] }) })
3263
3393
  ]
@@ -3894,6 +4024,6 @@ function getApiKey() {
3894
4024
  return "";
3895
4025
  }
3896
4026
 
3897
- export { CalendarView, CheckoutForm, EventCalendar, EventEmbed, EventModal, EventTile, EventsWidget, OfferingCard, OfferingList, ProductDetail, ProductEmbed, ProductGrid, ProductPage, RegistrationForm, UpcomingEvents, createCheckoutSession, fetchActiveProcessor, fetchCategories, fetchLatestOffering, fetchNextEvent, fetchOffering, fetchOfferings, fetchProductBySlug, fetchProducts, fetchProductsPublic, fetchServices, fetchUpcomingEvents, formatDate, formatDateRange, formatDateTime, formatPrice, formatTime, getOfferingUrl, getRelativeTimeUntil, getSpotsRemaining, isEventSoldOut, registerForEvent, useEventModal };
3898
- //# sourceMappingURL=chunk-A37Z47FZ.mjs.map
3899
- //# sourceMappingURL=chunk-A37Z47FZ.mjs.map
4027
+ export { CalendarView, CheckoutForm, EventCalendar, EventEmbed, EventModal, EventTile, EventsWidget, OfferingCard, OfferingList, ProductDetail, ProductEmbed, ProductGrid, ProductPage, RegistrationForm, UpcomingEvents, createCheckoutSession, fetchActiveProcessor, fetchCategories, fetchLatestOffering, fetchNextEvent, fetchOffering, fetchOfferings, fetchProcessorConfig, fetchProductBySlug, fetchProducts, fetchProductsPublic, fetchServices, fetchUpcomingEvents, formatDate, formatDateRange, formatDateTime, formatPrice, formatTime, getOfferingUrl, getRelativeTimeUntil, getSpotsRemaining, isEventSoldOut, registerForEvent, useEventModal };
4028
+ //# sourceMappingURL=chunk-R4OKQVFD.mjs.map
4029
+ //# sourceMappingURL=chunk-R4OKQVFD.mjs.map