@bigz-app/booking-widget 0.2.3 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.cjs CHANGED
@@ -257,9 +257,9 @@ const resolveSemanticColor = (colorValue, fallbackValue) => {
257
257
  const themes = {
258
258
  // --- Light Themes ---
259
259
  "light-fresh": {
260
- highlight: "oklch(0.65 0.2 190)", // accent-strong
261
- background: "oklch(0.99 0.005 220)", // neutral-strong (background)
262
- surface: "oklch(1 0 0)", // card (pure white)
260
+ highlight: "#00a8a1", // accent-strong
261
+ background: "#f8fdfe", // neutral-strong (background)
262
+ surface: "#ffffff", // card (pure white)
263
263
  text: "#0e7490", // Turquoise 800
264
264
  border: "#bae6fd", // Blue 200
265
265
  success: "#38bdf8", // Blue 400
@@ -9132,7 +9132,7 @@ var reactStripe_umd = {exports: {}};
9132
9132
 
9133
9133
  var reactStripe_umdExports = reactStripe_umd.exports;
9134
9134
 
9135
- const spinner = (borderColor) => (jsxRuntime.jsx("div", { style: {
9135
+ const spinner$1 = (borderColor) => (jsxRuntime.jsx("div", { style: {
9136
9136
  width: "auto",
9137
9137
  height: "auto",
9138
9138
  fontSize: "16px",
@@ -9268,7 +9268,7 @@ function PaymentFormInner({ config, eventDetails, formData, totalAmount, discoun
9268
9268
  if (!isLoading) {
9269
9269
  e.currentTarget.style.backgroundColor = "var(--bw-highlight-color)";
9270
9270
  }
9271
- }, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner("var(--bw-surface-color)"), " Verarbeite Zahlung..."] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [" ", totalAmount <
9271
+ }, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [spinner$1("var(--bw-surface-color)"), " Verarbeite Zahlung..."] })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [" ", totalAmount <
9272
9272
  eventDetails.price * formData.participants.filter((p) => p.name.trim()).length
9273
9273
  ? "Anzahlen & buchen"
9274
9274
  : "Jetzt buchen"] })) }), jsxRuntime.jsx("style", { children: `
@@ -9284,6 +9284,64 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
9284
9284
  const [paymentIntentId, setPaymentIntentId] = React__default.useState(null);
9285
9285
  const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = React__default.useState(false);
9286
9286
  const [paymentError, setPaymentError] = React__default.useState(null);
9287
+ // LocalStorage persistence (scoped by organization + event) for paymentIntentId only
9288
+ const storageKey = typeof window !== "undefined"
9289
+ ? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
9290
+ : "";
9291
+ const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000; // 24 hours
9292
+ function loadPersistedPaymentIntent() {
9293
+ if (typeof window === "undefined" || !storageKey)
9294
+ return null;
9295
+ try {
9296
+ const raw = window.localStorage.getItem(storageKey);
9297
+ if (!raw)
9298
+ return null;
9299
+ const parsed = JSON.parse(raw);
9300
+ if (!parsed?.id || !parsed?.ts)
9301
+ return null;
9302
+ if (Date.now() - parsed.ts > PAYMENT_INTENT_TTL) {
9303
+ window.localStorage.removeItem(storageKey);
9304
+ return null;
9305
+ }
9306
+ return parsed.id;
9307
+ }
9308
+ catch {
9309
+ return null;
9310
+ }
9311
+ }
9312
+ function persistPaymentIntent(id) {
9313
+ if (typeof window === "undefined" || !storageKey || !id)
9314
+ return;
9315
+ try {
9316
+ const payload = { id, ts: Date.now() };
9317
+ window.localStorage.setItem(storageKey, JSON.stringify(payload));
9318
+ }
9319
+ catch { }
9320
+ }
9321
+ function clearPersistedPaymentIntent() {
9322
+ if (typeof window === "undefined" || !storageKey)
9323
+ return;
9324
+ try {
9325
+ window.localStorage.removeItem(storageKey);
9326
+ }
9327
+ catch { }
9328
+ }
9329
+ // On mount (and when scope changes), restore persisted paymentIntentId
9330
+ React__default.useEffect(() => {
9331
+ if (!paymentIntentId) {
9332
+ const restored = loadPersistedPaymentIntent();
9333
+ if (restored) {
9334
+ setPaymentIntentId(restored);
9335
+ }
9336
+ }
9337
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9338
+ }, [storageKey]);
9339
+ // Persist whenever paymentIntentId changes
9340
+ React__default.useEffect(() => {
9341
+ if (paymentIntentId) {
9342
+ persistPaymentIntent(paymentIntentId);
9343
+ }
9344
+ }, [paymentIntentId]);
9287
9345
  // Create payment intent when component mounts or when relevant data changes
9288
9346
  React__default.useEffect(() => {
9289
9347
  const createPaymentIntent = async () => {
@@ -9388,7 +9446,7 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
9388
9446
  justifyContent: "center",
9389
9447
  padding: "var(--bw-spacing)",
9390
9448
  gap: "8px",
9391
- }, children: [spinner(), jsxRuntime.jsx("span", { style: {
9449
+ }, children: [spinner$1(), jsxRuntime.jsx("span", { style: {
9392
9450
  color: "var(--bw-text-muted)",
9393
9451
  fontFamily: "var(--bw-font-family)",
9394
9452
  fontSize: "var(--bw-font-size)",
@@ -9413,10 +9471,16 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
9413
9471
  clientSecret,
9414
9472
  appearance: stripeAppearance || { theme: "stripe" },
9415
9473
  locale: config.locale || "de",
9416
- }, children: jsxRuntime.jsx(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, clientSecret: clientSecret }) }));
9474
+ }, children: jsxRuntime.jsx(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: (result) => {
9475
+ // Clear persisted PI data on successful payment
9476
+ clearPersistedPaymentIntent();
9477
+ setPaymentIntentId(null);
9478
+ setClientSecret(null);
9479
+ onSuccess(result);
9480
+ }, onError: onError, systemConfig: systemConfig, clientSecret: clientSecret }) }));
9417
9481
  }
9418
9482
 
9419
- function Sidebar({ isOpen, onClose, title, children, width = "400px" }) {
9483
+ function Sidebar({ isOpen, onClose, title, children, width = "450px" }) {
9420
9484
  const [isVisible, setIsVisible] = React__default.useState(false);
9421
9485
  const [isAnimating, setIsAnimating] = React__default.useState(false);
9422
9486
  const themedStyles = useStyles();
@@ -9815,7 +9879,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
9815
9879
  marginBottom: "8px",
9816
9880
  fontFamily: "var(--bw-font-family)",
9817
9881
  };
9818
- return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Buchung - ${eventDetails.name}`, width: "420px", children: jsxRuntime.jsxs("div", { className: "booking-widget-container", style: { padding: "var(--bw-spacing)" }, children: [jsxRuntime.jsxs("div", { style: {
9882
+ return (jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Buchung - ${eventDetails.name}`, children: jsxRuntime.jsxs("div", { className: "booking-widget-container", style: { padding: "var(--bw-spacing)" }, children: [jsxRuntime.jsxs("div", { style: {
9819
9883
  backgroundColor: "var(--bw-surface-color)",
9820
9884
  border: `1px solid var(--bw-border-color)`,
9821
9885
  backdropFilter: "blur(4px)",
@@ -10033,7 +10097,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
10033
10097
  fontFamily: "var(--bw-font-family)",
10034
10098
  display: "block",
10035
10099
  marginBottom: "8px",
10036
- }, children: "Kommentar (optional)" }), jsxRuntime.jsx("textarea", { id: "booking-comment", ...form.register("comment"), placeholder: "Zus\u00E4tzliche Anmerkungen zu Ihrer Buchung...", rows: 3, style: {
10100
+ }, children: "Kommentar (optional)" }), jsxRuntime.jsx("textarea", { id: "booking-comment", ...form.register("comment"), placeholder: "Zus\u00E4tzliche Anmerkungen zur Buchung...", rows: 3, style: {
10037
10101
  width: "100%",
10038
10102
  padding: "12px",
10039
10103
  border: `1px solid var(--bw-border-color)`,
@@ -10228,6 +10292,258 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
10228
10292
  ` })] }) }));
10229
10293
  }
10230
10294
 
10295
+ /**
10296
+ * Google Ads Conversion Tracking Utility
10297
+ *
10298
+ * This utility handles Google Ads conversion tracking for the booking widget.
10299
+ * It includes consent checking, gtag initialization, and conversion tracking.
10300
+ */
10301
+ /**
10302
+ * Check if Google Consent Mode has granted the necessary permissions for ads tracking
10303
+ * This only checks Google's official consent mode - the proper way to handle Google Ads consent
10304
+ */
10305
+ function checkGoogleConsent() {
10306
+ console.log("[Google Ads Tracking] 🔍 Checking Google consent...");
10307
+ if (typeof window === "undefined") {
10308
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10309
+ return false;
10310
+ }
10311
+ console.log("[Google Ads Tracking] ✅ Window object available");
10312
+ // Check for gtag consent mode
10313
+ if (typeof window.gtag === "function") {
10314
+ console.log("[Google Ads Tracking] ✅ gtag function found");
10315
+ try {
10316
+ // Try to get consent state from Google's consent mode
10317
+ let consentGranted = false;
10318
+ window.gtag("get", (consentState) => {
10319
+ console.log("[Google Ads Tracking] 📋 Consent state received:", consentState);
10320
+ // For Google Ads conversion tracking, we need ad_storage consent
10321
+ consentGranted = consentState.ad_storage === "granted";
10322
+ console.log("[Google Ads Tracking] 🔐 ad_storage consent:", consentState.ad_storage);
10323
+ });
10324
+ console.log("[Google Ads Tracking] 🎯 Final consent result:", consentGranted);
10325
+ return consentGranted;
10326
+ }
10327
+ catch (error) {
10328
+ console.warn("[Google Ads Tracking] ⚠️ Error checking gtag consent:", error);
10329
+ }
10330
+ }
10331
+ else {
10332
+ console.log("[Google Ads Tracking] ❌ gtag function not found");
10333
+ }
10334
+ // If gtag is not available, we assume consent has not been properly configured
10335
+ // The host page should implement Google Consent Mode if they want tracking
10336
+ console.log("[Google Ads Tracking] 🚫 No consent mechanism found, returning false");
10337
+ return false;
10338
+ }
10339
+ /**
10340
+ * Check if gtag is already initialized on the host page
10341
+ */
10342
+ function isGtagInitialized() {
10343
+ console.log("[Google Ads Tracking] 🔍 Checking if gtag is initialized...");
10344
+ if (typeof window === "undefined") {
10345
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10346
+ return false;
10347
+ }
10348
+ console.log("[Google Ads Tracking] ✅ Window object available");
10349
+ // Check if gtag function exists
10350
+ if (typeof window.gtag === "function") {
10351
+ console.log("[Google Ads Tracking] ✅ gtag function exists");
10352
+ return true;
10353
+ }
10354
+ console.log("[Google Ads Tracking] ❌ gtag function not found, checking for scripts...");
10355
+ // Check if Google Analytics or Google Ads scripts are already loaded
10356
+ const scripts = document.querySelectorAll('script[src*="googletagmanager.com"]');
10357
+ console.log("[Google Ads Tracking] 📜 Found", scripts.length, "Google Tag Manager scripts");
10358
+ const isInitialized = scripts.length > 0;
10359
+ console.log("[Google Ads Tracking] 🎯 gtag initialization status:", isInitialized);
10360
+ return isInitialized;
10361
+ }
10362
+ /**
10363
+ * Initialize Google Tag (gtag) if not already present
10364
+ */
10365
+ function initializeGtag(tagId) {
10366
+ console.log("[Google Ads Tracking] 🚀 Starting gtag initialization with tagId:", tagId);
10367
+ return new Promise((resolve, reject) => {
10368
+ if (typeof window === "undefined") {
10369
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10370
+ reject(new Error("Window object not available"));
10371
+ return;
10372
+ }
10373
+ console.log("[Google Ads Tracking] ✅ Window object available");
10374
+ // If gtag is already initialized, just resolve
10375
+ if (isGtagInitialized()) {
10376
+ console.log("[Google Ads Tracking] ⚡ gtag already initialized, skipping setup");
10377
+ resolve();
10378
+ return;
10379
+ }
10380
+ console.log("[Google Ads Tracking] 📦 gtag not found, creating new script element...");
10381
+ try {
10382
+ // Create the gtag script
10383
+ const script = document.createElement("script");
10384
+ script.async = true;
10385
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;
10386
+ console.log("[Google Ads Tracking] 🌐 Script src set to:", script.src);
10387
+ script.onload = () => {
10388
+ console.log("[Google Ads Tracking] 📥 Script loaded successfully, initializing gtag...");
10389
+ // Initialize gtag
10390
+ window.dataLayer = window.dataLayer || [];
10391
+ console.log("[Google Ads Tracking] 📊 dataLayer initialized");
10392
+ window.gtag = (...args) => {
10393
+ window.dataLayer.push(args);
10394
+ };
10395
+ console.log("[Google Ads Tracking] 🔧 gtag function created");
10396
+ // Configure gtag
10397
+ console.log("[Google Ads Tracking] ⚙️ Configuring gtag with privacy settings...");
10398
+ window.gtag("js", new Date());
10399
+ window.gtag("config", tagId, {
10400
+ // Respect consent settings
10401
+ anonymize_ip: true,
10402
+ allow_google_signals: false,
10403
+ allow_ad_personalization_signals: false,
10404
+ });
10405
+ console.log("[Google Ads Tracking] ✅ gtag initialized successfully with tagId:", tagId);
10406
+ resolve();
10407
+ };
10408
+ script.onerror = (error) => {
10409
+ console.error("[Google Ads Tracking] ❌ Failed to load Google Tag script:", error);
10410
+ reject(new Error("Failed to load Google Tag script"));
10411
+ };
10412
+ console.log("[Google Ads Tracking] 📑 Adding script to document head...");
10413
+ // Add script to head
10414
+ document.head.appendChild(script);
10415
+ console.log("[Google Ads Tracking] ✅ Script element appended to head");
10416
+ }
10417
+ catch (error) {
10418
+ console.error("[Google Ads Tracking] ❌ Error during gtag initialization:", error);
10419
+ reject(error);
10420
+ }
10421
+ });
10422
+ }
10423
+ /**
10424
+ * Track a Google Ads conversion
10425
+ */
10426
+ function trackConversion(config) {
10427
+ console.log("[Google Ads Tracking] 🎯 Starting conversion tracking with config:", config);
10428
+ if (typeof window === "undefined") {
10429
+ console.warn("[Google Ads Tracking] ❌ Window object not available");
10430
+ return;
10431
+ }
10432
+ console.log("[Google Ads Tracking] ✅ Window object available");
10433
+ if (!config.tagId || !config.conversionId) {
10434
+ console.warn("[Google Ads Tracking] ❌ Missing tagId or conversionId", {
10435
+ tagId: config.tagId,
10436
+ conversionId: config.conversionId,
10437
+ });
10438
+ return;
10439
+ }
10440
+ console.log("[Google Ads Tracking] ✅ Required config fields present:", {
10441
+ tagId: config.tagId,
10442
+ conversionId: config.conversionId,
10443
+ });
10444
+ if (typeof window.gtag !== "function") {
10445
+ console.warn("[Google Ads Tracking] ❌ gtag function not available");
10446
+ return;
10447
+ }
10448
+ console.log("[Google Ads Tracking] ✅ gtag function is available");
10449
+ try {
10450
+ console.log("[Google Ads Tracking] 🔧 Building conversion data...");
10451
+ const conversionData = {
10452
+ send_to: `${config.tagId}/${config.conversionId}`,
10453
+ };
10454
+ console.log("[Google Ads Tracking] 📋 Base conversion data:", conversionData);
10455
+ // Add optional parameters
10456
+ if (config.conversionValue !== undefined) {
10457
+ conversionData.value = config.conversionValue;
10458
+ console.log("[Google Ads Tracking] 💰 Added conversion value:", config.conversionValue);
10459
+ }
10460
+ if (config.conversionCurrency) {
10461
+ conversionData.currency = config.conversionCurrency;
10462
+ console.log("[Google Ads Tracking] 💱 Added currency:", config.conversionCurrency);
10463
+ }
10464
+ if (config.transactionId) {
10465
+ conversionData.transaction_id = config.transactionId;
10466
+ console.log("[Google Ads Tracking] 🔖 Added transaction ID:", config.transactionId);
10467
+ }
10468
+ console.log("[Google Ads Tracking] 📦 Final conversion data:", conversionData);
10469
+ // Track the conversion
10470
+ console.log("[Google Ads Tracking] 🚀 Sending conversion event to gtag...");
10471
+ window.gtag("event", "conversion", conversionData);
10472
+ console.log("[Google Ads Tracking] ✅ Conversion tracked successfully:", conversionData);
10473
+ }
10474
+ catch (error) {
10475
+ console.error("[Google Ads Tracking] ❌ Error tracking conversion:", error);
10476
+ }
10477
+ }
10478
+ /**
10479
+ * Main function to handle Google Ads conversion tracking
10480
+ * This function checks consent, initializes gtag if needed, and tracks the conversion
10481
+ */
10482
+ async function handleGoogleAdsConversion(config) {
10483
+ console.log("[Google Ads Tracking] 🎬 Starting handleGoogleAdsConversion with config:", config);
10484
+ // Validate config
10485
+ if (!config.tagId || !config.conversionId) {
10486
+ console.log("[Google Ads Tracking] ❌ No tagId or conversionId provided, skipping conversion tracking", { tagId: config.tagId, conversionId: config.conversionId });
10487
+ return;
10488
+ }
10489
+ console.log("[Google Ads Tracking] ✅ Config validation passed");
10490
+ // Check consent first
10491
+ console.log("[Google Ads Tracking] 🔐 Checking consent...");
10492
+ if (!checkGoogleConsent()) {
10493
+ console.log("[Google Ads Tracking] 🚫 Google consent not granted, skipping conversion tracking");
10494
+ return;
10495
+ }
10496
+ console.log("[Google Ads Tracking] ✅ Consent check passed");
10497
+ try {
10498
+ // Initialize gtag if not already present
10499
+ console.log("[Google Ads Tracking] 🔍 Checking if gtag needs initialization...");
10500
+ if (!isGtagInitialized()) {
10501
+ console.log("[Google Ads Tracking] 🚀 Initializing gtag...");
10502
+ await initializeGtag(config.tagId);
10503
+ console.log("[Google Ads Tracking] ✅ gtag initialization completed");
10504
+ }
10505
+ else {
10506
+ console.log("[Google Ads Tracking] ⚡ gtag already initialized, proceeding...");
10507
+ }
10508
+ // Small delay to ensure gtag is ready
10509
+ console.log("[Google Ads Tracking] ⏱️ Adding 100ms delay to ensure gtag is ready...");
10510
+ setTimeout(() => {
10511
+ console.log("[Google Ads Tracking] 🎯 Delay completed, tracking conversion...");
10512
+ trackConversion(config);
10513
+ }, 100);
10514
+ }
10515
+ catch (error) {
10516
+ console.error("[Google Ads Tracking] ❌ Error handling conversion:", error);
10517
+ }
10518
+ }
10519
+ /**
10520
+ * Utility function to get conversion value from booking data
10521
+ */
10522
+ function getConversionValueFromBooking(bookingData) {
10523
+ console.log("[Google Ads Tracking] 💰 Extracting conversion value from booking data:", bookingData);
10524
+ // Try to get the total amount from various possible structures
10525
+ const possiblePaths = [
10526
+ { path: "order.total", value: bookingData?.order?.total },
10527
+ { path: "booking.total", value: bookingData?.booking?.total },
10528
+ { path: "total", value: bookingData?.total },
10529
+ { path: "amount", value: bookingData?.amount },
10530
+ { path: "payment.amount", value: bookingData?.payment?.amount },
10531
+ { path: "stripePaymentIntent.amount", value: bookingData?.stripePaymentIntent?.amount },
10532
+ ];
10533
+ console.log("[Google Ads Tracking] 🔍 Checking possible value paths:", possiblePaths);
10534
+ for (const { path, value } of possiblePaths) {
10535
+ console.log(`[Google Ads Tracking] 📋 Checking path '${path}':`, value);
10536
+ if (typeof value === "number" && value > 0) {
10537
+ // Convert from cents to euros
10538
+ const convertedValue = value / 100;
10539
+ console.log(`[Google Ads Tracking] ✅ Found valid value at '${path}': ${value} cents = ${convertedValue} euros`);
10540
+ return convertedValue;
10541
+ }
10542
+ }
10543
+ console.log("[Google Ads Tracking] ❌ No valid conversion value found in booking data");
10544
+ return undefined;
10545
+ }
10546
+
10231
10547
  const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId, }) => {
10232
10548
  const [bookingData, setBookingData] = React__default.useState(null);
10233
10549
  const [eventDetails, setEventDetails] = React__default.useState(null);
@@ -10272,6 +10588,27 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
10272
10588
  });
10273
10589
  // Set payment status from Stripe data or order status
10274
10590
  setPaymentStatus(data.stripePaymentIntent?.status || data.order.status);
10591
+ // Trigger Google Ads conversion tracking if payment is successful
10592
+ const finalPaymentStatus = data.stripePaymentIntent?.status || data.order.status;
10593
+ if (finalPaymentStatus === "succeeded" &&
10594
+ config.googleAds?.tagId &&
10595
+ config.googleAds?.conversionId) {
10596
+ // Prepare conversion tracking data
10597
+ const conversionValue = config.googleAds.includeValue !== false
10598
+ ? getConversionValueFromBooking(data)
10599
+ : undefined;
10600
+ const transactionId = data.order.id;
10601
+ // Track the conversion
10602
+ handleGoogleAdsConversion({
10603
+ tagId: config.googleAds.tagId,
10604
+ conversionId: config.googleAds.conversionId,
10605
+ conversionValue,
10606
+ conversionCurrency: config.googleAds.conversionCurrency || "EUR",
10607
+ transactionId,
10608
+ }).catch((error) => {
10609
+ console.warn("[BookingSuccessModal] Google Ads conversion tracking failed:", error);
10610
+ });
10611
+ }
10275
10612
  }
