@voyantjs/bookings-ui 0.19.0 → 0.21.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/booking-dialog.d.ts.map +1 -1
- package/dist/components/booking-dialog.js +10 -1
- package/dist/components/booking-group-section.d.ts +11 -1
- package/dist/components/booking-group-section.d.ts.map +1 -1
- package/dist/components/booking-group-section.js +16 -2
- package/dist/components/booking-item-list.d.ts.map +1 -1
- package/dist/components/booking-item-list.js +71 -7
- package/dist/components/booking-list.d.ts.map +1 -1
- package/dist/components/booking-list.js +11 -3
- package/dist/components/booking-payments-summary.d.ts +28 -1
- package/dist/components/booking-payments-summary.d.ts.map +1 -1
- package/dist/components/booking-payments-summary.js +66 -11
- package/dist/components/traveler-list.d.ts +2 -1
- package/dist/components/traveler-list.d.ts.map +1 -1
- package/dist/components/traveler-list.js +126 -12
- package/dist/i18n/en.d.ts +12 -0
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +13 -1
- package/dist/i18n/messages.d.ts +14 -1
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/provider.d.ts +24 -0
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/ro.d.ts +12 -0
- package/dist/i18n/ro.d.ts.map +1 -1
- package/dist/i18n/ro.js +13 -1
- package/dist/journey/components/booking-journey.d.ts +3 -0
- package/dist/journey/components/booking-journey.d.ts.map +1 -0
- package/dist/journey/components/booking-journey.js +376 -0
- package/dist/journey/components/contract-preview-dialog.d.ts +47 -0
- package/dist/journey/components/contract-preview-dialog.d.ts.map +1 -0
- package/dist/journey/components/contract-preview-dialog.js +119 -0
- package/dist/journey/components/journey-steps.d.ts +47 -0
- package/dist/journey/components/journey-steps.d.ts.map +1 -0
- package/dist/journey/components/journey-steps.js +582 -0
- package/dist/journey/components/side-panel.d.ts +12 -0
- package/dist/journey/components/side-panel.d.ts.map +1 -0
- package/dist/journey/components/side-panel.js +172 -0
- package/dist/journey/components/step-header.d.ts +7 -0
- package/dist/journey/components/step-header.d.ts.map +1 -0
- package/dist/journey/components/step-header.js +28 -0
- package/dist/journey/index.d.ts +18 -0
- package/dist/journey/index.d.ts.map +1 -0
- package/dist/journey/index.js +17 -0
- package/dist/journey/lib/draft-state.d.ts +34 -0
- package/dist/journey/lib/draft-state.d.ts.map +1 -0
- package/dist/journey/lib/draft-state.js +54 -0
- package/dist/journey/types.d.ts +248 -0
- package/dist/journey/types.d.ts.map +1 -0
- package/dist/journey/types.js +17 -0
- package/package.json +31 -17
- package/src/styles.css +11 -0
|
@@ -0,0 +1,172 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { Accordion, AccordionContent, AccordionItem, AccordionTrigger, } from "@voyantjs/ui/components/accordion";
|
|
4
|
+
import { Card, CardContent } from "@voyantjs/ui/components/card";
|
|
5
|
+
import { Skeleton } from "@voyantjs/ui/components/skeleton";
|
|
6
|
+
/**
|
|
7
|
+
* Right-rail summary panel. Shows what's being booked, an
|
|
8
|
+
* accordion-per-step recap of the user's input, and the live
|
|
9
|
+
* pricing breakdown at the bottom. The current step's accordion is
|
|
10
|
+
* expanded by default; users can click any step to peek at what
|
|
11
|
+
* they've filled in elsewhere.
|
|
12
|
+
*/
|
|
13
|
+
export function PriceSidePanel({ pricing, isQuoting, invalidReason, entitySummary, currentStep, steps, draft, className, }) {
|
|
14
|
+
return (_jsxs(Card, { className: className, children: [entitySummary ? _jsx(EntityHeader, { summary: entitySummary }) : null, _jsxs(CardContent, { className: "space-y-4", children: [steps && steps.length > 0 && currentStep && draft ? (_jsx(StepRecap, { steps: steps, currentStep: currentStep, draft: draft })) : null, isQuoting && !pricing ? (_jsxs("div", { className: "space-y-2", children: [_jsx(Skeleton, { className: "h-4 w-24" }), _jsx(Skeleton, { className: "h-4 w-32" }), _jsx(Skeleton, { className: "h-4 w-20" })] })) : null, invalidReason ? _jsx("p", { className: "text-destructive text-sm", children: invalidReason }) : null, pricing ? (_jsxs("div", { className: "space-y-2 border-t pt-4", children: [_jsx("ul", { className: "space-y-1 text-sm", children: pricing.lines.map((line) => (_jsxs("li", { className: "flex justify-between", children: [_jsxs("span", { children: [line.label, line.quantity ? (_jsxs("span", { className: "text-muted-foreground", children: [" \u00D7 ", line.quantity] })) : null] }), _jsx("span", { children: formatMoney(line.totalAmount, pricing.currency) })] }, `${line.kind}-${line.label}-${line.totalAmount}`))) }), pricing.taxes.length > 0 ? (_jsx("ul", { className: "space-y-1 border-t pt-2 text-sm text-muted-foreground", children: pricing.taxes.map((tax) => (_jsxs("li", { className: "flex justify-between", children: [_jsx("span", { children: tax.label }), _jsx("span", { children: formatMoney(tax.amount, pricing.currency) })] }, tax.code))) })) : null, _jsxs("div", { className: "flex justify-between border-t pt-2 font-medium", children: [_jsx("span", { children: "Total" }), _jsx("span", { children: formatMoney(pricing.total, pricing.currency) })] })] })) : null] })] }));
|
|
15
|
+
}
|
|
16
|
+
function EntityHeader({ summary }) {
|
|
17
|
+
return (_jsxs("div", { className: "overflow-hidden rounded-t-xl", children: [summary.heroImageUrl ? (_jsx("img", { src: summary.heroImageUrl, alt: summary.name, className: "aspect-video w-full object-cover" })) : null, _jsxs("div", { className: "space-y-1 px-6 pt-4 pb-2", children: [_jsx("div", { className: "text-muted-foreground text-xs uppercase tracking-wide", children: "You're booking" }), _jsx("div", { className: "font-semibold leading-tight", children: summary.name }), summary.subtitle ? (_jsx("div", { className: "text-muted-foreground text-sm", children: summary.subtitle })) : null, summary.whenLabel || summary.locationLabel ? (_jsx("div", { className: "text-muted-foreground text-xs", children: [summary.whenLabel, summary.locationLabel].filter(Boolean).join(" · ") })) : null] })] }));
|
|
18
|
+
}
|
|
19
|
+
function StepRecap({ steps, currentStep, draft, }) {
|
|
20
|
+
if (!draft)
|
|
21
|
+
return null;
|
|
22
|
+
// Default-open the current step. Uncontrolled — users can toggle
|
|
23
|
+
// freely. Re-mounts when currentStep changes (key) so the
|
|
24
|
+
// newly-active step opens automatically as the user advances.
|
|
25
|
+
return (_jsx(Accordion, { defaultValue: [currentStep], multiple: true, children: steps.map((step) => (_jsxs(AccordionItem, { value: step, children: [_jsx(AccordionTrigger, { className: "py-3", children: _jsxs("div", { className: "flex flex-1 flex-col text-left", children: [_jsx("span", { className: step === currentStep ? "font-semibold" : "text-muted-foreground font-medium", children: STEP_LABELS[step] }), _jsx(StepSummaryLine, { step: step, draft: draft })] }) }), _jsx(AccordionContent, { children: _jsx(StepDetails, { step: step, draft: draft }) })] }, step))) }, currentStep));
|
|
26
|
+
}
|
|
27
|
+
function StepSummaryLine({ step, draft, }) {
|
|
28
|
+
const text = stepHeadline(step, draft);
|
|
29
|
+
if (!text)
|
|
30
|
+
return null;
|
|
31
|
+
return _jsx("span", { className: "text-muted-foreground text-xs", children: text });
|
|
32
|
+
}
|
|
33
|
+
function stepHeadline(step, draft) {
|
|
34
|
+
switch (step) {
|
|
35
|
+
case "configure": {
|
|
36
|
+
const total = paxTotal(draft);
|
|
37
|
+
const slot = draft.configure?.departureSlotId ?? draft.configure?.departureDate ?? undefined;
|
|
38
|
+
const range = draft.configure?.dateRange;
|
|
39
|
+
const when = range?.checkIn && range?.checkOut ? `${range.checkIn} → ${range.checkOut}` : slot;
|
|
40
|
+
return when ? `${total} ${total === 1 ? "guest" : "guests"} · ${when}` : `${total} guests`;
|
|
41
|
+
}
|
|
42
|
+
case "billing": {
|
|
43
|
+
const c = draft.billing.contact;
|
|
44
|
+
const name = [c.firstName, c.lastName].filter(Boolean).join(" ").trim();
|
|
45
|
+
return name || c.email || "Not set";
|
|
46
|
+
}
|
|
47
|
+
case "travelers": {
|
|
48
|
+
const filled = draft.travelers.filter((t) => t.firstName && t.lastName).length;
|
|
49
|
+
return `${filled} of ${draft.travelers.length} filled`;
|
|
50
|
+
}
|
|
51
|
+
case "accommodation": {
|
|
52
|
+
const rooms = draft.accommodation?.rooms ?? [];
|
|
53
|
+
if (rooms.length === 0)
|
|
54
|
+
return "";
|
|
55
|
+
return `${rooms.length} room${rooms.length === 1 ? "" : "s"}`;
|
|
56
|
+
}
|
|
57
|
+
case "addons": {
|
|
58
|
+
const addons = draft.addons ?? [];
|
|
59
|
+
if (addons.length === 0)
|
|
60
|
+
return "None";
|
|
61
|
+
return `${addons.length} add-on${addons.length === 1 ? "" : "s"}`;
|
|
62
|
+
}
|
|
63
|
+
case "payment": {
|
|
64
|
+
const intent = draft.payment.intent;
|
|
65
|
+
if (intent === "card")
|
|
66
|
+
return "Card";
|
|
67
|
+
if (intent === "hold")
|
|
68
|
+
return "Hold";
|
|
69
|
+
if (intent === "ticket_on_credit")
|
|
70
|
+
return "On credit";
|
|
71
|
+
return "";
|
|
72
|
+
}
|
|
73
|
+
case "review":
|
|
74
|
+
return "Confirm and book";
|
|
75
|
+
default:
|
|
76
|
+
return "";
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
function StepDetails({ step, draft, }) {
|
|
80
|
+
switch (step) {
|
|
81
|
+
case "configure":
|
|
82
|
+
return _jsx(ConfigureDetails, { draft: draft });
|
|
83
|
+
case "billing":
|
|
84
|
+
return _jsx(BillingDetails, { draft: draft });
|
|
85
|
+
case "travelers":
|
|
86
|
+
return _jsx(TravelersDetails, { draft: draft });
|
|
87
|
+
case "accommodation":
|
|
88
|
+
return _jsx(AccommodationDetails, { draft: draft });
|
|
89
|
+
case "addons":
|
|
90
|
+
return _jsx(AddonsDetails, { draft: draft });
|
|
91
|
+
case "payment":
|
|
92
|
+
return _jsx(PaymentDetails, { draft: draft });
|
|
93
|
+
case "review":
|
|
94
|
+
return (_jsx("p", { className: "text-muted-foreground text-xs", children: "Review your details and confirm to book." }));
|
|
95
|
+
default:
|
|
96
|
+
return null;
|
|
97
|
+
}
|
|
98
|
+
}
|
|
99
|
+
function ConfigureDetails({ draft, }) {
|
|
100
|
+
const cfg = draft.configure ?? {};
|
|
101
|
+
const range = cfg.dateRange;
|
|
102
|
+
return (_jsxs("dl", { className: "space-y-1 text-xs", children: [_jsx(Row, { label: "Adults", value: String(cfg.pax?.adult ?? 0) }), (cfg.pax?.child ?? 0) > 0 ? _jsx(Row, { label: "Children", value: String(cfg.pax.child) }) : null, (cfg.pax?.infant ?? 0) > 0 ? _jsx(Row, { label: "Infants", value: String(cfg.pax.infant) }) : null, cfg.departureSlotId ? _jsx(Row, { label: "Departure", value: cfg.departureSlotId }) : null, cfg.departureDate ? _jsx(Row, { label: "Date", value: cfg.departureDate }) : null, range?.checkIn ? _jsx(Row, { label: "Check-in", value: range.checkIn }) : null, range?.checkOut ? _jsx(Row, { label: "Check-out", value: range.checkOut }) : null, cfg.cabinCategoryId ? _jsx(Row, { label: "Cabin", value: cfg.cabinCategoryId }) : null] }));
|
|
103
|
+
}
|
|
104
|
+
function BillingDetails({ draft, }) {
|
|
105
|
+
const c = draft.billing.contact;
|
|
106
|
+
const a = draft.billing.address;
|
|
107
|
+
const addressLine = [a.line1, a.line2, a.city, a.postal, a.country].filter(Boolean).join(", ");
|
|
108
|
+
return (_jsxs("dl", { className: "space-y-1 text-xs", children: [_jsx(Row, { label: "Name", value: [c.firstName, c.lastName].filter(Boolean).join(" ") || "—" }), _jsx(Row, { label: "Email", value: c.email || "—" }), c.phone ? _jsx(Row, { label: "Phone", value: c.phone }) : null, _jsx(Row, { label: "Buyer", value: draft.billing.buyerType === "B2B" ? "Company" : "Individual" }), draft.billing.company?.name ? (_jsx(Row, { label: "Company", value: draft.billing.company.name })) : null, draft.billing.company?.vatId ? (_jsx(Row, { label: "VAT", value: draft.billing.company.vatId })) : null, addressLine ? _jsx(Row, { label: "Address", value: addressLine }) : null] }));
|
|
109
|
+
}
|
|
110
|
+
function TravelersDetails({ draft, }) {
|
|
111
|
+
if (draft.travelers.length === 0) {
|
|
112
|
+
return _jsx("p", { className: "text-muted-foreground text-xs", children: "No travelers yet." });
|
|
113
|
+
}
|
|
114
|
+
return (_jsx("ul", { className: "space-y-3 text-xs", children: draft.travelers.map((t, i) => {
|
|
115
|
+
const key = t.rowId ?? `t-${i}`;
|
|
116
|
+
const name = [t.firstName, t.lastName].filter(Boolean).join(" ").trim();
|
|
117
|
+
const docType = t.documents?.documentType;
|
|
118
|
+
const docNum = t.documents?.documentNumber;
|
|
119
|
+
const docExpiry = t.documents?.documentExpiry;
|
|
120
|
+
const docLine = [docType, docNum, docExpiry ? `exp ${docExpiry}` : null]
|
|
121
|
+
.filter(Boolean)
|
|
122
|
+
.join(" · ");
|
|
123
|
+
return (_jsxs("li", { className: "space-y-0.5", children: [_jsxs("div", { className: "flex justify-between gap-2", children: [_jsxs("span", { className: "text-muted-foreground", children: ["Traveler ", i + 1] }), _jsx("span", { className: "truncate font-medium", children: name || "—" })] }), t.email ? _jsx("div", { className: "text-muted-foreground truncate", children: t.email }) : null, t.phone ? _jsx("div", { className: "text-muted-foreground", children: t.phone }) : null, t.dateOfBirth ? (_jsxs("div", { className: "text-muted-foreground", children: ["DOB ", t.dateOfBirth] })) : null, docLine ? _jsx("div", { className: "text-muted-foreground", children: docLine }) : null] }, key));
|
|
124
|
+
}) }));
|
|
125
|
+
}
|
|
126
|
+
function AccommodationDetails({ draft, }) {
|
|
127
|
+
const rooms = draft.accommodation?.rooms ?? [];
|
|
128
|
+
if (rooms.length === 0) {
|
|
129
|
+
return _jsx("p", { className: "text-muted-foreground text-xs", children: "Not selected." });
|
|
130
|
+
}
|
|
131
|
+
return (_jsx("ul", { className: "space-y-1 text-xs", children: rooms.map((r) => (_jsxs("li", { className: "flex justify-between gap-2", children: [_jsx("span", { className: "truncate", children: r.optionUnitId }), _jsxs("span", { className: "text-muted-foreground", children: ["\u00D7 ", r.quantity, r.ratePlanId ? ` · ${r.ratePlanId}` : ""] })] }, `${r.optionUnitId}-${r.ratePlanId ?? ""}-${r.quantity}`))) }));
|
|
132
|
+
}
|
|
133
|
+
function AddonsDetails({ draft, }) {
|
|
134
|
+
const addons = draft.addons ?? [];
|
|
135
|
+
if (addons.length === 0) {
|
|
136
|
+
return _jsx("p", { className: "text-muted-foreground text-xs", children: "No add-ons selected." });
|
|
137
|
+
}
|
|
138
|
+
return (_jsx("ul", { className: "space-y-1 text-xs", children: addons.map((a) => (_jsxs("li", { className: "flex justify-between gap-2", children: [_jsx("span", { className: "truncate", children: a.extraId }), _jsxs("span", { className: "text-muted-foreground", children: ["\u00D7 ", a.quantity] })] }, `${a.extraId}-${a.quantity}`))) }));
|
|
139
|
+
}
|
|
140
|
+
function PaymentDetails({ draft, }) {
|
|
141
|
+
const intent = draft.payment.intent;
|
|
142
|
+
const label = intent === "card"
|
|
143
|
+
? "Pay by card"
|
|
144
|
+
: intent === "ticket_on_credit"
|
|
145
|
+
? "Ticket on credit"
|
|
146
|
+
: "Hold (no charge yet)";
|
|
147
|
+
return (_jsxs("dl", { className: "space-y-1 text-xs", children: [_jsx(Row, { label: "Method", value: label }), draft.payment.schedule ? (_jsx(Row, { label: "Schedule", value: String(draft.payment.schedule) })) : null] }));
|
|
148
|
+
}
|
|
149
|
+
function Row({ label, value }) {
|
|
150
|
+
return (_jsxs("div", { className: "flex justify-between gap-2", children: [_jsx("dt", { className: "text-muted-foreground", children: label }), _jsx("dd", { className: "truncate text-right", children: value })] }));
|
|
151
|
+
}
|
|
152
|
+
const STEP_LABELS = {
|
|
153
|
+
configure: "Configure",
|
|
154
|
+
billing: "Billing & contact",
|
|
155
|
+
travelers: "Travelers",
|
|
156
|
+
accommodation: "Accommodation",
|
|
157
|
+
addons: "Add-ons",
|
|
158
|
+
payment: "Payment",
|
|
159
|
+
review: "Review",
|
|
160
|
+
};
|
|
161
|
+
function paxTotal(draft) {
|
|
162
|
+
const pax = draft.configure?.pax ?? {};
|
|
163
|
+
return (pax.adult ?? 0) + (pax.child ?? 0) + (pax.infant ?? 0);
|
|
164
|
+
}
|
|
165
|
+
function formatMoney(cents, currency) {
|
|
166
|
+
try {
|
|
167
|
+
return new Intl.NumberFormat(undefined, { style: "currency", currency }).format(cents / 100);
|
|
168
|
+
}
|
|
169
|
+
catch {
|
|
170
|
+
return `${(cents / 100).toFixed(2)} ${currency}`;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
import type { JourneyHeaderState, JourneyStep } from "../types.js";
|
|
2
|
+
interface StepHeaderProps extends JourneyHeaderState {
|
|
3
|
+
onJumpTo: (step: JourneyStep) => void;
|
|
4
|
+
}
|
|
5
|
+
export declare function StepHeader({ current, visited, steps, onJumpTo, }: StepHeaderProps): React.ReactElement;
|
|
6
|
+
export {};
|
|
7
|
+
//# sourceMappingURL=step-header.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"step-header.d.ts","sourceRoot":"","sources":["../../../src/journey/components/step-header.tsx"],"names":[],"mappings":"AAIA,OAAO,KAAK,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,aAAa,CAAA;AAElE,UAAU,eAAgB,SAAQ,kBAAkB;IAClD,QAAQ,EAAE,CAAC,IAAI,EAAE,WAAW,KAAK,IAAI,CAAA;CACtC;AAED,wBAAgB,UAAU,CAAC,EACzB,OAAO,EACP,OAAO,EACP,KAAK,EACL,QAAQ,GACT,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAuBtC"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { Button } from "@voyantjs/ui/components/button";
|
|
4
|
+
export function StepHeader({ current, visited, steps, onJumpTo, }) {
|
|
5
|
+
return (_jsx("ol", { className: "flex flex-wrap gap-2", children: steps.map((step, idx) => {
|
|
6
|
+
const isCurrent = step === current;
|
|
7
|
+
const isVisited = visited.includes(step);
|
|
8
|
+
return (_jsx("li", { children: _jsxs(Button, { type: "button", variant: isCurrent ? "default" : isVisited ? "outline" : "ghost", size: "sm", disabled: !isVisited && !isCurrent, onClick: () => onJumpTo(step), children: [_jsxs("span", { className: "mr-1 font-mono text-xs", children: [idx + 1, "."] }), labelForStep(step)] }) }, step));
|
|
9
|
+
}) }));
|
|
10
|
+
}
|
|
11
|
+
function labelForStep(step) {
|
|
12
|
+
switch (step) {
|
|
13
|
+
case "configure":
|
|
14
|
+
return "Configure";
|
|
15
|
+
case "billing":
|
|
16
|
+
return "Billing";
|
|
17
|
+
case "travelers":
|
|
18
|
+
return "Travelers";
|
|
19
|
+
case "accommodation":
|
|
20
|
+
return "Accommodation";
|
|
21
|
+
case "addons":
|
|
22
|
+
return "Add-ons";
|
|
23
|
+
case "payment":
|
|
24
|
+
return "Payment";
|
|
25
|
+
case "review":
|
|
26
|
+
return "Review";
|
|
27
|
+
}
|
|
28
|
+
}
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voyantjs/bookings-ui/journey` — the unified booking journey shell.
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/architecture/booking-journey-architecture.md`.
|
|
5
|
+
*
|
|
6
|
+
* Single shell, slot-injected. Operator and storefront consume the
|
|
7
|
+
* same `<BookingJourney />` and inject surface-specific behavior
|
|
8
|
+
* (CRM picker, payment provider widget, B2B vs B2C defaults, post-
|
|
9
|
+
* commit handoff) via render-prop slots.
|
|
10
|
+
*/
|
|
11
|
+
export { BookingJourney } from "./components/booking-journey.js";
|
|
12
|
+
export { type ContractAcceptance, ContractPreviewDialog, type ContractPreviewDialogProps, } from "./components/contract-preview-dialog.js";
|
|
13
|
+
export { AccommodationStep, AddonsStep, BillingStep, ConfigureStep, PaymentStep, ReviewStep, TravelersStep, } from "./components/journey-steps.js";
|
|
14
|
+
export { PriceSidePanel } from "./components/side-panel.js";
|
|
15
|
+
export { StepHeader } from "./components/step-header.js";
|
|
16
|
+
export { type Draft, emptyDraft, patchBilling, patchConfigure, patchPaxCount, setAccommodation, setAddons, setPayment, setTravelers, totalPax, } from "./lib/draft-state.js";
|
|
17
|
+
export { type BookingEntitySummary, type BookingJourneyCheckoutContext, type BookingJourneyProps, type ContractAcceptanceEvent, JOURNEY_STEP_ORDER, type JourneyHeaderState, type JourneyStep, type JourneySurface, type LeadContactPickerProps, type PaymentProviderCapabilities, type PaymentProviderStepRenderProps, type SidePanelState, type TravelerContactPickerProps, } from "./types.js";
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/journey/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;GASG;AAEH,OAAO,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAA;AAChE,OAAO,EACL,KAAK,kBAAkB,EACvB,qBAAqB,EACrB,KAAK,0BAA0B,GAChC,MAAM,yCAAyC,CAAA;AAChD,OAAO,EACL,iBAAiB,EACjB,UAAU,EACV,WAAW,EACX,aAAa,EACb,WAAW,EACX,UAAU,EACV,aAAa,GACd,MAAM,+BAA+B,CAAA;AACtC,OAAO,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC3D,OAAO,EAAE,UAAU,EAAE,MAAM,6BAA6B,CAAA;AACxD,OAAO,EACL,KAAK,KAAK,EACV,UAAU,EACV,YAAY,EACZ,cAAc,EACd,aAAa,EACb,gBAAgB,EAChB,SAAS,EACT,UAAU,EACV,YAAY,EACZ,QAAQ,GACT,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EACL,KAAK,oBAAoB,EACzB,KAAK,6BAA6B,EAClC,KAAK,mBAAmB,EACxB,KAAK,uBAAuB,EAC5B,kBAAkB,EAClB,KAAK,kBAAkB,EACvB,KAAK,WAAW,EAChB,KAAK,cAAc,EACnB,KAAK,sBAAsB,EAC3B,KAAK,2BAA2B,EAChC,KAAK,8BAA8B,EACnC,KAAK,cAAc,EACnB,KAAK,0BAA0B,GAChC,MAAM,YAAY,CAAA"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* `@voyantjs/bookings-ui/journey` — the unified booking journey shell.
|
|
3
|
+
*
|
|
4
|
+
* Per `docs/architecture/booking-journey-architecture.md`.
|
|
5
|
+
*
|
|
6
|
+
* Single shell, slot-injected. Operator and storefront consume the
|
|
7
|
+
* same `<BookingJourney />` and inject surface-specific behavior
|
|
8
|
+
* (CRM picker, payment provider widget, B2B vs B2C defaults, post-
|
|
9
|
+
* commit handoff) via render-prop slots.
|
|
10
|
+
*/
|
|
11
|
+
export { BookingJourney } from "./components/booking-journey.js";
|
|
12
|
+
export { ContractPreviewDialog, } from "./components/contract-preview-dialog.js";
|
|
13
|
+
export { AccommodationStep, AddonsStep, BillingStep, ConfigureStep, PaymentStep, ReviewStep, TravelersStep, } from "./components/journey-steps.js";
|
|
14
|
+
export { PriceSidePanel } from "./components/side-panel.js";
|
|
15
|
+
export { StepHeader } from "./components/step-header.js";
|
|
16
|
+
export { emptyDraft, patchBilling, patchConfigure, patchPaxCount, setAccommodation, setAddons, setPayment, setTravelers, totalPax, } from "./lib/draft-state.js";
|
|
17
|
+
export { JOURNEY_STEP_ORDER, } from "./types.js";
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local draft-state reducers + initialization helpers.
|
|
3
|
+
*
|
|
4
|
+
* The journey holds the draft as a single useState at the root and
|
|
5
|
+
* passes immutable views down to step components, which call the
|
|
6
|
+
* supplied setter to apply patches. Per
|
|
7
|
+
* booking-journey-architecture §4.
|
|
8
|
+
*/
|
|
9
|
+
import type { BookingDraftV1 } from "@voyantjs/catalog/booking-engine";
|
|
10
|
+
export type Draft = BookingDraftV1;
|
|
11
|
+
export interface DraftEntityIdentity {
|
|
12
|
+
module: string;
|
|
13
|
+
id: string;
|
|
14
|
+
/**
|
|
15
|
+
* Source kind. Empty string on the storefront before the engine's
|
|
16
|
+
* server-side resolver fills it in; operator-side wiring sets
|
|
17
|
+
* the real kind upfront.
|
|
18
|
+
*/
|
|
19
|
+
sourceKind: string;
|
|
20
|
+
sourceConnectionId?: string;
|
|
21
|
+
sourceRef?: string;
|
|
22
|
+
}
|
|
23
|
+
export declare function emptyDraft(entity: DraftEntityIdentity, defaults?: {
|
|
24
|
+
buyerType?: "B2C" | "B2B";
|
|
25
|
+
}): Draft;
|
|
26
|
+
export declare function patchConfigure(draft: Draft, patch: Partial<Draft["configure"]>): Draft;
|
|
27
|
+
export declare function patchBilling(draft: Draft, patch: Partial<Draft["billing"]>): Draft;
|
|
28
|
+
export declare function patchPaxCount(draft: Draft, band: string, count: number): Draft;
|
|
29
|
+
export declare function setTravelers(draft: Draft, travelers: Draft["travelers"]): Draft;
|
|
30
|
+
export declare function setAccommodation(draft: Draft, accommodation: Draft["accommodation"]): Draft;
|
|
31
|
+
export declare function setAddons(draft: Draft, addons: Draft["addons"]): Draft;
|
|
32
|
+
export declare function setPayment(draft: Draft, payment: Draft["payment"]): Draft;
|
|
33
|
+
export declare function totalPax(draft: Draft): number;
|
|
34
|
+
//# sourceMappingURL=draft-state.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"draft-state.d.ts","sourceRoot":"","sources":["../../../src/journey/lib/draft-state.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,kCAAkC,CAAA;AAEtE,MAAM,MAAM,KAAK,GAAG,cAAc,CAAA;AAElC,MAAM,WAAW,mBAAmB;IAClC,MAAM,EAAE,MAAM,CAAA;IACd,EAAE,EAAE,MAAM,CAAA;IACV;;;;OAIG;IACH,UAAU,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;CACnB;AAED,wBAAgB,UAAU,CACxB,MAAM,EAAE,mBAAmB,EAC3B,QAAQ,GAAE;IAAE,SAAS,CAAC,EAAE,KAAK,GAAG,KAAK,CAAA;CAAO,GAC3C,KAAK,CAaP;AAED,wBAAgB,cAAc,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC,GAAG,KAAK,CAEtF;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,KAAK,EAAE,OAAO,CAAC,KAAK,CAAC,SAAS,CAAC,CAAC,GAAG,KAAK,CAElF;AAED,wBAAgB,aAAa,CAAC,KAAK,EAAE,KAAK,EAAE,IAAI,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,KAAK,CAK9E;AAED,wBAAgB,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,SAAS,EAAE,KAAK,CAAC,WAAW,CAAC,GAAG,KAAK,CAE/E;AAED,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,KAAK,EAAE,aAAa,EAAE,KAAK,CAAC,eAAe,CAAC,GAAG,KAAK,CAE3F;AAED,wBAAgB,SAAS,CAAC,KAAK,EAAE,KAAK,EAAE,MAAM,EAAE,KAAK,CAAC,QAAQ,CAAC,GAAG,KAAK,CAEtE;AAED,wBAAgB,UAAU,CAAC,KAAK,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,CAAC,SAAS,CAAC,GAAG,KAAK,CAEzE;AAED,wBAAgB,QAAQ,CAAC,KAAK,EAAE,KAAK,GAAG,MAAM,CAM7C"}
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Local draft-state reducers + initialization helpers.
|
|
3
|
+
*
|
|
4
|
+
* The journey holds the draft as a single useState at the root and
|
|
5
|
+
* passes immutable views down to step components, which call the
|
|
6
|
+
* supplied setter to apply patches. Per
|
|
7
|
+
* booking-journey-architecture §4.
|
|
8
|
+
*/
|
|
9
|
+
export function emptyDraft(entity, defaults = {}) {
|
|
10
|
+
return {
|
|
11
|
+
entity,
|
|
12
|
+
configure: { pax: {} },
|
|
13
|
+
billing: {
|
|
14
|
+
buyerType: defaults.buyerType ?? "B2C",
|
|
15
|
+
contact: { firstName: "", lastName: "", email: "" },
|
|
16
|
+
address: {},
|
|
17
|
+
},
|
|
18
|
+
travelers: [],
|
|
19
|
+
addons: [],
|
|
20
|
+
payment: { intent: "hold" },
|
|
21
|
+
};
|
|
22
|
+
}
|
|
23
|
+
export function patchConfigure(draft, patch) {
|
|
24
|
+
return { ...draft, configure: { ...draft.configure, ...patch } };
|
|
25
|
+
}
|
|
26
|
+
export function patchBilling(draft, patch) {
|
|
27
|
+
return { ...draft, billing: { ...draft.billing, ...patch } };
|
|
28
|
+
}
|
|
29
|
+
export function patchPaxCount(draft, band, count) {
|
|
30
|
+
const safeCount = Number.isFinite(count) && count >= 0 ? Math.floor(count) : 0;
|
|
31
|
+
return patchConfigure(draft, {
|
|
32
|
+
pax: { ...draft.configure.pax, [band]: safeCount },
|
|
33
|
+
});
|
|
34
|
+
}
|
|
35
|
+
export function setTravelers(draft, travelers) {
|
|
36
|
+
return { ...draft, travelers };
|
|
37
|
+
}
|
|
38
|
+
export function setAccommodation(draft, accommodation) {
|
|
39
|
+
return { ...draft, accommodation };
|
|
40
|
+
}
|
|
41
|
+
export function setAddons(draft, addons) {
|
|
42
|
+
return { ...draft, addons };
|
|
43
|
+
}
|
|
44
|
+
export function setPayment(draft, payment) {
|
|
45
|
+
return { ...draft, payment };
|
|
46
|
+
}
|
|
47
|
+
export function totalPax(draft) {
|
|
48
|
+
let total = 0;
|
|
49
|
+
for (const v of Object.values(draft.configure.pax ?? {})) {
|
|
50
|
+
if (typeof v === "number")
|
|
51
|
+
total += v;
|
|
52
|
+
}
|
|
53
|
+
return total;
|
|
54
|
+
}
|
|
@@ -0,0 +1,248 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell-side types for `<BookingJourney />`. These complement the
|
|
3
|
+
* engine contracts in `@voyantjs/catalog/booking-engine/contracts`
|
|
4
|
+
* with React-specific slots and event shapes that don't belong on
|
|
5
|
+
* the wire.
|
|
6
|
+
*
|
|
7
|
+
* Per booking-journey-architecture §8.1.
|
|
8
|
+
*/
|
|
9
|
+
import type { BookingDraftShape, BookingDraftV1, BookResponseV1, PricingBreakdownV1 } from "@voyantjs/catalog/booking-engine";
|
|
10
|
+
import type { ReactNode } from "react";
|
|
11
|
+
export type JourneyStep = "configure" | "billing" | "travelers" | "accommodation" | "addons" | "payment" | "review";
|
|
12
|
+
export declare const JOURNEY_STEP_ORDER: ReadonlyArray<JourneyStep>;
|
|
13
|
+
export interface JourneySurface {
|
|
14
|
+
/** Operator-side or storefront — drives default slot behavior. */
|
|
15
|
+
kind: "admin" | "public";
|
|
16
|
+
}
|
|
17
|
+
export interface LeadContactPickerProps {
|
|
18
|
+
/** Apply a picked contact to the draft's billing fields. Email is
|
|
19
|
+
* optional because CRM-backed people may not have one stored —
|
|
20
|
+
* the billing form will surface it as empty for the operator to
|
|
21
|
+
* fill in. */
|
|
22
|
+
apply: (contact: {
|
|
23
|
+
firstName: string;
|
|
24
|
+
lastName: string;
|
|
25
|
+
email?: string;
|
|
26
|
+
phone?: string;
|
|
27
|
+
personId?: string;
|
|
28
|
+
}) => void;
|
|
29
|
+
}
|
|
30
|
+
export interface TravelerContactPickerProps {
|
|
31
|
+
rowIndex: number;
|
|
32
|
+
/** Apply a picked contact to the traveler at `rowIndex`. */
|
|
33
|
+
apply: (contact: {
|
|
34
|
+
firstName: string;
|
|
35
|
+
lastName: string;
|
|
36
|
+
email?: string;
|
|
37
|
+
phone?: string;
|
|
38
|
+
personId?: string;
|
|
39
|
+
}) => void;
|
|
40
|
+
}
|
|
41
|
+
/**
|
|
42
|
+
* Capabilities supplied by the template — checkout-ui's PaymentStep
|
|
43
|
+
* consumes these to render the right provider widget. Each flag is
|
|
44
|
+
* an independent on/off switch the operator configures per
|
|
45
|
+
* deployment / supplier:
|
|
46
|
+
*
|
|
47
|
+
* - `acceptsCard` — Stripe / Netopia / generic card flow. The
|
|
48
|
+
* `renderPaymentProviderStep` slot supplies the actual widget.
|
|
49
|
+
* - `acceptsBankTransfer` — operator emails the buyer bank details;
|
|
50
|
+
* booking is created with status "awaiting_payment". Inventory
|
|
51
|
+
* hold is still placed so capacity is reserved.
|
|
52
|
+
* - `acceptsHold` — staff/agent-only soft-hold path; useful when an
|
|
53
|
+
* operator is brokering for an end customer.
|
|
54
|
+
* - `acceptsTicketOnCredit` — agency credit account.
|
|
55
|
+
* - `acceptsInquiry` — lead-only flow: NO inventory hold, NO charge.
|
|
56
|
+
* The "booking" is recorded as an inquiry for the operator to
|
|
57
|
+
* manually follow up on. Right for tour operators where a quote
|
|
58
|
+
* conversation precedes booking.
|
|
59
|
+
*/
|
|
60
|
+
export interface PaymentProviderCapabilities {
|
|
61
|
+
acceptsCard: boolean;
|
|
62
|
+
acceptsHold: boolean;
|
|
63
|
+
acceptsBankTransfer?: boolean;
|
|
64
|
+
acceptsTicketOnCredit: boolean;
|
|
65
|
+
acceptsInquiry?: boolean;
|
|
66
|
+
/** Free-form provider-specific config (e.g. Netopia merchant id,
|
|
67
|
+
* Stripe publishable key, bank-transfer instructions). */
|
|
68
|
+
config?: Record<string, unknown>;
|
|
69
|
+
}
|
|
70
|
+
export interface PaymentProviderStepRenderProps {
|
|
71
|
+
intent: BookingDraftV1["payment"]["intent"];
|
|
72
|
+
schedule: BookingDraftV1["payment"]["schedule"];
|
|
73
|
+
capabilities: PaymentProviderCapabilities;
|
|
74
|
+
}
|
|
75
|
+
export interface BookingJourneyProps {
|
|
76
|
+
/** What to book. */
|
|
77
|
+
entityModule: string;
|
|
78
|
+
entityId: string;
|
|
79
|
+
/**
|
|
80
|
+
* Source pointer fields. Optional on the public surface — the
|
|
81
|
+
* engine resolves provenance server-side from
|
|
82
|
+
* `(entityModule, entityId)` via the catalog plane's
|
|
83
|
+
* sourced-entry lookup. Operator surfaces should still pass
|
|
84
|
+
* `sourceKind` explicitly when known.
|
|
85
|
+
*/
|
|
86
|
+
sourceKind?: string;
|
|
87
|
+
sourceConnectionId?: string;
|
|
88
|
+
sourceRef?: string;
|
|
89
|
+
/** Surface — drives audience defaults and slot wiring. */
|
|
90
|
+
surface?: "admin" | "public";
|
|
91
|
+
/** Stable draft id — caller persists in URL or session storage so
|
|
92
|
+
* the journey survives page refresh. */
|
|
93
|
+
draftId: string;
|
|
94
|
+
/** Default buyer type — operator: B2B, storefront: B2C. */
|
|
95
|
+
defaultBuyerType?: "B2C" | "B2B";
|
|
96
|
+
/** Initial fallback shape — rendered before the first quote
|
|
97
|
+
* resolves. The engine returns a more specific shape on the
|
|
98
|
+
* first quote response. */
|
|
99
|
+
fallbackShape?: BookingDraftShape;
|
|
100
|
+
/**
|
|
101
|
+
* Pre-locked configure state. When set, the journey skips the
|
|
102
|
+
* Configure step entirely — Configure already happened on the
|
|
103
|
+
* product detail page. Mirrors the BookingDraft's `configure`
|
|
104
|
+
* shape (loosely typed so storefront callers can pass a
|
|
105
|
+
* vertical-specific subset without fighting the contract).
|
|
106
|
+
*/
|
|
107
|
+
initialConfigure?: Record<string, unknown>;
|
|
108
|
+
/** Pre-locked accommodation slice (room/rate picks made on the
|
|
109
|
+
* detail page). Loosely typed for the same reason. */
|
|
110
|
+
initialAccommodation?: Record<string, unknown>;
|
|
111
|
+
/**
|
|
112
|
+
* When true, the wizard hides Configure regardless of descriptor
|
|
113
|
+
* flags. Use for storefront flows where the product detail page
|
|
114
|
+
* already collected those choices.
|
|
115
|
+
*/
|
|
116
|
+
hideConfigure?: boolean;
|
|
117
|
+
/** Per-payment-provider capabilities — passed through to the
|
|
118
|
+
* Payment step's provider widget. */
|
|
119
|
+
paymentCapabilities?: PaymentProviderCapabilities;
|
|
120
|
+
/** Operator: pulls from CRM. Storefront: bare inline form. */
|
|
121
|
+
renderLeadContactPicker?: (props: LeadContactPickerProps) => ReactNode;
|
|
122
|
+
renderTravelerContactPicker?: (props: TravelerContactPickerProps) => ReactNode;
|
|
123
|
+
/** Hook for the actual payment-provider widget — checkout-ui's
|
|
124
|
+
* PaymentStep is the canonical implementation. When omitted, the
|
|
125
|
+
* shell renders a "Hold only — no card collected" stub. */
|
|
126
|
+
renderPaymentProviderStep?: (props: PaymentProviderStepRenderProps) => ReactNode;
|
|
127
|
+
/** Optional pre/post-step extension slots — useful when a
|
|
128
|
+
* template wants to inject a custom block (e.g. coupon code
|
|
129
|
+
* banner, marketing opt-in). */
|
|
130
|
+
renderConfigureExtras?: () => ReactNode;
|
|
131
|
+
renderBillingExtras?: () => ReactNode;
|
|
132
|
+
renderReviewExtras?: () => ReactNode;
|
|
133
|
+
/** Fired on successful commit — typically a navigation. */
|
|
134
|
+
onCommitted?: (result: BookResponseV1) => void;
|
|
135
|
+
/** Fired when the user explicitly abandons the journey via the
|
|
136
|
+
* shell's cancel button. */
|
|
137
|
+
onCancelled?: () => void;
|
|
138
|
+
/** Optional class names. */
|
|
139
|
+
className?: string;
|
|
140
|
+
sidePanelClassName?: string;
|
|
141
|
+
/**
|
|
142
|
+
* Optional summary of the entity being booked — surfaces in the
|
|
143
|
+
* side panel so the customer keeps context while filling out the
|
|
144
|
+
* journey. Shape is loose because each vertical carries different
|
|
145
|
+
* fields (cruises have ports, hospitality has check-in/out, etc.).
|
|
146
|
+
*/
|
|
147
|
+
entitySummary?: BookingEntitySummary;
|
|
148
|
+
/**
|
|
149
|
+
* Contract preview integration. When `templateSlug` is set, the
|
|
150
|
+
* Review step's "Continue to checkout" button opens the contract
|
|
151
|
+
* preview dialog with the rendered template instead of committing
|
|
152
|
+
* directly. The journey then surfaces the acceptance back to the
|
|
153
|
+
* caller via `onContractAccepted` so the storefront can call its
|
|
154
|
+
* own `/checkout/start` endpoint.
|
|
155
|
+
*
|
|
156
|
+
* - `previewUrl` — absolute URL of the public render endpoint.
|
|
157
|
+
* - `resolveVariables` — maps the draft to the template's variable
|
|
158
|
+
* schema. The storefront supplies a default mapper that covers
|
|
159
|
+
* passenger / billing / room / dates.
|
|
160
|
+
* - `marketingLabel` — when set, an additional opt-in checkbox is
|
|
161
|
+
* rendered in the dialog. Marketing consent is optional and is
|
|
162
|
+
* passed through `onContractAccepted` so the caller can decide
|
|
163
|
+
* what to do with it.
|
|
164
|
+
*/
|
|
165
|
+
contract?: {
|
|
166
|
+
templateSlug: string;
|
|
167
|
+
previewUrl: string;
|
|
168
|
+
acceptLanguage?: string;
|
|
169
|
+
resolveVariables: (input: {
|
|
170
|
+
draft: BookingDraftV1;
|
|
171
|
+
pricing: PricingBreakdownV1 | null;
|
|
172
|
+
}) => Record<string, unknown>;
|
|
173
|
+
marketingLabel?: ReactNode;
|
|
174
|
+
termsLabel?: ReactNode;
|
|
175
|
+
};
|
|
176
|
+
/**
|
|
177
|
+
* Fired when the user accepts the contract in the preview dialog.
|
|
178
|
+
* The caller persists the acceptance + dispatches the checkout
|
|
179
|
+
* workflow. Receives the rendered HTML so it can be stored
|
|
180
|
+
* verbatim for the audit trail.
|
|
181
|
+
*/
|
|
182
|
+
/**
|
|
183
|
+
* Called when the user clicks Confirm on the Review step. Receives
|
|
184
|
+
* the rendered contract acceptance when the contract dialog was
|
|
185
|
+
* shown; receives `null` when the journey skipped the dialog
|
|
186
|
+
* (no template configured — the storefront still wants to drive
|
|
187
|
+
* the post-confirm /checkout/start flow).
|
|
188
|
+
*/
|
|
189
|
+
onContractAccepted?: (acceptance: ContractAcceptanceEvent | null, context: BookingJourneyCheckoutContext) => void | Promise<void>;
|
|
190
|
+
}
|
|
191
|
+
export interface BookingJourneyCheckoutContext {
|
|
192
|
+
draft: BookingDraftV1;
|
|
193
|
+
pricing: PricingBreakdownV1 | null;
|
|
194
|
+
quoteId?: string;
|
|
195
|
+
}
|
|
196
|
+
export interface ContractAcceptanceEvent {
|
|
197
|
+
templateId: string;
|
|
198
|
+
templateSlug: string;
|
|
199
|
+
templateName: string;
|
|
200
|
+
acceptedTerms: true;
|
|
201
|
+
acceptedMarketing: boolean;
|
|
202
|
+
acceptedAt: string;
|
|
203
|
+
renderedHtml: string;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Caller-provided context for the side-panel summary. Keep it
|
|
207
|
+
* vertical-agnostic — the panel renders whatever subset is present.
|
|
208
|
+
*/
|
|
209
|
+
export interface BookingEntitySummary {
|
|
210
|
+
/** Headline name — e.g. product / cruise / hotel name. */
|
|
211
|
+
name: string;
|
|
212
|
+
/** Optional second line — e.g. "Iceland · 1 day", "7 nights · Mediterranean". */
|
|
213
|
+
subtitle?: string;
|
|
214
|
+
/** Optional hero image — small thumbnail at the top of the panel. */
|
|
215
|
+
heroImageUrl?: string;
|
|
216
|
+
/** Vertical badge — drives the "What you're booking" header copy. */
|
|
217
|
+
vertical?: "products" | "cruises" | "hospitality" | string;
|
|
218
|
+
/** Optional ISO date or formatted date — e.g. "Tue, May 5, 2026". */
|
|
219
|
+
whenLabel?: string;
|
|
220
|
+
/** Optional location label — e.g. "Reykjavík", "Mediterranean", "Bucharest". */
|
|
221
|
+
locationLabel?: string;
|
|
222
|
+
/** ISO start date (YYYY-MM-DD or full ISO) — used by the contract
|
|
223
|
+
* preview when the draft only carries a slot id. */
|
|
224
|
+
startDate?: string;
|
|
225
|
+
/** ISO end date — paired with `startDate` for ranges and contract
|
|
226
|
+
* variables like `departure.end_date`. */
|
|
227
|
+
endDate?: string;
|
|
228
|
+
/** Free-form destination / route string — surfaced as
|
|
229
|
+
* `product.destination` in contract variables. */
|
|
230
|
+
destination?: string;
|
|
231
|
+
}
|
|
232
|
+
export interface JourneyHeaderState {
|
|
233
|
+
current: JourneyStep;
|
|
234
|
+
visited: ReadonlyArray<JourneyStep>;
|
|
235
|
+
steps: ReadonlyArray<JourneyStep>;
|
|
236
|
+
shape: BookingDraftShape;
|
|
237
|
+
}
|
|
238
|
+
export interface SidePanelState {
|
|
239
|
+
pricing: PricingBreakdownV1 | null;
|
|
240
|
+
isQuoting: boolean;
|
|
241
|
+
invalidReason?: string;
|
|
242
|
+
entitySummary?: BookingEntitySummary;
|
|
243
|
+
currentStep?: JourneyStep;
|
|
244
|
+
steps?: ReadonlyArray<JourneyStep>;
|
|
245
|
+
shape?: BookingDraftShape;
|
|
246
|
+
draft?: BookingDraftV1;
|
|
247
|
+
}
|
|
248
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/journey/types.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,iBAAiB,EACjB,cAAc,EACd,cAAc,EACd,kBAAkB,EACnB,MAAM,kCAAkC,CAAA;AACzC,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AAEtC,MAAM,MAAM,WAAW,GACnB,WAAW,GACX,SAAS,GACT,WAAW,GACX,eAAe,GACf,QAAQ,GACR,SAAS,GACT,QAAQ,CAAA;AAEZ,eAAO,MAAM,kBAAkB,EAAE,aAAa,CAAC,WAAW,CAQzD,CAAA;AAED,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,IAAI,EAAE,OAAO,GAAG,QAAQ,CAAA;CACzB;AAED,MAAM,WAAW,sBAAsB;IACrC;;;mBAGe;IACf,KAAK,EAAE,CAAC,OAAO,EAAE;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,KAAK,IAAI,CAAA;CACX;AAED,MAAM,WAAW,0BAA0B;IACzC,QAAQ,EAAE,MAAM,CAAA;IAChB,4DAA4D;IAC5D,KAAK,EAAE,CAAC,OAAO,EAAE;QACf,SAAS,EAAE,MAAM,CAAA;QACjB,QAAQ,EAAE,MAAM,CAAA;QAChB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;KAClB,KAAK,IAAI,CAAA;CACX;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,WAAW,2BAA2B;IAC1C,WAAW,EAAE,OAAO,CAAA;IACpB,WAAW,EAAE,OAAO,CAAA;IACpB,mBAAmB,CAAC,EAAE,OAAO,CAAA;IAC7B,qBAAqB,EAAE,OAAO,CAAA;IAC9B,cAAc,CAAC,EAAE,OAAO,CAAA;IACxB;+DAC2D;IAC3D,MAAM,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CACjC;AAED,MAAM,WAAW,8BAA8B;IAC7C,MAAM,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC,QAAQ,CAAC,CAAA;IAC3C,QAAQ,EAAE,cAAc,CAAC,SAAS,CAAC,CAAC,UAAU,CAAC,CAAA;IAC/C,YAAY,EAAE,2BAA2B,CAAA;CAC1C;AAED,MAAM,WAAW,mBAAmB;IAClC,oBAAoB;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,MAAM,CAAA;IACnB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAC3B,SAAS,CAAC,EAAE,MAAM,CAAA;IAElB,0DAA0D;IAC1D,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;IAE5B;6CACyC;IACzC,OAAO,EAAE,MAAM,CAAA;IAEf,2DAA2D;IAC3D,gBAAgB,CAAC,EAAE,KAAK,GAAG,KAAK,CAAA;IAEhC;;gCAE4B;IAC5B,aAAa,CAAC,EAAE,iBAAiB,CAAA;IAEjC;;;;;;OAMG;IACH,gBAAgB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC1C;2DACuD;IACvD,oBAAoB,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IAC9C;;;;OAIG;IACH,aAAa,CAAC,EAAE,OAAO,CAAA;IAEvB;0CACsC;IACtC,mBAAmB,CAAC,EAAE,2BAA2B,CAAA;IAEjD,8DAA8D;IAC9D,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,SAAS,CAAA;IACtE,2BAA2B,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,SAAS,CAAA;IAE9E;;gEAE4D;IAC5D,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,SAAS,CAAA;IAEhF;;qCAEiC;IACjC,qBAAqB,CAAC,EAAE,MAAM,SAAS,CAAA;IACvC,mBAAmB,CAAC,EAAE,MAAM,SAAS,CAAA;IACrC,kBAAkB,CAAC,EAAE,MAAM,SAAS,CAAA;IAEpC,2DAA2D;IAC3D,WAAW,CAAC,EAAE,CAAC,MAAM,EAAE,cAAc,KAAK,IAAI,CAAA;IAE9C;iCAC6B;IAC7B,WAAW,CAAC,EAAE,MAAM,IAAI,CAAA;IAExB,4BAA4B;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAA;IAE3B;;;;;OAKG;IACH,aAAa,CAAC,EAAE,oBAAoB,CAAA;IAEpC;;;;;;;;;;;;;;;;OAgBG;IACH,QAAQ,CAAC,EAAE;QACT,YAAY,EAAE,MAAM,CAAA;QACpB,UAAU,EAAE,MAAM,CAAA;QAClB,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,gBAAgB,EAAE,CAAC,KAAK,EAAE;YACxB,KAAK,EAAE,cAAc,CAAA;YACrB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAA;SACnC,KAAK,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;QAC7B,cAAc,CAAC,EAAE,SAAS,CAAA;QAC1B,UAAU,CAAC,EAAE,SAAS,CAAA;KACvB,CAAA;IACD;;;;;OAKG;IACH;;;;;;OAMG;IACH,kBAAkB,CAAC,EAAE,CACnB,UAAU,EAAE,uBAAuB,GAAG,IAAI,EAC1C,OAAO,EAAE,6BAA6B,KACnC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,CAAA;CAC1B;AAED,MAAM,WAAW,6BAA6B;IAC5C,KAAK,EAAE,cAAc,CAAA;IACrB,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAClC,OAAO,CAAC,EAAE,MAAM,CAAA;CACjB;AAED,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;IACpB,YAAY,EAAE,MAAM,CAAA;IACpB,aAAa,EAAE,IAAI,CAAA;IACnB,iBAAiB,EAAE,OAAO,CAAA;IAC1B,UAAU,EAAE,MAAM,CAAA;IAClB,YAAY,EAAE,MAAM,CAAA;CACrB;AAED;;;GAGG;AACH,MAAM,WAAW,oBAAoB;IACnC,0DAA0D;IAC1D,IAAI,EAAE,MAAM,CAAA;IACZ,iFAAiF;IACjF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,qEAAqE;IACrE,YAAY,CAAC,EAAE,MAAM,CAAA;IACrB,qEAAqE;IACrE,QAAQ,CAAC,EAAE,UAAU,GAAG,SAAS,GAAG,aAAa,GAAG,MAAM,CAAA;IAC1D,qEAAqE;IACrE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gFAAgF;IAChF,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB;yDACqD;IACrD,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;+CAC2C;IAC3C,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB;uDACmD;IACnD,WAAW,CAAC,EAAE,MAAM,CAAA;CACrB;AAED,MAAM,WAAW,kBAAkB;IACjC,OAAO,EAAE,WAAW,CAAA;IACpB,OAAO,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;IACnC,KAAK,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;IACjC,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAED,MAAM,WAAW,cAAc;IAC7B,OAAO,EAAE,kBAAkB,GAAG,IAAI,CAAA;IAClC,SAAS,EAAE,OAAO,CAAA;IAClB,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,aAAa,CAAC,EAAE,oBAAoB,CAAA;IACpC,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,KAAK,CAAC,EAAE,aAAa,CAAC,WAAW,CAAC,CAAA;IAClC,KAAK,CAAC,EAAE,iBAAiB,CAAA;IACzB,KAAK,CAAC,EAAE,cAAc,CAAA;CACvB"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Shell-side types for `<BookingJourney />`. These complement the
|
|
3
|
+
* engine contracts in `@voyantjs/catalog/booking-engine/contracts`
|
|
4
|
+
* with React-specific slots and event shapes that don't belong on
|
|
5
|
+
* the wire.
|
|
6
|
+
*
|
|
7
|
+
* Per booking-journey-architecture §8.1.
|
|
8
|
+
*/
|
|
9
|
+
export const JOURNEY_STEP_ORDER = [
|
|
10
|
+
"configure",
|
|
11
|
+
"billing",
|
|
12
|
+
"travelers",
|
|
13
|
+
"accommodation",
|
|
14
|
+
"addons",
|
|
15
|
+
"payment",
|
|
16
|
+
"review",
|
|
17
|
+
];
|