@resira/ui 0.4.16 → 0.4.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.cjs +129 -29
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +129 -29
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -302,6 +302,45 @@ function writeCache(cache, key, value, ttlMs) {
|
|
|
302
302
|
expiresAt: Date.now() + Math.max(1e3, ttlMs)
|
|
303
303
|
});
|
|
304
304
|
}
|
|
305
|
+
function normalizePositiveInt(value) {
|
|
306
|
+
if (typeof value !== "number" || !Number.isFinite(value)) return null;
|
|
307
|
+
const rounded = Math.round(value);
|
|
308
|
+
return rounded > 0 ? rounded : null;
|
|
309
|
+
}
|
|
310
|
+
function toIsoOrNull(value) {
|
|
311
|
+
if (typeof value !== "string" || !value.trim()) return null;
|
|
312
|
+
const date = new Date(value);
|
|
313
|
+
if (Number.isNaN(date.getTime())) return null;
|
|
314
|
+
return date.toISOString();
|
|
315
|
+
}
|
|
316
|
+
function withCanonicalPaymentWindow(payload) {
|
|
317
|
+
const next = { ...payload };
|
|
318
|
+
const durationMinutes = normalizePositiveInt(next.durationMinutes);
|
|
319
|
+
if (next.durationMinutes != null && !durationMinutes) {
|
|
320
|
+
throw new Error("Invalid duration selected.");
|
|
321
|
+
}
|
|
322
|
+
if (durationMinutes) {
|
|
323
|
+
next.durationMinutes = durationMinutes;
|
|
324
|
+
next.durationHours = Number((durationMinutes / 60).toFixed(6));
|
|
325
|
+
}
|
|
326
|
+
const startIso = toIsoOrNull(next.startTime);
|
|
327
|
+
const endIso = toIsoOrNull(next.endTime);
|
|
328
|
+
if (startIso) next.startTime = startIso;
|
|
329
|
+
if (durationMinutes && startIso) {
|
|
330
|
+
const computedEnd = new Date(
|
|
331
|
+
new Date(startIso).getTime() + durationMinutes * 6e4
|
|
332
|
+
).toISOString();
|
|
333
|
+
if (!endIso) {
|
|
334
|
+
next.endTime = computedEnd;
|
|
335
|
+
} else {
|
|
336
|
+
const matchesDuration = new Date(endIso).getTime() - new Date(startIso).getTime() === durationMinutes * 6e4;
|
|
337
|
+
next.endTime = matchesDuration ? endIso : computedEnd;
|
|
338
|
+
}
|
|
339
|
+
} else if (endIso) {
|
|
340
|
+
next.endTime = endIso;
|
|
341
|
+
}
|
|
342
|
+
return next;
|
|
343
|
+
}
|
|
305
344
|
async function getDishCompat(client, dishId) {
|
|
306
345
|
const maybeClient = client;
|
|
307
346
|
if (typeof maybeClient.getDish === "function") {
|
|
@@ -614,7 +653,8 @@ function usePaymentIntent() {
|
|
|
614
653
|
setCreating(true);
|
|
615
654
|
setError(null);
|
|
616
655
|
try {
|
|
617
|
-
const
|
|
656
|
+
const payload = withCanonicalPaymentWindow(data);
|
|
657
|
+
const response = await client.createPaymentIntent(payload);
|
|
618
658
|
setPaymentIntent(response);
|
|
619
659
|
return response;
|
|
620
660
|
} catch (err) {
|
|
@@ -2953,6 +2993,15 @@ function formatTime(isoStr) {
|
|
|
2953
2993
|
return isoStr;
|
|
2954
2994
|
}
|
|
2955
2995
|
}
|
|
2996
|
+
function renderTimeRange(start, end) {
|
|
2997
|
+
if (start && end) {
|
|
2998
|
+
return `${formatTime(start)} \u2013 ${formatTime(end)}`;
|
|
2999
|
+
}
|
|
3000
|
+
if (start || end) {
|
|
3001
|
+
return "Time unavailable";
|
|
3002
|
+
}
|
|
3003
|
+
return "Time unavailable";
|
|
3004
|
+
}
|
|
2956
3005
|
function formatPrice4(cents, currency) {
|
|
2957
3006
|
return new Intl.NumberFormat("default", {
|
|
2958
3007
|
style: "currency",
|
|
@@ -3006,14 +3055,11 @@ function SummaryPreview({
|
|
|
3006
3055
|
/* @__PURE__ */ jsx("span", { className: "resira-summary-item-value", children: formatDate(selection.startDate) })
|
|
3007
3056
|
] })
|
|
3008
3057
|
] }),
|
|
3009
|
-
!isDateBased && selection.startTime && /* @__PURE__ */ jsxs("div", { className: "resira-summary-item", children: [
|
|
3058
|
+
!isDateBased && (selection.startTime || selection.endTime) && /* @__PURE__ */ jsxs("div", { className: "resira-summary-item", children: [
|
|
3010
3059
|
/* @__PURE__ */ jsx(ClockIcon, { size: 14 }),
|
|
3011
3060
|
/* @__PURE__ */ jsxs("div", { className: "resira-summary-item-content", children: [
|
|
3012
3061
|
/* @__PURE__ */ jsx("span", { className: "resira-summary-item-label", children: "Time" }),
|
|
3013
|
-
/* @__PURE__ */
|
|
3014
|
-
formatTime(selection.startTime),
|
|
3015
|
-
selection.endTime && ` \u2013 ${formatTime(selection.endTime)}`
|
|
3016
|
-
] })
|
|
3062
|
+
/* @__PURE__ */ jsx("span", { className: "resira-summary-item-value", children: renderTimeRange(selection.startTime, selection.endTime) })
|
|
3017
3063
|
] })
|
|
3018
3064
|
] }),
|
|
3019
3065
|
/* @__PURE__ */ jsxs("div", { className: "resira-summary-item", children: [
|
|
@@ -3083,14 +3129,11 @@ function ConfirmationView({ reservation }) {
|
|
|
3083
3129
|
/* @__PURE__ */ jsx("span", { className: "resira-summary-item-value", children: formatDate(reservation.endDate) })
|
|
3084
3130
|
] })
|
|
3085
3131
|
] }),
|
|
3086
|
-
reservation.startTime && /* @__PURE__ */ jsxs("div", { className: "resira-summary-item", children: [
|
|
3132
|
+
(reservation.startTime || reservation.endTime) && /* @__PURE__ */ jsxs("div", { className: "resira-summary-item", children: [
|
|
3087
3133
|
/* @__PURE__ */ jsx(ClockIcon, { size: 14 }),
|
|
3088
3134
|
/* @__PURE__ */ jsxs("div", { className: "resira-summary-item-content", children: [
|
|
3089
3135
|
/* @__PURE__ */ jsx("span", { className: "resira-summary-item-label", children: "Time" }),
|
|
3090
|
-
/* @__PURE__ */
|
|
3091
|
-
formatTime(reservation.startTime),
|
|
3092
|
-
reservation.endTime && ` \u2013 ${formatTime(reservation.endTime)}`
|
|
3093
|
-
] })
|
|
3136
|
+
/* @__PURE__ */ jsx("span", { className: "resira-summary-item-value", children: renderTimeRange(reservation.startTime, reservation.endTime) })
|
|
3094
3137
|
] })
|
|
3095
3138
|
] }),
|
|
3096
3139
|
/* @__PURE__ */ jsxs("div", { className: "resira-summary-item", children: [
|
|
@@ -3278,6 +3321,9 @@ function ResiraBookingWidget() {
|
|
|
3278
3321
|
duration: domainConfig.defaultDuration
|
|
3279
3322
|
});
|
|
3280
3323
|
const [partySizeByProductId, setPartySizeByProductId] = useState({});
|
|
3324
|
+
const slotByProductRef = useRef({});
|
|
3325
|
+
const selectionRef = useRef(selection);
|
|
3326
|
+
selectionRef.current = selection;
|
|
3281
3327
|
const [guest, setGuest] = useState({
|
|
3282
3328
|
guestName: "",
|
|
3283
3329
|
guestEmail: "",
|
|
@@ -3294,6 +3340,25 @@ function ResiraBookingWidget() {
|
|
|
3294
3340
|
setPromoValidation(result);
|
|
3295
3341
|
}, []);
|
|
3296
3342
|
const [slotDate, setSlotDate] = useState(todayStr());
|
|
3343
|
+
const handleSlotDateChange = useCallback(
|
|
3344
|
+
(date) => {
|
|
3345
|
+
setSlotDate(date);
|
|
3346
|
+
setSelection((s) => {
|
|
3347
|
+
const next = { ...s, startDate: date, endDate: date, startTime: void 0, endTime: void 0 };
|
|
3348
|
+
if (selectedProduct?.id) {
|
|
3349
|
+
slotByProductRef.current[selectedProduct.id] = {
|
|
3350
|
+
...slotByProductRef.current[selectedProduct.id],
|
|
3351
|
+
startDate: date,
|
|
3352
|
+
endDate: date,
|
|
3353
|
+
startTime: void 0,
|
|
3354
|
+
endTime: void 0
|
|
3355
|
+
};
|
|
3356
|
+
}
|
|
3357
|
+
return next;
|
|
3358
|
+
});
|
|
3359
|
+
},
|
|
3360
|
+
[selectedProduct?.id]
|
|
3361
|
+
);
|
|
3297
3362
|
const activeDurationMinutes = normalizeDurationMinutes(selection.duration);
|
|
3298
3363
|
const selectedProductPartyBounds = useMemo(
|
|
3299
3364
|
() => getServicePartySizeBounds(selectedProduct, domainConfig),
|
|
@@ -3479,13 +3544,22 @@ function ResiraBookingWidget() {
|
|
|
3479
3544
|
);
|
|
3480
3545
|
const handleSlotSelect = useCallback(
|
|
3481
3546
|
(start, end) => {
|
|
3482
|
-
setSelection((prev) =>
|
|
3483
|
-
|
|
3484
|
-
|
|
3485
|
-
|
|
3486
|
-
|
|
3487
|
-
|
|
3488
|
-
|
|
3547
|
+
setSelection((prev) => {
|
|
3548
|
+
const next = {
|
|
3549
|
+
...prev,
|
|
3550
|
+
startDate: slotDate,
|
|
3551
|
+
endDate: slotDate,
|
|
3552
|
+
startTime: start,
|
|
3553
|
+
endTime: end
|
|
3554
|
+
};
|
|
3555
|
+
if (selectedProduct?.id) {
|
|
3556
|
+
slotByProductRef.current[selectedProduct.id] = {
|
|
3557
|
+
...slotByProductRef.current[selectedProduct.id],
|
|
3558
|
+
...next
|
|
3559
|
+
};
|
|
3560
|
+
}
|
|
3561
|
+
return next;
|
|
3562
|
+
});
|
|
3489
3563
|
if (promoterEnabled && promoterMode.autoAdvanceAvailability && step === "availability") {
|
|
3490
3564
|
const nextIdx = stepIndex(step, STEPS) + 1;
|
|
3491
3565
|
if (nextIdx < STEPS.length) {
|
|
@@ -3493,7 +3567,7 @@ function ResiraBookingWidget() {
|
|
|
3493
3567
|
}
|
|
3494
3568
|
}
|
|
3495
3569
|
},
|
|
3496
|
-
[slotDate, promoterEnabled, promoterMode.autoAdvanceAvailability, step, STEPS]
|
|
3570
|
+
[slotDate, promoterEnabled, promoterMode.autoAdvanceAvailability, step, STEPS, selectedProduct?.id]
|
|
3497
3571
|
);
|
|
3498
3572
|
const handlePartySizeChange = useCallback((size) => {
|
|
3499
3573
|
const clamped = clampPartySize(size, selectedProductPartyBounds);
|
|
@@ -3503,15 +3577,40 @@ function ResiraBookingWidget() {
|
|
|
3503
3577
|
}
|
|
3504
3578
|
}, [selectedProduct?.id, selectedProductPartyBounds]);
|
|
3505
3579
|
const handleDurationChange = useCallback((minutes) => {
|
|
3506
|
-
setSelection((prev) =>
|
|
3507
|
-
|
|
3508
|
-
|
|
3509
|
-
|
|
3510
|
-
|
|
3511
|
-
|
|
3512
|
-
|
|
3580
|
+
setSelection((prev) => {
|
|
3581
|
+
const next = {
|
|
3582
|
+
...prev,
|
|
3583
|
+
duration: minutes,
|
|
3584
|
+
startTime: void 0,
|
|
3585
|
+
endTime: void 0
|
|
3586
|
+
};
|
|
3587
|
+
if (selectedProduct?.id) {
|
|
3588
|
+
slotByProductRef.current[selectedProduct.id] = {
|
|
3589
|
+
...slotByProductRef.current[selectedProduct.id],
|
|
3590
|
+
duration: minutes,
|
|
3591
|
+
startTime: void 0,
|
|
3592
|
+
endTime: void 0
|
|
3593
|
+
};
|
|
3594
|
+
}
|
|
3595
|
+
return next;
|
|
3596
|
+
});
|
|
3597
|
+
}, [selectedProduct?.id]);
|
|
3513
3598
|
const handleProductSelect = useCallback(
|
|
3514
3599
|
(product) => {
|
|
3600
|
+
if (selectedProduct?.id) {
|
|
3601
|
+
const s = selectionRef.current;
|
|
3602
|
+
slotByProductRef.current[selectedProduct.id] = {
|
|
3603
|
+
startTime: s.startTime,
|
|
3604
|
+
endTime: s.endTime,
|
|
3605
|
+
startDate: s.startDate,
|
|
3606
|
+
endDate: s.endDate,
|
|
3607
|
+
duration: s.duration
|
|
3608
|
+
};
|
|
3609
|
+
}
|
|
3610
|
+
const saved = slotByProductRef.current[product.id] ?? {};
|
|
3611
|
+
if (saved.startDate && typeof saved.startDate === "string" && saved.startDate.length >= 10) {
|
|
3612
|
+
setSlotDate(saved.startDate.slice(0, 10));
|
|
3613
|
+
}
|
|
3515
3614
|
setSelectedProduct(product);
|
|
3516
3615
|
if (product.equipmentIds?.length) {
|
|
3517
3616
|
setActiveResourceId(product.equipmentIds[0]);
|
|
@@ -3523,9 +3622,10 @@ function ResiraBookingWidget() {
|
|
|
3523
3622
|
persistedPartySize ?? prev.partySize,
|
|
3524
3623
|
bounds
|
|
3525
3624
|
);
|
|
3526
|
-
const defaultDuration = product.durationPricing?.[0]?.durationMinutes ?? product.durationMinutes ?? prev.duration;
|
|
3625
|
+
const defaultDuration = saved.duration ?? product.durationPricing?.[0]?.durationMinutes ?? product.durationMinutes ?? prev.duration;
|
|
3527
3626
|
return {
|
|
3528
3627
|
...prev,
|
|
3628
|
+
...saved,
|
|
3529
3629
|
productId: product.id,
|
|
3530
3630
|
duration: defaultDuration,
|
|
3531
3631
|
partySize: nextPartySize
|
|
@@ -3547,7 +3647,7 @@ function ResiraBookingWidget() {
|
|
|
3547
3647
|
}
|
|
3548
3648
|
}
|
|
3549
3649
|
},
|
|
3550
|
-
[setActiveResourceId, domainConfig, partySizeByProductId, selection.partySize, step, isServiceBased, STEPS]
|
|
3650
|
+
[setActiveResourceId, domainConfig, partySizeByProductId, selection.partySize, step, isServiceBased, STEPS, selectedProduct?.id]
|
|
3551
3651
|
);
|
|
3552
3652
|
const handleResourceSelect = useCallback(
|
|
3553
3653
|
(resourceId) => {
|
|
@@ -3868,7 +3968,7 @@ function ResiraBookingWidget() {
|
|
|
3868
3968
|
{
|
|
3869
3969
|
timeSlots: availability?.timeSlots ?? [],
|
|
3870
3970
|
selectedDate: slotDate,
|
|
3871
|
-
onDateChange:
|
|
3971
|
+
onDateChange: handleSlotDateChange,
|
|
3872
3972
|
selectedSlot: selection.startTime,
|
|
3873
3973
|
onSlotSelect: handleSlotSelect,
|
|
3874
3974
|
partySize: selection.partySize,
|