10276
10613
  else {
10277
10614
  onError?.(data.error || "Fehler beim Abrufen der Buchungsdaten");
@@ -10661,44 +10998,42 @@ const getPriceDisplayInfo = (price, yearPrices) => {
10661
10998
  // Allocation Badge Component
10662
10999
  const AllocationBadge = ({ availableSpots, maxParticipants, }) => {
10663
11000
  const badgeInfo = getAllocationBadgeInfo(availableSpots, maxParticipants);
10664
- if (!badgeInfo)
10665
- return null;
10666
11001
  return (jsxRuntime.jsx("div", { style: {
10667
- position: "absolute",
10668
- bottom: "4px",
10669
- right: "20px",
10670
- backgroundColor: badgeInfo.backgroundColor,
10671
- color: badgeInfo.textColor,
11002
+ backgroundColor: badgeInfo?.backgroundColor || "transparent",
11003
+ color: badgeInfo?.textColor || "transparent",
10672
11004
  fontSize: "11px",
10673
11005
  fontWeight: "bold",
10674
- padding: "3px 8px",
11006
+ padding: "1px 8px",
11007
+ display: "flex",
11008
+ marginRight: "auto",
11009
+ marginTop: "-24px",
11010
+ marginBottom: "5px",
10675
11011
  borderRadius: "var(--bw-border-radius-small)",
10676
11012
  fontFamily: "var(--bw-font-family)",
10677
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
10678
11013
  zIndex: 50,
10679
11014
  whiteSpace: "nowrap",
10680
- }, children: badgeInfo.text }));
11015
+ width: "fit-content",
11016
+ }, children: badgeInfo?.text || " - " }));
10681
11017
  };
