@voyant-travel/flights-react 0.119.2

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.
Files changed (187) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +74 -0
  3. package/dist/admin/index.d.ts +134 -0
  4. package/dist/admin/index.d.ts.map +1 -0
  5. package/dist/admin/index.js +122 -0
  6. package/dist/admin/pages/flight-book-page.d.ts +12 -0
  7. package/dist/admin/pages/flight-book-page.d.ts.map +1 -0
  8. package/dist/admin/pages/flight-book-page.js +40 -0
  9. package/dist/admin/pages/flights-index-page.d.ts +14 -0
  10. package/dist/admin/pages/flights-index-page.d.ts.map +1 -0
  11. package/dist/admin/pages/flights-index-page.js +28 -0
  12. package/dist/client.d.ts +16 -0
  13. package/dist/client.d.ts.map +1 -0
  14. package/dist/client.js +75 -0
  15. package/dist/components/airline-logo.d.ts +19 -0
  16. package/dist/components/airline-logo.d.ts.map +1 -0
  17. package/dist/components/airline-logo.js +18 -0
  18. package/dist/components/airport-combobox.d.ts +20 -0
  19. package/dist/components/airport-combobox.d.ts.map +1 -0
  20. package/dist/components/airport-combobox.js +31 -0
  21. package/dist/components/billing-pickers.d.ts +19 -0
  22. package/dist/components/billing-pickers.d.ts.map +1 -0
  23. package/dist/components/billing-pickers.js +148 -0
  24. package/dist/components/flight-baggage-step.d.ts +32 -0
  25. package/dist/components/flight-baggage-step.d.ts.map +1 -0
  26. package/dist/components/flight-baggage-step.js +119 -0
  27. package/dist/components/flight-billing-step.d.ts +69 -0
  28. package/dist/components/flight-billing-step.d.ts.map +1 -0
  29. package/dist/components/flight-billing-step.js +117 -0
  30. package/dist/components/flight-booking-journey.d.ts +31 -0
  31. package/dist/components/flight-booking-journey.d.ts.map +1 -0
  32. package/dist/components/flight-booking-journey.js +103 -0
  33. package/dist/components/flight-booking-ledger.d.ts +53 -0
  34. package/dist/components/flight-booking-ledger.d.ts.map +1 -0
  35. package/dist/components/flight-booking-ledger.js +104 -0
  36. package/dist/components/flight-booking-page.d.ts +25 -0
  37. package/dist/components/flight-booking-page.d.ts.map +1 -0
  38. package/dist/components/flight-booking-page.js +175 -0
  39. package/dist/components/flight-booking-shell-helpers.d.ts +29 -0
  40. package/dist/components/flight-booking-shell-helpers.d.ts.map +1 -0
  41. package/dist/components/flight-booking-shell-helpers.js +204 -0
  42. package/dist/components/flight-booking-shell-panels.d.ts +24 -0
  43. package/dist/components/flight-booking-shell-panels.d.ts.map +1 -0
  44. package/dist/components/flight-booking-shell-panels.js +39 -0
  45. package/dist/components/flight-booking-shell-types.d.ts +49 -0
  46. package/dist/components/flight-booking-shell-types.d.ts.map +1 -0
  47. package/dist/components/flight-booking-shell-types.js +18 -0
  48. package/dist/components/flight-booking-shell.d.ts +12 -0
  49. package/dist/components/flight-booking-shell.d.ts.map +1 -0
  50. package/dist/components/flight-booking-shell.js +210 -0
  51. package/dist/components/flight-contact-form.d.ts +16 -0
  52. package/dist/components/flight-contact-form.d.ts.map +1 -0
  53. package/dist/components/flight-contact-form.js +25 -0
  54. package/dist/components/flight-fare-upsell-step.d.ts +26 -0
  55. package/dist/components/flight-fare-upsell-step.d.ts.map +1 -0
  56. package/dist/components/flight-fare-upsell-step.js +169 -0
  57. package/dist/components/flight-filters-bar.d.ts +19 -0
  58. package/dist/components/flight-filters-bar.d.ts.map +1 -0
  59. package/dist/components/flight-filters-bar.js +98 -0
  60. package/dist/components/flight-itinerary.d.ts +28 -0
  61. package/dist/components/flight-itinerary.d.ts.map +1 -0
  62. package/dist/components/flight-itinerary.js +110 -0
  63. package/dist/components/flight-offer-detail.d.ts +21 -0
  64. package/dist/components/flight-offer-detail.d.ts.map +1 -0
  65. package/dist/components/flight-offer-detail.js +49 -0
  66. package/dist/components/flight-offer-row.d.ts +25 -0
  67. package/dist/components/flight-offer-row.d.ts.map +1 -0
  68. package/dist/components/flight-offer-row.js +78 -0
  69. package/dist/components/flight-order-confirmation.d.ts +13 -0
  70. package/dist/components/flight-order-confirmation.d.ts.map +1 -0
  71. package/dist/components/flight-order-confirmation.js +46 -0
  72. package/dist/components/flight-passenger-form.d.ts +49 -0
  73. package/dist/components/flight-passenger-form.d.ts.map +1 -0
  74. package/dist/components/flight-passenger-form.js +159 -0
  75. package/dist/components/flight-payment-selector.d.ts +13 -0
  76. package/dist/components/flight-payment-selector.d.ts.map +1 -0
  77. package/dist/components/flight-payment-selector.js +32 -0
  78. package/dist/components/flight-payment-step.d.ts +32 -0
  79. package/dist/components/flight-payment-step.d.ts.map +1 -0
  80. package/dist/components/flight-payment-step.js +81 -0
  81. package/dist/components/flight-search-form.d.ts +14 -0
  82. package/dist/components/flight-search-form.d.ts.map +1 -0
  83. package/dist/components/flight-search-form.js +58 -0
  84. package/dist/components/flight-seat-map.d.ts +32 -0
  85. package/dist/components/flight-seat-map.d.ts.map +1 -0
  86. package/dist/components/flight-seat-map.js +101 -0
  87. package/dist/components/flight-seats-step.d.ts +40 -0
  88. package/dist/components/flight-seats-step.d.ts.map +1 -0
  89. package/dist/components/flight-seats-step.js +214 -0
  90. package/dist/components/flight-services-step.d.ts +27 -0
  91. package/dist/components/flight-services-step.d.ts.map +1 -0
  92. package/dist/components/flight-services-step.js +123 -0
  93. package/dist/components/flights-page-panels.d.ts +27 -0
  94. package/dist/components/flights-page-panels.d.ts.map +1 -0
  95. package/dist/components/flights-page-panels.js +40 -0
  96. package/dist/components/flights-page-types.d.ts +39 -0
  97. package/dist/components/flights-page-types.d.ts.map +1 -0
  98. package/dist/components/flights-page-types.js +1 -0
  99. package/dist/components/flights-page-utils.d.ts +14 -0
  100. package/dist/components/flights-page-utils.d.ts.map +1 -0
  101. package/dist/components/flights-page-utils.js +79 -0
  102. package/dist/components/flights-page.d.ts +4 -0
  103. package/dist/components/flights-page.d.ts.map +1 -0
  104. package/dist/components/flights-page.js +209 -0
  105. package/dist/components/passenger-contact-picker.d.ts +16 -0
  106. package/dist/components/passenger-contact-picker.d.ts.map +1 -0
  107. package/dist/components/passenger-contact-picker.js +45 -0
  108. package/dist/components/pax-cabin-popover.d.ts +18 -0
  109. package/dist/components/pax-cabin-popover.d.ts.map +1 -0
  110. package/dist/components/pax-cabin-popover.js +35 -0
  111. package/dist/components/popular-routes.d.ts +42 -0
  112. package/dist/components/popular-routes.d.ts.map +1 -0
  113. package/dist/components/popular-routes.js +108 -0
  114. package/dist/hooks/index.d.ts +13 -0
  115. package/dist/hooks/index.d.ts.map +1 -0
  116. package/dist/hooks/index.js +12 -0
  117. package/dist/hooks/use-aircraft.d.ts +17 -0
  118. package/dist/hooks/use-aircraft.d.ts.map +1 -0
  119. package/dist/hooks/use-aircraft.js +18 -0
  120. package/dist/hooks/use-airlines.d.ts +18 -0
  121. package/dist/hooks/use-airlines.d.ts.map +1 -0
  122. package/dist/hooks/use-airlines.js +18 -0
  123. package/dist/hooks/use-airport-search.d.ts +28 -0
  124. package/dist/hooks/use-airport-search.d.ts.map +1 -0
  125. package/dist/hooks/use-airport-search.js +23 -0
  126. package/dist/hooks/use-airports.d.ts +21 -0
  127. package/dist/hooks/use-airports.d.ts.map +1 -0
  128. package/dist/hooks/use-airports.js +17 -0
  129. package/dist/hooks/use-flight-ancillaries.d.ts +63 -0
  130. package/dist/hooks/use-flight-ancillaries.d.ts.map +1 -0
  131. package/dist/hooks/use-flight-ancillaries.js +24 -0
  132. package/dist/hooks/use-flight-book.d.ts +139 -0
  133. package/dist/hooks/use-flight-book.d.ts.map +1 -0
  134. package/dist/hooks/use-flight-book.js +24 -0
  135. package/dist/hooks/use-flight-offer.d.ts +106 -0
  136. package/dist/hooks/use-flight-offer.d.ts.map +1 -0
  137. package/dist/hooks/use-flight-offer.js +20 -0
  138. package/dist/hooks/use-flight-order.d.ts +286 -0
  139. package/dist/hooks/use-flight-order.d.ts.map +1 -0
  140. package/dist/hooks/use-flight-order.js +38 -0
  141. package/dist/hooks/use-flight-orders.d.ts +147 -0
  142. package/dist/hooks/use-flight-orders.d.ts.map +1 -0
  143. package/dist/hooks/use-flight-orders.js +31 -0
  144. package/dist/hooks/use-flight-search.d.ts +110 -0
  145. package/dist/hooks/use-flight-search.d.ts.map +1 -0
  146. package/dist/hooks/use-flight-search.js +18 -0
  147. package/dist/hooks/use-flight-seat-map.d.ts +49 -0
  148. package/dist/hooks/use-flight-seat-map.d.ts.map +1 -0
  149. package/dist/hooks/use-flight-seat-map.js +23 -0
  150. package/dist/hooks/use-saved-payment-methods.d.ts +23 -0
  151. package/dist/hooks/use-saved-payment-methods.d.ts.map +1 -0
  152. package/dist/hooks/use-saved-payment-methods.js +20 -0
  153. package/dist/i18n/en.d.ts +465 -0
  154. package/dist/i18n/en.d.ts.map +1 -0
  155. package/dist/i18n/en.js +520 -0
  156. package/dist/i18n/index.d.ts +5 -0
  157. package/dist/i18n/index.d.ts.map +1 -0
  158. package/dist/i18n/index.js +3 -0
  159. package/dist/i18n/messages.d.ts +392 -0
  160. package/dist/i18n/messages.d.ts.map +1 -0
  161. package/dist/i18n/messages.js +1 -0
  162. package/dist/i18n/provider.d.ts +952 -0
  163. package/dist/i18n/provider.d.ts.map +1 -0
  164. package/dist/i18n/provider.js +44 -0
  165. package/dist/i18n/ro.d.ts +465 -0
  166. package/dist/i18n/ro.d.ts.map +1 -0
  167. package/dist/i18n/ro.js +520 -0
  168. package/dist/index.d.ts +7 -0
  169. package/dist/index.d.ts.map +1 -0
  170. package/dist/index.js +6 -0
  171. package/dist/provider.d.ts +2 -0
  172. package/dist/provider.d.ts.map +1 -0
  173. package/dist/provider.js +1 -0
  174. package/dist/query-keys.d.ts +42 -0
  175. package/dist/query-keys.d.ts.map +1 -0
  176. package/dist/query-keys.js +22 -0
  177. package/dist/query-options.d.ts +827 -0
  178. package/dist/query-options.d.ts.map +1 -0
  179. package/dist/query-options.js +58 -0
  180. package/dist/schemas.d.ts +1658 -0
  181. package/dist/schemas.d.ts.map +1 -0
  182. package/dist/schemas.js +295 -0
  183. package/dist/ui.d.ts +31 -0
  184. package/dist/ui.d.ts.map +1 -0
  185. package/dist/ui.js +28 -0
  186. package/package.json +148 -0
  187. package/src/styles.css +11 -0
