@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.esm.js CHANGED
@@ -237,9 +237,9 @@ const resolveSemanticColor = (colorValue, fallbackValue) => {
237
237
  const themes = {
238
238
  // --- Light Themes ---
239
239
  "light-fresh": {
240
- highlight: "oklch(0.65 0.2 190)", // accent-strong
241
- background: "oklch(0.99 0.005 220)", // neutral-strong (background)
242
- surface: "oklch(1 0 0)", // card (pure white)
240
+ highlight: "#00a8a1", // accent-strong
241
+ background: "#f8fdfe", // neutral-strong (background)
242
+ surface: "#ffffff", // card (pure white)
243
243
  text: "#0e7490", // Turquoise 800
244
244
  border: "#bae6fd", // Blue 200
245
245
  success: "#38bdf8", // Blue 400
@@ -9112,7 +9112,7 @@ var reactStripe_umd = {exports: {}};
9112
9112
 
9113
9113
  var reactStripe_umdExports = reactStripe_umd.exports;
9114
9114
 
9115
- const spinner = (borderColor) => (jsx("div", { style: {
9115
+ const spinner$1 = (borderColor) => (jsx("div", { style: {
9116
9116
  width: "auto",
9117
9117
  height: "auto",
9118
9118
  fontSize: "16px",
@@ -9248,7 +9248,7 @@ function PaymentFormInner({ config, eventDetails, formData, totalAmount, discoun
9248
9248
  if (!isLoading) {
9249
9249
  e.currentTarget.style.backgroundColor = "var(--bw-highlight-color)";
9250
9250
  }
9251
- }, children: isLoading ? (jsxs(Fragment, { children: [spinner("var(--bw-surface-color)"), " Verarbeite Zahlung..."] })) : (jsxs(Fragment, { children: [" ", totalAmount <
9251
+ }, children: isLoading ? (jsxs(Fragment, { children: [spinner$1("var(--bw-surface-color)"), " Verarbeite Zahlung..."] })) : (jsxs(Fragment, { children: [" ", totalAmount <
9252
9252
  eventDetails.price * formData.participants.filter((p) => p.name.trim()).length
9253
9253
  ? "Anzahlen & buchen"
9254
9254
  : "Jetzt buchen"] })) }), jsx("style", { children: `
@@ -9264,6 +9264,64 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
9264
9264
  const [paymentIntentId, setPaymentIntentId] = useState(null);
9265
9265
  const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = useState(false);
9266
9266
  const [paymentError, setPaymentError] = useState(null);
9267
+ // LocalStorage persistence (scoped by organization + event) for paymentIntentId only
9268
+ const storageKey = typeof window !== "undefined"
9269
+ ? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
9270
+ : "";
9271
+ const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000; // 24 hours
9272
+ function loadPersistedPaymentIntent() {
9273
+ if (typeof window === "undefined" || !storageKey)
9274
+ return null;
9275
+ try {
9276
+ const raw = window.localStorage.getItem(storageKey);
9277
+ if (!raw)
9278
+ return null;
9279
+ const parsed = JSON.parse(raw);
9280
+ if (!parsed?.id || !parsed?.ts)
9281
+ return null;
9282
+ if (Date.now() - parsed.ts > PAYMENT_INTENT_TTL) {
9283
+ window.localStorage.removeItem(storageKey);
9284
+ return null;
9285
+ }
9286
+ return parsed.id;
9287
+ }
9288
+ catch {
9289
+ return null;
9290
+ }
9291
+ }
9292
+ function persistPaymentIntent(id) {
9293
+ if (typeof window === "undefined" || !storageKey || !id)
9294
+ return;
9295
+ try {
9296
+ const payload = { id, ts: Date.now() };
9297
+ window.localStorage.setItem(storageKey, JSON.stringify(payload));
9298
+ }
9299
+ catch { }
9300
+ }
9301
+ function clearPersistedPaymentIntent() {
9302
+ if (typeof window === "undefined" || !storageKey)
9303
+ return;
9304
+ try {
9305
+ window.localStorage.removeItem(storageKey);
9306
+ }
9307
+ catch { }
9308
+ }
9309
+ // On mount (and when scope changes), restore persisted paymentIntentId
9310
+ useEffect(() => {
9311
+ if (!paymentIntentId) {
9312
+ const restored = loadPersistedPaymentIntent();
9313
+ if (restored) {
9314
+ setPaymentIntentId(restored);
9315
+ }
9316
+ }
9317
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9318
+ }, [storageKey]);
9319
+ // Persist whenever paymentIntentId changes
9320
+ useEffect(() => {
9321
+ if (paymentIntentId) {
9322
+ persistPaymentIntent(paymentIntentId);
9323
+ }
9324
+ }, [paymentIntentId]);
9267
9325
  // Create payment intent when component mounts or when relevant data changes
9268
9326
  useEffect(() => {
9269
9327
  const createPaymentIntent = async () => {
@@ -9368,7 +9426,7 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
9368
9426
  justifyContent: "center",
9369
9427
  padding: "var(--bw-spacing)",
9370
9428
  gap: "8px",
9371
- }, children: [spinner(), jsx("span", { style: {
9429
+ }, children: [spinner$1(), jsx("span", { style: {
9372
9430
  color: "var(--bw-text-muted)",
9373
9431
  fontFamily: "var(--bw-font-family)",
9374
9432
  fontSize: "var(--bw-font-size)",
@@ -9393,10 +9451,16 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
9393
9451
  clientSecret,
9394
9452
  appearance: stripeAppearance || { theme: "stripe" },
9395
9453
  locale: config.locale || "de",
9396
- }, children: jsx(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, clientSecret: clientSecret }) }));
9454
+ }, children: jsx(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: (result) => {
9455
+ // Clear persisted PI data on successful payment
9456
+ clearPersistedPaymentIntent();
9457
+ setPaymentIntentId(null);
9458
+ setClientSecret(null);
9459
+ onSuccess(result);
9460
+ }, onError: onError, systemConfig: systemConfig, clientSecret: clientSecret }) }));
9397
9461
  }
9398
9462
 
9399
- function Sidebar({ isOpen, onClose, title, children, width = "400px" }) {
9463
+ function Sidebar({ isOpen, onClose, title, children, width = "450px" }) {
9400
9464
  const [isVisible, setIsVisible] = useState(false);
9401
9465
  const [isAnimating, setIsAnimating] = useState(false);
9402
9466
  const themedStyles = useStyles();
@@ -9795,7 +9859,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
9795
9859
  marginBottom: "8px",
9796
9860
  fontFamily: "var(--bw-font-family)",
9797
9861
  };
9798
- return (jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Buchung - ${eventDetails.name}`, width: "420px", children: jsxs("div", { className: "booking-widget-container", style: { padding: "var(--bw-spacing)" }, children: [jsxs("div", { style: {
9862
+ return (jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Buchung - ${eventDetails.name}`, children: jsxs("div", { className: "booking-widget-container", style: { padding: "var(--bw-spacing)" }, children: [jsxs("div", { style: {
9799
9863
  backgroundColor: "var(--bw-surface-color)",
9800
9864
  border: `1px solid var(--bw-border-color)`,
9801
9865
  backdropFilter: "blur(4px)",
@@ -10013,7 +10077,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
10013
10077
  fontFamily: "var(--bw-font-family)",
10014
10078
  display: "block",
10015
10079
  marginBottom: "8px",
10016
- }, children: "Kommentar (optional)" }), jsx("textarea", { id: "booking-comment", ...form.register("comment"), placeholder: "Zus\u00E4tzliche Anmerkungen zu Ihrer Buchung...", rows: 3, style: {
10080
+ }, children: "Kommentar (optional)" }), jsx("textarea", { id: "booking-comment", ...form.register("comment"), placeholder: "Zus\u00E4tzliche Anmerkungen zur Buchung...", rows: 3, style: {
10017
10081
  width: "100%",
10018
10082
  padding: "12px",
10019
10083
  border: `1px solid var(--bw-border-color)`,
@@ -10208,6 +10272,258 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
10208
10272
  ` })] }) }));
10209
10273
  }
10210
10274
 
10275
+ /**
10276
+ * Google Ads Conversion Tracking Utility
10277
+ *
10278
+ * This utility handles Google Ads conversion tracking for the booking widget.
10279
+ * It includes consent checking, gtag initialization, and conversion tracking.
10280
+ */
10281
+ /**
10282
+ * Check if Google Consent Mode has granted the necessary permissions for ads tracking
10283
+ * This only checks Google's official consent mode - the proper way to handle Google Ads consent
10284
+ */
10285
+ function checkGoogleConsent() {
10286
+ console.log("[Google Ads Tracking] 🔍 Checking Google consent...");
10287
+ if (typeof window === "undefined") {
10288
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10289
+ return false;
10290
+ }
10291
+ console.log("[Google Ads Tracking] ✅ Window object available");
10292
+ // Check for gtag consent mode
10293
+ if (typeof window.gtag === "function") {
10294
+ console.log("[Google Ads Tracking] ✅ gtag function found");
10295
+ try {
10296
+ // Try to get consent state from Google's consent mode
10297
+ let consentGranted = false;
10298
+ window.gtag("get", (consentState) => {
10299
+ console.log("[Google Ads Tracking] 📋 Consent state received:", consentState);
10300
+ // For Google Ads conversion tracking, we need ad_storage consent
10301
+ consentGranted = consentState.ad_storage === "granted";
10302
+ console.log("[Google Ads Tracking] 🔐 ad_storage consent:", consentState.ad_storage);
10303
+ });
10304
+ console.log("[Google Ads Tracking] 🎯 Final consent result:", consentGranted);
10305
+ return consentGranted;
10306
+ }
10307
+ catch (error) {
10308
+ console.warn("[Google Ads Tracking] ⚠️ Error checking gtag consent:", error);
10309
+ }
10310
+ }
10311
+ else {
10312
+ console.log("[Google Ads Tracking] ❌ gtag function not found");
10313
+ }
10314
+ // If gtag is not available, we assume consent has not been properly configured
10315
+ // The host page should implement Google Consent Mode if they want tracking
10316
+ console.log("[Google Ads Tracking] 🚫 No consent mechanism found, returning false");
10317
+ return false;
10318
+ }
10319
+ /**
10320
+ * Check if gtag is already initialized on the host page
10321
+ */
10322
+ function isGtagInitialized() {
10323
+ console.log("[Google Ads Tracking] 🔍 Checking if gtag is initialized...");
10324
+ if (typeof window === "undefined") {
10325
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10326
+ return false;
10327
+ }
10328
+ console.log("[Google Ads Tracking] ✅ Window object available");
10329
+ // Check if gtag function exists
10330
+ if (typeof window.gtag === "function") {
10331
+ console.log("[Google Ads Tracking] ✅ gtag function exists");
10332
+ return true;
10333
+ }
10334
+ console.log("[Google Ads Tracking] ❌ gtag function not found, checking for scripts...");
10335
+ // Check if Google Analytics or Google Ads scripts are already loaded
10336
+ const scripts = document.querySelectorAll('script[src*="googletagmanager.com"]');
10337
+ console.log("[Google Ads Tracking] 📜 Found", scripts.length, "Google Tag Manager scripts");
10338
+ const isInitialized = scripts.length > 0;
10339
+ console.log("[Google Ads Tracking] 🎯 gtag initialization status:", isInitialized);
10340
+ return isInitialized;
10341
+ }
10342
+ /**
10343
+ * Initialize Google Tag (gtag) if not already present
10344
+ */
10345
+ function initializeGtag(tagId) {
10346
+ console.log("[Google Ads Tracking] 🚀 Starting gtag initialization with tagId:", tagId);
10347
+ return new Promise((resolve, reject) => {
10348
+ if (typeof window === "undefined") {
10349
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10350
+ reject(new Error("Window object not available"));
10351
+ return;
10352
+ }
10353
+ console.log("[Google Ads Tracking] ✅ Window object available");
10354
+ // If gtag is already initialized, just resolve
10355
+ if (isGtagInitialized()) {
10356
+ console.log("[Google Ads Tracking] ⚡ gtag already initialized, skipping setup");
10357
+ resolve();
10358
+ return;
10359
+ }
10360
+ console.log("[Google Ads Tracking] 📦 gtag not found, creating new script element...");
10361
+ try {
10362
+ // Create the gtag script
10363
+ const script = document.createElement("script");
10364
+ script.async = true;
10365
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;
10366
+ console.log("[Google Ads Tracking] 🌐 Script src set to:", script.src);
10367
+ script.onload = () => {
10368
+ console.log("[Google Ads Tracking] 📥 Script loaded successfully, initializing gtag...");
10369
+ // Initialize gtag
10370
+ window.dataLayer = window.dataLayer || [];
10371
+ console.log("[Google Ads Tracking] 📊 dataLayer initialized");
10372
+ window.gtag = (...args) => {
10373
+ window.dataLayer.push(args);
10374
+ };
10375
+ console.log("[Google Ads Tracking] 🔧 gtag function created");
10376
+ // Configure gtag
10377
+ console.log("[Google Ads Tracking] ⚙️ Configuring gtag with privacy settings...");
10378
+ window.gtag("js", new Date());
10379
+ window.gtag("config", tagId, {
10380
+ // Respect consent settings
10381
+ anonymize_ip: true,
10382
+ allow_google_signals: false,
10383
+ allow_ad_personalization_signals: false,
10384
+ });
10385
+ console.log("[Google Ads Tracking] ✅ gtag initialized successfully with tagId:", tagId);
10386
+ resolve();
10387
+ };
10388
+ script.onerror = (error) => {
10389
+ console.error("[Google Ads Tracking] ❌ Failed to load Google Tag script:", error);
10390
+ reject(new Error("Failed to load Google Tag script"));
10391
+ };
10392
+ console.log("[Google Ads Tracking] 📑 Adding script to document head...");
10393
+ // Add script to head
10394
+ document.head.appendChild(script);
10395
+ console.log("[Google Ads Tracking] ✅ Script element appended to head");
10396
+ }
10397
+ catch (error) {
10398
+ console.error("[Google Ads Tracking] ❌ Error during gtag initialization:", error);
10399
+ reject(error);
10400
+ }
10401
+ });
10402
+ }
10403
+ /**
10404
+ * Track a Google Ads conversion
10405
+ */
10406
+ function trackConversion(config) {
10407
+ console.log("[Google Ads Tracking] 🎯 Starting conversion tracking with config:", config);
10408
+ if (typeof window === "undefined") {
10409
+ console.warn("[Google Ads Tracking] ❌ Window object not available");
10410
+ return;
10411
+ }
10412
+ console.log("[Google Ads Tracking] ✅ Window object available");
10413
+ if (!config.tagId || !config.conversionId) {
10414
+ console.warn("[Google Ads Tracking] ❌ Missing tagId or conversionId", {
10415
+ tagId: config.tagId,
10416
+ conversionId: config.conversionId,
10417
+ });
10418
+ return;
10419
+ }
10420
+ console.log("[Google Ads Tracking] ✅ Required config fields present:", {
10421
+ tagId: config.tagId,
10422
+ conversionId: config.conversionId,
10423
+ });
10424
+ if (typeof window.gtag !== "function") {
10425
+ console.warn("[Google Ads Tracking] ❌ gtag function not available");
10426
+ return;
10427
+ }
10428
+ console.log("[Google Ads Tracking] ✅ gtag function is available");
10429
+ try {
10430
+ console.log("[Google Ads Tracking] 🔧 Building conversion data...");
10431
+ const conversionData = {
10432
+ send_to: `${config.tagId}/${config.conversionId}`,
10433
+ };
10434
+ console.log("[Google Ads Tracking] 📋 Base conversion data:", conversionData);
10435
+ // Add optional parameters
10436
+ if (config.conversionValue !== undefined) {
10437
+ conversionData.value = config.conversionValue;
10438
+ console.log("[Google Ads Tracking] 💰 Added conversion value:", config.conversionValue);
10439
+ }
10440
+ if (config.conversionCurrency) {
10441
+ conversionData.currency = config.conversionCurrency;
10442
+ console.log("[Google Ads Tracking] 💱 Added currency:", config.conversionCurrency);
10443
+ }
10444
+ if (config.transactionId) {
10445
+ conversionData.transaction_id = config.transactionId;
10446
+ console.log("[Google Ads Tracking] 🔖 Added transaction ID:", config.transactionId);
10447
+ }
10448
+ console.log("[Google Ads Tracking] 📦 Final conversion data:", conversionData);
10449
+ // Track the conversion
10450
+ console.log("[Google Ads Tracking] 🚀 Sending conversion event to gtag...");
10451
+ window.gtag("event", "conversion", conversionData);
10452
+ console.log("[Google Ads Tracking] ✅ Conversion tracked successfully:", conversionData);
10453
+ }
10454
+ catch (error) {
10455
+ console.error("[Google Ads Tracking] ❌ Error tracking conversion:", error);
10456
+ }
10457
+ }
10458
+ /**
10459
+ * Main function to handle Google Ads conversion tracking
10460
+ * This function checks consent, initializes gtag if needed, and tracks the conversion
10461
+ */
10462
+ async function handleGoogleAdsConversion(config) {
10463
+ console.log("[Google Ads Tracking] 🎬 Starting handleGoogleAdsConversion with config:", config);
10464
+ // Validate config
10465
+ if (!config.tagId || !config.conversionId) {
10466
+ console.log("[Google Ads Tracking] ❌ No tagId or conversionId provided, skipping conversion tracking", { tagId: config.tagId, conversionId: config.conversionId });
10467
+ return;
10468
+ }
10469
+ console.log("[Google Ads Tracking] ✅ Config validation passed");
10470
+ // Check consent first
10471
+ console.log("[Google Ads Tracking] 🔐 Checking consent...");
10472
+ if (!checkGoogleConsent()) {
10473
+ console.log("[Google Ads Tracking] 🚫 Google consent not granted, skipping conversion tracking");
10474
+ return;
10475
+ }
10476
+ console.log("[Google Ads Tracking] ✅ Consent check passed");
10477
+ try {
10478
+ // Initialize gtag if not already present
10479
+ console.log("[Google Ads Tracking] 🔍 Checking if gtag needs initialization...");
10480
+ if (!isGtagInitialized()) {
10481
+ console.log("[Google Ads Tracking] 🚀 Initializing gtag...");
10482
+ await initializeGtag(config.tagId);
10483
+ console.log("[Google Ads Tracking] ✅ gtag initialization completed");
10484
+ }
10485
+ else {
10486
+ console.log("[Google Ads Tracking] ⚡ gtag already initialized, proceeding...");
10487
+ }
10488
+ // Small delay to ensure gtag is ready
10489
+ console.log("[Google Ads Tracking] ⏱️ Adding 100ms delay to ensure gtag is ready...");
10490
+ setTimeout(() => {
10491
+ console.log("[Google Ads Tracking] 🎯 Delay completed, tracking conversion...");
10492
+ trackConversion(config);
10493
+ }, 100);
10494
+ }
10495
+ catch (error) {
10496
+ console.error("[Google Ads Tracking] ❌ Error handling conversion:", error);
10497
+ }
10498
+ }
10499
+ /**
10500
+ * Utility function to get conversion value from booking data
10501
+ */
10502
+ function getConversionValueFromBooking(bookingData) {
10503
+ console.log("[Google Ads Tracking] 💰 Extracting conversion value from booking data:", bookingData);
10504
+ // Try to get the total amount from various possible structures
10505
+ const possiblePaths = [
10506
+ { path: "order.total", value: bookingData?.order?.total },
10507
+ { path: "booking.total", value: bookingData?.booking?.total },
10508
+ { path: "total", value: bookingData?.total },
10509
+ { path: "amount", value: bookingData?.amount },
10510
+ { path: "payment.amount", value: bookingData?.payment?.amount },
10511
+ { path: "stripePaymentIntent.amount", value: bookingData?.stripePaymentIntent?.amount },
10512
+ ];
10513
+ console.log("[Google Ads Tracking] 🔍 Checking possible value paths:", possiblePaths);
10514
+ for (const { path, value } of possiblePaths) {
10515
+ console.log(`[Google Ads Tracking] 📋 Checking path '${path}':`, value);
10516
+ if (typeof value === "number" && value > 0) {
10517
+ // Convert from cents to euros
10518
+ const convertedValue = value / 100;
10519
+ console.log(`[Google Ads Tracking] ✅ Found valid value at '${path}': ${value} cents = ${convertedValue} euros`);
10520
+ return convertedValue;
10521
+ }
10522
+ }
10523
+ console.log("[Google Ads Tracking] ❌ No valid conversion value found in booking data");
10524
+ return undefined;
10525
+ }
10526
+
10211
10527
  const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId, }) => {
10212
10528
  const [bookingData, setBookingData] = useState(null);
10213
10529
  const [eventDetails, setEventDetails] = useState(null);
@@ -10252,6 +10568,27 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
10252
10568
  });
10253
10569
  // Set payment status from Stripe data or order status
10254
10570
  setPaymentStatus(data.stripePaymentIntent?.status || data.order.status);
10571
+ // Trigger Google Ads conversion tracking if payment is successful
10572
+ const finalPaymentStatus = data.stripePaymentIntent?.status || data.order.status;
10573
+ if (finalPaymentStatus === "succeeded" &&
10574
+ config.googleAds?.tagId &&
10575
+ config.googleAds?.conversionId) {
10576
+ // Prepare conversion tracking data
10577
+ const conversionValue = config.googleAds.includeValue !== false
10578
+ ? getConversionValueFromBooking(data)
10579
+ : undefined;
10580
+ const transactionId = data.order.id;
10581
+ // Track the conversion
10582
+ handleGoogleAdsConversion({
10583
+ tagId: config.googleAds.tagId,
10584
+ conversionId: config.googleAds.conversionId,
10585
+ conversionValue,
10586
+ conversionCurrency: config.googleAds.conversionCurrency || "EUR",
10587
+ transactionId,
10588
+ }).catch((error) => {
10589
+ console.warn("[BookingSuccessModal] Google Ads conversion tracking failed:", error);
10590
+ });
10591
+ }
10255
10592
  }
10256
10593
  else {
10257
10594
  onError?.(data.error || "Fehler beim Abrufen der Buchungsdaten");
@@ -10641,44 +10978,42 @@ const getPriceDisplayInfo = (price, yearPrices) => {
10641
10978
  // Allocation Badge Component
10642
10979
  const AllocationBadge = ({ availableSpots, maxParticipants, }) => {
10643
10980
  const badgeInfo = getAllocationBadgeInfo(availableSpots, maxParticipants);
10644
- if (!badgeInfo)
10645
- return null;
10646
10981
  return (jsx("div", { style: {
10647
- position: "absolute",
10648
- bottom: "4px",
10649
- right: "20px",
10650
- backgroundColor: badgeInfo.backgroundColor,
10651
- color: badgeInfo.textColor,
10982
+ backgroundColor: badgeInfo?.backgroundColor || "transparent",
10983
+ color: badgeInfo?.textColor || "transparent",
10652
10984
  fontSize: "11px",
10653
10985
  fontWeight: "bold",
10654
- padding: "3px 8px",
10986
+ padding: "1px 8px",
10987
+ display: "flex",
10988
+ marginRight: "auto",
10989
+ marginTop: "-24px",
10990
+ marginBottom: "5px",
10655
10991
  borderRadius: "var(--bw-border-radius-small)",
10656
10992
  fontFamily: "var(--bw-font-family)",
10657
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
10658
10993
  zIndex: 50,
10659
10994
  whiteSpace: "nowrap",
10660
- }, children: badgeInfo.text }));
10995
+ width: "fit-content",
10996
+ }, children: badgeInfo?.text || " - " }));
10661
10997
  };
10662
10998
  // Price Badge Component (for special prices)
10663
10999
  const SpecialPriceBadge = ({ price, yearPrices }) => {
10664
11000
  const badgeInfo = getPriceBadgeInfo(price, yearPrices);
10665
- if (!badgeInfo)
10666
- return null;
10667
11001
  return (jsx("div", { style: {
10668
- position: "absolute",
10669
- bottom: "28px",
10670
- right: "20px",
10671
- backgroundColor: badgeInfo.backgroundColor,
10672
- color: badgeInfo.textColor,
11002
+ backgroundColor: badgeInfo?.backgroundColor || "transparent",
11003
+ color: badgeInfo?.textColor || "transparent",
10673
11004
  fontSize: "11px",
10674
11005
  fontWeight: "bold",
10675
- padding: "3px 8px",
11006
+ padding: "1px 8px",
11007
+ display: "flex",
11008
+ marginLeft: "auto",
11009
+ marginTop: "-20px",
11010
+ marginBottom: "5px",
10676
11011
  borderRadius: "var(--bw-border-radius-small)",
10677
11012
  fontFamily: "var(--bw-font-family)",
10678
- boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
10679
11013
  zIndex: 50,
10680
11014
  whiteSpace: "nowrap",
10681
- }, children: badgeInfo.text }));
11015
+ width: "fit-content",
11016
+ }, children: badgeInfo?.text || " - " }));
10682
11017
  };
10683
11018
  // Price Display Component (with special price styling)
10684
11019
  const PriceDisplay = ({ price, yearPrices }) => {
@@ -10691,13 +11026,27 @@ const PriceDisplay = ({ price, yearPrices }) => {
10691
11026
  fontSize: "var(--bw-font-size-medium)",
10692
11027
  fontWeight: "600",
10693
11028
  padding: "3px 8px",
10694
- marginTop: "-3px",
10695
11029
  borderRadius: "var(--bw-border-radius-small)",
10696
11030
  fontFamily: "var(--bw-font-family)",
10697
11031
  border: displayInfo ? "none" : "1px solid var(--bw-border-color)",
10698
11032
  boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
10699
11033
  }, children: formatCurrency(price) }));
10700
11034
  };