10682
11018
  // Price Badge Component (for special prices)
10683
11019
  const SpecialPriceBadge = ({ price, yearPrices }) => {
10684
11020
  const badgeInfo = getPriceBadgeInfo(price, yearPrices);
10685
- if (!badgeInfo)
10686
- return null;
10687
11021
  return (jsxRuntime.jsx("div", { style: {
10688
- position: "absolute",
10689
- bottom: "28px",
10690
- right: "20px",
10691
- backgroundColor: badgeInfo.backgroundColor,
10692
- color: badgeInfo.textColor,
11022
+ backgroundColor: badgeInfo?.backgroundColor || "transparent",
11023
+ color: badgeInfo?.textColor || "transparent",
10693
11024
  fontSize: "11px",
10694
11025
  fontWeight: "bold",
10695
- padding: "3px 8px",
11026
+ padding: "1px 8px",
11027
+ display: "flex",
11028
+ marginLeft: "auto",
11029
+ marginTop: "-20px",
11030
+ marginBottom: "5px",
10696
11031
  borderRadius: "var(--bw-border-radius-small)",
10697
11032
  fontFamily: "var(--bw-font-family)",
10698
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
10699
11033
  zIndex: 50,
10700
11034
  whiteSpace: "nowrap",
10701
- }, children: badgeInfo.text }));
11035
+ width: "fit-content",
11036
+ }, children: badgeInfo?.text || " - " }));
10702
11037
  };
