@bigz-app/booking-widget 1.4.2 → 1.4.3

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.
@@ -14258,7 +14258,7 @@
14258
14258
  padding: "2px 8px",
14259
14259
  borderRadius: "var(--bw-border-radius)",
14260
14260
  fontFamily: "var(--bw-font-family)",
14261
- }, children: eventType.category.name }) }), u$2("div", { className: "event-type-img", style: { position: "relative", width: "100%", height: "300px" }, children: u$2(ImageCarousel, { images: eventType.images, eventName: eventType.name }) }), u$2("div", { className: "event-type-content", style: {
14261
+ }, children: eventType.category.name }) }), eventType.images && eventType.images.length > 0 && (u$2("div", { className: "event-type-img", style: { position: "relative", width: "100%", height: "300px" }, children: u$2(ImageCarousel, { images: eventType.images, eventName: eventType.name }) })), u$2("div", { className: "event-type-content", style: {
14262
14262
  padding: "12px 18px",
14263
14263
  display: "flex",
14264
14264
  flexDirection: "column",
@@ -14401,8 +14401,7 @@
14401
14401
  alignItems: "center",
14402
14402
  marginTop: "10px",
14403
14403
  gap: "12px",
14404
- }, children: [(eventType.description ||
14405
- (eventType.highlights && eventType.highlights.length > 0)) && (u$2("button", { onClick: (e) => {
14404
+ }, children: [eventType.description && (u$2("button", { onClick: (e) => {
14406
14405
  e.stopPropagation();
14407
14406
  handleShowDetails(eventType);
14408
14407
  }, style: {
@@ -14966,55 +14965,44 @@
14966
14965
  gap: "8px",
14967
14966
  }, children: [u$2("span", { style: { fontSize: "16px" }, children: "\uD83D\uDD04" }), t("common.reload")] })] }) }));
14968
14967
  }
14968
+ const today = new Date();
14969
14969
  return (u$2("div", { style: {
14970
14970
  maxWidth: "500px",
14971
14971
  margin: "0 auto",
14972
14972
  padding: "12px 16px",
14973
14973
  fontFamily: "var(--bw-font-family)",
14974
- }, children: [u$2("div", { style: { display: "flex", flexDirection: "column", gap: "4px", marginBottom: "10px" }, children: events.map((event) => {
14974
+ }, children: [u$2("div", { style: { display: "flex", flexDirection: "column", gap: "12px", marginBottom: "10px" }, children: events.map((event) => {
14975
14975
  const availableSpots = event.maxParticipants - event.participantCount;
14976
14976
  const isFullyBooked = availableSpots === 0;
14977
14977
  const startDate = new Date(event.startTime);
14978
- const today = new Date();
14979
14978
  const isPastEvent = today.toISOString() >= startDate.toISOString();
14980
- const isFullAvailability = availableSpots / event.maxParticipants >= 0.8;
14981
- const isHighAvailability = availableSpots / event.maxParticipants >= 0.5;
14982
- const isMediumAvailability = availableSpots / event.maxParticipants >= 0.3;
14983
- const isLowAvailability = availableSpots / event.maxParticipants >= 0.15;
14984
- const getAvailabilityColor = () => {
14985
- if (isFullAvailability)
14986
- return "var(--bw-success-color)";
14987
- if (isHighAvailability)
14988
- return "var(--bw-warning-color)";
14989
- if (isMediumAvailability)
14990
- return "var(--bw-availability-medium-color, #f97316)";
14991
- if (isLowAvailability)
14992
- return "var(--bw-availability-low-color, #8b5cf6)";
14993
- return "var(--bw-highlight-color)";
14994
- };
14995
14979
  const isDisabled = isFullyBooked || isPastEvent || !event.bookingOpen;
14980
+ const availabilityRatio = availableSpots / event.maxParticipants;
14981
+ const allocationBadge = (() => {
14982
+ if (availabilityRatio >= 0.6)
14983
+ return null;
14984
+ if (availabilityRatio === 0)
14985
+ return { text: t("events.soldOut"), backgroundColor: "#7f1d1d", textColor: "#fca5a5" };
14986
+ if (availabilityRatio <= 0.3)
14987
+ return { text: t("instances.almostSoldOut"), backgroundColor: "#7f1d1d", textColor: "#fca5a5" };
14988
+ return { text: t("instances.popularDate"), backgroundColor: "#b45309", textColor: "#fbbf24" };
14989
+ })();
14990
+ const sameDay = formatWeekday(event.startTime, timezone, locale) === formatWeekday(event.endTime, timezone, locale);
14996
14991
  return (u$2("div", { style: {
14997
14992
  position: "relative",
14998
- borderRadius: "var(--bw-border-radius-small)",
14999
- padding: "0 4px",
14993
+ border: "1px solid var(--bw-border-color)",
14994
+ backgroundColor: "var(--bw-surface-color)",
14995
+ borderRadius: "var(--bw-border-radius)",
14996
+ padding: "16px 10px",
15000
14997
  transition: "all 0.2s ease",
15001
- border: "1px solid transparent",
15002
14998
  fontFamily: "var(--bw-font-family)",
15003
14999
  opacity: isDisabled ? 0.3 : 1,
15004
- filter: isDisabled ? "grayscale(100%)" : "none",
15000
+ filter: isDisabled ? "grayscale(40%)" : "none",
15005
15001
  cursor: isDisabled ? "not-allowed" : "pointer",
15006
15002
  }, onClick: () => {
15007
15003
  if (!isDisabled) {
15008
15004
  handleEventSelect(event.id);
15009
15005
  }
15010
- }, onMouseEnter: (hoverEvent) => {
15011
- if (!isDisabled) {
15012
- hoverEvent.currentTarget.style.backgroundColor = "var(--bw-surface-color)";
15013
- hoverEvent.currentTarget.style.borderColor = getAvailabilityColor();
15014
- }
15015
- }, onMouseLeave: (hoverEvent) => {
15016
- hoverEvent.currentTarget.style.backgroundColor = "transparent";
15017
- hoverEvent.currentTarget.style.borderColor = "transparent";
15018
15006
  }, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (u$2("div", { style: {
15019
15007
  position: "absolute",
15020
15008
  inset: 0,
@@ -15023,37 +15011,71 @@
15023
15011
  justifyContent: "center",
15024
15012
  backgroundColor: "rgba(15, 23, 42, 0.8)",
15025
15013
  borderRadius: "var(--bw-border-radius)",
15026
- }, children: u$2("div", { style: {
15027
- width: "32px",
15028
- height: "32px",
15029
- color: "var(--bw-highlight-color)",
15030
- fontSize: "32px",
15031
- animation: "spin 1s linear infinite",
15032
- }, children: "\u27F3" }) })), u$2("div", { style: {
15033
- minHeight: "32px",
15034
- display: "grid",
15035
- gridTemplateColumns: "1fr auto 20px",
15036
- alignItems: "center",
15037
- gap: "8px",
15014
+ }, children: u$2("div", { style: { width: "32px", height: "32px", color: "var(--bw-highlight-color)", opacity: 0.8, fontSize: "32px" }, children: "\u27F3" }) })), u$2("div", { style: {
15015
+ display: "flex",
15016
+ marginLeft: "auto",
15017
+ marginTop: "-20px",
15018
+ marginBottom: "4px",
15019
+ borderRadius: "var(--bw-border-radius-small)",
15020
+ fontFamily: "var(--bw-font-family)",
15021
+ zIndex: 50,
15022
+ whiteSpace: "nowrap",
15023
+ width: "fit-content",
15024
+ fontSize: "11px",
15025
+ fontWeight: 700,
15026
+ padding: "2px 8px",
15027
+ backgroundColor: allocationBadge?.backgroundColor || "transparent",
15028
+ color: allocationBadge?.textColor || "transparent",
15029
+ }, children: allocationBadge?.text || " - " }), u$2("div", { style: {
15030
+ display: "flex",
15031
+ justifyContent: "space-between",
15038
15032
  width: "100%",
15039
- fontSize: "13px",
15040
- color: "var(--bw-text-muted)",
15041
- }, children: [u$2("span", { style: {
15042
- whiteSpace: "nowrap",
15043
- display: "flex",
15044
- alignItems: "center",
15045
- gap: "3px",
15046
- overflow: "hidden",
15047
- textOverflow: "ellipsis",
15048
- textTransform: "capitalize",
15049
- }, children: [formatWeekday(event.startTime, timezone, locale), " ", formatDate(event.startTime, timezone, locale)] }), u$2("span", { style: {
15050
- display: "flex",
15051
- alignItems: "center",
15052
- gap: "4px",
15053
- whiteSpace: "nowrap",
15054
- fontWeight: 700,
15055
- color: "var(--bw-text-color)",
15056
- }, children: event.price !== null ? formatCurrency(event.price) : t("nextEvents.priceOnRequest") }), event.notes ? u$2(InfoBadge, { text: event.notes }) : u$2("span", {})] })] }, event.id));
15033
+ alignItems: "flex-start",
15034
+ gap: "12px",
15035
+ marginBottom: "4px",
15036
+ }, children: [u$2("div", { style: { display: "flex", alignItems: "flex-start", gap: "12px" }, children: [u$2("div", { style: {
15037
+ fontSize: "16px",
15038
+ transition: "all 0.2s ease",
15039
+ borderRadius: "var(--bw-border-radius-small)",
15040
+ borderTop: "4px solid var(--bw-border-color)",
15041
+ border: "1px solid var(--bw-border-color)",
15042
+ width: "40px",
15043
+ height: "40px",
15044
+ display: "flex",
15045
+ alignItems: "center",
15046
+ justifyContent: "center",
15047
+ fontWeight: 700,
15048
+ color: "var(--bw-text-color)",
15049
+ backgroundColor: "var(--bw-background-color)",
15050
+ }, children: startDate.getDate() }), u$2("div", { style: {
15051
+ fontSize: "16px",
15052
+ color: "var(--bw-text-color)",
15053
+ display: "flex",
15054
+ flexDirection: "column",
15055
+ alignItems: "flex-start",
15056
+ justifyContent: "flex-start",
15057
+ lineHeight: 1.25,
15058
+ }, children: [u$2("div", { children: [u$2("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.startTime, timezone, locale) }), !sameDay && (u$2(k$3, { children: [u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), u$2("span", { style: { fontWeight: 600, marginBottom: "2px", textTransform: "capitalize" }, children: formatWeekday(event.endTime, timezone, locale) })] }))] }), u$2("div", { children: sameDay ? (u$2(k$3, { children: [u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.startTime, timezone, locale) }), u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: " - " }), u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: formatTime(event.endTime, timezone, locale) })] })) : (u$2("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime, timezone, locale), " ", t("instances.oclock")] })) })] }), u$2("span", { style: {
15059
+ fontSize: "12px",
15060
+ fontWeight: 400,
15061
+ color: "var(--bw-text-muted)",
15062
+ marginLeft: "6px",
15063
+ backgroundColor: "rgba(0, 0, 0, 0.05)",
15064
+ whiteSpace: "nowrap",
15065
+ padding: "2px 6px",
15066
+ borderRadius: "var(--bw-border-radius-small)",
15067
+ }, children: [event.durationDays, " ", event.durationDays > 1 ? t("common.days") : t("common.day")] })] }), u$2("div", { style: { textAlign: "right", display: "flex", flexDirection: "column", alignItems: "flex-end", gap: "4px" }, children: [u$2("span", { style: {
15068
+ display: "inline-flex",
15069
+ alignItems: "center",
15070
+ fontSize: "16px",
15071
+ fontWeight: 600,
15072
+ padding: "2px 8px",
15073
+ borderRadius: "var(--bw-border-radius-small)",
15074
+ fontFamily: "var(--bw-font-family)",
15075
+ backgroundColor: "var(--bw-background-color)",
15076
+ color: "var(--bw-text-color)",
15077
+ border: "1px solid var(--bw-border-color)",
15078
+ }, children: event.price !== null ? formatCurrency(event.price) : t("nextEvents.priceOnRequest") }), event.notes && u$2(InfoBadge, { text: event.notes })] })] })] }, event.id));
15057
15079
  }) }), showAllButton && (u$2("div", { style: { textAlign: "center" }, children: u$2("button", { type: "button", disabled: isLoadingShowAll, style: {
15058
15080
  display: "inline-flex",
15059
15081
  alignItems: "center",
@@ -15874,6 +15896,8 @@
15874
15896
  const [isSuccess, setIsSuccess] = d$1(false);
15875
15897
  const [successPaymentId, setSuccessPaymentId] = d$1(null);
15876
15898
  const [systemConfig, setSystemConfig] = d$1(null);
15899
+ // When true, loadEventInstances skips the single-instance auto-select (used by handleShowAllEvents)
15900
+ const skipInstanceAutoSelectRef = A$2(false);
15877
15901
  // PERFORMANCE OPTIMIZATION: Lazy component loading
15878
15902
  const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = d$1(false);
15879
15903
  const [shouldRenderUpsells, setShouldRenderUpsells] = d$1(false);
@@ -16369,7 +16393,7 @@
16369
16393
  }
16370
16394
  setStripePromise(loadStripe(data.stripePublishableKey, stripeOptions));
16371
16395
  }
16372
- if (data.eventInstances.length === 1) {
16396
+ if (!skipInstanceAutoSelectRef.current && data.eventInstances.length === 1) {
16373
16397
  setSelectedEventInstance(data.eventInstances[0]);
16374
16398
  setCurrentStep("booking");
16375
16399
  await loadEventDetails(data.eventInstances[0].id);
@@ -16764,12 +16788,30 @@
16764
16788
  const handleShowAllEvents = async () => {
16765
16789
  setIsLoadingShowAll(true);
16766
16790
  try {
16767
- if (isSingleEventTypeMode && selectedEventType) {
16791
+ if (isSingleEventTypeMode) {
16792
+ // Lock in the correct render branch before any async work so no
16793
+ // intermediate state can accidentally show the cards view.
16768
16794
  setShouldRenderInstanceSelection(true);
16769
- setIsLoadingEventInstances(true);
16770
- await loadEventInstances(selectedEventType.id);
16771
16795
  setCurrentStep("eventInstances");
16772
16796
  setShowingPreview(false);
16797
+ setIsLoadingEventInstances(true);
16798
+ // Suppress the single-instance auto-select inside loadEventInstances
16799
+ // so it doesn't override currentStep to "booking" mid-flight.
16800
+ skipInstanceAutoSelectRef.current = true;
16801
+ try {
16802
+ if (selectedEventType) {
16803
+ await loadEventInstances(selectedEventType.id);
16804
+ }
16805
+ else {
16806
+ // next-events mode skips event type loading on init.
16807
+ // loadEventTypes sets selectedEventType (for the sidebar title)
16808
+ // and internally calls loadEventInstances (auto-select suppressed).
16809
+ await loadEventTypes();
16810
+ }
16811
+ }
16812
+ finally {
16813
+ skipInstanceAutoSelectRef.current = false;
16814
+ }
16773
16815
  setIsLoadingEventInstances(false);
16774
16816
  }
16775
16817
  else {
@@ -16932,12 +16974,17 @@
16932
16974
  }, config: config, googleAdsConfig: googleAdsConfig, onError: setError, paymentIntentId: successPaymentId })] }), showPromoDialog && config.promo && (u$2(PromoDialog, { config: config.promo, onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
16933
16975
  }
16934
16976
  if (viewMode === "button" && (isSingleEventTypeMode || isDirectInstanceMode)) {
16977
+ const isButtonBusy = (isDirectInstanceMode && isLoadingEventDetails) ||
16978
+ (!isDirectInstanceMode && (isLoadingEventInstances || (shouldRenderInstanceSelection && !sidebarOpen)));
16935
16979
  return (u$2(StyleProvider, { config: config, children: [u$2("div", { ref: setWidgetContainerRef, style: {
16936
16980
  display: "flex",
16937
16981
  justifyContent: "center",
16938
16982
  alignItems: "center",
16939
16983
  minHeight: "120px",
16940
- }, children: [u$2("button", { type: "button", style: {
16984
+ }, children: [u$2("button", { type: "button", disabled: isButtonBusy, style: {
16985
+ display: "inline-flex",
16986
+ alignItems: "center",
16987
+ gap: "10px",
16941
16988
  backgroundColor: "var(--bw-highlight-color)",
16942
16989
  color: "white",
16943
16990
  padding: "16px 32px",
@@ -16947,7 +16994,9 @@
16947
16994
  fontWeight: 600,
16948
16995
  fontFamily: "var(--bw-font-family)",
16949
16996
  boxShadow: "var(--bw-shadow-md)",
16950
- cursor: "pointer",
16997
+ cursor: isButtonBusy ? "default" : "pointer",
16998
+ opacity: isButtonBusy ? 0.85 : 1,
16999
+ transition: "opacity 0.2s ease",
16951
17000
  }, onClick: () => {
16952
17001
  if (isDirectInstanceMode) {
16953
17002
  setCurrentStep("booking");
@@ -16958,8 +17007,8 @@
16958
17007
  setSidebarOpen(true);
16959
17008
  setShouldRenderInstanceSelection(true);
16960
17009
  }
16961
- }, children: config.buttonText ||
16962
- (isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates")) }), shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (u$2(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
17010
+ }, children: [isButtonBusy && u$2(Spinner, { size: 20, borderColor: "white" }), config.buttonText ||
17011
+ (isDirectInstanceMode ? t("button.bookNow") : t("button.viewDates"))] }), shouldRenderInstanceSelection && (u$2(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails, hasUpsellsStep: hasUpsellsFlowStep })), shouldRenderUpsells && (u$2(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), shouldRenderBookingForm && eventDetails && (u$2(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, isOpen: currentStep === "booking" && !!eventDetails, onClose: handleBackFromBooking, systemConfig: systemConfig, selectedUpsells: selectedUpsells, upsells: upsells, persistedState: bookingPersistedState, onPersistedStateChange: setBookingPersistedState })), u$2(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
16963
17012
  setIsSuccess(false);
16964
17013
  setCurrentStep("eventTypes");
16965
17014
  setSidebarOpen(false);