@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/README.md +28 -2
- package/dist/booking-widget.js +397 -76
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/BookingForm.d.ts.map +1 -1
- package/dist/components/BookingSuccessModal.d.ts.map +1 -1
- package/dist/components/EventInstanceSelection.d.ts +0 -1
- package/dist/components/EventInstanceSelection.d.ts.map +1 -1
- package/dist/components/PaymentForm.d.ts.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts +21 -0
- package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
- package/dist/index.cjs +397 -76
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +397 -76
- package/dist/index.esm.js.map +1 -1
- package/dist/utils/google-ads-tracking.d.ts +65 -0
- package/dist/utils/google-ads-tracking.d.ts.map +1 -0
- package/package.json +1 -1
package/dist/booking-widget.js
CHANGED
|
@@ -295,9 +295,9 @@
|
|
|
295
295
|
const themes = {
|
|
296
296
|
// --- Light Themes ---
|
|
297
297
|
"light-fresh": {
|
|
298
|
-
highlight: "
|
|
299
|
-
background: "
|
|
300
|
-
surface: "
|
|
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
|
|
@@ -9197,7 +9197,7 @@
|
|
|
9197
9197
|
|
|
9198
9198
|
var reactStripe_umdExports = reactStripe_umd.exports;
|
|
9199
9199
|
|
|
9200
|
-
const spinner = (borderColor) => (u$2("div", { style: {
|
|
9200
|
+
const spinner$1 = (borderColor) => (u$2("div", { style: {
|
|
9201
9201
|
width: "auto",
|
|
9202
9202
|
height: "auto",
|
|
9203
9203
|
fontSize: "16px",
|
|
@@ -9333,7 +9333,7 @@
|
|
|
9333
9333
|
if (!isLoading) {
|
|
9334
9334
|
e.currentTarget.style.backgroundColor = "var(--bw-highlight-color)";
|
|
9335
9335
|
}
|
|
9336
|
-
}, children: isLoading ? (u$2(k$3, { children: [spinner("var(--bw-surface-color)"), " Verarbeite Zahlung..."] })) : (u$2(k$3, { children: [" ", totalAmount <
|
|
9336
|
+
}, children: isLoading ? (u$2(k$3, { children: [spinner$1("var(--bw-surface-color)"), " Verarbeite Zahlung..."] })) : (u$2(k$3, { children: [" ", totalAmount <
|
|
9337
9337
|
eventDetails.price * formData.participants.filter((p) => p.name.trim()).length
|
|
9338
9338
|
? "Anzahlen & buchen"
|
|
9339
9339
|
: "Jetzt buchen"] })) }), u$2("style", { children: `
|
|
@@ -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 () => {
|
|
@@ -9453,7 +9511,7 @@
|
|
|
9453
9511
|
justifyContent: "center",
|
|
9454
9512
|
padding: "var(--bw-spacing)",
|
|
9455
9513
|
gap: "8px",
|
|
9456
|
-
}, children: [spinner(), u$2("span", { style: {
|
|
9514
|
+
}, children: [spinner$1(), u$2("span", { style: {
|
|
9457
9515
|
color: "var(--bw-text-muted)",
|
|
9458
9516
|
fontFamily: "var(--bw-font-family)",
|
|
9459
9517
|
fontSize: "var(--bw-font-size)",
|
|
@@ -9478,10 +9536,16 @@
|
|
|
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:
|
|
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
|
-
function Sidebar({ isOpen, onClose, title, children, width = "
|
|
9548
|
+
function Sidebar({ isOpen, onClose, title, children, width = "450px" }) {
|
|
9485
9549
|
const [isVisible, setIsVisible] = d$1(false);
|
|
9486
9550
|
const [isAnimating, setIsAnimating] = d$1(false);
|
|
9487
9551
|
const themedStyles = useStyles();
|
|
@@ -9880,7 +9944,7 @@
|
|
|
9880
9944
|
marginBottom: "8px",
|
|
9881
9945
|
fontFamily: "var(--bw-font-family)",
|
|
9882
9946
|
};
|
|
9883
|
-
return (u$2(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Buchung - ${eventDetails.name}`,
|
|
9947
|
+
return (u$2(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Buchung - ${eventDetails.name}`, children: u$2("div", { className: "booking-widget-container", style: { padding: "var(--bw-spacing)" }, children: [u$2("div", { style: {
|
|
9884
9948
|
backgroundColor: "var(--bw-surface-color)",
|
|
9885
9949
|
border: `1px solid var(--bw-border-color)`,
|
|
9886
9950
|
backdropFilter: "blur(4px)",
|
|
@@ -10098,7 +10162,7 @@
|
|
|
10098
10162
|
fontFamily: "var(--bw-font-family)",
|
|
10099
10163
|
display: "block",
|
|
10100
10164
|
marginBottom: "8px",
|
|
10101
|
-
}, children: "Kommentar (optional)" }), u$2("textarea", { id: "booking-comment", ...form.register("comment"), placeholder: "Zus\u00E4tzliche Anmerkungen
|
|
10165
|
+
}, children: "Kommentar (optional)" }), u$2("textarea", { id: "booking-comment", ...form.register("comment"), placeholder: "Zus\u00E4tzliche Anmerkungen zur Buchung...", rows: 3, style: {
|
|
10102
10166
|
width: "100%",
|
|
10103
10167
|
padding: "12px",
|
|
10104
10168
|
border: `1px solid var(--bw-border-color)`,
|
|
@@ -10293,6 +10357,258 @@
|
|
|
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 only checks Google's official consent mode - the proper way to handle Google Ads consent
|
|
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
|
+
// Check for gtag consent mode
|
|
10378
|
+
if (typeof window.gtag === "function") {
|
|
10379
|
+
console.log("[Google Ads Tracking] ✅ gtag function found");
|
|
10380
|
+
try {
|
|
10381
|
+
// Try to get consent state from Google's consent mode
|
|
10382
|
+
let consentGranted = false;
|
|
10383
|
+
window.gtag("get", (consentState) => {
|
|
10384
|
+
console.log("[Google Ads Tracking] 📋 Consent state received:", consentState);
|
|
10385
|
+
// For Google Ads conversion tracking, we need ad_storage consent
|
|
10386
|
+
consentGranted = consentState.ad_storage === "granted";
|
|
10387
|
+
console.log("[Google Ads Tracking] 🔐 ad_storage consent:", consentState.ad_storage);
|
|
10388
|
+
});
|
|
10389
|
+
console.log("[Google Ads Tracking] 🎯 Final consent result:", consentGranted);
|
|
10390
|
+
return consentGranted;
|
|
10391
|
+
}
|
|
10392
|
+
catch (error) {
|
|
10393
|
+
console.warn("[Google Ads Tracking] ⚠️ Error checking gtag consent:", error);
|
|
10394
|
+
}
|
|
10395
|
+
}
|
|
10396
|
+
else {
|
|
10397
|
+
console.log("[Google Ads Tracking] ❌ gtag function not found");
|
|
10398
|
+
}
|
|
10399
|
+
// If gtag is not available, we assume consent has not been properly configured
|
|
10400
|
+
// The host page should implement Google Consent Mode if they want tracking
|
|
10401
|
+
console.log("[Google Ads Tracking] 🚫 No consent mechanism found, returning false");
|
|
10402
|
+
return false;
|
|
10403
|
+
}
|
|
10404
|
+
/**
|
|
10405
|
+
* Check if gtag is already initialized on the host page
|
|
10406
|
+
*/
|
|
10407
|
+
function isGtagInitialized() {
|
|
10408
|
+
console.log("[Google Ads Tracking] 🔍 Checking if gtag is initialized...");
|
|
10409
|
+
if (typeof window === "undefined") {
|
|
10410
|
+
console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
|
|
10411
|
+
return false;
|
|
10412
|
+
}
|
|
10413
|
+
console.log("[Google Ads Tracking] ✅ Window object available");
|
|
10414
|
+
// Check if gtag function exists
|
|
10415
|
+
if (typeof window.gtag === "function") {
|
|
10416
|
+
console.log("[Google Ads Tracking] ✅ gtag function exists");
|
|
10417
|
+
return true;
|
|
10418
|
+
}
|
|
10419
|
+
console.log("[Google Ads Tracking] ❌ gtag function not found, checking for scripts...");
|
|
10420
|
+
// Check if Google Analytics or Google Ads scripts are already loaded
|
|
10421
|
+
const scripts = document.querySelectorAll('script[src*="googletagmanager.com"]');
|
|
10422
|
+
console.log("[Google Ads Tracking] 📜 Found", scripts.length, "Google Tag Manager scripts");
|
|
10423
|
+
const isInitialized = scripts.length > 0;
|
|
10424
|
+
console.log("[Google Ads Tracking] 🎯 gtag initialization status:", isInitialized);
|
|
10425
|
+
return isInitialized;
|
|
10426
|
+
}
|
|
10427
|
+
/**
|
|
10428
|
+
* Initialize Google Tag (gtag) if not already present
|
|
10429
|
+
*/
|
|
10430
|
+
function initializeGtag(tagId) {
|
|
10431
|
+
console.log("[Google Ads Tracking] 🚀 Starting gtag initialization with tagId:", tagId);
|
|
10432
|
+
return new Promise((resolve, reject) => {
|
|
10433
|
+
if (typeof window === "undefined") {
|
|
10434
|
+
console.log("[Google Ads Tracking] ❌ Window object not available (SSR context)");
|
|
10435
|
+
reject(new Error("Window object not available"));
|
|
10436
|
+
return;
|
|
10437
|
+
}
|
|
10438
|
+
console.log("[Google Ads Tracking] ✅ Window object available");
|
|
10439
|
+
// If gtag is already initialized, just resolve
|
|
10440
|
+
if (isGtagInitialized()) {
|
|
10441
|
+
console.log("[Google Ads Tracking] ⚡ gtag already initialized, skipping setup");
|
|
10442
|
+
resolve();
|
|
10443
|
+
return;
|
|
10444
|
+
}
|
|
10445
|
+
console.log("[Google Ads Tracking] 📦 gtag not found, creating new script element...");
|
|
10446
|
+
try {
|
|
10447
|
+
// Create the gtag script
|
|
10448
|
+
const script = document.createElement("script");
|
|
10449
|
+
script.async = true;
|
|
10450
|
+
script.src = `https://www.googletagmanager.com/gtag/js?id=${tagId}`;
|
|
10451
|
+
console.log("[Google Ads Tracking] 🌐 Script src set to:", script.src);
|
|
10452
|
+
script.onload = () => {
|
|
10453
|
+
console.log("[Google Ads Tracking] 📥 Script loaded successfully, initializing gtag...");
|
|
10454
|
+
// Initialize gtag
|
|
10455
|
+
window.dataLayer = window.dataLayer || [];
|
|
10456
|
+
console.log("[Google Ads Tracking] 📊 dataLayer initialized");
|
|
10457
|
+
window.gtag = (...args) => {
|
|
10458
|
+
window.dataLayer.push(args);
|
|
10459
|
+
};
|
|
10460
|
+
console.log("[Google Ads Tracking] 🔧 gtag function created");
|
|
10461
|
+
// Configure gtag
|
|
10462
|
+
console.log("[Google Ads Tracking] ⚙️ Configuring gtag with privacy settings...");
|
|
10463
|
+
window.gtag("js", new Date());
|
|
10464
|
+
window.gtag("config", tagId, {
|
|
10465
|
+
// Respect consent settings
|
|
10466
|
+
anonymize_ip: true,
|
|
10467
|
+
allow_google_signals: false,
|
|
10468
|
+
allow_ad_personalization_signals: false,
|
|
10469
|
+
});
|
|
10470
|
+
console.log("[Google Ads Tracking] ✅ gtag initialized successfully with tagId:", tagId);
|
|
10471
|
+
resolve();
|
|
10472
|
+
};
|
|
10473
|
+
script.onerror = (error) => {
|
|
10474
|
+
console.error("[Google Ads Tracking] ❌ Failed to load Google Tag script:", error);
|
|
10475
|
+
reject(new Error("Failed to load Google Tag script"));
|
|
10476
|
+
};
|
|
10477
|
+
console.log("[Google Ads Tracking] 📑 Adding script to document head...");
|
|
10478
|
+
// Add script to head
|
|
10479
|
+
document.head.appendChild(script);
|
|
10480
|
+
console.log("[Google Ads Tracking] ✅ Script element appended to head");
|
|
10481
|
+
}
|
|
10482
|
+
catch (error) {
|
|
10483
|
+
console.error("[Google Ads Tracking] ❌ Error during gtag initialization:", error);
|
|
10484
|
+
reject(error);
|
|
10485
|
+
}
|
|
10486
|
+
});
|
|
10487
|
+
}
|
|
10488
|
+
/**
|
|
10489
|
+
* Track a Google Ads conversion
|
|
10490
|
+
*/
|
|
10491
|
+
function trackConversion(config) {
|
|
10492
|
+
console.log("[Google Ads Tracking] 🎯 Starting conversion tracking with config:", config);
|
|
10493
|
+
if (typeof window === "undefined") {
|
|
10494
|
+
console.warn("[Google Ads Tracking] ❌ Window object not available");
|
|
10495
|
+
return;
|
|
10496
|
+
}
|
|
10497
|
+
console.log("[Google Ads Tracking] ✅ Window object available");
|
|
10498
|
+
if (!config.tagId || !config.conversionId) {
|
|
10499
|
+
console.warn("[Google Ads Tracking] ❌ Missing tagId or conversionId", {
|
|
10500
|
+
tagId: config.tagId,
|
|
10501
|
+
conversionId: config.conversionId,
|
|
10502
|
+
});
|
|
10503
|
+
return;
|
|
10504
|
+
}
|
|
10505
|
+
console.log("[Google Ads Tracking] ✅ Required config fields present:", {
|
|
10506
|
+
tagId: config.tagId,
|
|
10507
|
+
conversionId: config.conversionId,
|
|
10508
|
+
});
|
|
10509
|
+
if (typeof window.gtag !== "function") {
|
|
10510
|
+
console.warn("[Google Ads Tracking] ❌ gtag function not available");
|
|
10511
|
+
return;
|
|
10512
|
+
}
|
|
10513
|
+
console.log("[Google Ads Tracking] ✅ gtag function is available");
|
|
10514
|
+
try {
|
|
10515
|
+
console.log("[Google Ads Tracking] 🔧 Building conversion data...");
|
|
10516
|
+
const conversionData = {
|
|
10517
|
+
send_to: `${config.tagId}/${config.conversionId}`,
|
|
10518
|
+
};
|
|
10519
|
+
console.log("[Google Ads Tracking] 📋 Base conversion data:", conversionData);
|
|
10520
|
+
// Add optional parameters
|
|
10521
|
+
if (config.conversionValue !== undefined) {
|
|
10522
|
+
conversionData.value = config.conversionValue;
|
|
10523
|
+
console.log("[Google Ads Tracking] 💰 Added conversion value:", config.conversionValue);
|
|
10524
|
+
}
|
|
10525
|
+
if (config.conversionCurrency) {
|
|
10526
|
+
conversionData.currency = config.conversionCurrency;
|
|
10527
|
+
console.log("[Google Ads Tracking] 💱 Added currency:", config.conversionCurrency);
|
|
10528
|
+
}
|
|
10529
|
+
if (config.transactionId) {
|
|
10530
|
+
conversionData.transaction_id = config.transactionId;
|
|
10531
|
+
console.log("[Google Ads Tracking] 🔖 Added transaction ID:", config.transactionId);
|
|
10532
|
+
}
|
|
10533
|
+
console.log("[Google Ads Tracking] 📦 Final conversion data:", conversionData);
|
|
10534
|
+
// Track the conversion
|
|
10535
|
+
console.log("[Google Ads Tracking] 🚀 Sending conversion event to gtag...");
|
|
10536
|
+
window.gtag("event", "conversion", conversionData);
|
|
10537
|
+
console.log("[Google Ads Tracking] ✅ Conversion tracked successfully:", conversionData);
|
|
10538
|
+
}
|
|
10539
|
+
catch (error) {
|
|
10540
|
+
console.error("[Google Ads Tracking] ❌ Error tracking conversion:", error);
|
|
10541
|
+
}
|
|
10542
|
+
}
|
|
10543
|
+
/**
|
|
10544
|
+
* Main function to handle Google Ads conversion tracking
|
|
10545
|
+
* This function checks consent, initializes gtag if needed, and tracks the conversion
|
|
10546
|
+
*/
|
|
10547
|
+
async function handleGoogleAdsConversion(config) {
|
|
10548
|
+
console.log("[Google Ads Tracking] 🎬 Starting handleGoogleAdsConversion with config:", config);
|
|
10549
|
+
// Validate config
|
|
10550
|
+
if (!config.tagId || !config.conversionId) {
|
|
10551
|
+
console.log("[Google Ads Tracking] ❌ No tagId or conversionId provided, skipping conversion tracking", { tagId: config.tagId, conversionId: config.conversionId });
|
|
10552
|
+
return;
|
|
10553
|
+
}
|
|
10554
|
+
console.log("[Google Ads Tracking] ✅ Config validation passed");
|
|
10555
|
+
// Check consent first
|
|
10556
|
+
console.log("[Google Ads Tracking] 🔐 Checking consent...");
|
|
10557
|
+
if (!checkGoogleConsent()) {
|
|
10558
|
+
console.log("[Google Ads Tracking] 🚫 Google consent not granted, skipping conversion tracking");
|
|
10559
|
+
return;
|
|
10560
|
+
}
|
|
10561
|
+
console.log("[Google Ads Tracking] ✅ Consent check passed");
|
|
10562
|
+
try {
|
|
10563
|
+
// Initialize gtag if not already present
|
|
10564
|
+
console.log("[Google Ads Tracking] 🔍 Checking if gtag needs initialization...");
|
|
10565
|
+
if (!isGtagInitialized()) {
|
|
10566
|
+
console.log("[Google Ads Tracking] 🚀 Initializing gtag...");
|
|
10567
|
+
await initializeGtag(config.tagId);
|
|
10568
|
+
console.log("[Google Ads Tracking] ✅ gtag initialization completed");
|
|
10569
|
+
}
|
|
10570
|
+
else {
|
|
10571
|
+
console.log("[Google Ads Tracking] ⚡ gtag already initialized, proceeding...");
|
|
10572
|
+
}
|
|
10573
|
+
// Small delay to ensure gtag is ready
|
|
10574
|
+
console.log("[Google Ads Tracking] ⏱️ Adding 100ms delay to ensure gtag is ready...");
|
|
10575
|
+
setTimeout(() => {
|
|
10576
|
+
console.log("[Google Ads Tracking] 🎯 Delay completed, tracking conversion...");
|
|
10577
|
+
trackConversion(config);
|
|
10578
|
+
}, 100);
|
|
10579
|
+
}
|
|
10580
|
+
catch (error) {
|
|
10581
|
+
console.error("[Google Ads Tracking] ❌ Error handling conversion:", error);
|
|
10582
|
+
}
|
|
10583
|
+
}
|
|
10584
|
+
/**
|
|
10585
|
+
* Utility function to get conversion value from booking data
|
|
10586
|
+
*/
|
|
10587
|
+
function getConversionValueFromBooking(bookingData) {
|
|
10588
|
+
console.log("[Google Ads Tracking] 💰 Extracting conversion value from booking data:", bookingData);
|
|
10589
|
+
// Try to get the total amount from various possible structures
|
|
10590
|
+
const possiblePaths = [
|
|
10591
|
+
{ path: "order.total", value: bookingData?.order?.total },
|
|
10592
|
+
{ path: "booking.total", value: bookingData?.booking?.total },
|
|
10593
|
+
{ path: "total", value: bookingData?.total },
|
|
10594
|
+
{ path: "amount", value: bookingData?.amount },
|
|
10595
|
+
{ path: "payment.amount", value: bookingData?.payment?.amount },
|
|
10596
|
+
{ path: "stripePaymentIntent.amount", value: bookingData?.stripePaymentIntent?.amount },
|
|
10597
|
+
];
|
|
10598
|
+
console.log("[Google Ads Tracking] 🔍 Checking possible value paths:", possiblePaths);
|
|
10599
|
+
for (const { path, value } of possiblePaths) {
|
|
10600
|
+
console.log(`[Google Ads Tracking] 📋 Checking path '${path}':`, value);
|
|
10601
|
+
if (typeof value === "number" && value > 0) {
|
|
10602
|
+
// Convert from cents to euros
|
|
10603
|
+
const convertedValue = value / 100;
|
|
10604
|
+
console.log(`[Google Ads Tracking] ✅ Found valid value at '${path}': ${value} cents = ${convertedValue} euros`);
|
|
10605
|
+
return convertedValue;
|
|
10606
|
+
}
|
|
10607
|
+
}
|
|
10608
|
+
console.log("[Google Ads Tracking] ❌ No valid conversion value found in booking data");
|
|
10609
|
+
return undefined;
|
|
10610
|
+
}
|
|
10611
|
+
|
|
10296
10612
|
const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId, }) => {
|
|
10297
10613
|
const [bookingData, setBookingData] = d$1(null);
|
|
10298
10614
|
const [eventDetails, setEventDetails] = d$1(null);
|
|
@@ -10337,6 +10653,27 @@
|
|
|
10337
10653
|
});
|
|
10338
10654
|
// Set payment status from Stripe data or order status
|
|
10339
10655
|
setPaymentStatus(data.stripePaymentIntent?.status || data.order.status);
|
|
10656
|
+
// Trigger Google Ads conversion tracking if payment is successful
|
|
10657
|
+
const finalPaymentStatus = data.stripePaymentIntent?.status || data.order.status;
|
|
10658
|
+
if (finalPaymentStatus === "succeeded" &&
|
|
10659
|
+
config.googleAds?.tagId &&
|
|
10660
|
+
config.googleAds?.conversionId) {
|
|
10661
|
+
// Prepare conversion tracking data
|
|
10662
|
+
const conversionValue = config.googleAds.includeValue !== false
|
|
10663
|
+
? getConversionValueFromBooking(data)
|
|
10664
|
+
: undefined;
|
|
10665
|
+
const transactionId = data.order.id;
|
|
10666
|
+
// Track the conversion
|
|
10667
|
+
handleGoogleAdsConversion({
|
|
10668
|
+
tagId: config.googleAds.tagId,
|
|
10669
|
+
conversionId: config.googleAds.conversionId,
|
|
10670
|
+
conversionValue,
|
|
10671
|
+
conversionCurrency: config.googleAds.conversionCurrency || "EUR",
|
|
10672
|
+
transactionId,
|
|
10673
|
+
}).catch((error) => {
|
|
10674
|
+
console.warn("[BookingSuccessModal] Google Ads conversion tracking failed:", error);
|
|
10675
|
+
});
|
|
10676
|
+
}
|
|
10340
10677
|
}
|
|
10341
10678
|
else {
|
|
10342
10679
|
onError?.(data.error || "Fehler beim Abrufen der Buchungsdaten");
|
|
@@ -10726,44 +11063,42 @@
|
|
|
10726
11063
|
// Allocation Badge Component
|
|
10727
11064
|
const AllocationBadge = ({ availableSpots, maxParticipants, }) => {
|
|
10728
11065
|
const badgeInfo = getAllocationBadgeInfo(availableSpots, maxParticipants);
|
|
10729
|
-
if (!badgeInfo)
|
|
10730
|
-
return null;
|
|
10731
11066
|
return (u$2("div", { style: {
|
|
10732
|
-
|
|
10733
|
-
|
|
10734
|
-
right: "20px",
|
|
10735
|
-
backgroundColor: badgeInfo.backgroundColor,
|
|
10736
|
-
color: badgeInfo.textColor,
|
|
11067
|
+
backgroundColor: badgeInfo?.backgroundColor || "transparent",
|
|
11068
|
+
color: badgeInfo?.textColor || "transparent",
|
|
10737
11069
|
fontSize: "11px",
|
|
10738
11070
|
fontWeight: "bold",
|
|
10739
|
-
padding: "
|
|
11071
|
+
padding: "1px 8px",
|
|
11072
|
+
display: "flex",
|
|
11073
|
+
marginRight: "auto",
|
|
11074
|
+
marginTop: "-24px",
|
|
11075
|
+
marginBottom: "5px",
|
|
10740
11076
|
borderRadius: "var(--bw-border-radius-small)",
|
|
10741
11077
|
fontFamily: "var(--bw-font-family)",
|
|
10742
|
-
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
|
|
10743
11078
|
zIndex: 50,
|
|
10744
11079
|
whiteSpace: "nowrap",
|
|
10745
|
-
|
|
11080
|
+
width: "fit-content",
|
|
11081
|
+
}, children: badgeInfo?.text || " - " }));
|
|
10746
11082
|
};
|
|
10747
11083
|
// Price Badge Component (for special prices)
|
|
10748
11084
|
const SpecialPriceBadge = ({ price, yearPrices }) => {
|
|
10749
11085
|
const badgeInfo = getPriceBadgeInfo(price, yearPrices);
|
|
10750
|
-
if (!badgeInfo)
|
|
10751
|
-
return null;
|
|
10752
11086
|
return (u$2("div", { style: {
|
|
10753
|
-
|
|
10754
|
-
|
|
10755
|
-
right: "20px",
|
|
10756
|
-
backgroundColor: badgeInfo.backgroundColor,
|
|
10757
|
-
color: badgeInfo.textColor,
|
|
11087
|
+
backgroundColor: badgeInfo?.backgroundColor || "transparent",
|
|
11088
|
+
color: badgeInfo?.textColor || "transparent",
|
|
10758
11089
|
fontSize: "11px",
|
|
10759
11090
|
fontWeight: "bold",
|
|
10760
|
-
padding: "
|
|
11091
|
+
padding: "1px 8px",
|
|
11092
|
+
display: "flex",
|
|
11093
|
+
marginLeft: "auto",
|
|
11094
|
+
marginTop: "-20px",
|
|
11095
|
+
marginBottom: "5px",
|
|
10761
11096
|
borderRadius: "var(--bw-border-radius-small)",
|
|
10762
11097
|
fontFamily: "var(--bw-font-family)",
|
|
10763
|
-
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.2)",
|
|
10764
11098
|
zIndex: 50,
|
|
10765
11099
|
whiteSpace: "nowrap",
|
|
10766
|
-
|
|
11100
|
+
width: "fit-content",
|
|
11101
|
+
}, children: badgeInfo?.text || " - " }));
|
|
10767
11102
|
};
|
|
10768
11103
|
// Price Display Component (with special price styling)
|
|
10769
11104
|
const PriceDisplay = ({ price, yearPrices }) => {
|
|
@@ -10776,13 +11111,27 @@
|
|
|
10776
11111
|
fontSize: "var(--bw-font-size-medium)",
|
|
10777
11112
|
fontWeight: "600",
|
|
10778
11113
|
padding: "3px 8px",
|
|
10779
|
-
marginTop: "-3px",
|
|
10780
11114
|
borderRadius: "var(--bw-border-radius-small)",
|
|
10781
11115
|
fontFamily: "var(--bw-font-family)",
|
|
10782
11116
|
border: displayInfo ? "none" : "1px solid var(--bw-border-color)",
|
|
10783
11117
|
boxShadow: displayInfo ? "0 2px 4px rgba(0, 0, 0, 0.2)" : "none",
|
|
10784
11118
|
}, children: formatCurrency(price) }));
|
|
10785
11119
|
};
|
|
11120
|
+
const spinner = (borderColor) => (u$2("div", { style: {
|
|
11121
|
+
width: "auto",
|
|
11122
|
+
height: "auto",
|
|
11123
|
+
fontSize: "16px",
|
|
11124
|
+
color: "var(--bw-text-color)",
|
|
11125
|
+
animation: "spin 1s linear infinite",
|
|
11126
|
+
display: "flex",
|
|
11127
|
+
justifyContent: "center",
|
|
11128
|
+
alignItems: "center",
|
|
11129
|
+
}, children: u$2("div", { style: {
|
|
11130
|
+
width: "16px",
|
|
11131
|
+
height: "16px",
|
|
11132
|
+
border: `3px dotted ${"var(--bw-highlight-color)"}`,
|
|
11133
|
+
borderRadius: "50%",
|
|
11134
|
+
} }) }));
|
|
10786
11135
|
function EventInstanceSelection({ eventInstances, selectedEventType, onEventInstanceSelect, onBackToEventTypes, isOpen, onClose, isLoadingEventInstances = false, isLoadingEventDetails = false, }) {
|
|
10787
11136
|
const [selectedEventInstanceId, setSelectedEventInstanceId] = d$1(null);
|
|
10788
11137
|
const [openMonths, setOpenMonths] = d$1(new Set());
|
|
@@ -10859,7 +11208,6 @@
|
|
|
10859
11208
|
.bw-accordion-trigger,
|
|
10860
11209
|
.bw-event-instance-card {
|
|
10861
11210
|
position: relative;
|
|
10862
|
-
overflow: hidden;
|
|
10863
11211
|
}
|
|
10864
11212
|
|
|
10865
11213
|
.bw-accordion-trigger::before,
|
|
@@ -10908,14 +11256,11 @@
|
|
|
10908
11256
|
height: 32px !important;
|
|
10909
11257
|
font-size: 1rem !important;
|
|
10910
11258
|
}
|
|
10911
|
-
.bw-event-instance-title {
|
|
10912
|
-
font-size: 1rem !important;
|
|
10913
|
-
}
|
|
10914
11259
|
.bw-event-instance-price {
|
|
10915
11260
|
font-size: 1.1rem !important;
|
|
10916
11261
|
}
|
|
10917
11262
|
}
|
|
10918
|
-
` }), u$2(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name} Terminauswahl`,
|
|
11263
|
+
` }), u$2(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name} Terminauswahl`, children: u$2("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: u$2("div", { style: {
|
|
10919
11264
|
display: "flex",
|
|
10920
11265
|
flexDirection: "column",
|
|
10921
11266
|
gap: "20px",
|
|
@@ -11017,13 +11362,7 @@
|
|
|
11017
11362
|
height: "20px",
|
|
11018
11363
|
backgroundColor: "var(--bw-border-color)",
|
|
11019
11364
|
borderRadius: "8px",
|
|
11020
|
-
} })] }), eventIdx
|
|
11021
|
-
width: "90%",
|
|
11022
|
-
height: "12px",
|
|
11023
|
-
backgroundColor: "var(--bw-border-color)",
|
|
11024
|
-
borderRadius: "4px",
|
|
11025
|
-
marginTop: "8px",
|
|
11026
|
-
} }))] }, eventIdx))) })] }, idx))) }) }) })] }));
|
|
11365
|
+
} })] })] }, eventIdx))) })] }, idx))) }) }) })] }));
|
|
11027
11366
|
}
|
|
11028
11367
|
// Show empty state only if not loading and no event instances
|
|
11029
11368
|
if (eventInstances.length === 0) {
|
|
@@ -11032,7 +11371,6 @@
|
|
|
11032
11371
|
.bw-accordion-trigger,
|
|
11033
11372
|
.bw-event-instance-card {
|
|
11034
11373
|
position: relative;
|
|
11035
|
-
overflow: hidden;
|
|
11036
11374
|
}
|
|
11037
11375
|
|
|
11038
11376
|
.bw-accordion-trigger::before,
|
|
@@ -11081,9 +11419,6 @@
|
|
|
11081
11419
|
height: 32px !important;
|
|
11082
11420
|
font-size: 1rem !important;
|
|
11083
11421
|
}
|
|
11084
|
-
.bw-event-instance-title {
|
|
11085
|
-
font-size: 1rem !important;
|
|
11086
|
-
}
|
|
11087
11422
|
.bw-event-instance-price {
|
|
11088
11423
|
font-size: 1.1rem !important;
|
|
11089
11424
|
}
|
|
@@ -11114,7 +11449,6 @@
|
|
|
11114
11449
|
.bw-accordion-trigger,
|
|
11115
11450
|
.bw-event-instance-card {
|
|
11116
11451
|
position: relative;
|
|
11117
|
-
overflow: hidden;
|
|
11118
11452
|
}
|
|
11119
11453
|
|
|
11120
11454
|
.bw-accordion-trigger::before,
|
|
@@ -11163,14 +11497,11 @@
|
|
|
11163
11497
|
height: 32px !important;
|
|
11164
11498
|
font-size: 1rem !important;
|
|
11165
11499
|
}
|
|
11166
|
-
.bw-event-instance-title {
|
|
11167
|
-
font-size: 1rem !important;
|
|
11168
|
-
}
|
|
11169
11500
|
.bw-event-instance-price {
|
|
11170
11501
|
font-size: 1.1rem !important;
|
|
11171
11502
|
}
|
|
11172
11503
|
}
|
|
11173
|
-
` }), u$2(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Terminauswahl - ${selectedEventType?.name || "Event"}`,
|
|
11504
|
+
` }), u$2(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `Terminauswahl - ${selectedEventType?.name || "Event"}`, children: u$2("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: u$2("div", { style: {
|
|
11174
11505
|
display: "flex",
|
|
11175
11506
|
flexDirection: "column",
|
|
11176
11507
|
gap: "20px",
|
|
@@ -11245,7 +11576,7 @@
|
|
|
11245
11576
|
color: "var(--bw-highlight-color-muted, rgba(59, 130, 246, 0.8))",
|
|
11246
11577
|
animation: "spin 1s linear infinite",
|
|
11247
11578
|
fontSize: "32px",
|
|
11248
|
-
}, children:
|
|
11579
|
+
}, children: spinner() }) })), u$2(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices }), u$2(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants }), u$2("div", { style: {
|
|
11249
11580
|
display: "flex",
|
|
11250
11581
|
justifyContent: "space-between",
|
|
11251
11582
|
width: "100%",
|
|
@@ -11274,12 +11605,21 @@
|
|
|
11274
11605
|
alignItems: "start",
|
|
11275
11606
|
justifyContent: "start",
|
|
11276
11607
|
lineHeight: "1.2",
|
|
11277
|
-
}, children: [u$2("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.startTime) }),
|
|
11608
|
+
}, children: [u$2("div", { children: [u$2("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.startTime) }), formatWeekday(event.startTime) !==
|
|
11609
|
+
formatWeekday(event.endTime) && (u$2(k$3, { children: [u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), u$2("span", { className: "bw-event-instance-title", style: { fontWeight: "600", marginBottom: "2px" }, children: formatWeekday(event.endTime) })] }))] }), u$2("div", { children: formatWeekday(event.startTime) ===
|
|
11610
|
+
formatWeekday(event.endTime) ? (u$2(k$3, { children: [u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime) }), u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime) })] })) : (u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime), " Uhr"] })) })] }), u$2("span", { style: {
|
|
11611
|
+
fontSize: "12px",
|
|
11612
|
+
fontWeight: 400,
|
|
11613
|
+
color: "var(--bw-text-muted)",
|
|
11614
|
+
marginLeft: "6px",
|
|
11615
|
+
background: "var(--bw-background-muted)",
|
|
11616
|
+
whiteSpace: "nowrap",
|
|
11617
|
+
}, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), u$2("div", { className: "bw-event-instance-price", style: {
|
|
11278
11618
|
textAlign: "right",
|
|
11279
11619
|
display: "flex",
|
|
11280
11620
|
flexDirection: "column",
|
|
11281
11621
|
alignItems: "end",
|
|
11282
|
-
}, children: u$2(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), u$2("h4", { className: "bw-event-instance-title", style: {
|
|
11622
|
+
}, children: u$2(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), event.name !== selectedEventType?.name && (u$2("h4", { className: "bw-event-instance-title", style: {
|
|
11283
11623
|
fontSize: "var(--bw-font-size)",
|
|
11284
11624
|
fontWeight: "600",
|
|
11285
11625
|
color: "var(--bw-text-color)",
|
|
@@ -11288,26 +11628,8 @@
|
|
|
11288
11628
|
display: "flex",
|
|
11289
11629
|
alignItems: "center",
|
|
11290
11630
|
gap: "8px",
|
|
11291
|
-
|
|
11292
|
-
|
|
11293
|
-
fontWeight: 400,
|
|
11294
|
-
color: "var(--bw-text-muted)",
|
|
11295
|
-
marginLeft: "6px",
|
|
11296
|
-
background: "var(--bw-background-muted)",
|
|
11297
|
-
borderRadius: "8px",
|
|
11298
|
-
padding: "2px 8px",
|
|
11299
|
-
whiteSpace: "nowrap",
|
|
11300
|
-
}, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), event.notes && (u$2("p", { style: {
|
|
11301
|
-
fontSize: "12px",
|
|
11302
|
-
color: "var(--bw-text-muted)",
|
|
11303
|
-
marginTop: "8px",
|
|
11304
|
-
display: "-webkit-box",
|
|
11305
|
-
WebkitBoxOrient: "vertical",
|
|
11306
|
-
WebkitLineClamp: 2,
|
|
11307
|
-
overflow: "hidden",
|
|
11308
|
-
margin: "0",
|
|
11309
|
-
lineHeight: "1.3",
|
|
11310
|
-
}, children: event.notes }))] }, event.id));
|
|
11631
|
+
maxWidth: "230px",
|
|
11632
|
+
}, children: event.name }))] }, event.id));
|
|
11311
11633
|
}) }) }, month));
|
|
11312
11634
|
}) }) }) })] }));
|
|
11313
11635
|
}
|
|
@@ -11948,7 +12270,6 @@
|
|
|
11948
12270
|
body: JSON.stringify(requestBody),
|
|
11949
12271
|
});
|
|
11950
12272
|
const data = await response.json();
|
|
11951
|
-
console.log("Event details response:", { status: response.status, data }); // Debug log
|
|
11952
12273
|
if (response.ok) {
|
|
11953
12274
|
setEventDetails(data.eventDetails);
|
|
11954
12275
|
// Store system configuration for PaymentForm
|