10703
11038
  // Price Display Component (with special price styling)
10704
11039
  const PriceDisplay = ({ price, yearPrices }) => {
@@ -10711,13 +11046,27 @@ const PriceDisplay = ({ price, yearPrices }) => {
10711
11046
  fontSize: "var(--bw-font-size-medium)",
10712
11047
  fontWeight: "600",
10713
11048
  padding: "3px 8px",
10714
- marginTop: "-3px",
10715
11049
  borderRadius: "var(--bw-border-radius-small)",
10716
11050
  fontFamily: "var(--bw-font-family)",
10717
11051
  border: displayInfo ? "none" : "1px solid var(--bw-border-color)",
10718
11052
  boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
10719
11053
  }, children: formatCurrency(price) }));
10720
11054
  };
11055
+ const spinner = (borderColor) => (jsxRuntime.jsx("div", { style: {
11056
+ width: "auto",
11057
+ height: "auto",
11058
+ fontSize: "16px",
11059
+ color: "var(--bw-text-color)",
11060
+ animation: "spin 1s linear infinite",
11061
+ display: "flex",
11062
+ justifyContent: "center",
11063
+ alignItems: "center",
11064
+ }, children: jsxRuntime.jsx("div", { style: {
11065
+ width: "16px",
11066
+ height: "16px",
11067
+ border: `3px dotted ${"var(--bw-highlight-color)"}`,
11068
+ borderRadius: "50%",
11069
+ } }) }));
10721
11070
  function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, }) {
10722
11071
  const [selectedEventInstanceId, setSelectedEventInstanceId] = React__default.useState(null);
10723
11072
  const [openMonths, setOpenMonths] = React__default.useState(new Set());
@@ -10794,7 +11143,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10794
11143
  .bw-accordion-trigger,
10795
11144
  .bw-event-instance-card {
10796
11145
  position: relative;
10797
- overflow: hidden;
10798
11146
  }
10799
11147
 
10800
11148
  .bw-accordion-trigger::before,
@@ -10843,14 +11191,11 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10843
11191
  height: 32px !important;
10844
11192
  font-size: 1rem !important;
10845
11193
  }
