@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/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: "
|
|
241
|
-
background: "
|
|
242
|
-
surface: "
|
|
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:
|
|
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 = "
|
|
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}`,
|
|
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
|
|
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
|
-
|
|
10648
|
-
|
|
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: "
|
|
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
|
-
|
|
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
|
-
|
|
10669
|
-
|
|
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: "
|
|
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
|
-
|
|
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`,
|
|
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
|
|
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"}`,
|
|
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:
|
|
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) }),
|
|
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 }) })] }),
|
|
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
|
-
|
|
11207
|
-
|
|
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
|