@bigz-app/booking-widget 0.3.0 → 0.3.2

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/README.md CHANGED
@@ -10,7 +10,7 @@ pnpm install @bigz-app/booking-widget
10
10
 
11
11
  ## Quick Start
12
12
 
13
- Import the widget and provide the necessary configuration. The widget handles the entire booking flow, from event selection to payment.
13
+ Import the widget and provide the necessary configuration. The widget handles the entire booking flow, from event selection to payment, with optional Google Ads conversion tracking.
14
14
 
15
15
  ```tsx
16
16
  import { UniversalBookingWidget } from '@bigz-app/booking-widget';
@@ -100,4 +100,30 @@ The widget can be configured dynamically using URL query parameters. These will
100
100
  | `instanceId` | `eventInstanceId`| `?instanceId=123` |
101
101
  | `categoryId` | `categoryId` | `?categoryId=1` |
102
102
  | `eventTypeId` | `eventTypeId` | `?eventTypeId=5` |
103
- | `eventTypeIds` | `eventTypeIds` | `?eventTypeIds=1,3,5` |
103
+ | `eventTypeIds` | `eventTypeIds` | `?eventTypeIds=1,3,5` |
104
+
105
+ ## Google Ads Conversion Tracking
106
+
107
+ The widget supports automatic Google Ads conversion tracking when payments are successful. This feature requires Google Consent Mode to be implemented on your website.
108
+
109
+ ```tsx
110
+ const config = {
111
+ // ... your existing config
112
+ googleAds: {
113
+ tagId: 'AW-XXXXXXX', // Your Google Ads Tag ID (required)
114
+ conversionId: 'booking_conversion', // Your conversion label (required)
115
+ conversionCurrency: 'EUR', // Optional, defaults to 'EUR'
116
+ includeValue: true // Optional, defaults to true
117
+ }
118
+ };
119
+ ```
120
+
121
+ **Key Features:**
122
+ - ✅ Uses Google Consent Mode for proper consent management
123
+ - ✅ Prevents duplicate gtag initialization
124
+ - ✅ Includes booking value and transaction ID
125
+ - ✅ Automatic error handling and fallbacks
126
+
127
+ **Required:** You must implement [Google Consent Mode](https://developers.google.com/tag-platform/security/consent) on your website for tracking to work.
128
+
129
+ For detailed setup instructions, see [Google Ads Tracking Documentation](./docs/google-ads-tracking.md).
@@ -295,9 +295,9 @@
295
295
  const themes = {
296
296
  // --- Light Themes ---
297
297
  "light-fresh": {
298
- highlight: "oklch(0.65 0.2 190)", // accent-strong
299
- background: "oklch(0.99 0.005 220)", // neutral-strong (background)
300
- surface: "oklch(1 0 0)", // card (pure white)
298
+ highlight: "#00a8a1", // accent-strong
299
+ background: "#f8fdfe", // neutral-strong (background)
300
+ surface: "#ffffff", // card (pure white)
301
301
  text: "#0e7490", // Turquoise 800
302
302
  border: "#bae6fd", // Blue 200
303
303
  success: "#38bdf8", // Blue 400
@@ -9349,6 +9349,64 @@
9349
9349
  const [paymentIntentId, setPaymentIntentId] = d$1(null);
9350
9350
  const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = d$1(false);
9351
9351
  const [paymentError, setPaymentError] = d$1(null);
9352
+ // LocalStorage persistence (scoped by organization + event) for paymentIntentId only
9353
+ const storageKey = typeof window !== "undefined"
9354
+ ? `bw_pi_${config?.organizationId}_${config?.eventInstanceId || eventDetails?.id}`
9355
+ : "";
9356
+ const PAYMENT_INTENT_TTL = 24 * 60 * 60 * 1000; // 24 hours
9357
+ function loadPersistedPaymentIntent() {
9358
+ if (typeof window === "undefined" || !storageKey)
9359
+ return null;
9360
+ try {
9361
+ const raw = window.localStorage.getItem(storageKey);
9362
+ if (!raw)
9363
+ return null;
9364
+ const parsed = JSON.parse(raw);
9365
+ if (!parsed?.id || !parsed?.ts)
9366
+ return null;
9367
+ if (Date.now() - parsed.ts > PAYMENT_INTENT_TTL) {
9368
+ window.localStorage.removeItem(storageKey);
9369
+ return null;
9370
+ }
9371
+ return parsed.id;
9372
+ }
9373
+ catch {
9374
+ return null;
9375
+ }
9376
+ }
9377
+ function persistPaymentIntent(id) {
9378
+ if (typeof window === "undefined" || !storageKey || !id)
9379
+ return;
9380
+ try {
9381
+ const payload = { id, ts: Date.now() };
9382
+ window.localStorage.setItem(storageKey, JSON.stringify(payload));
9383
+ }
9384
+ catch { }
9385
+ }
9386
+ function clearPersistedPaymentIntent() {
9387
+ if (typeof window === "undefined" || !storageKey)
9388
+ return;
9389
+ try {
9390
+ window.localStorage.removeItem(storageKey);
9391
+ }
9392
+ catch { }
9393
+ }
9394
+ // On mount (and when scope changes), restore persisted paymentIntentId
9395
+ y$1(() => {
9396
+ if (!paymentIntentId) {
9397
+ const restored = loadPersistedPaymentIntent();
9398
+ if (restored) {
9399
+ setPaymentIntentId(restored);
9400
+ }
9401
+ }
9402
+ // eslint-disable-next-line react-hooks/exhaustive-deps
9403
+ }, [storageKey]);
9404
+ // Persist whenever paymentIntentId changes
9405
+ y$1(() => {
9406
+ if (paymentIntentId) {
9407
+ persistPaymentIntent(paymentIntentId);
9408
+ }
9409
+ }, [paymentIntentId]);
9352
9410
  // Create payment intent when component mounts or when relevant data changes
9353
9411
  y$1(() => {
9354
9412
  const createPaymentIntent = async () => {
@@ -9478,7 +9536,13 @@
9478
9536
  clientSecret,
9479
9537
  appearance: stripeAppearance || { theme: "stripe" },
9480
9538
  locale: config.locale || "de",
9481
- }, children: u$2(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, clientSecret: clientSecret }) }));
9539
+ }, children: u$2(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: (result) => {
9540
+ // Clear persisted PI data on successful payment
9541
+ clearPersistedPaymentIntent();
9542
+ setPaymentIntentId(null);
9543
+ setClientSecret(null);
9544
+ onSuccess(result);
9545
+ }, onError: onError, systemConfig: systemConfig, clientSecret: clientSecret }) }));
9482
9546
  }
9483
9547
 
9484
9548
  function Sidebar({ isOpen, onClose, title, children, width = "450px" }) {
@@ -10293,6 +10357,317 @@
10293
10357
  ` })] }) }));