10846
- .bw-event-instance-title {
10847
- font-size: 1rem !important;
10848
- }
10849
11194
  .bw-event-instance-price {
10850
11195
  font-size: 1.1rem !important;
10851
11196
  }
10852
11197
  }
10853
- ` }), jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name} Terminauswahl`, width: "500px", children: jsxRuntime.jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsxRuntime.jsx("div", { style: {
11198
+ ` }), jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name} Terminauswahl`, children: jsxRuntime.jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsxRuntime.jsx("div", { style: {
10854
11199
  display: "flex",
10855
11200
  flexDirection: "column",
10856
11201
  gap: "20px",
@@ -10952,13 +11297,7 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10952
11297
  height: "20px",
10953
11298
  backgroundColor: "var(--bw-border-color)",
10954
11299
  borderRadius: "8px",
10955
- } })] }), eventIdx === 0 && (jsxRuntime.jsx("div", { style: {
10956
- width: "90%",
10957
- height: "12px",
10958
- backgroundColor: "var(--bw-border-color)",
10959
- borderRadius: "4px",
10960
- marginTop: "8px",
10961
- } }))] }, eventIdx))) })] }, idx))) }) }) })] }));
11300
+ } })] })] }, eventIdx))) })] }, idx))) }) }) })] }));
10962
11301
  }