@@ -0,0 +1,175 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { useQueries, useQueryClient } from "@tanstack/react-query";
4
+ import { Button } from "@voyant-travel/ui/components/button";
5
+ import { cn } from "@voyant-travel/ui/lib/utils";
6
+ import { ChevronLeft, Plane } from "lucide-react";
7
+ import { useEffect, useMemo, useState } from "react";
8
+ import { useFlightsUiMessagesOrDefault } from "../i18n/index.js";
9
+ import { flightsQueryKeys, getFlightSeatMapQueryOptions, useAirlines, useAirports, useFlightAncillaries, useFlightOfferPrice, useSavedPaymentMethods, useVoyantFlightsContext, } from "../index.js";
10
+ import { BillingOrgPicker, BillingPersonPicker } from "./billing-pickers.js";
11
+ import { FlightBookingShell, } from "./flight-booking-shell.js";
12
+ import { PassengerContactPicker } from "./passenger-contact-picker.js";
13
+ export function FlightBookingPage({ outboundOfferId, returnOfferId, passengers, onBackToSearch, onBook, onBooked, onEditOutbound, onEditReturn, onSaveBillingDefaults, paymentCapabilities, renderPassengerPicker, renderBillingPersonPicker, renderBillingOrgPicker, onAddPassengerContact, className, }) {
14
+ const messages = useFlightsUiMessagesOrDefault().flightBookingPage;
15
+ const qc = useQueryClient();
16
+ const airlinesQuery = useAirlines();
17
+ const airportsQuery = useAirports({ limit: 200 });
18
+ const carrierName = (code) => airlinesQuery.data?.data.find((airline) => airline.iataCode === code)?.name;
19
+ const airportName = (code) => {
20
+ const airport = airportsQuery.data?.data.find((item) => item.iataCode === code);
21
+ return airport ? `${airport.city} (${airport.iataCode})` : undefined;
22
+ };
23
+ const [outbound, setOutbound] = useState(() => readOfferFromCache(qc, outboundOfferId));
24
+ const [returnLeg, setReturnLeg] = useState(() => returnOfferId ? readOfferFromCache(qc, returnOfferId) : null);
25
+ const [livePriceError, setLivePriceError] = useState(null);
26
+ const [pricedReady, setPricedReady] = useState(false);
27
+ const [selectedPersonId, setSelectedPersonId] = useState(null);
28
+ const priceMutation = useFlightOfferPrice();
29
+ // biome-ignore lint/correctness/useExhaustiveDependencies: re-price once on mount per offer pair -- owner: flights-react; existing suppression is intentional pending typed cleanup.
30
+ useEffect(() => {
31
+ let cancelled = false;
32
+ const repriceLeg = async (offer, setter) => {
33
+ if (!offer)
34
+ return null;
35
+ try {
36
+ const result = await priceMutation.mutateAsync({ offerId: offer.offerId, offer });
37
+ if (cancelled)
38
+ return null;
39
+ if (!result.valid)
40
+ return result.invalidReason ?? messages.offerUnavailable;
41
+ setter(result.offer);
42
+ return null;
43
+ }
44
+ catch (err) {
45
+ return err instanceof Error ? err.message : String(err);
46
+ }
47
+ };
48
+ Promise.all([repriceLeg(outbound, setOutbound), repriceLeg(returnLeg, setReturnLeg)]).then(([err1, err2]) => {
49
+ if (cancelled)
50
+ return;
51
+ const err = err1 ?? err2;
52
+ if (err)
53
+ setLivePriceError(err);
54
+ else
55
+ setPricedReady(true);
56
+ });
57
+ return () => {
58
+ cancelled = true;
59
+ };
60
+ }, [outboundOfferId, returnOfferId]);
61
+ const outboundAncillaries = useFlightAncillaries(outbound ? { offerId: outbound.offerId, offer: outbound } : null, { enabled: pricedReady && outbound != null });
62
+ const returnAncillaries = useFlightAncillaries(returnLeg ? { offerId: returnLeg.offerId, offer: returnLeg } : null, { enabled: pricedReady && returnLeg != null });
63
+ const ancillaries = {
64
+ outboundCatalog: outboundAncillaries.data?.catalog ?? null,
65
+ returnCatalog: returnAncillaries.data?.catalog ?? null,
66
+ loading: outboundAncillaries.isLoading || (returnLeg != null && returnAncillaries.isLoading),
67
+ };
68
+ const seatMaps = useSeatMapFetcher({ outbound, returnLeg, enabled: pricedReady, messages });
69
+ const savedMethodsQuery = useSavedPaymentMethods(selectedPersonId, {
70
+ enabled: !!selectedPersonId,
71
+ });
72
+ const savedPaymentMethods = {
73
+ methods: (savedMethodsQuery.data?.data ?? []).map((method) => ({
74
+ id: method.id,
75
+ label: [brandHumanLabel(method.brand, messages), method.last4 ? `....${method.last4}` : null]
76
+ .filter(Boolean)
77
+ .join(" "),
78
+ provider: null,
79
+ instrumentType: method.brand === "bank_transfer" ? "bank_account" : "credit_card",
80
+ status: "active",
81
+ brand: method.brand,
82
+ last4: method.last4,
83
+ expiryMonth: method.expMonth ?? null,
84
+ expiryYear: method.expYear ?? null,
85
+ isDefault: method.isDefault,
86
+ })),
87
+ loading: savedMethodsQuery.isLoading,
88
+ };
89
+ const documentsRequired = useMemo(() => detectInternational(outbound) || detectInternational(returnLeg), [outbound, returnLeg]);
90
+ const selection = useMemo(() => {
91
+ if (!outbound)
92
+ return null;
93
+ if (returnOfferId && !returnLeg)
94
+ return null;
95
+ return returnLeg ? { outbound, return: returnLeg } : { outbound };
96
+ }, [outbound, returnLeg, returnOfferId]);
97
+ if (!selection) {
98
+ return (_jsx("div", { className: cn("mx-auto w-full max-w-2xl px-6 py-10", className), children: _jsxs("div", { className: "rounded-xl border border-dashed bg-card p-8 text-center", children: [_jsx(Plane, { className: "mx-auto mb-3 h-8 w-8 text-muted-foreground" }), _jsx("h2", { className: "font-medium text-base", children: messages.offerNotInSessionTitle }), _jsx("p", { className: "mx-auto mt-2 max-w-md text-muted-foreground text-sm", children: messages.offerNotInSessionDescription }), _jsxs(Button, { className: "mt-4", onClick: onBackToSearch, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), messages.backToFlightSearch] })] }) }));
99
+ }
100
+ if (livePriceError) {
101
+ return (_jsx("div", { className: cn("mx-auto w-full max-w-2xl px-6 py-10", className), children: _jsxs("div", { className: "rounded-xl border border-destructive/40 bg-destructive/5 p-6 text-center text-destructive text-sm", children: [_jsx("p", { className: "font-medium", children: livePriceError }), _jsxs(Button, { variant: "outline", className: "mt-4", onClick: onBackToSearch, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), messages.backToFlightSearch] })] }) }));
102
+ }
103
+ const defaultPassengerPicker = (_slot, onPicked) => (_jsx(PassengerContactPicker, { onPick: onPicked, onAddContact: onAddPassengerContact, onPersonSelected: setSelectedPersonId }));
104
+ return (_jsxs("div", { className: cn("mx-auto flex w-full max-w-screen-2xl flex-col gap-6 px-6 py-6 lg:px-8", className), children: [_jsxs("header", { className: "flex items-center justify-between gap-4", children: [_jsxs("div", { children: [_jsx("h1", { className: "font-semibold text-2xl", children: messages.title }), _jsx("p", { className: "text-muted-foreground text-sm", children: selection.return ? messages.descriptionTrip : messages.descriptionOffer })] }), _jsxs(Button, { variant: "ghost", onClick: onBackToSearch, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), messages.backToResults] })] }), _jsx(FlightBookingShell, { selection: selection, passengers: passengers, carrierName: carrierName, airportName: airportName, ancillaries: ancillaries, seatMaps: seatMaps, savedPaymentMethods: savedPaymentMethods, paymentCapabilities: paymentCapabilities, documentsRequired: documentsRequired, renderPassengerPicker: renderPassengerPicker ?? defaultPassengerPicker, renderBillingPersonPicker: (apply) => renderBillingPersonPicker ? (renderBillingPersonPicker(apply, { onPersonSelected: setSelectedPersonId })) : (_jsx(BillingPersonPicker, { apply: apply, onPersonSelected: setSelectedPersonId })), renderBillingOrgPicker: (apply) => renderBillingOrgPicker ? (renderBillingOrgPicker(apply)) : (_jsx(BillingOrgPicker, { apply: apply })), onSaveBillingDefaults: onSaveBillingDefaults, onCancel: onBackToSearch, onEditOutbound: onEditOutbound ?? onBackToSearch, onEditReturn: onEditReturn ?? onBackToSearch, onBook: onBook, onBooked: onBooked })] }));
105
+ }
106
+ function brandHumanLabel(brand, messages) {
107
+ switch (brand) {
108
+ case "visa":
109
+ return messages.paymentBrandLabels.visa;
110
+ case "mastercard":
111
+ return messages.paymentBrandLabels.mastercard;
112
+ case "amex":
113
+ return messages.paymentBrandLabels.amex;
114
+ case "revolut":
115
+ return messages.paymentBrandLabels.revolut;
116
+ case "bank_transfer":
117
+ return messages.paymentBrandLabels.bank_transfer;
118
+ default:
119
+ return brand;
120
+ }
121
+ }
122
+ function readOfferFromCache(qc, offerId) {
123
+ const cached = qc.getQueryData(flightsQueryKeys.offerDetail(offerId));
124
+ return cached?.offer ?? null;
125
+ }
126
+ function detectInternational(offer) {
127
+ if (!offer)
128
+ return false;
129
+ const first = offer.itineraries[0]?.segments[0];
130
+ const lastItinerary = offer.itineraries[offer.itineraries.length - 1];
131
+ const last = lastItinerary?.segments[lastItinerary.segments.length - 1];
132
+ if (!first || !last)
133
+ return false;
134
+ return first.departure.iataCode.slice(0, 1) !== last.arrival.iataCode.slice(0, 1);
135
+ }
136
+ function useSeatMapFetcher({ outbound, returnLeg, enabled, messages, }) {
137
+ const client = useVoyantFlightsContext();
138
+ const segmentInputs = useMemo(() => {
139
+ const list = [];
140
+ const addFrom = (offer) => {
141
+ if (!offer)
142
+ return;
143
+ for (const itinerary of offer.itineraries) {
144
+ for (const segment of itinerary.segments) {
145
+ list.push({ offerId: offer.offerId, segmentId: segment.segmentId, offer });
146
+ }
147
+ }
148
+ };
149
+ addFrom(outbound);
150
+ addFrom(returnLeg);
151
+ return list;
152
+ }, [outbound, returnLeg]);
153
+ const results = useQueries({
154
+ queries: segmentInputs.map((input) => ({
155
+ ...getFlightSeatMapQueryOptions(client, input),
156
+ enabled,
157
+ staleTime: 5 * 60_000,
158
+ })),
159
+ });
160
+ const slotsBySegment = useMemo(() => {
161
+ const map = new Map();
162
+ segmentInputs.forEach((input, index) => {
163
+ const result = results[index];
164
+ map.set(input.segmentId, {
165
+ seatMap: result?.data?.seatMap ?? null,
166
+ loading: result?.isLoading,
167
+ error: result?.error instanceof Error ? result.error.message : null,
168
+ });
169
+ });
170
+ return map;
171
+ }, [segmentInputs, results]);
172
+ return useMemo(() => ({
173
+ getSeatMap: ({ segmentId }) => slotsBySegment.get(segmentId) ?? { seatMap: null, error: messages.segmentNotFound },
174
+ }), [slotsBySegment, messages.segmentNotFound]);
175
+ }
@@ -0,0 +1,29 @@
1
+ import type { AncillaryCatalog, AncillarySelection, FlightOffer } from "@voyant-travel/flights/contract/types";
2
+ import type { useFlightsUiMessagesOrDefault } from "../i18n/index.js";
3
+ import type { FlightItinerarySelection, LedgerLineItem } from "./flight-booking-ledger.js";
4
+ import { type FlightBookingAncillaries, type FlightBookingSeatMaps, type StepDef } from "./flight-booking-shell-types.js";
5
+ type FlightsUiMessages = ReturnType<typeof useFlightsUiMessagesOrDefault>;
6
+ export declare function getVisibleFlightBookingSteps({ selection, ancillaries, seatMaps, }: {
7
+ selection: FlightItinerarySelection;
8
+ ancillaries?: FlightBookingAncillaries;
9
+ seatMaps?: FlightBookingSeatMaps;
10
+ }): ReadonlyArray<StepDef>;
11
+ export declare function buildLedgerExtras({ baggage, extras, assistance, seats, fareBundles, outboundOffer, returnOffer, outboundCatalog, returnCatalog, seatMaps, messages, }: {
12
+ baggage: NonNullable<AncillarySelection["baggage"]>;
13
+ extras: NonNullable<AncillarySelection["extras"]>;
14
+ assistance: NonNullable<AncillarySelection["assistance"]>;
15
+ seats: NonNullable<AncillarySelection["seats"]>;
16
+ fareBundles: NonNullable<AncillarySelection["fareBundle"]>;
17
+ outboundOffer: FlightOffer;
18
+ returnOffer?: FlightOffer;
19
+ outboundCatalog: AncillaryCatalog | null;
20
+ returnCatalog: AncillaryCatalog | null;
21
+ seatMaps?: FlightBookingSeatMaps;
22
+ messages: FlightsUiMessages;
23
+ }): {
24
+ outboundExtras: LedgerLineItem[];
25
+ returnExtras: LedgerLineItem[];
26
+ };
27
+ export declare function mergeOffers(selection: FlightItinerarySelection): FlightOffer;
28
+ export {};
29
+ //# sourceMappingURL=flight-booking-shell-helpers.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flight-booking-shell-helpers.d.ts","sourceRoot":"","sources":["../../src/components/flight-booking-shell-helpers.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,kBAAkB,EAClB,WAAW,EAEZ,MAAM,uCAAuC,CAAA;AAE9C,OAAO,KAAK,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAA;AACrE,OAAO,KAAK,EAAE,wBAAwB,EAAE,cAAc,EAAE,MAAM,4BAA4B,CAAA;AAC1F,OAAO,EAGL,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,OAAO,EACb,MAAM,iCAAiC,CAAA;AAExC,KAAK,iBAAiB,GAAG,UAAU,CAAC,OAAO,6BAA6B,CAAC,CAAA;AAEzE,wBAAgB,4BAA4B,CAAC,EAC3C,SAAS,EACT,WAAW,EACX,QAAQ,GACT,EAAE;IACD,SAAS,EAAE,wBAAwB,CAAA;IACnC,WAAW,CAAC,EAAE,wBAAwB,CAAA;IACtC,QAAQ,CAAC,EAAE,qBAAqB,CAAA;CACjC,GAAG,aAAa,CAAC,OAAO,CAAC,CAyBzB;AAED,wBAAgB,iBAAiB,CAAC,EAChC,OAAO,EACP,MAAM,EACN,UAAU,EACV,KAAK,EACL,WAAW,EACX,aAAa,EACb,WAAW,EACX,eAAe,EACf,aAAa,EACb,QAAQ,EACR,QAAQ,GACT,EAAE;IACD,OAAO,EAAE,WAAW,CAAC,kBAAkB,CAAC,SAAS,CAAC,CAAC,CAAA;IACnD,MAAM,EAAE,WAAW,CAAC,kBAAkB,CAAC,QAAQ,CAAC,CAAC,CAAA;IACjD,UAAU,EAAE,WAAW,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAA;IACzD,KAAK,EAAE,WAAW,CAAC,kBAAkB,CAAC,OAAO,CAAC,CAAC,CAAA;IAC/C,WAAW,EAAE,WAAW,CAAC,kBAAkB,CAAC,YAAY,CAAC,CAAC,CAAA;IAC1D,aAAa,EAAE,WAAW,CAAA;IAC1B,WAAW,CAAC,EAAE,WAAW,CAAA;IACzB,eAAe,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACxC,aAAa,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACtC,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,QAAQ,EAAE,iBAAiB,CAAA;CAC5B,GAAG;IAAE,cAAc,EAAE,cAAc,EAAE,CAAC;IAAC,YAAY,EAAE,cAAc,EAAE,CAAA;CAAE,CAkJvE;AAED,wBAAgB,WAAW,CAAC,SAAS,EAAE,wBAAwB,GAAG,WAAW,CAqB5E"}
@@ -0,0 +1,204 @@
1
+ import { formatMessage } from "@voyant-travel/i18n";
2
+ import { ALL_STEPS, ALWAYS_VISIBLE, } from "./flight-booking-shell-types.js";
3
+ export function getVisibleFlightBookingSteps({ selection, ancillaries, seatMaps, }) {
4
+ const hasFareBundles = (selection.outbound.fareBundles?.length ?? 0) > 0 ||
5
+ (selection.return?.fareBundles?.length ?? 0) > 0;
6
+ const cat = ancillaries?.outboundCatalog;
7
+ const catReturn = ancillaries?.returnCatalog;
8
+ const ancillariesLoading = !!ancillaries?.loading;
9
+ const hasBags = ancillariesLoading || (cat?.baggage.length ?? 0) > 0 || (catReturn?.baggage.length ?? 0) > 0;
10
+ const hasServices = ancillariesLoading ||
11
+ (cat?.assistance.length ?? 0) > 0 ||
12
+ (cat?.extras.length ?? 0) > 0 ||
13
+ (catReturn?.assistance.length ?? 0) > 0 ||
14
+ (catReturn?.extras.length ?? 0) > 0;
15
+ const hasSeats = !!seatMaps;
16
+ return ALL_STEPS.filter((s) => {
17
+ if (ALWAYS_VISIBLE.has(s.id))
18
+ return true;
19
+ if (s.id === "fares")
20
+ return hasFareBundles;
21
+ if (s.id === "bags")
22
+ return hasBags;
23
+ if (s.id === "seats")
24
+ return hasSeats;
25
+ if (s.id === "services")
26
+ return hasServices;
27
+ return true;
28
+ });
29
+ }
30
+ export function buildLedgerExtras({ baggage, extras, assistance, seats, fareBundles, outboundOffer, returnOffer, outboundCatalog, returnCatalog, seatMaps, messages, }) {
31
+ const lines = (sliceIndex, catalog, offer) => {
32
+ const out = [];
33
+ if (offer?.fareBundles) {
34
+ const legPicks = fareBundles.filter((p) => p.sliceIndex === sliceIndex);
35
+ const agg = new Map();
36
+ for (const p of legPicks) {
37
+ const bundle = offer.fareBundles.find((b) => b.id === p.bundleId);
38
+ if (!bundle)
39
+ continue;
40
+ const prev = agg.get(bundle.id);
41
+ if (prev) {
42
+ prev.count += 1;
43
+ prev.price += Number(bundle.priceDelta.amount);
44
+ }
45
+ else {
46
+ agg.set(bundle.id, {
47
+ count: 1,
48
+ label: bundle.label,
49
+ price: Number(bundle.priceDelta.amount),
50
+ currency: bundle.priceDelta.currency,
51
+ });
52
+ }
53
+ }
54
+ for (const [, v] of agg) {
55
+ const labelSuffix = v.count > 1 ? ` (${v.count} ${messages.common.pax})` : "";
56
+ out.push({
57
+ label: formatMessage(messages.flightBookingShell.lineItems.fare, {
58
+ label: v.label,
59
+ suffix: labelSuffix,
60
+ }),
61
+ amount: v.price > 0 ? { amount: v.price.toFixed(2), currency: v.currency } : undefined,
62
+ meta: v.price === 0 ? messages.common.included : undefined,
63
+ });
64
+ }
65
+ }
66
+ if (catalog) {
67
+ const bagPicks = baggage.filter((b) => b.sliceIndex === sliceIndex);
68
+ const bagAgg = new Map();
69
+ for (const p of bagPicks) {
70
+ const opt = catalog.baggage.find((o) => o.id === p.optionId);
71
+ if (!opt)
72
+ continue;
73
+ const prev = bagAgg.get(opt.id);
74
+ const qty = p.quantity ?? 1;
75
+ if (prev) {
76
+ prev.count += qty;
77
+ prev.price += Number(opt.price.amount) * qty;
78
+ }
79
+ else {
80
+ bagAgg.set(opt.id, {
81
+ count: qty,
82
+ label: opt.label,
83
+ price: Number(opt.price.amount) * qty,
84
+ currency: opt.price.currency,
85
+ });
86
+ }
87
+ }
88
+ for (const [, v] of bagAgg) {
89
+ out.push({
90
+ label: v.count > 1 ? `${v.count}× ${v.label}` : v.label,
91
+ amount: v.price > 0 ? { amount: v.price.toFixed(2), currency: v.currency } : undefined,
92
+ meta: v.price === 0 ? messages.common.included : undefined,
93
+ });
94
+ }
95
+ const extraPicks = extras.filter((b) => b.sliceIndex === sliceIndex);
96
+ const extAgg = new Map();
97
+ for (const p of extraPicks) {
98
+ const opt = catalog.extras.find((o) => o.id === p.optionId);
99
+ if (!opt)
100
+ continue;
101
+ const qty = p.quantity ?? 1;
102
+ const prev = extAgg.get(opt.id);
103
+ if (prev) {
104
+ prev.count += qty;
105
+ prev.price += Number(opt.price.amount) * qty;
106
+ }
107
+ else {
108
+ extAgg.set(opt.id, {
109
+ count: qty,
110
+ label: opt.label,
111
+ price: Number(opt.price.amount) * qty,
112
+ currency: opt.price.currency,
113
+ });
114
+ }
115
+ }
116
+ for (const [, v] of extAgg) {
117
+ out.push({
118
+ label: v.count > 1 ? `${v.count}× ${v.label}` : v.label,
119
+ amount: { amount: v.price.toFixed(2), currency: v.currency },
120
+ });
121
+ }
122
+ }
123
+ if (offer && seatMaps) {
124
+ const segIds = new Set();
125
+ for (const itin of offer.itineraries) {
126
+ for (const seg of itin.segments)
127
+ segIds.add(seg.segmentId);
128
+ }
129
+ const seatPicks = seats.filter((p) => segIds.has(p.segmentId));
130
+ if (seatPicks.length > 0) {
131
+ let total = 0;
132
+ let currency = "EUR";
133
+ for (const pick of seatPicks) {
134
+ const slot = seatMaps.getSeatMap({ offerId: offer.offerId, segmentId: pick.segmentId });
135
+ const seat = slot.seatMap ? findSeatInMap(slot.seatMap, pick.seatNumber) : null;
136
+ if (seat?.price) {
137
+ total += Number(seat.price.amount);
138
+ currency = seat.price.currency;
139
+ }
140
+ }
141
+ out.push({
142
+ label: formatMessage(messages.flightBookingShell.lineItems.seatsPicked, {
143
+ count: seatPicks.length,
144
+ plural: seatPicks.length > 1 ? "s" : "",
145
+ }),
146
+ amount: total > 0 ? { amount: total.toFixed(2), currency } : undefined,
147
+ meta: total === 0 ? messages.common.free : undefined,
148
+ });
149
+ }
150
+ }
151
+ if (sliceIndex === 0 && assistance.length > 0) {
152
+ out.push({
153
+ label: formatMessage(messages.flightBookingShell.lineItems.specialAssistance, {
154
+ count: assistance.length,
155
+ }),
156
+ meta: messages.common.free,
157
+ });
158
+ }
159
+ return out;
160
+ };
161
+ return {
162
+ outboundExtras: lines(0, outboundCatalog, outboundOffer),
163
+ returnExtras: lines(1, returnCatalog ?? outboundCatalog, returnOffer),
164
+ };
165
+ }
166
+ export function mergeOffers(selection) {
167
+ const { outbound, return: ret } = selection;
168
+ if (!ret)
169
+ return outbound;
170
+ const currency = outbound.totalPrice.currency;
171
+ const amount = (Number(outbound.totalPrice.amount) + Number(ret.totalPrice.amount)).toFixed(2);
172
+ return {
173
+ offerId: `${outbound.offerId}+${ret.offerId}`,
174
+ source: outbound.source,
175
+ itineraries: [...outbound.itineraries, ...ret.itineraries],
176
+ fareBreakdowns: [...outbound.fareBreakdowns, ...ret.fareBreakdowns],
177
+ totalPrice: { amount, currency },
178
+ validatingCarrier: outbound.validatingCarrier,
179
+ expiresAt: pickEarliest(outbound.expiresAt, ret.expiresAt),
180
+ lastTicketingDate: pickEarliest(outbound.lastTicketingDate, ret.lastTicketingDate),
181
+ instantTicketing: (outbound.instantTicketing ?? false) && (ret.instantTicketing ?? false),
182
+ providerData: {
183
+ ...(outbound.providerData ?? {}),
184
+ ...(ret.providerData ?? {}),
185
+ __mergedFrom: { outbound: outbound.offerId, return: ret.offerId },
186
+ },
187
+ };
188
+ }
189
+ function findSeatInMap(map, seatNumber) {
190
+ for (const row of map.rows) {
191
+ for (const seat of row.seats) {
192
+ if (seat.seatNumber === seatNumber)
193
+ return seat;
194
+ }
195
+ }
196
+ return null;
197
+ }
198
+ function pickEarliest(a, b) {
199
+ if (!a)
200
+ return b;
201
+ if (!b)
202
+ return a;
203
+ return new Date(a).getTime() <= new Date(b).getTime() ? a : b;
204
+ }
@@ -0,0 +1,24 @@
1
+ import type { FlightPassenger, PaymentIntent } from "@voyant-travel/flights/contract/types";
2
+ import { useFlightsUiMessagesOrDefault } from "../i18n/index.js";
3
+ import type { BillingValue } from "./flight-billing-step.js";
4
+ import type { FlightItinerarySelection } from "./flight-booking-ledger.js";
5
+ import type { StepDef } from "./flight-booking-shell-types.js";
6
+ export declare function ReviewStep({ selection, carrierName, airportName, }: {
7
+ selection: FlightItinerarySelection;
8
+ carrierName?: (iataCode: string) => string | undefined;
9
+ airportName?: (iataCode: string) => string | undefined;
10
+ }): import("react/jsx-runtime").JSX.Element;
11
+ export declare function ConfirmStep({ selection, passengers, billing, payment, carrierName, airportName, }: {
12
+ selection: FlightItinerarySelection;
13
+ passengers: FlightPassenger[];
14
+ billing: BillingValue;
15
+ payment: PaymentIntent;
16
+ carrierName?: (iataCode: string) => string | undefined;
17
+ airportName?: (iataCode: string) => string | undefined;
18
+ }): import("react/jsx-runtime").JSX.Element;
19
+ export declare function Stepper({ steps, currentIdx, messages, }: {
20
+ steps: ReadonlyArray<StepDef>;
21
+ currentIdx: number;
22
+ messages: ReturnType<typeof useFlightsUiMessagesOrDefault>;
23
+ }): import("react/jsx-runtime").JSX.Element;
24
+ //# sourceMappingURL=flight-booking-shell-panels.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flight-booking-shell-panels.d.ts","sourceRoot":"","sources":["../../src/components/flight-booking-shell-panels.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,eAAe,EAAE,aAAa,EAAE,MAAM,uCAAuC,CAAA;AAK3F,OAAO,EAAE,6BAA6B,EAAE,MAAM,kBAAkB,CAAA;AAChE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AAC1E,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,iCAAiC,CAAA;AAG9D,wBAAgB,UAAU,CAAC,EACzB,SAAS,EACT,WAAW,EACX,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,wBAAwB,CAAA;IACnC,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACtD,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;CACvD,2CA0BA;AAED,wBAAgB,WAAW,CAAC,EAC1B,SAAS,EACT,UAAU,EACV,OAAO,EACP,OAAO,EACP,WAAW,EACX,WAAW,GACZ,EAAE;IACD,SAAS,EAAE,wBAAwB,CAAA;IACnC,UAAU,EAAE,eAAe,EAAE,CAAA;IAC7B,OAAO,EAAE,YAAY,CAAA;IACrB,OAAO,EAAE,aAAa,CAAA;IACtB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACtD,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;CACvD,2CAoDA;AAED,wBAAgB,OAAO,CAAC,EACtB,KAAK,EACL,UAAU,EACV,QAAQ,GACT,EAAE;IACD,KAAK,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;IAC7B,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,EAAE,UAAU,CAAC,OAAO,6BAA6B,CAAC,CAAA;CAC3D,2CAgCA"}
@@ -0,0 +1,39 @@
1
+ "use client";
2
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
3
+ import { formatMessage } from "@voyant-travel/i18n";
4
+ import { cn } from "@voyant-travel/ui/lib/utils";
5
+ import { Check } from "lucide-react";
6
+ import { useFlightsUiMessagesOrDefault } from "../i18n/index.js";
7
+ import { FlightItinerary } from "./flight-itinerary.js";
8
+ export function ReviewStep({ selection, carrierName, airportName, }) {
9
+ const messages = useFlightsUiMessagesOrDefault();
10
+ const isRoundTrip = !!selection.return;
11
+ return (_jsxs("div", { className: "flex flex-col gap-5 rounded-xl border bg-card p-5 shadow-sm", children: [_jsx("h2", { className: "font-semibold text-base", children: isRoundTrip
12
+ ? messages.flightBookingShell.reviewTrip
13
+ : messages.flightBookingShell.reviewFlight }), _jsx(FlightItinerary, { itinerary: selection.outbound.itineraries[0] ?? { segments: [] }, label: isRoundTrip ? messages.common.legLabels.outbound : undefined, carrierName: carrierName, airportName: airportName }), selection.return && (_jsx(FlightItinerary, { itinerary: selection.return.itineraries[0] ?? { segments: [] }, label: messages.common.legLabels.return, carrierName: carrierName, airportName: airportName }))] }));
14
+ }
15
+ export function ConfirmStep({ selection, passengers, billing, payment, carrierName, airportName, }) {
16
+ const messages = useFlightsUiMessagesOrDefault();
17
+ const docsCount = passengers.filter((p) => (p.documents?.length ?? 0) > 0).length;
18
+ const isRoundTrip = !!selection.return;
19
+ return (_jsxs("div", { className: "flex flex-col gap-5 rounded-xl border bg-card p-5 shadow-sm", children: [_jsx("h2", { className: "font-semibold text-base", children: messages.flightBookingShell.confirmTitle }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsx(FlightItinerary, { itinerary: selection.outbound.itineraries[0] ?? { segments: [] }, label: isRoundTrip ? messages.common.legLabels.outbound : undefined, compact: true, carrierName: carrierName, airportName: airportName }), selection.return && (_jsx(FlightItinerary, { itinerary: selection.return.itineraries[0] ?? { segments: [] }, label: messages.common.legLabels.return, compact: true, carrierName: carrierName, airportName: airportName }))] }), _jsx(Row, { label: messages.flightBookingShell.rows.passengers, children: passengers.length }), _jsx(Row, { label: messages.flightBookingShell.rows.documents, children: docsCount === passengers.length && passengers.length > 0
20
+ ? formatMessage(messages.flightBookingShell.documentsAllAdded, { count: docsCount })
21
+ : docsCount > 0
22
+ ? formatMessage(messages.flightBookingShell.documentsSomeAdded, {
23
+ count: docsCount,
24
+ total: passengers.length,
25
+ })
26
+ : messages.flightBookingShell.documentsAddAtCheckIn }), _jsx(Row, { label: messages.flightBookingShell.rows.contact, children: billing.email || messages.common.noValue }), _jsx(Row, { label: messages.flightBookingShell.rows.billedTo, children: billing.mode === "company"
27
+ ? `${billing.companyName ?? messages.common.noValue} · ${billing.vatNumber ?? ""}`
28
+ : `${billing.firstName} ${billing.lastName}`.trim() || messages.common.noValue }), _jsx(Row, { label: messages.flightBookingShell.rows.payment, children: _jsx("span", { className: "capitalize", children: payment.type.replace("_", " ") }) }), _jsx("p", { className: "text-muted-foreground text-xs", children: messages.flightBookingShell.confirmDescription })] }));
29
+ }
30
+ export function Stepper({ steps, currentIdx, messages, }) {
31
+ return (_jsx("ol", { className: "flex items-center gap-2 overflow-x-auto", children: steps.map((s, i) => {
32
+ const isActive = i === currentIdx;
33
+ const isComplete = i < currentIdx;
34
+ return (_jsxs("li", { className: "flex flex-1 items-center gap-2", children: [_jsx("div", { className: cn("flex h-7 w-7 shrink-0 items-center justify-center rounded-full border font-medium text-xs tabular-nums", isComplete && "border-primary bg-primary text-primary-foreground", isActive && !isComplete && "border-primary text-primary", !isActive && !isComplete && "border-border text-muted-foreground"), children: isComplete ? _jsx(Check, { className: "h-3.5 w-3.5" }) : i + 1 }), _jsx("span", { className: cn("truncate text-sm", isActive ? "font-medium text-foreground" : "text-muted-foreground"), children: messages.flightBookingShell.steps[s.id] }), i < steps.length - 1 && _jsx("div", { className: "h-px flex-1 bg-border" })] }, s.id));
35
+ }) }));
36
+ }
37
+ function Row({ label, children }) {
38
+ return (_jsxs("div", { className: "flex items-baseline justify-between border-b py-2 text-sm last:border-b-0", children: [_jsx("span", { className: "text-muted-foreground", children: label }), _jsx("span", { children: children })] }));
39
+ }
@@ -0,0 +1,49 @@
1
+ import type { AncillaryCatalog, FlightBookRequest, FlightOrder, PassengerCounts } from "@voyant-travel/flights/contract/types";
2
+ import type React from "react";
3
+ import type { BillingValue } from "./flight-billing-step.js";
4
+ import type { FlightItinerarySelection } from "./flight-booking-ledger.js";
5
+ import type { FlightPassengerFormProps } from "./flight-passenger-form.js";
6
+ import type { PaymentStepCapabilities, SavedPaymentMethod } from "./flight-payment-step.js";
7
+ import type { FlightSeatMapSlot } from "./flight-seats-step.js";
8
+ export type StepId = "review" | "fares" | "passengers" | "bags" | "seats" | "services" | "billing" | "payment" | "confirm";
9
+ export interface StepDef {
10
+ id: StepId;
11
+ }
12
+ export declare const ALL_STEPS: ReadonlyArray<StepDef>;
13
+ export declare const ALWAYS_VISIBLE: ReadonlySet<StepId>;
14
+ export interface FlightBookingAncillaries {
15
+ outboundCatalog: AncillaryCatalog | null;
16
+ returnCatalog?: AncillaryCatalog | null;
17
+ loading?: boolean;
18
+ }
19
+ export interface FlightBookingSeatMaps {
20
+ getSeatMap: (input: {
21
+ offerId: string;
22
+ segmentId: string;
23
+ }) => FlightSeatMapSlot;
24
+ }
25
+ export interface FlightBookingSavedPaymentMethods {
26
+ methods: SavedPaymentMethod[];
27
+ loading?: boolean;
28
+ }
29
+ export interface FlightBookingShellProps {
30
+ selection: FlightItinerarySelection;
31
+ passengers: PassengerCounts;
32
+ onBook: (request: FlightBookRequest) => Promise<FlightOrder> | FlightOrder;
33
+ onBooked?: (order: FlightOrder) => void;
34
+ onCancel?: () => void;
35
+ onEditOutbound?: () => void;
36
+ onEditReturn?: () => void;
37
+ carrierName?: (iataCode: string) => string | undefined;
38
+ airportName?: (iataCode: string) => string | undefined;
39
+ ancillaries?: FlightBookingAncillaries;
40
+ seatMaps?: FlightBookingSeatMaps;
41
+ savedPaymentMethods?: FlightBookingSavedPaymentMethods;
42
+ paymentCapabilities?: PaymentStepCapabilities;
43
+ documentsRequired?: boolean;
44
+ renderPassengerPicker?: FlightPassengerFormProps["renderPicker"];
45
+ renderBillingPersonPicker?: (apply: (prefill: Partial<BillingValue>) => void) => React.ReactNode;
46
+ renderBillingOrgPicker?: (apply: (prefill: Partial<BillingValue>) => void) => React.ReactNode;
47
+ onSaveBillingDefaults?: (value: BillingValue) => void;
48
+ }
49
+ //# sourceMappingURL=flight-booking-shell-types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flight-booking-shell-types.d.ts","sourceRoot":"","sources":["../../src/components/flight-booking-shell-types.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,gBAAgB,EAChB,iBAAiB,EACjB,WAAW,EACX,eAAe,EAChB,MAAM,uCAAuC,CAAA;AAC9C,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,0BAA0B,CAAA;AAC5D,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AAC1E,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,4BAA4B,CAAA;AAC1E,OAAO,KAAK,EAAE,uBAAuB,EAAE,kBAAkB,EAAE,MAAM,0BAA0B,CAAA;AAC3F,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAE/D,MAAM,MAAM,MAAM,GACd,QAAQ,GACR,OAAO,GACP,YAAY,GACZ,MAAM,GACN,OAAO,GACP,UAAU,GACV,SAAS,GACT,SAAS,GACT,SAAS,CAAA;AAEb,MAAM,WAAW,OAAO;IACtB,EAAE,EAAE,MAAM,CAAA;CACX;AAED,eAAO,MAAM,SAAS,EAAE,aAAa,CAAC,OAAO,CAU5C,CAAA;AAED,eAAO,MAAM,cAAc,EAAE,WAAW,CAAC,MAAM,CAM7C,CAAA;AAEF,MAAM,WAAW,wBAAwB;IACvC,eAAe,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACxC,aAAa,CAAC,EAAE,gBAAgB,GAAG,IAAI,CAAA;IACvC,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,qBAAqB;IACpC,UAAU,EAAE,CAAC,KAAK,EAAE;QAAE,OAAO,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,KAAK,iBAAiB,CAAA;CACjF;AAED,MAAM,WAAW,gCAAgC;IAC/C,OAAO,EAAE,kBAAkB,EAAE,CAAA;IAC7B,OAAO,CAAC,EAAE,OAAO,CAAA;CAClB;AAED,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,wBAAwB,CAAA;IACnC,UAAU,EAAE,eAAe,CAAA;IAC3B,MAAM,EAAE,CAAC,OAAO,EAAE,iBAAiB,KAAK,OAAO,CAAC,WAAW,CAAC,GAAG,WAAW,CAAA;IAC1E,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,WAAW,KAAK,IAAI,CAAA;IACvC,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;IACrB,cAAc,CAAC,EAAE,MAAM,IAAI,CAAA;IAC3B,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACtD,WAAW,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,MAAM,GAAG,SAAS,CAAA;IACtD,WAAW,CAAC,EAAE,wBAAwB,CAAA;IACtC,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,mBAAmB,CAAC,EAAE,gCAAgC,CAAA;IACtD,mBAAmB,CAAC,EAAE,uBAAuB,CAAA;IAC7C,iBAAiB,CAAC,EAAE,OAAO,CAAA;IAC3B,qBAAqB,CAAC,EAAE,wBAAwB,CAAC,cAAc,CAAC,CAAA;IAChE,yBAAyB,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,KAAK,KAAK,CAAC,SAAS,CAAA;IAChG,sBAAsB,CAAC,EAAE,CAAC,KAAK,EAAE,CAAC,OAAO,EAAE,OAAO,CAAC,YAAY,CAAC,KAAK,IAAI,KAAK,KAAK,CAAC,SAAS,CAAA;IAC7F,qBAAqB,CAAC,EAAE,CAAC,KAAK,EAAE,YAAY,KAAK,IAAI,CAAA;CACtD"}
@@ -0,0 +1,18 @@
1
+ export const ALL_STEPS = [
2
+ { id: "review" },
3
+ { id: "fares" },
4
+ { id: "passengers" },
5
+ { id: "bags" },
6
+ { id: "seats" },
7
+ { id: "services" },
8
+ { id: "billing" },
9
+ { id: "payment" },
10
+ { id: "confirm" },
11
+ ];
12
+ export const ALWAYS_VISIBLE = new Set([
13
+ "review",
14
+ "passengers",
15
+ "billing",
16
+ "payment",
17
+ "confirm",
18
+ ]);
@@ -0,0 +1,12 @@
1
+ import type { FlightBookingShellProps } from "./flight-booking-shell-types.js";
2
+ export type { FlightBookingAncillaries, FlightBookingSavedPaymentMethods, FlightBookingSeatMaps, FlightBookingShellProps, } from "./flight-booking-shell-types.js";
3
+ /**
4
+ * Multi-step booking shell with a sticky right-rail price ledger and a top
5
+ * stepper. Steps: review → passengers → bags → services → contact+payment
6
+ * → confirm. The shell owns the per-leg `FlightItinerarySelection` plus
7
+ * ancillary picks (baggage / assistance / extras), and synthesizes a
8
+ * combined `FlightOffer` (with both itineraries merged) when `onBook` is
9
+ * called — so the booking adapter sees a single offer with all legs intact.
10
+ */
11
+ export declare function FlightBookingShell({ selection, passengers, onBook, onBooked, onCancel, onEditOutbound, onEditReturn, carrierName, airportName, ancillaries, seatMaps, savedPaymentMethods, paymentCapabilities, documentsRequired, renderPassengerPicker, renderBillingPersonPicker, renderBillingOrgPicker, onSaveBillingDefaults, }: FlightBookingShellProps): import("react/jsx-runtime").JSX.Element | null;
12
+ //# sourceMappingURL=flight-booking-shell.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"flight-booking-shell.d.ts","sourceRoot":"","sources":["../../src/components/flight-booking-shell.tsx"],"names":[],"mappings":"AAyBA,OAAO,KAAK,EAAE,uBAAuB,EAAU,MAAM,iCAAiC,CAAA;AAEtF,YAAY,EACV,wBAAwB,EACxB,gCAAgC,EAChC,qBAAqB,EACrB,uBAAuB,GACxB,MAAM,iCAAiC,CAAA;AAQxC;;;;;;;GAOG;AACH,wBAAgB,kBAAkB,CAAC,EACjC,SAAS,EACT,UAAU,EACV,MAAM,EACN,QAAQ,EACR,QAAQ,EACR,cAAc,EACd,YAAY,EACZ,WAAW,EACX,WAAW,EACX,WAAW,EACX,QAAQ,EACR,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,qBAAqB,EACrB,yBAAyB,EACzB,sBAAsB,EACtB,qBAAqB,GACtB,EAAE,uBAAuB,kDAsWzB"}