10294
10358
  }
10295
10359
 
10360
+ /**
10361
+ * Google Ads Conversion Tracking Utility
10362
+ *
10363
+ * This utility handles Google Ads conversion tracking for the booking widget.
10364
+ * It includes consent checking, gtag initialization, and conversion tracking.
10365
+ */
10366
+ /**
10367
+ * Check if Google Consent Mode has granted the necessary permissions for ads tracking
10368
+ * This checks multiple sources of consent state in order of preference
10369
+ */
10370
+ function checkGoogleConsent() {
10371
+ console.log("[Google Ads Tracking] 🔍 Checking Google consent...");
10372
+ if (typeof window === "undefined") {
10373
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10374
+ return false;
10375
+ }
10376
+ console.log("[Google Ads Tracking] ✅ Window object available");
10377
+ // Method 1: Check dataLayer for consent_update events (most reliable)
10378
+ if (window.dataLayer && Array.isArray(window.dataLayer)) {
10379
+ console.log("[Google Ads Tracking] 🔍 Checking dataLayer for consent events...");
10380
+ const dataLayer = window.dataLayer;
10381
+ // Debug: Show the entire dataLayer contents
10382
+ console.log("[Google Ads Tracking] 🗂️ Complete dataLayer contents:", JSON.stringify(dataLayer, null, 2));
10383
+ console.log("[Google Ads Tracking] 📊 dataLayer length:", dataLayer.length);
10384
+ // Look for the most recent consent update in dataLayer
10385
+ let latestConsentState = null;
10386
+ const foundEvents = [];
10387
+ // Search backwards through dataLayer for most recent consent state
10388
+ for (let i = dataLayer.length - 1; i >= 0; i--) {
10389
+ const event = dataLayer[i];
10390
+ console.log(`[Google Ads Tracking] 🔍 Checking dataLayer[${i}]:`, event);
10391
+ if (event && typeof event === "object") {
10392
+ // Check for various consent event patterns
10393
+ if (event.event === "consent_update" || event.event === "default_consent") {
10394
+ console.log("[Google Ads Tracking] 📋 Found consent event:", event);
10395
+ foundEvents.push(event);
10396
+ if (event.consent_mode) {
10397
+ latestConsentState = event.consent_mode;
10398
+ break;
10399
+ }
10400
+ }
10401
+ // Also check for direct consent_mode properties
10402
+ if (event.consent_mode) {
10403
+ console.log("[Google Ads Tracking] 📋 Found consent_mode property:", event);
10404
+ foundEvents.push(event);
10405
+ latestConsentState = event.consent_mode;
10406
+ break;
10407
+ }
10408
+ }
10409
+ }
10410
+ console.log("[Google Ads Tracking] 📋 All found consent events:", foundEvents);
10411
+ if (latestConsentState) {
10412
+ const adStorageGranted = latestConsentState.ad_storage === "granted";
10413
+ console.log("[Google Ads Tracking] 🔐 ad_storage from dataLayer:", latestConsentState.ad_storage);
10414
+ console.log("[Google Ads Tracking] 🎯 Consent result from dataLayer:", adStorageGranted);
10415
+ return adStorageGranted;
10416
+ }
10417
+ console.log("[Google Ads Tracking] ❌ No consent events found in dataLayer");
10418
+ }
10419
+ console.log("[Google Ads Tracking] ❌ dataLayer not found or not an array");
10420
+ // Method 2: Check for cookie-based consent (fallback for host implementation)
10421
+ console.log("[Google Ads Tracking] 🔍 Checking cookie-based consent...");
10422
+ try {
10423
+ // Debug: Show all cookies
10424
+ console.log("[Google Ads Tracking] 🍪 All cookies:", document.cookie);
10425
+ const allCookies = document.cookie.split(";").map((cookie) => cookie.trim());
10426
+ console.log("[Google Ads Tracking] 🍪 Parsed cookies:", allCookies);
10427
+ // Check for the host page's conversion tracking consent cookie
10428
+ const conversionTrackingCookie = document.cookie
10429
+ .split(";")
10430
+ .find((cookie) => cookie.trim().startsWith("conversionTrackingConsent="));
10431
+ if (conversionTrackingCookie) {
10432
+ const cookieValue = conversionTrackingCookie.split("=")[1];
10433
+ const isGranted = cookieValue === "true";
10434
+ console.log("[Google Ads Tracking] 🍪 Found conversionTrackingConsent cookie:", cookieValue);
10435
+ console.log("[Google Ads Tracking] 🎯 Consent result from cookie:", isGranted);
10436
+ return isGranted;
10437
+ }
10438
+ console.log("[Google Ads Tracking] ❌ conversionTrackingConsent cookie not found");
10439
+ // Also check for other potential cookie names
10440
+ const alternativeCookieNames = ["analyticsConsent", "ads_consent", "ad_storage"];
10441
+ for (const cookieName of alternativeCookieNames) {
10442
+ const alternativeCookie = allCookies.find((cookie) => cookie.startsWith(`${cookieName}=`));
10443
+ if (alternativeCookie) {
10444
+ console.log(`[Google Ads Tracking] 🍪 Found alternative cookie ${cookieName}:`, alternativeCookie);
10445
+ }
10446
+ }
10447
+ }
10448
+ catch (error) {
10449
+ console.warn("[Google Ads Tracking] ⚠️ Error checking cookies:", error);
10450
+ }
10451
+ // Method 3: Check if gtag exists but no consent state found
10452
+ if (typeof window.gtag === "function") {
10453
+ console.log("[Google Ads Tracking] ⚠️ gtag function exists but no consent state found");
10454
+ console.log("[Google Ads Tracking] 💡 This might indicate consent hasn't been set yet");
10455
+ }
10456
+ else {
10457
+ console.log("[Google Ads Tracking] ❌ gtag function not found");
10458
+ }
10459
+ // If no consent mechanism is found, assume consent is not granted
10460
+ console.log("[Google Ads Tracking] 🚫 No valid consent state found, returning false");
10461
+ return false;
10462
+ }
10463
+ /**
10464
+ * Check if gtag is already initialized on the host page
10465
+ */
10466
+ function isGtagInitialized() {
10467
+ console.log("[Google Ads Tracking] 🔍 Checking if gtag is initialized...");
10468
+ if (typeof window === "undefined") {
10469
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10470
+ return false;
10471
+ }
10472
+ console.log("[Google Ads Tracking] ✅ Window object available");
10473
+ // Check if gtag function exists
10474
+ if (typeof window.gtag === "function") {
10475
+ console.log("[Google Ads Tracking] ✅ gtag function exists");
10476
+ return true;
10477
+ }
10478
+ console.log("[Google Ads Tracking] ❌ gtag function not found, checking for scripts...");
10479
+ // Check if Google Analytics or Google Ads scripts are already loaded
10480
+ const scripts = document.querySelectorAll('script[src*="googletagmanager.com"]');
10481
+ console.log("[Google Ads Tracking] 📜 Found", scripts.length, "Google Tag Manager scripts");
10482
+ const isInitialized = scripts.length > 0;
10483
+ console.log("[Google Ads Tracking] 🎯 gtag initialization status:", isInitialized);
10484
+ return isInitialized;
10485
+ }
10486
+ /**
10487
+ * Initialize Google Tag (gtag) if not already present
10488
+ */
10489
+ function initializeGtag(tagId) {
10490
+ console.log("[Google Ads Tracking] 🚀 Starting gtag initialization with tagId:", tagId);
10491
+ return new Promise((resolve, reject) => {
10492
+ if (typeof window === "undefined") {
10493
+ console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
10494
+ reject(new Error("Window object not available"));
10495
+ return;
10496
+ }
10497
+ console.log("[Google Ads Tracking] ✅ Window object available");
10498
+ // If gtag is already initialized, just resolve
10499
+ if (isGtagInitialized()) {
10500
+ console.log("[Google Ads Tracking] ⚡ gtag already initialized, skipping setup");
10501
+ resolve();
10502
+ return;
10503
+ }
10504
+ console.log("[Google Ads Tracking] 📦 gtag not found, creating new script element...");
10505
+ try {
10506
+ // Create the gtag script
10507
+ const script = document.createElement("script");
10508
+ script.async = true;
10509
+ script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;
10510
+ console.log("[Google Ads Tracking] 🌐 Script src set to:", script.src);
10511
+ script.onload = () => {
10512
+ console.log("[Google Ads Tracking] 📥 Script loaded successfully, initializing gtag...");
10513
+ // Initialize gtag
10514
+ window.dataLayer = window.dataLayer || [];
10515
+ console.log("[Google Ads Tracking] 📊 dataLayer initialized");
10516
+ window.gtag = (...args) => {
10517
+ window.dataLayer.push(args);
10518
+ };
10519
+ console.log("[Google Ads Tracking] 🔧 gtag function created");
10520
+ // Configure gtag
10521
+ console.log("[Google Ads Tracking] ⚙️ Configuring gtag with privacy settings...");
10522
+ window.gtag("js", new Date());
10523
+ window.gtag("config", tagId, {
10524
+ // Respect consent settings
10525
+ anonymize_ip: true,
10526
+ allow_google_signals: false,
10527
+ allow_ad_personalization_signals: false,
10528
+ });
10529
+ console.log("[Google Ads Tracking] ✅ gtag initialized successfully with tagId:", tagId);
10530
+ resolve();
10531
+ };
10532
+ script.onerror = (error) => {
10533
+ console.error("[Google Ads Tracking] ❌ Failed to load Google Tag script:", error);
10534
+ reject(new Error("Failed to load Google Tag script"));
10535
+ };
10536
+ console.log("[Google Ads Tracking] 📑 Adding script to document head...");
10537
+ // Add script to head
10538
+ document.head.appendChild(script);
10539
+ console.log("[Google Ads Tracking] ✅ Script element appended to head");
10540
+ }
10541
+ catch (error) {
10542
+ console.error("[Google Ads Tracking] ❌ Error during gtag initialization:", error);
10543
+ reject(error);
10544
+ }
10545
+ });
10546
+ }
10547
+ /**
10548
+ * Track a Google Ads conversion
10549
+ */
10550
+ function trackConversion(config) {
10551
+ console.log("[Google Ads Tracking] 🎯 Starting conversion tracking with config:", config);
10552
+ if (typeof window === "undefined") {
10553
+ console.warn("[Google Ads Tracking] ❌ Window object not available");
10554
+ return;
10555
+ }
10556
+ console.log("[Google Ads Tracking] ✅ Window object available");
10557
+ if (!config.tagId || !config.conversionId) {
10558
+ console.warn("[Google Ads Tracking] ❌ Missing tagId or conversionId", {
10559
+ tagId: config.tagId,
10560
+ conversionId: config.conversionId,
10561
+ });
10562
+ return;
10563
+ }
10564
+ console.log("[Google Ads Tracking] ✅ Required config fields present:", {
10565
+ tagId: config.tagId,
10566
+ conversionId: config.conversionId,
10567
+ });
10568
+ if (typeof window.gtag !== "function") {
10569
+ console.warn("[Google Ads Tracking] ❌ gtag function not available");
10570
+ return;
10571
+ }
10572
+ console.log("[Google Ads Tracking] ✅ gtag function is available");
10573
+ try {
10574
+ console.log("[Google Ads Tracking] 🔧 Building conversion data...");
10575
+ const conversionData = {
10576
+ send_to: `${config.tagId}/${config.conversionId}`,
10577
+ };
10578
+ console.log("[Google Ads Tracking] 📋 Base conversion data:", conversionData);
10579
+ // Add optional parameters
10580
+ if (config.conversionValue !== undefined) {
10581
+ conversionData.value = config.conversionValue;
10582
+ console.log("[Google Ads Tracking] 💰 Added conversion value:", config.conversionValue);
10583
+ }
10584
+ if (config.conversionCurrency) {
10585
+ conversionData.currency = config.conversionCurrency;
10586
+ console.log("[Google Ads Tracking] 💱 Added currency:", config.conversionCurrency);
10587
+ }
10588
+ if (config.transactionId) {
10589
+ conversionData.transaction_id = config.transactionId;
10590
+ console.log("[Google Ads Tracking] 🔖 Added transaction ID:", config.transactionId);
10591
+ }
10592
+ console.log("[Google Ads Tracking] 📦 Final conversion data:", conversionData);
10593
+ // Track the conversion
10594
+ console.log("[Google Ads Tracking] 🚀 Sending conversion event to gtag...");
10595
+ window.gtag("event", "conversion", conversionData);
10596
+ console.log("[Google Ads Tracking] ✅ Conversion tracked successfully:", conversionData);
10597
+ }
10598
+ catch (error) {
10599
+ console.error("[Google Ads Tracking] ❌ Error tracking conversion:", error);
10600
+ }
10601
+ }
10602
+ /**
10603
+ * Main function to handle Google Ads conversion tracking
10604
+ * This function checks consent, initializes gtag if needed, and tracks the conversion
10605
+ */
10606
+ async function handleGoogleAdsConversion(config) {
10607
+ console.log("[Google Ads Tracking] 🎬 Starting handleGoogleAdsConversion with config:", config);
10608
+ // Validate config
10609
+ if (!config.tagId || !config.conversionId) {
10610
+ console.log("[Google Ads Tracking] ❌ No tagId or conversionId provided, skipping conversion tracking", { tagId: config.tagId, conversionId: config.conversionId });
10611
+ return;
10612
+ }
10613
+ console.log("[Google Ads Tracking] ✅ Config validation passed");
10614
+ // Check consent first
10615
+ console.log("[Google Ads Tracking] 🔐 Checking consent...");
10616
+ if (!checkGoogleConsent()) {
10617
+ console.log("[Google Ads Tracking] 🚫 Google consent not granted, skipping conversion tracking");
10618
+ return;
10619
+ }
10620
+ console.log("[Google Ads Tracking] ✅ Consent check passed");
10621
+ try {
10622
+ // Initialize gtag if not already present
10623
+ console.log("[Google Ads Tracking] 🔍 Checking if gtag needs initialization...");
10624
+ if (!isGtagInitialized()) {
10625
+ console.log("[Google Ads Tracking] 🚀 Initializing gtag...");
10626
+ await initializeGtag(config.tagId);
10627
+ console.log("[Google Ads Tracking] ✅ gtag initialization completed");
10628
+ }
10629
+ else {
10630
+ console.log("[Google Ads Tracking] ⚡ gtag already initialized, proceeding...");
10631
+ }
10632
+ // Small delay to ensure gtag is ready
10633
+ console.log("[Google Ads Tracking] ⏱️ Adding 100ms delay to ensure gtag is ready...");
10634
+ setTimeout(() => {
10635
+ console.log("[Google Ads Tracking] 🎯 Delay completed, tracking conversion...");
10636
+ trackConversion(config);
10637
+ }, 100);
10638
+ }
10639
+ catch (error) {
10640
+ console.error("[Google Ads Tracking] ❌ Error handling conversion:", error);
10641
+ }
10642
+ }
10643
+ /**
10644
+ * Utility function to get conversion value from booking data
10645
+ */
10646
+ function getConversionValueFromBooking(bookingData) {
10647
+ console.log("[Google Ads Tracking] 💰 Extracting conversion value from booking data:", bookingData);
10648
+ // Try to get the total amount from various possible structures
10649
+ const possiblePaths = [
10650
+ { path: "order.total", value: bookingData?.order?.total },
10651
+ { path: "booking.total", value: bookingData?.booking?.total },
10652
+ { path: "total", value: bookingData?.total },
10653
+ { path: "amount", value: bookingData?.amount },
10654
+ { path: "payment.amount", value: bookingData?.payment?.amount },
10655
+ { path: "stripePaymentIntent.amount", value: bookingData?.stripePaymentIntent?.amount },
10656
+ ];
10657
+ console.log("[Google Ads Tracking] 🔍 Checking possible value paths:", possiblePaths);
10658
+ for (const { path, value } of possiblePaths) {
10659
+ console.log(`[Google Ads Tracking] 📋 Checking path '${path}':`, value);
10660
+ if (typeof value === "number" && value > 0) {
10661
+ // Convert from cents to euros
10662
+ const convertedValue = value / 100;
10663
+ console.log(`[Google Ads Tracking] ✅ Found valid value at '${path}': ${value} cents = ${convertedValue} euros`);
10664
+ return convertedValue;
10665
+ }
10666
+ }
10667
+ console.log("[Google Ads Tracking] ❌ No valid conversion value found in booking data");
10668
+ return undefined;
10669
+ }
10670
+
10296
10671
  const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId, }) => {
10297
10672
  const [bookingData, setBookingData] = d$1(null);
10298
10673
  const [eventDetails, setEventDetails] = d$1(null);
@@ -10337,6 +10712,27 @@
10337
10712
  });
