@ticketboothapp/booking 1.2.62 → 1.2.63
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/package.json +1 -1
- package/src/components/booking/AdminChangeBookingFlow.tsx +4915 -0
- package/src/components/booking/BookingFlow.tsx +8 -3
- package/src/components/booking/ChangeBookingFlow.tsx +112 -578
- package/src/components/booking/booking-flow-types.ts +3 -5
- package/src/index.ts +1 -0
- package/src/lib/booking/change-flow-pricing.ts +6 -5
|
@@ -11,7 +11,6 @@ import {
|
|
|
11
11
|
quoteChangeBooking,
|
|
12
12
|
confirmFreeChangeBooking,
|
|
13
13
|
createChangeBookingPaymentIntent,
|
|
14
|
-
confirmBookingWithoutPayment,
|
|
15
14
|
getAddOns,
|
|
16
15
|
validatePromoCode,
|
|
17
16
|
getPromoDiscount,
|
|
@@ -33,7 +32,6 @@ import {
|
|
|
33
32
|
} from '../../lib/booking-constants';
|
|
34
33
|
import { getSundayOfWeek } from '../../lib/booking/sunday-week';
|
|
35
34
|
import { Calendar } from './Calendar';
|
|
36
|
-
import { AdminPaymentChoiceModal } from './AdminPaymentChoiceModal';
|
|
37
35
|
import { ItineraryBox } from './ItineraryBox';
|
|
38
36
|
import { ItineraryPlaceholder } from './ItineraryPlaceholder';
|
|
39
37
|
import { PickupTimeSelector } from './PickupTimeSelector';
|
|
@@ -98,7 +96,6 @@ import { getItineraryStepLabel } from '../../lib/booking/itinerary-display';
|
|
|
98
96
|
import { MANAGE_BOOKING_FROM_CHANGE_PAYMENT, MANAGE_BOOKING_QUERY_FROM } from '../../lib/manage-booking-post-checkout';
|
|
99
97
|
import { useBookingHost } from '../../runtime';
|
|
100
98
|
import type { BookingFlowUiOptions } from './booking-flow-ui';
|
|
101
|
-
import type { ProviderDashboardChangeBookingPayload } from './provider-dashboard-change-booking';
|
|
102
99
|
import type { ChangeBookingFlowProps, ChangeFlowSelectionPreview } from './booking-flow-types';
|
|
103
100
|
import { BOOKING_FLOW_ABANDON_EVENT } from '../../providers/booking-dialog-provider';
|
|
104
101
|
|
|
@@ -113,7 +110,6 @@ export type { ChangeBookingFlowProps } from './booking-flow-types';
|
|
|
113
110
|
*
|
|
114
111
|
* Until the first successful quote, `selfServeCheckoutPlaceholder` still avoids showing unchecked totals.
|
|
115
112
|
*
|
|
116
|
-
* **Provider dashboard** (`onChangeBooking`) — unchanged; manual pricing via `flowUi.providerDashboardChangePricingUi`.
|
|
117
113
|
*/
|
|
118
114
|
function mergeQuoteSliceWithServerPreview(
|
|
119
115
|
slice: ReturnType<typeof sliceChangeQuoteForUi>,
|
|
@@ -410,9 +406,8 @@ function pickOutboundMatchingPreviousSelection(
|
|
|
410
406
|
anchor: { productOptionId: string | null; minutesFromMidnight: number },
|
|
411
407
|
companyTimezone: string,
|
|
412
408
|
optionsMap: Map<string, { mostPopular?: boolean }>,
|
|
413
|
-
isAdmin: boolean
|
|
414
409
|
): Availability | null {
|
|
415
|
-
const selectable = timesForSelectedDate.filter((a) => (a.vacancies ?? 0) > 0
|
|
410
|
+
const selectable = timesForSelectedDate.filter((a) => (a.vacancies ?? 0) > 0);
|
|
416
411
|
if (selectable.length === 0) return null;
|
|
417
412
|
|
|
418
413
|
const withOption =
|
|
@@ -446,9 +441,8 @@ function pickReturnMatchingPreviousSelection(
|
|
|
446
441
|
sortedReturnOptions: ReturnOption[],
|
|
447
442
|
anchor: { returnLocation: string; minutesFromMidnight: number },
|
|
448
443
|
companyTimezone: string,
|
|
449
|
-
isAdmin: boolean
|
|
450
444
|
): ReturnOption | null {
|
|
451
|
-
const eligible = sortedReturnOptions.filter((o) => (o.vacancies ?? 0) > 0
|
|
445
|
+
const eligible = sortedReturnOptions.filter((o) => (o.vacancies ?? 0) > 0);
|
|
452
446
|
if (eligible.length === 0) return null;
|
|
453
447
|
|
|
454
448
|
const sameLoc = eligible.filter((o) => o.returnLocation === anchor.returnLocation);
|
|
@@ -637,7 +631,7 @@ function resolveInitialAvailabilityFromBooking(
|
|
|
637
631
|
}
|
|
638
632
|
|
|
639
633
|
/**
|
|
640
|
-
* Customer self-serve
|
|
634
|
+
* Customer self-serve **change booking** only (no standard new-booking paths).
|
|
641
635
|
* Duplicated from {@link NewBookingFlow} intentionally so each flow can evolve independently.
|
|
642
636
|
*/
|
|
643
637
|
export function ChangeBookingFlow({
|
|
@@ -662,7 +656,6 @@ export function ChangeBookingFlow({
|
|
|
662
656
|
partnerPortalBooking = false,
|
|
663
657
|
availabilityPricingProfileId,
|
|
664
658
|
availabilityCancellationPolicyProfileId,
|
|
665
|
-
onChangeBooking,
|
|
666
659
|
}: ChangeBookingFlowProps) {
|
|
667
660
|
/** Always the booking’s sold currency — not the site currency switcher / parent default. */
|
|
668
661
|
const currency = useMemo((): Currency => {
|
|
@@ -674,16 +667,6 @@ export function ChangeBookingFlow({
|
|
|
674
667
|
return DEFAULT_CURRENCY;
|
|
675
668
|
}, [originalReceipt?.currency, initialValues?.currency, currencyFromParent]);
|
|
676
669
|
|
|
677
|
-
const isManualOverrideEligibleLine = (line: { editable: boolean; type?: string; label?: string }): boolean => {
|
|
678
|
-
if (!line.editable) return false;
|
|
679
|
-
const type = (line.type ?? '').toUpperCase();
|
|
680
|
-
const label = (line.label ?? '').toLowerCase();
|
|
681
|
-
const isPromoLikeType =
|
|
682
|
-
type.includes('PROMO') || type.includes('DISCOUNT') || type.includes('VOUCHER') || type.includes('GIFT');
|
|
683
|
-
const isPromoLikeLabel =
|
|
684
|
-
label.includes('promo') || label.includes('discount') || label.includes('voucher') || label.includes('gift');
|
|
685
|
-
return !(isPromoLikeType || isPromoLikeLabel);
|
|
686
|
-
};
|
|
687
670
|
const { env, analytics } = useBookingHost();
|
|
688
671
|
const { t } = useTranslations();
|
|
689
672
|
const { locale } = useLocale();
|
|
@@ -692,15 +675,12 @@ export function ChangeBookingFlow({
|
|
|
692
675
|
const cancellationPolicyProfileIdForAvailabilities =
|
|
693
676
|
(availabilityCancellationPolicyProfileId ?? '').trim() || null;
|
|
694
677
|
const {
|
|
695
|
-
permissions,
|
|
696
678
|
isSimplifiedPricingView,
|
|
697
679
|
onShowManage,
|
|
698
680
|
getSuccessUrl,
|
|
699
681
|
suppressCalendarDateScroll,
|
|
700
|
-
mode: bookingAppMode,
|
|
701
682
|
} = useBookingApp();
|
|
702
683
|
const availabilitiesCache = useAvailabilitiesCache();
|
|
703
|
-
const isAdmin = permissions.viewerRole === 'admin';
|
|
704
684
|
const [availabilities, setAvailabilities] = useState<Availability[]>([]);
|
|
705
685
|
const [selectedAvailability, setSelectedAvailability] = useState<Availability | null>(null);
|
|
706
686
|
const [selectedReturnOption, setSelectedReturnOption] = useState<ReturnOption | null>(null);
|
|
@@ -799,12 +779,6 @@ export function ChangeBookingFlow({
|
|
|
799
779
|
differenceTotal: number;
|
|
800
780
|
};
|
|
801
781
|
} | null>(null);
|
|
802
|
-
/** Admin only: skip sending confirmation at creation (provider dashboard). */
|
|
803
|
-
const [skipConfirmationCommunications, setSkipConfirmationCommunications] = useState(false);
|
|
804
|
-
/** Admin only: disable all auto communications for this booking (provider dashboard). */
|
|
805
|
-
const [disableAutoCommunications, setDisableAutoCommunications] = useState(false);
|
|
806
|
-
/** Admin only: show choice to pay now or confirm without payment (full balance owed). */
|
|
807
|
-
const [showAdminPaymentChoice, setShowAdminPaymentChoice] = useState(false);
|
|
808
782
|
const hasAppliedInitialValuesRef = useRef(false);
|
|
809
783
|
const hasAppliedInitialQuantitiesRef = useRef(false);
|
|
810
784
|
const hasHydratedAddOnsFromReceiptRef = useRef(false);
|
|
@@ -823,10 +797,8 @@ export function ChangeBookingFlow({
|
|
|
823
797
|
return null;
|
|
824
798
|
}
|
|
825
799
|
}, [initialValues?.dateTime, companyTimezone]);
|
|
826
|
-
const isProviderDashboardChange = Boolean(onChangeBooking);
|
|
827
|
-
const isCustomerSelfServeChange = !isProviderDashboardChange;
|
|
828
800
|
/** Do not render catalog-/FE-derived dollar amounts in UI until `quoteChangeBooking` returns `serverDisplay`. */
|
|
829
|
-
const suppressSelfServeCurrencyUi =
|
|
801
|
+
const suppressSelfServeCurrencyUi = true;
|
|
830
802
|
|
|
831
803
|
useEffect(() => {
|
|
832
804
|
setPartnerAttributionConfirmed(false);
|
|
@@ -836,13 +808,13 @@ export function ChangeBookingFlow({
|
|
|
836
808
|
* user picks a different return time — baseline is the first auto-selected return for this outbound.
|
|
837
809
|
*/
|
|
838
810
|
const [implicitReturnBaselineId, setImplicitReturnBaselineId] = useState<string | null>(null);
|
|
839
|
-
/**
|
|
811
|
+
/** Promo from booking is fixed — show read-only, never add new. */
|
|
840
812
|
const lockedPromoCode = initialValues?.promoCode?.trim()
|
|
841
813
|
? initialValues.promoCode.trim().toUpperCase()
|
|
842
814
|
: null;
|
|
843
815
|
/** Public self-serve only: cannot reduce tickets below original counts. */
|
|
844
816
|
const changeBookingMinimumQuantities = useMemo(() => {
|
|
845
|
-
if (!
|
|
817
|
+
if (!initialValues?.bookingItems?.length) return undefined;
|
|
846
818
|
const m: Record<string, number> = {};
|
|
847
819
|
for (const item of initialValues.bookingItems) {
|
|
848
820
|
const key = item.category?.trim();
|
|
@@ -850,30 +822,7 @@ export function ChangeBookingFlow({
|
|
|
850
822
|
m[key] = Math.max(0, Number(item.count) || 0);
|
|
851
823
|
}
|
|
852
824
|
return m;
|
|
853
|
-
}, [
|
|
854
|
-
const [adminChoiceData, setAdminChoiceData] = useState<{
|
|
855
|
-
reservationReference: string;
|
|
856
|
-
reservationExpiration?: string;
|
|
857
|
-
checkoutBreakdown: { lineItems: Array<{ label: string; amount: number; type?: string; quantity?: number }>; totalAmount: number; currency: string };
|
|
858
|
-
totalAmount: number;
|
|
859
|
-
datePart: string;
|
|
860
|
-
timePart: string;
|
|
861
|
-
availabilityProductOptionId: string;
|
|
862
|
-
itineraryDisplay?: ItineraryDisplayStep[] | null;
|
|
863
|
-
clientSecret: string;
|
|
864
|
-
ticketLinesForModal: CheckoutModalLineItem[];
|
|
865
|
-
feeLineItems: OrderSummary['feeLineItems'];
|
|
866
|
-
returnPriceAdjustment: number;
|
|
867
|
-
cancellationPolicyFee: number;
|
|
868
|
-
cancellationPolicyLabel?: string;
|
|
869
|
-
subtotal: number;
|
|
870
|
-
tax: number;
|
|
871
|
-
totalQuantity: number;
|
|
872
|
-
isTaxIncludedInPrice: boolean;
|
|
873
|
-
taxRate: number;
|
|
874
|
-
promoDiscountAmount: number;
|
|
875
|
-
discountLabel?: string | null;
|
|
876
|
-
} | null>(null);
|
|
825
|
+
}, [initialValues?.bookingItems]);
|
|
877
826
|
const [latestChangeQuote, setLatestChangeQuote] = useState<{
|
|
878
827
|
priceDiff: number;
|
|
879
828
|
currency: Currency;
|
|
@@ -1710,14 +1659,12 @@ export function ChangeBookingFlow({
|
|
|
1710
1659
|
}, [initialValues?.productId, product.productId]);
|
|
1711
1660
|
|
|
1712
1661
|
/**
|
|
1713
|
-
* Receipt pricing on protected seats/fees
|
|
1714
|
-
* + same product option) vs Rule B (`max(receipt, live)` when date or option changes).
|
|
1662
|
+
* Receipt pricing on protected seats/fees: Rule A (exact receipt unit when same calendar day
|
|
1663
|
+
* + same product option) vs Rule B (`max(receipt, live)` when date or option changes).
|
|
1715
1664
|
*/
|
|
1716
1665
|
const changeFlowApplyReceiptPaidFloors = useMemo(
|
|
1717
|
-
() =>
|
|
1718
|
-
|
|
1719
|
-
changeFlowBookingParentProductIdForFloors === product.productId.trim(),
|
|
1720
|
-
[isProviderDashboardChange, changeFlowBookingParentProductIdForFloors, product.productId],
|
|
1666
|
+
() => changeFlowBookingParentProductIdForFloors === product.productId.trim(),
|
|
1667
|
+
[changeFlowBookingParentProductIdForFloors, product.productId],
|
|
1721
1668
|
);
|
|
1722
1669
|
|
|
1723
1670
|
useEffect(() => {
|
|
@@ -1843,27 +1790,27 @@ export function ChangeBookingFlow({
|
|
|
1843
1790
|
|
|
1844
1791
|
const initialAddOnMinQtyByKey = useMemo(() => {
|
|
1845
1792
|
const map = new Map<string, number>();
|
|
1846
|
-
if (
|
|
1793
|
+
if (false) return map;
|
|
1847
1794
|
for (const sel of initialAddOnBaselineSelections) {
|
|
1848
1795
|
const key = `${sel.addOnId.trim()}::${sel.variantId?.trim() || ''}`;
|
|
1849
1796
|
map.set(key, (map.get(key) ?? 0) + Math.max(1, Number(sel.quantity) || 1));
|
|
1850
1797
|
}
|
|
1851
1798
|
return map;
|
|
1852
|
-
}, [
|
|
1799
|
+
}, [true, initialAddOnBaselineSelections]);
|
|
1853
1800
|
|
|
1854
1801
|
const initialAddOnMinTotalByAddOnId = useMemo(() => {
|
|
1855
1802
|
const map = new Map<string, number>();
|
|
1856
|
-
if (
|
|
1803
|
+
if (false) return map;
|
|
1857
1804
|
for (const sel of initialAddOnBaselineSelections) {
|
|
1858
1805
|
const addOnId = sel.addOnId.trim();
|
|
1859
1806
|
map.set(addOnId, (map.get(addOnId) ?? 0) + Math.max(1, Number(sel.quantity) || 1));
|
|
1860
1807
|
}
|
|
1861
1808
|
return map;
|
|
1862
|
-
}, [
|
|
1809
|
+
}, [true, initialAddOnBaselineSelections]);
|
|
1863
1810
|
|
|
1864
1811
|
const applyChangeFlowAddOnFloor = useCallback(
|
|
1865
1812
|
(nextSelections: Array<{ addOnId: string; variantId?: string; quantity?: number }>) => {
|
|
1866
|
-
if (
|
|
1813
|
+
if (false || initialAddOnMinQtyByKey.size === 0) return nextSelections;
|
|
1867
1814
|
const qtyByKey = new Map<string, number>();
|
|
1868
1815
|
for (const sel of nextSelections) {
|
|
1869
1816
|
const key = `${sel.addOnId.trim()}::${sel.variantId?.trim() || ''}`;
|
|
@@ -1890,7 +1837,7 @@ export function ChangeBookingFlow({
|
|
|
1890
1837
|
return { addOnId, variantId, quantity: qty };
|
|
1891
1838
|
});
|
|
1892
1839
|
},
|
|
1893
|
-
[
|
|
1840
|
+
[true, initialAddOnMinQtyByKey, initialAddOnMinTotalByAddOnId]
|
|
1894
1841
|
);
|
|
1895
1842
|
|
|
1896
1843
|
const updateAddOnSelections = useCallback(
|
|
@@ -2504,14 +2451,14 @@ export function ChangeBookingFlow({
|
|
|
2504
2451
|
const options = selectedAvailability?.returnOptions ?? [];
|
|
2505
2452
|
const serverReturnMap = latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId;
|
|
2506
2453
|
const floor = effectiveChangeFlowReturnUnitFloorPerPerson;
|
|
2507
|
-
const applyReturnFloor = floor != null &&
|
|
2454
|
+
const applyReturnFloor = floor != null && true;
|
|
2508
2455
|
return options.map((opt) => {
|
|
2509
2456
|
const vacancyCredit = changeFlowSeatCreditForReturnAvailabilityId(opt.returnAvailabilityId);
|
|
2510
2457
|
const adjustedVacancies = Math.max(0, opt.vacancies ?? 0) + vacancyCredit;
|
|
2511
2458
|
const rawPerPerson = opt.priceAdjustmentByCurrency?.[currency] ?? 0;
|
|
2512
2459
|
const flooredPerPerson = applyReturnFloor ? Math.max(rawPerPerson, floor) : rawPerPerson;
|
|
2513
2460
|
let perPerson = flooredPerPerson;
|
|
2514
|
-
if (
|
|
2461
|
+
if (true && serverReturnMap && opt.returnAvailabilityId) {
|
|
2515
2462
|
const sid = opt.returnAvailabilityId.trim();
|
|
2516
2463
|
const sp = serverReturnMap[sid];
|
|
2517
2464
|
if (sp != null && Number.isFinite(sp)) {
|
|
@@ -2534,7 +2481,7 @@ export function ChangeBookingFlow({
|
|
|
2534
2481
|
}, [
|
|
2535
2482
|
selectedAvailability?.returnOptions,
|
|
2536
2483
|
effectiveChangeFlowReturnUnitFloorPerPerson,
|
|
2537
|
-
|
|
2484
|
+
true,
|
|
2538
2485
|
currency,
|
|
2539
2486
|
changeFlowSeatCreditForReturnAvailabilityId,
|
|
2540
2487
|
latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId,
|
|
@@ -2543,11 +2490,11 @@ export function ChangeBookingFlow({
|
|
|
2543
2490
|
const selectedReturnOptionWithFloor = useMemo(() => {
|
|
2544
2491
|
if (!selectedReturnOption) return selectedReturnOption;
|
|
2545
2492
|
const floor = effectiveChangeFlowReturnUnitFloorPerPerson;
|
|
2546
|
-
const applyReturnFloor = floor != null &&
|
|
2493
|
+
const applyReturnFloor = floor != null && true;
|
|
2547
2494
|
const rawPerPerson = selectedReturnOption.priceAdjustmentByCurrency?.[currency] ?? 0;
|
|
2548
2495
|
let perPerson = rawPerPerson;
|
|
2549
2496
|
const serverReturnMap = latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId;
|
|
2550
|
-
if (
|
|
2497
|
+
if (true && serverReturnMap && selectedReturnOption.returnAvailabilityId) {
|
|
2551
2498
|
const sid = selectedReturnOption.returnAvailabilityId.trim();
|
|
2552
2499
|
const sp = serverReturnMap[sid];
|
|
2553
2500
|
if (sp != null && Number.isFinite(sp)) {
|
|
@@ -2568,7 +2515,7 @@ export function ChangeBookingFlow({
|
|
|
2568
2515
|
}, [
|
|
2569
2516
|
selectedReturnOption,
|
|
2570
2517
|
effectiveChangeFlowReturnUnitFloorPerPerson,
|
|
2571
|
-
|
|
2518
|
+
true,
|
|
2572
2519
|
currency,
|
|
2573
2520
|
latestChangeQuote?.serverPreview?.returnOptionPriceByReturnAvailabilityId,
|
|
2574
2521
|
]);
|
|
@@ -2609,7 +2556,7 @@ export function ChangeBookingFlow({
|
|
|
2609
2556
|
/** Quote `ticketUnitPriceByCategory` overrides catalog units for ticket rows (customer self-serve only). */
|
|
2610
2557
|
const pricingForTicketSelector = useMemo(() => {
|
|
2611
2558
|
const m = latestChangeQuote?.serverPreview?.ticketUnitPriceByCategory;
|
|
2612
|
-
if (
|
|
2559
|
+
if (false || !m) return pricing;
|
|
2613
2560
|
return pricing.map((r) => {
|
|
2614
2561
|
const u = m[r.category.toUpperCase()];
|
|
2615
2562
|
if (u == null || !Number.isFinite(u)) return r;
|
|
@@ -2620,7 +2567,7 @@ export function ChangeBookingFlow({
|
|
|
2620
2567
|
baseInDisplayCurrency: u,
|
|
2621
2568
|
};
|
|
2622
2569
|
});
|
|
2623
|
-
}, [pricing, latestChangeQuote?.serverPreview?.ticketUnitPriceByCategory,
|
|
2570
|
+
}, [pricing, latestChangeQuote?.serverPreview?.ticketUnitPriceByCategory, true]);
|
|
2624
2571
|
|
|
2625
2572
|
// Price breakdown: mid-layer returns line items (base + one per rule/deal). UI renders each line; rate in brackets when used.
|
|
2626
2573
|
const getPriceBreakdown = useCallback((category: string, priceCAD: number, baseInDisplayCurrency: number | undefined, appliedAdjustments: Array<{ type: string; id: string; name: string; changeByCurrency?: Record<string, number> }> = []): PriceBreakdownData | null => {
|
|
@@ -2725,7 +2672,7 @@ export function ChangeBookingFlow({
|
|
|
2725
2672
|
|
|
2726
2673
|
// When return selection (or refreshed availabilities) caps the party below current ticket counts, trim tickets (non-admin).
|
|
2727
2674
|
useEffect(() => {
|
|
2728
|
-
if (
|
|
2675
|
+
if (false || !selectedAvailability) return;
|
|
2729
2676
|
const cap = effectivePartySizeCap;
|
|
2730
2677
|
if (totalQuantity <= cap) return;
|
|
2731
2678
|
const over = totalQuantity - cap;
|
|
@@ -2753,7 +2700,7 @@ export function ChangeBookingFlow({
|
|
|
2753
2700
|
effectivePartySizeCap,
|
|
2754
2701
|
totalQuantity,
|
|
2755
2702
|
selectedAvailability,
|
|
2756
|
-
|
|
2703
|
+
false,
|
|
2757
2704
|
changeBookingMinimumQuantities,
|
|
2758
2705
|
]);
|
|
2759
2706
|
|
|
@@ -2827,7 +2774,7 @@ export function ChangeBookingFlow({
|
|
|
2827
2774
|
*/
|
|
2828
2775
|
const changeFlowProtectedReturnAdjustment = useMemo(() => {
|
|
2829
2776
|
if (totalQuantity <= 0) return returnPriceAdjustment;
|
|
2830
|
-
if (
|
|
2777
|
+
if (false) return returnPriceAdjustment;
|
|
2831
2778
|
if (effectiveChangeFlowReturnUnitFloorPerPerson == null) return returnPriceAdjustment;
|
|
2832
2779
|
const livePerPerson =
|
|
2833
2780
|
returnOptionCatalogPerPerson ?? (selectedReturnOption?.priceAdjustmentByCurrency?.[currency] ?? 0);
|
|
@@ -2841,7 +2788,7 @@ export function ChangeBookingFlow({
|
|
|
2841
2788
|
}, [
|
|
2842
2789
|
totalQuantity,
|
|
2843
2790
|
returnPriceAdjustment,
|
|
2844
|
-
|
|
2791
|
+
false,
|
|
2845
2792
|
effectiveChangeFlowReturnUnitFloorPerPerson,
|
|
2846
2793
|
selectedReturnOption,
|
|
2847
2794
|
returnOptionCatalogPerPerson,
|
|
@@ -2852,7 +2799,7 @@ export function ChangeBookingFlow({
|
|
|
2852
2799
|
|
|
2853
2800
|
/** Return row amount for PriceSummary, Stripe breakdown, and CheckoutModal (catalog vs protected same-product-option). */
|
|
2854
2801
|
const checkoutReturnLineAmount = useMemo(() => {
|
|
2855
|
-
if (
|
|
2802
|
+
if (true) {
|
|
2856
2803
|
return changeFlowProtectedReturnAdjustment;
|
|
2857
2804
|
}
|
|
2858
2805
|
if (changeFlowApplyReceiptPaidFloors) {
|
|
@@ -2860,7 +2807,7 @@ export function ChangeBookingFlow({
|
|
|
2860
2807
|
}
|
|
2861
2808
|
return returnPriceAdjustment;
|
|
2862
2809
|
}, [
|
|
2863
|
-
|
|
2810
|
+
true,
|
|
2864
2811
|
changeFlowApplyReceiptPaidFloors,
|
|
2865
2812
|
changeFlowProtectedReturnAdjustment,
|
|
2866
2813
|
returnPriceAdjustment,
|
|
@@ -2932,35 +2879,13 @@ export function ChangeBookingFlow({
|
|
|
2932
2879
|
return [...feeLineItems, ...addOnLines];
|
|
2933
2880
|
}, [feeLineItems, addOnSelections, addOns]);
|
|
2934
2881
|
|
|
2935
|
-
const providerPricingUi = flowUi?.providerDashboardChangePricingUi;
|
|
2936
|
-
const providerQuotedLines = providerPricingUi?.quotedLines ?? [];
|
|
2937
|
-
const providerEditableLines = providerQuotedLines.filter((line) => isManualOverrideEligibleLine(line));
|
|
2938
|
-
const showProviderPricingInlineEditor =
|
|
2939
|
-
isProviderDashboardChange && isAdmin && (
|
|
2940
|
-
providerPricingUi?.loading ||
|
|
2941
|
-
providerPricingUi?.error != null ||
|
|
2942
|
-
providerQuotedLines.length > 0
|
|
2943
|
-
);
|
|
2944
|
-
const normalizeReceiptLabel = (value: string): string =>
|
|
2945
|
-
value
|
|
2946
|
-
.toLowerCase()
|
|
2947
|
-
.replace(/\([^)]*\)/g, '')
|
|
2948
|
-
.replace(/[^a-z0-9]+/g, '')
|
|
2949
|
-
.trim();
|
|
2950
|
-
const providerEditableLineByNormalizedLabel = useMemo(() => {
|
|
2951
|
-
const m = new Map<string, (typeof providerEditableLines)[number]>();
|
|
2952
|
-
for (const line of providerEditableLines) {
|
|
2953
|
-
const key = normalizeReceiptLabel(line.label ?? '');
|
|
2954
|
-
if (key) m.set(key, line);
|
|
2955
|
-
}
|
|
2956
|
-
return m;
|
|
2957
|
-
}, [providerEditableLines]);
|
|
2958
|
-
|
|
2959
2882
|
const checkoutPriceSummaryLines = useMemo((): PriceSummaryLine[] => {
|
|
2960
2883
|
if (!selectedAvailability) return [];
|
|
2961
2884
|
const returnLineAmount = checkoutReturnLineAmount;
|
|
2885
|
+
/** Show row when a return is selected and either the priced amount is non-zero or a receipt return floor exists (catalog slot can be $0 while floored total is not). */
|
|
2962
2886
|
const showReturnLine =
|
|
2963
|
-
Boolean(selectedReturnOption) &&
|
|
2887
|
+
Boolean(selectedReturnOption) &&
|
|
2888
|
+
(Math.abs(returnLineAmount) > 0.0005 || effectiveChangeFlowReturnUnitFloorPerPerson != null);
|
|
2964
2889
|
return [
|
|
2965
2890
|
...ticketLineItemsForChangeFlowDisplay.map((line): PriceSummaryLine => {
|
|
2966
2891
|
const rate = pricing.find((r) => r.category === line.category);
|
|
@@ -2972,13 +2897,8 @@ export function ChangeBookingFlow({
|
|
|
2972
2897
|
);
|
|
2973
2898
|
return {
|
|
2974
2899
|
kind: 'ticket',
|
|
2975
|
-
lineKey:
|
|
2976
|
-
|
|
2977
|
-
? providerEditableLineByNormalizedLabel.get(normalizeReceiptLabel(line.category))?.lineKey
|
|
2978
|
-
: undefined,
|
|
2979
|
-
editable:
|
|
2980
|
-
showProviderPricingInlineEditor &&
|
|
2981
|
-
providerEditableLineByNormalizedLabel.has(normalizeReceiptLabel(line.category)),
|
|
2900
|
+
lineKey: undefined,
|
|
2901
|
+
editable: false,
|
|
2982
2902
|
category: line.category,
|
|
2983
2903
|
qty: line.qty,
|
|
2984
2904
|
itemTotal: line.itemTotal,
|
|
@@ -3013,25 +2933,8 @@ export function ChangeBookingFlow({
|
|
|
3013
2933
|
fee.name.toLowerCase().includes('license'));
|
|
3014
2934
|
return {
|
|
3015
2935
|
kind: 'line' as const,
|
|
3016
|
-
lineKey:
|
|
3017
|
-
|
|
3018
|
-
? providerEditableLineByNormalizedLabel.get(
|
|
3019
|
-
normalizeReceiptLabel(
|
|
3020
|
-
feeLineItems.some((f) => f.name === fee.name)
|
|
3021
|
-
? `${fee.name} (${totalQuantity} ${totalQuantity === 1 ? 'person' : 'people'})`
|
|
3022
|
-
: fee.name
|
|
3023
|
-
)
|
|
3024
|
-
)?.lineKey
|
|
3025
|
-
: undefined,
|
|
3026
|
-
editable:
|
|
3027
|
-
showProviderPricingInlineEditor &&
|
|
3028
|
-
providerEditableLineByNormalizedLabel.has(
|
|
3029
|
-
normalizeReceiptLabel(
|
|
3030
|
-
feeLineItems.some((f) => f.name === fee.name)
|
|
3031
|
-
? `${fee.name} (${totalQuantity} ${totalQuantity === 1 ? 'person' : 'people'})`
|
|
3032
|
-
: fee.name
|
|
3033
|
-
)
|
|
3034
|
-
),
|
|
2936
|
+
lineKey: undefined,
|
|
2937
|
+
editable: false,
|
|
3035
2938
|
label: feeLineItems.some((f) => f.name === fee.name)
|
|
3036
2939
|
? `${fee.name} (${totalQuantity} ${totalQuantity === 1 ? 'person' : 'people'})`
|
|
3037
2940
|
: fee.name,
|
|
@@ -3057,8 +2960,7 @@ export function ChangeBookingFlow({
|
|
|
3057
2960
|
effectiveCancellationPolicyLabel,
|
|
3058
2961
|
feeLineItemsWithAddOns,
|
|
3059
2962
|
feeLineItems,
|
|
3060
|
-
|
|
3061
|
-
providerEditableLineByNormalizedLabel,
|
|
2963
|
+
effectiveChangeFlowReturnUnitFloorPerPerson,
|
|
3062
2964
|
]);
|
|
3063
2965
|
|
|
3064
2966
|
const checkoutPriceSummaryLinesForCheckout = useMemo(() => {
|
|
@@ -3524,62 +3426,31 @@ export function ChangeBookingFlow({
|
|
|
3524
3426
|
addOnSelections,
|
|
3525
3427
|
initialAddOnMinQtyByKey,
|
|
3526
3428
|
]);
|
|
3527
|
-
const hasChangeSelection =
|
|
3528
|
-
isProviderDashboardChange
|
|
3529
|
-
? changeSelectionDetails.hasOperationalChangesFromInitial
|
|
3530
|
-
: changeSelectionDetails.hasChangesFromInitial;
|
|
3429
|
+
const hasChangeSelection = changeSelectionDetails.hasChangesFromInitial;
|
|
3531
3430
|
|
|
3532
3431
|
const changeFlowNeedsServerPrice =
|
|
3533
|
-
|
|
3432
|
+
true &&
|
|
3534
3433
|
hasChangeSelection &&
|
|
3535
3434
|
!!initialValues?.bookingReference?.trim() &&
|
|
3536
3435
|
!!lastName.trim();
|
|
3537
3436
|
|
|
3538
|
-
const isChangeQuoteBlocked =
|
|
3539
|
-
const requiresReturnInChangeFlow =
|
|
3437
|
+
const isChangeQuoteBlocked = true && latestChangeQuote?.canProceed === false;
|
|
3438
|
+
const requiresReturnInChangeFlow = true && !!initialValues?.returnAvailabilityId?.trim();
|
|
3540
3439
|
const missingRequiredReturnSelection = requiresReturnInChangeFlow && !selectedReturnOption;
|
|
3541
3440
|
|
|
3542
3441
|
const changeFlowSubmitDisabled =
|
|
3543
3442
|
missingRequiredReturnSelection ||
|
|
3544
|
-
(
|
|
3443
|
+
(true &&
|
|
3545
3444
|
changeFlowNeedsServerPrice &&
|
|
3546
3445
|
(changeQuoteLoading || (!latestChangeQuote && !changeQuoteFetchError)));
|
|
3547
3446
|
|
|
3548
|
-
const providerTotalsPreview =
|
|
3549
|
-
|
|
3550
|
-
? providerPricingUi.totalsPreview
|
|
3551
|
-
: null;
|
|
3552
|
-
const providerHasEditedLineOverrides =
|
|
3553
|
-
isProviderDashboardChange &&
|
|
3554
|
-
providerQuotedLines.some((line) => {
|
|
3555
|
-
if (!isManualOverrideEligibleLine(line)) return false;
|
|
3556
|
-
const raw = providerPricingUi?.lineAmountInputs?.[line.lineKey];
|
|
3557
|
-
const parsed = raw == null || raw.trim() === '' ? line.amount : Number(raw);
|
|
3558
|
-
if (!Number.isFinite(parsed)) return false;
|
|
3559
|
-
const rounded = Math.round(parsed * 100) / 100;
|
|
3560
|
-
return Math.abs(rounded - line.amount) > 0.0001;
|
|
3561
|
-
});
|
|
3562
|
-
const providerHasAdditionalAdjustments =
|
|
3563
|
-
isProviderDashboardChange &&
|
|
3564
|
-
(providerPricingUi?.additionalAdjustments ?? []).some((adj) => {
|
|
3565
|
-
const parsed = Number((adj.amountInput ?? '').trim());
|
|
3566
|
-
const hasAmount = Number.isFinite(parsed) && parsed > 0;
|
|
3567
|
-
const currentAmount = hasAmount ? (Math.round(parsed * 100) / 100).toFixed(2) : '';
|
|
3568
|
-
const originalAmount = adj.originalAmountInput ?? '';
|
|
3569
|
-
const currentLabel = (adj.label ?? '').trim();
|
|
3570
|
-
const originalLabel = (adj.originalLabel ?? '').trim();
|
|
3571
|
-
const currentMode = adj.mode;
|
|
3572
|
-
const originalMode = adj.originalMode;
|
|
3573
|
-
if (!originalMode) return hasAmount || currentLabel.length > 0;
|
|
3574
|
-
return currentAmount !== originalAmount || currentLabel !== originalLabel || currentMode !== originalMode;
|
|
3575
|
-
});
|
|
3576
|
-
const hasEffectiveChangeSelection =
|
|
3577
|
-
hasChangeSelection || providerHasEditedLineOverrides || providerHasAdditionalAdjustments;
|
|
3447
|
+
const providerTotalsPreview = null;
|
|
3448
|
+
const hasEffectiveChangeSelection = hasChangeSelection;
|
|
3578
3449
|
|
|
3579
3450
|
const displayedChangeAmounts = resolveChangeFlowDisplayedAmounts({
|
|
3580
3451
|
providerPreview: providerTotalsPreview,
|
|
3581
3452
|
serverQuotePreview:
|
|
3582
|
-
|
|
3453
|
+
true && latestChangeQuote?.serverDisplay
|
|
3583
3454
|
? latestChangeQuote.serverDisplay
|
|
3584
3455
|
: null,
|
|
3585
3456
|
fromCart: {
|
|
@@ -3595,13 +3466,13 @@ export function ChangeBookingFlow({
|
|
|
3595
3466
|
const changeFlowClientEstimateDue = (() => {
|
|
3596
3467
|
if (!originalReceipt) return totalPrice;
|
|
3597
3468
|
// Customer self-serve: amount due comes from POST .../change/quote (`amountDueCents` / priceDiff), not FE delta math.
|
|
3598
|
-
if (
|
|
3469
|
+
if (true && latestChangeQuote != null && !changeQuoteFetchError) {
|
|
3599
3470
|
return normalizeNearZeroOwed(latestChangeQuote.priceDiff);
|
|
3600
3471
|
}
|
|
3601
3472
|
return changeFlowBalanceVsOriginal({
|
|
3602
3473
|
newTotal: displayChangeFlowProposedTotal,
|
|
3603
3474
|
originalReceiptTotal: originalReceipt.total,
|
|
3604
|
-
audience:
|
|
3475
|
+
audience: 'customer',
|
|
3605
3476
|
});
|
|
3606
3477
|
})();
|
|
3607
3478
|
|
|
@@ -3610,14 +3481,6 @@ export function ChangeBookingFlow({
|
|
|
3610
3481
|
|
|
3611
3482
|
const changeCheckoutButtonLabel = (() => {
|
|
3612
3483
|
if (!hasEffectiveChangeSelection) return undefined;
|
|
3613
|
-
if (isProviderDashboardChange) {
|
|
3614
|
-
const est = Math.round(changeFlowClientEstimateDue * 100) / 100;
|
|
3615
|
-
return est > 0
|
|
3616
|
-
? `Change booking (${formatCurrencyAmount(est, currency, locale as 'en' | 'fr')})`
|
|
3617
|
-
: est < 0
|
|
3618
|
-
? `Change booking (${formatCurrencyAmount(est, currency, locale as 'en' | 'fr')})`
|
|
3619
|
-
: 'Change booking (no charge)';
|
|
3620
|
-
}
|
|
3621
3484
|
if (changeFlowNeedsServerPrice) {
|
|
3622
3485
|
if (changeQuoteLoading) {
|
|
3623
3486
|
const tr = t('booking.updatingPrice');
|
|
@@ -3651,39 +3514,8 @@ export function ChangeBookingFlow({
|
|
|
3651
3514
|
const checkoutFormError =
|
|
3652
3515
|
(error || '') ||
|
|
3653
3516
|
(missingRequiredReturnSelection ? 'Removing return option in self-serve is not available. Please contact support.' : '') ||
|
|
3654
|
-
(
|
|
3655
|
-
(
|
|
3656
|
-
|
|
3657
|
-
const providerPricingOverrides =
|
|
3658
|
-
isProviderDashboardChange && providerQuotedLines.length > 0
|
|
3659
|
-
? providerQuotedLines
|
|
3660
|
-
.filter((line) => isManualOverrideEligibleLine(line))
|
|
3661
|
-
.map((line) => {
|
|
3662
|
-
const raw = providerPricingUi?.lineAmountInputs?.[line.lineKey];
|
|
3663
|
-
const parsed = raw == null || raw.trim() === '' ? line.amount : Number(raw);
|
|
3664
|
-
if (!Number.isFinite(parsed)) return null;
|
|
3665
|
-
const rounded = Math.round(parsed * 100) / 100;
|
|
3666
|
-
return Math.abs(rounded - line.amount) > 0.0001
|
|
3667
|
-
? { lineKey: line.lineKey, amount: rounded, reason: 'Provider dashboard override' }
|
|
3668
|
-
: null;
|
|
3669
|
-
})
|
|
3670
|
-
.filter((v): v is { lineKey: string; amount: number; reason: string } => v != null)
|
|
3671
|
-
: [];
|
|
3672
|
-
const providerAdditionalAdjustments =
|
|
3673
|
-
isProviderDashboardChange
|
|
3674
|
-
? (providerPricingUi?.additionalAdjustments ?? [])
|
|
3675
|
-
.map((adj) => {
|
|
3676
|
-
const parsed = Number((adj.amountInput ?? '').trim());
|
|
3677
|
-
if (!Number.isFinite(parsed) || parsed <= 0) return null;
|
|
3678
|
-
const rounded = Math.round(parsed * 100) / 100;
|
|
3679
|
-
const signed = adj.mode === 'DISCOUNT' ? -rounded : rounded;
|
|
3680
|
-
return {
|
|
3681
|
-
label: adj.label?.trim() || (signed < 0 ? 'Provider discount' : 'Provider charge'),
|
|
3682
|
-
amount: signed,
|
|
3683
|
-
};
|
|
3684
|
-
})
|
|
3685
|
-
.filter((v): v is { label: string; amount: number } => v != null)
|
|
3686
|
-
: [];
|
|
3517
|
+
(true && isChangeQuoteBlocked ? (latestChangeQuote?.reasonIfBlocked ?? '') : '') ||
|
|
3518
|
+
(true ? changeQuoteFetchError ?? '' : '');
|
|
3687
3519
|
|
|
3688
3520
|
const changeFlowSelectionPreview = useMemo((): ChangeFlowSelectionPreview | null => {
|
|
3689
3521
|
if (!selectedAvailability || totalQuantity <= 0) return null;
|
|
@@ -3789,7 +3621,7 @@ export function ChangeBookingFlow({
|
|
|
3789
3621
|
|
|
3790
3622
|
/** Debounced server quote so CTA + “amount owed” match PaymentIntent; avoids free confirm when FE estimate ≠ BE. */
|
|
3791
3623
|
useEffect(() => {
|
|
3792
|
-
if (
|
|
3624
|
+
if (false) {
|
|
3793
3625
|
setChangeQuoteLoading(false);
|
|
3794
3626
|
setChangeQuoteFetchError(null);
|
|
3795
3627
|
setLatestChangeQuote(null);
|
|
@@ -3880,7 +3712,7 @@ export function ChangeBookingFlow({
|
|
|
3880
3712
|
window.clearTimeout(timer);
|
|
3881
3713
|
};
|
|
3882
3714
|
}, [
|
|
3883
|
-
|
|
3715
|
+
true,
|
|
3884
3716
|
hasChangeSelection,
|
|
3885
3717
|
selectedAvailability,
|
|
3886
3718
|
selectedAvailability?.dateTime,
|
|
@@ -3931,8 +3763,7 @@ export function ChangeBookingFlow({
|
|
|
3931
3763
|
timesForSelectedDate,
|
|
3932
3764
|
anchor,
|
|
3933
3765
|
companyTimezone,
|
|
3934
|
-
optionsMap
|
|
3935
|
-
isAdmin
|
|
3766
|
+
optionsMap
|
|
3936
3767
|
);
|
|
3937
3768
|
changeFlowOutboundAnchorRef.current = null;
|
|
3938
3769
|
if (matched) {
|
|
@@ -4023,8 +3854,7 @@ export function ChangeBookingFlow({
|
|
|
4023
3854
|
const fromAnchor = pickReturnMatchingPreviousSelection(
|
|
4024
3855
|
sorted,
|
|
4025
3856
|
returnAnchor,
|
|
4026
|
-
companyTimezone
|
|
4027
|
-
isAdmin
|
|
3857
|
+
companyTimezone
|
|
4028
3858
|
);
|
|
4029
3859
|
changeFlowReturnAnchorRef.current = null;
|
|
4030
3860
|
if (fromAnchor) {
|
|
@@ -4070,7 +3900,7 @@ export function ChangeBookingFlow({
|
|
|
4070
3900
|
selectedAvailability,
|
|
4071
3901
|
selectedReturnOption,
|
|
4072
3902
|
companyTimezone,
|
|
4073
|
-
|
|
3903
|
+
false,
|
|
4074
3904
|
initialValues?.returnAvailabilityId,
|
|
4075
3905
|
initialValues?.returnDateTime,
|
|
4076
3906
|
]);
|
|
@@ -4133,9 +3963,9 @@ export function ChangeBookingFlow({
|
|
|
4133
3963
|
const firstWithInventory = dates.find((d) => {
|
|
4134
3964
|
const rows = availabilitiesByDate[d] ?? [];
|
|
4135
3965
|
if (rows.length === 0) return false;
|
|
4136
|
-
if (
|
|
3966
|
+
if (false) return rows.some((a) => (a.vacancies ?? 0) > 0);
|
|
4137
3967
|
if (
|
|
4138
|
-
|
|
3968
|
+
true &&
|
|
4139
3969
|
changeFlowInitialTicketCount > 0
|
|
4140
3970
|
) {
|
|
4141
3971
|
return rows.some(
|
|
@@ -4144,7 +3974,7 @@ export function ChangeBookingFlow({
|
|
|
4144
3974
|
}
|
|
4145
3975
|
return rows.some((a) => (a.vacancies ?? 0) > 0);
|
|
4146
3976
|
});
|
|
4147
|
-
const first = firstWithInventory ?? (
|
|
3977
|
+
const first = firstWithInventory ?? (false && dates[0] ? dates[0] : undefined);
|
|
4148
3978
|
if (!first) return;
|
|
4149
3979
|
|
|
4150
3980
|
hasAutoSelectedPartnerDateRef.current = true;
|
|
@@ -4167,8 +3997,8 @@ export function ChangeBookingFlow({
|
|
|
4167
3997
|
selectedDate,
|
|
4168
3998
|
dates,
|
|
4169
3999
|
availabilitiesByDate,
|
|
4170
|
-
|
|
4171
|
-
|
|
4000
|
+
false,
|
|
4001
|
+
true,
|
|
4172
4002
|
changeFlowInitialTicketCount,
|
|
4173
4003
|
getCalendarEffectiveOutboundVacancies,
|
|
4174
4004
|
useWindowScroll,
|
|
@@ -4206,7 +4036,7 @@ export function ChangeBookingFlow({
|
|
|
4206
4036
|
};
|
|
4207
4037
|
|
|
4208
4038
|
const handleQuantityChange = (category: string, delta: number) => {
|
|
4209
|
-
const maxAvailable =
|
|
4039
|
+
const maxAvailable = false ? Number.MAX_SAFE_INTEGER : effectivePartySizeCap;
|
|
4210
4040
|
const currentQty = quantities[category] || 0;
|
|
4211
4041
|
const minQ =
|
|
4212
4042
|
changeBookingMinimumQuantities != null
|
|
@@ -4214,7 +4044,7 @@ export function ChangeBookingFlow({
|
|
|
4214
4044
|
: 0;
|
|
4215
4045
|
const newQty = Math.max(minQ, currentQty + delta);
|
|
4216
4046
|
// Admin can overbook; non-admin cannot exceed vacancies
|
|
4217
|
-
if (delta > 0 && !
|
|
4047
|
+
if (delta > 0 && !false && orderSummary.totalQuantity >= maxAvailable) {
|
|
4218
4048
|
return;
|
|
4219
4049
|
}
|
|
4220
4050
|
setQuantities(prev => ({
|
|
@@ -4347,30 +4177,27 @@ export function ChangeBookingFlow({
|
|
|
4347
4177
|
return;
|
|
4348
4178
|
}
|
|
4349
4179
|
|
|
4350
|
-
|
|
4351
|
-
if (!
|
|
4352
|
-
|
|
4353
|
-
|
|
4354
|
-
|
|
4355
|
-
return;
|
|
4356
|
-
}
|
|
4180
|
+
// Validate email (required)
|
|
4181
|
+
if (!email) {
|
|
4182
|
+
setError(t('booking.enterEmail') || 'Please enter your email address');
|
|
4183
|
+
return;
|
|
4184
|
+
}
|
|
4357
4185
|
|
|
4358
|
-
|
|
4359
|
-
|
|
4360
|
-
|
|
4361
|
-
|
|
4186
|
+
if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)) {
|
|
4187
|
+
setError(t('booking.invalidEmail') || 'Please enter a valid email address');
|
|
4188
|
+
return;
|
|
4189
|
+
}
|
|
4362
4190
|
|
|
4363
|
-
|
|
4364
|
-
|
|
4365
|
-
|
|
4366
|
-
|
|
4367
|
-
|
|
4191
|
+
// Validate first name (required)
|
|
4192
|
+
if (!firstName?.trim()) {
|
|
4193
|
+
setError(t('booking.enterFirstName') || 'Please enter your first name');
|
|
4194
|
+
return;
|
|
4195
|
+
}
|
|
4368
4196
|
|
|
4369
|
-
|
|
4370
|
-
|
|
4371
|
-
|
|
4372
|
-
|
|
4373
|
-
}
|
|
4197
|
+
// Validate last name (required for manage booking lookup)
|
|
4198
|
+
if (!lastName?.trim()) {
|
|
4199
|
+
setError(t('booking.enterLastName') || 'Please enter your last name');
|
|
4200
|
+
return;
|
|
4374
4201
|
}
|
|
4375
4202
|
|
|
4376
4203
|
// Allow checkout if pickup location is selected OR if user chose "I don't know"
|
|
@@ -4398,44 +4225,6 @@ export function ChangeBookingFlow({
|
|
|
4398
4225
|
return;
|
|
4399
4226
|
}
|
|
4400
4227
|
|
|
4401
|
-
if (onChangeBooking) {
|
|
4402
|
-
const pickupForChange = pickupLocationId
|
|
4403
|
-
? product.pickupLocations?.find((loc) => loc.id === pickupLocationId)
|
|
4404
|
-
: null;
|
|
4405
|
-
await onChangeBooking({
|
|
4406
|
-
productId: availabilityProductOptionId,
|
|
4407
|
-
dateTime: selectedAvailability.dateTime,
|
|
4408
|
-
bookingItems,
|
|
4409
|
-
returnAvailabilityId: selectedReturnOption?.returnAvailabilityId ?? null,
|
|
4410
|
-
pickupLocationId: pickupLocationId ?? null,
|
|
4411
|
-
travelerHotel: pickupForChange?.name ?? null,
|
|
4412
|
-
startTime: selectedAvailability.dateTime ?? null,
|
|
4413
|
-
passengerCount: null,
|
|
4414
|
-
childSafetySeatsCount: null,
|
|
4415
|
-
foodRestrictions: null,
|
|
4416
|
-
addOnSelections: addOnSelections.length > 0 ? addOnSelections : null,
|
|
4417
|
-
cancellationPolicyId: cancellationPolicyId ?? initialValues?.cancellationPolicyId ?? null,
|
|
4418
|
-
promoCode: appliedPromoCode ?? null,
|
|
4419
|
-
newTotalAmount: displayChangeFlowProposedTotal,
|
|
4420
|
-
additionalHoursCount: null,
|
|
4421
|
-
pricingAdjustment:
|
|
4422
|
-
providerPricingOverrides.length > 0 || providerAdditionalAdjustments.length > 0
|
|
4423
|
-
? {
|
|
4424
|
-
mode: 'MANUAL_LINES',
|
|
4425
|
-
lineOverrides: providerPricingOverrides,
|
|
4426
|
-
additionalAdjustments: providerAdditionalAdjustments,
|
|
4427
|
-
}
|
|
4428
|
-
: undefined,
|
|
4429
|
-
capacitySeatCredit: {
|
|
4430
|
-
enabled: true,
|
|
4431
|
-
previousPassengerCount: changeFlowInitialTicketCount,
|
|
4432
|
-
previousAvailabilityId: initialValues?.availabilityId ?? null,
|
|
4433
|
-
previousReturnAvailabilityId: initialValues?.returnAvailabilityId ?? null,
|
|
4434
|
-
},
|
|
4435
|
-
});
|
|
4436
|
-
setLoading(false);
|
|
4437
|
-
return;
|
|
4438
|
-
}
|
|
4439
4228
|
|
|
4440
4229
|
const bookingSourceContext = buildBookingSourceContext(bookingSourceAttribution, {
|
|
4441
4230
|
clientChannelSource: inferClientBookingSourceFromProductIds(
|
|
@@ -4443,18 +4232,17 @@ export function ChangeBookingFlow({
|
|
|
4443
4232
|
availabilityProductOptionId,
|
|
4444
4233
|
),
|
|
4445
4234
|
forcePartnerPortalChannel: partnerPortalBooking,
|
|
4446
|
-
forceDashboardSource:
|
|
4235
|
+
forceDashboardSource: false,
|
|
4447
4236
|
});
|
|
4448
4237
|
|
|
4449
4238
|
// Get the hotel name if a pickup location was selected
|
|
4450
4239
|
const selectedPickupLocation = pickupLocationId
|
|
4451
4240
|
? product.pickupLocations?.find(loc => loc.id === pickupLocationId)
|
|
4452
4241
|
: null;
|
|
4453
|
-
let quotedPriceDiff: number | null = null;
|
|
4454
4242
|
let changeIntentIdForCheckout: string | undefined;
|
|
4455
4243
|
let changeBookingReferenceForPaidFlow: string | undefined;
|
|
4456
4244
|
|
|
4457
|
-
|
|
4245
|
+
{
|
|
4458
4246
|
const changeBookingReference = initialValues?.bookingReference?.trim();
|
|
4459
4247
|
const changeLastName = lastName.trim();
|
|
4460
4248
|
if (!changeBookingReference || !changeLastName) {
|
|
@@ -4482,7 +4270,6 @@ export function ChangeBookingFlow({
|
|
|
4482
4270
|
},
|
|
4483
4271
|
currency
|
|
4484
4272
|
);
|
|
4485
|
-
quotedPriceDiff = quoteSlice.priceDiff;
|
|
4486
4273
|
changeBookingReferenceForPaidFlow = changeBookingReference;
|
|
4487
4274
|
changeIntentIdForCheckout = quoteSlice.changeIntentId ?? undefined;
|
|
4488
4275
|
setLatestChangeQuote(
|
|
@@ -4574,13 +4361,11 @@ export function ChangeBookingFlow({
|
|
|
4574
4361
|
// Build checkout breakdown from the exact same values we show in the UI and Stripe modal.
|
|
4575
4362
|
// Backend will charge totalAmount and store this as the receipt so /manage matches.
|
|
4576
4363
|
const taxForBreakdown = effectivePromoDiscountAmount > 0 ? effectiveTax : tax;
|
|
4577
|
-
const amountDueForCheckout =
|
|
4578
|
-
|
|
4579
|
-
|
|
4580
|
-
|
|
4581
|
-
|
|
4582
|
-
})
|
|
4583
|
-
: totalPrice;
|
|
4364
|
+
const amountDueForCheckout = changeFlowBalanceVsOriginal({
|
|
4365
|
+
newTotal: changeFlowNewBookingTotal,
|
|
4366
|
+
originalReceiptTotal: originalReceipt?.total ?? 0,
|
|
4367
|
+
audience: 'customer',
|
|
4368
|
+
});
|
|
4584
4369
|
const lines = [
|
|
4585
4370
|
...ticketLineItemsForChangeFlowDisplay.map((line) => ({
|
|
4586
4371
|
label: line.category,
|
|
@@ -4657,8 +4442,7 @@ export function ChangeBookingFlow({
|
|
|
4657
4442
|
roundingLabel: t('booking.rounding') || 'Rounding',
|
|
4658
4443
|
});
|
|
4659
4444
|
|
|
4660
|
-
const paymentIntent =
|
|
4661
|
-
? await createChangeBookingPaymentIntent(
|
|
4445
|
+
const paymentIntent = await createChangeBookingPaymentIntent(
|
|
4662
4446
|
(() => {
|
|
4663
4447
|
const id = changeIntentIdForCheckout ?? latestChangeQuote?.changeIntentId;
|
|
4664
4448
|
if (!id) {
|
|
@@ -4666,76 +4450,8 @@ export function ChangeBookingFlow({
|
|
|
4666
4450
|
}
|
|
4667
4451
|
return id;
|
|
4668
4452
|
})()
|
|
4669
|
-
)
|
|
4670
|
-
: await createPaymentIntent({
|
|
4671
|
-
productId: product.productId,
|
|
4672
|
-
optionId: availabilityProductOptionId,
|
|
4673
|
-
date: datePart,
|
|
4674
|
-
time: timePart,
|
|
4675
|
-
quantity: totalQuantity,
|
|
4676
|
-
customerEmail: email,
|
|
4677
|
-
customerFirstName: firstName.trim() || undefined,
|
|
4678
|
-
customerLastName: lastName.trim() || undefined,
|
|
4679
|
-
currency: currency,
|
|
4680
|
-
travelerHotel: selectedPickupLocation?.name || undefined,
|
|
4681
|
-
pickupLocationId: pickupLocationId || undefined,
|
|
4682
|
-
itineraryDisplay: itineraryDisplay ?? undefined,
|
|
4683
|
-
returnAvailabilityId: selectedReturnOption?.returnAvailabilityId,
|
|
4684
|
-
promoCode: (lockedPromoCode || appliedPromoCode) || undefined,
|
|
4685
|
-
cancellationPolicyId: cancellationPolicyId || undefined,
|
|
4686
|
-
termsAcceptedAt: termsAcceptedAt ?? undefined,
|
|
4687
|
-
checkoutBreakdown,
|
|
4688
|
-
skipConfirmationCommunications: isAdmin && skipConfirmationCommunications ? true : undefined,
|
|
4689
|
-
disableAutoCommunications: isAdmin && disableAutoCommunications ? true : undefined,
|
|
4690
|
-
...bookingSourceContext,
|
|
4691
|
-
});
|
|
4453
|
+
);
|
|
4692
4454
|
|
|
4693
|
-
// Admin: show choice to pay now or confirm without payment (customer owes full balance)
|
|
4694
|
-
if (isAdmin) {
|
|
4695
|
-
const adminReservationRef =
|
|
4696
|
-
initialValues?.bookingReference?.trim() ??
|
|
4697
|
-
changeBookingReferenceForPaidFlow ??
|
|
4698
|
-
'';
|
|
4699
|
-
if (!adminReservationRef) {
|
|
4700
|
-
throw new Error('Missing reservation reference for admin payment flow');
|
|
4701
|
-
}
|
|
4702
|
-
setError('');
|
|
4703
|
-
setAdminChoiceData({
|
|
4704
|
-
reservationReference: adminReservationRef,
|
|
4705
|
-
reservationExpiration: undefined,
|
|
4706
|
-
checkoutBreakdown,
|
|
4707
|
-
totalAmount: amountDueForCheckout,
|
|
4708
|
-
datePart,
|
|
4709
|
-
timePart,
|
|
4710
|
-
availabilityProductOptionId,
|
|
4711
|
-
itineraryDisplay: itineraryDisplay ?? undefined,
|
|
4712
|
-
clientSecret: paymentIntent.clientSecret ?? '',
|
|
4713
|
-
ticketLinesForModal: ticketLineItemsForChangeFlowDisplay.map((line) => {
|
|
4714
|
-
const rate = pricing.find((r) => r.category === line.category);
|
|
4715
|
-
const breakdown = getPriceBreakdown(
|
|
4716
|
-
line.category,
|
|
4717
|
-
rate?.priceCAD ?? 0,
|
|
4718
|
-
rate?.baseInDisplayCurrency,
|
|
4719
|
-
rate?.appliedAdjustments ?? []
|
|
4720
|
-
);
|
|
4721
|
-
return { line, breakdown };
|
|
4722
|
-
}),
|
|
4723
|
-
feeLineItems: feeLineItemsWithAddOns,
|
|
4724
|
-
returnPriceAdjustment: checkoutReturnLineAmount,
|
|
4725
|
-
cancellationPolicyFee,
|
|
4726
|
-
cancellationPolicyLabel: effectiveCancellationPolicyLabel,
|
|
4727
|
-
subtotal: effectiveSubtotal,
|
|
4728
|
-
tax: effectivePromoDiscountAmount > 0 ? effectiveTax : tax,
|
|
4729
|
-
totalQuantity,
|
|
4730
|
-
isTaxIncludedInPrice,
|
|
4731
|
-
taxRate: pricingConfig?.taxRate ?? 0,
|
|
4732
|
-
promoDiscountAmount: effectivePromoDiscountAmount > 0 ? effectivePromoDiscountAmount : 0,
|
|
4733
|
-
discountLabel: appliedPromoCode ? `Promo: ${appliedPromoCode}` : (originalReceipt?.promoLabel || undefined),
|
|
4734
|
-
});
|
|
4735
|
-
setShowAdminPaymentChoice(true);
|
|
4736
|
-
setLoading(false);
|
|
4737
|
-
return;
|
|
4738
|
-
}
|
|
4739
4455
|
|
|
4740
4456
|
const ticketLinesForModal: CheckoutModalLineItem[] = ticketLineItemsForChangeFlowDisplay.map((line) => {
|
|
4741
4457
|
const rate = pricing.find((r) => r.category === line.category);
|
|
@@ -4757,7 +4473,7 @@ export function ChangeBookingFlow({
|
|
|
4757
4473
|
// Paid change: always return to stable ref+lastName + explicit intent (not reservationRef).
|
|
4758
4474
|
// /manage-booking runs bounded refresh only when `from=change_payment` (see manage-booking page).
|
|
4759
4475
|
successUrlOverride:
|
|
4760
|
-
|
|
4476
|
+
true && changeBookingReferenceForPaidFlow
|
|
4761
4477
|
? (() => {
|
|
4762
4478
|
const origin = typeof window !== 'undefined' ? window.location.origin : '';
|
|
4763
4479
|
const ref = encodeURIComponent(
|
|
@@ -4785,7 +4501,7 @@ export function ChangeBookingFlow({
|
|
|
4785
4501
|
promoDiscountAmount: effectivePromoDiscountAmount > 0 ? effectivePromoDiscountAmount : 0,
|
|
4786
4502
|
discountLabel: appliedPromoCode ? `Promo: ${appliedPromoCode}` : (originalReceipt?.promoLabel || undefined),
|
|
4787
4503
|
changeTotals:
|
|
4788
|
-
|
|
4504
|
+
true && originalReceipt
|
|
4789
4505
|
? {
|
|
4790
4506
|
previousTotal: originalReceipt.total,
|
|
4791
4507
|
newTotal: displayChangeFlowProposedTotal,
|
|
@@ -4854,85 +4570,7 @@ export function ChangeBookingFlow({
|
|
|
4854
4570
|
}
|
|
4855
4571
|
};
|
|
4856
4572
|
|
|
4857
|
-
const handleConfirmWithoutPayment = async () => {
|
|
4858
|
-
if (!adminChoiceData) return;
|
|
4859
|
-
setLoading(true);
|
|
4860
|
-
setError('');
|
|
4861
|
-
try {
|
|
4862
|
-
const bookingSourceContext = buildBookingSourceContext(bookingSourceAttribution, {
|
|
4863
|
-
clientChannelSource: inferClientBookingSourceFromProductIds(
|
|
4864
|
-
product.productId,
|
|
4865
|
-
adminChoiceData.availabilityProductOptionId,
|
|
4866
|
-
),
|
|
4867
|
-
forcePartnerPortalChannel: partnerPortalBooking,
|
|
4868
|
-
forceDashboardSource: bookingAppMode === 'provider-dashboard',
|
|
4869
|
-
});
|
|
4870
|
-
const result = await confirmBookingWithoutPayment({
|
|
4871
|
-
reservationReference: adminChoiceData.reservationReference,
|
|
4872
|
-
productId: product.productId,
|
|
4873
|
-
optionId: adminChoiceData.availabilityProductOptionId,
|
|
4874
|
-
date: adminChoiceData.datePart,
|
|
4875
|
-
time: adminChoiceData.timePart,
|
|
4876
|
-
customerEmail: email || undefined,
|
|
4877
|
-
customerFirstName: firstName.trim() || undefined,
|
|
4878
|
-
customerLastName: lastName.trim() || undefined,
|
|
4879
|
-
currency: currency,
|
|
4880
|
-
travelerHotel: product.pickupLocations?.find(loc => loc.id === pickupLocationId)?.name || undefined,
|
|
4881
|
-
pickupLocationId: pickupLocationId || undefined,
|
|
4882
|
-
itineraryDisplay: adminChoiceData.itineraryDisplay ?? undefined,
|
|
4883
|
-
termsAcceptedAt: termsAcceptedAt ?? undefined,
|
|
4884
|
-
skipConfirmationCommunications: skipConfirmationCommunications ? true : undefined,
|
|
4885
|
-
disableAutoCommunications: disableAutoCommunications ? true : undefined,
|
|
4886
|
-
checkoutBreakdown: adminChoiceData.checkoutBreakdown,
|
|
4887
|
-
depositAmount: 0,
|
|
4888
|
-
balanceAmount: adminChoiceData.totalAmount,
|
|
4889
|
-
totalAmount: adminChoiceData.totalAmount,
|
|
4890
|
-
...bookingSourceContext,
|
|
4891
|
-
});
|
|
4892
|
-
pendingReservationRef.current = null;
|
|
4893
|
-
const ref = formatBookingRefForDisplay(result.bookingReference);
|
|
4894
|
-
const ln = lastName.trim();
|
|
4895
|
-
setShowAdminPaymentChoice(false);
|
|
4896
|
-
setAdminChoiceData(null);
|
|
4897
|
-
onSuccess?.({ reservationReference: adminChoiceData.reservationReference });
|
|
4898
|
-
if (onShowManage) {
|
|
4899
|
-
onShowManage({ ref, lastName: ln });
|
|
4900
|
-
} else {
|
|
4901
|
-
const params = new URLSearchParams({ ref, lastName: ln, booking_complete: '1' });
|
|
4902
|
-
window.location.href = `/manage-booking?${params.toString()}`;
|
|
4903
|
-
}
|
|
4904
|
-
} catch (err) {
|
|
4905
|
-
setError(err instanceof Error ? err.message : 'Failed to confirm booking');
|
|
4906
|
-
} finally {
|
|
4907
|
-
setLoading(false);
|
|
4908
|
-
}
|
|
4909
|
-
};
|
|
4910
4573
|
|
|
4911
|
-
const handlePayNow = () => {
|
|
4912
|
-
if (!adminChoiceData) return;
|
|
4913
|
-
setShowAdminPaymentChoice(false);
|
|
4914
|
-
setCheckoutClientSecret(adminChoiceData.clientSecret);
|
|
4915
|
-
setCheckoutModalData({
|
|
4916
|
-
reservationReference: adminChoiceData.reservationReference,
|
|
4917
|
-
reservationExpiration: adminChoiceData.reservationExpiration,
|
|
4918
|
-
customerLastName: lastName.trim(),
|
|
4919
|
-
ticketLines: adminChoiceData.ticketLinesForModal,
|
|
4920
|
-
feeLineItems: adminChoiceData.feeLineItems,
|
|
4921
|
-
returnPriceAdjustment: adminChoiceData.returnPriceAdjustment,
|
|
4922
|
-
cancellationPolicyFee: adminChoiceData.cancellationPolicyFee,
|
|
4923
|
-
cancellationPolicyLabel: adminChoiceData.cancellationPolicyLabel,
|
|
4924
|
-
subtotal: adminChoiceData.subtotal,
|
|
4925
|
-
tax: adminChoiceData.tax,
|
|
4926
|
-
total: adminChoiceData.totalAmount,
|
|
4927
|
-
totalQuantity: adminChoiceData.totalQuantity,
|
|
4928
|
-
isTaxIncludedInPrice: adminChoiceData.isTaxIncludedInPrice,
|
|
4929
|
-
taxRate: adminChoiceData.taxRate,
|
|
4930
|
-
promoDiscountAmount: adminChoiceData.promoDiscountAmount,
|
|
4931
|
-
discountLabel: adminChoiceData.discountLabel,
|
|
4932
|
-
});
|
|
4933
|
-
setShowCheckoutModal(true);
|
|
4934
|
-
setAdminChoiceData(null);
|
|
4935
|
-
};
|
|
4936
4574
|
|
|
4937
4575
|
if (activeOptions.length === 0) {
|
|
4938
4576
|
return (
|
|
@@ -4944,17 +4582,6 @@ export function ChangeBookingFlow({
|
|
|
4944
4582
|
|
|
4945
4583
|
return (
|
|
4946
4584
|
<div className="booking-flow-root space-y-8">
|
|
4947
|
-
{/* Admin: choose to pay now or confirm without payment (full balance owed) */}
|
|
4948
|
-
<AdminPaymentChoiceModal
|
|
4949
|
-
open={!!(showAdminPaymentChoice && adminChoiceData)}
|
|
4950
|
-
totalAmount={adminChoiceData?.totalAmount ?? 0}
|
|
4951
|
-
currency={currency}
|
|
4952
|
-
loading={loading}
|
|
4953
|
-
error={error}
|
|
4954
|
-
onPayNow={handlePayNow}
|
|
4955
|
-
onConfirmWithoutPayment={handleConfirmWithoutPayment}
|
|
4956
|
-
onCancel={() => { setShowAdminPaymentChoice(false); setAdminChoiceData(null); setError(''); }}
|
|
4957
|
-
/>
|
|
4958
4585
|
{checkoutModalData && (
|
|
4959
4586
|
<CheckoutModal
|
|
4960
4587
|
open={showCheckoutModal}
|
|
@@ -5028,12 +4655,12 @@ export function ChangeBookingFlow({
|
|
|
5028
4655
|
syncVisibleWeekToSelectedDate={true}
|
|
5029
4656
|
selectableSoldOutDate={changeFlowOriginalDate}
|
|
5030
4657
|
partySizeRequiredForCalendarSelection={
|
|
5031
|
-
|
|
4658
|
+
true && !false && changeFlowInitialTicketCount > 0
|
|
5032
4659
|
? changeFlowInitialTicketCount
|
|
5033
4660
|
: undefined
|
|
5034
4661
|
}
|
|
5035
4662
|
getEffectiveVacancies={
|
|
5036
|
-
|
|
4663
|
+
true && !false && changeFlowInitialTicketCount > 0
|
|
5037
4664
|
? getCalendarEffectiveOutboundVacancies
|
|
5038
4665
|
: undefined
|
|
5039
4666
|
}
|
|
@@ -5057,7 +4684,7 @@ export function ChangeBookingFlow({
|
|
|
5057
4684
|
earliestDate={earliestAvailabilityDate}
|
|
5058
4685
|
onVisibleRangeChange={handleVisibleRangeChange}
|
|
5059
4686
|
currency={currency}
|
|
5060
|
-
showCapacity={
|
|
4687
|
+
showCapacity={false}
|
|
5061
4688
|
extraDiscountPercent={calendarDiscountPercent}
|
|
5062
4689
|
capDiscountBadgesToBookingDate={changeFlowOriginalDate}
|
|
5063
4690
|
/>
|
|
@@ -5113,7 +4740,7 @@ export function ChangeBookingFlow({
|
|
|
5113
4740
|
selectedTicketCount={totalQuantity}
|
|
5114
4741
|
optionsMap={optionsMap}
|
|
5115
4742
|
hasAnyMostPopular={hasAnyMostPopular}
|
|
5116
|
-
isAdmin={
|
|
4743
|
+
isAdmin={false}
|
|
5117
4744
|
pickupLocationSkipped={pickupLocationSkipped}
|
|
5118
4745
|
t={t}
|
|
5119
4746
|
onTimeSelect={handleTimeSelect}
|
|
@@ -5129,7 +4756,7 @@ export function ChangeBookingFlow({
|
|
|
5129
4756
|
companyTimezone={companyTimezone}
|
|
5130
4757
|
currency={currency}
|
|
5131
4758
|
locale={locale}
|
|
5132
|
-
isAdmin={
|
|
4759
|
+
isAdmin={false}
|
|
5133
4760
|
t={t}
|
|
5134
4761
|
onReturnSelect={(option) => {
|
|
5135
4762
|
const raw = selectedAvailability.returnOptions?.find(
|
|
@@ -5157,7 +4784,7 @@ export function ChangeBookingFlow({
|
|
|
5157
4784
|
resourceCount={selectedReturnOption ? null : (selectedAvailability.resourceCount ?? null)}
|
|
5158
4785
|
currency={currency}
|
|
5159
4786
|
locale={locale}
|
|
5160
|
-
isAdmin={
|
|
4787
|
+
isAdmin={false}
|
|
5161
4788
|
isSimplifiedPricingView={isSimplifiedPricingView}
|
|
5162
4789
|
t={t}
|
|
5163
4790
|
onQuantityChange={handleQuantityChange}
|
|
@@ -5179,7 +4806,7 @@ export function ChangeBookingFlow({
|
|
|
5179
4806
|
currency={currency}
|
|
5180
4807
|
locale={locale}
|
|
5181
4808
|
onSelectionsChange={updateAddOnSelections}
|
|
5182
|
-
minimumTotalByAddOnId={
|
|
4809
|
+
minimumTotalByAddOnId={initialAddOnMinTotalByAddOnId}
|
|
5183
4810
|
suppressPrices={false}
|
|
5184
4811
|
/>
|
|
5185
4812
|
)}
|
|
@@ -5209,99 +4836,15 @@ export function ChangeBookingFlow({
|
|
|
5209
4836
|
currency={currency}
|
|
5210
4837
|
locale={locale}
|
|
5211
4838
|
t={t}
|
|
5212
|
-
extraBetweenTaxAndTotal={
|
|
5213
|
-
|
|
5214
|
-
{showProviderPricingInlineEditor && providerPricingUi?.error ? (
|
|
5215
|
-
<div className="mt-2 text-sm text-red-700">{providerPricingUi.error}</div>
|
|
5216
|
-
) : null}
|
|
5217
|
-
{showProviderPricingInlineEditor &&
|
|
5218
|
-
providerPricingUi?.loading &&
|
|
5219
|
-
providerQuotedLines.length === 0 ? (
|
|
5220
|
-
<div className="mt-2 text-sm text-stone-500">Loading price lines...</div>
|
|
5221
|
-
) : null}
|
|
5222
|
-
{showProviderPricingInlineEditor &&
|
|
5223
|
-
providerPricingUi?.helperText &&
|
|
5224
|
-
!providerPricingUi.error ? (
|
|
5225
|
-
<div className="mt-2 text-xs text-stone-500">{providerPricingUi.helperText}</div>
|
|
5226
|
-
) : null}
|
|
5227
|
-
</>
|
|
5228
|
-
}
|
|
5229
|
-
extraBeforeSubtotal={
|
|
5230
|
-
showProviderPricingInlineEditor && (providerPricingUi?.additionalAdjustments?.length ?? 0) > 0 ? (
|
|
5231
|
-
<div className="space-y-1">
|
|
5232
|
-
{providerPricingUi?.additionalAdjustments?.map((adj) => (
|
|
5233
|
-
<div key={adj.id} className="flex items-center justify-between gap-2 text-sm">
|
|
5234
|
-
<div className="flex min-w-0 items-center gap-1">
|
|
5235
|
-
<button
|
|
5236
|
-
type="button"
|
|
5237
|
-
className="rounded border border-stone-300 px-1 text-xs text-stone-600 hover:bg-stone-100"
|
|
5238
|
-
onClick={() => providerPricingUi?.onRemoveAdditionalAdjustment?.(adj.id)}
|
|
5239
|
-
>
|
|
5240
|
-
-
|
|
5241
|
-
</button>
|
|
5242
|
-
<input
|
|
5243
|
-
type="text"
|
|
5244
|
-
className="w-40 rounded border border-stone-300 px-2 py-0.5 text-sm"
|
|
5245
|
-
placeholder="Line description"
|
|
5246
|
-
value={adj.label}
|
|
5247
|
-
onChange={(e) =>
|
|
5248
|
-
providerPricingUi?.onUpdateAdditionalAdjustment?.(adj.id, { label: e.target.value })
|
|
5249
|
-
}
|
|
5250
|
-
/>
|
|
5251
|
-
</div>
|
|
5252
|
-
<div className="flex items-center gap-1">
|
|
5253
|
-
<select
|
|
5254
|
-
className="rounded border border-stone-300 px-1 py-0.5 text-xs"
|
|
5255
|
-
value={adj.mode}
|
|
5256
|
-
onChange={(e) =>
|
|
5257
|
-
providerPricingUi?.onUpdateAdditionalAdjustment?.(adj.id, {
|
|
5258
|
-
mode: e.target.value as 'DISCOUNT' | 'CHARGE',
|
|
5259
|
-
})
|
|
5260
|
-
}
|
|
5261
|
-
>
|
|
5262
|
-
<option value="DISCOUNT">-</option>
|
|
5263
|
-
<option value="CHARGE">+</option>
|
|
5264
|
-
</select>
|
|
5265
|
-
<input
|
|
5266
|
-
type="text"
|
|
5267
|
-
inputMode="decimal"
|
|
5268
|
-
className="h-6 w-24 rounded border border-stone-300 bg-white px-2 py-0.5 text-right text-sm font-medium leading-none text-stone-700"
|
|
5269
|
-
placeholder="0.00"
|
|
5270
|
-
value={adj.amountInput}
|
|
5271
|
-
onChange={(e) =>
|
|
5272
|
-
providerPricingUi?.onUpdateAdditionalAdjustment?.(adj.id, {
|
|
5273
|
-
amountInput: e.target.value,
|
|
5274
|
-
})
|
|
5275
|
-
}
|
|
5276
|
-
/>
|
|
5277
|
-
</div>
|
|
5278
|
-
</div>
|
|
5279
|
-
))}
|
|
5280
|
-
<button
|
|
5281
|
-
type="button"
|
|
5282
|
-
className="rounded border border-stone-300 px-2 py-0.5 text-xs text-stone-600 hover:bg-stone-100"
|
|
5283
|
-
onClick={() => providerPricingUi?.onAddAdditionalAdjustment?.()}
|
|
5284
|
-
>
|
|
5285
|
-
+ add line item
|
|
5286
|
-
</button>
|
|
5287
|
-
</div>
|
|
5288
|
-
) : showProviderPricingInlineEditor ? (
|
|
5289
|
-
<button
|
|
5290
|
-
type="button"
|
|
5291
|
-
className="rounded border border-stone-300 px-2 py-0.5 text-xs text-stone-600 hover:bg-stone-100"
|
|
5292
|
-
onClick={() => providerPricingUi?.onAddAdditionalAdjustment?.()}
|
|
5293
|
-
>
|
|
5294
|
-
+ add line item
|
|
5295
|
-
</button>
|
|
5296
|
-
) : undefined
|
|
5297
|
-
}
|
|
4839
|
+
extraBetweenTaxAndTotal={undefined}
|
|
4840
|
+
extraBeforeSubtotal={undefined}
|
|
5298
4841
|
firstName={firstName}
|
|
5299
4842
|
lastName={lastName}
|
|
5300
4843
|
email={email}
|
|
5301
4844
|
onFirstNameChange={(v) => { setFirstName(v); setError(''); }}
|
|
5302
4845
|
onLastNameChange={(v) => { setLastName(v); setError(''); }}
|
|
5303
4846
|
onEmailChange={(v) => { setEmail(v); setError(''); }}
|
|
5304
|
-
readOnlyContactFields
|
|
4847
|
+
readOnlyContactFields={false}
|
|
5305
4848
|
pickupLocations={
|
|
5306
4849
|
selectedDate && product.pickupLocations && product.pickupLocations.length > 0
|
|
5307
4850
|
? product.pickupLocations
|
|
@@ -5335,38 +4878,29 @@ export function ChangeBookingFlow({
|
|
|
5335
4878
|
setTermsAccepted(checked);
|
|
5336
4879
|
setTermsAcceptedAt(checked ? new Date().toISOString() : null);
|
|
5337
4880
|
}}
|
|
5338
|
-
isAdmin={
|
|
4881
|
+
isAdmin={false}
|
|
5339
4882
|
showCommunicationAdminSection={false}
|
|
5340
|
-
skipConfirmationCommunications={
|
|
5341
|
-
disableAutoCommunications={
|
|
5342
|
-
onSkipConfirmationChange={
|
|
5343
|
-
onDisableCommunicationsChange={
|
|
4883
|
+
skipConfirmationCommunications={false}
|
|
4884
|
+
disableAutoCommunications={false}
|
|
4885
|
+
onSkipConfirmationChange={() => {}}
|
|
4886
|
+
onDisableCommunicationsChange={() => {}}
|
|
5344
4887
|
error={checkoutFormError}
|
|
5345
4888
|
loading={loading}
|
|
5346
4889
|
totalQuantity={totalQuantity}
|
|
5347
4890
|
onCheckout={handleCheckout}
|
|
5348
4891
|
submitLabel={changeCheckoutButtonLabel ?? deferredInvoiceSubmitLabel}
|
|
5349
4892
|
hideSubmitButton={
|
|
5350
|
-
showCheckoutModal ||
|
|
5351
|
-
showAdminPaymentChoice ||
|
|
5352
|
-
!hasEffectiveChangeSelection ||
|
|
5353
|
-
isChangeQuoteBlocked
|
|
4893
|
+
showCheckoutModal || !hasEffectiveChangeSelection || isChangeQuoteBlocked
|
|
5354
4894
|
}
|
|
5355
4895
|
submitDisabled={changeFlowSubmitDisabled}
|
|
5356
4896
|
attributionSummary={flowUi?.partnerAttributionSummary}
|
|
5357
4897
|
attributionConfirmLabel={flowUi?.partnerAttributionConfirmLabel}
|
|
5358
4898
|
attributionConfirmed={partnerAttributionConfirmed}
|
|
5359
4899
|
onAttributionConfirmedChange={setPartnerAttributionConfirmed}
|
|
5360
|
-
lineAmountInputs={
|
|
5361
|
-
onLineAmountInputChange={
|
|
5362
|
-
|
|
5363
|
-
}
|
|
5364
|
-
onLineAmountInputBlur={
|
|
5365
|
-
showProviderPricingInlineEditor ? providerPricingUi?.onLineAmountInputBlur : undefined
|
|
5366
|
-
}
|
|
5367
|
-
onLineAmountReset={
|
|
5368
|
-
showProviderPricingInlineEditor ? providerPricingUi?.onLineAmountReset : undefined
|
|
5369
|
-
}
|
|
4900
|
+
lineAmountInputs={undefined}
|
|
4901
|
+
onLineAmountInputChange={undefined}
|
|
4902
|
+
onLineAmountInputBlur={undefined}
|
|
4903
|
+
onLineAmountReset={undefined}
|
|
5370
4904
|
/>
|
|
5371
4905
|
</>
|
|
5372
4906
|
)}
|