11035
+ const spinner = (borderColor) => (jsx("div", { style: {
11036
+ width: "auto",
11037
+ height: "auto",
11038
+ fontSize: "16px",
11039
+ color: "var(--bw-text-color)",
11040
+ animation: "spin 1s linear infinite",
11041
+ display: "flex",
11042
+ justifyContent: "center",
11043
+ alignItems: "center",
11044
+ }, children: jsx("div", { style: {
11045
+ width: "16px",
11046
+ height: "16px",
11047
+ border: `3px dotted ${"var(--bw-highlight-color)"}`,
11048
+ borderRadius: "50%",
11049
+ } }) }));
10701
11050
  function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, }) {
10702
11051
  const [selectedEventInstanceId, setSelectedEventInstanceId] = useState(null);
10703
11052
  const [openMonths, setOpenMonths] = useState(new Set());
@@ -10774,7 +11123,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10774
11123
  .bw-accordion-trigger,
10775
11124
  .bw-event-instance-card {
10776
11125
  position: relative;
10777
- overflow: hidden;
10778
11126
  }
10779
11127
 
10780
11128
  .bw-accordion-trigger::before,
@@ -10823,14 +11171,11 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10823
11171
  height: 32px !important;
10824
11172
  font-size: 1rem !important;
10825
11173
  }
