@bigz-app/booking-widget 0.3.6 → 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/booking-widget.js +1016 -300
- package/dist/booking-widget.js.map +1 -1
- package/dist/components/BookingForm.d.ts.map +1 -1
- package/dist/components/EventInstanceSelection.d.ts.map +1 -1
- package/dist/components/PaymentForm.d.ts +3 -1
- package/dist/components/PaymentForm.d.ts.map +1 -1
- package/dist/components/PromoDialog.d.ts +7 -0
- package/dist/components/PromoDialog.d.ts.map +1 -0
- package/dist/components/UniversalBookingWidget.d.ts.map +1 -1
- package/dist/components/VoucherInput.d.ts +29 -0
- package/dist/components/VoucherInput.d.ts.map +1 -0
- package/dist/index.cjs +1017 -301
- package/dist/index.cjs.map +1 -1
- package/dist/index.esm.js +1018 -302
- package/dist/index.esm.js.map +1 -1
- package/package.json +77 -70
package/dist/index.cjs
CHANGED
|
@@ -6908,7 +6908,7 @@ const preprocessMarkdown$1 = (markdown) => {
|
|
|
6908
6908
|
// Convert double underscores to HTML underline tags for React Markdown
|
|
6909
6909
|
return markdown.replace(/__([^_]+)__/g, "<u>$1</u>");
|
|
6910
6910
|
};
|
|
6911
|
-
const IconCheck$
|
|
6911
|
+
const IconCheck$2 = ({ size = 16, color = "#10b981" }) => (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
6912
6912
|
const IconWave$1 = ({ size = 20, color = "#0ea5e9" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M2 18c2-2 6-2 8 0s6 2 8 0 6-2 8 0" }), jsxRuntime.jsx("path", { d: "M2 12c2-2 6-2 8 0s6 2 8 0 6-2 8 0" }), jsxRuntime.jsx("path", { d: "M2 6c2-2 6-2 8 0s6 2 8 0 6-2 8 0" })] }));
|
|
6913
6913
|
function EventTypeDetailsDialog({ isOpen, onClose, eventType, onEventTypeSelect, }) {
|
|
6914
6914
|
if (!isOpen || !eventType)
|
|
@@ -6955,7 +6955,7 @@ function EventTypeDetailsDialog({ isOpen, onClose, eventType, onEventTypeSelect,
|
|
|
6955
6955
|
fontSize: "16px",
|
|
6956
6956
|
lineHeight: "1.6",
|
|
6957
6957
|
color: "var(--bw-text-color)",
|
|
6958
|
-
}, children: [jsxRuntime.jsx("div", { style: { marginTop: "4px", flexShrink: 0 }, children: jsxRuntime.jsx(IconCheck$
|
|
6958
|
+
}, children: [jsxRuntime.jsx("div", { style: { marginTop: "4px", flexShrink: 0 }, children: jsxRuntime.jsx(IconCheck$2, { size: 16, color: "var(--bw-success-color)" }) }), jsxRuntime.jsx("span", { children: highlight.trim() })] }, index))) }) }) }))] }), eventType.description && (jsxRuntime.jsxs("div", { style: {
|
|
6959
6959
|
marginBottom: "24px",
|
|
6960
6960
|
color: "var(--bw-text-muted)",
|
|
6961
6961
|
fontSize: "16px",
|
|
@@ -7264,7 +7264,7 @@ const preprocessMarkdown = (markdown) => {
|
|
|
7264
7264
|
// Custom minimal SVG icons (Lucide-style)
|
|
7265
7265
|
const IconClock = ({ size = 16, color = "#10b981" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }), jsxRuntime.jsx("polyline", { points: "12 6 12 12 16 14" })] }));
|
|
7266
7266
|
const IconCalendar = ({ size = 16, color = "#3b82f6" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("rect", { x: "3", y: "4", width: "18", height: "18", rx: "2" }), jsxRuntime.jsx("line", { x1: "16", y1: "2", x2: "16", y2: "6" }), jsxRuntime.jsx("line", { x1: "8", y1: "2", x2: "8", y2: "6" }), jsxRuntime.jsx("line", { x1: "3", y1: "10", x2: "21", y2: "10" })] }));
|
|
7267
|
-
const IconCheck = ({ size = 16, color = "#10b981" }) => (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
7267
|
+
const IconCheck$1 = ({ size = 16, color = "#10b981" }) => (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
7268
7268
|
// Wave icon for booking action
|
|
7269
7269
|
const IconWave = ({ size = 20, color = "#0ea5e9" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M2 18c2-2 6-2 8 0s6 2 8 0 6-2 8 0" }), jsxRuntime.jsx("path", { d: "M2 12c2-2 6-2 8 0s6 2 8 0 6-2 8 0" }), jsxRuntime.jsx("path", { d: "M2 6c2-2 6-2 8 0s6 2 8 0 6-2 8 0" })] }));
|
|
7270
7270
|
// Loading skeleton component that matches the actual design
|
|
@@ -7728,7 +7728,7 @@ function EventTypeSelection({ eventTypes, onEventTypeSelect, isLoading = false,
|
|
|
7728
7728
|
color: "var(--bw-text-muted)",
|
|
7729
7729
|
position: "relative",
|
|
7730
7730
|
maxWidth: "100%",
|
|
7731
|
-
}, children: [jsxRuntime.jsx("div", { style: { marginTop: "4px", flexShrink: 0 }, children: jsxRuntime.jsx(IconCheck, { size: 16, color: "var(--bw-success-color)" }) }), jsxRuntime.jsx("span", { style: {
|
|
7731
|
+
}, children: [jsxRuntime.jsx("div", { style: { marginTop: "4px", flexShrink: 0 }, children: jsxRuntime.jsx(IconCheck$1, { size: 16, color: "var(--bw-success-color)" }) }), jsxRuntime.jsx("span", { style: {
|
|
7732
7732
|
textOverflow: "ellipsis",
|
|
7733
7733
|
overflow: "hidden",
|
|
7734
7734
|
whiteSpace: "nowrap",
|
|
@@ -9131,6 +9131,110 @@ var reactStripe_umd = {exports: {}};
|
|
|
9131
9131
|
|
|
9132
9132
|
var reactStripe_umdExports = reactStripe_umd.exports;
|
|
9133
9133
|
|
|
9134
|
+
// Component for bookings fully covered by gift cards (no Stripe payment needed)
|
|
9135
|
+
function GiftCardOnlyBooking({ config, eventDetails, formData, discountCode, giftCards, onSuccess, onError, }) {
|
|
9136
|
+
const [isLoading, setIsLoading] = React__default.useState(false);
|
|
9137
|
+
const [error, setError] = React__default.useState(null);
|
|
9138
|
+
const handleBooking = async () => {
|
|
9139
|
+
setIsLoading(true);
|
|
9140
|
+
setError(null);
|
|
9141
|
+
try {
|
|
9142
|
+
// Create booking directly without Stripe payment
|
|
9143
|
+
const requestData = {
|
|
9144
|
+
eventInstanceId: config.eventInstanceId || eventDetails.id,
|
|
9145
|
+
organizationId: config.organizationId,
|
|
9146
|
+
participants: formData.participants.filter((p) => p.name?.trim()),
|
|
9147
|
+
discountCode: discountCode?.code,
|
|
9148
|
+
giftCardCodes: giftCards.map((gc) => gc.code),
|
|
9149
|
+
customerName: formData.customerName?.trim(),
|
|
9150
|
+
customerEmail: formData.customerEmail?.trim(),
|
|
9151
|
+
customerPhone: formData.customerPhone?.trim(),
|
|
9152
|
+
comment: formData.comment?.trim(),
|
|
9153
|
+
paymentMethod: "gift_card",
|
|
9154
|
+
};
|
|
9155
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/create-gift-card-booking"), {
|
|
9156
|
+
method: "POST",
|
|
9157
|
+
headers: createApiHeaders(config),
|
|
9158
|
+
body: JSON.stringify(createRequestBody(config, requestData)),
|
|
9159
|
+
});
|
|
9160
|
+
const data = await response.json();
|
|
9161
|
+
if (response.ok) {
|
|
9162
|
+
onSuccess({
|
|
9163
|
+
booking: data.booking,
|
|
9164
|
+
order: data.order,
|
|
9165
|
+
giftCardRedemptions: data.giftCardRedemptions,
|
|
9166
|
+
});
|
|
9167
|
+
}
|
|
9168
|
+
else {
|
|
9169
|
+
setError(data.error || "Fehler beim Erstellen der Buchung");
|
|
9170
|
+
onError(data.error || "Fehler beim Erstellen der Buchung");
|
|
9171
|
+
}
|
|
9172
|
+
}
|
|
9173
|
+
catch (err) {
|
|
9174
|
+
setError(err.message || "Fehler beim Erstellen der Buchung");
|
|
9175
|
+
onError(err.message || "Fehler beim Erstellen der Buchung");
|
|
9176
|
+
}
|
|
9177
|
+
finally {
|
|
9178
|
+
setIsLoading(false);
|
|
9179
|
+
}
|
|
9180
|
+
};
|
|
9181
|
+
const totalGiftCardAmount = giftCards.reduce((sum, gc) => sum + (gc.balanceToUse || gc.discountAmount || 0), 0);
|
|
9182
|
+
return (jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "var(--bw-spacing)" }, children: [jsxRuntime.jsxs("div", { style: {
|
|
9183
|
+
backgroundColor: "var(--bw-success-color)15",
|
|
9184
|
+
border: "1px solid var(--bw-success-color)40",
|
|
9185
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9186
|
+
padding: "var(--bw-spacing)",
|
|
9187
|
+
}, children: [jsxRuntime.jsx("div", { style: {
|
|
9188
|
+
display: "flex",
|
|
9189
|
+
alignItems: "center",
|
|
9190
|
+
gap: "8px",
|
|
9191
|
+
marginBottom: "8px",
|
|
9192
|
+
color: "var(--bw-success-color)",
|
|
9193
|
+
fontFamily: "var(--bw-font-family)",
|
|
9194
|
+
fontWeight: "600",
|
|
9195
|
+
}, children: "\uD83C\uDF81 Vollst\u00E4ndig durch Gutschein(e) gedeckt" }), jsxRuntime.jsxs("div", { style: {
|
|
9196
|
+
fontSize: "var(--bw-font-size)",
|
|
9197
|
+
color: "var(--bw-text-muted)",
|
|
9198
|
+
fontFamily: "var(--bw-font-family)",
|
|
9199
|
+
}, children: ["Gutschein-Guthaben: ", formatCurrency(totalGiftCardAmount)] })] }), error && (jsxRuntime.jsxs("div", { style: {
|
|
9200
|
+
backgroundColor: "var(--bw-error-color)15",
|
|
9201
|
+
border: "1px solid var(--bw-error-color)40",
|
|
9202
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9203
|
+
padding: "var(--bw-spacing)",
|
|
9204
|
+
color: "var(--bw-error-color)",
|
|
9205
|
+
fontSize: "var(--bw-font-size)",
|
|
9206
|
+
fontFamily: "var(--bw-font-family)",
|
|
9207
|
+
}, children: ["\u26A0\uFE0F ", error] })), jsxRuntime.jsx("button", { type: "button", onClick: handleBooking, disabled: isLoading, style: {
|
|
9208
|
+
width: "100%",
|
|
9209
|
+
padding: "12px 24px",
|
|
9210
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
9211
|
+
color: "#fff",
|
|
9212
|
+
border: "none",
|
|
9213
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9214
|
+
fontSize: "var(--bw-font-size)",
|
|
9215
|
+
fontWeight: "600",
|
|
9216
|
+
cursor: isLoading ? "not-allowed" : "pointer",
|
|
9217
|
+
opacity: isLoading ? 0.6 : 1,
|
|
9218
|
+
transition: "all 0.2s ease",
|
|
9219
|
+
fontFamily: "var(--bw-font-family)",
|
|
9220
|
+
display: "flex",
|
|
9221
|
+
alignItems: "center",
|
|
9222
|
+
justifyContent: "center",
|
|
9223
|
+
gap: "8px",
|
|
9224
|
+
}, children: isLoading ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("div", { style: {
|
|
9225
|
+
width: "16px",
|
|
9226
|
+
height: "16px",
|
|
9227
|
+
border: "2px solid #fff",
|
|
9228
|
+
borderTopColor: "transparent",
|
|
9229
|
+
borderRadius: "50%",
|
|
9230
|
+
animation: "spin 1s linear infinite",
|
|
9231
|
+
} }), "Buchung wird erstellt..."] })) : ("Mit Gutschein buchen") }), jsxRuntime.jsx("style", { children: `
|
|
9232
|
+
@keyframes spin {
|
|
9233
|
+
from { transform: rotate(0deg); }
|
|
9234
|
+
to { transform: rotate(360deg); }
|
|
9235
|
+
}
|
|
9236
|
+
` })] }));
|
|
9237
|
+
}
|
|
9134
9238
|
const spinner$1 = (borderColor) => (jsxRuntime.jsx("div", { style: {
|
|
9135
9239
|
width: "auto",
|
|
9136
9240
|
height: "auto",
|
|
@@ -9147,7 +9251,7 @@ const spinner$1 = (borderColor) => (jsxRuntime.jsx("div", { style: {
|
|
|
9147
9251
|
borderRadius: "50%",
|
|
9148
9252
|
} }) }));
|
|
9149
9253
|
// Inner component that uses the Stripe hooks
|
|
9150
|
-
function PaymentFormInner({ config, eventDetails, formData, totalAmount, discountCode, onSuccess, onError, }) {
|
|
9254
|
+
function PaymentFormInner({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, }) {
|
|
9151
9255
|
const stripe = reactStripe_umdExports.useStripe();
|
|
9152
9256
|
const elements = reactStripe_umdExports.useElements();
|
|
9153
9257
|
const [isLoading, setIsLoading] = React__default.useState(false);
|
|
@@ -9278,7 +9382,7 @@ function PaymentFormInner({ config, eventDetails, formData, totalAmount, discoun
|
|
|
9278
9382
|
` })] }));
|
|
9279
9383
|
}
|
|
9280
9384
|
// Main PaymentForm component that handles payment intent creation and Elements wrapper
|
|
9281
|
-
function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode, onSuccess, onError, systemConfig, stripePromise, stripeAppearance, }) {
|
|
9385
|
+
function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode, giftCards, onSuccess, onError, systemConfig, stripePromise, stripeAppearance, }) {
|
|
9282
9386
|
const [clientSecret, setClientSecret] = React__default.useState(null);
|
|
9283
9387
|
const [paymentIntentId, setPaymentIntentId] = React__default.useState(null);
|
|
9284
9388
|
const [isCreatingPaymentIntent, setIsCreatingPaymentIntent] = React__default.useState(false);
|
|
@@ -9374,6 +9478,7 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
|
|
|
9374
9478
|
currency: "eur",
|
|
9375
9479
|
participants: formData.participants.filter((p) => p.name?.trim()),
|
|
9376
9480
|
discountCode: discountCode?.code,
|
|
9481
|
+
giftCardCodes: giftCards?.map((gc) => gc.code) || [],
|
|
9377
9482
|
customerName: formData.customerName?.trim(),
|
|
9378
9483
|
customerEmail: formData.customerEmail?.trim(),
|
|
9379
9484
|
customerPhone: formData.customerPhone?.trim(),
|
|
@@ -9435,8 +9540,21 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
|
|
|
9435
9540
|
formData.customerName,
|
|
9436
9541
|
totalAmount,
|
|
9437
9542
|
discountCode,
|
|
9543
|
+
giftCards,
|
|
9438
9544
|
config,
|
|
9439
9545
|
]);
|
|
9546
|
+
// Calculate total gift card coverage
|
|
9547
|
+
const totalGiftCardAmount = giftCards?.reduce((sum, gc) => sum + (gc.balanceToUse || gc.discountAmount || 0), 0) || 0;
|
|
9548
|
+
const baseTotal = eventDetails?.price
|
|
9549
|
+
? eventDetails.price * (formData.participants?.filter((p) => p.name?.trim()).length || 0)
|
|
9550
|
+
: 0;
|
|
9551
|
+
const discountAmount = discountCode?.discountAmount || 0;
|
|
9552
|
+
const amountAfterDiscount = Math.max(0, baseTotal - discountAmount);
|
|
9553
|
+
const isFullyCoveredByGiftCards = totalGiftCardAmount >= amountAfterDiscount && amountAfterDiscount > 0;
|
|
9554
|
+
// If gift cards fully cover the payment, show a simplified booking button
|
|
9555
|
+
if (isFullyCoveredByGiftCards && totalAmount <= 0) {
|
|
9556
|
+
return (jsxRuntime.jsx(GiftCardOnlyBooking, { config: config, eventDetails: eventDetails, formData: formData, discountCode: discountCode, giftCards: giftCards || [], onSuccess: onSuccess, onError: onError }));
|
|
9557
|
+
}
|
|
9440
9558
|
// Show loading state while creating payment intent
|
|
9441
9559
|
if (isCreatingPaymentIntent || !clientSecret) {
|
|
9442
9560
|
return (jsxRuntime.jsxs("div", { style: {
|
|
@@ -9470,7 +9588,7 @@ function PaymentForm({ config, eventDetails, formData, totalAmount, discountCode
|
|
|
9470
9588
|
clientSecret,
|
|
9471
9589
|
appearance: stripeAppearance || { theme: "stripe" },
|
|
9472
9590
|
locale: config.locale || "de",
|
|
9473
|
-
}, children: jsxRuntime.jsx(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, onSuccess: (result) => {
|
|
9591
|
+
}, children: jsxRuntime.jsx(PaymentFormInner, { config: config, eventDetails: eventDetails, formData: formData, totalAmount: totalAmount, discountCode: discountCode, giftCards: giftCards, onSuccess: (result) => {
|
|
9474
9592
|
// Clear persisted PI data on successful payment
|
|
9475
9593
|
clearPersistedPaymentIntent();
|
|
9476
9594
|
setPaymentIntentId(null);
|
|
@@ -9675,6 +9793,222 @@ function Accordion({ title, priceInfo, children, isOpen, onToggle }) {
|
|
|
9675
9793
|
}, children: children }))] }));
|
|
9676
9794
|
}
|
|
9677
9795
|
|
|
9796
|
+
// Icons
|
|
9797
|
+
const IconTicket = ({ size = 20, color = "currentColor" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("path", { d: "M2 9a3 3 0 0 1 0 6v2a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-2a3 3 0 0 1 0-6V7a2 2 0 0 0-2-2H4a2 2 0 0 0-2 2Z" }), jsxRuntime.jsx("path", { d: "M13 5v2" }), jsxRuntime.jsx("path", { d: "M13 17v2" }), jsxRuntime.jsx("path", { d: "M13 11v2" })] }));
|
|
9798
|
+
const IconGift = ({ size = 20, color = "currentColor" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("rect", { x: "3", y: "8", width: "18", height: "4", rx: "1" }), jsxRuntime.jsx("path", { d: "M12 8v13" }), jsxRuntime.jsx("path", { d: "M19 12v7a2 2 0 0 1-2 2H7a2 2 0 0 1-2-2v-7" }), jsxRuntime.jsx("path", { d: "M7.5 8a2.5 2.5 0 0 1 0-5A4.8 8 0 0 1 12 8a4.8 8 0 0 1 4.5-5 2.5 2.5 0 0 1 0 5" })] }));
|
|
9799
|
+
const IconCheck = ({ size = 16, color = "currentColor" }) => (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: jsxRuntime.jsx("polyline", { points: "20 6 9 17 4 12" }) }));
|
|
9800
|
+
const IconX = ({ size = 16, color = "currentColor" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("line", { x1: "18", y1: "6", x2: "6", y2: "18" }), jsxRuntime.jsx("line", { x1: "6", y1: "6", x2: "18", y2: "18" })] }));
|
|
9801
|
+
const IconSpinner = ({ size = 16, color = "currentColor" }) => (jsxRuntime.jsx("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", style: { animation: "spin 1s linear infinite" }, children: jsxRuntime.jsx("path", { d: "M21 12a9 9 0 1 1-6.219-8.56" }) }));
|
|
9802
|
+
function VoucherInput({ config, orderValue, eventInstanceId, customerEmail, onVoucherValidated, appliedVouchers, onRemoveVoucher, disabled = false, }) {
|
|
9803
|
+
const [inputValue, setInputValue] = React__default.useState("");
|
|
9804
|
+
const [isLoading, setIsLoading] = React__default.useState(false);
|
|
9805
|
+
const [error, setError] = React__default.useState(null);
|
|
9806
|
+
const [isExpanded, setIsExpanded] = React__default.useState(false);
|
|
9807
|
+
// Check if a discount code is already applied (only one allowed)
|
|
9808
|
+
const hasDiscountCode = appliedVouchers.some((v) => v.type === "discount");
|
|
9809
|
+
const validateVoucher = React__default.useCallback(async (code) => {
|
|
9810
|
+
if (!code.trim()) {
|
|
9811
|
+
setError(null);
|
|
9812
|
+
return;
|
|
9813
|
+
}
|
|
9814
|
+
// Check if code is already applied
|
|
9815
|
+
if (appliedVouchers.some((v) => v.code.toUpperCase() === code.toUpperCase())) {
|
|
9816
|
+
setError("Dieser Code wurde bereits angewendet");
|
|
9817
|
+
return;
|
|
9818
|
+
}
|
|
9819
|
+
setIsLoading(true);
|
|
9820
|
+
setError(null);
|
|
9821
|
+
try {
|
|
9822
|
+
const response = await fetch(getApiUrl(config.apiBaseUrl, "/booking/validate-voucher"), {
|
|
9823
|
+
method: "POST",
|
|
9824
|
+
headers: createApiHeaders(config),
|
|
9825
|
+
body: JSON.stringify(createRequestBody(config, {
|
|
9826
|
+
code: code.trim(),
|
|
9827
|
+
orderValue: orderValue,
|
|
9828
|
+
eventInstanceId: eventInstanceId,
|
|
9829
|
+
customerEmail: customerEmail,
|
|
9830
|
+
})),
|
|
9831
|
+
});
|
|
9832
|
+
const data = await response.json();
|
|
9833
|
+
if (data.valid && data.voucher) {
|
|
9834
|
+
// Check if trying to add a second discount code
|
|
9835
|
+
if (data.voucher.type === "discount" && hasDiscountCode) {
|
|
9836
|
+
setError("Es kann nur ein Rabattcode verwendet werden");
|
|
9837
|
+
onVoucherValidated(null, "Es kann nur ein Rabattcode verwendet werden");
|
|
9838
|
+
return;
|
|
9839
|
+
}
|
|
9840
|
+
onVoucherValidated(data.voucher);
|
|
9841
|
+
setInputValue("");
|
|
9842
|
+
setError(null);
|
|
9843
|
+
}
|
|
9844
|
+
else {
|
|
9845
|
+
setError(data.error || "Code nicht gefunden oder ungültig");
|
|
9846
|
+
onVoucherValidated(null, data.error);
|
|
9847
|
+
}
|
|
9848
|
+
}
|
|
9849
|
+
catch (err) {
|
|
9850
|
+
const errorMsg = "Fehler beim Validieren des Codes";
|
|
9851
|
+
setError(errorMsg);
|
|
9852
|
+
onVoucherValidated(null, errorMsg);
|
|
9853
|
+
}
|
|
9854
|
+
finally {
|
|
9855
|
+
setIsLoading(false);
|
|
9856
|
+
}
|
|
9857
|
+
}, [
|
|
9858
|
+
config,
|
|
9859
|
+
orderValue,
|
|
9860
|
+
eventInstanceId,
|
|
9861
|
+
customerEmail,
|
|
9862
|
+
appliedVouchers,
|
|
9863
|
+
hasDiscountCode,
|
|
9864
|
+
onVoucherValidated,
|
|
9865
|
+
]);
|
|
9866
|
+
const handleSubmit = () => {
|
|
9867
|
+
if (inputValue.trim() && !isLoading && !disabled) {
|
|
9868
|
+
validateVoucher(inputValue);
|
|
9869
|
+
}
|
|
9870
|
+
};
|
|
9871
|
+
const handleKeyDown = (e) => {
|
|
9872
|
+
if (e.key === "Enter") {
|
|
9873
|
+
e.preventDefault();
|
|
9874
|
+
if (inputValue.trim() && !isLoading && !disabled) {
|
|
9875
|
+
validateVoucher(inputValue);
|
|
9876
|
+
}
|
|
9877
|
+
}
|
|
9878
|
+
};
|
|
9879
|
+
const inputStyle = {
|
|
9880
|
+
flex: 1,
|
|
9881
|
+
padding: "10px 12px",
|
|
9882
|
+
backgroundColor: "var(--bw-background-color)",
|
|
9883
|
+
border: "1px solid var(--bw-border-color)",
|
|
9884
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9885
|
+
color: "var(--bw-text-color)",
|
|
9886
|
+
fontSize: "var(--bw-font-size)",
|
|
9887
|
+
fontFamily: "var(--bw-font-family)",
|
|
9888
|
+
outline: "none",
|
|
9889
|
+
transition: "all 0.2s ease",
|
|
9890
|
+
textTransform: "uppercase",
|
|
9891
|
+
};
|
|
9892
|
+
const buttonStyle = {
|
|
9893
|
+
padding: "10px 16px",
|
|
9894
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
9895
|
+
border: "none",
|
|
9896
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9897
|
+
color: "#fff",
|
|
9898
|
+
fontSize: "var(--bw-font-size)",
|
|
9899
|
+
fontFamily: "var(--bw-font-family)",
|
|
9900
|
+
fontWeight: "600",
|
|
9901
|
+
cursor: disabled || isLoading ? "not-allowed" : "pointer",
|
|
9902
|
+
opacity: disabled || isLoading ? 0.6 : 1,
|
|
9903
|
+
transition: "all 0.2s ease",
|
|
9904
|
+
display: "flex",
|
|
9905
|
+
alignItems: "center",
|
|
9906
|
+
justifyContent: "center",
|
|
9907
|
+
gap: "6px",
|
|
9908
|
+
minWidth: "100px",
|
|
9909
|
+
};
|
|
9910
|
+
const appliedVoucherStyle = {
|
|
9911
|
+
display: "flex",
|
|
9912
|
+
alignItems: "center",
|
|
9913
|
+
justifyContent: "space-between",
|
|
9914
|
+
padding: "10px 12px",
|
|
9915
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
9916
|
+
border: "1px solid var(--bw-border-color)",
|
|
9917
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9918
|
+
marginBottom: "8px",
|
|
9919
|
+
};
|
|
9920
|
+
const removeButtonStyle = {
|
|
9921
|
+
background: "none",
|
|
9922
|
+
border: "none",
|
|
9923
|
+
padding: "4px",
|
|
9924
|
+
cursor: "pointer",
|
|
9925
|
+
color: "var(--bw-error-color)",
|
|
9926
|
+
display: "flex",
|
|
9927
|
+
alignItems: "center",
|
|
9928
|
+
justifyContent: "center",
|
|
9929
|
+
borderRadius: "50%",
|
|
9930
|
+
transition: "background-color 0.2s ease",
|
|
9931
|
+
};
|
|
9932
|
+
return (jsxRuntime.jsxs("div", { style: {
|
|
9933
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
9934
|
+
border: "1px solid var(--bw-border-color)",
|
|
9935
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9936
|
+
overflow: "hidden",
|
|
9937
|
+
}, children: [jsxRuntime.jsxs("button", { type: "button", onClick: () => setIsExpanded(!isExpanded), style: {
|
|
9938
|
+
width: "100%",
|
|
9939
|
+
padding: "var(--bw-spacing)",
|
|
9940
|
+
backgroundColor: "transparent",
|
|
9941
|
+
border: "none",
|
|
9942
|
+
cursor: "pointer",
|
|
9943
|
+
display: "flex",
|
|
9944
|
+
alignItems: "center",
|
|
9945
|
+
justifyContent: "space-between",
|
|
9946
|
+
color: "var(--bw-text-color)",
|
|
9947
|
+
fontFamily: "var(--bw-font-family)",
|
|
9948
|
+
fontSize: "var(--bw-font-size)",
|
|
9949
|
+
fontWeight: "500",
|
|
9950
|
+
}, children: [jsxRuntime.jsxs("span", { style: { display: "flex", alignItems: "center", gap: "8px" }, children: [jsxRuntime.jsx(IconTicket, { size: 18, color: "var(--bw-highlight-color)" }), "Rabattcode oder Gutschein", appliedVouchers.length > 0 && (jsxRuntime.jsx("span", { style: {
|
|
9951
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
9952
|
+
color: "#fff",
|
|
9953
|
+
padding: "2px 8px",
|
|
9954
|
+
borderRadius: "12px",
|
|
9955
|
+
fontSize: "12px",
|
|
9956
|
+
fontWeight: "600",
|
|
9957
|
+
}, children: appliedVouchers.length }))] }), jsxRuntime.jsx("span", { style: {
|
|
9958
|
+
transform: isExpanded ? "rotate(180deg)" : "rotate(0deg)",
|
|
9959
|
+
transition: "transform 0.2s ease",
|
|
9960
|
+
}, children: "\u25BC" })] }), isExpanded && (jsxRuntime.jsxs("div", { style: { padding: "0 var(--bw-spacing) var(--bw-spacing)" }, children: [appliedVouchers.length > 0 && (jsxRuntime.jsx("div", { style: { marginBottom: "12px" }, children: appliedVouchers.map((voucher) => (jsxRuntime.jsxs("div", { style: appliedVoucherStyle, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "center", gap: "10px" }, children: [voucher.type === "discount" ? (jsxRuntime.jsx(IconTicket, { size: 18, color: "var(--bw-success-color)" })) : (jsxRuntime.jsx(IconGift, { size: 18, color: "var(--bw-success-color)" })), jsxRuntime.jsxs("div", { children: [jsxRuntime.jsxs("div", { style: {
|
|
9961
|
+
fontFamily: "var(--bw-font-family)",
|
|
9962
|
+
fontSize: "var(--bw-font-size)",
|
|
9963
|
+
fontWeight: "600",
|
|
9964
|
+
color: "var(--bw-text-color)",
|
|
9965
|
+
display: "flex",
|
|
9966
|
+
alignItems: "center",
|
|
9967
|
+
gap: "6px",
|
|
9968
|
+
}, children: [jsxRuntime.jsx("span", { style: { fontFamily: "monospace" }, children: voucher.code }), jsxRuntime.jsx(IconCheck, { size: 14, color: "var(--bw-success-color)" })] }), jsxRuntime.jsxs("div", { style: {
|
|
9969
|
+
fontFamily: "var(--bw-font-family)",
|
|
9970
|
+
fontSize: "12px",
|
|
9971
|
+
color: "var(--bw-success-color)",
|
|
9972
|
+
}, children: [voucher.type === "discount"
|
|
9973
|
+
? `−${formatCurrency(voucher.discountAmount)} Rabatt`
|
|
9974
|
+
: `−${formatCurrency(voucher.balanceToUse || voucher.discountAmount)} Gutschein`, voucher.type === "giftCard" &&
|
|
9975
|
+
voucher.remainingBalance !== undefined &&
|
|
9976
|
+
voucher.remainingBalance > 0 && (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", marginLeft: "8px" }, children: ["(Rest: ", formatCurrency(voucher.remainingBalance), ")"] }))] })] })] }), jsxRuntime.jsx("button", { type: "button", onClick: () => onRemoveVoucher(voucher.code), style: removeButtonStyle, title: "Entfernen", children: jsxRuntime.jsx(IconX, { size: 16 }) })] }, voucher.code))) })), jsxRuntime.jsxs("div", { style: { display: "flex", gap: "8px" }, children: [jsxRuntime.jsx("input", { type: "text", value: inputValue, onChange: (e) => {
|
|
9977
|
+
setInputValue(e.target.value.toUpperCase());
|
|
9978
|
+
setError(null);
|
|
9979
|
+
}, onKeyDown: handleKeyDown, placeholder: hasDiscountCode
|
|
9980
|
+
? "Gutscheincode eingeben..."
|
|
9981
|
+
: "Rabatt- oder Gutscheincode eingeben...", style: inputStyle, disabled: disabled || isLoading, onFocus: (e) => {
|
|
9982
|
+
e.target.style.borderColor = "var(--bw-highlight-color)";
|
|
9983
|
+
e.target.style.boxShadow = "0 0 0 2px var(--bw-highlight-color)33";
|
|
9984
|
+
}, onBlur: (e) => {
|
|
9985
|
+
e.target.style.borderColor = "var(--bw-border-color)";
|
|
9986
|
+
e.target.style.boxShadow = "none";
|
|
9987
|
+
} }), jsxRuntime.jsx("button", { type: "button", onClick: handleSubmit, style: buttonStyle, disabled: disabled || isLoading || !inputValue.trim(), children: isLoading ? (jsxRuntime.jsx(IconSpinner, { size: 16, color: "#fff" })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx(IconCheck, { size: 16 }), "Einl\u00F6sen"] })) })] }), error && (jsxRuntime.jsxs("div", { style: {
|
|
9988
|
+
marginTop: "8px",
|
|
9989
|
+
padding: "8px 12px",
|
|
9990
|
+
backgroundColor: "var(--bw-error-color)15",
|
|
9991
|
+
border: "1px solid var(--bw-error-color)40",
|
|
9992
|
+
borderRadius: "var(--bw-border-radius)",
|
|
9993
|
+
color: "var(--bw-error-color)",
|
|
9994
|
+
fontSize: "var(--bw-font-size)",
|
|
9995
|
+
fontFamily: "var(--bw-font-family)",
|
|
9996
|
+
display: "flex",
|
|
9997
|
+
alignItems: "center",
|
|
9998
|
+
gap: "8px",
|
|
9999
|
+
}, children: [jsxRuntime.jsx(IconX, { size: 16 }), error] })), hasDiscountCode && (jsxRuntime.jsx("div", { style: {
|
|
10000
|
+
marginTop: "8px",
|
|
10001
|
+
fontSize: "12px",
|
|
10002
|
+
color: "var(--bw-text-muted)",
|
|
10003
|
+
fontFamily: "var(--bw-font-family)",
|
|
10004
|
+
}, children: "\uD83D\uDCA1 Es wurde bereits ein Rabattcode angewendet. Du kannst weitere Gutscheine hinzuf\u00FCgen." }))] })), jsxRuntime.jsx("style", { children: `
|
|
10005
|
+
@keyframes spin {
|
|
10006
|
+
from { transform: rotate(0deg); }
|
|
10007
|
+
to { transform: rotate(360deg); }
|
|
10008
|
+
}
|
|
10009
|
+
` })] }));
|
|
10010
|
+
}
|
|
10011
|
+
|
|
9678
10012
|
// Form schemas
|
|
9679
10013
|
const participantSchema = objectType({
|
|
9680
10014
|
name: stringType().min(1, "Name ist erforderlich"),
|
|
@@ -9694,6 +10028,10 @@ const bookingFormSchema = objectType({
|
|
|
9694
10028
|
const IconWarning = ({ size = 48, color = "var(--bw-error-color)" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "10" }), jsxRuntime.jsx("line", { x1: "12", y1: "8", x2: "12", y2: "12" }), jsxRuntime.jsx("circle", { cx: "12", cy: "16", r: "1" })] }));
|
|
9695
10029
|
const IconMoney = ({ size = 20, color = "var(--bw-text-muted)" }) => (jsxRuntime.jsxs("svg", { width: size, height: size, viewBox: "0 0 24 24", fill: "none", stroke: color, strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round", children: [jsxRuntime.jsx("rect", { x: "2", y: "6", width: "20", height: "12", rx: "2" }), jsxRuntime.jsx("circle", { cx: "12", cy: "12", r: "4" }), jsxRuntime.jsx("line", { x1: "2", y1: "10", x2: "2", y2: "14" }), jsxRuntime.jsx("line", { x1: "22", y1: "10", x2: "22", y2: "14" })] }));
|
|
9696
10030
|
function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError, onBackToEventInstances, onBackToEventTypes, selectedEventType, selectedEventInstance, isOpen, onClose, systemConfig, }) {
|
|
10031
|
+
// New voucher system - supports multiple gift cards + one discount code
|
|
10032
|
+
const [appliedVouchers, setAppliedVouchers] = React__default.useState([]);
|
|
10033
|
+
const [voucherError, setVoucherError] = React__default.useState(null);
|
|
10034
|
+
// Legacy state for backward compatibility
|
|
9697
10035
|
const [discountCode, setDiscountCode] = React__default.useState(null);
|
|
9698
10036
|
const [discountLoading, setDiscountLoading] = React__default.useState(false);
|
|
9699
10037
|
const [discountError, setDiscountError] = React__default.useState(null);
|
|
@@ -9707,28 +10045,66 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
9707
10045
|
},
|
|
9708
10046
|
});
|
|
9709
10047
|
const watchedParticipants = form.watch("participants");
|
|
9710
|
-
|
|
10048
|
+
form.watch("discountCode");
|
|
9711
10049
|
const watchedCustomerName = form.watch("customerName");
|
|
9712
10050
|
const watchedCustomerEmail = form.watch("customerEmail");
|
|
9713
10051
|
const customerNameError = form.formState.errors.customerName;
|
|
9714
10052
|
const customerEmailError = form.formState.errors.customerEmail;
|
|
9715
10053
|
const watchedAcceptTerms = form.watch("acceptTerms");
|
|
9716
|
-
// Calculate total
|
|
9717
|
-
const
|
|
10054
|
+
// Calculate base total before any discounts
|
|
10055
|
+
const calculateBaseTotal = React__default.useCallback(() => {
|
|
9718
10056
|
if (!eventDetails)
|
|
9719
10057
|
return 0;
|
|
9720
|
-
|
|
9721
|
-
|
|
9722
|
-
|
|
10058
|
+
return eventDetails.price * watchedParticipants.filter((p) => p.name.trim()).length;
|
|
10059
|
+
}, [eventDetails, watchedParticipants]);
|
|
10060
|
+
// Calculate total discount from all applied vouchers
|
|
10061
|
+
const calculateTotalDiscount = React__default.useCallback(() => {
|
|
10062
|
+
return appliedVouchers.reduce((total, voucher) => {
|
|
10063
|
+
if (voucher.type === "discount") {
|
|
10064
|
+
return total + voucher.discountAmount;
|
|
10065
|
+
}
|
|
10066
|
+
else if (voucher.type === "giftCard") {
|
|
10067
|
+
return total + (voucher.balanceToUse || voucher.discountAmount);
|
|
10068
|
+
}
|
|
10069
|
+
return total;
|
|
10070
|
+
}, 0);
|
|
10071
|
+
}, [appliedVouchers]);
|
|
10072
|
+
// Calculate total amount after discounts
|
|
10073
|
+
const calculateTotal = React__default.useCallback(() => {
|
|
10074
|
+
const baseTotal = calculateBaseTotal();
|
|
10075
|
+
const totalDiscount = calculateTotalDiscount();
|
|
10076
|
+
return Math.max(0, baseTotal - totalDiscount);
|
|
10077
|
+
}, [calculateBaseTotal, calculateTotalDiscount]);
|
|
9723
10078
|
const calculateDeposit = () => {
|
|
9724
10079
|
if (!eventDetails || !eventDetails.deposit)
|
|
9725
10080
|
return 0;
|
|
9726
10081
|
const participantCount = watchedParticipants.filter((p) => p.name.trim()).length;
|
|
9727
10082
|
return eventDetails.deposit * participantCount;
|
|
9728
10083
|
};
|
|
10084
|
+
const baseTotal = calculateBaseTotal();
|
|
10085
|
+
const totalDiscount = calculateTotalDiscount();
|
|
9729
10086
|
const totalAmount = calculateTotal();
|
|
9730
10087
|
const depositAmount = calculateDeposit();
|
|
9731
|
-
|
|
10088
|
+
// If there's a deposit, we pay the deposit; otherwise we pay the total after discounts
|
|
10089
|
+
const paymentAmount = depositAmount > 0 ? Math.max(0, depositAmount - totalDiscount) : totalAmount;
|
|
10090
|
+
// Get discount code for legacy compatibility
|
|
10091
|
+
const appliedDiscountCode = appliedVouchers.find((v) => v.type === "discount");
|
|
10092
|
+
// Get gift cards
|
|
10093
|
+
const appliedGiftCards = appliedVouchers.filter((v) => v.type === "giftCard");
|
|
10094
|
+
// Voucher handlers
|
|
10095
|
+
const handleVoucherValidated = React__default.useCallback((voucher, error) => {
|
|
10096
|
+
if (error) {
|
|
10097
|
+
setVoucherError(error);
|
|
10098
|
+
return;
|
|
10099
|
+
}
|
|
10100
|
+
if (voucher) {
|
|
10101
|
+
setAppliedVouchers((prev) => [...prev, voucher]);
|
|
10102
|
+
setVoucherError(null);
|
|
10103
|
+
}
|
|
10104
|
+
}, []);
|
|
10105
|
+
const handleRemoveVoucher = React__default.useCallback((code) => {
|
|
10106
|
+
setAppliedVouchers((prev) => prev.filter((v) => v.code !== code));
|
|
10107
|
+
}, []);
|
|
9732
10108
|
// Form validation helper
|
|
9733
10109
|
const isFormValid = () => {
|
|
9734
10110
|
const participantCount = watchedParticipants.filter((p) => p.name.trim()).length;
|
|
@@ -9742,48 +10118,51 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
9742
10118
|
const hasAcceptedTerms = watchedAcceptTerms === true;
|
|
9743
10119
|
return validParticipants && participantsWithinLimit && hasName && hasEmail && hasAcceptedTerms;
|
|
9744
10120
|
};
|
|
9745
|
-
//
|
|
10121
|
+
// Re-validate vouchers when participant count changes (affects order value)
|
|
9746
10122
|
React__default.useEffect(() => {
|
|
9747
|
-
|
|
9748
|
-
|
|
9749
|
-
|
|
9750
|
-
|
|
9751
|
-
|
|
9752
|
-
|
|
9753
|
-
|
|
9754
|
-
|
|
9755
|
-
|
|
9756
|
-
|
|
9757
|
-
|
|
9758
|
-
|
|
9759
|
-
|
|
9760
|
-
|
|
9761
|
-
|
|
9762
|
-
|
|
9763
|
-
|
|
9764
|
-
|
|
9765
|
-
|
|
9766
|
-
|
|
9767
|
-
|
|
10123
|
+
// When participants change, we need to recalculate voucher amounts
|
|
10124
|
+
// For now, we'll clear vouchers if the order value changes significantly
|
|
10125
|
+
// In a production app, you might want to re-validate each voucher
|
|
10126
|
+
if (appliedVouchers.length > 0) {
|
|
10127
|
+
// Recalculate discount amounts based on new order value
|
|
10128
|
+
const newBaseTotal = eventDetails?.price
|
|
10129
|
+
? eventDetails.price * watchedParticipants.filter((p) => p.name.trim()).length
|
|
10130
|
+
: 0;
|
|
10131
|
+
// Update voucher amounts (simplified - in production, re-validate via API)
|
|
10132
|
+
setAppliedVouchers((prev) => prev.map((voucher) => {
|
|
10133
|
+
if (voucher.type === "discount") {
|
|
10134
|
+
let newDiscountAmount = 0;
|
|
10135
|
+
if (voucher.discountType === "percentage") {
|
|
10136
|
+
newDiscountAmount = Math.round((newBaseTotal * (voucher.discountValue || 0)) / 10000);
|
|
10137
|
+
}
|
|
10138
|
+
else {
|
|
10139
|
+
newDiscountAmount = voucher.discountValue || 0;
|
|
10140
|
+
}
|
|
10141
|
+
newDiscountAmount = Math.min(newDiscountAmount, newBaseTotal);
|
|
10142
|
+
return {
|
|
10143
|
+
...voucher,
|
|
10144
|
+
discountAmount: newDiscountAmount,
|
|
10145
|
+
newTotal: newBaseTotal - newDiscountAmount,
|
|
10146
|
+
};
|
|
9768
10147
|
}
|
|
9769
|
-
else {
|
|
9770
|
-
|
|
9771
|
-
|
|
10148
|
+
else if (voucher.type === "giftCard") {
|
|
10149
|
+
// Gift card balance stays the same, but amount to use might change
|
|
10150
|
+
const remainingAfterDiscount = newBaseTotal -
|
|
10151
|
+
prev
|
|
10152
|
+
.filter((v) => v.type === "discount")
|
|
10153
|
+
.reduce((sum, v) => sum + v.discountAmount, 0);
|
|
10154
|
+
const balanceToUse = Math.min(voucher.currentBalance || 0, Math.max(0, remainingAfterDiscount));
|
|
10155
|
+
return {
|
|
10156
|
+
...voucher,
|
|
10157
|
+
balanceToUse,
|
|
10158
|
+
remainingBalance: (voucher.currentBalance || 0) - balanceToUse,
|
|
10159
|
+
discountAmount: balanceToUse,
|
|
10160
|
+
};
|
|
9772
10161
|
}
|
|
9773
|
-
|
|
9774
|
-
|
|
9775
|
-
|
|
9776
|
-
|
|
9777
|
-
}
|
|
9778
|
-
finally {
|
|
9779
|
-
setDiscountLoading(false);
|
|
9780
|
-
}
|
|
9781
|
-
};
|
|
9782
|
-
const timer = setTimeout(() => {
|
|
9783
|
-
validateDiscountCode(watchedDiscountCode || "");
|
|
9784
|
-
}, 500);
|
|
9785
|
-
return () => clearTimeout(timer);
|
|
9786
|
-
}, [watchedDiscountCode, watchedParticipants, eventDetails, config]);
|
|
10162
|
+
return voucher;
|
|
10163
|
+
}));
|
|
10164
|
+
}
|
|
10165
|
+
}, [watchedParticipants, eventDetails]);
|
|
9787
10166
|
// Helper functions
|
|
9788
10167
|
const addParticipant = () => {
|
|
9789
10168
|
const currentParticipants = form.getValues("participants");
|
|
@@ -9938,7 +10317,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
9938
10317
|
color: "var(--bw-text-color)",
|
|
9939
10318
|
fontWeight: "500",
|
|
9940
10319
|
fontFamily: "var(--bw-font-family)",
|
|
9941
|
-
}, children: [formatCurrency(eventDetails.price), " pro Person"] })] })] })] }), jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "var(--bw-spacing-large)" }, children: [jsxRuntime.jsxs("
|
|
10320
|
+
}, children: [formatCurrency(eventDetails.price), " pro Person"] })] })] })] }), jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "var(--bw-spacing-large)" }, children: [jsxRuntime.jsxs("div", { style: { display: "flex", flexDirection: "column", gap: "var(--bw-spacing-large)" }, children: [jsxRuntime.jsxs("div", { style: {
|
|
9942
10321
|
backgroundColor: "var(--bw-surface-color)",
|
|
9943
10322
|
border: `1px solid var(--bw-border-color)`,
|
|
9944
10323
|
backdropFilter: "blur(4px)",
|
|
@@ -10083,7 +10462,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
10083
10462
|
color: "var(--bw-error-color)",
|
|
10084
10463
|
fontSize: "var(--bw-font-size)",
|
|
10085
10464
|
fontFamily: "var(--bw-font-family)",
|
|
10086
|
-
}, children: ["Maximal ", eventDetails.availableSpots, " Pl\u00E4tze verf\u00FCgbar."] }))] })] }), jsxRuntime.jsxs("div", { style: {
|
|
10465
|
+
}, children: ["Maximal ", eventDetails.availableSpots, " Pl\u00E4tze verf\u00FCgbar."] }))] })] }), jsxRuntime.jsx(VoucherInput, { config: config, orderValue: baseTotal, eventInstanceId: eventDetails?.id, customerEmail: watchedCustomerEmail, onVoucherValidated: handleVoucherValidated, appliedVouchers: appliedVouchers, onRemoveVoucher: handleRemoveVoucher, disabled: !eventDetails }), jsxRuntime.jsxs("div", { style: {
|
|
10087
10466
|
backgroundColor: "var(--bw-surface-color)",
|
|
10088
10467
|
border: `1px solid var(--bw-border-color)`,
|
|
10089
10468
|
backdropFilter: "blur(4px)",
|
|
@@ -10182,7 +10561,7 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
10182
10561
|
color: "var(--bw-text-color)",
|
|
10183
10562
|
fontWeight: "500",
|
|
10184
10563
|
fontFamily: "var(--bw-font-family)",
|
|
10185
|
-
}, children: formatCurrency(eventDetails.deposit || 0) })] })),
|
|
10564
|
+
}, children: formatCurrency(eventDetails.deposit || 0) })] })), appliedVouchers.length > 0 && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("div", { style: {
|
|
10186
10565
|
display: "flex",
|
|
10187
10566
|
justifyContent: "space-between",
|
|
10188
10567
|
alignItems: "center",
|
|
@@ -10191,20 +10570,31 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
10191
10570
|
fontFamily: "var(--bw-font-family)",
|
|
10192
10571
|
}, children: "Zwischensumme:" }), jsxRuntime.jsx("span", { style: {
|
|
10193
10572
|
color: "var(--bw-text-muted)",
|
|
10194
|
-
textDecoration: "line-through",
|
|
10573
|
+
textDecoration: totalDiscount > 0 ? "line-through" : "none",
|
|
10195
10574
|
fontFamily: "var(--bw-font-family)",
|
|
10196
|
-
}, children: formatCurrency(
|
|
10197
|
-
watchedParticipants.filter((p) => p.name.trim()).length) })] }), jsxRuntime.jsxs("div", { style: {
|
|
10575
|
+
}, children: formatCurrency(baseTotal) })] }), appliedDiscountCode && (jsxRuntime.jsxs("div", { style: {
|
|
10198
10576
|
display: "flex",
|
|
10199
10577
|
justifyContent: "space-between",
|
|
10200
10578
|
alignItems: "center",
|
|
10201
|
-
}, children: [jsxRuntime.
|
|
10579
|
+
}, children: [jsxRuntime.jsxs("span", { style: {
|
|
10202
10580
|
color: "var(--bw-success-color)",
|
|
10203
10581
|
fontFamily: "var(--bw-font-family)",
|
|
10204
|
-
|
|
10582
|
+
fontSize: "var(--bw-font-size)",
|
|
10583
|
+
}, children: ["Rabatt (", appliedDiscountCode.code, "):"] }), jsxRuntime.jsxs("span", { style: {
|
|
10205
10584
|
color: "var(--bw-success-color)",
|
|
10206
10585
|
fontFamily: "var(--bw-font-family)",
|
|
10207
|
-
}, children: ["-", formatCurrency(
|
|
10586
|
+
}, children: ["-", formatCurrency(appliedDiscountCode.discountAmount)] })] })), appliedGiftCards.map((giftCard) => (jsxRuntime.jsxs("div", { style: {
|
|
10587
|
+
display: "flex",
|
|
10588
|
+
justifyContent: "space-between",
|
|
10589
|
+
alignItems: "center",
|
|
10590
|
+
}, children: [jsxRuntime.jsxs("span", { style: {
|
|
10591
|
+
color: "var(--bw-success-color)",
|
|
10592
|
+
fontFamily: "var(--bw-font-family)",
|
|
10593
|
+
fontSize: "var(--bw-font-size)",
|
|
10594
|
+
}, children: ["Gutschein (", giftCard.code, "):"] }), jsxRuntime.jsxs("span", { style: {
|
|
10595
|
+
color: "var(--bw-success-color)",
|
|
10596
|
+
fontFamily: "var(--bw-font-family)",
|
|
10597
|
+
}, children: ["-", formatCurrency(giftCard.balanceToUse || giftCard.discountAmount)] })] }, giftCard.code)))] })), jsxRuntime.jsxs("div", { style: {
|
|
10208
10598
|
borderTop: `1px solid var(--bw-border-color)`,
|
|
10209
10599
|
paddingTop: "12px",
|
|
10210
10600
|
}, children: [depositAmount > 0 && (jsxRuntime.jsxs("div", { style: {
|
|
@@ -10277,7 +10667,15 @@ function BookingForm({ config, eventDetails, stripePromise, onSuccess, onError,
|
|
|
10277
10667
|
fontFamily: "var(--bw-font-family)",
|
|
10278
10668
|
borderBottom: "2px solid var(--bw-highlight-color)",
|
|
10279
10669
|
paddingBottom: 4,
|
|
10280
|
-
}, children: "Zahlung" }), jsxRuntime.jsx(PaymentForm, { config: config, eventDetails: eventDetails, formData: form.getValues(), totalAmount: paymentAmount, discountCode:
|
|
10670
|
+
}, children: "Zahlung" }), jsxRuntime.jsx(PaymentForm, { config: config, eventDetails: eventDetails, formData: form.getValues(), totalAmount: paymentAmount, discountCode: appliedDiscountCode ? {
|
|
10671
|
+
id: appliedDiscountCode.id,
|
|
10672
|
+
code: appliedDiscountCode.code,
|
|
10673
|
+
description: appliedDiscountCode.description || undefined,
|
|
10674
|
+
type: appliedDiscountCode.discountType || "percentage",
|
|
10675
|
+
value: appliedDiscountCode.discountValue || 0,
|
|
10676
|
+
discountAmount: appliedDiscountCode.discountAmount,
|
|
10677
|
+
newTotal: appliedDiscountCode.newTotal,
|
|
10678
|
+
} : null, giftCards: appliedGiftCards, onSuccess: onSuccess, onError: onError, systemConfig: systemConfig, stripePromise: stripePromise, stripeAppearance: stripeAppearance })] }));
|
|
10281
10679
|
})()] })] }), jsxRuntime.jsx("style", { children: `
|
|
10282
10680
|
.booking-widget-container *,
|
|
10283
10681
|
.booking-widget-container *::before,
|
|
@@ -11304,137 +11702,155 @@ function EventInstanceSelection({ eventInstances, selectedEventType, onEventInst
|
|
|
11304
11702
|
font-size: 1.1rem !important;
|
|
11305
11703
|
}
|
|
11306
11704
|
}
|
|
11307
|
-
` }), jsxRuntime.
|
|
11308
|
-
|
|
11309
|
-
|
|
11310
|
-
|
|
11311
|
-
|
|
11312
|
-
|
|
11313
|
-
|
|
11314
|
-
|
|
11315
|
-
backgroundColor:
|
|
11316
|
-
|
|
11317
|
-
|
|
11318
|
-
|
|
11319
|
-
|
|
11320
|
-
|
|
11321
|
-
|
|
11322
|
-
|
|
11323
|
-
|
|
11324
|
-
|
|
11325
|
-
|
|
11326
|
-
:
|
|
11327
|
-
|
|
11328
|
-
|
|
11329
|
-
|
|
11330
|
-
|
|
11331
|
-
|
|
11332
|
-
|
|
11333
|
-
|
|
11334
|
-
|
|
11335
|
-
|
|
11336
|
-
|
|
11337
|
-
|
|
11338
|
-
|
|
11339
|
-
|
|
11340
|
-
|
|
11341
|
-
|
|
11342
|
-
|
|
11343
|
-
|
|
11344
|
-
|
|
11345
|
-
|
|
11346
|
-
|
|
11347
|
-
|
|
11348
|
-
|
|
11349
|
-
|
|
11350
|
-
|
|
11351
|
-
|
|
11352
|
-
|
|
11353
|
-
|
|
11354
|
-
|
|
11355
|
-
|
|
11356
|
-
|
|
11357
|
-
|
|
11358
|
-
|
|
11359
|
-
|
|
11360
|
-
|
|
11361
|
-
|
|
11362
|
-
|
|
11363
|
-
|
|
11364
|
-
|
|
11365
|
-
|
|
11366
|
-
|
|
11367
|
-
|
|
11368
|
-
|
|
11369
|
-
|
|
11370
|
-
|
|
11371
|
-
|
|
11372
|
-
|
|
11373
|
-
|
|
11374
|
-
|
|
11375
|
-
|
|
11376
|
-
|
|
11377
|
-
|
|
11378
|
-
|
|
11379
|
-
|
|
11380
|
-
|
|
11381
|
-
|
|
11382
|
-
}, children:
|
|
11383
|
-
|
|
11384
|
-
|
|
11385
|
-
|
|
11386
|
-
|
|
11387
|
-
|
|
11388
|
-
|
|
11389
|
-
|
|
11390
|
-
|
|
11391
|
-
|
|
11392
|
-
|
|
11393
|
-
|
|
11394
|
-
|
|
11395
|
-
|
|
11396
|
-
|
|
11397
|
-
|
|
11398
|
-
|
|
11399
|
-
|
|
11400
|
-
|
|
11401
|
-
|
|
11402
|
-
|
|
11403
|
-
|
|
11404
|
-
|
|
11405
|
-
|
|
11406
|
-
|
|
11407
|
-
|
|
11408
|
-
|
|
11409
|
-
|
|
11410
|
-
|
|
11411
|
-
|
|
11412
|
-
|
|
11413
|
-
|
|
11414
|
-
|
|
11415
|
-
|
|
11416
|
-
|
|
11417
|
-
|
|
11418
|
-
|
|
11419
|
-
|
|
11420
|
-
|
|
11421
|
-
|
|
11422
|
-
|
|
11423
|
-
|
|
11424
|
-
|
|
11425
|
-
|
|
11426
|
-
|
|
11427
|
-
|
|
11428
|
-
|
|
11429
|
-
|
|
11430
|
-
|
|
11431
|
-
|
|
11432
|
-
|
|
11433
|
-
|
|
11434
|
-
|
|
11435
|
-
|
|
11436
|
-
|
|
11437
|
-
|
|
11705
|
+
` }), jsxRuntime.jsx(Sidebar, { isOpen: isOpen, onClose: handleClose, title: `${selectedEventType?.name}`, children: jsxRuntime.jsx("div", { className: "bw-event-instance-list", style: { padding: "24px" }, children: jsxRuntime.jsx("div", { style: {
|
|
11706
|
+
display: "flex",
|
|
11707
|
+
flexDirection: "column",
|
|
11708
|
+
gap: "20px",
|
|
11709
|
+
}, children: monthYearGroups.map(({ key, label, events, minPrice, year }, idx) => {
|
|
11710
|
+
const monthPriceDisplayInfo = getMonthPriceDisplayInfo(minPrice);
|
|
11711
|
+
return (jsxRuntime.jsxs(React__default.Fragment, { children: [idx > 0 && monthYearGroups[idx - 1].year !== year && (jsxRuntime.jsx("div", { style: {
|
|
11712
|
+
height: 1,
|
|
11713
|
+
backgroundColor: "var(--bw-border-color)",
|
|
11714
|
+
margin: "4px 0",
|
|
11715
|
+
} })), jsxRuntime.jsx(Accordion, { title: label, priceInfo: jsxRuntime.jsx("div", { style: {
|
|
11716
|
+
fontSize: "1rem",
|
|
11717
|
+
backgroundColor: monthPriceDisplayInfo
|
|
11718
|
+
? monthPriceDisplayInfo.backgroundColor
|
|
11719
|
+
: "#14532d",
|
|
11720
|
+
color: monthPriceDisplayInfo
|
|
11721
|
+
? monthPriceDisplayInfo.textColor
|
|
11722
|
+
: "#4ade80",
|
|
11723
|
+
fontWeight: 500,
|
|
11724
|
+
marginLeft: "auto",
|
|
11725
|
+
padding: "4px 8px",
|
|
11726
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
11727
|
+
border: monthPriceDisplayInfo ? "none" : undefined,
|
|
11728
|
+
boxShadow: monthPriceDisplayInfo
|
|
11729
|
+
? "0 2px 4px rgba(0, 0, 0, 0.2)"
|
|
11730
|
+
: undefined,
|
|
11731
|
+
}, children: `ab ${formatCurrency(minPrice)}` }), isOpen: openGroups.has(key), onToggle: () => toggleGroup(key), children: jsxRuntime.jsx("div", { style: {
|
|
11732
|
+
display: "flex",
|
|
11733
|
+
flexDirection: "column",
|
|
11734
|
+
gap: "12px",
|
|
11735
|
+
paddingTop: "12px",
|
|
11736
|
+
}, children: events.map((event) => {
|
|
11737
|
+
const availableSpots = event.maxParticipants - event.participantCount;
|
|
11738
|
+
const isFullyBooked = availableSpots === 0;
|
|
11739
|
+
const startDate = new Date(event.startTime);
|
|
11740
|
+
const isPastEvent = today.toISOString() >= startDate.toISOString();
|
|
11741
|
+
return (jsxRuntime.jsxs("div", { className: "bw-event-instance-card", style: {
|
|
11742
|
+
position: "relative",
|
|
11743
|
+
cursor: !isFullyBooked && !isPastEvent && event.bookingOpen
|
|
11744
|
+
? "pointer"
|
|
11745
|
+
: "not-allowed",
|
|
11746
|
+
border: "1px solid var(--bw-border-color)",
|
|
11747
|
+
backgroundColor: "var(--bw-surface-color)",
|
|
11748
|
+
borderRadius: "var(--bw-border-radius)",
|
|
11749
|
+
padding: "16px 20px",
|
|
11750
|
+
transition: "all 0.2s ease",
|
|
11751
|
+
opacity: isFullyBooked || isPastEvent ? 0.3 : 1,
|
|
11752
|
+
filter: isFullyBooked || isPastEvent ? "grayscale(40%)" : "none",
|
|
11753
|
+
fontFamily: "var(--bw-font-family)",
|
|
11754
|
+
}, onClick: () => {
|
|
11755
|
+
if (!isFullyBooked && !isPastEvent && event.bookingOpen) {
|
|
11756
|
+
handleEventInstanceSelect(event);
|
|
11757
|
+
}
|
|
11758
|
+
}, onMouseEnter: (e) => {
|
|
11759
|
+
if (!isFullyBooked && !isPastEvent && event.bookingOpen) {
|
|
11760
|
+
e.currentTarget.style.transform = "scale(1.02)";
|
|
11761
|
+
e.currentTarget.style.backgroundColor =
|
|
11762
|
+
"var(--bw-surface-muted, rgba(59, 130, 246, 0.1))";
|
|
11763
|
+
}
|
|
11764
|
+
}, onMouseLeave: (e) => {
|
|
11765
|
+
if (!isFullyBooked && !isPastEvent && event.bookingOpen) {
|
|
11766
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
11767
|
+
e.currentTarget.style.backgroundColor = "var(--bw-surface-color)";
|
|
11768
|
+
}
|
|
11769
|
+
}, children: [selectedEventInstanceId === event.id && isLoadingEventDetails && (jsxRuntime.jsx("div", { style: {
|
|
11770
|
+
position: "absolute",
|
|
11771
|
+
top: 0,
|
|
11772
|
+
left: 0,
|
|
11773
|
+
width: "100%",
|
|
11774
|
+
height: "100%",
|
|
11775
|
+
backgroundColor: "var(--bw-overlay-color, rgba(15, 23, 42, 0.8))",
|
|
11776
|
+
borderRadius: "var(--bw-border-radius)",
|
|
11777
|
+
display: "flex",
|
|
11778
|
+
alignItems: "center",
|
|
11779
|
+
justifyContent: "center",
|
|
11780
|
+
}, children: jsxRuntime.jsx("div", { style: {
|
|
11781
|
+
width: "32px",
|
|
11782
|
+
height: "32px",
|
|
11783
|
+
color: "var(--bw-highlight-color-muted, rgba(59, 130, 246, 0.8))",
|
|
11784
|
+
animation: "spin 1s linear infinite",
|
|
11785
|
+
fontSize: "32px",
|
|
11786
|
+
}, children: spinner() }) })), jsxRuntime.jsx(SpecialPriceBadge, { price: event.price, yearPrices: yearPrices }), jsxRuntime.jsx(AllocationBadge, { availableSpots: availableSpots, maxParticipants: event.maxParticipants }), jsxRuntime.jsxs("div", { style: {
|
|
11787
|
+
display: "flex",
|
|
11788
|
+
justifyContent: "space-between",
|
|
11789
|
+
width: "100%",
|
|
11790
|
+
alignItems: "start",
|
|
11791
|
+
gap: "12px",
|
|
11792
|
+
marginBottom: "4px",
|
|
11793
|
+
}, children: [jsxRuntime.jsxs("div", { style: { display: "flex", alignItems: "start", gap: "12px" }, children: [jsxRuntime.jsx("div", { className: "bw-event-instance-datebox", style: {
|
|
11794
|
+
fontSize: "var(--bw-font-size)",
|
|
11795
|
+
transition: "all 0.2s ease",
|
|
11796
|
+
borderRadius: "var(--bw-border-radius-small)",
|
|
11797
|
+
borderTop: `4px solid var(--bw-border-color)`,
|
|
11798
|
+
border: "1px solid var(--bw-border-color)",
|
|
11799
|
+
width: "40px",
|
|
11800
|
+
height: "40px",
|
|
11801
|
+
display: "flex",
|
|
11802
|
+
alignItems: "center",
|
|
11803
|
+
justifyContent: "center",
|
|
11804
|
+
fontWeight: "bold",
|
|
11805
|
+
color: "var(--bw-text-color)",
|
|
11806
|
+
backgroundColor: "var(--bw-background-color)",
|
|
11807
|
+
}, children: startDate.getDate() }), jsxRuntime.jsxs("div", { style: {
|
|
11808
|
+
fontSize: "var(--bw-font-size)",
|
|
11809
|
+
color: "var(--bw-text-color)",
|
|
11810
|
+
display: "flex",
|
|
11811
|
+
flexDirection: "column",
|
|
11812
|
+
alignItems: "start",
|
|
11813
|
+
justifyContent: "start",
|
|
11814
|
+
lineHeight: "1.2",
|
|
11815
|
+
}, 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) !==
|
|
11816
|
+
formatWeekday(event.endTime) && (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: {
|
|
11817
|
+
color: "var(--bw-text-muted)",
|
|
11818
|
+
fontSize: "14px",
|
|
11819
|
+
}, 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) ===
|
|
11820
|
+
formatWeekday(event.endTime) ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("span", { style: {
|
|
11821
|
+
color: "var(--bw-text-muted)",
|
|
11822
|
+
fontSize: "14px",
|
|
11823
|
+
}, children: formatTime(event.startTime) }), jsxRuntime.jsx("span", { style: {
|
|
11824
|
+
color: "var(--bw-text-muted)",
|
|
11825
|
+
fontSize: "14px",
|
|
11826
|
+
}, children: " - " }), jsxRuntime.jsx("span", { style: {
|
|
11827
|
+
color: "var(--bw-text-muted)",
|
|
11828
|
+
fontSize: "14px",
|
|
11829
|
+
}, children: formatTime(event.endTime) })] })) : (jsxRuntime.jsxs("span", { style: { color: "var(--bw-text-muted)", fontSize: "14px" }, children: [formatTime(event.startTime), " Uhr"] })) })] }), jsxRuntime.jsxs("span", { style: {
|
|
11830
|
+
fontSize: "12px",
|
|
11831
|
+
fontWeight: 400,
|
|
11832
|
+
color: "var(--bw-text-muted)",
|
|
11833
|
+
marginLeft: "6px",
|
|
11834
|
+
background: "var(--bw-background-muted)",
|
|
11835
|
+
whiteSpace: "nowrap",
|
|
11836
|
+
}, children: [event.durationDays, " Tag", event.durationDays > 1 ? "e" : ""] })] }), jsxRuntime.jsx("div", { className: "bw-event-instance-price", style: {
|
|
11837
|
+
textAlign: "right",
|
|
11838
|
+
display: "flex",
|
|
11839
|
+
flexDirection: "column",
|
|
11840
|
+
alignItems: "end",
|
|
11841
|
+
}, children: jsxRuntime.jsx(PriceDisplay, { price: event.price, yearPrices: yearPrices }) })] }), event.name !== selectedEventType?.name && (jsxRuntime.jsx("h4", { className: "bw-event-instance-title", style: {
|
|
11842
|
+
fontSize: "var(--bw-font-size)",
|
|
11843
|
+
fontWeight: "600",
|
|
11844
|
+
color: "var(--bw-text-color)",
|
|
11845
|
+
lineHeight: "1.25",
|
|
11846
|
+
margin: "0 0 2px 0",
|
|
11847
|
+
display: "flex",
|
|
11848
|
+
alignItems: "center",
|
|
11849
|
+
gap: "8px",
|
|
11850
|
+
maxWidth: "230px",
|
|
11851
|
+
}, children: event.name }))] }, event.id));
|
|
11852
|
+
}) }) })] }, key));
|
|
11853
|
+
}) }) }) })] }));
|
|
11438
11854
|
}
|
|
11439
11855
|
|
|
11440
11856
|
// Loading skeleton component for NextEventsPreview
|
|
@@ -11829,6 +12245,263 @@ function NextEventsPreview({ events, onEventSelect, onShowAll, showAllButtonText
|
|
|
11829
12245
|
` })] }));
|
|
11830
12246
|
}
|
|
11831
12247
|
|
|
12248
|
+
function PromoDialog({ onClose, onCtaClick }) {
|
|
12249
|
+
const [copied, setCopied] = React__default.useState(false);
|
|
12250
|
+
const [isVisible, setIsVisible] = React__default.useState(false);
|
|
12251
|
+
// Hardcoded Xmas surf school content
|
|
12252
|
+
const discountCode = "X-MAS";
|
|
12253
|
+
// Animate in on mount
|
|
12254
|
+
React__default.useEffect(() => {
|
|
12255
|
+
const timer = setTimeout(() => setIsVisible(true), 50);
|
|
12256
|
+
return () => clearTimeout(timer);
|
|
12257
|
+
}, []);
|
|
12258
|
+
const handleCopyCode = async () => {
|
|
12259
|
+
try {
|
|
12260
|
+
await navigator.clipboard.writeText(discountCode);
|
|
12261
|
+
setCopied(true);
|
|
12262
|
+
setTimeout(() => setCopied(false), 2000);
|
|
12263
|
+
}
|
|
12264
|
+
catch (err) {
|
|
12265
|
+
// Fallback for older browsers
|
|
12266
|
+
const textArea = document.createElement("textarea");
|
|
12267
|
+
textArea.value = discountCode;
|
|
12268
|
+
document.body.appendChild(textArea);
|
|
12269
|
+
textArea.select();
|
|
12270
|
+
document.execCommand("copy");
|
|
12271
|
+
document.body.removeChild(textArea);
|
|
12272
|
+
setCopied(true);
|
|
12273
|
+
setTimeout(() => setCopied(false), 2000);
|
|
12274
|
+
}
|
|
12275
|
+
};
|
|
12276
|
+
const handleClose = () => {
|
|
12277
|
+
setIsVisible(false);
|
|
12278
|
+
setTimeout(onClose, 200);
|
|
12279
|
+
};
|
|
12280
|
+
const handleCtaClick = () => {
|
|
12281
|
+
setIsVisible(false);
|
|
12282
|
+
setTimeout(onCtaClick, 200);
|
|
12283
|
+
};
|
|
12284
|
+
return (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsx("style", { children: `
|
|
12285
|
+
@keyframes promo-wave {
|
|
12286
|
+
0%, 100% { transform: translateX(0) translateY(0); }
|
|
12287
|
+
25% { transform: translateX(5px) translateY(-3px); }
|
|
12288
|
+
50% { transform: translateX(0) translateY(-5px); }
|
|
12289
|
+
75% { transform: translateX(-5px) translateY(-3px); }
|
|
12290
|
+
}
|
|
12291
|
+
@keyframes promo-float {
|
|
12292
|
+
0%, 100% { transform: translateY(0); }
|
|
12293
|
+
50% { transform: translateY(-8px); }
|
|
12294
|
+
}
|
|
12295
|
+
@keyframes promo-shimmer {
|
|
12296
|
+
0% { background-position: -200% center; }
|
|
12297
|
+
100% { background-position: 200% center; }
|
|
12298
|
+
}
|
|
12299
|
+
@keyframes promo-sparkle {
|
|
12300
|
+
0%, 100% { opacity: 0.3; transform: scale(1); }
|
|
12301
|
+
50% { opacity: 1; transform: scale(1.2); }
|
|
12302
|
+
}
|
|
12303
|
+
@keyframes promo-snow {
|
|
12304
|
+
0% { transform: translateY(-10px) rotate(0deg); opacity: 0; }
|
|
12305
|
+
10% { opacity: 1; }
|
|
12306
|
+
90% { opacity: 1; }
|
|
12307
|
+
100% { transform: translateY(350px) rotate(360deg); opacity: 0; }
|
|
12308
|
+
}
|
|
12309
|
+
` }), jsxRuntime.jsx("div", { onClick: handleClose, style: {
|
|
12310
|
+
position: "fixed",
|
|
12311
|
+
inset: 0,
|
|
12312
|
+
backgroundColor: "rgba(0, 20, 40, 0.85)",
|
|
12313
|
+
backdropFilter: "blur(8px)",
|
|
12314
|
+
zIndex: 9998,
|
|
12315
|
+
opacity: isVisible ? 1 : 0,
|
|
12316
|
+
transition: "opacity 300ms ease-out",
|
|
12317
|
+
} }), jsxRuntime.jsx("div", { style: {
|
|
12318
|
+
position: "fixed",
|
|
12319
|
+
top: "50%",
|
|
12320
|
+
left: "50%",
|
|
12321
|
+
transform: `translate(-50%, -50%) scale(${isVisible ? 1 : 0.9})`,
|
|
12322
|
+
zIndex: 9999,
|
|
12323
|
+
width: "92%",
|
|
12324
|
+
maxWidth: "440px",
|
|
12325
|
+
opacity: isVisible ? 1 : 0,
|
|
12326
|
+
transition: "all 300ms cubic-bezier(0.34, 1.56, 0.64, 1)",
|
|
12327
|
+
}, children: jsxRuntime.jsxs("div", { style: {
|
|
12328
|
+
position: "relative",
|
|
12329
|
+
background: "linear-gradient(165deg, #0c4a6e 0%, #0e7490 40%, #0891b2 100%)",
|
|
12330
|
+
borderRadius: "28px",
|
|
12331
|
+
overflow: "hidden",
|
|
12332
|
+
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)",
|
|
12333
|
+
}, children: [Array.from({ length: 15 }).map((_, i) => (jsxRuntime.jsx("div", { style: {
|
|
12334
|
+
position: "absolute",
|
|
12335
|
+
left: `${5 + Math.random() * 90}%`,
|
|
12336
|
+
top: "-10px",
|
|
12337
|
+
fontSize: `${10 + Math.random() * 14}px`,
|
|
12338
|
+
color: "white",
|
|
12339
|
+
opacity: 0,
|
|
12340
|
+
animation: `promo-snow ${4 + Math.random() * 3}s linear infinite`,
|
|
12341
|
+
animationDelay: `${Math.random() * 4}s`,
|
|
12342
|
+
pointerEvents: "none",
|
|
12343
|
+
zIndex: 1,
|
|
12344
|
+
}, children: "\u2744" }, i))), jsxRuntime.jsxs("div", { style: {
|
|
12345
|
+
position: "relative",
|
|
12346
|
+
height: "180px",
|
|
12347
|
+
background: "linear-gradient(180deg, rgba(0,0,0,0) 0%, rgba(12,74,110,0.8) 100%)",
|
|
12348
|
+
display: "flex",
|
|
12349
|
+
alignItems: "center",
|
|
12350
|
+
justifyContent: "center",
|
|
12351
|
+
overflow: "hidden",
|
|
12352
|
+
}, children: [jsxRuntime.jsx("img", { src: "https://images.unsplash.com/photo-1502680390469-be75c86b636f?w=600&q=80", alt: "Surfer at sunset", style: {
|
|
12353
|
+
position: "absolute",
|
|
12354
|
+
inset: 0,
|
|
12355
|
+
width: "100%",
|
|
12356
|
+
height: "100%",
|
|
12357
|
+
objectFit: "cover",
|
|
12358
|
+
opacity: 0.6,
|
|
12359
|
+
} }), jsxRuntime.jsx("div", { style: {
|
|
12360
|
+
position: "absolute",
|
|
12361
|
+
inset: 0,
|
|
12362
|
+
background: "linear-gradient(180deg, rgba(12,74,110,0.3) 0%, rgba(12,74,110,0.95) 100%)",
|
|
12363
|
+
} }), jsxRuntime.jsx("div", { style: {
|
|
12364
|
+
position: "relative",
|
|
12365
|
+
zIndex: 2,
|
|
12366
|
+
fontSize: "64px",
|
|
12367
|
+
animation: "promo-float 3s ease-in-out infinite",
|
|
12368
|
+
filter: "drop-shadow(0 8px 16px rgba(0,0,0,0.4))",
|
|
12369
|
+
}, children: "\uD83C\uDFC4\u200D\u2642\uFE0F" }), jsxRuntime.jsx("div", { style: {
|
|
12370
|
+
position: "absolute",
|
|
12371
|
+
top: "16px",
|
|
12372
|
+
left: "20px",
|
|
12373
|
+
fontSize: "28px",
|
|
12374
|
+
animation: "promo-sparkle 2s ease-in-out infinite",
|
|
12375
|
+
}, children: "\uD83C\uDF84" }), jsxRuntime.jsx("div", { style: {
|
|
12376
|
+
position: "absolute",
|
|
12377
|
+
top: "20px",
|
|
12378
|
+
right: "20px",
|
|
12379
|
+
fontSize: "24px",
|
|
12380
|
+
animation: "promo-sparkle 2s ease-in-out infinite 0.5s",
|
|
12381
|
+
}, children: "\u2B50" })] }), jsxRuntime.jsx("button", { onClick: handleClose, style: {
|
|
12382
|
+
position: "absolute",
|
|
12383
|
+
top: "16px",
|
|
12384
|
+
right: "16px",
|
|
12385
|
+
width: "36px",
|
|
12386
|
+
height: "36px",
|
|
12387
|
+
borderRadius: "50%",
|
|
12388
|
+
border: "none",
|
|
12389
|
+
background: "rgba(0, 0, 0, 0.3)",
|
|
12390
|
+
backdropFilter: "blur(4px)",
|
|
12391
|
+
color: "white",
|
|
12392
|
+
fontSize: "22px",
|
|
12393
|
+
cursor: "pointer",
|
|
12394
|
+
display: "flex",
|
|
12395
|
+
alignItems: "center",
|
|
12396
|
+
justifyContent: "center",
|
|
12397
|
+
transition: "all 150ms ease",
|
|
12398
|
+
zIndex: 10,
|
|
12399
|
+
lineHeight: 1,
|
|
12400
|
+
}, onMouseEnter: (e) => {
|
|
12401
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.5)";
|
|
12402
|
+
e.currentTarget.style.transform = "scale(1.1)";
|
|
12403
|
+
}, onMouseLeave: (e) => {
|
|
12404
|
+
e.currentTarget.style.background = "rgba(0, 0, 0, 0.3)";
|
|
12405
|
+
e.currentTarget.style.transform = "scale(1)";
|
|
12406
|
+
}, children: "\u00D7" }), jsxRuntime.jsxs("div", { style: { padding: "28px 28px 32px", textAlign: "center", position: "relative", zIndex: 2 }, children: [jsxRuntime.jsx("h2", { style: {
|
|
12407
|
+
fontSize: "26px",
|
|
12408
|
+
fontWeight: "800",
|
|
12409
|
+
color: "white",
|
|
12410
|
+
marginBottom: "6px",
|
|
12411
|
+
textShadow: "0 2px 8px rgba(0,0,0,0.3)",
|
|
12412
|
+
letterSpacing: "-0.5px",
|
|
12413
|
+
}, children: "Frohe Weihnachten! \uD83C\uDF85" }), jsxRuntime.jsxs("p", { style: {
|
|
12414
|
+
fontSize: "17px",
|
|
12415
|
+
color: "rgba(255, 255, 255, 0.9)",
|
|
12416
|
+
marginBottom: "20px",
|
|
12417
|
+
lineHeight: 1.5,
|
|
12418
|
+
}, children: ["Schenk dir oder deinen Liebsten", jsxRuntime.jsx("br", {}), jsxRuntime.jsx("strong", { style: { color: "#fbbf24" }, children: "10% Rabatt" }), " auf alle Kurse!"] }), jsxRuntime.jsxs("div", { style: {
|
|
12419
|
+
background: "white",
|
|
12420
|
+
borderRadius: "16px",
|
|
12421
|
+
padding: "18px 20px",
|
|
12422
|
+
marginBottom: "20px",
|
|
12423
|
+
boxShadow: "0 8px 24px rgba(0,0,0,0.15), inset 0 -2px 0 rgba(0,0,0,0.05)",
|
|
12424
|
+
}, children: [jsxRuntime.jsx("p", { style: {
|
|
12425
|
+
fontSize: "11px",
|
|
12426
|
+
textTransform: "uppercase",
|
|
12427
|
+
letterSpacing: "1.5px",
|
|
12428
|
+
color: "#64748b",
|
|
12429
|
+
marginBottom: "10px",
|
|
12430
|
+
fontWeight: "600",
|
|
12431
|
+
}, children: "Dein Geschenk-Code" }), jsxRuntime.jsxs("div", { style: {
|
|
12432
|
+
display: "flex",
|
|
12433
|
+
alignItems: "center",
|
|
12434
|
+
justifyContent: "center",
|
|
12435
|
+
gap: "14px",
|
|
12436
|
+
}, children: [jsxRuntime.jsx("div", { style: {
|
|
12437
|
+
background: "linear-gradient(135deg, #dc2626 0%, #b91c1c 100%)",
|
|
12438
|
+
padding: "10px 20px",
|
|
12439
|
+
borderRadius: "10px",
|
|
12440
|
+
boxShadow: "0 4px 12px rgba(220, 38, 38, 0.3)",
|
|
12441
|
+
}, children: jsxRuntime.jsx("span", { style: {
|
|
12442
|
+
fontSize: "28px",
|
|
12443
|
+
fontWeight: "900",
|
|
12444
|
+
color: "white",
|
|
12445
|
+
letterSpacing: "6px",
|
|
12446
|
+
textShadow: "0 2px 4px rgba(0,0,0,0.2)",
|
|
12447
|
+
}, children: discountCode }) }), jsxRuntime.jsx("button", { onClick: handleCopyCode, style: {
|
|
12448
|
+
padding: "12px 16px",
|
|
12449
|
+
borderRadius: "10px",
|
|
12450
|
+
border: "2px solid",
|
|
12451
|
+
borderColor: copied ? "#22c55e" : "#e2e8f0",
|
|
12452
|
+
background: copied ? "#dcfce7" : "#f8fafc",
|
|
12453
|
+
color: copied ? "#15803d" : "#475569",
|
|
12454
|
+
fontSize: "13px",
|
|
12455
|
+
fontWeight: "600",
|
|
12456
|
+
cursor: "pointer",
|
|
12457
|
+
transition: "all 150ms ease",
|
|
12458
|
+
display: "flex",
|
|
12459
|
+
alignItems: "center",
|
|
12460
|
+
gap: "6px",
|
|
12461
|
+
whiteSpace: "nowrap",
|
|
12462
|
+
}, children: copied ? (jsxRuntime.jsx(jsxRuntime.Fragment, { children: "\u2713 Kopiert!" })) : (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: [jsxRuntime.jsxs("svg", { width: "16", height: "16", viewBox: "0 0 24 24", fill: "none", stroke: "currentColor", strokeWidth: "2.5", children: [jsxRuntime.jsx("rect", { x: "9", y: "9", width: "13", height: "13", rx: "2", ry: "2" }), jsxRuntime.jsx("path", { d: "M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1" })] }), "Kopieren"] })) })] })] }), jsxRuntime.jsx("div", { style: {
|
|
12463
|
+
display: "flex",
|
|
12464
|
+
justifyContent: "center",
|
|
12465
|
+
gap: "8px",
|
|
12466
|
+
marginBottom: "20px",
|
|
12467
|
+
flexWrap: "wrap",
|
|
12468
|
+
}, children: ["🏄 Surfen", "🪁 Wingen", "🏄♀️ SUP", "💨 Windsurfen"].map((activity) => (jsxRuntime.jsx("span", { style: {
|
|
12469
|
+
background: "rgba(255,255,255,0.15)",
|
|
12470
|
+
backdropFilter: "blur(4px)",
|
|
12471
|
+
padding: "6px 12px",
|
|
12472
|
+
borderRadius: "20px",
|
|
12473
|
+
fontSize: "13px",
|
|
12474
|
+
color: "white",
|
|
12475
|
+
fontWeight: "500",
|
|
12476
|
+
}, children: activity }, activity))) }), jsxRuntime.jsxs("button", { onClick: handleCtaClick, style: {
|
|
12477
|
+
width: "100%",
|
|
12478
|
+
padding: "18px 24px",
|
|
12479
|
+
borderRadius: "14px",
|
|
12480
|
+
border: "none",
|
|
12481
|
+
background: "linear-gradient(135deg, #f59e0b 0%, #d97706 100%)",
|
|
12482
|
+
color: "white",
|
|
12483
|
+
fontSize: "18px",
|
|
12484
|
+
fontWeight: "700",
|
|
12485
|
+
cursor: "pointer",
|
|
12486
|
+
transition: "all 150ms ease",
|
|
12487
|
+
boxShadow: "0 8px 24px rgba(245, 158, 11, 0.4), inset 0 1px 0 rgba(255,255,255,0.2)",
|
|
12488
|
+
display: "flex",
|
|
12489
|
+
alignItems: "center",
|
|
12490
|
+
justifyContent: "center",
|
|
12491
|
+
gap: "10px",
|
|
12492
|
+
}, onMouseEnter: (e) => {
|
|
12493
|
+
e.currentTarget.style.transform = "translateY(-2px)";
|
|
12494
|
+
e.currentTarget.style.boxShadow = "0 12px 28px rgba(245, 158, 11, 0.5), inset 0 1px 0 rgba(255,255,255,0.2)";
|
|
12495
|
+
}, onMouseLeave: (e) => {
|
|
12496
|
+
e.currentTarget.style.transform = "translateY(0)";
|
|
12497
|
+
e.currentTarget.style.boxShadow = "0 8px 24px rgba(245, 158, 11, 0.4), inset 0 1px 0 rgba(255,255,255,0.2)";
|
|
12498
|
+
}, children: [jsxRuntime.jsx("span", { style: { animation: "promo-wave 2s ease-in-out infinite" }, children: "\uD83C\uDF81" }), "Jetzt Kurs buchen", jsxRuntime.jsx("span", { children: "\u2192" })] }), jsxRuntime.jsx("p", { style: {
|
|
12499
|
+
marginTop: "16px",
|
|
12500
|
+
fontSize: "12px",
|
|
12501
|
+
color: "rgba(255,255,255,0.6)",
|
|
12502
|
+
}, children: "G\u00FCltig f\u00FCr alle Buchungen bis 31. Dezember 2025" })] })] }) })] }));
|
|
12503
|
+
}
|
|
12504
|
+
|
|
11832
12505
|
// Predefined themes & Style Provider have been moved to ../styles/StyleProvider.tsx
|
|
11833
12506
|
// Main widget component
|
|
11834
12507
|
function UniversalBookingWidget({ config: baseConfig }) {
|
|
@@ -11871,6 +12544,9 @@ function UniversalBookingWidget({ config: baseConfig }) {
|
|
|
11871
12544
|
// PERFORMANCE OPTIMIZATION: Lazy component loading
|
|
11872
12545
|
const [shouldRenderInstanceSelection, setShouldRenderInstanceSelection] = React__default.useState(false);
|
|
11873
12546
|
const [shouldRenderBookingForm, setShouldRenderBookingForm] = React__default.useState(false);
|
|
12547
|
+
// Promo dialog state
|
|
12548
|
+
const [showPromoDialog, setShowPromoDialog] = React__default.useState(false);
|
|
12549
|
+
const [widgetContainerRef, setWidgetContainerRef] = React__default.useState(null);
|
|
11874
12550
|
// Determine initial step and load data
|
|
11875
12551
|
React__default.useEffect(() => {
|
|
11876
12552
|
const initializeWidget = async () => {
|
|
@@ -11945,6 +12621,46 @@ function UniversalBookingWidget({ config: baseConfig }) {
|
|
|
11945
12621
|
setShouldRenderBookingForm(true);
|
|
11946
12622
|
}
|
|
11947
12623
|
}, [currentStep, shouldRenderInstanceSelection, shouldRenderBookingForm]);
|
|
12624
|
+
// Promo dialog: show Xmas promo once per user during holiday season, prevent double-opening across multiple widgets
|
|
12625
|
+
React__default.useEffect(() => {
|
|
12626
|
+
// Only show during holiday season (December and January)
|
|
12627
|
+
const now = new Date();
|
|
12628
|
+
const month = now.getMonth(); // 0 = January, 11 = December
|
|
12629
|
+
const isHolidaySeason = month === 11 || month === 0; // December (11) or January (0)
|
|
12630
|
+
if (!isHolidaySeason) {
|
|
12631
|
+
return;
|
|
12632
|
+
}
|
|
12633
|
+
const promoId = "xmas-2024";
|
|
12634
|
+
const storageKey = `bigz-promo-${promoId}-shown`;
|
|
12635
|
+
const globalFlagKey = `__bigzPromoShown_${promoId}`;
|
|
12636
|
+
// Check if already shown in this session (localStorage) or claimed by another widget (global flag)
|
|
12637
|
+
const alreadyShown = localStorage.getItem(storageKey) === "true";
|
|
12638
|
+
const claimedByOtherWidget = window[globalFlagKey] === true;
|
|
12639
|
+
if (alreadyShown || claimedByOtherWidget) {
|
|
12640
|
+
return;
|
|
12641
|
+
}
|
|
12642
|
+
// Claim this promo for this widget instance (prevents other widgets from showing it)
|
|
12643
|
+
window[globalFlagKey] = true;
|
|
12644
|
+
// Show the dialog after a short delay for better UX
|
|
12645
|
+
const timer = setTimeout(() => {
|
|
12646
|
+
setShowPromoDialog(true);
|
|
12647
|
+
}, 1000);
|
|
12648
|
+
return () => clearTimeout(timer);
|
|
12649
|
+
}, []);
|
|
12650
|
+
// Handle promo dialog close
|
|
12651
|
+
const handlePromoDialogClose = () => {
|
|
12652
|
+
setShowPromoDialog(false);
|
|
12653
|
+
localStorage.setItem("bigz-promo-xmas-2024-shown", "true");
|
|
12654
|
+
};
|
|
12655
|
+
// Handle promo dialog CTA click - scroll to widget
|
|
12656
|
+
const handlePromoCtaClick = () => {
|
|
12657
|
+
setShowPromoDialog(false);
|
|
12658
|
+
localStorage.setItem("bigz-promo-xmas-2024-shown", "true");
|
|
12659
|
+
// Scroll to the widget container
|
|
12660
|
+
if (widgetContainerRef) {
|
|
12661
|
+
widgetContainerRef.scrollIntoView({ behavior: "smooth", block: "start" });
|
|
12662
|
+
}
|
|
12663
|
+
};
|
|
11948
12664
|
const loadEventTypes = async () => {
|
|
11949
12665
|
const requestBody = {
|
|
11950
12666
|
organizationId: config.organizationId,
|
|
@@ -12344,104 +13060,104 @@ function UniversalBookingWidget({ config: baseConfig }) {
|
|
|
12344
13060
|
// Main view based on view mode
|
|
12345
13061
|
if (viewMode === "next-events" && showingPreview) {
|
|
12346
13062
|
// Next events preview mode
|
|
12347
|
-
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
|
|
12348
|
-
|
|
12349
|
-
|
|
12350
|
-
|
|
12351
|
-
|
|
12352
|
-
|
|
12353
|
-
|
|
12354
|
-
|
|
12355
|
-
|
|
12356
|
-
|
|
12357
|
-
|
|
12358
|
-
|
|
12359
|
-
|
|
12360
|
-
|
|
12361
|
-
|
|
12362
|
-
|
|
12363
|
-
|
|
12364
|
-
|
|
12365
|
-
|
|
12366
|
-
|
|
12367
|
-
|
|
12368
|
-
|
|
12369
|
-
|
|
12370
|
-
|
|
12371
|
-
|
|
12372
|
-
|
|
12373
|
-
|
|
12374
|
-
|
|
13063
|
+
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [jsxRuntime.jsx(NextEventsPreview, { events: upcomingEvents, onEventSelect: handleUpcomingEventSelect, onShowAll: handleShowAllEvents, showAllButtonText: nextEventsSettings.showAllButtonText, showAllButton: nextEventsSettings.showAllButton, isLoadingEventDetails: isLoadingEventDetails, isLoadingShowAll: isLoadingShowAll, isLoading: isLoading }), shouldRenderBookingForm && eventDetails && (jsxRuntime.jsx(BookingForm, { config: config, eventDetails: eventDetails, stripePromise: stripePromise, onSuccess: handleBookingSuccess, onError: handleBookingError, onBackToEventInstances: () => {
|
|
13064
|
+
setCurrentStep("eventTypes");
|
|
13065
|
+
setShowingPreview(true);
|
|
13066
|
+
setEventDetails(null);
|
|
13067
|
+
}, onBackToEventTypes: () => {
|
|
13068
|
+
setCurrentStep("eventTypes");
|
|
13069
|
+
setShowingPreview(true);
|
|
13070
|
+
setEventDetails(null);
|
|
13071
|
+
}, selectedEventType: selectedEventType, selectedEventInstance: selectedEventInstance, isOpen: currentStep === "booking" && !!eventDetails, onClose: () => {
|
|
13072
|
+
setCurrentStep("eventTypes");
|
|
13073
|
+
setShowingPreview(true);
|
|
13074
|
+
setEventDetails(null);
|
|
13075
|
+
}, systemConfig: systemConfig })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
13076
|
+
setIsSuccess(false);
|
|
13077
|
+
setCurrentStep("eventTypes");
|
|
13078
|
+
setShowingPreview(true);
|
|
13079
|
+
// Reset state
|
|
13080
|
+
setSuccessPaymentIntentId(null);
|
|
13081
|
+
// Reset lazy loading flags
|
|
13082
|
+
setShouldRenderInstanceSelection(false);
|
|
13083
|
+
setShouldRenderBookingForm(false);
|
|
13084
|
+
// Clean up URL to remove Stripe parameters
|
|
13085
|
+
const url = new URL(window.location.href);
|
|
13086
|
+
url.searchParams.delete("payment_intent");
|
|
13087
|
+
url.searchParams.delete("payment_intent_client_secret");
|
|
13088
|
+
url.searchParams.delete("redirect_status");
|
|
13089
|
+
window.history.replaceState({}, "", url.toString());
|
|
13090
|
+
}, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsxRuntime.jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
12375
13091
|
}
|
|
12376
13092
|
if (viewMode === "next-events" && !showingPreview && currentStep === "eventInstances") {
|
|
12377
13093
|
// Show all events for the single event type
|
|
12378
|
-
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
|
|
12379
|
-
|
|
12380
|
-
|
|
12381
|
-
|
|
12382
|
-
|
|
12383
|
-
|
|
12384
|
-
|
|
12385
|
-
|
|
12386
|
-
|
|
12387
|
-
|
|
12388
|
-
|
|
12389
|
-
|
|
12390
|
-
|
|
12391
|
-
|
|
12392
|
-
|
|
12393
|
-
|
|
12394
|
-
|
|
12395
|
-
|
|
12396
|
-
|
|
12397
|
-
|
|
12398
|
-
|
|
12399
|
-
|
|
13094
|
+
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => {
|
|
13095
|
+
setShowingPreview(true);
|
|
13096
|
+
setCurrentStep("eventTypes");
|
|
13097
|
+
}, isOpen: currentStep === "eventInstances", onClose: () => {
|
|
13098
|
+
setShowingPreview(true);
|
|
13099
|
+
setCurrentStep("eventTypes");
|
|
13100
|
+
}, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
13101
|
+
setIsSuccess(false);
|
|
13102
|
+
setCurrentStep("eventTypes");
|
|
13103
|
+
setShowingPreview(true);
|
|
13104
|
+
// Reset state
|
|
13105
|
+
setSuccessPaymentIntentId(null);
|
|
13106
|
+
// Reset lazy loading flags
|
|
13107
|
+
setShouldRenderInstanceSelection(false);
|
|
13108
|
+
setShouldRenderBookingForm(false);
|
|
13109
|
+
// Clean up URL to remove Stripe parameters
|
|
13110
|
+
const url = new URL(window.location.href);
|
|
13111
|
+
url.searchParams.delete("payment_intent");
|
|
13112
|
+
url.searchParams.delete("payment_intent_client_secret");
|
|
13113
|
+
url.searchParams.delete("redirect_status");
|
|
13114
|
+
window.history.replaceState({}, "", url.toString());
|
|
13115
|
+
}, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsxRuntime.jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
12400
13116
|
}
|
|
12401
13117
|
if (viewMode === "button" && (isSingleEventTypeMode || isDirectInstanceMode)) {
|
|
12402
13118
|
// Button mode - show button that opens sidebar/booking directly
|
|
12403
|
-
return (jsxRuntime.
|
|
12404
|
-
|
|
12405
|
-
|
|
12406
|
-
|
|
12407
|
-
|
|
12408
|
-
|
|
12409
|
-
|
|
12410
|
-
|
|
12411
|
-
|
|
12412
|
-
|
|
12413
|
-
|
|
12414
|
-
|
|
12415
|
-
|
|
12416
|
-
|
|
12417
|
-
|
|
12418
|
-
|
|
12419
|
-
|
|
12420
|
-
|
|
12421
|
-
|
|
12422
|
-
|
|
12423
|
-
|
|
12424
|
-
|
|
12425
|
-
|
|
12426
|
-
|
|
12427
|
-
|
|
12428
|
-
|
|
12429
|
-
|
|
12430
|
-
|
|
12431
|
-
|
|
12432
|
-
|
|
12433
|
-
|
|
12434
|
-
|
|
12435
|
-
|
|
12436
|
-
|
|
12437
|
-
|
|
12438
|
-
|
|
12439
|
-
|
|
12440
|
-
|
|
12441
|
-
|
|
12442
|
-
|
|
12443
|
-
|
|
12444
|
-
|
|
13119
|
+
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, style: {
|
|
13120
|
+
display: "flex",
|
|
13121
|
+
justifyContent: "center",
|
|
13122
|
+
alignItems: "center",
|
|
13123
|
+
minHeight: "120px",
|
|
13124
|
+
}, children: [jsxRuntime.jsx("button", { type: "button", style: {
|
|
13125
|
+
backgroundColor: "var(--bw-highlight-color)",
|
|
13126
|
+
color: "white",
|
|
13127
|
+
padding: "16px 32px",
|
|
13128
|
+
border: "none",
|
|
13129
|
+
borderRadius: "var(--bw-border-radius)",
|
|
13130
|
+
fontSize: "18px",
|
|
13131
|
+
fontWeight: 600,
|
|
13132
|
+
fontFamily: "var(--bw-font-family)",
|
|
13133
|
+
boxShadow: "var(--bw-shadow-md)",
|
|
13134
|
+
cursor: "pointer",
|
|
13135
|
+
}, onClick: () => {
|
|
13136
|
+
if (isDirectInstanceMode) {
|
|
13137
|
+
setCurrentStep("booking");
|
|
13138
|
+
setShouldRenderBookingForm(true);
|
|
13139
|
+
}
|
|
13140
|
+
else {
|
|
13141
|
+
setSidebarOpen(true);
|
|
13142
|
+
setShouldRenderInstanceSelection(true);
|
|
13143
|
+
}
|
|
13144
|
+
}, children: config.buttonText ||
|
|
13145
|
+
(isDirectInstanceMode ? "Jetzt buchen" : "Jetzt Termin auswählen") }), shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: () => setSidebarOpen(false), isOpen: sidebarOpen, onClose: () => setSidebarOpen(false), isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsxRuntime.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 })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
13146
|
+
setIsSuccess(false);
|
|
13147
|
+
setCurrentStep("eventTypes");
|
|
13148
|
+
setSidebarOpen(false);
|
|
13149
|
+
// Reset state
|
|
13150
|
+
setSuccessPaymentIntentId(null);
|
|
13151
|
+
// Reset lazy loading flags
|
|
13152
|
+
setShouldRenderInstanceSelection(false);
|
|
13153
|
+
setShouldRenderBookingForm(false);
|
|
13154
|
+
// Clean up URL to remove Stripe parameters
|
|
13155
|
+
const url = new URL(window.location.href);
|
|
13156
|
+
url.searchParams.delete("payment_intent");
|
|
13157
|
+
url.searchParams.delete("payment_intent_client_secret");
|
|
13158
|
+
url.searchParams.delete("redirect_status");
|
|
13159
|
+
window.history.replaceState({}, "", url.toString());
|
|
13160
|
+
}, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsxRuntime.jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
12445
13161
|
}
|
|
12446
13162
|
// Cards mode (default) - show event type selection
|
|
12447
13163
|
const cardsView = (jsxRuntime.jsx(EventTypeSelection, { eventTypes: eventTypes, onEventTypeSelect: handleEventTypeSelect, isLoading: isLoading, skeletonCount: getSkeletonCount() }));
|
|
@@ -12474,21 +13190,21 @@ function UniversalBookingWidget({ config: baseConfig }) {
|
|
|
12474
13190
|
};
|
|
12475
13191
|
};
|
|
12476
13192
|
const backHandlers = getBackHandlers();
|
|
12477
|
-
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [cardsView, shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsxRuntime.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 })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
12478
|
-
|
|
12479
|
-
|
|
12480
|
-
|
|
12481
|
-
|
|
12482
|
-
|
|
12483
|
-
|
|
12484
|
-
|
|
12485
|
-
|
|
12486
|
-
|
|
12487
|
-
|
|
12488
|
-
|
|
12489
|
-
|
|
12490
|
-
|
|
12491
|
-
|
|
13193
|
+
return (jsxRuntime.jsxs(StyleProvider, { config: config, children: [jsxRuntime.jsxs("div", { ref: setWidgetContainerRef, children: [cardsView, shouldRenderInstanceSelection && (jsxRuntime.jsx(EventInstanceSelection, { eventInstances: eventInstances, selectedEventType: selectedEventType, onEventInstanceSelect: handleEventInstanceSelect, onBackToEventTypes: handleBackToEventTypes, isOpen: currentStep === "eventInstances", onClose: handleBackToEventTypes, isLoadingEventInstances: isLoadingEventInstances, isLoadingEventDetails: isLoadingEventDetails })), shouldRenderBookingForm && eventDetails && (jsxRuntime.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 })), jsxRuntime.jsx(BookingSuccessModal, { isOpen: isSuccess, onClose: () => {
|
|
13194
|
+
setIsSuccess(false);
|
|
13195
|
+
setCurrentStep("eventTypes");
|
|
13196
|
+
// Reset state
|
|
13197
|
+
setSuccessPaymentIntentId(null);
|
|
13198
|
+
// Reset lazy loading flags
|
|
13199
|
+
setShouldRenderInstanceSelection(false);
|
|
13200
|
+
setShouldRenderBookingForm(false);
|
|
13201
|
+
// Clean up URL to remove Stripe parameters
|
|
13202
|
+
const url = new URL(window.location.href);
|
|
13203
|
+
url.searchParams.delete("payment_intent");
|
|
13204
|
+
url.searchParams.delete("payment_intent_client_secret");
|
|
13205
|
+
url.searchParams.delete("redirect_status");
|
|
13206
|
+
window.history.replaceState({}, "", url.toString());
|
|
13207
|
+
}, config: config, onError: setError, paymentIntentId: successPaymentIntentId })] }), showPromoDialog && (jsxRuntime.jsx(PromoDialog, { onClose: handlePromoDialogClose, onCtaClick: handlePromoCtaClick }))] }));
|
|
12492
13208
|
}
|
|
12493
13209
|
|
|
12494
13210
|
// Export init function for vanilla JS usage
|