10338
10713
  // Set payment status from Stripe data or order status
10339
10714
  setPaymentStatus(data.stripePaymentIntent?.status || data.order.status);
10715
+ // Trigger Google Ads conversion tracking if payment is successful
10716
+ const finalPaymentStatus = data.stripePaymentIntent?.status || data.order.status;
10717
+ if (finalPaymentStatus === "succeeded" &&
10718
+ config.googleAds?.tagId &&
10719
+ config.googleAds?.conversionId) {
10720
+ // Prepare conversion tracking data
10721
+ const conversionValue = config.googleAds.includeValue !== false
10722
+ ? getConversionValueFromBooking(data)
10723
+ : undefined;
10724
+ const transactionId = data.order.id;
10725
+ // Track the conversion
10726
+ handleGoogleAdsConversion({
10727
+ tagId: config.googleAds.tagId,
10728
+ conversionId: config.googleAds.conversionId,
10729
+ conversionValue,
10730
+ conversionCurrency: config.googleAds.conversionCurrency || "EUR",
10731
+ transactionId,
10732
+ }).catch((error) => {
10733
+ console.warn("[BookingSuccessModal] Google Ads conversion tracking failed:", error);
10734
+ });
10735
+ }
10340
10736
  }
10341
10737
  else {
10342
10738
  onError?.(data.error || "Fehler beim Abrufen der Buchungsdaten");
@@ -11933,7 +12329,6 @@
11933
12329
  body: JSON.stringify(requestBody),
11934
12330
  });
11935
12331
  const data = await response.json();
11936
- console.log("Event details response:", { status: response.status, data }); // Debug log
11937
12332
  if (response.ok) {
11938
12333
  setEventDetails(data.eventDetails);
11939
12334
  // Store system configuration for PaymentForm