@ticketboothapp/booking 1.2.136 → 1.2.138
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/AdminChangeReceiptComparison.tsx +18 -33
- package/src/components/booking/PriceBreakdown.tsx +4 -0
- package/src/components/booking/PriceSummary.tsx +2 -0
- package/src/components/booking/booking-flow-types.ts +1 -0
- package/src/lib/booking/change-booking-server-preview.ts +32 -0
- package/src/lib/booking-api.ts +18 -0
package/package.json
CHANGED
|
@@ -2,7 +2,6 @@ import { useMemo, type ReactNode } from 'react';
|
|
|
2
2
|
import type { AdminFeAuthoritativeReceipt } from '../../lib/booking-api';
|
|
3
3
|
import { roundMoney } from '../../lib/booking/change-flow-pricing';
|
|
4
4
|
import { mapQuoteLineItemsToPriceSummaryLines } from '../../lib/booking/change-booking-server-preview';
|
|
5
|
-
import type { PriceBreakdown as PriceBreakdownData } from '../../lib/booking/pricing';
|
|
6
5
|
import { formatCurrencyAmount } from '../../lib/currency';
|
|
7
6
|
import type { Locale } from '../../lib/booking/i18n/config';
|
|
8
7
|
import type { ChangeBookingFlowProps } from './booking-flow-types';
|
|
@@ -41,39 +40,9 @@ function hasTaxLine(lines: PriceSummaryLine[]): boolean {
|
|
|
41
40
|
);
|
|
42
41
|
}
|
|
43
42
|
|
|
44
|
-
function storedReceiptTicketBreakdown(line: Extract<PriceSummaryLine, { kind: 'ticket' }>): PriceBreakdownData {
|
|
45
|
-
const qty = Math.max(1, line.qty);
|
|
46
|
-
const unitAmount = roundMoney(line.itemTotal / qty);
|
|
47
|
-
return {
|
|
48
|
-
lineItems: [
|
|
49
|
-
{
|
|
50
|
-
type: 'base',
|
|
51
|
-
name: 'Stored receipt unit',
|
|
52
|
-
amountInDisplayCurrency: unitAmount,
|
|
53
|
-
},
|
|
54
|
-
],
|
|
55
|
-
subtotalAfterAdjustments: unitAmount,
|
|
56
|
-
feeLines: [],
|
|
57
|
-
taxRate: 0,
|
|
58
|
-
taxAmount: 0,
|
|
59
|
-
isTaxIncluded: false,
|
|
60
|
-
finalPrice: unitAmount,
|
|
61
|
-
};
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
function withStoredReceiptBreakdowns(lines: PriceSummaryLine[]): PriceSummaryLine[] {
|
|
65
|
-
return lines.map((line) => {
|
|
66
|
-
if (line.kind !== 'ticket' || line.breakdown) return line;
|
|
67
|
-
return {
|
|
68
|
-
...line,
|
|
69
|
-
breakdown: storedReceiptTicketBreakdown(line),
|
|
70
|
-
};
|
|
71
|
-
});
|
|
72
|
-
}
|
|
73
|
-
|
|
74
43
|
function mapOriginalReceiptLines(originalReceipt: OriginalReceipt): PriceSummaryLine[] {
|
|
75
44
|
if (!originalReceipt.lineItems?.length) return [];
|
|
76
|
-
return
|
|
45
|
+
return mapQuoteLineItemsToPriceSummaryLines(originalReceipt.lineItems);
|
|
77
46
|
}
|
|
78
47
|
|
|
79
48
|
function lineIdentity(line: PriceSummaryLine): string {
|
|
@@ -194,6 +163,22 @@ export function AdminChangeReceiptComparison({
|
|
|
194
163
|
);
|
|
195
164
|
const changeSubtotal = roundMoney(amountDue - changeTax);
|
|
196
165
|
const originalCurrency = originalReceipt.currency ?? currency;
|
|
166
|
+
const originalReceiptLinesWithUnitAmounts = useMemo(
|
|
167
|
+
() =>
|
|
168
|
+
originalReceiptLines.map((line) =>
|
|
169
|
+
line.kind === 'ticket' && line.qty > 0
|
|
170
|
+
? {
|
|
171
|
+
...line,
|
|
172
|
+
unitAmountLabel: `${formatCurrencyAmount(
|
|
173
|
+
roundMoney(line.itemTotal / line.qty),
|
|
174
|
+
originalCurrency,
|
|
175
|
+
displayLocale,
|
|
176
|
+
)} each`,
|
|
177
|
+
}
|
|
178
|
+
: line,
|
|
179
|
+
),
|
|
180
|
+
[displayLocale, originalCurrency, originalReceiptLines],
|
|
181
|
+
);
|
|
197
182
|
const amountDueClass =
|
|
198
183
|
amountDue < -0.005
|
|
199
184
|
? 'text-emerald-700'
|
|
@@ -212,7 +197,7 @@ export function AdminChangeReceiptComparison({
|
|
|
212
197
|
</span>
|
|
213
198
|
</div>
|
|
214
199
|
<PriceSummary
|
|
215
|
-
lines={
|
|
200
|
+
lines={originalReceiptLinesWithUnitAmounts}
|
|
216
201
|
total={originalReceipt.total}
|
|
217
202
|
currency={originalCurrency}
|
|
218
203
|
locale={displayLocale}
|
|
@@ -12,6 +12,7 @@ export interface PriceBreakdownProps {
|
|
|
12
12
|
category: string;
|
|
13
13
|
qty: number;
|
|
14
14
|
itemTotal: number;
|
|
15
|
+
unitAmountLabel?: string;
|
|
15
16
|
breakdown: PriceBreakdownType | null;
|
|
16
17
|
currency: Currency;
|
|
17
18
|
locale: Locale;
|
|
@@ -59,6 +60,7 @@ export function PriceBreakdown({
|
|
|
59
60
|
category,
|
|
60
61
|
qty,
|
|
61
62
|
itemTotal,
|
|
63
|
+
unitAmountLabel,
|
|
62
64
|
breakdown,
|
|
63
65
|
currency,
|
|
64
66
|
locale,
|
|
@@ -96,6 +98,7 @@ export function PriceBreakdown({
|
|
|
96
98
|
) : (
|
|
97
99
|
<span className="text-sm text-stone-600">
|
|
98
100
|
{category} {qty > 1 ? `× ${qty}` : ''}
|
|
101
|
+
{unitAmountLabel ? <span className="ml-1 text-xs text-stone-400">· {unitAmountLabel}</span> : null}
|
|
99
102
|
</span>
|
|
100
103
|
)}
|
|
101
104
|
{editable && onEditableChange ? (
|
|
@@ -157,6 +160,7 @@ export function PriceBreakdown({
|
|
|
157
160
|
}
|
|
158
161
|
>
|
|
159
162
|
{category} {qty > 1 ? `× ${qty}` : ''}
|
|
163
|
+
{unitAmountLabel ? <span className="ml-1 text-xs text-stone-400">· {unitAmountLabel}</span> : null}
|
|
160
164
|
</span>
|
|
161
165
|
)}
|
|
162
166
|
<div className="relative flex-shrink-0 whitespace-nowrap">
|
|
@@ -18,6 +18,7 @@ export type PriceSummaryLine =
|
|
|
18
18
|
category: string;
|
|
19
19
|
qty: number;
|
|
20
20
|
itemTotal: number;
|
|
21
|
+
unitAmountLabel?: string;
|
|
21
22
|
breakdown?: PriceBreakdownType | null;
|
|
22
23
|
}
|
|
23
24
|
| {
|
|
@@ -210,6 +211,7 @@ export function PriceSummary({
|
|
|
210
211
|
category={row.category}
|
|
211
212
|
qty={row.qty}
|
|
212
213
|
itemTotal={row.itemTotal}
|
|
214
|
+
unitAmountLabel={row.unitAmountLabel}
|
|
213
215
|
breakdown={row.breakdown ?? null}
|
|
214
216
|
currency={currency}
|
|
215
217
|
locale={locale}
|
|
@@ -177,6 +177,7 @@ export interface ChangeBookingFlowProps extends BookingFlowBaseProps {
|
|
|
177
177
|
label?: string;
|
|
178
178
|
amount?: number;
|
|
179
179
|
quantity?: number;
|
|
180
|
+
priceBasis?: import('../../lib/booking-api').PriceBasisSnapshot | null;
|
|
180
181
|
}>;
|
|
181
182
|
} | null;
|
|
182
183
|
/** Admin/provider dashboard integration hook (used by AdminChangeBookingFlow). */
|
|
@@ -6,6 +6,7 @@
|
|
|
6
6
|
*/
|
|
7
7
|
import type { ChangeBookingQuoteResponse } from '../booking-api';
|
|
8
8
|
import type { PricingV2QuoteLineSnapshot } from '../booking-api';
|
|
9
|
+
import type { PriceBasisSnapshot } from '../booking-api';
|
|
9
10
|
import type { PriceSummaryLine } from '../../components/booking/PriceSummary';
|
|
10
11
|
import { roundMoney, serverTotalsFromChangeQuoteResponse } from './change-flow-pricing';
|
|
11
12
|
|
|
@@ -41,6 +42,7 @@ type QuoteSummaryInputLine =
|
|
|
41
42
|
amount?: number;
|
|
42
43
|
type?: string;
|
|
43
44
|
quantity?: number;
|
|
45
|
+
priceBasis?: PriceBasisSnapshot | null;
|
|
44
46
|
};
|
|
45
47
|
|
|
46
48
|
function lineItemsForSummary(quote: ChangeBookingQuoteResponse): QuoteSummaryInputLine[] | undefined {
|
|
@@ -102,6 +104,34 @@ function amountForDisplay(line: QuoteSummaryInputLine): number {
|
|
|
102
104
|
return isReductionLine(line) && amount > 0 ? -amount : amount;
|
|
103
105
|
}
|
|
104
106
|
|
|
107
|
+
function breakdownFromPriceBasis(priceBasis: PriceBasisSnapshot | null | undefined) {
|
|
108
|
+
if (!priceBasis) return undefined;
|
|
109
|
+
return {
|
|
110
|
+
lineItems: [
|
|
111
|
+
{
|
|
112
|
+
type: 'base' as const,
|
|
113
|
+
name: 'Base price',
|
|
114
|
+
amountInDisplayCurrency: priceBasis.baseUnitAmount,
|
|
115
|
+
},
|
|
116
|
+
...(priceBasis.adjustments ?? []).map((adjustment) => ({
|
|
117
|
+
type: 'adjustment' as const,
|
|
118
|
+
id: adjustment.id,
|
|
119
|
+
name: adjustment.name,
|
|
120
|
+
amountInDisplayCurrency: adjustment.amount,
|
|
121
|
+
adjustmentType: adjustment.adjustmentType,
|
|
122
|
+
adjustmentValue: adjustment.adjustmentValue,
|
|
123
|
+
sourceType: adjustment.type === 'deal' ? ('deal' as const) : ('dynamic' as const),
|
|
124
|
+
})),
|
|
125
|
+
],
|
|
126
|
+
subtotalAfterAdjustments: priceBasis.finalUnitAmount,
|
|
127
|
+
feeLines: [],
|
|
128
|
+
taxRate: 0,
|
|
129
|
+
taxAmount: 0,
|
|
130
|
+
isTaxIncluded: false,
|
|
131
|
+
finalPrice: priceBasis.finalUnitAmount,
|
|
132
|
+
};
|
|
133
|
+
}
|
|
134
|
+
|
|
105
135
|
function hasPaymentCreditLine(lines: PriceSummaryLine[]): boolean {
|
|
106
136
|
return lines.some((line) => {
|
|
107
137
|
if (line.kind !== 'line') return false;
|
|
@@ -163,6 +193,7 @@ export function mapQuoteLineItemsToPriceSummaryLines(
|
|
|
163
193
|
category,
|
|
164
194
|
qty: qty > 0 ? qty : 1,
|
|
165
195
|
itemTotal: amount,
|
|
196
|
+
breakdown: breakdownFromPriceBasis(item.priceBasis),
|
|
166
197
|
});
|
|
167
198
|
continue;
|
|
168
199
|
}
|
|
@@ -182,6 +213,7 @@ export function mapQuoteLineItemsToPriceSummaryLines(
|
|
|
182
213
|
category: lettersOnly,
|
|
183
214
|
qty,
|
|
184
215
|
itemTotal: amount,
|
|
216
|
+
breakdown: breakdownFromPriceBasis(item.priceBasis),
|
|
185
217
|
});
|
|
186
218
|
continue;
|
|
187
219
|
}
|
package/src/lib/booking-api.ts
CHANGED
|
@@ -1972,6 +1972,24 @@ export interface PricingV2QuoteLineSnapshot {
|
|
|
1972
1972
|
discountBehavior?: string | null;
|
|
1973
1973
|
source?: string | null;
|
|
1974
1974
|
metadata?: Record<string, string> | null;
|
|
1975
|
+
priceBasis?: PriceBasisSnapshot | null;
|
|
1976
|
+
}
|
|
1977
|
+
|
|
1978
|
+
export interface PriceBasisAdjustmentSnapshot {
|
|
1979
|
+
type: string;
|
|
1980
|
+
id: string;
|
|
1981
|
+
name: string;
|
|
1982
|
+
amount: number;
|
|
1983
|
+
adjustmentType: string;
|
|
1984
|
+
adjustmentValue: number;
|
|
1985
|
+
}
|
|
1986
|
+
|
|
1987
|
+
export interface PriceBasisSnapshot {
|
|
1988
|
+
baseUnitAmount: number;
|
|
1989
|
+
finalUnitAmount: number;
|
|
1990
|
+
currency: string;
|
|
1991
|
+
adjustments?: PriceBasisAdjustmentSnapshot[] | null;
|
|
1992
|
+
pricingProfileId?: string | null;
|
|
1975
1993
|
}
|
|
1976
1994
|
|
|
1977
1995
|
export interface PricingV2QuoteSnapshot {
|