@voyant-travel/bookings-react 0.151.4 → 0.152.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/README.md +6 -1
- package/dist/admin/booking-detail-host.d.ts +12 -12
- package/dist/admin/booking-detail-host.d.ts.map +1 -1
- package/dist/admin/booking-detail-host.js +12 -5
- package/dist/admin/index.d.ts +5 -26
- package/dist/admin/index.d.ts.map +1 -1
- package/dist/admin/index.js +11 -25
- package/dist/admin/pages/booking-detail-page.d.ts +3 -5
- package/dist/admin/pages/booking-detail-page.d.ts.map +1 -1
- package/dist/admin/pages/booking-detail-page.js +3 -5
- package/dist/admin/pages/bookings-index-page.d.ts +2 -12
- package/dist/admin/pages/bookings-index-page.d.ts.map +1 -1
- package/dist/admin/pages/bookings-index-page.js +4 -3
- package/dist/admin/slots.d.ts +7 -0
- package/dist/admin/slots.d.ts.map +1 -1
- package/dist/admin/slots.js +7 -0
- package/dist/hooks/use-public-booking-session-flow-mutation.d.ts +1 -1
- package/dist/hooks/use-public-booking-session.d.ts +1 -1
- package/dist/query-options.d.ts +4 -4
- package/dist/storefront/index.d.ts +4 -0
- package/dist/storefront/index.d.ts.map +1 -0
- package/dist/storefront/index.js +3 -0
- package/dist/storefront/resolve-contract-variables.d.ts +131 -0
- package/dist/storefront/resolve-contract-variables.d.ts.map +1 -0
- package/dist/storefront/resolve-contract-variables.js +492 -0
- package/dist/storefront/storefront-booking-errors.d.ts +13 -0
- package/dist/storefront/storefront-booking-errors.d.ts.map +1 -0
- package/dist/storefront/storefront-booking-errors.js +50 -0
- package/dist/storefront/storefront-booking-journey.d.ts +72 -0
- package/dist/storefront/storefront-booking-journey.d.ts.map +1 -0
- package/dist/storefront/storefront-booking-journey.js +380 -0
- package/dist/storefront/storefront-booking-page.d.ts +36 -0
- package/dist/storefront/storefront-booking-page.d.ts.map +1 -0
- package/dist/storefront/storefront-booking-page.js +213 -0
- package/package.json +59 -24
|
@@ -0,0 +1,213 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { useQuery } from "@tanstack/react-query";
|
|
3
|
+
import { useStorefrontUi } from "@voyant-travel/storefront-react/storefront";
|
|
4
|
+
import { useMemo } from "react";
|
|
5
|
+
import { z } from "zod";
|
|
6
|
+
import { StorefrontBookingJourney, } from "./storefront-booking-journey.js";
|
|
7
|
+
export const storefrontBookingSearchSchema = z.object({
|
|
8
|
+
departureSlotId: z.string().optional(),
|
|
9
|
+
cabinCategoryId: z.string().optional(),
|
|
10
|
+
cabinNumberId: z.string().optional(),
|
|
11
|
+
airArrangement: z.enum(["cruise_line", "independent", "none"]).optional(),
|
|
12
|
+
checkIn: z.string().optional(),
|
|
13
|
+
checkOut: z.string().optional(),
|
|
14
|
+
roomTypeId: z.string().optional(),
|
|
15
|
+
ratePlanId: z.string().optional(),
|
|
16
|
+
board: z.string().optional(),
|
|
17
|
+
adult: z.coerce.number().int().min(0).optional(),
|
|
18
|
+
child: z.coerce.number().int().min(0).optional(),
|
|
19
|
+
infant: z.coerce.number().int().min(0).optional(),
|
|
20
|
+
draftId: z.string().optional(),
|
|
21
|
+
});
|
|
22
|
+
/** Customer booking page shared by Node applications and dedicated storefronts. */
|
|
23
|
+
export function StorefrontBookingPage({ entityModule, entityId, search, messages, extensions, }) {
|
|
24
|
+
const { apiUrl, navigate, scope } = useStorefrontUi();
|
|
25
|
+
const draftId = useMemo(() => search.draftId ?? generateDraftId(), [search.draftId]);
|
|
26
|
+
const initialConfigure = buildInitialConfigure(search);
|
|
27
|
+
const initialAccommodation = buildInitialAccommodation(search);
|
|
28
|
+
const { summary, source } = useEntityContent(apiUrl, entityModule, entityId, search);
|
|
29
|
+
return (_jsx(StorefrontBookingJourney, { entityModule: entityModule, entityId: entityId, draftId: draftId, initialConfigure: initialConfigure, initialAccommodation: initialAccommodation, entitySummary: summary, entitySource: source, messages: messages, scope: scope, onNavigateToShop: () => navigate({ to: "/shop" }), onNavigateToConfirmation: (bookingId, kind) => navigate({
|
|
30
|
+
to: "/shop/confirmation/$bookingId",
|
|
31
|
+
params: { bookingId },
|
|
32
|
+
...(kind ? { search: { kind } } : {}),
|
|
33
|
+
}), ...(messages.marketingLabel !== undefined
|
|
34
|
+
? { contractMarketingLabel: messages.marketingLabel }
|
|
35
|
+
: {}), ...extensions }));
|
|
36
|
+
}
|
|
37
|
+
function buildInitialConfigure(search) {
|
|
38
|
+
const configure = {
|
|
39
|
+
pax: {
|
|
40
|
+
adult: search.adult ?? 1,
|
|
41
|
+
child: search.child ?? 0,
|
|
42
|
+
infant: search.infant ?? 0,
|
|
43
|
+
},
|
|
44
|
+
};
|
|
45
|
+
if (search.departureSlotId)
|
|
46
|
+
configure.departureSlotId = search.departureSlotId;
|
|
47
|
+
if (search.cabinCategoryId)
|
|
48
|
+
configure.cabinCategoryId = search.cabinCategoryId;
|
|
49
|
+
if (search.cabinNumberId)
|
|
50
|
+
configure.cabinNumberId = search.cabinNumberId;
|
|
51
|
+
if (search.airArrangement)
|
|
52
|
+
configure.airArrangement = search.airArrangement;
|
|
53
|
+
if (search.checkIn && search.checkOut) {
|
|
54
|
+
configure.dateRange = { checkIn: search.checkIn, checkOut: search.checkOut };
|
|
55
|
+
}
|
|
56
|
+
if (search.roomTypeId)
|
|
57
|
+
configure.roomTypeId = search.roomTypeId;
|
|
58
|
+
if (search.ratePlanId)
|
|
59
|
+
configure.ratePlanId = search.ratePlanId;
|
|
60
|
+
if (search.board)
|
|
61
|
+
configure.board = search.board;
|
|
62
|
+
return configure;
|
|
63
|
+
}
|
|
64
|
+
function buildInitialAccommodation(search) {
|
|
65
|
+
if (!search.roomTypeId)
|
|
66
|
+
return undefined;
|
|
67
|
+
return {
|
|
68
|
+
rooms: [
|
|
69
|
+
{
|
|
70
|
+
optionUnitId: search.roomTypeId,
|
|
71
|
+
quantity: 1,
|
|
72
|
+
...(search.ratePlanId ? { ratePlanId: search.ratePlanId } : {}),
|
|
73
|
+
},
|
|
74
|
+
],
|
|
75
|
+
travelerAssignments: {},
|
|
76
|
+
};
|
|
77
|
+
}
|
|
78
|
+
function useEntityContent(apiUrl, entityModule, entityId, search) {
|
|
79
|
+
const url = entityContentUrl(apiUrl, entityModule, entityId);
|
|
80
|
+
const { data } = useQuery({
|
|
81
|
+
queryKey: ["public-entity-summary", entityModule, entityId],
|
|
82
|
+
queryFn: async () => {
|
|
83
|
+
if (!url)
|
|
84
|
+
return null;
|
|
85
|
+
const response = await fetch(url, { credentials: "include" });
|
|
86
|
+
if (!response.ok)
|
|
87
|
+
return null;
|
|
88
|
+
const json = (await response.json());
|
|
89
|
+
return json.data ?? null;
|
|
90
|
+
},
|
|
91
|
+
enabled: Boolean(url),
|
|
92
|
+
staleTime: 60_000,
|
|
93
|
+
});
|
|
94
|
+
const content = data?.content ?? null;
|
|
95
|
+
const source = useMemo(() => resolveEntitySource(entityModule, data?.provenance, content), [entityModule, data?.provenance, content]);
|
|
96
|
+
const summary = useMemo(() => resolveEntitySummary(entityModule, content, search), [content, entityModule, search]);
|
|
97
|
+
return { summary, source };
|
|
98
|
+
}
|
|
99
|
+
function entityContentUrl(apiUrl, entityModule, entityId) {
|
|
100
|
+
const encodedId = encodeURIComponent(entityId);
|
|
101
|
+
if (entityModule === "cruises")
|
|
102
|
+
return `${apiUrl}/v1/public/cruises/${encodedId}/content`;
|
|
103
|
+
if (entityModule === "accommodations") {
|
|
104
|
+
return `${apiUrl}/v1/public/accommodations/${encodedId}/content`;
|
|
105
|
+
}
|
|
106
|
+
if (entityModule === "products")
|
|
107
|
+
return `${apiUrl}/v1/public/products/${encodedId}/content`;
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
function resolveEntitySummary(entityModule, content, search) {
|
|
111
|
+
if (!content)
|
|
112
|
+
return undefined;
|
|
113
|
+
if (entityModule === "products")
|
|
114
|
+
return resolveProductSummary(content, search);
|
|
115
|
+
if (entityModule === "cruises")
|
|
116
|
+
return resolveCruiseSummary(content, search);
|
|
117
|
+
if (entityModule === "accommodations") {
|
|
118
|
+
return resolveAccommodationSummary(content, search);
|
|
119
|
+
}
|
|
120
|
+
return undefined;
|
|
121
|
+
}
|
|
122
|
+
function resolveProductSummary(content, search) {
|
|
123
|
+
const subtitle = [
|
|
124
|
+
content.product.duration_days
|
|
125
|
+
? `${content.product.duration_days} day${content.product.duration_days === 1 ? "" : "s"}`
|
|
126
|
+
: null,
|
|
127
|
+
content.product.country ?? null,
|
|
128
|
+
].filter(Boolean);
|
|
129
|
+
const departure = content.departures?.find((item) => item.id === search.departureSlotId);
|
|
130
|
+
return {
|
|
131
|
+
name: content.product.name,
|
|
132
|
+
subtitle: subtitle.join(" · ") || undefined,
|
|
133
|
+
heroImageUrl: content.product.hero_image_url ?? content.media?.[0]?.url ?? undefined,
|
|
134
|
+
vertical: "products",
|
|
135
|
+
whenLabel: departure ? formatDate(departure.starts_at) : undefined,
|
|
136
|
+
locationLabel: content.product.departure_city ?? content.product.country ?? undefined,
|
|
137
|
+
startDate: departure?.starts_at ?? undefined,
|
|
138
|
+
endDate: departure?.ends_at ?? undefined,
|
|
139
|
+
destination: content.product.country ?? content.product.departure_city ?? undefined,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
function resolveCruiseSummary(content, search) {
|
|
143
|
+
const sailing = content.sailings.find((item) => item.id === search.departureSlotId);
|
|
144
|
+
const subtitle = [
|
|
145
|
+
content.cruise.duration_nights
|
|
146
|
+
? `${content.cruise.duration_nights} night${content.cruise.duration_nights === 1 ? "" : "s"}`
|
|
147
|
+
: null,
|
|
148
|
+
content.ship?.name ?? null,
|
|
149
|
+
].filter(Boolean);
|
|
150
|
+
const route = sailing
|
|
151
|
+
? sailing.embarkation_port && sailing.disembarkation_port
|
|
152
|
+
? `${sailing.embarkation_port} → ${sailing.disembarkation_port}`
|
|
153
|
+
: (sailing.embarkation_port ?? null)
|
|
154
|
+
: null;
|
|
155
|
+
return {
|
|
156
|
+
name: content.cruise.name,
|
|
157
|
+
subtitle: subtitle.join(" · ") || undefined,
|
|
158
|
+
heroImageUrl: content.cruise.hero_image_url ?? undefined,
|
|
159
|
+
vertical: "cruises",
|
|
160
|
+
whenLabel: sailing ? formatDate(sailing.start_date) : undefined,
|
|
161
|
+
locationLabel: route ?? undefined,
|
|
162
|
+
startDate: sailing?.start_date ?? undefined,
|
|
163
|
+
endDate: sailing?.end_date ?? undefined,
|
|
164
|
+
destination: route ?? undefined,
|
|
165
|
+
};
|
|
166
|
+
}
|
|
167
|
+
function resolveAccommodationSummary(content, search) {
|
|
168
|
+
const stars = content.hotel.star_rating ? "★".repeat(Math.floor(content.hotel.star_rating)) : null;
|
|
169
|
+
return {
|
|
170
|
+
name: content.hotel.name,
|
|
171
|
+
subtitle: stars ?? undefined,
|
|
172
|
+
heroImageUrl: content.hotel.hero_image_url ?? undefined,
|
|
173
|
+
vertical: "accommodations",
|
|
174
|
+
whenLabel: search.checkIn && search.checkOut
|
|
175
|
+
? `${formatDate(search.checkIn)} → ${formatDate(search.checkOut)}`
|
|
176
|
+
: undefined,
|
|
177
|
+
startDate: search.checkIn ?? undefined,
|
|
178
|
+
endDate: search.checkOut ?? undefined,
|
|
179
|
+
destination: content.hotel.city ?? content.hotel.country ?? undefined,
|
|
180
|
+
};
|
|
181
|
+
}
|
|
182
|
+
function resolveEntitySource(entityModule, provenance, content) {
|
|
183
|
+
if (!provenance)
|
|
184
|
+
return undefined;
|
|
185
|
+
const supplierName = entityModule === "products" && content
|
|
186
|
+
? (content.product.supplier ?? "")
|
|
187
|
+
: "";
|
|
188
|
+
return {
|
|
189
|
+
kind: provenance.source_kind,
|
|
190
|
+
connectionId: provenance.source_connection_id ?? "",
|
|
191
|
+
ref: provenance.source_ref ?? "",
|
|
192
|
+
supplier: { id: "", name: supplierName },
|
|
193
|
+
};
|
|
194
|
+
}
|
|
195
|
+
function formatDate(iso) {
|
|
196
|
+
try {
|
|
197
|
+
return new Date(iso).toLocaleDateString(undefined, {
|
|
198
|
+
weekday: "short",
|
|
199
|
+
day: "numeric",
|
|
200
|
+
month: "short",
|
|
201
|
+
year: "numeric",
|
|
202
|
+
});
|
|
203
|
+
}
|
|
204
|
+
catch {
|
|
205
|
+
return iso;
|
|
206
|
+
}
|
|
207
|
+
}
|
|
208
|
+
function generateDraftId() {
|
|
209
|
+
if (typeof globalThis.crypto !== "undefined" && globalThis.crypto.randomUUID) {
|
|
210
|
+
return `bdrf_${globalThis.crypto.randomUUID().replace(/-/g, "")}`;
|
|
211
|
+
}
|
|
212
|
+
return `bdrf_${Date.now().toString(36)}${Math.random().toString(36).slice(2, 10)}`;
|
|
213
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@voyant-travel/bookings-react",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.152.0",
|
|
4
4
|
"license": "Apache-2.0",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -116,6 +116,11 @@
|
|
|
116
116
|
"import": "./dist/journey/index.js",
|
|
117
117
|
"default": "./dist/journey/index.js"
|
|
118
118
|
},
|
|
119
|
+
"./storefront": {
|
|
120
|
+
"types": "./dist/storefront/index.d.ts",
|
|
121
|
+
"import": "./dist/storefront/index.js",
|
|
122
|
+
"default": "./dist/storefront/index.js"
|
|
123
|
+
},
|
|
119
124
|
"./extras": {
|
|
120
125
|
"types": "./dist/extras.d.ts",
|
|
121
126
|
"import": "./dist/extras.js",
|
|
@@ -145,26 +150,38 @@
|
|
|
145
150
|
"react-dom": "^19.0.0",
|
|
146
151
|
"react-hook-form": "^7.80.0",
|
|
147
152
|
"zod": "^4.0.0",
|
|
148
|
-
"@voyant-travel/
|
|
149
|
-
"@voyant-travel/
|
|
150
|
-
"@voyant-travel/
|
|
151
|
-
"@voyant-travel/
|
|
152
|
-
"@voyant-travel/
|
|
153
|
-
"@voyant-travel/
|
|
154
|
-
"@voyant-travel/
|
|
155
|
-
"@voyant-travel/
|
|
156
|
-
"@voyant-travel/
|
|
157
|
-
"@voyant-travel/
|
|
153
|
+
"@voyant-travel/accommodations": "^0.112.5",
|
|
154
|
+
"@voyant-travel/admin": "^0.121.0",
|
|
155
|
+
"@voyant-travel/bookings": "^0.152.0",
|
|
156
|
+
"@voyant-travel/catalog": "^0.150.0",
|
|
157
|
+
"@voyant-travel/catalog-react": "^0.150.0",
|
|
158
|
+
"@voyant-travel/distribution-react": "^0.142.0",
|
|
159
|
+
"@voyant-travel/relationships-react": "^0.152.0",
|
|
160
|
+
"@voyant-travel/finance": "^0.152.0",
|
|
161
|
+
"@voyant-travel/finance-react": "^0.152.0",
|
|
162
|
+
"@voyant-travel/identity-react": "^0.152.0",
|
|
163
|
+
"@voyant-travel/legal-react": "^0.152.0",
|
|
164
|
+
"@voyant-travel/commerce-react": "^0.34.0",
|
|
165
|
+
"@voyant-travel/cruises": "^0.151.0",
|
|
166
|
+
"@voyant-travel/inventory-react": "^0.34.0",
|
|
167
|
+
"@voyant-travel/inventory": "^0.8.6",
|
|
168
|
+
"@voyant-travel/storefront-react": "^0.154.0",
|
|
158
169
|
"@voyant-travel/ui": "^0.108.11",
|
|
159
|
-
"@voyant-travel/operations-react": "^0.
|
|
170
|
+
"@voyant-travel/operations-react": "^0.33.0"
|
|
160
171
|
},
|
|
161
172
|
"peerDependenciesMeta": {
|
|
162
173
|
"@tanstack/react-table": {
|
|
163
174
|
"optional": true
|
|
164
175
|
},
|
|
176
|
+
"@voyant-travel/accommodations": {
|
|
177
|
+
"optional": true
|
|
178
|
+
},
|
|
165
179
|
"@voyant-travel/admin": {
|
|
166
180
|
"optional": true
|
|
167
181
|
},
|
|
182
|
+
"@voyant-travel/catalog": {
|
|
183
|
+
"optional": true
|
|
184
|
+
},
|
|
168
185
|
"@voyant-travel/catalog-react": {
|
|
169
186
|
"optional": true
|
|
170
187
|
},
|
|
@@ -177,6 +194,9 @@
|
|
|
177
194
|
"@voyant-travel/finance-react": {
|
|
178
195
|
"optional": true
|
|
179
196
|
},
|
|
197
|
+
"@voyant-travel/finance": {
|
|
198
|
+
"optional": true
|
|
199
|
+
},
|
|
180
200
|
"@voyant-travel/identity-react": {
|
|
181
201
|
"optional": true
|
|
182
202
|
},
|
|
@@ -186,9 +206,18 @@
|
|
|
186
206
|
"@voyant-travel/commerce-react": {
|
|
187
207
|
"optional": true
|
|
188
208
|
},
|
|
209
|
+
"@voyant-travel/cruises": {
|
|
210
|
+
"optional": true
|
|
211
|
+
},
|
|
189
212
|
"@voyant-travel/inventory-react": {
|
|
190
213
|
"optional": true
|
|
191
214
|
},
|
|
215
|
+
"@voyant-travel/inventory": {
|
|
216
|
+
"optional": true
|
|
217
|
+
},
|
|
218
|
+
"@voyant-travel/storefront-react": {
|
|
219
|
+
"optional": true
|
|
220
|
+
},
|
|
192
221
|
"@voyant-travel/ui": {
|
|
193
222
|
"optional": true
|
|
194
223
|
},
|
|
@@ -207,7 +236,7 @@
|
|
|
207
236
|
"@voyant-travel/catalog-contracts": "^0.109.0",
|
|
208
237
|
"@voyant-travel/i18n": "^0.110.0",
|
|
209
238
|
"@voyant-travel/react": "^0.104.1",
|
|
210
|
-
"@voyant-travel/types": "^0.
|
|
239
|
+
"@voyant-travel/types": "^0.108.0"
|
|
211
240
|
},
|
|
212
241
|
"devDependencies": {
|
|
213
242
|
"@tanstack/react-query": "^5.101.2",
|
|
@@ -222,20 +251,26 @@
|
|
|
222
251
|
"typescript": "^6.0.3",
|
|
223
252
|
"vitest": "^4.1.9",
|
|
224
253
|
"zod": "^4.4.3",
|
|
225
|
-
"@voyant-travel/
|
|
226
|
-
"@voyant-travel/
|
|
227
|
-
"@voyant-travel/
|
|
228
|
-
"@voyant-travel/
|
|
229
|
-
"@voyant-travel/
|
|
230
|
-
"@voyant-travel/
|
|
231
|
-
"@voyant-travel/
|
|
232
|
-
"@voyant-travel/
|
|
233
|
-
"@voyant-travel/
|
|
234
|
-
"@voyant-travel/
|
|
254
|
+
"@voyant-travel/accommodations": "^0.112.5",
|
|
255
|
+
"@voyant-travel/admin": "^0.121.0",
|
|
256
|
+
"@voyant-travel/bookings": "^0.152.0",
|
|
257
|
+
"@voyant-travel/catalog": "^0.150.0",
|
|
258
|
+
"@voyant-travel/catalog-react": "^0.150.0",
|
|
259
|
+
"@voyant-travel/distribution-react": "^0.142.0",
|
|
260
|
+
"@voyant-travel/relationships-react": "^0.152.0",
|
|
261
|
+
"@voyant-travel/finance": "^0.152.0",
|
|
262
|
+
"@voyant-travel/finance-react": "^0.152.0",
|
|
263
|
+
"@voyant-travel/identity-react": "^0.152.0",
|
|
264
|
+
"@voyant-travel/legal-react": "^0.152.0",
|
|
265
|
+
"@voyant-travel/commerce-react": "^0.34.0",
|
|
266
|
+
"@voyant-travel/cruises": "^0.151.0",
|
|
267
|
+
"@voyant-travel/inventory-react": "^0.34.0",
|
|
268
|
+
"@voyant-travel/inventory": "^0.8.6",
|
|
235
269
|
"@voyant-travel/react": "^0.104.1",
|
|
270
|
+
"@voyant-travel/storefront-react": "^0.154.0",
|
|
236
271
|
"@voyant-travel/ui": "^0.108.11",
|
|
237
272
|
"@voyant-travel/voyant-typescript-config": "^0.1.0",
|
|
238
|
-
"@voyant-travel/operations-react": "^0.
|
|
273
|
+
"@voyant-travel/operations-react": "^0.33.0"
|
|
239
274
|
},
|
|
240
275
|
"files": [
|
|
241
276
|
"dist",
|