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