@voyantjs/flights-ui 0.30.7 → 0.31.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 +66 -0
- package/dist/components/billing-pickers.d.ts +19 -0
- package/dist/components/billing-pickers.d.ts.map +1 -0
- package/dist/components/billing-pickers.js +148 -0
- package/dist/components/flight-booking-page.d.ts +25 -0
- package/dist/components/flight-booking-page.d.ts.map +1 -0
- package/dist/components/flight-booking-page.js +175 -0
- package/dist/components/flights-page.d.ts +39 -0
- package/dist/components/flights-page.d.ts.map +1 -0
- package/dist/components/flights-page.js +318 -0
- package/dist/components/passenger-contact-picker.d.ts +16 -0
- package/dist/components/passenger-contact-picker.d.ts.map +1 -0
- package/dist/components/passenger-contact-picker.js +45 -0
- package/dist/i18n/en.d.ts +61 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +60 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/messages.d.ts +61 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/provider.d.ts +144 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro.d.ts +61 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +60 -0
- package/dist/index.d.ts +5 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -0
- package/package.json +32 -11
|
@@ -0,0 +1,318 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { useQueryClient } from "@tanstack/react-query";
|
|
4
|
+
import { flightsQueryKeys, useAirlines, useAirports, useFlightSearch, } from "@voyantjs/flights-react";
|
|
5
|
+
import { formatMessage } from "@voyantjs/i18n";
|
|
6
|
+
import { Badge } from "@voyantjs/ui/components/badge";
|
|
7
|
+
import { Button } from "@voyantjs/ui/components/button";
|
|
8
|
+
import { Sheet, SheetContent, SheetFooter, SheetHeader, SheetTitle, } from "@voyantjs/ui/components/sheet";
|
|
9
|
+
import { cn } from "@voyantjs/ui/lib/utils";
|
|
10
|
+
import { ChevronLeft, ChevronRight, Pencil, Plane } from "lucide-react";
|
|
11
|
+
import { useMemo, useState } from "react";
|
|
12
|
+
import { useFlightsUiMessagesOrDefault } from "../i18n/index.js";
|
|
13
|
+
import { EMPTY_FLIGHT_FILTERS, FlightFiltersBar, } from "./flight-filters-bar.js";
|
|
14
|
+
import { FlightItinerary } from "./flight-itinerary.js";
|
|
15
|
+
import { FlightOfferDetail } from "./flight-offer-detail.js";
|
|
16
|
+
import { FlightOfferRow } from "./flight-offer-row.js";
|
|
17
|
+
import { FlightSearchForm } from "./flight-search-form.js";
|
|
18
|
+
import { DEFAULT_POPULAR_ROUTES, PopularRoutes } from "./popular-routes.js";
|
|
19
|
+
const PAGE_SIZE = 20;
|
|
20
|
+
export function FlightsPage({ search, onSearchChange, onBookOffer, routes = DEFAULT_POPULAR_ROUTES, className, }) {
|
|
21
|
+
const messages = useFlightsUiMessagesOrDefault().flightsPage;
|
|
22
|
+
const qc = useQueryClient();
|
|
23
|
+
const [openOffer, setOpenOffer] = useState(null);
|
|
24
|
+
const isRoundTrip = (search.tripType ?? "round_trip") === "round_trip";
|
|
25
|
+
const stage = (() => {
|
|
26
|
+
if (!isRoundTrip)
|
|
27
|
+
return "outbound";
|
|
28
|
+
if (!search.outboundOfferId)
|
|
29
|
+
return "outbound";
|
|
30
|
+
if (!search.returnOfferId)
|
|
31
|
+
return "return";
|
|
32
|
+
return "ready";
|
|
33
|
+
})();
|
|
34
|
+
const outboundOffer = useMemo(() => (search.outboundOfferId ? readOfferFromCache(qc, search.outboundOfferId) : null), [search.outboundOfferId, qc]);
|
|
35
|
+
const returnOffer = useMemo(() => (search.returnOfferId ? readOfferFromCache(qc, search.returnOfferId) : null), [search.returnOfferId, qc]);
|
|
36
|
+
const airlinesQuery = useAirlines();
|
|
37
|
+
const airportsQuery = useAirports({ limit: 200 });
|
|
38
|
+
const carrierMap = useMemo(() => {
|
|
39
|
+
const map = new Map();
|
|
40
|
+
for (const airline of airlinesQuery.data?.data ?? []) {
|
|
41
|
+
map.set(airline.iataCode, airline.name);
|
|
42
|
+
}
|
|
43
|
+
return map;
|
|
44
|
+
}, [airlinesQuery.data]);
|
|
45
|
+
const airportMap = useMemo(() => {
|
|
46
|
+
const map = new Map();
|
|
47
|
+
for (const airport of airportsQuery.data?.data ?? []) {
|
|
48
|
+
map.set(airport.iataCode, `${airport.city} (${airport.iataCode})`);
|
|
49
|
+
}
|
|
50
|
+
return map;
|
|
51
|
+
}, [airportsQuery.data]);
|
|
52
|
+
const carrierName = (code) => carrierMap.get(code);
|
|
53
|
+
const airportName = (code) => airportMap.get(code);
|
|
54
|
+
const baseRequest = useMemo(() => urlToBaseRequest(search, stage === "ready" ? "outbound" : stage), [search, stage]);
|
|
55
|
+
const filters = useMemo(() => urlToFilters(search), [search]);
|
|
56
|
+
const searchEnabled = baseRequest != null && stage !== "ready";
|
|
57
|
+
const request = useMemo(() => baseRequest && stage !== "ready"
|
|
58
|
+
? composeRequest(baseRequest, filters, search.page ?? 1)
|
|
59
|
+
: null, [baseRequest, filters, search.page, stage]);
|
|
60
|
+
const flightSearchQuery = useFlightSearch(request ?? EMPTY_REQUEST_FOR_DISABLED, {
|
|
61
|
+
enabled: searchEnabled,
|
|
62
|
+
});
|
|
63
|
+
const offers = flightSearchQuery.data?.offers ?? [];
|
|
64
|
+
const meta = flightSearchQuery.data?.pagination;
|
|
65
|
+
const total = meta?.total ?? offers.length;
|
|
66
|
+
const totalPages = Math.max(1, Math.ceil(total / PAGE_SIZE));
|
|
67
|
+
const page = search.page ?? 1;
|
|
68
|
+
const rangeStart = total === 0 ? 0 : (page - 1) * PAGE_SIZE + 1;
|
|
69
|
+
const rangeEnd = Math.min(page * PAGE_SIZE, total);
|
|
70
|
+
const passengers = useMemo(() => ({
|
|
71
|
+
adults: search.pax_a ?? 1,
|
|
72
|
+
children: search.pax_c ?? 0,
|
|
73
|
+
infants: search.pax_i ?? 0,
|
|
74
|
+
}), [search.pax_a, search.pax_c, search.pax_i]);
|
|
75
|
+
const cabin = search.cabin ?? "economy";
|
|
76
|
+
const pickOffer = (offer) => {
|
|
77
|
+
qc.setQueryData(flightsQueryKeys.offerDetail(offer.offerId), { offer });
|
|
78
|
+
if (!isRoundTrip) {
|
|
79
|
+
onBookOffer({
|
|
80
|
+
outboundOfferId: offer.offerId,
|
|
81
|
+
passengers,
|
|
82
|
+
cabin,
|
|
83
|
+
});
|
|
84
|
+
return;
|
|
85
|
+
}
|
|
86
|
+
if (stage === "outbound") {
|
|
87
|
+
onSearchChange({
|
|
88
|
+
...search,
|
|
89
|
+
leg: "return",
|
|
90
|
+
outboundOfferId: offer.offerId,
|
|
91
|
+
returnOfferId: undefined,
|
|
92
|
+
page: 1,
|
|
93
|
+
});
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
if (stage === "return") {
|
|
97
|
+
onSearchChange({
|
|
98
|
+
...search,
|
|
99
|
+
returnOfferId: offer.offerId,
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
};
|
|
103
|
+
const proceedToBooking = () => {
|
|
104
|
+
if (!search.outboundOfferId)
|
|
105
|
+
return;
|
|
106
|
+
onBookOffer({
|
|
107
|
+
outboundOfferId: search.outboundOfferId,
|
|
108
|
+
returnOfferId: search.returnOfferId,
|
|
109
|
+
passengers,
|
|
110
|
+
cabin,
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
const changeOutbound = () => {
|
|
114
|
+
onSearchChange({
|
|
115
|
+
...search,
|
|
116
|
+
leg: "outbound",
|
|
117
|
+
outboundOfferId: undefined,
|
|
118
|
+
returnOfferId: undefined,
|
|
119
|
+
page: 1,
|
|
120
|
+
});
|
|
121
|
+
};
|
|
122
|
+
const changeReturn = () => {
|
|
123
|
+
onSearchChange({
|
|
124
|
+
...search,
|
|
125
|
+
leg: "return",
|
|
126
|
+
returnOfferId: undefined,
|
|
127
|
+
page: 1,
|
|
128
|
+
});
|
|
129
|
+
};
|
|
130
|
+
const handleSubmit = (next) => {
|
|
131
|
+
const first = next.slices[0];
|
|
132
|
+
const second = next.slices[1];
|
|
133
|
+
onSearchChange({
|
|
134
|
+
tripType: next.slices.length === 2 ? "round_trip" : "one_way",
|
|
135
|
+
from: first?.origin,
|
|
136
|
+
to: first?.destination,
|
|
137
|
+
depart: first?.departureDate,
|
|
138
|
+
ret: second?.departureDate,
|
|
139
|
+
pax_a: next.passengers.adults,
|
|
140
|
+
pax_c: next.passengers.children ?? 0,
|
|
141
|
+
pax_i: next.passengers.infants ?? 0,
|
|
142
|
+
cabin: next.cabin ?? "economy",
|
|
143
|
+
leg: "outbound",
|
|
144
|
+
outboundOfferId: undefined,
|
|
145
|
+
returnOfferId: undefined,
|
|
146
|
+
page: 1,
|
|
147
|
+
});
|
|
148
|
+
};
|
|
149
|
+
const handleFiltersChange = (nextFilters) => {
|
|
150
|
+
onSearchChange({
|
|
151
|
+
...search,
|
|
152
|
+
carriers: nextFilters.carriers.length > 0 ? nextFilters.carriers : undefined,
|
|
153
|
+
maxStops: nextFilters.maxStops ?? undefined,
|
|
154
|
+
maxPrice: nextFilters.maxPrice ?? undefined,
|
|
155
|
+
page: 1,
|
|
156
|
+
}, { replace: true });
|
|
157
|
+
};
|
|
158
|
+
const setPage = (next) => {
|
|
159
|
+
onSearchChange({ ...search, page: next }, { replace: true });
|
|
160
|
+
};
|
|
161
|
+
const formInitial = useMemo(() => {
|
|
162
|
+
if (!search.from || !search.to || !search.depart)
|
|
163
|
+
return undefined;
|
|
164
|
+
const slices = [{ origin: search.from, destination: search.to, departureDate: search.depart }];
|
|
165
|
+
if (isRoundTrip && search.ret) {
|
|
166
|
+
slices.push({ origin: search.to, destination: search.from, departureDate: search.ret });
|
|
167
|
+
}
|
|
168
|
+
return {
|
|
169
|
+
slices,
|
|
170
|
+
passengers,
|
|
171
|
+
cabin,
|
|
172
|
+
tripType: search.tripType,
|
|
173
|
+
};
|
|
174
|
+
}, [
|
|
175
|
+
cabin,
|
|
176
|
+
isRoundTrip,
|
|
177
|
+
passengers,
|
|
178
|
+
search.depart,
|
|
179
|
+
search.from,
|
|
180
|
+
search.ret,
|
|
181
|
+
search.to,
|
|
182
|
+
search.tripType,
|
|
183
|
+
]);
|
|
184
|
+
const hasSearchInput = baseRequest != null;
|
|
185
|
+
return (_jsxs("div", { className: cn("mx-auto flex w-full max-w-screen-2xl flex-col gap-5 px-6 py-6 lg:px-8", className), children: [_jsxs("header", { children: [_jsx("h1", { className: "font-semibold text-2xl", children: messages.title }), _jsx("p", { className: "text-muted-foreground text-sm", children: messages.description })] }), _jsx(FlightSearchForm, { onSearch: handleSubmit, loading: flightSearchQuery.isFetching, initial: formInitial }, hasSearchInput
|
|
186
|
+
? `${search.from}-${search.to}-${search.depart}-${search.ret ?? ""}`
|
|
187
|
+
: "empty"), !hasSearchInput && _jsx(PopularRoutes, { routes: routes, onSelect: handleSubmit }), stage === "ready" && outboundOffer && returnOffer && (_jsx(ReadyToBookPanel, { outbound: outboundOffer, returnLeg: returnOffer, carrierName: carrierName, airportName: airportName, onChangeOutbound: changeOutbound, onChangeReturn: changeReturn, onContinue: proceedToBooking })), stage === "ready" && (!outboundOffer || !returnOffer) && (_jsx(CacheColdBanner, { message: messages.pickedOfferMissing, onReset: changeOutbound })), stage === "return" && !outboundOffer && (_jsx(CacheColdBanner, { message: messages.outboundOfferMissing, onReset: changeOutbound })), hasSearchInput && stage !== "ready" && flightSearchQuery.isError && (_jsx("div", { className: "rounded-md border border-destructive/40 bg-destructive/5 p-4 text-destructive text-sm", children: flightSearchQuery.error instanceof Error
|
|
188
|
+
? flightSearchQuery.error.message
|
|
189
|
+
: messages.searchFailed })), hasSearchInput && stage !== "ready" && !flightSearchQuery.isError && (_jsxs(_Fragment, { children: [_jsx(FlightFiltersBar, { value: filters, onChange: handleFiltersChange, offers: offers, carrierName: carrierName }), stage === "return" && outboundOffer && (_jsx(PickedLegBanner, { label: messages.selectedOutbound, offer: outboundOffer, carrierName: carrierName, airportName: airportName, onChange: changeOutbound })), _jsxs("div", { className: "flex items-center justify-between gap-4", children: [_jsx("h2", { className: "font-medium text-base", children: legHeading(messages, stage, isRoundTrip, search.from, search.to) }), _jsx("span", { className: "text-muted-foreground text-sm", children: flightSearchQuery.isFetching
|
|
190
|
+
? messages.searching
|
|
191
|
+
: total === 0
|
|
192
|
+
? messages.zeroOffers
|
|
193
|
+
: formatMessage(messages.offersSummary, {
|
|
194
|
+
start: String(rangeStart),
|
|
195
|
+
end: String(rangeEnd),
|
|
196
|
+
total: String(total),
|
|
197
|
+
plural: total === 1 ? "" : "s",
|
|
198
|
+
}) })] }), flightSearchQuery.isFetching ? (_jsx("div", { className: "flex flex-col gap-2", children: Array.from({ length: 4 }).map((_, index) => (_jsx("div", { className: "h-24 animate-pulse rounded-lg border bg-muted/40" }, index))) })) : offers.length === 0 ? (_jsx(NoResults, { hasFilters: hasActiveFilters(filters) })) : (_jsxs(_Fragment, { children: [_jsx("div", { className: "flex flex-col gap-2", children: offers.map((offer) => (_jsx(FlightOfferRow, { offer: offer, carrierName: carrierName, onClick: (nextOffer) => setOpenOffer(nextOffer), onSelect: pickOffer, selectLabel: selectLabel(messages, stage, isRoundTrip) }, offer.offerId))) }), totalPages > 1 && (_jsxs("div", { className: "mt-2 flex items-center justify-between gap-2", children: [_jsx("span", { className: "text-muted-foreground text-sm", children: formatMessage(messages.pageSummary, {
|
|
199
|
+
page: String(page),
|
|
200
|
+
totalPages: String(totalPages),
|
|
201
|
+
}) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPage(Math.max(1, page - 1)), disabled: page <= 1, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), messages.previous] }), _jsxs(Button, { variant: "outline", size: "sm", onClick: () => setPage(Math.min(totalPages, page + 1)), disabled: page >= totalPages || meta?.hasMore === false, children: [messages.next, _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }))] }))] })), _jsx(Sheet, { open: openOffer != null, onOpenChange: (open) => {
|
|
202
|
+
if (!open)
|
|
203
|
+
setOpenOffer(null);
|
|
204
|
+
}, children: _jsx(SheetContent, { className: "w-full p-0 sm:max-w-2xl", children: _jsxs("div", { className: "flex h-full flex-col", children: [_jsx(SheetHeader, { className: "border-b px-6 py-5", children: _jsxs(SheetTitle, { className: "flex items-center gap-2 text-base", children: [_jsx(Plane, { className: "h-4 w-4" }), messages.flightOffer, openOffer?.validatingCarrier && (_jsx(Badge, { variant: "secondary", children: openOffer.validatingCarrier }))] }) }), _jsx("div", { className: "flex-1 overflow-y-auto px-6 py-5", children: openOffer && (_jsx(FlightOfferDetail, { offer: openOffer, carrierName: carrierName, airportName: airportName })) }), openOffer && (_jsx(SheetFooter, { className: "border-t bg-muted/20 px-6 py-3", children: _jsx("div", { className: "flex w-full items-center justify-end gap-2", children: _jsx(Button, { onClick: () => {
|
|
205
|
+
const offer = openOffer;
|
|
206
|
+
setOpenOffer(null);
|
|
207
|
+
pickOffer(offer);
|
|
208
|
+
}, children: selectLabel(messages, stage, isRoundTrip) }) }) }))] }) }) })] }));
|
|
209
|
+
}
|
|
210
|
+
function PickedLegBanner({ label, offer, carrierName, airportName, onChange, }) {
|
|
211
|
+
const messages = useFlightsUiMessagesOrDefault().flightsPage;
|
|
212
|
+
const itinerary = offer.itineraries[0];
|
|
213
|
+
if (!itinerary)
|
|
214
|
+
return null;
|
|
215
|
+
return (_jsxs("div", { className: "rounded-xl border bg-card p-4 shadow-sm", children: [_jsxs("div", { className: "mb-3 flex items-center justify-between gap-3", children: [_jsx("span", { className: "font-medium text-[11px] uppercase tracking-wider text-emerald-700", children: label }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx("span", { className: "font-semibold text-base tabular-nums", children: formatMoney(offer.totalPrice.amount, offer.totalPrice.currency) }), _jsxs(Button, { variant: "ghost", size: "sm", onClick: onChange, children: [_jsx(Pencil, { className: "mr-1 h-3.5 w-3.5" }), messages.change] })] })] }), _jsx(FlightItinerary, { itinerary: itinerary, compact: true, carrierName: carrierName, airportName: airportName })] }));
|
|
216
|
+
}
|
|
217
|
+
function ReadyToBookPanel({ outbound, returnLeg, carrierName, airportName, onChangeOutbound, onChangeReturn, onContinue, }) {
|
|
218
|
+
const messages = useFlightsUiMessagesOrDefault().flightsPage;
|
|
219
|
+
const total = Number(outbound.totalPrice.amount) + Number(returnLeg.totalPrice.amount);
|
|
220
|
+
const currency = outbound.totalPrice.currency;
|
|
221
|
+
return (_jsxs("section", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-col gap-3", children: [_jsx(PickedLegBanner, { label: messages.selectedOutbound, offer: outbound, carrierName: carrierName, airportName: airportName, onChange: onChangeOutbound }), _jsx(PickedLegBanner, { label: messages.selectedReturn, offer: returnLeg, carrierName: carrierName, airportName: airportName, onChange: onChangeReturn })] }), _jsxs("div", { className: "flex flex-col items-stretch gap-3 rounded-xl border bg-card p-5 shadow-sm md:flex-row md:items-center md:justify-between", children: [_jsxs("div", { className: "flex flex-col leading-tight", children: [_jsx("span", { className: "font-medium text-[11px] uppercase tracking-wider text-muted-foreground", children: messages.tripTotal }), _jsx("span", { className: "font-semibold text-2xl tabular-nums", children: formatMoney(total.toFixed(2), currency) }), _jsx("span", { className: "text-muted-foreground text-xs", children: messages.tripTotalDescription })] }), _jsxs(Button, { size: "lg", onClick: onContinue, className: "md:px-8", children: [messages.continueToBooking, _jsx(ChevronRight, { className: "ml-1 h-4 w-4" })] })] })] }));
|
|
222
|
+
}
|
|
223
|
+
function CacheColdBanner({ message, onReset }) {
|
|
224
|
+
const messages = useFlightsUiMessagesOrDefault().flightsPage;
|
|
225
|
+
return (_jsxs("div", { className: "rounded-md border border-dashed bg-card p-6 text-center text-muted-foreground text-sm", children: [_jsx("p", { children: message }), _jsxs(Button, { className: "mt-3", variant: "outline", onClick: onReset, children: [_jsx(ChevronLeft, { className: "mr-1 h-4 w-4" }), messages.pickOutboundAgain] })] }));
|
|
226
|
+
}
|
|
227
|
+
function legHeading(messages, stage, isRoundTrip, from, to) {
|
|
228
|
+
if (!isRoundTrip)
|
|
229
|
+
return messages.availableFlights;
|
|
230
|
+
if (stage === "outbound") {
|
|
231
|
+
return formatMessage(messages.outboundHeading, { from: from ?? "?", to: to ?? "?" });
|
|
232
|
+
}
|
|
233
|
+
if (stage === "return") {
|
|
234
|
+
return formatMessage(messages.returnHeading, { from: to ?? "?", to: from ?? "?" });
|
|
235
|
+
}
|
|
236
|
+
return messages.tripHeading;
|
|
237
|
+
}
|
|
238
|
+
function selectLabel(messages, stage, isRoundTrip) {
|
|
239
|
+
if (!isRoundTrip)
|
|
240
|
+
return messages.bookThisFlight;
|
|
241
|
+
if (stage === "outbound")
|
|
242
|
+
return messages.selectOutbound;
|
|
243
|
+
if (stage === "return")
|
|
244
|
+
return messages.selectReturn;
|
|
245
|
+
return messages.continueToBooking;
|
|
246
|
+
}
|
|
247
|
+
function NoResults({ hasFilters }) {
|
|
248
|
+
const messages = useFlightsUiMessagesOrDefault().flightsPage;
|
|
249
|
+
return (_jsx("div", { className: "rounded-lg border border-dashed p-8 text-center text-muted-foreground text-sm", children: hasFilters ? messages.noFilteredResults : messages.noRouteResults }));
|
|
250
|
+
}
|
|
251
|
+
function hasActiveFilters(filters) {
|
|
252
|
+
return filters.carriers.length > 0 || filters.maxStops != null || filters.maxPrice != null;
|
|
253
|
+
}
|
|
254
|
+
function readOfferFromCache(qc, offerId) {
|
|
255
|
+
const cached = qc.getQueryData(flightsQueryKeys.offerDetail(offerId));
|
|
256
|
+
return cached?.offer ?? null;
|
|
257
|
+
}
|
|
258
|
+
function formatMoney(amount, currency) {
|
|
259
|
+
const n = Number(amount);
|
|
260
|
+
if (!Number.isFinite(n))
|
|
261
|
+
return `${amount} ${currency}`;
|
|
262
|
+
return new Intl.NumberFormat(undefined, {
|
|
263
|
+
style: "currency",
|
|
264
|
+
currency,
|
|
265
|
+
maximumFractionDigits: 0,
|
|
266
|
+
}).format(n);
|
|
267
|
+
}
|
|
268
|
+
const EMPTY_REQUEST_FOR_DISABLED = {
|
|
269
|
+
slices: [],
|
|
270
|
+
passengers: { adults: 1 },
|
|
271
|
+
cabin: "economy",
|
|
272
|
+
};
|
|
273
|
+
function urlToBaseRequest(search, leg) {
|
|
274
|
+
if (!search.from || !search.to || !search.depart)
|
|
275
|
+
return null;
|
|
276
|
+
const isRoundTrip = (search.tripType ?? "round_trip") === "round_trip";
|
|
277
|
+
if (leg === "return" && (!isRoundTrip || !search.ret))
|
|
278
|
+
return null;
|
|
279
|
+
const slice = leg === "outbound"
|
|
280
|
+
? { origin: search.from, destination: search.to, departureDate: search.depart }
|
|
281
|
+
: { origin: search.to, destination: search.from, departureDate: search.ret };
|
|
282
|
+
return {
|
|
283
|
+
slices: [slice],
|
|
284
|
+
passengers: {
|
|
285
|
+
adults: search.pax_a ?? 1,
|
|
286
|
+
children: search.pax_c ?? 0,
|
|
287
|
+
infants: search.pax_i ?? 0,
|
|
288
|
+
},
|
|
289
|
+
cabin: search.cabin ?? "economy",
|
|
290
|
+
};
|
|
291
|
+
}
|
|
292
|
+
function urlToFilters(search) {
|
|
293
|
+
return {
|
|
294
|
+
...EMPTY_FLIGHT_FILTERS,
|
|
295
|
+
carriers: search.carriers ?? [],
|
|
296
|
+
maxStops: search.maxStops ?? null,
|
|
297
|
+
maxPrice: search.maxPrice ?? null,
|
|
298
|
+
};
|
|
299
|
+
}
|
|
300
|
+
function composeRequest(base, filters, page) {
|
|
301
|
+
const searchOptions = {};
|
|
302
|
+
if (filters.carriers.length > 0)
|
|
303
|
+
searchOptions.includeCarriers = filters.carriers;
|
|
304
|
+
if (filters.maxStops === 0)
|
|
305
|
+
searchOptions.directOnly = true;
|
|
306
|
+
else if (filters.maxStops != null)
|
|
307
|
+
searchOptions.maxStops = filters.maxStops;
|
|
308
|
+
if (filters.maxPrice != null)
|
|
309
|
+
searchOptions.maxPrice = filters.maxPrice;
|
|
310
|
+
return {
|
|
311
|
+
...base,
|
|
312
|
+
...(Object.keys(searchOptions).length > 0 ? { searchOptions } : {}),
|
|
313
|
+
pagination: {
|
|
314
|
+
limit: PAGE_SIZE,
|
|
315
|
+
...(page > 1 ? { cursor: String(page) } : {}),
|
|
316
|
+
},
|
|
317
|
+
};
|
|
318
|
+
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { PassengerPrefill } from "./flight-passenger-form.js";
|
|
2
|
+
export interface PassengerContactPickerProps {
|
|
3
|
+
/** Called when the user picks a CRM person; fields get merged into the passenger card. */
|
|
4
|
+
onPick: (prefill: PassengerPrefill) => void;
|
|
5
|
+
/** Optional CRM route callback. When omitted, the "add contact" action is hidden. */
|
|
6
|
+
onAddContact?: () => void;
|
|
7
|
+
/** Exposes the selected CRM person id to parent flows such as saved-payment lookup. */
|
|
8
|
+
onPersonSelected?: (personId: string | null) => void;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Passenger card "Pick from contacts" trigger. Opens a popover with a
|
|
12
|
+
* searchable list of CRM people and maps the picked person into the
|
|
13
|
+
* `PassengerPrefill` shape expected by `FlightPassengerForm`.
|
|
14
|
+
*/
|
|
15
|
+
export declare function PassengerContactPicker({ onPick, onAddContact, onPersonSelected, }: PassengerContactPickerProps): import("react/jsx-runtime").JSX.Element;
|
|
16
|
+
//# sourceMappingURL=passenger-contact-picker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"passenger-contact-picker.d.ts","sourceRoot":"","sources":["../../src/components/passenger-contact-picker.tsx"],"names":[],"mappings":"AAkBA,OAAO,KAAK,EAAE,gBAAgB,EAAE,MAAM,4BAA4B,CAAA;AAElE,MAAM,WAAW,2BAA2B;IAC1C,0FAA0F;IAC1F,MAAM,EAAE,CAAC,OAAO,EAAE,gBAAgB,KAAK,IAAI,CAAA;IAC3C,qFAAqF;IACrF,YAAY,CAAC,EAAE,MAAM,IAAI,CAAA;IACzB,uFAAuF;IACvF,gBAAgB,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;CACrD;AAED;;;;GAIG;AACH,wBAAgB,sBAAsB,CAAC,EACrC,MAAM,EACN,YAAY,EACZ,gBAAgB,GACjB,EAAE,2BAA2B,2CA0F7B"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
|
+
import { usePeople } from "@voyantjs/crm-react";
|
|
4
|
+
import { Button } from "@voyantjs/ui/components/button";
|
|
5
|
+
import { Command, CommandEmpty, CommandGroup, CommandInput, CommandItem, CommandList, CommandSeparator, } from "@voyantjs/ui/components/command";
|
|
6
|
+
import { Popover, PopoverContent, PopoverTrigger } from "@voyantjs/ui/components/popover";
|
|
7
|
+
import { ChevronDown, UserPlus, Users } from "lucide-react";
|
|
8
|
+
import { useState } from "react";
|
|
9
|
+
import { useFlightsUiMessagesOrDefault } from "../i18n/index.js";
|
|
10
|
+
/**
|
|
11
|
+
* Passenger card "Pick from contacts" trigger. Opens a popover with a
|
|
12
|
+
* searchable list of CRM people and maps the picked person into the
|
|
13
|
+
* `PassengerPrefill` shape expected by `FlightPassengerForm`.
|
|
14
|
+
*/
|
|
15
|
+
export function PassengerContactPicker({ onPick, onAddContact, onPersonSelected, }) {
|
|
16
|
+
const messages = useFlightsUiMessagesOrDefault().passengerContactPicker;
|
|
17
|
+
const [open, setOpen] = useState(false);
|
|
18
|
+
const [search, setSearch] = useState("");
|
|
19
|
+
const peopleQuery = usePeople({
|
|
20
|
+
search: search.trim() || undefined,
|
|
21
|
+
limit: 30,
|
|
22
|
+
enabled: open,
|
|
23
|
+
});
|
|
24
|
+
const people = peopleQuery.data?.data ?? [];
|
|
25
|
+
return (_jsxs(Popover, { open: open, onOpenChange: setOpen, children: [_jsxs(PopoverTrigger, { render: _jsx(Button, { type: "button", variant: "outline", size: "sm", className: "h-8 gap-2" }), children: [_jsx(Users, { className: "h-3.5 w-3.5" }), messages.trigger, _jsx(ChevronDown, { className: "h-3.5 w-3.5 text-muted-foreground" })] }), _jsx(PopoverContent, { className: "w-[320px] p-0", align: "end", children: _jsxs(Command, { shouldFilter: false, children: [_jsx(CommandInput, { value: search, onValueChange: setSearch, placeholder: messages.searchPlaceholder }), _jsxs(CommandList, { children: [_jsx(CommandEmpty, { children: peopleQuery.isLoading ? messages.searching : messages.empty }), _jsx(CommandGroup, { children: people.map((person) => {
|
|
26
|
+
const fullName = `${person.firstName} ${person.lastName}`.trim();
|
|
27
|
+
return (_jsx(CommandItem, { value: `${fullName} ${person.email ?? ""}`, onSelect: () => {
|
|
28
|
+
onPick({
|
|
29
|
+
firstName: person.firstName,
|
|
30
|
+
...(person.middleName ? { middleName: person.middleName } : {}),
|
|
31
|
+
lastName: person.lastName,
|
|
32
|
+
email: person.email ?? undefined,
|
|
33
|
+
phone: person.phone ?? undefined,
|
|
34
|
+
...(person.gender ? { gender: person.gender } : {}),
|
|
35
|
+
...(person.birthday ? { dateOfBirth: person.birthday } : {}),
|
|
36
|
+
});
|
|
37
|
+
onPersonSelected?.(person.id);
|
|
38
|
+
setOpen(false);
|
|
39
|
+
setSearch("");
|
|
40
|
+
}, children: _jsxs("div", { className: "flex min-w-0 flex-1 flex-col leading-tight", children: [_jsx("span", { className: "truncate font-medium text-sm", children: fullName || messages.emptyName }), person.email && (_jsx("span", { className: "truncate text-muted-foreground text-xs", children: person.email }))] }) }, person.id));
|
|
41
|
+
}) }), onAddContact && (_jsxs(_Fragment, { children: [_jsx(CommandSeparator, {}), _jsx(CommandGroup, { children: _jsxs(CommandItem, { value: "__add-new", onSelect: () => {
|
|
42
|
+
onAddContact();
|
|
43
|
+
setOpen(false);
|
|
44
|
+
}, className: "text-primary", children: [_jsx(UserPlus, { className: "mr-2 h-4 w-4" }), messages.addNewContact] }) })] }))] })] }) })] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export declare const flightsUiEn: {
|
|
2
|
+
flightsPage: {
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
searchFailed: string;
|
|
6
|
+
selectedOutbound: string;
|
|
7
|
+
selectedReturn: string;
|
|
8
|
+
change: string;
|
|
9
|
+
tripTotal: string;
|
|
10
|
+
tripTotalDescription: string;
|
|
11
|
+
continueToBooking: string;
|
|
12
|
+
pickedOfferMissing: string;
|
|
13
|
+
outboundOfferMissing: string;
|
|
14
|
+
pickOutboundAgain: string;
|
|
15
|
+
availableFlights: string;
|
|
16
|
+
outboundHeading: string;
|
|
17
|
+
returnHeading: string;
|
|
18
|
+
tripHeading: string;
|
|
19
|
+
searching: string;
|
|
20
|
+
zeroOffers: string;
|
|
21
|
+
offersSummary: string;
|
|
22
|
+
pageSummary: string;
|
|
23
|
+
previous: string;
|
|
24
|
+
next: string;
|
|
25
|
+
flightOffer: string;
|
|
26
|
+
bookThisFlight: string;
|
|
27
|
+
selectOutbound: string;
|
|
28
|
+
selectReturn: string;
|
|
29
|
+
noFilteredResults: string;
|
|
30
|
+
noRouteResults: string;
|
|
31
|
+
};
|
|
32
|
+
flightBookingPage: {
|
|
33
|
+
title: string;
|
|
34
|
+
descriptionTrip: string;
|
|
35
|
+
descriptionOffer: string;
|
|
36
|
+
offerNotInSessionTitle: string;
|
|
37
|
+
offerNotInSessionDescription: string;
|
|
38
|
+
backToFlightSearch: string;
|
|
39
|
+
backToResults: string;
|
|
40
|
+
};
|
|
41
|
+
passengerContactPicker: {
|
|
42
|
+
trigger: string;
|
|
43
|
+
searchPlaceholder: string;
|
|
44
|
+
searching: string;
|
|
45
|
+
empty: string;
|
|
46
|
+
addNewContact: string;
|
|
47
|
+
emptyName: string;
|
|
48
|
+
};
|
|
49
|
+
billingPickers: {
|
|
50
|
+
personTrigger: string;
|
|
51
|
+
personSearchPlaceholder: string;
|
|
52
|
+
peopleSearching: string;
|
|
53
|
+
peopleEmpty: string;
|
|
54
|
+
orgTrigger: string;
|
|
55
|
+
orgSearchPlaceholder: string;
|
|
56
|
+
orgsSearching: string;
|
|
57
|
+
orgsEmpty: string;
|
|
58
|
+
emptyName: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=en.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,WAAW;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA4DK,CAAA"}
|
package/dist/i18n/en.js
ADDED
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
export const flightsUiEn = {
|
|
2
|
+
flightsPage: {
|
|
3
|
+
title: "Flights",
|
|
4
|
+
description: "Search live flight offers across configured connectors.",
|
|
5
|
+
searchFailed: "Search failed.",
|
|
6
|
+
selectedOutbound: "Outbound selected",
|
|
7
|
+
selectedReturn: "Return selected",
|
|
8
|
+
change: "Change",
|
|
9
|
+
tripTotal: "Trip total",
|
|
10
|
+
tripTotalDescription: "Both legs - taxes included",
|
|
11
|
+
continueToBooking: "Continue to booking",
|
|
12
|
+
pickedOfferMissing: "One of your picked offers is not in your session anymore.",
|
|
13
|
+
outboundOfferMissing: "The outbound offer is not in your session anymore.",
|
|
14
|
+
pickOutboundAgain: "Pick outbound again",
|
|
15
|
+
availableFlights: "Available flights",
|
|
16
|
+
outboundHeading: "Outbound - {from} to {to}",
|
|
17
|
+
returnHeading: "Return - {from} to {to}",
|
|
18
|
+
tripHeading: "Trip",
|
|
19
|
+
searching: "Searching...",
|
|
20
|
+
zeroOffers: "0 offers",
|
|
21
|
+
offersSummary: "{start}-{end} of {total} offer{plural}",
|
|
22
|
+
pageSummary: "Page {page} of {totalPages}",
|
|
23
|
+
previous: "Previous",
|
|
24
|
+
next: "Next",
|
|
25
|
+
flightOffer: "Flight offer",
|
|
26
|
+
bookThisFlight: "Book this flight",
|
|
27
|
+
selectOutbound: "Select outbound",
|
|
28
|
+
selectReturn: "Select return",
|
|
29
|
+
noFilteredResults: "No offers match the current filters.",
|
|
30
|
+
noRouteResults: "No flights found for this route on this date.",
|
|
31
|
+
},
|
|
32
|
+
flightBookingPage: {
|
|
33
|
+
title: "Book your flight",
|
|
34
|
+
descriptionTrip: "Confirm passenger details, contact, and payment to lock in this trip.",
|
|
35
|
+
descriptionOffer: "Confirm passenger details, contact, and payment to lock in this offer.",
|
|
36
|
+
offerNotInSessionTitle: "Offer not in session",
|
|
37
|
+
offerNotInSessionDescription: "This booking link references an offer that is no longer in your browser session. Run the search again to pick a fresh offer.",
|
|
38
|
+
backToFlightSearch: "Back to flight search",
|
|
39
|
+
backToResults: "Back to results",
|
|
40
|
+
},
|
|
41
|
+
passengerContactPicker: {
|
|
42
|
+
trigger: "Pick from contacts",
|
|
43
|
+
searchPlaceholder: "Search contacts...",
|
|
44
|
+
searching: "Searching...",
|
|
45
|
+
empty: "No contacts.",
|
|
46
|
+
addNewContact: "Add new contact in CRM",
|
|
47
|
+
emptyName: "-",
|
|
48
|
+
},
|
|
49
|
+
billingPickers: {
|
|
50
|
+
personTrigger: "Use details from contact",
|
|
51
|
+
personSearchPlaceholder: "Search contacts...",
|
|
52
|
+
peopleSearching: "Searching...",
|
|
53
|
+
peopleEmpty: "No contacts.",
|
|
54
|
+
orgTrigger: "Use company on file",
|
|
55
|
+
orgSearchPlaceholder: "Search companies...",
|
|
56
|
+
orgsSearching: "Searching...",
|
|
57
|
+
orgsEmpty: "No companies.",
|
|
58
|
+
emptyName: "-",
|
|
59
|
+
},
|
|
60
|
+
};
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
export { flightsUiEn } from "./en.js";
|
|
2
|
+
export type { FlightsUiMessages } from "./messages.js";
|
|
3
|
+
export { type FlightsUiMessageOverrides, FlightsUiMessagesProvider, flightsUiMessageDefinitions, getFlightsUiI18n, resolveFlightsUiMessages, useFlightsUiI18n, useFlightsUiI18nOrDefault, useFlightsUiMessages, useFlightsUiMessagesOrDefault, } from "./provider.js";
|
|
4
|
+
export { flightsUiRo } from "./ro.js";
|
|
5
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/i18n/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA;AACrC,YAAY,EAAE,iBAAiB,EAAE,MAAM,eAAe,CAAA;AACtD,OAAO,EACL,KAAK,yBAAyB,EAC9B,yBAAyB,EACzB,2BAA2B,EAC3B,gBAAgB,EAChB,wBAAwB,EACxB,gBAAgB,EAChB,yBAAyB,EACzB,oBAAoB,EACpB,6BAA6B,GAC9B,MAAM,eAAe,CAAA;AACtB,OAAO,EAAE,WAAW,EAAE,MAAM,SAAS,CAAA"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export { flightsUiEn } from "./en.js";
|
|
2
|
+
export { FlightsUiMessagesProvider, flightsUiMessageDefinitions, getFlightsUiI18n, resolveFlightsUiMessages, useFlightsUiI18n, useFlightsUiI18nOrDefault, useFlightsUiMessages, useFlightsUiMessagesOrDefault, } from "./provider.js";
|
|
3
|
+
export { flightsUiRo } from "./ro.js";
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export type FlightsUiMessages = {
|
|
2
|
+
flightsPage: {
|
|
3
|
+
title: string;
|
|
4
|
+
description: string;
|
|
5
|
+
searchFailed: string;
|
|
6
|
+
selectedOutbound: string;
|
|
7
|
+
selectedReturn: string;
|
|
8
|
+
change: string;
|
|
9
|
+
tripTotal: string;
|
|
10
|
+
tripTotalDescription: string;
|
|
11
|
+
continueToBooking: string;
|
|
12
|
+
pickedOfferMissing: string;
|
|
13
|
+
outboundOfferMissing: string;
|
|
14
|
+
pickOutboundAgain: string;
|
|
15
|
+
availableFlights: string;
|
|
16
|
+
outboundHeading: string;
|
|
17
|
+
returnHeading: string;
|
|
18
|
+
tripHeading: string;
|
|
19
|
+
searching: string;
|
|
20
|
+
zeroOffers: string;
|
|
21
|
+
offersSummary: string;
|
|
22
|
+
pageSummary: string;
|
|
23
|
+
previous: string;
|
|
24
|
+
next: string;
|
|
25
|
+
flightOffer: string;
|
|
26
|
+
bookThisFlight: string;
|
|
27
|
+
selectOutbound: string;
|
|
28
|
+
selectReturn: string;
|
|
29
|
+
noFilteredResults: string;
|
|
30
|
+
noRouteResults: string;
|
|
31
|
+
};
|
|
32
|
+
flightBookingPage: {
|
|
33
|
+
title: string;
|
|
34
|
+
descriptionTrip: string;
|
|
35
|
+
descriptionOffer: string;
|
|
36
|
+
offerNotInSessionTitle: string;
|
|
37
|
+
offerNotInSessionDescription: string;
|
|
38
|
+
backToFlightSearch: string;
|
|
39
|
+
backToResults: string;
|
|
40
|
+
};
|
|
41
|
+
passengerContactPicker: {
|
|
42
|
+
trigger: string;
|
|
43
|
+
searchPlaceholder: string;
|
|
44
|
+
searching: string;
|
|
45
|
+
empty: string;
|
|
46
|
+
addNewContact: string;
|
|
47
|
+
emptyName: string;
|
|
48
|
+
};
|
|
49
|
+
billingPickers: {
|
|
50
|
+
personTrigger: string;
|
|
51
|
+
personSearchPlaceholder: string;
|
|
52
|
+
peopleSearching: string;
|
|
53
|
+
peopleEmpty: string;
|
|
54
|
+
orgTrigger: string;
|
|
55
|
+
orgSearchPlaceholder: string;
|
|
56
|
+
orgsSearching: string;
|
|
57
|
+
orgsEmpty: string;
|
|
58
|
+
emptyName: string;
|
|
59
|
+
};
|
|
60
|
+
};
|
|
61
|
+
//# sourceMappingURL=messages.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"messages.d.ts","sourceRoot":"","sources":["../../src/i18n/messages.ts"],"names":[],"mappings":"AAAA,MAAM,MAAM,iBAAiB,GAAG;IAC9B,WAAW,EAAE;QACX,KAAK,EAAE,MAAM,CAAA;QACb,WAAW,EAAE,MAAM,CAAA;QACnB,YAAY,EAAE,MAAM,CAAA;QACpB,gBAAgB,EAAE,MAAM,CAAA;QACxB,cAAc,EAAE,MAAM,CAAA;QACtB,MAAM,EAAE,MAAM,CAAA;QACd,SAAS,EAAE,MAAM,CAAA;QACjB,oBAAoB,EAAE,MAAM,CAAA;QAC5B,iBAAiB,EAAE,MAAM,CAAA;QACzB,kBAAkB,EAAE,MAAM,CAAA;QAC1B,oBAAoB,EAAE,MAAM,CAAA;QAC5B,iBAAiB,EAAE,MAAM,CAAA;QACzB,gBAAgB,EAAE,MAAM,CAAA;QACxB,eAAe,EAAE,MAAM,CAAA;QACvB,aAAa,EAAE,MAAM,CAAA;QACrB,WAAW,EAAE,MAAM,CAAA;QACnB,SAAS,EAAE,MAAM,CAAA;QACjB,UAAU,EAAE,MAAM,CAAA;QAClB,aAAa,EAAE,MAAM,CAAA;QACrB,WAAW,EAAE,MAAM,CAAA;QACnB,QAAQ,EAAE,MAAM,CAAA;QAChB,IAAI,EAAE,MAAM,CAAA;QACZ,WAAW,EAAE,MAAM,CAAA;QACnB,cAAc,EAAE,MAAM,CAAA;QACtB,cAAc,EAAE,MAAM,CAAA;QACtB,YAAY,EAAE,MAAM,CAAA;QACpB,iBAAiB,EAAE,MAAM,CAAA;QACzB,cAAc,EAAE,MAAM,CAAA;KACvB,CAAA;IACD,iBAAiB,EAAE;QACjB,KAAK,EAAE,MAAM,CAAA;QACb,eAAe,EAAE,MAAM,CAAA;QACvB,gBAAgB,EAAE,MAAM,CAAA;QACxB,sBAAsB,EAAE,MAAM,CAAA;QAC9B,4BAA4B,EAAE,MAAM,CAAA;QACpC,kBAAkB,EAAE,MAAM,CAAA;QAC1B,aAAa,EAAE,MAAM,CAAA;KACtB,CAAA;IACD,sBAAsB,EAAE;QACtB,OAAO,EAAE,MAAM,CAAA;QACf,iBAAiB,EAAE,MAAM,CAAA;QACzB,SAAS,EAAE,MAAM,CAAA;QACjB,KAAK,EAAE,MAAM,CAAA;QACb,aAAa,EAAE,MAAM,CAAA;QACrB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;IACD,cAAc,EAAE;QACd,aAAa,EAAE,MAAM,CAAA;QACrB,uBAAuB,EAAE,MAAM,CAAA;QAC/B,eAAe,EAAE,MAAM,CAAA;QACvB,WAAW,EAAE,MAAM,CAAA;QACnB,UAAU,EAAE,MAAM,CAAA;QAClB,oBAAoB,EAAE,MAAM,CAAA;QAC5B,aAAa,EAAE,MAAM,CAAA;QACrB,SAAS,EAAE,MAAM,CAAA;QACjB,SAAS,EAAE,MAAM,CAAA;KAClB,CAAA;CACF,CAAA"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {};
|