10826
- .bw-event-instance-title {
10827
- font-size: 1rem !important;
10828
- }
10829
11174
  .bw-event-instance-price {
10830
11175
  font-size: 1.1rem !important;
10831
11176
  }
10832
11177
  }
10833
- ` }), jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name} Terminauswahl`, width: "500px", children: jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsx("div", { style: {
11178
+ ` }), jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name} Terminauswahl`, children: jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsx("div", { style: {
10834
11179
  display: "flex",
10835
11180
  flexDirection: "column",
10836
11181
  gap: "20px",
@@ -10932,13 +11277,7 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10932
11277
  height: "20px",
10933
11278
  backgroundColor: "var(--bw-border-color)",
10934
11279
  borderRadius: "8px",
10935
- } })] }), eventIdx === 0 && (jsx("div", { style: {
10936
- width: "90%",
10937
- height: "12px",
10938
- backgroundColor: "var(--bw-border-color)",
10939
- borderRadius: "4px",
10940
- marginTop: "8px",
10941
- } }))] }, eventIdx))) })] }, idx))) }) }) })] }));
11280
+ } })] })] }, eventIdx))) })] }, idx))) }) }) })] }));
10942
11281
  }
10943
11282
  // Show empty state only if not loading and no event instances
10944
11283
  if (eventInstances.length === 0) {
@@ -10947,7 +11286,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10947
11286
  .bw-accordion-trigger,
10948
11287
  .bw-event-instance-card {
10949
11288
  position: relative;
10950
- overflow: hidden;
10951
11289
  }
10952
11290
 
10953
11291
  .bw-accordion-trigger::before,
@@ -10996,9 +11334,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
10996
11334
  height: 32px !important;
10997
11335
  font-size: 1rem !important;
10998
11336
  }
