@bigz-app/booking-widget 0.3.8 → 0.3.9

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.esm.js CHANGED
@@ -12225,6 +12225,263 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
12225
12225
  ` })] }));
12226
12226
  }
12227
12227
 
12228
+ function PromoDialog({ onClose, onCtaClick }) {
12229
+ const [copied, setCopied] = useState(false);
12230
+ const [isVisible, setIsVisible] = useState(false);
12231
+ // Hardcoded Xmas surf school content
12232
+ const discountCode = "X-MAS";
12233
+ // Animate in on mount
12234
+ useEffect(() => {
12235
+ const timer = setTimeout(() => setIsVisible(true), 50);
12236
+ return () => clearTimeout(timer);
12237
+ }, []);
12238
+ const handleCopyCode = async () => {
12239
+ try {
12240
+ await navigator.clipboard.writeText(discountCode);
12241
+ setCopied(true);
12242
+ setTimeout(() => setCopied(false), 2000);
12243
+ }
12244
+ catch (err) {
12245
+ // Fallback for older browsers
12246
+ const textArea = document.createElement("textarea");
12247
+ textArea.value = discountCode;
12248
+ document.body.appendChild(textArea);
12249
+ textArea.select();
12250
+ document.execCommand("copy");
12251
+ document.body.removeChild(textArea);
12252
+ setCopied(true);
12253
+ setTimeout(() => setCopied(false), 2000);
12254
+ }
12255
+ };
12256
+ const handleClose = () => {
12257
+ setIsVisible(false);
12258
+ setTimeout(onClose, 200);
12259
+ };
12260
+ const handleCtaClick = () => {
12261
+ setIsVisible(false);
12262
+ setTimeout(onCtaClick, 200);
12263
+ };
12264
+ return (jsxs(Fragment, { children: [jsx("style", { children: `
12265
+ @keyframes promo-wave {
12266
+ 0%, 100% { transform: translateX(0) translateY(0); }
12267
+ 25% { transform: translateX(5px) translateY(-3px); }
12268
+ 50% { transform: translateX(0) translateY(-5px); }
12269
+ 75% { transform: translateX(-5px) translateY(-3px); }
12270
+ }
12271
+ @keyframes promo-float {
12272
+ 0%, 100% { transform: translateY(0); }
12273
+ 50% { transform: translateY(-8px); }
12274
+ }
12275
+ @keyframes promo-shimmer {
12276
+ 0% { background-position: -200% center; }
12277
+ 100% { background-position: 200% center; }
12278
+ }
12279
+ @keyframes promo-sparkle {
12280
+ 0%, 100% { opacity: 0.3; transform: scale(1); }
12281
+ 50% { opacity: 1; transform: scale(1.2); }
12282
+ }
12283
+ @keyframes promo-snow {
12284
+ 0% { transform: translateY(-10px) rotate(0deg); opacity: 0; }
12285
+ 10% { opacity: 1; }
12286
+ 90% { opacity: 1; }
12287
+ 100% { transform: translateY(350px) rotate(360deg); opacity: 0; }
12288
+ }
12289
+ ` }), jsx("div", { onClick: handleClose, style: {
12290
+ position: "fixed",
12291
+ inset: 0,
12292
+ backgroundColor: "rgba(0, 20, 40, 0.85)",
12293
+ backdropFilter: "blur(8px)",
12294
+ zIndex: 9998,
12295
+ opacity: isVisible ? 1 : 0,
12296
+ transition: "opacity 300ms ease-out",
12297
+ } }), jsx("div", { style: {
12298
+ position: "fixed",
12299
+ top: "50%",
12300
+ left: "50%",
12301
+ transform: `translate(-50%, -50%) scale(${isVisible ? 1 : 0.9})`,
12302
+ zIndex: 9999,
12303
+ width: "92%",
12304
+ maxWidth: "440px",
12305
+ opacity: isVisible ? 1 : 0,
12306
+ transition: "all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)",
12307
+ }, children: jsxs("div", { style: {
12308
+ position: "relative",
12309
+ background: "linear-gradient(165deg, #0c4a6e 0%, #0e7490 40%, #0891b2 100%)",
12310
+ borderRadius: "28px",
12311
+ overflow: "hidden",
12312
+ boxShadow: "0 25px 60px -12px rgba(0, 0, 0, 0.5), 0 0 0 1px rgba(255,255,255,0.1), inset 0 1px 0 rgba(255,255,255,0.2)",
12313
+ }, children: [Array.from({ length: 15 }).map((_, i) => (jsx("div", { style: {
12314
+ position: "absolute",
12315
+ left: `${5 + Math.random() * 90}%`,
12316
+ top: "-10px",
12317
+ fontSize: `${10 + Math.random() * 14}px`,
12318
+ color: "white",
12319
+ opacity: 0,
12320
+ animation: `promo-snow ${4 + Math.random() * 3}s linear infinite`,
12321
+ animationDelay: `${Math.random() * 4}s`,
12322
+ pointerEvents: "none",
12323
+ zIndex: 1,
12324
+ }, children: "\u2744" }, i))), jsxs("div", { style: {
12325
+ position: "relative",
12326
+ height: "180px",
12327
+ background: "linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(12,74,110,0.8) 100%)",
12328
+ display: "flex",
12329
+ alignItems: "center",
12330
+ justifyContent: "center",
12331
+ overflow: "hidden",
12332
+ }, children: [jsx("img", { src: "https://images.unsplash.com/photo-1502680390469-be75c86b636f?w=600&q=80", alt: "Surfer at sunset", style: {
12333
+ position: "absolute",
12334
+ inset: 0,
12335
+ width: "100%",
12336
+ height: "100%",
12337
+ objectFit: "cover",
12338
+ opacity: 0.6,
12339
+ } }), jsx("div", { style: {
12340
+ position: "absolute",
12341
+ inset: 0,
12342
+ background: "linear-gradient(180deg, rgba(12,74,110,0.3) 0%, rgba(12,74,110,0.95) 100%)",
12343
+ } }), jsx("div", { style: {
12344
+ position: "relative",
12345
+ zIndex: 2,
12346
+ fontSize: "64px",
12347
+ animation: "promo-float 3s ease-in-out infinite",
12348
+ filter: "drop-shadow(0 8px 16px rgba(0,0,0,0.4))",
12349
+ }, children: "\uD83C\uDFC4\u200D\u2642\uFE0F" }), jsx("div", { style: {
12350
+ position: "absolute",
12351
+ top: "16px",
12352
+ left: "20px",
12353
+ fontSize: "28px",
12354
+ animation: "promo-sparkle 2s ease-in-out infinite",
12355
+ }, children: "\uD83C\uDF84" }), jsx("div", { style: {
12356
+ position: "absolute",
12357
+ top: "20px",
12358
+ right: "20px",
12359
+ fontSize: "24px",
12360
+ animation: "promo-sparkle 2s ease-in-out infinite 0.5s",
12361
+ }, children: "\u2B50" })] }), jsx("button", { onClick: handleClose, style: {
12362
+ position: "absolute",
12363
+ top: "16px",
12364
+ right: "16px",
12365
+ width: "36px",
12366
+ height: "36px",
12367
+ borderRadius: "50%",
12368
+ border: "none",
12369
+ background: "rgba(0, 0, 0, 0.3)",
12370
+ backdropFilter: "blur(4px)",
12371
+ color: "white",
12372
+ fontSize: "22px",
12373
+ cursor: "pointer",
12374
+ display: "flex",
12375
+ alignItems: "center",
12376
+ justifyContent: "center",
12377
+ transition: "all 150ms ease",
12378
+ zIndex: 10,
12379
+ lineHeight: 1,
12380
+ }, onMouseEnter: (e) => {
12381
+ e.currentTarget.style.background = "rgba(0, 0, 0, 0.5)";
12382
+ e.currentTarget.style.transform = "scale(1.1)";
12383
+ }, onMouseLeave: (e) => {
12384
+ e.currentTarget.style.background = "rgba(0, 0, 0, 0.3)";
12385
+ e.currentTarget.style.transform = "scale(1)";
12386
+ }, children: "\u00D7" }), jsxs("div", { style: { padding: "28px 28px 32px", textAlign: "center", position: "relative", zIndex: 2 }, children: [jsx("h2", { style: {
12387
+ fontSize: "26px",
12388
+ fontWeight: "800",
12389
+ color: "white",
12390
+ marginBottom: "6px",
12391
+ textShadow: "0 2px 8px rgba(0,0,0,0.3)",
12392
+ letterSpacing: "-0.5px",
12393
+ }, children: "Frohe Weihnachten! \uD83C\uDF85" }), jsxs("p", { style: {
12394
+ fontSize: "17px",
12395
+ color: "rgba(255, 255, 255, 0.9)",
12396
+ marginBottom: "20px",
12397
+ lineHeight: 1.5,
12398
+ }, children: ["Schenk dir oder deinen Liebsten", jsx("br", {}), jsx("strong", { style: { color: "#fbbf24" }, children: "10% Rabatt" }), " auf alle Kurse!"] }), jsxs("div", { style: {
12399
+ background: "white",
12400
+ borderRadius: "16px",
12401
+ padding: "18px 20px",
12402
+ marginBottom: "20px",
12403
+ boxShadow: "0 8px 24px rgba(0,0,0,0.15), inset 0 -2px 0 rgba(0,0,0,0.05)",
12404
+ }, children: [jsx("p", { style: {
12405
+ fontSize: "11px",
12406
+ textTransform: "uppercase",
12407
+ letterSpacing: "1.5px",
12408
+ color: "#64748b",
12409
+ marginBottom: "10px",
12410
+ fontWeight: "600",
12411
+ }, children: "Dein Geschenk-Code" }), jsxs("div", { style: {
12412
+ display: "flex",
12413
+ alignItems: "center",
12414
+ justifyContent: "center",
12415
+ gap: "14px",
12416
+ }, children: [jsx("div", { style: {
12417
+ background: "linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)",
12418
+ padding: "10px 20px",
12419
+ borderRadius: "10px",
12420
+ boxShadow: "0 4px 12px rgba(220, 38, 38, 0.3)",
12421
+ }, children: jsx("span", { style: {
12422
+ fontSize: "28px",
12423
+ fontWeight: "900",
12424
+ color: "white",
12425
+ letterSpacing: "6px",
12426
+ textShadow: "0 2px 4px rgba(0,0,0,0.2)",
12427
+ }, children: discountCode }) }), jsx("button", { onClick: handleCopyCode, style: {
12428
+ padding: "12px 16px",
12429
+ borderRadius: "10px",
12430
+ border: "2px solid",
12431
+ borderColor: copied ? "#22c55e" : "#e2e8f0",
12432
+ background: copied ? "#dcfce7" : "#f8fafc",
12433
+ color: copied ? "#15803d" : "#475569",
12434
+ fontSize: "13px",
12435
+ fontWeight: "600",
12436
+ cursor: "pointer",
12437
+ transition: "all 150ms ease",
12438
+ display: "flex",
12439
+ alignItems: "center",
12440
+ gap: "6px",
12441
+ whiteSpace: "nowrap",
12442
+ }, children: copied ? (jsx(Fragment, { children: "\u2713 Kopiert!" })) : (jsxs(Fragment, { children: [jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] }), "Kopieren"] })) })] })] }), jsx("div", { style: {
12443
+ display: "flex",
12444
+ justifyContent: "center",
12445
+ gap: "8px",
12446
+ marginBottom: "20px",
12447
+ flexWrap: "wrap",
12448
+ }, children: ["🏄 Surfen", "🪁 Wingen", "🏄‍♀️ SUP", "💨 Windsurfen"].map((activity) => (jsx("span", { style: {
12449
+ background: "rgba(255,255,255,0.15)",
12450
+ backdropFilter: "blur(4px)",
12451
+ padding: "6px 12px",
12452
+ borderRadius: "20px",
12453
+ fontSize: "13px",
12454
+ color: "white",
12455
+ fontWeight: "500",
12456
+ }, children: activity }, activity))) }), jsxs("button", { onClick: handleCtaClick, style: {
12457
+ width: "100%",
12458
+ padding: "18px 24px",
12459
+ borderRadius: "14px",
12460
+ border: "none",
12461
+ background: "linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",
12462
+ color: "white",
12463
+ fontSize: "18px",
12464
+ fontWeight: "700",
12465
+ cursor: "pointer",
12466
+ transition: "all 150ms ease",
12467
+ boxShadow: "0 8px 24px rgba(245, 158, 11, 0.4), inset 0 1px 0 rgba(255,255,255,0.2)",
12468
+ display: "flex",
12469
+ alignItems: "center",
12470
+ justifyContent: "center",
12471
+ gap: "10px",
12472
+ }, onMouseEnter: (e) => {
12473
+ e.currentTarget.style.transform = "translateY(-2px)";
12474
+ e.currentTarget.style.boxShadow = "0 12px 28px rgba(245, 158, 11, 0.5), inset 0 1px 0 rgba(255,255,255,0.2)";
12475
+ }, onMouseLeave: (e) => {
12476
+ e.currentTarget.style.transform = "translateY(0)";
12477
+ e.currentTarget.style.boxShadow = "0 8px 24px rgba(245, 158, 11, 0.4), inset 0 1px 0 rgba(255,255,255,0.2)";
12478
+ }, children: [jsx("span", { style: { animation: "promo-wave 2s ease-in-out infinite" }, children: "\uD83C\uDF81" }), "Jetzt Kurs buchen", jsx("span", { children: "\u2192" })] }), jsx("p", { style: {
12479
+ marginTop: "16px",
12480
+ fontSize: "12px",
12481
+ color: "rgba(255,255,255,0.6)",
12482
+ }, children: "G\u00FCltig f\u00FCr alle Buchungen bis 31. Dezember 2025" })] })] }) })] }));
12483
+ }
12484
+
12228
12485
  // Predefined themes & Style Provider have been moved to ../styles/StyleProvider.tsx
12229
12486
  // Main widget component
12230
12487
  function UniversalBookingWidget({ config: baseConfig }) {
@@ -12267,6 +12524,9 @@ function UniversalBookingWidget({ config: baseConfig }) {
12267
12524
  // PERFORMANCE OPTIMIZATION: Lazy component loading
12268
12525
  const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = useState(false);
12269
12526
  const [shouldRenderBookingForm, setShouldRenderBookingForm] = useState(false);
12527
+ // Promo dialog state
12528
+ const [showPromoDialog, setShowPromoDialog] = useState(false);
12529
+ const [widgetContainerRef, setWidgetContainerRef] = useState(null);
12270
12530
  // Determine initial step and load data
12271
12531
  useEffect(() => {
12272
12532
  const initializeWidget = async () => {
@@ -12341,6 +12601,46 @@ function UniversalBookingWidget({ config: baseConfig }) {
12341
12601
  setShouldRenderBookingForm(true);
12342
12602
  }
12343
12603
  }, [currentStep, shouldRenderInstanceSelection, shouldRenderBookingForm]);
12604
+ // Promo dialog: show Xmas promo once per user during holiday season, prevent double-opening across multiple widgets
12605
+ useEffect(() => {
12606
+ // Only show during holiday season (December and January)
12607
+ const now = new Date();
12608
+ const month = now.getMonth(); // 0 = January, 11 = December
12609
+ const isHolidaySeason = month === 11 || month === 0; // December (11) or January (0)
12610
+ if (!isHolidaySeason) {
12611
+ return;
12612
+ }
12613
+ const promoId = "xmas-2024";
12614
+ const storageKey = `bigz-promo-${promoId}-shown`;
12615
+ const globalFlagKey = `__bigzPromoShown_${promoId}`;
12616
+ // Check if already shown in this session (localStorage) or claimed by another widget (global flag)
12617
+ const alreadyShown = localStorage.getItem(storageKey) === "true";
12618
+ const claimedByOtherWidget = window[globalFlagKey] === true;
12619
+ if (alreadyShown || claimedByOtherWidget) {
12620
+ return;
12621
+ }
12622
+ // Claim this promo for this widget instance (prevents other widgets from showing it)
12623
+ window[globalFlagKey] = true;
12624
+ // Show the dialog after a short delay for better UX
12625
+ const timer = setTimeout(() => {
12626
+ setShowPromoDialog(true);
12627
+ }, 1000);
12628
+ return () => clearTimeout(timer);
12629
+ }, []);
12630
+ // Handle promo dialog close
12631
+ const handlePromoDialogClose = () => {
12632
+ setShowPromoDialog(false);
12633
+ localStorage.setItem("bigz-promo-xmas-2024-shown", "true");
12634
+ };
12635
+ // Handle promo dialog CTA click - scroll to widget
12636
+ const handlePromoCtaClick = () => {
12637
+ setShowPromoDialog(false);
12638
+ localStorage.setItem("bigz-promo-xmas-2024-shown", "true");
12639
+ // Scroll to the widget container
12640
+ if (widgetContainerRef) {
12641
+ widgetContainerRef.scrollIntoView({ behavior: "smooth", block: "start" });
12642
+ }
12643
+ };
12344
12644
  const loadEventTypes = async () => {
12345
12645
  const requestBody = {
12346
12646
  organizationId: config.organizationId,
@@ -12740,104 +13040,104 @@ function UniversalBookingWidget({ config: baseConfig }) {
12740
13040
  // Main view based on view mode
12741
13041
  if (viewMode === "next-events" && showingPreview) {
12742
13042
  // Next events preview mode
12743
- return (jsxs(StyleProvider, { config: config, children: [jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading }), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
12744
- setCurrentStep("eventTypes");
12745
- setShowingPreview(true);
12746
- setEventDetails(null);
12747
- }, onBackToEventTypes: () => {
12748
- setCurrentStep("eventTypes");
12749
- setShowingPreview(true);
12750
- setEventDetails(null);
12751
- }, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
12752
- setCurrentStep("eventTypes");
12753
- setShowingPreview(true);
12754
- setEventDetails(null);
12755
- }, systemConfig: systemConfig })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
12756
- setIsSuccess(false);
12757
- setCurrentStep("eventTypes");
12758
- setShowingPreview(true);
12759
- // Reset state
12760
- setSuccessPaymentIntentId(null);
12761
- // Reset lazy loading flags
12762
- setShouldRenderInstanceSelection(false);
12763
- setShouldRenderBookingForm(false);
12764
- // Clean up URL to remove Stripe parameters
12765
- const url = new URL(window.location.href);
12766
- url.searchParams.delete("payment_intent");
12767
- url.searchParams.delete("payment_intent_client_secret");
12768
- url.searchParams.delete("redirect_status");
12769
- window.history.replaceState({}, "", url.toString());
12770
- }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }));
13043
+ return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading }), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
13044
+ setCurrentStep("eventTypes");
13045
+ setShowingPreview(true);
13046
+ setEventDetails(null);
13047
+ }, onBackToEventTypes: () => {
13048
+ setCurrentStep("eventTypes");
13049
+ setShowingPreview(true);
13050
+ setEventDetails(null);
13051
+ }, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
13052
+ setCurrentStep("eventTypes");
13053
+ setShowingPreview(true);
13054
+ setEventDetails(null);
13055
+ }, systemConfig: systemConfig })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
13056
+ setIsSuccess(false);
13057
+ setCurrentStep("eventTypes");
13058
+ setShowingPreview(true);
13059
+ // Reset state
13060
+ setSuccessPaymentIntentId(null);
13061
+ // Reset lazy loading flags
13062
+ setShouldRenderInstanceSelection(false);
13063
+ setShouldRenderBookingForm(false);
13064
+ // Clean up URL to remove Stripe parameters
13065
+ const url = new URL(window.location.href);
13066
+ url.searchParams.delete("payment_intent");
13067
+ url.searchParams.delete("payment_intent_client_secret");
13068
+ url.searchParams.delete("redirect_status");
13069
+ window.history.replaceState({}, "", url.toString());
13070
+ }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
12771
13071
  }
12772
13072
  if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
12773
13073
  // Show all events for the single event type
12774
- return (jsxs(StyleProvider, { config: config, children: [shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
12775
- setShowingPreview(true);
12776
- setCurrentStep("eventTypes");
12777
- }, isOpen: currentStep === "eventInstances", onClose: () => {
12778
- setShowingPreview(true);
12779
- setCurrentStep("eventTypes");
12780
- }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
12781
- setIsSuccess(false);
12782
- setCurrentStep("eventTypes");
12783
- setShowingPreview(true);
12784
- // Reset state
12785
- setSuccessPaymentIntentId(null);
12786
- // Reset lazy loading flags
12787
- setShouldRenderInstanceSelection(false);
12788
- setShouldRenderBookingForm(false);
12789
- // Clean up URL to remove Stripe parameters
12790
- const url = new URL(window.location.href);
12791
- url.searchParams.delete("payment_intent");
12792
- url.searchParams.delete("payment_intent_client_secret");
12793
- url.searchParams.delete("redirect_status");
12794
- window.history.replaceState({}, "", url.toString());
12795
- }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }));
13074
+ return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
13075
+ setShowingPreview(true);
13076
+ setCurrentStep("eventTypes");
13077
+ }, isOpen: currentStep === "eventInstances", onClose: () => {
13078
+ setShowingPreview(true);
13079
+ setCurrentStep("eventTypes");
13080
+ }, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
13081
+ setIsSuccess(false);
13082
+ setCurrentStep("eventTypes");
13083
+ setShowingPreview(true);
13084
+ // Reset state
13085
+ setSuccessPaymentIntentId(null);
13086
+ // Reset lazy loading flags
13087
+ setShouldRenderInstanceSelection(false);
13088
+ setShouldRenderBookingForm(false);
13089
+ // Clean up URL to remove Stripe parameters
13090
+ const url = new URL(window.location.href);
13091
+ url.searchParams.delete("payment_intent");
13092
+ url.searchParams.delete("payment_intent_client_secret");
13093
+ url.searchParams.delete("redirect_status");
13094
+ window.history.replaceState({}, "", url.toString());
13095
+ }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
12796
13096
  }
12797
13097
  if (viewMode === "button" && (isSingleEventTypeMode || isDirectInstanceMode)) {
12798
13098
  // Button mode - show button that opens sidebar/booking directly
12799
- return (jsx(StyleProvider, { config: config, children: jsxs("div", { style: {
12800
- display: "flex",
12801
- justifyContent: "center",
12802
- alignItems: "center",
12803
- minHeight: "120px",
12804
- }, children: [jsx("button", { type: "button", style: {
12805
- backgroundColor: "var(--bw-highlight-color)",
12806
- color: "white",
12807
- padding: "16px 32px",
12808
- border: "none",
12809
- borderRadius: "var(--bw-border-radius)",
12810
- fontSize: "18px",
12811
- fontWeight: 600,
12812
- fontFamily: "var(--bw-font-family)",
12813
- boxShadow: "var(--bw-shadow-md)",
12814
- cursor: "pointer",
12815
- }, onClick: () => {
12816
- if (isDirectInstanceMode) {
12817
- setCurrentStep("booking");
12818
- setShouldRenderBookingForm(true);
12819
- }
12820
- else {
12821
- setSidebarOpen(true);
12822
- setShouldRenderInstanceSelection(true);
12823
- }
12824
- }, children: config.buttonText ||
12825
- (isDirectInstanceMode ? "Jetzt buchen" : "Jetzt Termin auswählen") }), shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen, onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => setCurrentStep(isDirectInstanceMode ? "eventTypes" : "eventInstances"), onBackToEventTypes: () => setSidebarOpen(false), selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => setCurrentStep(isDirectInstanceMode ? "eventTypes" : "eventInstances"), systemConfig: systemConfig })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
12826
- setIsSuccess(false);
12827
- setCurrentStep("eventTypes");
12828
- setSidebarOpen(false);
12829
- // Reset state
12830
- setSuccessPaymentIntentId(null);
12831
- // Reset lazy loading flags
12832
- setShouldRenderInstanceSelection(false);
12833
- setShouldRenderBookingForm(false);
12834
- // Clean up URL to remove Stripe parameters
12835
- const url = new URL(window.location.href);
12836
- url.searchParams.delete("payment_intent");
12837
- url.searchParams.delete("payment_intent_client_secret");
12838
- url.searchParams.delete("redirect_status");
12839
- window.history.replaceState({}, "", url.toString());
12840
- }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }) }));
13099
+ return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, style: {
13100
+ display: "flex",
13101
+ justifyContent: "center",
13102
+ alignItems: "center",
13103
+ minHeight: "120px",
13104
+ }, children: [jsx("button", { type: "button", style: {
13105
+ backgroundColor: "var(--bw-highlight-color)",
13106
+ color: "white",
13107
+ padding: "16px 32px",
13108
+ border: "none",
13109
+ borderRadius: "var(--bw-border-radius)",
13110
+ fontSize: "18px",
13111
+ fontWeight: 600,
13112
+ fontFamily: "var(--bw-font-family)",
13113
+ boxShadow: "var(--bw-shadow-md)",
13114
+ cursor: "pointer",
13115
+ }, onClick: () => {
13116
+ if (isDirectInstanceMode) {
13117
+ setCurrentStep("booking");
13118
+ setShouldRenderBookingForm(true);
13119
+ }
13120
+ else {
13121
+ setSidebarOpen(true);
13122
+ setShouldRenderInstanceSelection(true);
13123
+ }
13124
+ }, children: config.buttonText ||
13125
+ (isDirectInstanceMode ? "Jetzt buchen" : "Jetzt Termin auswählen") }), shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen, onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => setCurrentStep(isDirectInstanceMode ? "eventTypes" : "eventInstances"), onBackToEventTypes: () => setSidebarOpen(false), selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => setCurrentStep(isDirectInstanceMode ? "eventTypes" : "eventInstances"), systemConfig: systemConfig })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
13126
+ setIsSuccess(false);
13127
+ setCurrentStep("eventTypes");
13128
+ setSidebarOpen(false);
13129
+ // Reset state
13130
+ setSuccessPaymentIntentId(null);
13131
+ // Reset lazy loading flags
13132
+ setShouldRenderInstanceSelection(false);
13133
+ setShouldRenderBookingForm(false);
13134
+ // Clean up URL to remove Stripe parameters
13135
+ const url = new URL(window.location.href);
13136
+ url.searchParams.delete("payment_intent");
13137
+ url.searchParams.delete("payment_intent_client_secret");
13138
+ url.searchParams.delete("redirect_status");
13139
+ window.history.replaceState({}, "", url.toString());
13140
+ }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
12841
13141
  }
12842
13142
  // Cards mode (default) - show event type selection
12843
13143
  const cardsView = (jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount() }));
@@ -12870,21 +13170,21 @@ function UniversalBookingWidget({ config: baseConfig }) {
12870
13170
  };
12871
13171
  };
12872
13172
  const backHandlers = getBackHandlers();
12873
- return (jsxs(StyleProvider, { config: config, children: [cardsView, shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: backHandlers.onBackToEventInstances, onBackToEventTypes: backHandlers.onBackToEventTypes, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
12874
- setIsSuccess(false);
12875
- setCurrentStep("eventTypes");
12876
- // Reset state
12877
- setSuccessPaymentIntentId(null);
12878
- // Reset lazy loading flags
12879
- setShouldRenderInstanceSelection(false);
12880
- setShouldRenderBookingForm(false);
12881
- // Clean up URL to remove Stripe parameters
12882
- const url = new URL(window.location.href);
12883
- url.searchParams.delete("payment_intent");
12884
- url.searchParams.delete("payment_intent_client_secret");
12885
- url.searchParams.delete("redirect_status");
12886
- window.history.replaceState({}, "", url.toString());
12887
- }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }));
13173
+ return (jsxs(StyleProvider, { config: config, children: [jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: backHandlers.onBackToEventInstances, onBackToEventTypes: backHandlers.onBackToEventTypes, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: backHandlers.onClose, systemConfig: systemConfig })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
13174
+ setIsSuccess(false);
13175
+ setCurrentStep("eventTypes");
13176
+ // Reset state
13177
+ setSuccessPaymentIntentId(null);
13178
+ // Reset lazy loading flags
13179
+ setShouldRenderInstanceSelection(false);
13180
+ setShouldRenderBookingForm(false);
13181
+ // Clean up URL to remove Stripe parameters
13182
+ const url = new URL(window.location.href);
13183
+ url.searchParams.delete("payment_intent");
13184
+ url.searchParams.delete("payment_intent_client_secret");
13185
+ url.searchParams.delete("redirect_status");
13186
+ window.history.replaceState({}, "", url.toString());
13187
+ }, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
12888
13188
  }
12889
13189
 
12890
13190
  // Export init function for vanilla JS usage