@voyant-travel/trips-react 0.110.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.
- package/LICENSE +201 -0
- package/README.md +30 -0
- package/dist/admin/admin-trips-page-controls.d.ts +28 -0
- package/dist/admin/admin-trips-page-controls.d.ts.map +1 -0
- package/dist/admin/admin-trips-page-controls.js +28 -0
- package/dist/admin/admin-trips-page-model.d.ts +87 -0
- package/dist/admin/admin-trips-page-model.d.ts.map +1 -0
- package/dist/admin/admin-trips-page-model.js +457 -0
- package/dist/admin/admin-trips-page.d.ts +6 -0
- package/dist/admin/admin-trips-page.d.ts.map +1 -0
- package/dist/admin/admin-trips-page.js +322 -0
- package/dist/admin/admin-trips-panels.d.ts +11 -0
- package/dist/admin/admin-trips-panels.d.ts.map +1 -0
- package/dist/admin/admin-trips-panels.js +11 -0
- package/dist/admin/index.d.ts +63 -0
- package/dist/admin/index.d.ts.map +1 -0
- package/dist/admin/index.js +119 -0
- package/dist/admin/pages/trip-detail-page.d.ts +10 -0
- package/dist/admin/pages/trip-detail-page.d.ts.map +1 -0
- package/dist/admin/pages/trip-detail-page.js +12 -0
- package/dist/admin/trip-component-display.d.ts +10 -0
- package/dist/admin/trip-component-display.d.ts.map +1 -0
- package/dist/admin/trip-component-display.js +137 -0
- package/dist/admin/trip-detail-host.d.ts +12 -0
- package/dist/admin/trip-detail-host.d.ts.map +1 -0
- package/dist/admin/trip-detail-host.js +37 -0
- package/dist/admin/trip-detail-record-model.d.ts +30 -0
- package/dist/admin/trip-detail-record-model.d.ts.map +1 -0
- package/dist/admin/trip-detail-record-model.js +56 -0
- package/dist/admin/trip-detail-record.d.ts +47 -0
- package/dist/admin/trip-detail-record.d.ts.map +1 -0
- package/dist/admin/trip-detail-record.js +170 -0
- package/dist/admin/trip-list-filters.d.ts +33 -0
- package/dist/admin/trip-list-filters.d.ts.map +1 -0
- package/dist/admin/trip-list-filters.js +94 -0
- package/dist/admin/trips-host.d.ts +15 -0
- package/dist/admin/trips-host.d.ts.map +1 -0
- package/dist/admin/trips-host.js +233 -0
- package/dist/admin/trips-panels/catalog-configurator.d.ts +34 -0
- package/dist/admin/trips-panels/catalog-configurator.d.ts.map +1 -0
- package/dist/admin/trips-panels/catalog-configurator.js +200 -0
- package/dist/admin/trips-panels/catalog-options.d.ts +58 -0
- package/dist/admin/trips-panels/catalog-options.d.ts.map +1 -0
- package/dist/admin/trips-panels/catalog-options.js +124 -0
- package/dist/admin/trips-panels/committed-component-card.d.ts +27 -0
- package/dist/admin/trips-panels/committed-component-card.d.ts.map +1 -0
- package/dist/admin/trips-panels/committed-component-card.js +44 -0
- package/dist/admin/trips-panels/display.d.ts +51 -0
- package/dist/admin/trips-panels/display.d.ts.map +1 -0
- package/dist/admin/trips-panels/display.js +336 -0
- package/dist/admin/trips-panels/flight-configurator.d.ts +34 -0
- package/dist/admin/trips-panels/flight-configurator.d.ts.map +1 -0
- package/dist/admin/trips-panels/flight-configurator.js +208 -0
- package/dist/admin/trips-panels/manual-configurators.d.ts +16 -0
- package/dist/admin/trips-panels/manual-configurators.d.ts.map +1 -0
- package/dist/admin/trips-panels/manual-configurators.js +41 -0
- package/dist/admin/trips-panels/pending-component-card.d.ts +16 -0
- package/dist/admin/trips-panels/pending-component-card.d.ts.map +1 -0
- package/dist/admin/trips-panels/pending-component-card.js +29 -0
- package/dist/admin/trips-panels/shared.d.ts +122 -0
- package/dist/admin/trips-panels/shared.d.ts.map +1 -0
- package/dist/admin/trips-panels/shared.js +152 -0
- package/dist/admin/trips-panels/travelers-section.d.ts +53 -0
- package/dist/admin/trips-panels/travelers-section.d.ts.map +1 -0
- package/dist/admin/trips-panels/travelers-section.js +183 -0
- package/dist/admin/trips-panels/trip-preview-rail.d.ts +52 -0
- package/dist/admin/trips-panels/trip-preview-rail.d.ts.map +1 -0
- package/dist/admin/trips-panels/trip-preview-rail.js +122 -0
- package/dist/cache.d.ts +9 -0
- package/dist/cache.d.ts.map +1 -0
- package/dist/cache.js +21 -0
- package/dist/client.d.ts +15 -0
- package/dist/client.d.ts.map +1 -0
- package/dist/client.js +51 -0
- package/dist/hooks/index.d.ts +7 -0
- package/dist/hooks/index.d.ts.map +1 -0
- package/dist/hooks/index.js +6 -0
- package/dist/hooks/use-price-trip.d.ts +3 -0
- package/dist/hooks/use-price-trip.d.ts.map +1 -0
- package/dist/hooks/use-price-trip.js +17 -0
- package/dist/hooks/use-reserve-trip.d.ts +3 -0
- package/dist/hooks/use-reserve-trip.d.ts.map +1 -0
- package/dist/hooks/use-reserve-trip.js +17 -0
- package/dist/hooks/use-trip-checkout.d.ts +3 -0
- package/dist/hooks/use-trip-checkout.d.ts.map +1 -0
- package/dist/hooks/use-trip-checkout.js +17 -0
- package/dist/hooks/use-trip-components.d.ts +40 -0
- package/dist/hooks/use-trip-components.d.ts.map +1 -0
- package/dist/hooks/use-trip-components.js +13 -0
- package/dist/hooks/use-trip.d.ts +5 -0
- package/dist/hooks/use-trip.d.ts.map +1 -0
- package/dist/hooks/use-trip.js +13 -0
- package/dist/hooks/use-trips.d.ts +6 -0
- package/dist/hooks/use-trips.d.ts.map +1 -0
- package/dist/hooks/use-trips.js +12 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +8 -0
- package/dist/operations.d.ts +212 -0
- package/dist/operations.d.ts.map +1 -0
- package/dist/operations.js +92 -0
- package/dist/provider.d.ts +2 -0
- package/dist/provider.d.ts.map +1 -0
- package/dist/provider.js +1 -0
- package/dist/query-keys.d.ts +10 -0
- package/dist/query-keys.d.ts.map +1 -0
- package/dist/query-keys.js +9 -0
- package/dist/query-options.d.ts +167 -0
- package/dist/query-options.d.ts.map +1 -0
- package/dist/query-options.js +22 -0
- package/dist/schemas.d.ts +69 -0
- package/dist/schemas.d.ts.map +1 -0
- package/dist/schemas.js +16 -0
- package/package.json +133 -0
|
@@ -0,0 +1,322 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useMutation } from "@tanstack/react-query";
|
|
4
|
+
import { useOperatorAdminMessages as useAdminMessages, useAdminNavigate, } from "@voyant-travel/admin";
|
|
5
|
+
import { emptyPersonPickerValue } from "@voyant-travel/bookings-react/components/person-picker-section";
|
|
6
|
+
import { emptyVoucherPickerValue } from "@voyant-travel/bookings-react/components/voucher-picker-section";
|
|
7
|
+
import { PersonPickerSection, } from "@voyant-travel/bookings-react/ui";
|
|
8
|
+
import { usePerson } from "@voyant-travel/relationships-react";
|
|
9
|
+
import { CurrencyCombobox } from "@voyant-travel/ui/components/currency-combobox";
|
|
10
|
+
import { Textarea } from "@voyant-travel/ui/components/textarea";
|
|
11
|
+
import { useEffect, useMemo, useState } from "react";
|
|
12
|
+
import { addTripComponent, createTrip, getTrip, previewTripCancellation, priceTrip, removeTripComponent, reserveTrip, startTripCheckout, updateTripComponent, } from "../operations.js";
|
|
13
|
+
import { useVoyantTripsContext } from "../provider.js";
|
|
14
|
+
import { CancellationPreviewSection, CheckboxRow, } from "./admin-trips-page-controls.js";
|
|
15
|
+
import { apiError, assertTripCreationRequirements, booleanFromRecord, defaultPaymentCurrency, derivePayerEmail, derivePayerName, failuresToString, hydrateBilling, hydrateTravelers, hydrateVoucher, metadataWithComponentBookingSetup, pendingToAddInput, serializeBilling, stringFromRecord, } from "./admin-trips-page-model.js";
|
|
16
|
+
import { AddComponentMenu, CommittedComponentCard, ComponentsEmpty, componentTitleFor, Field, findOverlappingComponent, newPendingComponent, PendingComponentCard, PrimaryAction, Section, StatusAlert, sortComponentsBySchedule, TripPreviewRail, TripTravelersSection, } from "./admin-trips-panels.js";
|
|
17
|
+
export function AdminTripsPage({ initialTrip = null }) {
|
|
18
|
+
const navigateTo = useAdminNavigate();
|
|
19
|
+
const t = useAdminMessages().trips.adminComposer;
|
|
20
|
+
const [state, setState] = useState({
|
|
21
|
+
trip: initialTrip,
|
|
22
|
+
message: null,
|
|
23
|
+
error: null,
|
|
24
|
+
});
|
|
25
|
+
const [billing, setBilling] = useState(emptyPersonPickerValue);
|
|
26
|
+
const [travelers, setTravelers] = useState([]);
|
|
27
|
+
const [notes, setNotes] = useState("");
|
|
28
|
+
const [pending, setPending] = useState([]);
|
|
29
|
+
const [committingLocalId, setCommittingLocalId] = useState(null);
|
|
30
|
+
const [voucher, setVoucher] = useState(emptyVoucherPickerValue);
|
|
31
|
+
const [createAsDraft, setCreateAsDraft] = useState(false);
|
|
32
|
+
const [paymentCurrency, setPaymentCurrency] = useState(defaultPaymentCurrency);
|
|
33
|
+
const [selectedCancellationIds, setSelectedCancellationIds] = useState([]);
|
|
34
|
+
const [cancellationReason, setCancellationReason] = useState(t.cancellation.defaultReason);
|
|
35
|
+
const [cancellationPreview, setCancellationPreview] = useState(null);
|
|
36
|
+
const { baseUrl, fetcher } = useVoyantTripsContext();
|
|
37
|
+
const client = useMemo(() => ({ baseUrl, fetcher }), [baseUrl, fetcher]);
|
|
38
|
+
const trip = state.trip;
|
|
39
|
+
const envelopeId = trip?.envelope.id;
|
|
40
|
+
const components = useMemo(() => sortComponentsBySchedule((trip?.components ?? []).filter((component) => component.status !== "removed")), [trip?.components]);
|
|
41
|
+
const selectedCount = selectedCancellationIds.length;
|
|
42
|
+
const envelopeStatus = trip?.envelope.status;
|
|
43
|
+
// Once the trip is reserved or further along, removal touches real holds /
|
|
44
|
+
// bookings — operators must go through cancellation preview. Before that
|
|
45
|
+
// (`draft` / priced) a component is a no-op blueprint and can be deleted.
|
|
46
|
+
const trapReserved = envelopeStatus === "reserved" ||
|
|
47
|
+
envelopeStatus === "reserve_in_progress" ||
|
|
48
|
+
envelopeStatus === "checkout_started" ||
|
|
49
|
+
envelopeStatus === "booked";
|
|
50
|
+
const billingPersonQuery = usePerson(billing.personId || undefined, {
|
|
51
|
+
enabled: billing.mode === "existing" && Boolean(billing.personId),
|
|
52
|
+
});
|
|
53
|
+
const payerName = derivePayerName(billing, billingPersonQuery.data, t);
|
|
54
|
+
const payerEmail = derivePayerEmail(billing, billingPersonQuery.data);
|
|
55
|
+
useEffect(() => {
|
|
56
|
+
setState((current) => ({ ...current, trip: initialTrip }));
|
|
57
|
+
const travelerParty = initialTrip?.envelope.travelerParty;
|
|
58
|
+
if (!travelerParty) {
|
|
59
|
+
setBilling(emptyPersonPickerValue);
|
|
60
|
+
setTravelers([]);
|
|
61
|
+
setNotes("");
|
|
62
|
+
setVoucher(emptyVoucherPickerValue);
|
|
63
|
+
setCreateAsDraft(false);
|
|
64
|
+
setPaymentCurrency(defaultPaymentCurrency);
|
|
65
|
+
return;
|
|
66
|
+
}
|
|
67
|
+
setBilling(hydrateBilling(travelerParty));
|
|
68
|
+
setTravelers(hydrateTravelers(travelerParty));
|
|
69
|
+
setNotes(initialTrip.envelope.description ?? "");
|
|
70
|
+
setVoucher(hydrateVoucher(travelerParty));
|
|
71
|
+
const constraints = initialTrip.envelope.constraints;
|
|
72
|
+
setCreateAsDraft(booleanFromRecord(constraints, "createAsDraft"));
|
|
73
|
+
setPaymentCurrency(stringFromRecord(constraints, "paymentCurrency") ||
|
|
74
|
+
initialTrip.envelope.aggregateCurrency ||
|
|
75
|
+
defaultPaymentCurrency);
|
|
76
|
+
}, [initialTrip]);
|
|
77
|
+
function showError(error) {
|
|
78
|
+
setState((current) => ({ ...current, error: apiError(error, t), message: null }));
|
|
79
|
+
}
|
|
80
|
+
async function ensureTrip() {
|
|
81
|
+
if (state.trip)
|
|
82
|
+
return state.trip;
|
|
83
|
+
assertTripCreationRequirements({ billing, travelers, payerName, payerEmail }, t);
|
|
84
|
+
const created = await createTrip(client, {
|
|
85
|
+
description: notes || undefined,
|
|
86
|
+
travelerParty: {
|
|
87
|
+
billing: serializeBilling(billing, payerName, payerEmail),
|
|
88
|
+
travelers,
|
|
89
|
+
voucher: voucher.picked
|
|
90
|
+
? {
|
|
91
|
+
id: voucher.picked.id,
|
|
92
|
+
code: voucher.picked.code,
|
|
93
|
+
currency: voucher.picked.currency,
|
|
94
|
+
remainingAmountCents: voucher.picked.remainingAmountCents,
|
|
95
|
+
}
|
|
96
|
+
: null,
|
|
97
|
+
},
|
|
98
|
+
constraints: {
|
|
99
|
+
channel: "admin-composer",
|
|
100
|
+
compositionMode: "cross-vertical",
|
|
101
|
+
createAsDraft,
|
|
102
|
+
paymentCurrency,
|
|
103
|
+
},
|
|
104
|
+
});
|
|
105
|
+
setState((current) => ({ ...current, trip: created }));
|
|
106
|
+
return created;
|
|
107
|
+
}
|
|
108
|
+
const commitMutation = useMutation({
|
|
109
|
+
mutationFn: async (component) => {
|
|
110
|
+
const currentTrip = await ensureTrip();
|
|
111
|
+
const input = pendingToAddInput(component, {
|
|
112
|
+
billing,
|
|
113
|
+
travelers,
|
|
114
|
+
payerName,
|
|
115
|
+
payerEmail,
|
|
116
|
+
paymentCurrency,
|
|
117
|
+
}, t);
|
|
118
|
+
if (!input)
|
|
119
|
+
throw new Error(t.errors.componentNotReady);
|
|
120
|
+
await addTripComponent(client, currentTrip.envelope.id, input);
|
|
121
|
+
return priceTrip(client, currentTrip.envelope.id, {
|
|
122
|
+
scope: { locale: "en-US", audience: "staff", market: "default", currency: paymentCurrency },
|
|
123
|
+
});
|
|
124
|
+
},
|
|
125
|
+
onSuccess: (result, component) => {
|
|
126
|
+
setPending((current) => current.filter((p) => p.localId !== component.localId));
|
|
127
|
+
setState({
|
|
128
|
+
trip: { envelope: result.envelope, components: result.components },
|
|
129
|
+
message: t.statusMessages.componentAddedAndPriced,
|
|
130
|
+
error: failuresToString(result.failures, t),
|
|
131
|
+
});
|
|
132
|
+
setCancellationPreview(null);
|
|
133
|
+
setCommittingLocalId(null);
|
|
134
|
+
},
|
|
135
|
+
onError: (error, component) => {
|
|
136
|
+
const message = apiError(error, t);
|
|
137
|
+
setPending((current) => current.map((p) => (p.localId === component.localId ? { ...p, commitError: message } : p)));
|
|
138
|
+
setCommittingLocalId(null);
|
|
139
|
+
},
|
|
140
|
+
});
|
|
141
|
+
const reserveMutation = useMutation({
|
|
142
|
+
mutationFn: async () => {
|
|
143
|
+
if (!envelopeId)
|
|
144
|
+
throw new Error(t.errors.priceTripFirst);
|
|
145
|
+
assertTripCreationRequirements({ billing, travelers, payerName, payerEmail }, t);
|
|
146
|
+
const reserved = await reserveTrip(client, envelopeId, {
|
|
147
|
+
idempotencyKey: `admin-reserve-${envelopeId}`,
|
|
148
|
+
refreshScope: {
|
|
149
|
+
locale: "en-US",
|
|
150
|
+
audience: "staff",
|
|
151
|
+
market: "default",
|
|
152
|
+
currency: paymentCurrency,
|
|
153
|
+
},
|
|
154
|
+
});
|
|
155
|
+
if (reserved.failures.length > 0)
|
|
156
|
+
return { reserved, checkout: null };
|
|
157
|
+
const checkout = await startTripCheckout(client, envelopeId, {
|
|
158
|
+
idempotencyKey: `admin-checkout-${envelopeId}`,
|
|
159
|
+
intent: "card",
|
|
160
|
+
request: { initiatedBy: "admin-trips", collectionCurrency: paymentCurrency },
|
|
161
|
+
});
|
|
162
|
+
return { reserved, checkout };
|
|
163
|
+
},
|
|
164
|
+
onSuccess: ({ reserved, checkout }) => {
|
|
165
|
+
const trip = checkout
|
|
166
|
+
? { envelope: checkout.envelope, components: checkout.components }
|
|
167
|
+
: { envelope: reserved.envelope, components: reserved.components };
|
|
168
|
+
setState({
|
|
169
|
+
trip,
|
|
170
|
+
message: checkout?.target.paymentSessionId
|
|
171
|
+
? t.statusMessages.tripReservedWithLink
|
|
172
|
+
: t.statusMessages.tripReserved,
|
|
173
|
+
error: failuresToString(reserved.failures, t),
|
|
174
|
+
});
|
|
175
|
+
// Keep operators in the trip aggregate after reserve; individual booking
|
|
176
|
+
// links remain available from each component card.
|
|
177
|
+
if (reserved.failures.length === 0) {
|
|
178
|
+
navigateTo("trip.detail", { tripId: reserved.envelope.id });
|
|
179
|
+
}
|
|
180
|
+
},
|
|
181
|
+
onError: (error) => showError(error),
|
|
182
|
+
});
|
|
183
|
+
const removeComponentMutation = useMutation({
|
|
184
|
+
mutationFn: async (componentId) => {
|
|
185
|
+
if (!envelopeId)
|
|
186
|
+
throw new Error(t.errors.noTrip);
|
|
187
|
+
await removeTripComponent(client, componentId);
|
|
188
|
+
return getTrip(client, envelopeId);
|
|
189
|
+
},
|
|
190
|
+
onMutate: (componentId) => {
|
|
191
|
+
const previousTrip = state.trip;
|
|
192
|
+
// Optimistically mark the component as removed so the card disappears
|
|
193
|
+
// immediately. Aggregate totals re-derive from the visible components.
|
|
194
|
+
if (previousTrip) {
|
|
195
|
+
setState((current) => ({
|
|
196
|
+
...current,
|
|
197
|
+
trip: {
|
|
198
|
+
...previousTrip,
|
|
199
|
+
components: previousTrip.components.map((component) => component.id === componentId
|
|
200
|
+
? { ...component, status: "removed" }
|
|
201
|
+
: component),
|
|
202
|
+
},
|
|
203
|
+
}));
|
|
204
|
+
}
|
|
205
|
+
setSelectedCancellationIds((current) => current.filter((id) => id !== componentId));
|
|
206
|
+
setCancellationPreview(null);
|
|
207
|
+
return { previousTrip };
|
|
208
|
+
},
|
|
209
|
+
onSuccess: (updatedTrip) => {
|
|
210
|
+
setState({ trip: updatedTrip, message: t.statusMessages.componentRemoved, error: null });
|
|
211
|
+
},
|
|
212
|
+
onError: (error, _componentId, context) => {
|
|
213
|
+
if (context?.previousTrip) {
|
|
214
|
+
setState((current) => ({ ...current, trip: context.previousTrip }));
|
|
215
|
+
}
|
|
216
|
+
showError(error);
|
|
217
|
+
},
|
|
218
|
+
});
|
|
219
|
+
const updateComponentSetupMutation = useMutation({
|
|
220
|
+
mutationFn: async ({ componentId, metadata, }) => updateTripComponent(client, componentId, { metadata }),
|
|
221
|
+
onMutate: ({ componentId, metadata }) => {
|
|
222
|
+
const previousTrip = state.trip;
|
|
223
|
+
if (previousTrip) {
|
|
224
|
+
setState((current) => ({
|
|
225
|
+
...current,
|
|
226
|
+
trip: {
|
|
227
|
+
...previousTrip,
|
|
228
|
+
components: previousTrip.components.map((component) => component.id === componentId ? { ...component, metadata } : component),
|
|
229
|
+
},
|
|
230
|
+
}));
|
|
231
|
+
}
|
|
232
|
+
return { previousTrip };
|
|
233
|
+
},
|
|
234
|
+
onSuccess: (updatedComponent) => {
|
|
235
|
+
setState((current) => current.trip
|
|
236
|
+
? {
|
|
237
|
+
...current,
|
|
238
|
+
trip: {
|
|
239
|
+
...current.trip,
|
|
240
|
+
components: current.trip.components.map((component) => component.id === updatedComponent.id ? updatedComponent : component),
|
|
241
|
+
},
|
|
242
|
+
}
|
|
243
|
+
: current);
|
|
244
|
+
},
|
|
245
|
+
onError: (error, _input, context) => {
|
|
246
|
+
if (context?.previousTrip) {
|
|
247
|
+
setState((current) => ({ ...current, trip: context.previousTrip }));
|
|
248
|
+
}
|
|
249
|
+
showError(error);
|
|
250
|
+
},
|
|
251
|
+
});
|
|
252
|
+
const cancellationMutation = useMutation({
|
|
253
|
+
mutationFn: async () => {
|
|
254
|
+
if (!envelopeId)
|
|
255
|
+
throw new Error(t.errors.noTripToCancel);
|
|
256
|
+
return previewTripCancellation(client, envelopeId, {
|
|
257
|
+
componentIds: selectedCancellationIds,
|
|
258
|
+
reason: cancellationReason,
|
|
259
|
+
request: { initiatedBy: "admin" },
|
|
260
|
+
});
|
|
261
|
+
},
|
|
262
|
+
onSuccess: (result) => {
|
|
263
|
+
setCancellationPreview({
|
|
264
|
+
refund: result.preview.estimatedRefundAmountCents,
|
|
265
|
+
penalty: result.preview.estimatedPenaltyAmountCents,
|
|
266
|
+
staffActionRequired: result.preview.staffActionRequired,
|
|
267
|
+
warnings: result.preview.warnings,
|
|
268
|
+
});
|
|
269
|
+
setState({
|
|
270
|
+
trip: { envelope: result.envelope, components: result.components },
|
|
271
|
+
message: t.statusMessages.cancellationPreviewReady,
|
|
272
|
+
error: null,
|
|
273
|
+
});
|
|
274
|
+
},
|
|
275
|
+
onError: (error) => showError(error),
|
|
276
|
+
});
|
|
277
|
+
const isBusy = commitMutation.isPending ||
|
|
278
|
+
reserveMutation.isPending ||
|
|
279
|
+
cancellationMutation.isPending ||
|
|
280
|
+
removeComponentMutation.isPending ||
|
|
281
|
+
updateComponentSetupMutation.isPending;
|
|
282
|
+
function handleAddVertical(kind) {
|
|
283
|
+
setPending((current) => [...current, newPendingComponent(kind)]);
|
|
284
|
+
}
|
|
285
|
+
function updatePending(next) {
|
|
286
|
+
setPending((current) => current.map((p) => (p.localId === next.localId ? next : p)));
|
|
287
|
+
}
|
|
288
|
+
function removePending(localId) {
|
|
289
|
+
setPending((current) => current.filter((p) => p.localId !== localId));
|
|
290
|
+
}
|
|
291
|
+
function toggleCancellationSelection(componentId, checked) {
|
|
292
|
+
setCancellationPreview(null);
|
|
293
|
+
setSelectedCancellationIds((current) => checked
|
|
294
|
+
? [...new Set([...current, componentId])]
|
|
295
|
+
: current.filter((id) => id !== componentId));
|
|
296
|
+
}
|
|
297
|
+
function updateComponentBookingSetup(component, setup) {
|
|
298
|
+
const metadata = metadataWithComponentBookingSetup(component, setup);
|
|
299
|
+
updateComponentSetupMutation.mutate({ componentId: component.id, metadata });
|
|
300
|
+
}
|
|
301
|
+
function commitPending(component) {
|
|
302
|
+
const overlap = findOverlappingComponent(component, components);
|
|
303
|
+
if (overlap) {
|
|
304
|
+
setPending((current) => current.map((p) => p.localId === component.localId
|
|
305
|
+
? {
|
|
306
|
+
...p,
|
|
307
|
+
commitError: `These dates overlap with "${componentTitleFor(overlap)}". Adjust the schedule before adding to the trip.`,
|
|
308
|
+
}
|
|
309
|
+
: p));
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
312
|
+
setCommittingLocalId(component.localId);
|
|
313
|
+
setPending((current) => current.map((p) => (p.localId === component.localId ? { ...p, commitError: null } : p)));
|
|
314
|
+
commitMutation.mutate(component);
|
|
315
|
+
}
|
|
316
|
+
return (_jsxs("main", { className: "mx-auto flex w-full max-w-screen-2xl flex-col gap-6 px-4 py-6 sm:px-6 lg:px-8", children: [_jsxs("header", { className: "flex flex-col gap-1", children: [_jsx("h1", { className: "font-semibold text-2xl tracking-tight", children: t.heading }), _jsx("p", { className: "text-muted-foreground text-sm", children: t.subheading })] }), state.error ? (_jsx(StatusAlert, { title: t.requestFailed, message: state.error, tone: "error" })) : null, _jsxs("div", { className: "grid min-h-0 flex-1 gap-6 lg:grid-cols-12", children: [_jsxs("div", { className: "flex min-w-0 flex-col gap-4 lg:col-span-8", children: [_jsx(Section, { title: t.billingSectionTitle, children: _jsx(PersonPickerSection, { value: billing, onChange: setBilling }) }), _jsx(TripTravelersSection, { value: travelers, onChange: setTravelers, billingPersonId: billing.mode === "existing" ? billing.personId || null : null }), _jsxs("div", { className: "flex flex-col gap-3", children: [_jsx("h2", { className: "font-medium text-base", children: t.itinerarySectionTitle }), components.length === 0 && pending.length === 0 ? _jsx(ComponentsEmpty, {}) : null, components.map((component, index) => (_jsx(CommittedComponentCard, { component: component, index: index, selectable: trapReserved, selected: selectedCancellationIds.includes(component.id), onSelectedChange: (checked) => toggleCancellationSelection(component.id, checked), onRemove: trapReserved ? undefined : () => removeComponentMutation.mutate(component.id), removePending: removeComponentMutation.isPending &&
|
|
317
|
+
removeComponentMutation.variables === component.id, bookingSetupEditable: !trapReserved, bookingSetupSaving: updateComponentSetupMutation.isPending &&
|
|
318
|
+
updateComponentSetupMutation.variables?.componentId === component.id, onBookingSetupChange: updateComponentBookingSetup }, component.id))), pending.map((entry) => (_jsx(PendingComponentCard, { pending: entry, onChange: updatePending, onRemove: () => removePending(entry.localId), onCommit: () => commitPending(entry), committing: committingLocalId === entry.localId && commitMutation.isPending, travelers: travelers }, entry.localId))), _jsx(AddComponentMenu, { onAdd: handleAddVertical, disabled: isBusy })] }), trapReserved && selectedCount > 0 ? (_jsx(CancellationPreviewSection, { messages: t, selectedCount: selectedCount, cancellationReason: cancellationReason, onCancellationReasonChange: setCancellationReason, cancellationPreview: cancellationPreview, paymentCurrency: paymentCurrency, isBusy: isBusy, hasEnvelope: Boolean(envelopeId), isPending: cancellationMutation.isPending, onPreview: () => cancellationMutation.mutate(), onClearSelection: () => {
|
|
319
|
+
setSelectedCancellationIds([]);
|
|
320
|
+
setCancellationPreview(null);
|
|
321
|
+
} })) : null, _jsx(Section, { title: t.internalNotesSectionTitle, description: t.internalNotesSectionDescription, children: _jsx(Field, { label: t.internalNotesLabel, children: _jsx(Textarea, { rows: 3, value: notes, onChange: (event) => setNotes(event.target.value), placeholder: t.internalNotesPlaceholder }) }) }), _jsx(Section, { title: t.paymentSectionTitle, children: _jsx(Field, { label: t.paymentCurrencyLabel, children: _jsx(CurrencyCombobox, { value: paymentCurrency, onChange: (value) => setPaymentCurrency(value ?? defaultPaymentCurrency) }) }) }), _jsx(Section, { title: t.onReserveSectionTitle, description: t.onReserveSectionDescription, children: _jsx(CheckboxRow, { id: "composer-create-as-draft", checked: createAsDraft, onCheckedChange: setCreateAsDraft, label: t.startInDraftLabel, hint: t.startInDraftHint }) }), _jsx(PrimaryAction, { status: trip?.envelope.status, componentCount: components.length, isBusy: isBusy, pricePending: commitMutation.isPending, reservePending: reserveMutation.isPending, onReserve: () => reserveMutation.mutate() })] }), _jsx("aside", { className: "flex flex-col gap-4 lg:col-span-4", children: _jsx(TripPreviewRail, { trip: trip, pendingCount: pending.length, travelers: travelers, billing: billing, billingPersonId: billing.mode === "existing" ? billing.personId || null : null, voucher: voucher, onVoucherChange: setVoucher, paymentCurrency: paymentCurrency }) })] })] }));
|
|
322
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from "./trips-panels/catalog-configurator.js";
|
|
2
|
+
export * from "./trips-panels/catalog-options.js";
|
|
3
|
+
export * from "./trips-panels/committed-component-card.js";
|
|
4
|
+
export * from "./trips-panels/display.js";
|
|
5
|
+
export * from "./trips-panels/flight-configurator.js";
|
|
6
|
+
export * from "./trips-panels/manual-configurators.js";
|
|
7
|
+
export * from "./trips-panels/pending-component-card.js";
|
|
8
|
+
export * from "./trips-panels/shared.js";
|
|
9
|
+
export * from "./trips-panels/travelers-section.js";
|
|
10
|
+
export * from "./trips-panels/trip-preview-rail.js";
|
|
11
|
+
//# sourceMappingURL=admin-trips-panels.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"admin-trips-panels.d.ts","sourceRoot":"","sources":["../../src/admin/admin-trips-panels.tsx"],"names":[],"mappings":"AAEA,cAAc,wCAAwC,CAAA;AACtD,cAAc,mCAAmC,CAAA;AACjD,cAAc,4CAA4C,CAAA;AAC1D,cAAc,2BAA2B,CAAA;AACzC,cAAc,uCAAuC,CAAA;AACrD,cAAc,wCAAwC,CAAA;AACtD,cAAc,0CAA0C,CAAA;AACxD,cAAc,0BAA0B,CAAA;AACxC,cAAc,qCAAqC,CAAA;AACnD,cAAc,qCAAqC,CAAA"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
export * from "./trips-panels/catalog-configurator.js";
|
|
3
|
+
export * from "./trips-panels/catalog-options.js";
|
|
4
|
+
export * from "./trips-panels/committed-component-card.js";
|
|
5
|
+
export * from "./trips-panels/display.js";
|
|
6
|
+
export * from "./trips-panels/flight-configurator.js";
|
|
7
|
+
export * from "./trips-panels/manual-configurators.js";
|
|
8
|
+
export * from "./trips-panels/pending-component-card.js";
|
|
9
|
+
export * from "./trips-panels/shared.js";
|
|
10
|
+
export * from "./trips-panels/travelers-section.js";
|
|
11
|
+
export * from "./trips-panels/trip-preview-rail.js";
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { type AdminExtension, type NavItem } from "@voyant-travel/admin";
|
|
2
|
+
/**
|
|
3
|
+
* Semantic destinations the trips admin surfaces navigate to
|
|
4
|
+
* (packaged-admin RFC §4.7). The trips list opens the trip detail page, the
|
|
5
|
+
* detail page links back to the list and across to the booking/person
|
|
6
|
+
* pages — instead of importing a host route tree they resolve these keys
|
|
7
|
+
* through `useAdminHref`/`useAdminNavigate` from `@voyant-travel/admin`. Hosts
|
|
8
|
+
* register one resolver per key (`satisfies AdminDestinationResolvers`).
|
|
9
|
+
* Keys shared with other domains (`booking.detail`, `person.detail`) come
|
|
10
|
+
* from the bookings-react augmentation bound above.
|
|
11
|
+
*/
|
|
12
|
+
declare module "@voyant-travel/admin" {
|
|
13
|
+
interface AdminDestinations {
|
|
14
|
+
/** The trips list page. */
|
|
15
|
+
"trip.list": Record<string, never>;
|
|
16
|
+
/** A trip's detail page; the `"new"` pseudo-id opens the composer. */
|
|
17
|
+
"trip.detail": {
|
|
18
|
+
tripId: string;
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
export type { AdminTripsPageProps } from "./admin-trips-page.js";
|
|
23
|
+
export type { TripListFiltersPopoverProps, TripStatusFilter } from "./trip-list-filters.js";
|
|
24
|
+
export interface CreateTripsAdminExtensionOptions {
|
|
25
|
+
/** Mount path of the trips pages inside the admin workspace. Default `/trips`. */
|
|
26
|
+
basePath?: string;
|
|
27
|
+
/** Localized nav/page labels. Defaults are the English operator nav labels. */
|
|
28
|
+
labels?: {
|
|
29
|
+
trips?: string;
|
|
30
|
+
allTrips?: string;
|
|
31
|
+
newTrip?: string;
|
|
32
|
+
};
|
|
33
|
+
/** Nav icon — icon choice stays with the host (e.g. lucide `Route`). */
|
|
34
|
+
icon?: NavItem["icon"];
|
|
35
|
+
}
|
|
36
|
+
/**
|
|
37
|
+
* The trips admin contribution (packaged-admin RFC Phase 3,
|
|
38
|
+
* `@voyant-travel/<domain>-react/admin` convention).
|
|
39
|
+
*
|
|
40
|
+
* NAVIGATION: package-delivered. Trips is NOT part of the BASE operator
|
|
41
|
+
* navigation (`createOperatorAdminNavigation` in `@voyant-travel/admin`), so the
|
|
42
|
+
* extension contributes the Trips group itself — spliced in right after
|
|
43
|
+
* Bookings (both belong to the booking lifecycle) via `insertAfter`, with
|
|
44
|
+
* All trips / New trip sub-items. The icon stays a host choice.
|
|
45
|
+
*
|
|
46
|
+
* ROUTES: contributions carry the FULL route implementation (packaged-admin
|
|
47
|
+
* RFC §4.2/§4.8) — lazy `page` module loaders, data loaders fed by the
|
|
48
|
+
* host-supplied {@link AdminRouteLoaderContext} (QueryClient + runtime +
|
|
49
|
+
* params), per-route SSR mode. Hosts bind them into their code-assembled
|
|
50
|
+
* admin route tree; no per-route host files needed. The pages stay
|
|
51
|
+
* code-split because each contribution's `page` dynamically imports the
|
|
52
|
+
* specific host/page module — never this barrel — and the trips
|
|
53
|
+
* itself mounts through a nested `React.lazy`, so its heavy panel stack
|
|
54
|
+
* (flights search, catalog browse, booking journey) only fetches when an
|
|
55
|
+
* operator actually composes/edits a trip. The list keeps its filter/sort/
|
|
56
|
+
* paging state local (no URL search contracts), and every cross-route link
|
|
57
|
+
* resolves through the semantic destinations declared above — no app RPC
|
|
58
|
+
* client, no host route tree.
|
|
59
|
+
*
|
|
60
|
+
* WIDGETS: none contributed and no slots exposed yet.
|
|
61
|
+
*/
|
|
62
|
+
export declare function createTripsAdminExtension(options?: CreateTripsAdminExtensionOptions): AdminExtension;
|
|
63
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/admin/index.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,cAAc,EAKnB,KAAK,OAAO,EACb,MAAM,sBAAsB,CAAA;AAc7B;;;;;;;;;GASG;AACH,OAAO,QAAQ,sBAAsB,CAAC;IACpC,UAAU,iBAAiB;QACzB,2BAA2B;QAC3B,WAAW,EAAE,MAAM,CAAC,MAAM,EAAE,KAAK,CAAC,CAAA;QAClC,sEAAsE;QACtE,aAAa,EAAE;YAAE,MAAM,EAAE,MAAM,CAAA;SAAE,CAAA;KAClC;CACF;AAWD,YAAY,EAAE,mBAAmB,EAAE,MAAM,uBAAuB,CAAA;AAChE,YAAY,EAAE,2BAA2B,EAAE,gBAAgB,EAAE,MAAM,wBAAwB,CAAA;AAE3F,MAAM,WAAW,gCAAgC;IAC/C,kFAAkF;IAClF,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,+EAA+E;IAC/E,MAAM,CAAC,EAAE;QACP,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;IACD,wEAAwE;IACxE,IAAI,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,CAAA;CACvB;AAED;;;;;;;;;;;;;;;;;;;;;;;;;GAyBG;AACH,wBAAgB,yBAAyB,CACvC,OAAO,GAAE,gCAAqC,GAC7C,cAAc,CAiFhB"}
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
import { adminRoutePageModule, defineAdminExtension, } from "@voyant-travel/admin";
|
|
2
|
+
// Lean static only: the client module (fetcher). Query options resolve via
|
|
3
|
+
// dynamic import inside the loaders so the trips data layer
|
|
4
|
+
// (client + response schemas) stays out of the workspace-chrome chunk that
|
|
5
|
+
// evaluates this factory.
|
|
6
|
+
import { defaultFetcher } from "../client.js";
|
|
7
|
+
/**
|
|
8
|
+
* The trips admin contribution (packaged-admin RFC Phase 3,
|
|
9
|
+
* `@voyant-travel/<domain>-react/admin` convention).
|
|
10
|
+
*
|
|
11
|
+
* NAVIGATION: package-delivered. Trips is NOT part of the BASE operator
|
|
12
|
+
* navigation (`createOperatorAdminNavigation` in `@voyant-travel/admin`), so the
|
|
13
|
+
* extension contributes the Trips group itself — spliced in right after
|
|
14
|
+
* Bookings (both belong to the booking lifecycle) via `insertAfter`, with
|
|
15
|
+
* All trips / New trip sub-items. The icon stays a host choice.
|
|
16
|
+
*
|
|
17
|
+
* ROUTES: contributions carry the FULL route implementation (packaged-admin
|
|
18
|
+
* RFC §4.2/§4.8) — lazy `page` module loaders, data loaders fed by the
|
|
19
|
+
* host-supplied {@link AdminRouteLoaderContext} (QueryClient + runtime +
|
|
20
|
+
* params), per-route SSR mode. Hosts bind them into their code-assembled
|
|
21
|
+
* admin route tree; no per-route host files needed. The pages stay
|
|
22
|
+
* code-split because each contribution's `page` dynamically imports the
|
|
23
|
+
* specific host/page module — never this barrel — and the trips
|
|
24
|
+
* itself mounts through a nested `React.lazy`, so its heavy panel stack
|
|
25
|
+
* (flights search, catalog browse, booking journey) only fetches when an
|
|
26
|
+
* operator actually composes/edits a trip. The list keeps its filter/sort/
|
|
27
|
+
* paging state local (no URL search contracts), and every cross-route link
|
|
28
|
+
* resolves through the semantic destinations declared above — no app RPC
|
|
29
|
+
* client, no host route tree.
|
|
30
|
+
*
|
|
31
|
+
* WIDGETS: none contributed and no slots exposed yet.
|
|
32
|
+
*/
|
|
33
|
+
export function createTripsAdminExtension(options = {}) {
|
|
34
|
+
const { basePath = "/trips", labels = {}, icon } = options;
|
|
35
|
+
const { trips = "Trips", allTrips = "All trips", newTrip = "New trip" } = labels;
|
|
36
|
+
return defineAdminExtension({
|
|
37
|
+
id: "trips",
|
|
38
|
+
navigation: [
|
|
39
|
+
{
|
|
40
|
+
// Splice Trips in right after Bookings — both belong to the booking
|
|
41
|
+
// lifecycle. `insertAfter` keeps the contribution shape; the resolver
|
|
42
|
+
// splices in place rather than appending at the end.
|
|
43
|
+
insertAfter: "bookings",
|
|
44
|
+
items: [
|
|
45
|
+
{
|
|
46
|
+
id: "trips",
|
|
47
|
+
title: trips,
|
|
48
|
+
url: basePath,
|
|
49
|
+
icon,
|
|
50
|
+
items: [
|
|
51
|
+
{
|
|
52
|
+
id: "trips-list",
|
|
53
|
+
title: allTrips,
|
|
54
|
+
url: basePath,
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
id: "trips-new",
|
|
58
|
+
title: newTrip,
|
|
59
|
+
url: `${basePath}/new`,
|
|
60
|
+
},
|
|
61
|
+
],
|
|
62
|
+
},
|
|
63
|
+
],
|
|
64
|
+
},
|
|
65
|
+
],
|
|
66
|
+
routes: [
|
|
67
|
+
{
|
|
68
|
+
id: "trips-index",
|
|
69
|
+
path: basePath,
|
|
70
|
+
title: trips,
|
|
71
|
+
// Route-backed destination (RFC §4.7 endgame): the key resolves by
|
|
72
|
+
// pure path interpolation of this route, so the host's resolver is
|
|
73
|
+
// generated (`voyant admin generate --destinations`).
|
|
74
|
+
destination: "trip.list",
|
|
75
|
+
ssr: "data-only",
|
|
76
|
+
page: () => import("./trips-host.js").then((module) => adminRoutePageModule(module.TripsHost)),
|
|
77
|
+
// Dynamic import on purpose: the query options pull the
|
|
78
|
+
// trips data layer (client + response schemas), and a
|
|
79
|
+
// static import here would pin it into the workspace-chrome chunk
|
|
80
|
+
// that evaluates this factory. The host module carries the matching
|
|
81
|
+
// initial params so the seeded cache entry lines up with the page's
|
|
82
|
+
// first query.
|
|
83
|
+
loader: async ({ queryClient, runtime }) => {
|
|
84
|
+
const [{ listTripsQueryOptions }, { initialTripsListParams }] = await Promise.all([
|
|
85
|
+
import("../query-options.js"),
|
|
86
|
+
import("./trips-host.js"),
|
|
87
|
+
]);
|
|
88
|
+
return queryClient.ensureQueryData(listTripsQueryOptions(loaderClient(runtime), initialTripsListParams));
|
|
89
|
+
},
|
|
90
|
+
},
|
|
91
|
+
{
|
|
92
|
+
id: "trips-detail",
|
|
93
|
+
path: `${basePath}/$id`,
|
|
94
|
+
title: trips,
|
|
95
|
+
destination: "trip.detail",
|
|
96
|
+
destinationParams: { id: "tripId" },
|
|
97
|
+
ssr: "data-only",
|
|
98
|
+
page: () => import("./pages/trip-detail-page.js"),
|
|
99
|
+
// The `"new"` pseudo-id mounts the composer with no trip to fetch.
|
|
100
|
+
// Dynamic import on purpose — see the trips index loader above.
|
|
101
|
+
loader: async ({ queryClient, runtime, params }) => {
|
|
102
|
+
const id = params.id;
|
|
103
|
+
if (!id || id === "new")
|
|
104
|
+
return;
|
|
105
|
+
const { getTripQueryOptions } = await import("../query-options.js");
|
|
106
|
+
return queryClient.ensureQueryData(getTripQueryOptions(loaderClient(runtime), id));
|
|
107
|
+
},
|
|
108
|
+
},
|
|
109
|
+
],
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
/**
|
|
113
|
+
* Bridge the host-supplied {@link AdminRouteRuntime} (optional fetcher) to
|
|
114
|
+
* the required-fetcher client contract the trips query options
|
|
115
|
+
* take — SSR loaders run with the host runtime's cookie-forwarding fetcher.
|
|
116
|
+
*/
|
|
117
|
+
function loaderClient(runtime) {
|
|
118
|
+
return { baseUrl: runtime.baseUrl, fetcher: runtime.fetcher ?? defaultFetcher };
|
|
119
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { AdminRoutePageProps } from "@voyant-travel/admin";
|
|
2
|
+
/**
|
|
3
|
+
* Param-taking page for the `trips-detail` contribution: reads the
|
|
4
|
+
* trip envelope id off {@link AdminRoutePageProps} and binds it onto the
|
|
5
|
+
* packaged host. Resolved lazily through the contribution's `page` loader so
|
|
6
|
+
* the detail page (and the composer behind its Edit mode) lands in its own
|
|
7
|
+
* chunk.
|
|
8
|
+
*/
|
|
9
|
+
export default function TripDetailPage({ params }: AdminRoutePageProps): import("react/jsx-runtime").JSX.Element;
|
|
10
|
+
//# sourceMappingURL=trip-detail-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trip-detail-page.d.ts","sourceRoot":"","sources":["../../../src/admin/pages/trip-detail-page.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,mBAAmB,EAAE,MAAM,sBAAsB,CAAA;AAI/D;;;;;;GAMG;AACH,MAAM,CAAC,OAAO,UAAU,cAAc,CAAC,EAAE,MAAM,EAAE,EAAE,mBAAmB,2CAErE"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { TripDetailHost } from "../trip-detail-host.js";
|
|
3
|
+
/**
|
|
4
|
+
* Param-taking page for the `trips-detail` contribution: reads the
|
|
5
|
+
* trip envelope id off {@link AdminRoutePageProps} and binds it onto the
|
|
6
|
+
* packaged host. Resolved lazily through the contribution's `page` loader so
|
|
7
|
+
* the detail page (and the composer behind its Edit mode) lands in its own
|
|
8
|
+
* chunk.
|
|
9
|
+
*/
|
|
10
|
+
export default function TripDetailPage({ params }) {
|
|
11
|
+
return _jsx(TripDetailHost, { id: params.id ?? "" });
|
|
12
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { TripComponent } from "@voyant-travel/trips";
|
|
2
|
+
export declare function readComponentSchedule(component: TripComponent): {
|
|
3
|
+
start: string | null;
|
|
4
|
+
end: string | null;
|
|
5
|
+
};
|
|
6
|
+
export declare function formatScheduleLabel(component: TripComponent): string | null;
|
|
7
|
+
export declare function sortComponentsBySchedule(components: TripComponent[]): TripComponent[];
|
|
8
|
+
export declare function componentTitleFor(component: TripComponent, resolvedEntityName?: string | null): string;
|
|
9
|
+
export declare function componentReferenceLabelFor(component: TripComponent): string;
|
|
10
|
+
//# sourceMappingURL=trip-component-display.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"trip-component-display.d.ts","sourceRoot":"","sources":["../../src/admin/trip-component-display.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAA;AAEzD,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,aAAa,GAAG;IAC/D,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,GAAG,EAAE,MAAM,GAAG,IAAI,CAAA;CACnB,CA8BA;AAED,wBAAgB,mBAAmB,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,GAAG,IAAI,CAM3E;AAED,wBAAgB,wBAAwB,CAAC,UAAU,EAAE,aAAa,EAAE,GAAG,aAAa,EAAE,CAcrF;AAED,wBAAgB,iBAAiB,CAC/B,SAAS,EAAE,aAAa,EACxB,kBAAkB,CAAC,EAAE,MAAM,GAAG,IAAI,GACjC,MAAM,CAgER;AAED,wBAAgB,0BAA0B,CAAC,SAAS,EAAE,aAAa,GAAG,MAAM,CAW3E"}
|