10999
- .bw-event-instance-title {
11000
- font-size: 1rem !important;
11001
- }
11002
11337
  .bw-event-instance-price {
11003
11338
  font-size: 1.1rem !important;
11004
11339
  }
@@ -11029,7 +11364,6 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11029
11364
  .bw-accordion-trigger,
11030
11365
  .bw-event-instance-card {
11031
11366
  position: relative;
11032
- overflow: hidden;
11033
11367
  }
11034
11368
 
11035
11369
  .bw-accordion-trigger::before,
@@ -11078,14 +11412,11 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11078
11412
  height: 32px !important;
11079
11413
  font-size: 1rem !important;
11080
11414
  }
11081
- .bw-event-instance-title {
11082
- font-size: 1rem !important;
11083
- }
11084
11415
  .bw-event-instance-price {
11085
11416
  font-size: 1.1rem !important;
11086
11417
  }
11087
11418
  }
11088
- ` }), jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Terminauswahl - ${selectedEventType?.name || "Event"}`, width: "500px", children: jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsx("div", { style: {
11419
+ ` }), jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Terminauswahl - ${selectedEventType?.name || "Event"}`, children: jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsx("div", { style: {
11089
11420
  display: "flex",
11090
11421
  flexDirection: "column",
11091
11422
  gap: "20px",
@@ -11160,7 +11491,7 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11160
11491
  color: "var(--bw-highlight-color-muted, rgba(59, 130, 246, 0.8))",
11161
11492
  animation: "spin 1s linear infinite",
11162
11493
  fontSize: "32px",
11163
- }, children: "\u27F3" }) })), jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices }), jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants }), jsxs("div", { style: {
11494
+ }, children: spinner() }) })), jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices }), jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants }), jsxs("div", { style: {
11164
11495
  display: "flex",
11165
11496
  justifyContent: "space-between",
11166
11497
  width: "100%",
@@ -11189,12 +11520,21 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11189
11520
  alignItems: "start",
11190
11521
  justifyContent: "start",
11191
11522
  lineHeight: "1.2",
11192
- }, children: [jsx("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.startTime) }), jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime) })] })] }), jsx("div", { className: "bw-event-instance-price", style: {
11523
+ }, children: [jsxs("div", { children: [jsx("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.startTime) }), formatWeekday(event.startTime) !==
11524
+ formatWeekday(event.endTime) && (jsxs(Fragment, { children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsx("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.endTime) })] }))] }), jsx("div", { children: formatWeekday(event.startTime) ===
11525
+ formatWeekday(event.endTime) ? (jsxs(Fragment, { children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime) }), jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), jsx("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime) })] })) : (jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime), " Uhr"] })) })] }), jsxs("span", { style: {
11526
+ fontSize: "12px",
11527
+ fontWeight: 400,
11528
+ color: "var(--bw-text-muted)",
11529
+ marginLeft: "6px",
11530
+ background: "var(--bw-background-muted)",
11531
+ whiteSpace: "nowrap",
11532
+ }, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), jsx("div", { className: "bw-event-instance-price", style: {
11193
11533
  textAlign: "right",
11194
11534
  display: "flex",
11195
11535
  flexDirection: "column",
11196
11536
  alignItems: "end",
11197
- }, children: jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), jsxs("h4", { className: "bw-event-instance-title", style: {
11537
+ }, children: jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), event.name !== selectedEventType?.name && (jsx("h4", { className: "bw-event-instance-title", style: {
11198
11538
  fontSize: "var(--bw-font-size)",
11199
11539
  fontWeight: "600",
11200
11540
  color: "var(--bw-text-color)",
@@ -11203,26 +11543,8 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
11203
11543
  display: "flex",
11204
11544
  alignItems: "center",
11205
11545
  gap: "8px",
11206
- }, children: [event.name, jsxs("span", { style: {
11207
- fontSize: "12px",
11208
- fontWeight: 400,
11209
- color: "var(--bw-text-muted)",
11210
- marginLeft: "6px",
11211
- background: "var(--bw-background-muted)",
11212
- borderRadius: "8px",
11213
- padding: "2px 8px",
11214
- whiteSpace: "nowrap",
11215
- }, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), event.notes && (jsx("p", { style: {
11216
- fontSize: "12px",
11217
- color: "var(--bw-text-muted)",
11218
- marginTop: "8px",
11219
- display: "-webkit-box",
11220
- WebkitBoxOrient: "vertical",
11221
- WebkitLineClamp: 2,
11222
- overflow: "hidden",
11223
- margin: "0",
11224
- lineHeight: "1.3",
11225
- }, children: event.notes }))] }, event.id));
11546
+ maxWidth: "230px",
11547
+ }, children: event.name }))] }, event.id));
11226
11548
  }) }) }, month));
11227
11549
  }) }) }) })] }));
11228
11550
  }
@@ -11863,7 +12185,6 @@ function UniversalBookingWidget({ config: baseConfig }) {
11863
12185
  body: JSON.stringify(requestBody),
11864
12186
  });
11865
12187
  const data = await response.json();
11866
- console.log("Event details response:", { status: response.status, data }); // Debug log
11867
12188
  if (response.ok) {
11868
12189
  setEventDetails(data.eventDetails);
11869
12190
  // Store system configuration for PaymentForm