@bigz-app/booking-widget 1.1.5 → 1.1.7
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/booking-widget.js +59 -19
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
- package/dist/components/booking/BookingForm.d.ts +2 -0
- package/dist/components/booking/BookingForm.d.ts.map +1 -1
- package/dist/components/booking/BookingSuccessModal.d.ts.map +1 -1
- package/dist/components/booking/PaymentForm.d.ts.map +1 -1
- package/dist/components/upsells/UpsellCard.d.ts +3 -2
- package/dist/components/upsells/UpsellCard.d.ts.map +1 -1
- package/dist/components/upsells/UpsellsStep.d.ts +4 -1
- package/dist/components/upsells/UpsellsStep.d.ts.map +1 -1
- package/dist/components/upsells/index.d.ts +1 -1
- package/dist/components/upsells/index.d.ts.map +1 -1
- package/dist/i18n/i18n-context.d.ts.map +1 -1
- package/dist/i18n/locales/de.d.ts.map +1 -1
- package/dist/i18n/locales/en.d.ts.map +1 -1
- package/dist/i18n/locales/es.d.ts.map +1 -1
- package/dist/i18n/locales/pt.d.ts.map +1 -1
- package/dist/i18n/locales/sv.d.ts.map +1 -1
- package/dist/index.cjs +59 -19
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +59 -19
- package/dist/index.esm.js.map +1 -1
- package/package.json +1 -1
package/dist/index.esm.js
CHANGED
|
@@ -334,6 +334,7 @@ const de$1 = {
|
|
|
334
334
|
"success.age": "{{age}} Jahre",
|
|
335
335
|
"success.paymentSummary": "Zahlungsübersicht",
|
|
336
336
|
"success.total": "Gesamtbetrag",
|
|
337
|
+
"success.discount": "Rabatt",
|
|
337
338
|
"success.product": "Produkt",
|
|
338
339
|
"success.contactInfo": "Kontaktdaten",
|
|
339
340
|
"success.name": "Name:",
|
|
@@ -542,6 +543,7 @@ const en = {
|
|
|
542
543
|
"success.age": "{{age}} years",
|
|
543
544
|
"success.paymentSummary": "Payment Summary",
|
|
544
545
|
"success.total": "Total",
|
|
546
|
+
"success.discount": "Discount",
|
|
545
547
|
"success.product": "Product",
|
|
546
548
|
"success.contactInfo": "Contact Information",
|
|
547
549
|
"success.name": "Name:",
|
|
@@ -750,6 +752,7 @@ const es = {
|
|
|
750
752
|
"success.age": "{{age}} años",
|
|
751
753
|
"success.paymentSummary": "Resumen del pago",
|
|
752
754
|
"success.total": "Total",
|
|
755
|
+
"success.discount": "Descuento",
|
|
753
756
|
"success.product": "Producto",
|
|
754
757
|
"success.contactInfo": "Datos de contacto",
|
|
755
758
|
"success.name": "Nombre:",
|
|
@@ -958,6 +961,7 @@ const pt = {
|
|
|
958
961
|
"success.age": "{{age}} anos",
|
|
959
962
|
"success.paymentSummary": "Resumo do pagamento",
|
|
960
963
|
"success.total": "Total",
|
|
964
|
+
"success.discount": "Desconto",
|
|
961
965
|
"success.product": "Produto",
|
|
962
966
|
"success.contactInfo": "Dados de contacto",
|
|
963
967
|
"success.name": "Nome:",
|
|
@@ -1166,6 +1170,7 @@ const sv = {
|
|
|
1166
1170
|
"success.age": "{{age}} år",
|
|
1167
1171
|
"success.paymentSummary": "Betalningssammanfattning",
|
|
1168
1172
|
"success.total": "Totalt",
|
|
1173
|
+
"success.discount": "Rabatt",
|
|
1169
1174
|
"success.product": "Produkt",
|
|
1170
1175
|
"success.contactInfo": "Kontaktuppgifter",
|
|
1171
1176
|
"success.name": "Namn:",
|
|
@@ -1292,7 +1297,7 @@ function persistLocale(locale) {
|
|
|
1292
1297
|
const I18nContext = createContext(null);
|
|
1293
1298
|
function I18nProvider({ configLocale, children }) {
|
|
1294
1299
|
// Priority: configLocale (site owner) > persisted user choice > browser language > "de"
|
|
1295
|
-
// If configLocale is set, the site owner has locked the language
|
|
1300
|
+
// If configLocale is set, the site owner has locked the language - don't restore user choice.
|
|
1296
1301
|
const [overrideLocale, setOverrideLocale] = useState(() => {
|
|
1297
1302
|
if (configLocale)
|
|
1298
1303
|
return null;
|
|
@@ -6479,6 +6484,7 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
|
|
|
6479
6484
|
formData.participants,
|
|
6480
6485
|
formData.customerEmail,
|
|
6481
6486
|
formData.customerName,
|
|
6487
|
+
formData.comment,
|
|
6482
6488
|
totalAmount,
|
|
6483
6489
|
discountCode,
|
|
6484
6490
|
giftCards,
|
|
@@ -11001,6 +11007,13 @@ function mergeStyles(...styles) {
|
|
|
11001
11007
|
return Object.assign({}, ...styles.filter(Boolean));
|
|
11002
11008
|
}
|
|
11003
11009
|
|
|
11010
|
+
function getEffectivePrice(upsell, round) {
|
|
11011
|
+
const dp = upsell.discountPercent ?? 0;
|
|
11012
|
+
if (dp <= 0)
|
|
11013
|
+
return upsell.price;
|
|
11014
|
+
const raw = Math.round(upsell.price * (100 - dp) / 100);
|
|
11015
|
+
return round ? Math.floor(raw / 100) * 100 : raw;
|
|
11016
|
+
}
|
|
11004
11017
|
// Local style aliases from shared styles
|
|
11005
11018
|
const cardStyles = cardStyles$1.base;
|
|
11006
11019
|
const sectionHeaderStyles = sectionStyles.header;
|
|
@@ -11011,6 +11024,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11011
11024
|
const t$1 = useTranslations();
|
|
11012
11025
|
const { locale } = useLocale();
|
|
11013
11026
|
const timezone = useTimezone();
|
|
11027
|
+
const roundEnabled = systemConfig?.roundPricesEnabled !== false;
|
|
11014
11028
|
const [appliedVouchers, setAppliedVouchers] = useState([]);
|
|
11015
11029
|
const paymentSectionRef = useRef(null);
|
|
11016
11030
|
// Payment option: "deposit" or "full" - only relevant when deposit is available
|
|
@@ -11032,6 +11046,8 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11032
11046
|
const watchedParticipants = form.watch("participants");
|
|
11033
11047
|
const watchedCustomerName = form.watch("customerName");
|
|
11034
11048
|
const watchedCustomerEmail = form.watch("customerEmail");
|
|
11049
|
+
const watchedComment = form.watch("comment");
|
|
11050
|
+
const watchedCustomerPhone = form.watch("customerPhone");
|
|
11035
11051
|
const customerNameError = form.formState.errors.customerName;
|
|
11036
11052
|
const customerEmailError = form.formState.errors.customerEmail;
|
|
11037
11053
|
const watchedAcceptTerms = form.watch("acceptTerms");
|
|
@@ -11081,7 +11097,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11081
11097
|
participantUpsellIds.forEach(upsellId => {
|
|
11082
11098
|
const upsell = upsells.find(u => u.id === upsellId);
|
|
11083
11099
|
if (upsell) {
|
|
11084
|
-
total += upsell
|
|
11100
|
+
total += getEffectivePrice(upsell, roundEnabled);
|
|
11085
11101
|
}
|
|
11086
11102
|
});
|
|
11087
11103
|
}
|
|
@@ -11143,6 +11159,13 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11143
11159
|
participantIndices,
|
|
11144
11160
|
}));
|
|
11145
11161
|
}, [participantUpsells, watchedParticipants]);
|
|
11162
|
+
const paymentFormData = useMemo(() => ({
|
|
11163
|
+
customerName: watchedCustomerName,
|
|
11164
|
+
customerEmail: watchedCustomerEmail,
|
|
11165
|
+
customerPhone: watchedCustomerPhone,
|
|
11166
|
+
participants: watchedParticipants,
|
|
11167
|
+
comment: watchedComment,
|
|
11168
|
+
}), [watchedCustomerName, watchedCustomerEmail, watchedCustomerPhone, watchedParticipants, watchedComment]);
|
|
11146
11169
|
const appliedDiscountCode = appliedVouchers.find((v) => v.type === "discount");
|
|
11147
11170
|
const appliedGiftCards = appliedVouchers.filter((v) => v.type === "giftCard");
|
|
11148
11171
|
const handleVoucherValidated = useCallback((voucher, _error) => {
|
|
@@ -11357,7 +11380,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11357
11380
|
padding: 0,
|
|
11358
11381
|
}, children: "\u00D7" })] }))] }), upsells.length > 0 && (jsx("div", { style: participantUpsellStyles.container, children: upsells.map((upsell) => {
|
|
11359
11382
|
const isSelected = (participantUpsells[index] || []).includes(upsell.id);
|
|
11360
|
-
return (jsxs("label", { htmlFor: `upsell-${index}-${upsell.id}`, style: isSelected ? participantUpsellStyles.labelSelected : participantUpsellStyles.label, children: [jsx("input", { id: `upsell-${index}-${upsell.id}`, type: "checkbox", style: participantUpsellStyles.checkbox, checked: isSelected, onChange: () => toggleParticipantUpsell(index, upsell.id) }), jsx("span", { style: { fontWeight: 500 }, children: upsell.name }), jsxs("span", { style: { fontSize: "12px", opacity: 0.8 }, children: ["(+", formatCurrency(upsell
|
|
11383
|
+
return (jsxs("label", { htmlFor: `upsell-${index}-${upsell.id}`, style: isSelected ? participantUpsellStyles.labelSelected : participantUpsellStyles.label, children: [jsx("input", { id: `upsell-${index}-${upsell.id}`, type: "checkbox", style: participantUpsellStyles.checkbox, checked: isSelected, onChange: () => toggleParticipantUpsell(index, upsell.id) }), jsx("span", { style: { fontWeight: 500 }, children: upsell.name }), jsxs("span", { style: { fontSize: "12px", opacity: 0.8 }, children: ["(+", formatCurrency(getEffectivePrice(upsell, roundEnabled)), ")"] })] }, upsell.id));
|
|
11361
11384
|
}) }))] }, index))), watchedParticipants.length < eventDetails.availableSpots ? (jsx("div", { style: {
|
|
11362
11385
|
display: "flex",
|
|
11363
11386
|
flexDirection: "column",
|
|
@@ -11389,7 +11412,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11389
11412
|
const countWithUpsell = watchedParticipants.filter((p, idx) => p.name.trim() && (participantUpsells[idx] || []).includes(upsell.id)).length;
|
|
11390
11413
|
if (countWithUpsell === 0)
|
|
11391
11414
|
return null;
|
|
11392
|
-
const upsellLineTotal = upsell
|
|
11415
|
+
const upsellLineTotal = getEffectivePrice(upsell, roundEnabled) * countWithUpsell;
|
|
11393
11416
|
return (jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", fontSize: "13px" }, children: [jsxs("span", { style: { color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+ ", upsell.name, " (", countWithUpsell, "\u00D7)"] }), jsx("span", { style: { color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: formatCurrency(upsellLineTotal) })] }, upsell.id));
|
|
11394
11417
|
})] })), appliedVouchers.length > 0 && (jsxs(Fragment, { children: [jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsx("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)" }, children: t$1("summary.subtotal") }), jsx("span", { style: {
|
|
11395
11418
|
fontFamily: "var(--bw-font-family)",
|
|
@@ -11514,7 +11537,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11514
11537
|
: t$1("payment.fillRequired");
|
|
11515
11538
|
return (jsx("div", { style: {
|
|
11516
11539
|
...cardStyles,
|
|
11517
|
-
|
|
11540
|
+
border: "1px solid var(--bw-warning-color)",
|
|
11518
11541
|
color: "var(--bw-warning-color)",
|
|
11519
11542
|
fontFamily: "var(--bw-font-family)",
|
|
11520
11543
|
textAlign: "center",
|
|
@@ -11532,9 +11555,9 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
11532
11555
|
}
|
|
11533
11556
|
: null;
|
|
11534
11557
|
if (systemConfig?.paymentProvider === "mollie") {
|
|
11535
|
-
return (jsxs("div", { style: cardStyles, children: [jsx("h2", { style: { ...sectionHeaderStyles }, children: t$1("summary.payment") }), jsx(MolliePaymentForm, { config: config, eventDetails: eventDetails, formData:
|
|
11558
|
+
return (jsxs("div", { style: cardStyles, children: [jsx("h2", { style: { ...sectionHeaderStyles }, children: t$1("summary.payment") }), jsx(MolliePaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, upsellSelections: aggregatedUpsellSelections(), mollieProfileId: systemConfig?.mollieProfileId, mollieTestmode: systemConfig?.mollieTestmode })] }));
|
|
11536
11559
|
}
|
|
11537
|
-
return (jsxs("div", { style: cardStyles, children: [jsx("h2", { style: { ...sectionHeaderStyles }, children: t$1("summary.payment") }), jsx(PaymentForm, { config: config, eventDetails: eventDetails, formData:
|
|
11560
|
+
return (jsxs("div", { style: cardStyles, children: [jsx("h2", { style: { ...sectionHeaderStyles }, children: t$1("summary.payment") }), jsx(PaymentForm, { config: config, eventDetails: eventDetails, formData: paymentFormData, totalAmount: paymentAmount, discountCode: discountCodeProp, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig ?? null, stripePromise: stripePromise, stripeAppearance: stripeAppearance, upsellSelections: aggregatedUpsellSelections() })] }));
|
|
11538
11561
|
})() })] })] })] }) }));
|
|
11539
11562
|
}
|
|
11540
11563
|
|
|
@@ -11683,6 +11706,7 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
11683
11706
|
payments: data.payments,
|
|
11684
11707
|
orderItems: data.orderItems,
|
|
11685
11708
|
purchases: data.purchases || [],
|
|
11709
|
+
discount: data.discount || null,
|
|
11686
11710
|
stripePaymentIntent: data.stripePaymentIntent,
|
|
11687
11711
|
});
|
|
11688
11712
|
setEventDetails({
|
|
@@ -11932,7 +11956,7 @@ const BookingSuccessModal = ({ isOpen, onClose, config, onError, paymentIntentId
|
|
|
11932
11956
|
color: "var(--bw-text-color)",
|
|
11933
11957
|
margin: "0",
|
|
11934
11958
|
fontFamily: "var(--bw-font-family)",
|
|
11935
|
-
}, children: t("success.paymentSummary") }) }), jsx("div", { className: "print-only", children: jsx("div", { className: "print-section-title", children: t("success.paymentSummary") }) }), jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [jsxs("div", { className: "print-hidden", style: { display: "flex", flexDirection: "column", gap: "var(--bw-spacing-small)" }, children: [jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxs("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "var(--bw-font-size-small)" }, children: [eventDetails.name, " (", booking.participantCount, "x ", formatCurrency(eventDetails.price), ")"] }), jsx("span", { style: { color: "var(--bw-text-color)", fontFamily: "var(--bw-font-family)" }, children: formatCurrency(eventDetails.price * booking.participantCount) })] }), bookingData.purchases && bookingData.purchases.length > 0 && (jsx(Fragment, { children: bookingData.purchases.map((purchase) => (jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxs("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "var(--bw-font-size-small)" }, children: [purchase.product?.name || t("success.product"), " (", purchase.quantity, "x ", formatCurrency(purchase.unitPrice), ")"] }), jsx("span", { style: { color: "var(--bw-text-color)", fontFamily: "var(--bw-font-family)" }, children: formatCurrency(purchase.totalPrice) })] }, purchase.id))) })), jsxs("div", { style: {
|
|
11959
|
+
}, children: t("success.paymentSummary") }) }), jsx("div", { className: "print-only", children: jsx("div", { className: "print-section-title", children: t("success.paymentSummary") }) }), jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "8px" }, children: [jsxs("div", { className: "print-hidden", style: { display: "flex", flexDirection: "column", gap: "var(--bw-spacing-small)" }, children: [jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxs("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "var(--bw-font-size-small)" }, children: [eventDetails.name, " (", booking.participantCount, "x ", formatCurrency(eventDetails.price), ")"] }), jsx("span", { style: { color: "var(--bw-text-color)", fontFamily: "var(--bw-font-family)" }, children: formatCurrency(eventDetails.price * booking.participantCount) })] }), bookingData.discount && bookingData.discount.amount > 0 && (jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxs("span", { style: { color: "var(--bw-success-color, #10B981)", fontFamily: "var(--bw-font-family)", fontSize: "var(--bw-font-size-small)" }, children: [t("success.discount"), " (", bookingData.discount.code, ")"] }), jsxs("span", { style: { color: "var(--bw-success-color, #10B981)", fontFamily: "var(--bw-font-family)" }, children: ["-", formatCurrency(bookingData.discount.amount)] })] })), bookingData.purchases && bookingData.purchases.length > 0 && (jsx(Fragment, { children: bookingData.purchases.map((purchase) => (jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center" }, children: [jsxs("span", { style: { color: "var(--bw-text-muted)", fontFamily: "var(--bw-font-family)", fontSize: "var(--bw-font-size-small)" }, children: [purchase.product?.name || t("success.product"), " (", purchase.quantity, "x ", formatCurrency(purchase.unitPrice), ")"] }), jsx("span", { style: { color: "var(--bw-text-color)", fontFamily: "var(--bw-font-family)" }, children: formatCurrency(purchase.totalPrice) })] }, purchase.id))) })), jsxs("div", { style: {
|
|
11936
11960
|
display: "flex",
|
|
11937
11961
|
justifyContent: "space-between",
|
|
11938
11962
|
alignItems: "center",
|
|
@@ -13411,7 +13435,7 @@ function PromoDialog({ config, onClose, onCtaClick }) {
|
|
|
13411
13435
|
color: "rgba(255, 255, 255, 0.85)",
|
|
13412
13436
|
lineHeight: 1.5,
|
|
13413
13437
|
margin: "0 0 16px 0",
|
|
13414
|
-
}, children: [config.subtitle, config.discountAmount && (jsxs(Fragment, { children: ["
|
|
13438
|
+
}, children: [config.subtitle, config.discountAmount && (jsxs(Fragment, { children: [" - ", jsxs("strong", { style: { color: "white" }, children: [config.discountAmount, " ", t("promo.discount")] })] }))] }), config.discountCode && (jsxs("div", { style: {
|
|
13415
13439
|
marginBottom: "16px",
|
|
13416
13440
|
backgroundColor: "rgba(255,255,255,0.12)",
|
|
13417
13441
|
borderRadius: "12px",
|
|
@@ -13496,7 +13520,9 @@ const cardBaseStyles = {
|
|
|
13496
13520
|
flexDirection: "column",
|
|
13497
13521
|
padding: "16px",
|
|
13498
13522
|
backgroundColor: "var(--bw-surface-color)",
|
|
13499
|
-
|
|
13523
|
+
borderWidth: "2px",
|
|
13524
|
+
borderStyle: "solid",
|
|
13525
|
+
borderColor: "var(--bw-border-color)",
|
|
13500
13526
|
borderRadius: "var(--bw-border-radius)",
|
|
13501
13527
|
cursor: "pointer",
|
|
13502
13528
|
transition: "all 0.2s ease",
|
|
@@ -13521,7 +13547,9 @@ const checkboxContainerStyles = {
|
|
|
13521
13547
|
const checkboxInnerStyles = {
|
|
13522
13548
|
width: "24px",
|
|
13523
13549
|
height: "24px",
|
|
13524
|
-
|
|
13550
|
+
borderWidth: "2px",
|
|
13551
|
+
borderStyle: "solid",
|
|
13552
|
+
borderColor: "var(--bw-border-color)",
|
|
13525
13553
|
borderRadius: "6px",
|
|
13526
13554
|
display: "flex",
|
|
13527
13555
|
alignItems: "center",
|
|
@@ -13647,10 +13675,11 @@ function formatUnavailableReason(reason, t) {
|
|
|
13647
13675
|
return t("upsells.reason.notEnoughSpots", { eventTypeName: reason.eventTypeName });
|
|
13648
13676
|
}
|
|
13649
13677
|
}
|
|
13650
|
-
function UpsellCard({ upsell, isSelected, participantCount, onSelect, }) {
|
|
13678
|
+
function UpsellCard({ upsell, isSelected, participantCount, onSelect, roundPricesEnabled = true, }) {
|
|
13651
13679
|
const t = useTranslations();
|
|
13652
13680
|
const { locale } = useLocale();
|
|
13653
|
-
const
|
|
13681
|
+
const effectivePrice = getEffectiveUpsellPrice(upsell, roundPricesEnabled);
|
|
13682
|
+
const totalPrice = effectivePrice * participantCount;
|
|
13654
13683
|
const isDisabled = !upsell.available;
|
|
13655
13684
|
const getCardStyles = () => {
|
|
13656
13685
|
if (isDisabled)
|
|
@@ -13668,12 +13697,19 @@ function UpsellCard({ upsell, isSelected, participantCount, onSelect, }) {
|
|
|
13668
13697
|
weekday: "short",
|
|
13669
13698
|
day: "numeric",
|
|
13670
13699
|
month: "short",
|
|
13671
|
-
})] }), jsx("span", { style: { color: "var(--bw-text-muted)" }, children: t("upsells.spotsFree", { count: upsell.suggestedEventInstance.availableSpots }) })] }))] }), jsxs("div", { style: priceContainerStyles, children: [jsxs("span", { style: pricePerPersonStyles, children: [formatCurrency(
|
|
13700
|
+
})] }), jsx("span", { style: { color: "var(--bw-text-muted)" }, children: t("upsells.spotsFree", { count: upsell.suggestedEventInstance.availableSpots }) })] }))] }), jsxs("div", { style: priceContainerStyles, children: [jsxs("span", { style: pricePerPersonStyles, children: [formatCurrency(effectivePrice), "/", t("common.perPerson")] }), participantCount > 1 && (jsxs("span", { style: priceTotalStyles, children: ["= ", formatCurrency(totalPrice)] }))] }), isDisabled && (jsx("div", { style: unavailableOverlayStyles, children: jsx("span", { children: upsell.unavailableReason
|
|
13672
13701
|
? formatUnavailableReason(upsell.unavailableReason, t)
|
|
13673
13702
|
: t("upsells.notAvailable") }) }))] }));
|
|
13674
13703
|
}
|
|
13675
13704
|
|
|
13676
|
-
function
|
|
13705
|
+
function getEffectiveUpsellPrice(upsell, round = true) {
|
|
13706
|
+
const dp = upsell.discountPercent ?? 0;
|
|
13707
|
+
if (dp <= 0)
|
|
13708
|
+
return upsell.price;
|
|
13709
|
+
const raw = Math.round(upsell.price * (100 - dp) / 100);
|
|
13710
|
+
return round ? Math.floor(raw / 100) * 100 : raw;
|
|
13711
|
+
}
|
|
13712
|
+
function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, isOpen, onClose, onSelect, onContinue, onBack, roundPricesEnabled = true, }) {
|
|
13677
13713
|
const t = useTranslations();
|
|
13678
13714
|
const selectUpsell = (upsellId) => {
|
|
13679
13715
|
const exists = selectedUpsells.find((s) => s.upsellPackageId === upsellId);
|
|
@@ -13692,7 +13728,7 @@ function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, is
|
|
|
13692
13728
|
return selectedUpsells.reduce((total, selection) => {
|
|
13693
13729
|
const upsell = upsells.find((u) => u.id === selection.upsellPackageId);
|
|
13694
13730
|
if (upsell) {
|
|
13695
|
-
return total + upsell
|
|
13731
|
+
return total + getEffectiveUpsellPrice(upsell, roundPricesEnabled) * selection.quantity;
|
|
13696
13732
|
}
|
|
13697
13733
|
return total;
|
|
13698
13734
|
}, 0);
|
|
@@ -13700,7 +13736,7 @@ function UpsellsStep({ upsells, selectedUpsells, participantCount, isLoading, is
|
|
|
13700
13736
|
const selectedTotal = calculateTotal();
|
|
13701
13737
|
const selectedCount = selectedUpsells.length;
|
|
13702
13738
|
const footerContent = (jsxs(Fragment, { children: [jsx("button", { type: "button", onClick: onBack, style: mergeStyles(buttonStyles.secondary, buttonStyles.fullWidth), children: t("common.back") }), jsx("button", { type: "button", onClick: onContinue, style: mergeStyles(buttonStyles.primary, buttonStyles.fullWidth), children: selectedCount === 0 ? t("button.continueWithout") : t("button.continue") })] }));
|
|
13703
|
-
return (jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), jsx("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (jsx("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: jsx("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (jsx(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id) }, upsell.id))) })), selectedCount > 0 && (jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "16px", paddingBottom: "16px", paddingTop: "16px", borderTop: "1px solid var(--bw-border-color)", fontSize: "14px" }, children: [jsx("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), jsxs("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
|
|
13739
|
+
return (jsx(Sidebar, { isOpen: isOpen, onClose: onClose, title: t("upsells.title"), footer: footerContent, children: jsxs("div", { style: { display: "flex", flexDirection: "column", height: "100%", padding: "16px 16px" }, children: [isLoading && (jsxs("div", { style: { display: "flex", flexDirection: "column", alignItems: "center", justifyContent: "center", gap: "12px", padding: "40px 20px", ...textStyles.muted }, children: [spinner(), jsx("span", { children: t("upsells.loading") })] })), !isLoading && upsells.length === 0 && (jsx("div", { style: { textAlign: "center", padding: "40px 20px", ...textStyles.muted }, children: jsx("p", { children: t("upsells.noExtras") }) })), !isLoading && upsells.length > 0 && (jsx("div", { style: { display: "flex", flexDirection: "column", gap: "12px", flex: 1, overflowY: "auto", paddingBottom: "16px" }, children: upsells.map((upsell) => (jsx(UpsellCard, { upsell: upsell, isSelected: isSelected(upsell.id), participantCount: participantCount, onSelect: () => selectUpsell(upsell.id), roundPricesEnabled: roundPricesEnabled }, upsell.id))) })), selectedCount > 0 && (jsxs("div", { style: { display: "flex", justifyContent: "space-between", alignItems: "center", marginTop: "16px", paddingBottom: "16px", paddingTop: "16px", borderTop: "1px solid var(--bw-border-color)", fontSize: "14px" }, children: [jsx("span", { style: textStyles.muted, children: selectedCount === 1 ? t("upsells.selected", { count: selectedCount }) : t("upsells.selectedPlural", { count: selectedCount }) }), jsxs("span", { style: { fontWeight: 600, color: "var(--bw-highlight-color)", fontFamily: "var(--bw-font-family)" }, children: ["+", formatCurrency(selectedTotal)] })] }))] }) }));
|
|
13704
13740
|
}
|
|
13705
13741
|
|
|
13706
13742
|
// Main widget component
|
|
@@ -13960,6 +13996,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
13960
13996
|
connectedAccountId: data.connectedAccountId,
|
|
13961
13997
|
mollieProfileId: data.mollieProfileId,
|
|
13962
13998
|
mollieTestmode: data.mollieTestmode,
|
|
13999
|
+
roundPricesEnabled: data.roundPricesEnabled ?? true,
|
|
13963
14000
|
});
|
|
13964
14001
|
}
|
|
13965
14002
|
if (data.stripePublishableKey) {
|
|
@@ -14018,6 +14055,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
14018
14055
|
connectedAccountId: data.connectedAccountId,
|
|
14019
14056
|
mollieProfileId: data.mollieProfileId,
|
|
14020
14057
|
mollieTestmode: data.mollieTestmode,
|
|
14058
|
+
roundPricesEnabled: data.roundPricesEnabled ?? true,
|
|
14021
14059
|
});
|
|
14022
14060
|
if (!stripePromise && data.stripePublishableKey) {
|
|
14023
14061
|
const stripeOptions = {
|
|
@@ -14166,6 +14204,8 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
14166
14204
|
const handleBookingSuccess = (result) => {
|
|
14167
14205
|
setIsSuccess(true);
|
|
14168
14206
|
setSuccessPaymentIntentId(result.paymentIntent.id);
|
|
14207
|
+
setSidebarOpen(false);
|
|
14208
|
+
setShouldRenderBookingForm(false);
|
|
14169
14209
|
config.onSuccess?.(result);
|
|
14170
14210
|
};
|
|
14171
14211
|
const handleBookingError = (errorMessage) => {
|
|
@@ -14422,7 +14462,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
14422
14462
|
setShouldRenderInstanceSelection(true);
|
|
14423
14463
|
}
|
|
14424
14464
|
}, children: config.buttonText ||
|
|
14425
|
-
(isDirectInstanceMode ? t("button.bookNow") : t("button.selectDate")) }), shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderUpsells && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), 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, selectedUpsells: selectedUpsells, upsells: upsells })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
14465
|
+
(isDirectInstanceMode ? t("button.bookNow") : t("button.selectDate")) }), shouldRenderInstanceSelection && (jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen && currentStep === "eventInstances", onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderUpsells && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack, roundPricesEnabled: systemConfig?.roundPricesEnabled !== false })), 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, selectedUpsells: selectedUpsells, upsells: upsells })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
14426
14466
|
setIsSuccess(false);
|
|
14427
14467
|
setCurrentStep("eventTypes");
|
|
14428
14468
|
setSidebarOpen(false);
|
|
@@ -14469,7 +14509,7 @@ function UniversalBookingWidgetInner({ config: baseConfig, onWidgetLanguage, onT
|
|
|
14469
14509
|
};
|
|
14470
14510
|
};
|
|
14471
14511
|
const backHandlers = getBackHandlers();
|
|
14472
|
-
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 })), shouldRenderUpsells && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack })), 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, selectedUpsells: selectedUpsells, upsells: upsells })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
14512
|
+
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 })), shouldRenderUpsells && (jsx(UpsellsStep, { upsells: upsells, selectedUpsells: selectedUpsells, participantCount: tempParticipantCount, isLoading: isLoadingUpsells, isOpen: currentStep === "upsells", onClose: () => setCurrentStep("eventInstances"), onSelect: handleUpsellsSelect, onContinue: handleUpsellsContinue, onBack: handleUpsellsBack, roundPricesEnabled: systemConfig?.roundPricesEnabled !== false })), 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, selectedUpsells: selectedUpsells, upsells: upsells })), jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
14473
14513
|
setIsSuccess(false);
|
|
14474
14514
|
setCurrentStep("eventTypes");
|
|
14475
14515
|
setSuccessPaymentIntentId(null);
|