@ticketboothapp/booking 1.2.167 → 1.2.169
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/AdminChangeBookingContent.tsx +13 -1
- package/src/components/booking/AdminChangeBookingFlow.tsx +180 -65
- package/src/components/booking/AdminChangeCheckoutPanel.tsx +43 -13
- package/src/components/booking/AdminChangePricingPanel.tsx +400 -134
- package/src/components/booking/AdminChangeReceiptComparison.tsx +166 -145
- package/src/components/booking/ChangeBookingFlow.tsx +7 -3
- package/src/components/booking/ChangeBookingQuoteStatusPlaceholder.tsx +7 -2
- package/src/components/booking/ChangeBookingSelectionControlsPanel.tsx +6 -0
- package/src/components/booking/ChangeBookingTicketsAndAddOnsPanel.tsx +80 -0
- package/src/components/booking/PickupLocationSelector.module.css +22 -0
- package/src/components/booking/PickupLocationSelector.tsx +3 -3
- package/src/components/booking/PriceSummary.tsx +4 -0
- package/src/components/booking/PrivateShuttleBookingFlow.tsx +27 -13
- package/src/components/booking/PrivateShuttleCheckoutSection.tsx +95 -54
- package/src/components/booking/admin-adjustment-components.ts +44 -0
- package/src/components/booking/admin-adjustment-tax-behavior.ts +50 -0
- package/src/components/booking/admin-change-flow-state-helpers.ts +10 -0
- package/src/components/booking/admin-change-provider-payload.ts +3 -0
- package/src/components/booking/admin-change-quote-request-key.ts +27 -0
- package/src/components/booking/admin-change-v2-quote-request.ts +10 -2
- package/src/components/booking/admin-refund-decision-context.ts +37 -0
- package/src/components/booking/admin-refund-disposition.ts +71 -2
- package/src/components/booking/booking-flow-types.ts +4 -0
- package/src/components/booking/booking-flow.css +5 -0
- package/src/components/booking/change-booking-error-message.ts +137 -0
- package/src/components/booking/change-booking-flow-helpers.ts +77 -2
- package/src/components/booking/change-booking-quote-guards.ts +4 -1
- package/src/components/booking/change-booking-quote-state.ts +44 -1
- package/src/components/booking/incompatible-add-on-selections.ts +19 -0
- package/src/components/booking/private-shuttle-availability.ts +14 -0
- package/src/components/booking/private-shuttle-cancellation-policy.ts +9 -0
- package/src/components/booking/private-shuttle-checkout-controller.ts +5 -0
- package/src/components/booking/private-shuttle-provider-change-runner.ts +21 -0
- package/src/components/booking/private-shuttle-reservation-runner.ts +5 -3
- package/src/components/booking/provider-dashboard-change-booking.ts +16 -1
- package/src/components/booking/useAdminChangeCheckoutController.ts +16 -0
- package/src/components/booking/useAdminChangeProductReset.ts +6 -0
- package/src/components/booking/useAdminChangeProtectedPricing.ts +4 -0
- package/src/components/booking/useAdminChangeQuoteDisplayState.tsx +6 -4
- package/src/components/booking/useAdminChangeQuotePreview.ts +76 -20
- package/src/components/booking/useAdminCustomReceiptLines.ts +171 -10
- package/src/components/booking/useAdminProviderPricingAdjustments.ts +50 -19
- package/src/components/booking/useBookingAvailabilityAddOns.ts +19 -2
- package/src/components/booking/useChangeBookingAddOnFloorController.ts +15 -9
- package/src/components/booking/useChangeBookingInitialHydration.ts +10 -7
- package/src/components/booking/useChangeBookingQuotePreview.ts +3 -1
- package/src/components/booking/useChangeBookingSelectionDetails.ts +23 -17
- package/src/lib/booking/change-booking-server-preview.ts +26 -9
- package/src/lib/booking/change-flow-pricing.ts +12 -0
- package/src/lib/booking/i18n/messages/en.json +1 -0
- package/src/lib/booking/i18n/messages/fr.json +1 -0
- package/src/lib/booking-api.ts +64 -0
- package/test/change-booking-helpers.test.ts +853 -3
- package/src/components/booking/useAdminChangePricingDebugPanel.tsx +0 -178
package/package.json
CHANGED
|
@@ -142,6 +142,8 @@ const selectionControlsPropKeys = [
|
|
|
142
142
|
'applyReceiptPaidFloors',
|
|
143
143
|
'ticketUnitFloorByCategory',
|
|
144
144
|
'addOns',
|
|
145
|
+
'knownAddOns',
|
|
146
|
+
'addOnCatalogLoaded',
|
|
145
147
|
'addOnSelections',
|
|
146
148
|
'onAddOnSelectionsChange',
|
|
147
149
|
'minimumTotalByAddOnId',
|
|
@@ -157,8 +159,13 @@ const checkoutPanelPropKeys = [
|
|
|
157
159
|
'originalReceipt',
|
|
158
160
|
'priceSummaryLinesIncludeTaxRow',
|
|
159
161
|
'serverAmendmentLines',
|
|
162
|
+
'serverResultingReceiptLines',
|
|
160
163
|
'serverAmountDue',
|
|
161
164
|
'serverUpdatedTotal',
|
|
165
|
+
'serverNetPaid',
|
|
166
|
+
'serverCurrentPaymentCredit',
|
|
167
|
+
'serverProjectedPaymentCredit',
|
|
168
|
+
'serverRefundCandidate',
|
|
162
169
|
'serverQuoteError',
|
|
163
170
|
'refundDecisionOperations',
|
|
164
171
|
'returnPriceTreatmentOperations',
|
|
@@ -167,6 +174,8 @@ const checkoutPanelPropKeys = [
|
|
|
167
174
|
'refundDispositionsByOperationId',
|
|
168
175
|
'onRefundDispositionChange',
|
|
169
176
|
'refundQuoteLoading',
|
|
177
|
+
'sourceProductName',
|
|
178
|
+
'targetProductName',
|
|
170
179
|
'isTaxIncludedInPrice',
|
|
171
180
|
'taxRate',
|
|
172
181
|
'currency',
|
|
@@ -175,15 +184,18 @@ const checkoutPanelPropKeys = [
|
|
|
175
184
|
'showProviderPricingInlineEditor',
|
|
176
185
|
'providerPricingUi',
|
|
177
186
|
'providerQuotedLines',
|
|
178
|
-
'debugPanel',
|
|
179
187
|
'promoEditor',
|
|
180
188
|
'promoCode',
|
|
181
189
|
'showChangeFlowManualPriceLines',
|
|
182
190
|
'showAdminCustomLineEditor',
|
|
183
191
|
'adminCustomReceiptLines',
|
|
192
|
+
'hasPendingAdminCustomReceiptLineChanges',
|
|
193
|
+
'pendingAdminCustomReceiptLineIds',
|
|
194
|
+
'adjustmentComponentOptions',
|
|
184
195
|
'onAddAdminCustomReceiptLine',
|
|
185
196
|
'onUpdateAdminCustomReceiptLine',
|
|
186
197
|
'onRemoveAdminCustomReceiptLine',
|
|
198
|
+
'onApplyAdminCustomReceiptLines',
|
|
187
199
|
'firstName',
|
|
188
200
|
'lastName',
|
|
189
201
|
'email',
|
|
@@ -4,8 +4,10 @@ import { useState, useEffect, useMemo, useRef, useCallback } from 'react';
|
|
|
4
4
|
import {
|
|
5
5
|
type AdminAmendmentRefundDisposition,
|
|
6
6
|
type AdminPromoApplicationScope,
|
|
7
|
+
type AddOn,
|
|
7
8
|
type Availability,
|
|
8
9
|
type ReturnOption,
|
|
10
|
+
getAddOns,
|
|
9
11
|
} from '../../lib/booking-api';
|
|
10
12
|
import {
|
|
11
13
|
AdminChangeBookingContent,
|
|
@@ -20,12 +22,14 @@ import { filterAvailabilitiesAfterCurrentTime } from '../../lib/booking/booking-
|
|
|
20
22
|
import type { ChangeBookingFlowProps } from './booking-flow-types';
|
|
21
23
|
import {
|
|
22
24
|
normalizeAddOnSelections,
|
|
25
|
+
resolveChangeBookingCancellationPolicyId,
|
|
23
26
|
} from './change-booking-flow-helpers';
|
|
24
27
|
import {
|
|
25
28
|
buildChangeBookingMinimumQuantities,
|
|
26
29
|
isAdminManualOverrideEligibleLine,
|
|
27
30
|
resolveChangeFlowOriginalDate,
|
|
28
31
|
resolveInitialChangeProductOptionId,
|
|
32
|
+
shouldLockExistingAddOnQuantities,
|
|
29
33
|
} from './admin-change-flow-state-helpers';
|
|
30
34
|
import { useAdminChangeProducts } from './useAdminChangeProducts';
|
|
31
35
|
import {
|
|
@@ -42,6 +46,8 @@ import { useAdminChangePriceSummary } from './useAdminChangePriceSummary';
|
|
|
42
46
|
import { useAdminChangeReceiptDerivation } from './useAdminChangeReceiptDerivation';
|
|
43
47
|
import { useAdminChangeQuotePreview } from './useAdminChangeQuotePreview';
|
|
44
48
|
import { useBookingAvailabilityAddOns } from './useBookingAvailabilityAddOns';
|
|
49
|
+
import { incompatibleAddOnSelections } from './incompatible-add-on-selections';
|
|
50
|
+
import { incompatibleAddOnChangeError } from './change-booking-error-message';
|
|
45
51
|
import { useBookingPromoAndQuantityController } from './useBookingPromoAndQuantityController';
|
|
46
52
|
import { useChangeBookingItineraryUiState } from './useChangeBookingItineraryUiState';
|
|
47
53
|
import { useChangeBookingAutoSelections } from './useChangeBookingAutoSelections';
|
|
@@ -50,7 +56,6 @@ import { useChangeBookingAddOnFloorController } from './useChangeBookingAddOnFlo
|
|
|
50
56
|
import { useChangeBookingReceiptFloorController } from './useChangeBookingReceiptFloorController';
|
|
51
57
|
import { useAdminCustomReceiptLines } from './useAdminCustomReceiptLines';
|
|
52
58
|
import { useAdminChangeQuoteDisplayState } from './useAdminChangeQuoteDisplayState';
|
|
53
|
-
import { useAdminChangePricingDebugPanel } from './useAdminChangePricingDebugPanel';
|
|
54
59
|
import { useAdminChangeOrderSummaryModel } from './useAdminChangeOrderSummaryModel';
|
|
55
60
|
import { useActiveProductOptions } from './useActiveProductOptions';
|
|
56
61
|
import { useSeedPromoCodeInput } from './useSeedPromoCodeInput';
|
|
@@ -61,6 +66,8 @@ import { AdminChangePromoEditor } from './AdminChangePromoEditor';
|
|
|
61
66
|
import { buildAdminChangeQuoteRequestKey } from './admin-change-quote-request-key';
|
|
62
67
|
import type { AdminReleaseRefundDisposition } from './admin-refund-disposition';
|
|
63
68
|
import {
|
|
69
|
+
defaultKeepOriginalReturnPriceDispositions,
|
|
70
|
+
defaultPendingRefundDispositions,
|
|
64
71
|
mergeAdminRefundDecisionContext,
|
|
65
72
|
selectRefundDispositionsForOperations,
|
|
66
73
|
type AdminRefundDecisionContext,
|
|
@@ -179,27 +186,79 @@ export function AdminChangeBookingFlow({
|
|
|
179
186
|
useState<AdminPromoApplicationScope>('CHANGE_ONLY');
|
|
180
187
|
const [pickupLocationId, setPickupLocationId] = useState<string | null>(null);
|
|
181
188
|
const [pickupLocationSkipped, setPickupLocationSkipped] = useState(false);
|
|
182
|
-
/**
|
|
183
|
-
const cancellationPolicyId =
|
|
189
|
+
/** The policy remains non-editable, but a family conversion must adopt the target default. */
|
|
190
|
+
const [cancellationPolicyId, setCancellationPolicyId] = useState<string | null>(
|
|
184
191
|
() => initialValues?.cancellationPolicyId?.trim() ?? null,
|
|
185
|
-
[initialValues?.cancellationPolicyId],
|
|
186
192
|
);
|
|
187
193
|
/** Add-on selections (lunch, animals, etc.) - filtered by selected product option */
|
|
188
194
|
const [addOnSelections, setAddOnSelections] = useState<Array<{ addOnId: string; variantId?: string; quantity?: number }>>(() =>
|
|
189
195
|
normalizeAddOnSelections(initialValues?.addOnSelections ?? [])
|
|
190
196
|
);
|
|
191
197
|
/** Fetched add-ons for the selected product option */
|
|
192
|
-
const {
|
|
198
|
+
const {
|
|
199
|
+
addOns,
|
|
200
|
+
setAddOns,
|
|
201
|
+
availabilityProductOptionId,
|
|
202
|
+
loadedProductOptionId,
|
|
203
|
+
} = useBookingAvailabilityAddOns({
|
|
193
204
|
selectedAvailability,
|
|
194
205
|
companyId: product.companyId,
|
|
195
206
|
});
|
|
196
|
-
const
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
207
|
+
const [knownAddOns, setKnownAddOns] = useState<AddOn[]>([]);
|
|
208
|
+
const mergeKnownAddOns = useCallback((rows: AddOn[]) => {
|
|
209
|
+
if (rows.length === 0) return;
|
|
210
|
+
setKnownAddOns((current) => {
|
|
211
|
+
const merged = new Map(current.map((addOn) => [addOn.addOnId, addOn]));
|
|
212
|
+
let changed = false;
|
|
213
|
+
for (const addOn of rows) {
|
|
214
|
+
if (merged.get(addOn.addOnId) !== addOn) {
|
|
215
|
+
merged.set(addOn.addOnId, addOn);
|
|
216
|
+
changed = true;
|
|
217
|
+
}
|
|
218
|
+
}
|
|
219
|
+
return changed ? Array.from(merged.values()) : current;
|
|
220
|
+
});
|
|
221
|
+
}, []);
|
|
222
|
+
useEffect(() => {
|
|
223
|
+
mergeKnownAddOns(addOns);
|
|
224
|
+
}, [addOns, mergeKnownAddOns]);
|
|
225
|
+
useEffect(() => {
|
|
226
|
+
let cancelled = false;
|
|
227
|
+
if (!product.companyId) return;
|
|
228
|
+
getAddOns(product.companyId, {
|
|
229
|
+
preCheckout: true,
|
|
230
|
+
dateTime: initialValues?.dateTime?.trim() || undefined,
|
|
231
|
+
})
|
|
232
|
+
.then((rows) => {
|
|
233
|
+
if (!cancelled) mergeKnownAddOns(rows);
|
|
234
|
+
})
|
|
235
|
+
.catch(() => {
|
|
236
|
+
// The selected tour's catalog still renders; the warning keeps a generic fallback.
|
|
237
|
+
});
|
|
238
|
+
return () => {
|
|
239
|
+
cancelled = true;
|
|
240
|
+
};
|
|
241
|
+
}, [product.companyId, initialValues?.dateTime, mergeKnownAddOns]);
|
|
242
|
+
const addOnCatalogLoaded =
|
|
243
|
+
availabilityProductOptionId != null &&
|
|
244
|
+
loadedProductOptionId === availabilityProductOptionId;
|
|
245
|
+
const incompatibleSelections = incompatibleAddOnSelections(
|
|
246
|
+
addOnSelections,
|
|
247
|
+
addOns,
|
|
248
|
+
addOnCatalogLoaded,
|
|
249
|
+
);
|
|
250
|
+
const incompatibleAddOnIds = Array.from(
|
|
251
|
+
new Set(incompatibleSelections.map((selection) => selection.addOnId)),
|
|
252
|
+
);
|
|
253
|
+
const hasIncompatibleAddOnSelections = incompatibleAddOnIds.length > 0;
|
|
254
|
+
const incompatibleAddOnError = hasIncompatibleAddOnSelections
|
|
255
|
+
? incompatibleAddOnChangeError(
|
|
256
|
+
incompatibleAddOnIds.map((addOnId) => (
|
|
257
|
+
knownAddOns.find((addOn) => addOn.addOnId === addOnId)?.name ?? 'Selected add-on'
|
|
258
|
+
)),
|
|
259
|
+
product.name,
|
|
260
|
+
)
|
|
261
|
+
: '';
|
|
203
262
|
useEffect(() => {
|
|
204
263
|
if (!pickupLocationId) return;
|
|
205
264
|
const pickupLocations = product.pickupLocations ?? [];
|
|
@@ -212,7 +271,7 @@ export function AdminChangeBookingFlow({
|
|
|
212
271
|
useSeedPromoCodeInput(autoAppliedPromoCode, setPromoCodeInput);
|
|
213
272
|
const [loading, setLoading] = useState(false);
|
|
214
273
|
const [error, setError] = useState('');
|
|
215
|
-
const [termsAccepted, setTermsAccepted] = useState(
|
|
274
|
+
const [termsAccepted, setTermsAccepted] = useState(true);
|
|
216
275
|
const [termsAcceptedAt, setTermsAcceptedAt] = useState<string | null>(null);
|
|
217
276
|
const [partnerAttributionConfirmed, setPartnerAttributionConfirmed] = useState(false);
|
|
218
277
|
/** Admin only: skip sending confirmation at creation (provider dashboard). */
|
|
@@ -261,6 +320,21 @@ export function AdminChangeBookingFlow({
|
|
|
261
320
|
[isCustomerSelfServeChange, initialValues?.bookingItems, isAdmin],
|
|
262
321
|
);
|
|
263
322
|
const [latestChangeQuote, setLatestChangeQuote] = useState<AdminChangeLatestQuote | null>(null);
|
|
323
|
+
const {
|
|
324
|
+
adminCustomReceiptLines,
|
|
325
|
+
appliedAdminCustomReceiptLines,
|
|
326
|
+
adminReverseAdjustmentIds,
|
|
327
|
+
hasPendingAdminCustomReceiptLineChanges,
|
|
328
|
+
pendingAdminCustomReceiptLineIds,
|
|
329
|
+
hasHydratedExistingAdjustments,
|
|
330
|
+
handleAddAdminCustomReceiptLine,
|
|
331
|
+
handleUpdateAdminCustomReceiptLine,
|
|
332
|
+
handleRemoveAdminCustomReceiptLine,
|
|
333
|
+
handleApplyAdminCustomReceiptLines: handleApplyAdminCustomReceiptLineDrafts,
|
|
334
|
+
} = useAdminCustomReceiptLines(
|
|
335
|
+
selectedChangeProductId,
|
|
336
|
+
latestChangeQuote?.reversibleAdjustments,
|
|
337
|
+
);
|
|
264
338
|
const [refundDecisionContext, setRefundDecisionContext] = useState<AdminRefundDecisionContext | null>(null);
|
|
265
339
|
const [refundDispositionsByOperationId, setRefundDispositionsByOperationId] = useState<
|
|
266
340
|
Record<string, AdminAmendmentRefundDisposition>
|
|
@@ -278,6 +352,12 @@ export function AdminChangeBookingFlow({
|
|
|
278
352
|
(latestChangeQuote.refundDecisionOperations?.length ?? 0) > 0) &&
|
|
279
353
|
latestChangeQuote.serverDisplay != null;
|
|
280
354
|
const changeQuoteRequestSeq = useRef(0);
|
|
355
|
+
const handleApplyAdminCustomReceiptLines = useCallback(() => {
|
|
356
|
+
setLatestChangeQuote(null);
|
|
357
|
+
setChangeQuoteFetchError(null);
|
|
358
|
+
setChangeQuoteLoading(true);
|
|
359
|
+
handleApplyAdminCustomReceiptLineDrafts();
|
|
360
|
+
}, [handleApplyAdminCustomReceiptLineDrafts]);
|
|
281
361
|
const { activeOptions, activeOptionIdsKey, optionsMap } = useActiveProductOptions(product.options);
|
|
282
362
|
|
|
283
363
|
const {
|
|
@@ -311,8 +391,31 @@ export function AdminChangeBookingFlow({
|
|
|
311
391
|
setError,
|
|
312
392
|
});
|
|
313
393
|
|
|
394
|
+
useEffect(() => {
|
|
395
|
+
const policies = pricingConfig?.cancellationPolicies;
|
|
396
|
+
if (!policies?.length) return;
|
|
397
|
+
const isProductFamilyConversion =
|
|
398
|
+
isInitialPrivateShuttleBooking !== (product.productType === 'PRIVATE_SHUTTLE');
|
|
399
|
+
const resolvedPolicyId = resolveChangeBookingCancellationPolicyId(
|
|
400
|
+
cancellationPolicyId,
|
|
401
|
+
policies,
|
|
402
|
+
currency,
|
|
403
|
+
isProductFamilyConversion,
|
|
404
|
+
);
|
|
405
|
+
if (resolvedPolicyId !== cancellationPolicyId) {
|
|
406
|
+
setCancellationPolicyId(resolvedPolicyId);
|
|
407
|
+
}
|
|
408
|
+
}, [
|
|
409
|
+
cancellationPolicyId,
|
|
410
|
+
currency,
|
|
411
|
+
isInitialPrivateShuttleBooking,
|
|
412
|
+
pricingConfig?.cancellationPolicies,
|
|
413
|
+
product.productType,
|
|
414
|
+
]);
|
|
415
|
+
|
|
314
416
|
useAdminChangeProductReset({
|
|
315
417
|
selectedChangeProductId,
|
|
418
|
+
setQuantities,
|
|
316
419
|
setSelectedAvailability,
|
|
317
420
|
setSelectedReturnOption,
|
|
318
421
|
setAddOns,
|
|
@@ -376,6 +479,8 @@ export function AdminChangeBookingFlow({
|
|
|
376
479
|
timesForSelectedDate,
|
|
377
480
|
companyTimezone,
|
|
378
481
|
changeFlowResolvedInitialProductOptionId,
|
|
482
|
+
mostPopularProductOptionId:
|
|
483
|
+
activeOptions.find((option) => option.mostPopular)?.optionId ?? null,
|
|
379
484
|
precomputedPricesByOption,
|
|
380
485
|
currency,
|
|
381
486
|
originalReceiptSubtotal: originalReceipt?.subtotal,
|
|
@@ -396,7 +501,12 @@ export function AdminChangeBookingFlow({
|
|
|
396
501
|
setError,
|
|
397
502
|
});
|
|
398
503
|
|
|
504
|
+
const lockExistingAddOnQuantities = shouldLockExistingAddOnQuantities({
|
|
505
|
+
isCustomerSelfServeChange,
|
|
506
|
+
isAdmin,
|
|
507
|
+
});
|
|
399
508
|
const {
|
|
509
|
+
initialAddOnQtyByKey,
|
|
400
510
|
initialAddOnMinQtyByKey,
|
|
401
511
|
initialAddOnMinTotalByAddOnId,
|
|
402
512
|
updateAddOnSelections,
|
|
@@ -404,7 +514,7 @@ export function AdminChangeBookingFlow({
|
|
|
404
514
|
initialValues,
|
|
405
515
|
originalReceiptLineItems: originalReceipt?.lineItems,
|
|
406
516
|
addOns,
|
|
407
|
-
|
|
517
|
+
lockExistingAddOnQuantities,
|
|
408
518
|
setAddOnSelections,
|
|
409
519
|
});
|
|
410
520
|
|
|
@@ -530,7 +640,6 @@ export function AdminChangeBookingFlow({
|
|
|
530
640
|
editableSummaryLineAmountInputs,
|
|
531
641
|
editableSummaryLineLabelInputs,
|
|
532
642
|
editableSummaryPreSubtotalDelta,
|
|
533
|
-
editableSummaryPreSubtotalDebugRows,
|
|
534
643
|
handleEditableLineAmountInputChange,
|
|
535
644
|
handleEditableLineAmountInputBlur,
|
|
536
645
|
handleEditableLineAmountReset,
|
|
@@ -558,7 +667,7 @@ export function AdminChangeBookingFlow({
|
|
|
558
667
|
originalAddOnSelections: initialValues?.addOnSelections ?? null,
|
|
559
668
|
originalProductOptionId: initialValues?.productOptionId ?? null,
|
|
560
669
|
addOns,
|
|
561
|
-
adminCustomReceiptLines,
|
|
670
|
+
adminCustomReceiptLines: appliedAdminCustomReceiptLines,
|
|
562
671
|
quantities,
|
|
563
672
|
flowUi,
|
|
564
673
|
isProviderDashboardChange,
|
|
@@ -596,8 +705,6 @@ export function AdminChangeBookingFlow({
|
|
|
596
705
|
unchangedReceiptAnchor,
|
|
597
706
|
showChangeFlowManualPriceLines,
|
|
598
707
|
showAdminCustomLineEditor,
|
|
599
|
-
displayedChangeAmountsRaw,
|
|
600
|
-
displayLayerUsesExternalPricing,
|
|
601
708
|
displayChangeFlowTax,
|
|
602
709
|
displayChangeFlowProposedTotal,
|
|
603
710
|
displayChangeFlowProposedTotalWithEditableLines,
|
|
@@ -627,7 +734,7 @@ export function AdminChangeBookingFlow({
|
|
|
627
734
|
pickupLocationId,
|
|
628
735
|
quantities,
|
|
629
736
|
addOnSelections,
|
|
630
|
-
|
|
737
|
+
initialAddOnQtyByKey,
|
|
631
738
|
changeFlowResolvedInitialProductOptionId,
|
|
632
739
|
selectedParentProductChanged,
|
|
633
740
|
isProviderDashboardChange,
|
|
@@ -636,7 +743,8 @@ export function AdminChangeBookingFlow({
|
|
|
636
743
|
providerQuotedLines,
|
|
637
744
|
editableSummaryPreSubtotalDelta,
|
|
638
745
|
adminCustomAdjustmentTotal,
|
|
639
|
-
adminCustomReceiptLines,
|
|
746
|
+
adminCustomReceiptLines: appliedAdminCustomReceiptLines,
|
|
747
|
+
adminReverseAdjustmentIds,
|
|
640
748
|
isManualOverrideEligibleLine,
|
|
641
749
|
isAdmin,
|
|
642
750
|
useAdminFeAuthoritativeQuote,
|
|
@@ -674,37 +782,6 @@ export function AdminChangeBookingFlow({
|
|
|
674
782
|
editableSummaryPreSubtotalDelta,
|
|
675
783
|
});
|
|
676
784
|
|
|
677
|
-
const changeFlowAdminPricingDebugPanel = useAdminChangePricingDebugPanel({
|
|
678
|
-
isAdmin,
|
|
679
|
-
hasSelectedAvailability: selectedAvailability != null,
|
|
680
|
-
totalQuantity,
|
|
681
|
-
useAdminFeAuthoritativeQuote,
|
|
682
|
-
currency,
|
|
683
|
-
locale,
|
|
684
|
-
originalReceipt,
|
|
685
|
-
isCustomerSelfServeChange,
|
|
686
|
-
latestChangeQuote,
|
|
687
|
-
changeQuoteFetchError,
|
|
688
|
-
displayLayerUsesExternalPricing,
|
|
689
|
-
selfServePricingConfirmed,
|
|
690
|
-
changeQuoteLoading,
|
|
691
|
-
effectiveSubtotal,
|
|
692
|
-
adminCustomAdjustmentTotal,
|
|
693
|
-
editableSummaryPreSubtotalDebugRows,
|
|
694
|
-
effectiveSubtotalForCheckout,
|
|
695
|
-
effectiveTax,
|
|
696
|
-
effectivePromoDiscountAmount,
|
|
697
|
-
totalPrice,
|
|
698
|
-
changeFlowNewBookingTotal,
|
|
699
|
-
displayedChangeAmountsRaw,
|
|
700
|
-
editableSummaryPreSubtotalDelta,
|
|
701
|
-
displayChangeFlowProposedTotal,
|
|
702
|
-
displayChangeFlowProposedTotalWithEditableLines,
|
|
703
|
-
adminFeAuthoritativeReceipt,
|
|
704
|
-
changeFlowClientEstimateDue,
|
|
705
|
-
changeFlowAmountDue,
|
|
706
|
-
});
|
|
707
|
-
|
|
708
785
|
const refundDecisionSelectionKey = useMemo(() => buildAdminChangeQuoteRequestKey({
|
|
709
786
|
bookingReference: initialValues?.bookingReference,
|
|
710
787
|
lastName,
|
|
@@ -717,8 +794,9 @@ export function AdminChangeBookingFlow({
|
|
|
717
794
|
cancellationPolicyId,
|
|
718
795
|
promoCode: appliedPromoCode,
|
|
719
796
|
promoApplicationScope,
|
|
720
|
-
adminCustomReceiptLines,
|
|
797
|
+
adminCustomReceiptLines: appliedAdminCustomReceiptLines,
|
|
721
798
|
structuredAdjustments: adminStructuredAdjustments,
|
|
799
|
+
reverseAdjustmentIds: adminReverseAdjustmentIds,
|
|
722
800
|
useAdminFeAuthoritativeQuote,
|
|
723
801
|
}), [
|
|
724
802
|
initialValues?.bookingReference,
|
|
@@ -732,8 +810,9 @@ export function AdminChangeBookingFlow({
|
|
|
732
810
|
cancellationPolicyId,
|
|
733
811
|
appliedPromoCode,
|
|
734
812
|
promoApplicationScope,
|
|
735
|
-
|
|
813
|
+
appliedAdminCustomReceiptLines,
|
|
736
814
|
adminStructuredAdjustments,
|
|
815
|
+
adminReverseAdjustmentIds,
|
|
737
816
|
useAdminFeAuthoritativeQuote,
|
|
738
817
|
]);
|
|
739
818
|
const responseRefundDecisionOperations = latestChangeQuote?.refundDecisionOperations ?? [];
|
|
@@ -754,6 +833,12 @@ export function AdminChangeBookingFlow({
|
|
|
754
833
|
latestChangeQuote?.allowedRefundDispositionsByOperationId,
|
|
755
834
|
latestChangeQuote?.removedValueByOperationId,
|
|
756
835
|
]);
|
|
836
|
+
useEffect(() => {
|
|
837
|
+
if (!isProviderDashboardChange || responseRefundDecisionOperations.length === 0) return;
|
|
838
|
+
setRefundDispositionsByOperationId((current) =>
|
|
839
|
+
defaultPendingRefundDispositions(responseRefundDecisionOperations, current),
|
|
840
|
+
);
|
|
841
|
+
}, [isProviderDashboardChange, responseRefundDecisionOperations]);
|
|
757
842
|
const activeRefundDecisionContext = useMemo(() => mergeAdminRefundDecisionContext({
|
|
758
843
|
current: refundDecisionContext,
|
|
759
844
|
selectionKey: refundDecisionSelectionKey,
|
|
@@ -772,10 +857,14 @@ export function AdminChangeBookingFlow({
|
|
|
772
857
|
? responseReturnPriceTreatmentOperations
|
|
773
858
|
: [];
|
|
774
859
|
const activeRefundDispositionsByOperationId = useMemo(() => {
|
|
860
|
+
const dispositionsWithReturnPriceDefaults = defaultKeepOriginalReturnPriceDispositions(
|
|
861
|
+
activeReturnPriceTreatmentOperations,
|
|
862
|
+
refundDispositionsByOperationId,
|
|
863
|
+
);
|
|
775
864
|
return selectRefundDispositionsForOperations(
|
|
776
865
|
activeRefundDecisionContext,
|
|
777
866
|
activeReturnPriceTreatmentOperations,
|
|
778
|
-
|
|
867
|
+
dispositionsWithReturnPriceDefaults,
|
|
779
868
|
);
|
|
780
869
|
}, [
|
|
781
870
|
activeRefundDecisionContext?.operations,
|
|
@@ -816,8 +905,11 @@ export function AdminChangeBookingFlow({
|
|
|
816
905
|
cancellationPolicyId,
|
|
817
906
|
appliedPromoCode,
|
|
818
907
|
promoApplicationScope,
|
|
819
|
-
adminCustomReceiptLines,
|
|
908
|
+
adminCustomReceiptLines: appliedAdminCustomReceiptLines,
|
|
820
909
|
adminStructuredAdjustments,
|
|
910
|
+
adminReverseAdjustmentIds,
|
|
911
|
+
needsAdjustmentDiscovery:
|
|
912
|
+
isProviderDashboardChange && !hasHydratedExistingAdjustments,
|
|
821
913
|
refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
|
|
822
914
|
useAdminFeAuthoritativeQuote,
|
|
823
915
|
latestChangeQuote,
|
|
@@ -1036,6 +1128,7 @@ export function AdminChangeBookingFlow({
|
|
|
1036
1128
|
providerPricingOverrides,
|
|
1037
1129
|
mergedProviderAdditionalAdjustments,
|
|
1038
1130
|
adminStructuredAdjustments,
|
|
1131
|
+
adminReverseAdjustmentIds,
|
|
1039
1132
|
refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
|
|
1040
1133
|
providerApplyAuthoritativeReceipt,
|
|
1041
1134
|
onSuccess,
|
|
@@ -1050,7 +1143,7 @@ export function AdminChangeBookingFlow({
|
|
|
1050
1143
|
effectiveCancellationPolicyLabel,
|
|
1051
1144
|
addOns,
|
|
1052
1145
|
feeLineItems,
|
|
1053
|
-
adminCustomReceiptLines,
|
|
1146
|
+
adminCustomReceiptLines: appliedAdminCustomReceiptLines,
|
|
1054
1147
|
isTaxIncludedInPrice,
|
|
1055
1148
|
originalReceiptPromoAdjustment,
|
|
1056
1149
|
effectivePromoLineType,
|
|
@@ -1126,9 +1219,9 @@ export function AdminChangeBookingFlow({
|
|
|
1126
1219
|
isSimplifiedPricingView, onQuantityChange: handleQuantityChange,
|
|
1127
1220
|
minimumQuantities: changeBookingMinimumQuantities, applyReceiptPaidFloors: changeFlowApplyReceiptPaidFloors,
|
|
1128
1221
|
ticketUnitFloorByCategory: changeFlowTicketBookedUnitPriceByCategory,
|
|
1129
|
-
...{ addOns, addOnSelections },
|
|
1222
|
+
...{ addOns, knownAddOns, addOnCatalogLoaded, addOnSelections },
|
|
1130
1223
|
onAddOnSelectionsChange: updateAddOnSelections,
|
|
1131
|
-
minimumTotalByAddOnId:
|
|
1224
|
+
minimumTotalByAddOnId: lockExistingAddOnQuantities ? initialAddOnMinTotalByAddOnId : undefined,
|
|
1132
1225
|
priceSummaryLines: taxIncludedReconciledCheckoutPriceSummaryLines,
|
|
1133
1226
|
replacePriceSummary: hasChangeSelection ? selfServeCheckoutPlaceholder : undefined,
|
|
1134
1227
|
changeFlowAmountDue,
|
|
@@ -1140,13 +1233,23 @@ export function AdminChangeBookingFlow({
|
|
|
1140
1233
|
// normalized server preview, render it; do not discard valid data because an
|
|
1141
1234
|
// optional/versioned metadata string is missing or renamed.
|
|
1142
1235
|
serverAmendmentLines: latestChangeQuote?.serverPreview?.priceSummaryLines ?? null,
|
|
1236
|
+
serverResultingReceiptLines:
|
|
1237
|
+
latestChangeQuote?.serverPreview?.resultingReceiptLines ?? null,
|
|
1143
1238
|
serverAmountDue: latestChangeQuote?.serverPreview?.amountDue ?? null,
|
|
1144
1239
|
serverUpdatedTotal: latestChangeQuote?.serverPreview?.totalNewBooking ?? null,
|
|
1240
|
+
serverNetPaid: latestChangeQuote?.amountPreviouslyPaid ?? null,
|
|
1241
|
+
serverCurrentPaymentCredit: latestChangeQuote?.currentPaymentCreditTotal ?? 0,
|
|
1242
|
+
serverProjectedPaymentCredit: latestChangeQuote?.projectedPaymentCreditTotal ?? 0,
|
|
1243
|
+
serverRefundCandidate: latestChangeQuote?.refundCandidate ?? 0,
|
|
1145
1244
|
serverQuoteError:
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1245
|
+
hasIncompatibleAddOnSelections
|
|
1246
|
+
? incompatibleAddOnError
|
|
1247
|
+
: (
|
|
1248
|
+
changeQuoteFetchError ??
|
|
1249
|
+
(isChangeQuoteBlocked
|
|
1250
|
+
? (latestChangeQuote?.reasonIfBlocked ?? 'This change cannot be priced right now.')
|
|
1251
|
+
: null)
|
|
1252
|
+
),
|
|
1150
1253
|
refundDecisionOperations: activeRefundDecisionContext?.operations ?? [],
|
|
1151
1254
|
returnPriceTreatmentOperations: activeReturnPriceTreatmentOperations,
|
|
1152
1255
|
allowedRefundDispositionsByOperationId: {
|
|
@@ -1158,16 +1261,21 @@ export function AdminChangeBookingFlow({
|
|
|
1158
1261
|
refundDispositionsByOperationId: activeRefundDispositionsByOperationId,
|
|
1159
1262
|
onRefundDispositionChange: handleRefundDispositionChange,
|
|
1160
1263
|
refundQuoteLoading: changeQuoteLoading,
|
|
1264
|
+
sourceProductName: initialProduct.name,
|
|
1265
|
+
targetProductName: product.name,
|
|
1161
1266
|
...{ priceSummaryLinesIncludeTaxRow, isTaxIncludedInPrice, showProviderPricingInlineEditor },
|
|
1162
1267
|
taxRate: pricingConfig?.taxRate,
|
|
1163
1268
|
...{ providerPricingUi, providerQuotedLines },
|
|
1164
|
-
debugPanel: changeFlowAdminPricingDebugPanel,
|
|
1165
1269
|
promoEditor: adminPromoEditor,
|
|
1166
1270
|
promoCode: appliedPromoCode,
|
|
1167
1271
|
...{ showChangeFlowManualPriceLines, showAdminCustomLineEditor, adminCustomReceiptLines },
|
|
1272
|
+
hasPendingAdminCustomReceiptLineChanges,
|
|
1273
|
+
pendingAdminCustomReceiptLineIds,
|
|
1274
|
+
adjustmentComponentOptions: latestChangeQuote?.selectableAdjustmentComponents ?? [],
|
|
1168
1275
|
onAddAdminCustomReceiptLine: handleAddAdminCustomReceiptLine,
|
|
1169
1276
|
onUpdateAdminCustomReceiptLine: handleUpdateAdminCustomReceiptLine,
|
|
1170
1277
|
onRemoveAdminCustomReceiptLine: handleRemoveAdminCustomReceiptLine,
|
|
1278
|
+
onApplyAdminCustomReceiptLines: handleApplyAdminCustomReceiptLines,
|
|
1171
1279
|
onFirstNameChange: setFirstName, onLastNameChange: setLastName, onEmailChange: setEmail,
|
|
1172
1280
|
onClearError: () => setError(''),
|
|
1173
1281
|
pickupLocations: product.pickupLocations, destinations: product.destinations, highlightedPickupLocationIds,
|
|
@@ -1177,11 +1285,18 @@ export function AdminChangeBookingFlow({
|
|
|
1177
1285
|
...{ skipConfirmationCommunications, disableAutoCommunications },
|
|
1178
1286
|
onSkipConfirmationChange: setSkipConfirmationCommunications,
|
|
1179
1287
|
onDisableCommunicationsChange: setDisableAutoCommunications,
|
|
1180
|
-
checkoutError: checkoutFormError,
|
|
1288
|
+
checkoutError: incompatibleAddOnError || checkoutFormError,
|
|
1181
1289
|
onCheckout: handleCheckout,
|
|
1182
1290
|
submitLabel: changeCheckoutButtonLabel ?? deferredInvoiceSubmitLabel,
|
|
1183
1291
|
...{ hasEffectiveChangeSelection, isChangeQuoteBlocked },
|
|
1184
|
-
submitDisabled:
|
|
1292
|
+
submitDisabled:
|
|
1293
|
+
changeFlowSubmitDisabled ||
|
|
1294
|
+
hasIncompatibleAddOnSelections ||
|
|
1295
|
+
hasPendingAdminCustomReceiptLineChanges ||
|
|
1296
|
+
adminCustomReceiptLines.some(
|
|
1297
|
+
(line) =>
|
|
1298
|
+
line.scope === 'SELECTED_COMPONENTS' && line.selectedComponentIds.length === 0,
|
|
1299
|
+
),
|
|
1185
1300
|
attributionSummary: flowUi?.partnerAttributionSummary, attributionConfirmLabel: flowUi?.partnerAttributionConfirmLabel,
|
|
1186
1301
|
attributionConfirmed: partnerAttributionConfirmed,
|
|
1187
1302
|
onAttributionConfirmedChange: setPartnerAttributionConfirmed,
|