10963
11302
  // Show empty state only if not loading and no event instances
10964
11303
  if (eventInstances.length === 0) {
@@ -10967,7 +11306,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10967
11306
  .bw-accordion-trigger,
10968
11307
  .bw-event-instance-card {
10969
11308
  position: relative;
10970
- overflow: hidden;
10971
11309
  }
10972
11310
 
10973
11311
  .bw-accordion-trigger::before,
@@ -11016,9 +11354,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11016
11354
  height: 32px !important;
11017
11355
  font-size: 1rem !important;
11018
11356
  }
11019
- .bw-event-instance-title {
11020
- font-size: 1rem !important;
11021
- }
11022
11357
  .bw-event-instance-price {
11023
11358
  font-size: 1.1rem !important;
11024
11359
  }
@@ -11049,7 +11384,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11049
11384
  .bw-accordion-trigger,
11050
11385
  .bw-event-instance-card {
11051
11386
  position: relative;
11052
- overflow: hidden;
11053
11387
  }
11054
11388
 
11055
11389
  .bw-accordion-trigger::before,
@@ -11098,14 +11432,11 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11098
11432
  height: 32px !important;
11099
11433
  font-size: 1rem !important;
11100
11434
  }
11101
- .bw-event-instance-title {
11102
- font-size: 1rem !important;
11103
- }
11104
11435
  .bw-event-instance-price {
11105
11436
  font-size: 1.1rem !important;
11106
11437
  }
11107
11438
  }
11108
- ` }), jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Terminauswahl - ${selectedEventType?.name || "Event"}`, width: "500px", children: jsxRuntime.jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsxRuntime.jsx("div", { style: {
11439
+ ` }), jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Terminauswahl - ${selectedEventType?.name || "Event"}`, children: jsxRuntime.jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsxRuntime.jsx("div", { style: {
11109
11440
  display: "flex",
11110
11441
  flexDirection: "column",
11111
11442
  gap: "20px",
@@ -11180,7 +11511,7 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11180
11511
  color: "var(--bw-highlight-color-muted, rgba(59, 130, 246, 0.8))",
11181
11512
  animation: "spin 1s linear infinite",
11182
11513
  fontSize: "32px",
11183
- }, children: "\u27F3" }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants }), jsxRuntime.jsxs("div", { style: {
11514
+ }, children: spinner() }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants }), jsxRuntime.jsxs("div", { style: {
11184
11515
  display: "flex",
11185
11516
  justifyContent: "space-between",
11186
11517
  width: "100%",
@@ -11209,12 +11540,21 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11209
11540
  alignItems: "start",
11210
11541
  justifyContent: "start",
11211
11542
  lineHeight: "1.2",
11212
- }, children: [jsxRuntime.jsx("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.startTime) }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime) })] })] }), jsxRuntime.jsx("div", { className: "bw-event-instance-price", style: {
11543
+ }, children: [jsxRuntime.jsxs("div", { children: [jsxRuntime.jsx("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.startTime) }), formatWeekday(event.startTime) !==
11544
+ formatWeekday(event.endTime) && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.endTime) })] }))] }), jsxRuntime.jsx("div", { children: formatWeekday(event.startTime) ===
11545
+ formatWeekday(event.endTime) ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime) }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsxRuntime.jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime) })] })) : (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime), " Uhr"] })) })] }), jsxRuntime.jsxs("span", { style: {
11546
+ fontSize: "12px",
11547
+ fontWeight: 400,
11548
+ color: "var(--bw-text-muted)",
11549
+ marginLeft: "6px",
11550
+ background: "var(--bw-background-muted)",
11551
+ whiteSpace: "nowrap",
11552
+ }, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), jsxRuntime.jsx("div", { className: "bw-event-instance-price", style: {
11213
11553
  textAlign: "right",
11214
11554
  display: "flex",
11215
11555
  flexDirection: "column",
11216
11556
  alignItems: "end",
11217
- }, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), jsxRuntime.jsxs("h4", { className: "bw-event-instance-title", style: {
11557
+ }, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { className: "bw-event-instance-title", style: {
11218
11558
  fontSize: "var(--bw-font-size)",
11219
11559
  fontWeight: "600",
11220
11560
  color: "var(--bw-text-color)",
@@ -11223,26 +11563,8 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11223
11563
  display: "flex",
11224
11564
  alignItems: "center",
11225
11565
  gap: "8px",
11226
- }, children: [event.name, jsxRuntime.jsxs("span", { style: {
11227
- fontSize: "12px",
11228
- fontWeight: 400,
11229
- color: "var(--bw-text-muted)",
11230
- marginLeft: "6px",
11231
- background: "var(--bw-background-muted)",
11232
- borderRadius: "8px",
11233
- padding: "2px 8px",
11234
- whiteSpace: "nowrap",
11235
- }, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), event.notes && (jsxRuntime.jsx("p", { style: {
11236
- fontSize: "12px",
11237
- color: "var(--bw-text-muted)",
11238
- marginTop: "8px",
11239
- display: "-webkit-box",
11240
- WebkitBoxOrient: "vertical",
11241
- WebkitLineClamp: 2,
11242
- overflow: "hidden",
11243
- margin: "0",
11244
- lineHeight: "1.3",
11245
- }, children: event.notes }))] }, event.id));
11566
+ maxWidth: "230px",
11567
+ }, children: event.name }))] }, event.id));
11246
11568
  }) }) }, month));
11247
11569
  }) }) }) })] }));
11248
11570
  }
@@ -11883,7 +12205,6 @@ function UniversalBookingWidget({ config: baseConfig }) {
11883
12205
  body: JSON.stringify(requestBody),
11884
12206
  });
11885
12207
  const data = await response.json();
11886
- console.log("Event details response:", { status: response.status, data }); // Debug log
11887
12208
  if (response.ok) {
11888
12209
  setEventDetails(data.eventDetails);
11889
12210
  // Store system configuration for PaymentForm