@voyantjs/bookings-ui 0.34.0 → 0.37.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/components/booking-combobox.d.ts +13 -0
- package/dist/components/booking-combobox.d.ts.map +1 -0
- package/dist/components/booking-combobox.js +44 -0
- package/dist/components/booking-create-dialog.d.ts +9 -0
- package/dist/components/booking-create-dialog.d.ts.map +1 -1
- package/dist/components/booking-create-dialog.js +110 -90
- package/dist/components/booking-create-page.d.ts +11 -0
- package/dist/components/booking-create-page.d.ts.map +1 -0
- package/dist/components/booking-create-page.js +11 -0
- package/dist/components/booking-detail-page.d.ts.map +1 -1
- package/dist/components/booking-detail-page.js +4 -1
- package/dist/components/booking-item-list.d.ts.map +1 -1
- package/dist/components/booking-item-list.js +5 -3
- package/dist/components/booking-list-filters.d.ts +35 -0
- package/dist/components/booking-list-filters.d.ts.map +1 -0
- package/dist/components/booking-list-filters.js +148 -0
- package/dist/components/booking-list.d.ts.map +1 -1
- package/dist/components/booking-list.js +30 -46
- package/dist/components/person-picker-section.d.ts +15 -7
- package/dist/components/person-picker-section.d.ts.map +1 -1
- package/dist/components/person-picker-section.js +100 -21
- package/dist/components/price-breakdown-section.d.ts +16 -1
- package/dist/components/price-breakdown-section.d.ts.map +1 -1
- package/dist/components/price-breakdown-section.js +36 -5
- package/dist/components/product-picker-section.d.ts.map +1 -1
- package/dist/components/product-picker-section.js +38 -4
- package/dist/components/shared-room-section.d.ts +9 -8
- package/dist/components/shared-room-section.d.ts.map +1 -1
- package/dist/components/shared-room-section.js +67 -14
- package/dist/components/traveler-list.d.ts.map +1 -1
- package/dist/components/traveler-list.js +27 -16
- package/dist/i18n/en.d.ts +284 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +300 -17
- package/dist/i18n/messages.d.ts +255 -1
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/provider.d.ts +568 -2
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/ro.d.ts +284 -1
- package/dist/i18n/ro.d.ts.map +1 -1
- package/dist/i18n/ro.js +301 -18
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/journey/components/booking-journey.d.ts.map +1 -1
- package/dist/journey/components/booking-journey.js +22 -13
- package/dist/journey/components/contract-preview-dialog.d.ts.map +1 -1
- package/dist/journey/components/contract-preview-dialog.js +9 -4
- package/dist/journey/components/journey-steps.d.ts.map +1 -1
- package/dist/journey/components/journey-steps.js +94 -72
- package/dist/journey/components/side-panel.d.ts.map +1 -1
- package/dist/journey/components/side-panel.js +58 -35
- package/dist/journey/components/step-header.d.ts.map +1 -1
- package/dist/journey/components/step-header.js +3 -19
- package/package.json +24 -20
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
export interface BookingComboboxProps {
|
|
2
|
+
value: string | null | undefined;
|
|
3
|
+
onChange: (value: string | null) => void;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
emptyText?: string;
|
|
6
|
+
disabled?: boolean;
|
|
7
|
+
className?: string;
|
|
8
|
+
triggerClassName?: string;
|
|
9
|
+
clearable?: boolean;
|
|
10
|
+
limit?: number;
|
|
11
|
+
}
|
|
12
|
+
export declare function BookingCombobox({ value, onChange, placeholder, emptyText, disabled, className, triggerClassName, clearable, limit, }: BookingComboboxProps): import("react/jsx-runtime").JSX.Element;
|
|
13
|
+
//# sourceMappingURL=booking-combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booking-combobox.d.ts","sourceRoot":"","sources":["../../src/components/booking-combobox.tsx"],"names":[],"mappings":"AAQA,MAAM,WAAW,oBAAoB;IACnC,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,OAAO,CAAA;IAClB,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,SAAS,CAAC,EAAE,OAAO,CAAA;IACnB,KAAK,CAAC,EAAE,MAAM,CAAA;CACf;AAkCD,wBAAgB,eAAe,CAAC,EAC9B,KAAK,EACL,QAAQ,EACR,WAAW,EACX,SAAS,EACT,QAAQ,EACR,SAAS,EACT,gBAAgB,EAChB,SAAgB,EAChB,KAAqB,GACtB,EAAE,oBAAoB,2CAiCtB"}
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
3
|
+
import { useBooking, useBookings } from "@voyantjs/bookings-react";
|
|
4
|
+
import { AsyncCombobox } from "@voyantjs/ui/components/async-combobox";
|
|
5
|
+
import * as React from "react";
|
|
6
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
7
|
+
const DEFAULT_LIMIT = 20;
|
|
8
|
+
function compact(parts) {
|
|
9
|
+
return parts.map((part) => part?.trim()).filter(Boolean);
|
|
10
|
+
}
|
|
11
|
+
function formatCustomer(booking) {
|
|
12
|
+
const name = compact([booking.contactFirstName, booking.contactLastName]).join(" ");
|
|
13
|
+
return name || booking.contactEmail || null;
|
|
14
|
+
}
|
|
15
|
+
function formatPrimaryItem(booking) {
|
|
16
|
+
return booking.items?.find((item) => item.title.trim())?.title ?? null;
|
|
17
|
+
}
|
|
18
|
+
function formatDateRange(booking) {
|
|
19
|
+
const start = booking.startDate ?? booking.startsAt;
|
|
20
|
+
const end = booking.endDate ?? booking.endsAt;
|
|
21
|
+
if (start && end)
|
|
22
|
+
return `${start} - ${end}`;
|
|
23
|
+
return start ?? end ?? null;
|
|
24
|
+
}
|
|
25
|
+
function formatBookingLabel(booking) {
|
|
26
|
+
return compact([booking.bookingNumber, formatCustomer(booking), formatPrimaryItem(booking)]).join(" - ");
|
|
27
|
+
}
|
|
28
|
+
function formatBookingSecondary(booking) {
|
|
29
|
+
return compact([formatDateRange(booking), booking.sellCurrency]).join(" - ");
|
|
30
|
+
}
|
|
31
|
+
export function BookingCombobox({ value, onChange, placeholder, emptyText, disabled, className, triggerClassName, clearable = true, limit = DEFAULT_LIMIT, }) {
|
|
32
|
+
const messages = useBookingsUiMessagesOrDefault().bookingCombobox;
|
|
33
|
+
const [search, setSearch] = React.useState("");
|
|
34
|
+
const listQuery = useBookings({
|
|
35
|
+
search: search || undefined,
|
|
36
|
+
limit,
|
|
37
|
+
});
|
|
38
|
+
const selectedQuery = useBooking(value ?? undefined, { enabled: Boolean(value) });
|
|
39
|
+
const selectedBooking = selectedQuery.data?.data ?? null;
|
|
40
|
+
const bookings = listQuery.data?.data ?? [];
|
|
41
|
+
return (_jsx(AsyncCombobox, { value: value ?? null, onChange: onChange, items: bookings, selectedItem: selectedBooking, getKey: (booking) => booking.id, getLabel: formatBookingLabel, getSecondary: formatBookingSecondary, onSearchChange: setSearch, placeholder: placeholder ?? messages.placeholder, emptyText: listQuery.isPending || selectedQuery.isPending
|
|
42
|
+
? messages.loading
|
|
43
|
+
: (emptyText ?? messages.empty), disabled: disabled, className: className, triggerClassName: triggerClassName, clearable: clearable }));
|
|
44
|
+
}
|
|
@@ -6,6 +6,14 @@ export interface BookingCreateDialogProps {
|
|
|
6
6
|
/** When provided, pre-selects this product and hides the product picker. */
|
|
7
7
|
defaultProductId?: string;
|
|
8
8
|
}
|
|
9
|
+
export interface BookingCreateFormProps {
|
|
10
|
+
onCreated?: (booking: BookingRecord) => void;
|
|
11
|
+
/** When provided, pre-selects this product and hides the product picker. */
|
|
12
|
+
defaultProductId?: string;
|
|
13
|
+
/** Gates data fetching and resets transient form state when false. */
|
|
14
|
+
enabled?: boolean;
|
|
15
|
+
onCancel?: () => void;
|
|
16
|
+
}
|
|
9
17
|
/**
|
|
10
18
|
* Operator booking-create dialog. Composes the booking-create picker
|
|
11
19
|
* sections — product, departure, rooms, person, shared-room, passengers,
|
|
@@ -18,4 +26,5 @@ export interface BookingCreateDialogProps {
|
|
|
18
26
|
* sections individually and assemble their own dialog instead of forking.
|
|
19
27
|
*/
|
|
20
28
|
export declare function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProductId, }: BookingCreateDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
29
|
+
export declare function BookingCreateForm({ onCreated, defaultProductId, enabled, onCancel, }: BookingCreateFormProps): import("react/jsx-runtime").JSX.Element;
|
|
21
30
|
//# sourceMappingURL=booking-create-dialog.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"booking-create-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-create-dialog.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAOnB,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"booking-create-dialog.d.ts","sourceRoot":"","sources":["../../src/components/booking-create-dialog.tsx"],"names":[],"mappings":"AAGA,OAAO,EACL,KAAK,aAAa,EAOnB,MAAM,0BAA0B,CAAA;AAwJjC,MAAM,WAAW,wBAAwB;IACvC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;IACzB,sEAAsE;IACtE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,QAAQ,CAAC,EAAE,MAAM,IAAI,CAAA;CACtB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,gBAAgB,GACjB,EAAE,wBAAwB,2CAqB1B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,SAAS,EACT,gBAAgB,EAChB,OAAc,EACd,QAAQ,GACT,EAAE,sBAAsB,2CAycxB"}
|
|
@@ -1,9 +1,8 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
3
3
|
import { useSlots, useSlotUnitAvailability } from "@voyantjs/availability-react";
|
|
4
4
|
import { useBookingQuickCreateMutation, useBookingStatusByIdMutation, } from "@voyantjs/bookings-react";
|
|
5
|
-
import {
|
|
6
|
-
import { Button, Checkbox, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, } from "@voyantjs/ui/components";
|
|
5
|
+
import { Button, Checkbox, Dialog, DialogContent, DialogHeader, DialogTitle, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Textarea, } from "@voyantjs/ui/components";
|
|
7
6
|
import { Loader2 } from "lucide-react";
|
|
8
7
|
import * as React from "react";
|
|
9
8
|
import { formatMessage, useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault, } from "../i18n/provider.js";
|
|
@@ -112,6 +111,13 @@ function passengersToRows(value) {
|
|
|
112
111
|
* sections individually and assemble their own dialog instead of forking.
|
|
113
112
|
*/
|
|
114
113
|
export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProductId, }) {
|
|
114
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
115
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.bookingCreateDialog.title }) }), _jsx(BookingCreateForm, { enabled: open, defaultProductId: defaultProductId, onCancel: () => onOpenChange(false), onCreated: (booking) => {
|
|
116
|
+
onOpenChange(false);
|
|
117
|
+
onCreated?.(booking);
|
|
118
|
+
} })] }) }));
|
|
119
|
+
}
|
|
120
|
+
export function BookingCreateForm({ onCreated, defaultProductId, enabled = true, onCancel, }) {
|
|
115
121
|
const [product, setProduct] = React.useState({
|
|
116
122
|
productId: defaultProductId ?? "",
|
|
117
123
|
optionId: null,
|
|
@@ -122,6 +128,7 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
122
128
|
const [sharedRoom, setSharedRoom] = React.useState(emptySharedRoomValue);
|
|
123
129
|
const [passengers, setPassengers] = React.useState(emptyPassengerListValue);
|
|
124
130
|
const [voucher, setVoucher] = React.useState(emptyVoucherPickerValue);
|
|
131
|
+
const [pricing, setPricing] = React.useState(null);
|
|
125
132
|
const [paymentSchedule, setPaymentSchedule] = React.useState(emptyPaymentScheduleValue);
|
|
126
133
|
const [notes, setNotes] = React.useState("");
|
|
127
134
|
/**
|
|
@@ -136,7 +143,7 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
136
143
|
const { formatDate } = useBookingsUiI18nOrDefault();
|
|
137
144
|
const messages = useBookingsUiMessagesOrDefault();
|
|
138
145
|
React.useEffect(() => {
|
|
139
|
-
if (!
|
|
146
|
+
if (!enabled) {
|
|
140
147
|
setProduct({ productId: defaultProductId ?? "", optionId: null });
|
|
141
148
|
setSlotId(null);
|
|
142
149
|
setRooms(emptyRoomsStepperValue);
|
|
@@ -144,6 +151,7 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
144
151
|
setSharedRoom(emptySharedRoomValue);
|
|
145
152
|
setPassengers(emptyPassengerListValue);
|
|
146
153
|
setVoucher(emptyVoucherPickerValue);
|
|
154
|
+
setPricing(null);
|
|
147
155
|
setPaymentSchedule(emptyPaymentScheduleValue);
|
|
148
156
|
setNotes("");
|
|
149
157
|
setConfirmAfterCreate(false);
|
|
@@ -154,7 +162,7 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
154
162
|
? prev
|
|
155
163
|
: { productId: defaultProductId, optionId: null });
|
|
156
164
|
}
|
|
157
|
-
}, [
|
|
165
|
+
}, [enabled, defaultProductId]);
|
|
158
166
|
// biome-ignore lint/correctness/useExhaustiveDependencies: intentionally only resets when product/option changes
|
|
159
167
|
React.useEffect(() => {
|
|
160
168
|
setSlotId(null);
|
|
@@ -164,7 +172,7 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
164
172
|
productId: product.productId || undefined,
|
|
165
173
|
status: "open",
|
|
166
174
|
limit: 100,
|
|
167
|
-
enabled:
|
|
175
|
+
enabled: enabled && Boolean(product.productId),
|
|
168
176
|
});
|
|
169
177
|
const slots = React.useMemo(() => {
|
|
170
178
|
const nowIso = new Date().toISOString();
|
|
@@ -190,7 +198,7 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
190
198
|
}, [formatDate, messages]);
|
|
191
199
|
const slotUnitAvailability = useSlotUnitAvailability({
|
|
192
200
|
slotId: slotId ?? undefined,
|
|
193
|
-
enabled:
|
|
201
|
+
enabled: enabled && Boolean(slotId),
|
|
194
202
|
});
|
|
195
203
|
const roomUnitOptions = React.useMemo(() => {
|
|
196
204
|
const units = slotUnitAvailability.data?.data ?? [];
|
|
@@ -214,7 +222,8 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
214
222
|
// Consumers hooking in real product data should override this by wrapping
|
|
215
223
|
// the component or swapping in their own currency-aware hook.
|
|
216
224
|
const currency = messages.bookingCreateDialog.labels.currency;
|
|
217
|
-
const
|
|
225
|
+
const pricingCurrency = pricing?.currency ?? currency;
|
|
226
|
+
const pricingTotalAmountCents = pricing?.confirmedAmountCents ?? undefined;
|
|
218
227
|
const quickCreateMutation = useBookingQuickCreateMutation();
|
|
219
228
|
const statusMutation = useBookingStatusByIdMutation();
|
|
220
229
|
const handleSubmit = async () => {
|
|
@@ -224,33 +233,35 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
224
233
|
return;
|
|
225
234
|
}
|
|
226
235
|
let resolvedPersonId = null;
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
}
|
|
233
|
-
resolvedPersonId = person.personId;
|
|
236
|
+
let resolvedOrganizationId = null;
|
|
237
|
+
if ((person.billTo ?? "person") === "person") {
|
|
238
|
+
if (!person.personId) {
|
|
239
|
+
setError(messages.bookingCreateDialog.validation.selectPerson);
|
|
240
|
+
return;
|
|
234
241
|
}
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
firstName: person.newPerson.firstName.trim(),
|
|
242
|
-
lastName: person.newPerson.lastName.trim(),
|
|
243
|
-
email: person.newPerson.email.trim() || null,
|
|
244
|
-
phone: person.newPerson.phone.trim() || null,
|
|
245
|
-
});
|
|
246
|
-
resolvedPersonId = created.id;
|
|
242
|
+
resolvedPersonId = person.personId;
|
|
243
|
+
}
|
|
244
|
+
else {
|
|
245
|
+
if (!person.organizationId) {
|
|
246
|
+
setError(messages.bookingCreateDialog.validation.selectOrganization);
|
|
247
|
+
return;
|
|
247
248
|
}
|
|
249
|
+
resolvedOrganizationId = person.organizationId;
|
|
250
|
+
}
|
|
251
|
+
try {
|
|
248
252
|
if (sharedRoom.enabled && sharedRoom.mode === "join" && !sharedRoom.groupId) {
|
|
249
253
|
setError(messages.bookingCreateDialog.validation.selectSharedRoomGroup);
|
|
250
254
|
return;
|
|
251
255
|
}
|
|
252
256
|
const bookingNumber = generateBookingNumber();
|
|
253
|
-
const
|
|
257
|
+
const confirmedSellAmountCents = pricing?.confirmedAmountCents ?? null;
|
|
258
|
+
const catalogSellAmountCents = pricing?.catalogAmountCents ?? null;
|
|
259
|
+
const priceOverrideReason = pricing?.priceOverrideReason.trim() ?? "";
|
|
260
|
+
if (pricing?.requiresReason) {
|
|
261
|
+
setError(messages.bookingCreateDialog.labels.breakdownOverrideReasonRequired);
|
|
262
|
+
return;
|
|
263
|
+
}
|
|
264
|
+
const paymentSchedules = paymentScheduleToRows(paymentSchedule, pricingCurrency, confirmedSellAmountCents);
|
|
254
265
|
const travelers = passengersToRows(passengers);
|
|
255
266
|
const voucherRedemption = voucher.picked && voucher.picked.remainingAmountCents != null
|
|
256
267
|
? {
|
|
@@ -263,7 +274,8 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
263
274
|
? {
|
|
264
275
|
action: "create",
|
|
265
276
|
kind: "shared_room",
|
|
266
|
-
label:
|
|
277
|
+
label: sharedRoom.groupLabel?.trim() ||
|
|
278
|
+
`${messages.bookingCreateDialog.labels.sharedRoomGeneratedLabelPrefix} - ${bookingNumber}`,
|
|
267
279
|
optionUnitId: product.optionId,
|
|
268
280
|
makeBookingPrimary: true,
|
|
269
281
|
}
|
|
@@ -277,8 +289,11 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
277
289
|
optionId: product.optionId,
|
|
278
290
|
slotId,
|
|
279
291
|
personId: resolvedPersonId,
|
|
280
|
-
organizationId:
|
|
292
|
+
organizationId: resolvedOrganizationId,
|
|
281
293
|
internalNotes: notes.trim() || null,
|
|
294
|
+
catalogSellAmountCents,
|
|
295
|
+
confirmedSellAmountCents,
|
|
296
|
+
priceOverrideReason: priceOverrideReason || null,
|
|
282
297
|
travelers: travelers.length > 0 ? travelers : undefined,
|
|
283
298
|
paymentSchedules: paymentSchedules.length > 0 ? paymentSchedules : undefined,
|
|
284
299
|
voucherRedemption,
|
|
@@ -308,70 +323,75 @@ export function BookingCreateDialog({ open, onOpenChange, onCreated, defaultProd
|
|
|
308
323
|
return;
|
|
309
324
|
}
|
|
310
325
|
}
|
|
311
|
-
onOpenChange(false);
|
|
312
326
|
onCreated?.(finalBooking);
|
|
313
327
|
}
|
|
314
328
|
catch (err) {
|
|
315
329
|
setError(err instanceof Error ? err.message : messages.bookingCreateDialog.validation.createFailed);
|
|
316
330
|
}
|
|
317
331
|
};
|
|
318
|
-
const isSubmitting = quickCreateMutation.isPending ||
|
|
319
|
-
return (
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
332
|
+
const isSubmitting = quickCreateMutation.isPending || statusMutation.isPending;
|
|
333
|
+
return (_jsxs(_Fragment, { children: [_jsxs("div", { className: "grid gap-4", children: [_jsx(ProductPickerSection, { value: product, onChange: setProduct, enabled: enabled, lockProduct: Boolean(defaultProductId), labels: {
|
|
334
|
+
optionNone: messages.bookingCreateDialog.labels.noSpecificOption,
|
|
335
|
+
} }), product.productId ? (_jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { children: messages.bookingCreateDialog.fields.departure }), _jsxs(Select, { value: slotId ?? "__none__", onValueChange: (v) => setSlotId(v === "__none__" ? null : (v ?? null)), children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: messages.bookingCreateDialog.placeholders.departure }) }), _jsxs(SelectContent, { children: [_jsx(SelectItem, { value: "__none__", children: messages.bookingCreateDialog.placeholders.departureNone }), slots.length === 0 ? (_jsx(SelectItem, { value: "__empty__", disabled: true, children: messages.bookingCreateDialog.placeholders.departureEmpty })) : (slots.map((slot) => (_jsx(SelectItem, { value: slot.id, children: formatSlotLabel(slot) }, slot.id))))] })] })] })) : null, slotId ? (_jsx(RoomsStepperSection, { value: rooms, onChange: setRooms, slotId: slotId, enabled: enabled, labels: {
|
|
336
|
+
heading: messages.bookingCreateDialog.labels.roomsHeading,
|
|
337
|
+
noSlot: messages.bookingCreateDialog.labels.roomsNoSlot,
|
|
338
|
+
noUnits: messages.bookingCreateDialog.labels.roomsNoUnits,
|
|
339
|
+
remaining: messages.bookingCreateDialog.labels.roomsRemaining,
|
|
340
|
+
unlimited: messages.bookingCreateDialog.labels.roomsUnlimited,
|
|
341
|
+
} })) : null, _jsx(PersonPickerSection, { value: person, onChange: setPerson, enabled: enabled, labels: {
|
|
342
|
+
createNewPerson: messages.bookingCreateDialog.labels.createNewPerson,
|
|
343
|
+
selectExistingPerson: messages.bookingCreateDialog.labels.selectExistingPerson,
|
|
344
|
+
organizationNone: messages.bookingCreateDialog.labels.organizationNone,
|
|
345
|
+
} }), _jsx(SharedRoomSection, { value: sharedRoom, onChange: setSharedRoom, productId: product.productId || undefined, enabled: enabled, labels: {
|
|
346
|
+
toggle: messages.bookingCreateDialog.labels.sharedRoomToggle,
|
|
347
|
+
createMode: messages.bookingCreateDialog.labels.sharedRoomCreateMode,
|
|
348
|
+
joinMode: messages.bookingCreateDialog.labels.sharedRoomJoinMode,
|
|
349
|
+
selectPlaceholder: messages.bookingCreateDialog.labels.sharedRoomSelectPlaceholder,
|
|
350
|
+
noGroups: messages.bookingCreateDialog.labels.sharedRoomNoGroups,
|
|
351
|
+
createHint: messages.bookingCreateDialog.labels.sharedRoomCreateHint,
|
|
352
|
+
} }), product.productId ? (_jsx(PassengersSection, { value: passengers, onChange: setPassengers, roomUnits: roomUnitOptions.length > 0 ? roomUnitOptions : undefined, labels: {
|
|
353
|
+
heading: messages.bookingCreateDialog.labels.passengerHeading,
|
|
354
|
+
addPassenger: messages.bookingCreateDialog.labels.addPassenger,
|
|
355
|
+
role: messages.bookingCreateDialog.labels.passengerRole,
|
|
356
|
+
roleLead: messages.bookingCreateDialog.labels.passengerLead,
|
|
357
|
+
roleAdult: messages.bookingCreateDialog.labels.passengerAdult,
|
|
358
|
+
roleChild: messages.bookingCreateDialog.labels.passengerChild,
|
|
359
|
+
roleInfant: messages.bookingCreateDialog.labels.passengerInfant,
|
|
360
|
+
room: messages.bookingCreateDialog.labels.passengerRoom,
|
|
361
|
+
noRoom: messages.bookingCreateDialog.labels.passengerNoRoom,
|
|
362
|
+
remove: messages.bookingCreateDialog.labels.passengerRemove,
|
|
363
|
+
empty: messages.bookingCreateDialog.labels.passengerEmpty,
|
|
364
|
+
} })) : null, product.productId ? (_jsx(PriceBreakdownSection, { productId: product.productId, optionId: product.optionId, unitQuantities: rooms.quantities, labels: {
|
|
365
|
+
heading: messages.bookingCreateDialog.labels.breakdownHeading,
|
|
366
|
+
total: messages.bookingCreateDialog.labels.breakdownTotal,
|
|
367
|
+
onRequest: messages.bookingCreateDialog.labels.breakdownOnRequest,
|
|
368
|
+
groupRate: messages.bookingCreateDialog.labels.breakdownGroupRate,
|
|
369
|
+
empty: messages.bookingCreateDialog.labels.breakdownEmpty,
|
|
370
|
+
noPricing: messages.bookingCreateDialog.labels.breakdownNoPricing,
|
|
371
|
+
confirmedTotal: messages.bookingCreateDialog.labels.breakdownConfirmedTotal,
|
|
372
|
+
manualTotal: messages.bookingCreateDialog.labels.breakdownManualTotal,
|
|
373
|
+
useCatalogTotal: messages.bookingCreateDialog.labels.breakdownUseCatalogTotal,
|
|
374
|
+
overrideReason: messages.bookingCreateDialog.labels.breakdownOverrideReason,
|
|
375
|
+
overrideReasonPlaceholder: messages.bookingCreateDialog.labels.breakdownOverrideReasonPlaceholder,
|
|
376
|
+
overrideReasonRequired: messages.bookingCreateDialog.labels.breakdownOverrideReasonRequired,
|
|
377
|
+
}, onChange: setPricing })) : null, _jsx(VoucherPickerSection, { value: voucher, onChange: setVoucher, currency: currency, labels: {
|
|
378
|
+
heading: messages.bookingCreateDialog.labels.voucherHeading,
|
|
379
|
+
codePlaceholder: messages.bookingCreateDialog.labels.voucherCodePlaceholder,
|
|
380
|
+
apply: messages.bookingCreateDialog.labels.voucherApply,
|
|
381
|
+
clear: messages.bookingCreateDialog.labels.voucherClear,
|
|
382
|
+
remainingLabel: messages.bookingCreateDialog.labels.voucherRemainingLabel,
|
|
383
|
+
invalidLabel: messages.bookingCreateDialog.labels.voucherInvalidLabel,
|
|
384
|
+
} }), _jsx(PaymentScheduleSection, { value: paymentSchedule, onChange: setPaymentSchedule, currency: pricingCurrency, totalAmountCents: pricingTotalAmountCents, labels: {
|
|
385
|
+
heading: messages.bookingCreateDialog.labels.paymentHeading,
|
|
386
|
+
modeUnpaid: messages.bookingCreateDialog.labels.paymentModeUnpaid,
|
|
387
|
+
modeFull: messages.bookingCreateDialog.labels.paymentModeFull,
|
|
388
|
+
modeAdvance: messages.bookingCreateDialog.labels.paymentModeAdvance,
|
|
389
|
+
modeSplit: messages.bookingCreateDialog.labels.paymentModeSplit,
|
|
390
|
+
dueDate: messages.bookingCreateDialog.labels.paymentDueDate,
|
|
391
|
+
amount: messages.bookingCreateDialog.labels.paymentAmount,
|
|
392
|
+
firstInstallment: messages.bookingCreateDialog.labels.paymentFirstInstallment,
|
|
393
|
+
secondInstallment: messages.bookingCreateDialog.labels.paymentSecondInstallment,
|
|
394
|
+
preset5050: messages.bookingCreateDialog.labels.paymentPreset5050,
|
|
395
|
+
unpaidHint: messages.bookingCreateDialog.labels.paymentUnpaidHint,
|
|
396
|
+
} }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.bookingCreateDialog.fields.internalNotes }), _jsx(Textarea, { value: notes, onChange: (e) => setNotes(e.target.value), placeholder: messages.bookingCreateDialog.placeholders.internalNotes })] }), _jsxs("div", { className: "flex items-start gap-2 rounded-md border p-3", children: [_jsx(Checkbox, { id: "new-booking-confirm-after-create", checked: confirmAfterCreate, onCheckedChange: (v) => setConfirmAfterCreate(v === true), className: "mt-0.5" }), _jsxs("div", { className: "flex flex-col gap-1", children: [_jsx(Label, { htmlFor: "new-booking-confirm-after-create", className: "cursor-pointer text-sm", children: messages.bookingCreateDialog.fields.confirmAfterCreate }), _jsx("p", { className: "text-xs text-muted-foreground", children: messages.bookingCreateDialog.fields.confirmAfterCreateHint })] })] }), error && _jsx("p", { className: "text-xs text-destructive", children: error })] }), _jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: onCancel, disabled: isSubmitting, children: messages.common.cancel }), _jsxs(Button, { type: "button", size: "sm", onClick: handleSubmit, disabled: isSubmitting || !product.productId, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), messages.bookingCreateDialog.actions.createDraftBooking] })] })] }));
|
|
377
397
|
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import type { BookingRecord } from "@voyantjs/bookings-react";
|
|
2
|
+
export interface BookingCreatePageProps {
|
|
3
|
+
onCreated?: (booking: BookingRecord) => void;
|
|
4
|
+
/** When provided, pre-selects this product and hides the product picker. */
|
|
5
|
+
defaultProductId?: string;
|
|
6
|
+
}
|
|
7
|
+
/**
|
|
8
|
+
* Full-page booking create surface for route-based booking creation.
|
|
9
|
+
*/
|
|
10
|
+
export declare function BookingCreatePage({ onCreated, defaultProductId }: BookingCreatePageProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=booking-create-page.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booking-create-page.d.ts","sourceRoot":"","sources":["../../src/components/booking-create-page.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,0BAA0B,CAAA;AAI7D,MAAM,WAAW,sBAAsB;IACrC,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IAC5C,4EAA4E;IAC5E,gBAAgB,CAAC,EAAE,MAAM,CAAA;CAC1B;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,EAAE,SAAS,EAAE,gBAAgB,EAAE,EAAE,sBAAsB,2CAgBxF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider.js";
|
|
4
|
+
import { BookingCreateForm } from "./booking-create-dialog.js";
|
|
5
|
+
/**
|
|
6
|
+
* Full-page booking create surface for route-based booking creation.
|
|
7
|
+
*/
|
|
8
|
+
export function BookingCreatePage({ onCreated, defaultProductId }) {
|
|
9
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
10
|
+
return (_jsxs("main", { className: "mx-auto flex w-full max-w-4xl 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: "text-2xl font-semibold tracking-normal", children: messages.bookingCreatePage.title }), _jsx("p", { className: "text-sm text-muted-foreground", children: messages.bookingCreatePage.description })] }), _jsx("section", { className: "flex flex-col gap-4", children: _jsx(BookingCreateForm, { onCreated: onCreated, defaultProductId: defaultProductId }) })] }));
|
|
11
|
+
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"booking-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/booking-detail-page.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AA2BjC,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAgBhD,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IAC9C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACrD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACnD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACtD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACpD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IAClD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACjD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;CACpD;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,kBAAkB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAA;IACrD,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IACnD,KAAK,CAAC,EAAE,sBAAsB,CAAA;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,SAAS,EACT,MAAM,EACN,MAAM,EACN,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,GACN,EAAE,sBAAsB,
|
|
1
|
+
{"version":3,"file":"booking-detail-page.d.ts","sourceRoot":"","sources":["../../src/components/booking-detail-page.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AA2BjC,OAAO,EAAE,KAAK,SAAS,EAAY,MAAM,OAAO,CAAA;AAgBhD,MAAM,WAAW,sBAAsB;IACrC,MAAM,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IAC9C,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACpD,aAAa,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACrD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACnD,cAAc,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACtD,YAAY,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACpD,UAAU,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IAClD,SAAS,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;IACjD,WAAW,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,SAAS,CAAA;CACpD;AAED,MAAM,WAAW,sBAAsB;IACrC,EAAE,EAAE,MAAM,CAAA;IACV,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,MAAM,CAAC,EAAE,MAAM,IAAI,CAAA;IACnB,YAAY,CAAC,EAAE,CAAC,QAAQ,EAAE,MAAM,KAAK,IAAI,CAAA;IACzC,kBAAkB,CAAC,EAAE,CAAC,cAAc,EAAE,MAAM,KAAK,IAAI,CAAA;IACrD,gBAAgB,CAAC,EAAE,CAAC,OAAO,EAAE,aAAa,KAAK,IAAI,CAAA;IACnD,KAAK,CAAC,EAAE,sBAAsB,CAAA;CAC/B;AAED,wBAAgB,iBAAiB,CAAC,EAChC,EAAE,EACF,SAAS,EACT,MAAM,EACN,MAAM,EACN,YAAY,EACZ,kBAAkB,EAClB,gBAAgB,EAChB,KAAK,GACN,EAAE,sBAAsB,2CA4PxB;AAED,wBAAgB,yBAAyB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,aAAa,CAAA;CAAE,2CAmChF"}
|
|
@@ -36,12 +36,15 @@ export function BookingDetailPage({ id, className, locale, onBack, onPersonOpen,
|
|
|
36
36
|
return (_jsxs("div", { className: cn("flex flex-col items-center justify-center gap-4 py-12", className), children: [_jsx("p", { className: "text-muted-foreground", children: detailMessages.notFound }), onBack ? (_jsx(Button, { variant: "outline", onClick: onBack, children: detailMessages.backToBookings })) : null] }));
|
|
37
37
|
}
|
|
38
38
|
const canCancel = ["draft", "on_hold", "confirmed", "in_progress"].includes(booking.status);
|
|
39
|
+
const sellHint = booking.priceOverride?.isManual
|
|
40
|
+
? `${detailMessages.summaryPriceOverride}: ${booking.priceOverride.reason}`
|
|
41
|
+
: booking.sellCurrency;
|
|
39
42
|
return (_jsxs("div", { "data-slot": "booking-detail-page", className: cn("flex flex-col gap-6 p-6", className), children: [_jsxs("div", { className: "flex items-center gap-1.5 text-sm text-muted-foreground", children: [onBack ? (_jsx("button", { type: "button", onClick: onBack, className: "transition-colors hover:text-foreground", children: detailMessages.breadcrumbBookings })) : (_jsx("span", { children: detailMessages.breadcrumbBookings })), _jsx(ChevronRight, { className: "h-3.5 w-3.5", "aria-hidden": "true" }), _jsx("span", { className: "font-normal text-foreground", children: booking.bookingNumber })] }), _jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { className: "flex items-center gap-3", children: [_jsx("h1", { className: "text-2xl font-bold tracking-tight", children: booking.bookingNumber }), _jsx(Badge, { variant: bookingStatusBadgeVariant[booking.status], children: getBookingStatusLabel(booking.status, messages.common.bookingStatusLabels) })] }), _jsxs(ActionMenu, { children: [_jsxs(DropdownMenuItem, { onClick: () => setEditOpen(true), children: [_jsx(Pencil, { className: "h-4 w-4", "aria-hidden": "true" }), detailMessages.editAction] }), _jsxs(DropdownMenuItem, { onClick: () => setStatusDialogOpen(true), children: [_jsx(RefreshCw, { className: "h-4 w-4", "aria-hidden": "true" }), detailMessages.changeStatusAction] }), canCancel ? (_jsxs(DropdownMenuItem, { onClick: () => setCancelDialogOpen(true), children: [_jsx(Ban, { className: "h-4 w-4", "aria-hidden": "true" }), detailMessages.cancelBookingAction] })) : null, _jsx(DropdownMenuSeparator, {}), _jsxs(DropdownMenuItem, { variant: "destructive", disabled: remove.isPending, onClick: async () => {
|
|
40
43
|
if (confirm(detailMessages.deleteConfirm)) {
|
|
41
44
|
await remove.mutateAsync(id);
|
|
42
45
|
onBack?.();
|
|
43
46
|
}
|
|
44
|
-
}, children: [_jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" }), detailMessages.deleteAction] })] })] }), slots?.header?.(booking), _jsx(Card, { children: _jsxs(CardContent, { className: "grid grid-cols-2 gap-6 py-6 sm:grid-cols-4", children: [_jsx(SummaryStat, { label: detailMessages.summarySell, value: formatAmount(booking.sellAmountCents, booking.sellCurrency, resolvedLocale, detailMessages.noValue), hint:
|
|
47
|
+
}, children: [_jsx(Trash2, { className: "h-4 w-4", "aria-hidden": "true" }), detailMessages.deleteAction] })] })] }), slots?.header?.(booking), _jsx(Card, { children: _jsxs(CardContent, { className: "grid grid-cols-2 gap-6 py-6 sm:grid-cols-4", children: [_jsx(SummaryStat, { label: detailMessages.summarySell, value: formatAmount(booking.sellAmountCents, booking.sellCurrency, resolvedLocale, detailMessages.noValue), hint: sellHint }), _jsx(SummaryStat, { label: detailMessages.summaryCostMargin, value: formatAmount(booking.costAmountCents, booking.sellCurrency, resolvedLocale, detailMessages.noValue), hint: formatMargin(booking.marginPercent, detailMessages.noValue) }), _jsx(SummaryStat, { label: detailMessages.summaryDates, value: booking.startDate
|
|
45
48
|
? `${formatDate(booking.startDate, resolvedLocale, detailMessages.noValue)} - ${formatDate(booking.endDate, resolvedLocale, detailMessages.noValue)}`
|
|
46
49
|
: detailMessages.tbd, icon: _jsx(Calendar, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) }), _jsx(SummaryStat, { label: detailMessages.summaryTravelers, value: booking.pax != null ? String(booking.pax) : detailMessages.noValue, icon: _jsx(Users, { className: "h-3.5 w-3.5", "aria-hidden": "true" }) }), booking.personId ? (_jsx(SummaryLink, { label: detailMessages.summaryPerson, id: booking.personId, onOpen: onPersonOpen })) : null, booking.organizationId ? (_jsx(SummaryLink, { label: detailMessages.summaryOrganization, id: booking.organizationId, onOpen: onOrganizationOpen })) : null, _jsx(SummaryStat, { label: detailMessages.summaryCreated, value: formatDate(booking.createdAt, resolvedLocale, detailMessages.noValue) }), _jsx(SummaryStat, { label: detailMessages.summaryUpdated, value: formatDate(booking.updatedAt, resolvedLocale, detailMessages.noValue) })] }) }), slots?.afterSummary?.(booking), _jsxs(Tabs, { defaultValue: "overview", children: [_jsxs(TabsList, { className: "w-full justify-start", children: [_jsx(TabsTrigger, { value: "overview", children: detailMessages.tabOverview }), _jsx(TabsTrigger, { value: "travelers", children: detailMessages.tabTravelers }), _jsx(TabsTrigger, { value: "finance", children: detailMessages.tabFinance }), _jsx(TabsTrigger, { value: "suppliers", children: detailMessages.tabSuppliers }), _jsx(TabsTrigger, { value: "documents", children: detailMessages.tabDocuments }), _jsx(TabsTrigger, { value: "activity", children: detailMessages.tabActivity })] }), _jsxs(TabsContent, { value: "overview", className: "mt-4 flex flex-col gap-6", children: [slots?.overviewStart?.(booking), _jsx(BookingItemList, { bookingId: id }), _jsx(BookingGroupSection, { bookingId: id }), visibleInternalNotes(booking.internalNotes) ? (_jsx(Card, { children: _jsxs(CardContent, { className: "py-5", children: [_jsx("p", { className: "mb-1 text-xs font-medium text-muted-foreground", children: detailMessages.internalNotesLabel }), _jsx("p", { className: "whitespace-pre-wrap text-sm", children: visibleInternalNotes(booking.internalNotes) })] }) })) : null, slots?.overviewEnd?.(booking)] }), _jsxs(TabsContent, { value: "travelers", className: "mt-4 flex flex-col gap-6", children: [slots?.travelersStart?.(booking), _jsx(BookingBillingContextCard, { booking: booking }), _jsx(TravelerList, { bookingId: id, autoReveal: true })] }), _jsxs(TabsContent, { value: "finance", className: "mt-4 flex flex-col gap-6", children: [onCollectPayment ? (_jsx("div", { className: "flex items-center justify-end", children: _jsx(Button, { onClick: () => onCollectPayment(booking), children: detailMessages.collectPaymentAction }) })) : null, slots?.financeStart?.(booking), _jsx(BookingPaymentsSummary, { bookingId: id, variant: "admin" }), _jsx(BookingPaymentScheduleList, { bookingId: id }), _jsx(BookingGuaranteeList, { bookingId: id }), slots?.financeEnd?.(booking)] }), _jsx(TabsContent, { value: "suppliers", className: "mt-4", children: _jsx(SupplierStatusList, { bookingId: id }) }), _jsx(TabsContent, { value: "documents", className: "mt-4 flex flex-col gap-4", children: slots?.documents ? (slots.documents(booking)) : (_jsx("p", { className: "rounded-md border border-dashed p-4 text-sm text-muted-foreground", children: detailMessages.documentsSlotEmpty })) }), _jsxs(TabsContent, { value: "activity", className: "mt-4 flex flex-col gap-6", children: [_jsx(BookingActivityTimeline, { bookingId: id }), _jsx(BookingNotes, { bookingId: id }), slots?.activityEnd?.(booking)] })] }), _jsx(BookingDialog, { open: editOpen, onOpenChange: setEditOpen, booking: booking }), _jsx(StatusChangeDialog, { open: statusDialogOpen, onOpenChange: setStatusDialogOpen, bookingId: id, currentStatus: booking.status }), _jsx(BookingCancellationDialog, { open: cancelDialogOpen, onOpenChange: setCancelDialogOpen, booking: booking })] }));
|
|
47
50
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"booking-item-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-list.tsx"],"names":[],"mappings":"AAwBA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE,oBAAoB,
|
|
1
|
+
{"version":3,"file":"booking-item-list.d.ts","sourceRoot":"","sources":["../../src/components/booking-item-list.tsx"],"names":[],"mappings":"AAwBA,MAAM,WAAW,oBAAoB;IACnC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,eAAe,CAAC,EAAE,SAAS,EAAE,EAAE,oBAAoB,2CAqLlE"}
|
|
@@ -32,7 +32,9 @@ export function BookingItemList({ bookingId }) {
|
|
|
32
32
|
return (_jsxs(React.Fragment, { children: [_jsxs("tr", { className: "cursor-pointer border-b hover:bg-muted/30", onClick: () => setExpandedItemId(isExpanded ? null : item.id), children: [_jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: (e) => {
|
|
33
33
|
e.stopPropagation();
|
|
34
34
|
setExpandedItemId(isExpanded ? null : item.id);
|
|
35
|
-
}, className: "text-muted-foreground hover:text-foreground", "aria-label": isExpanded
|
|
35
|
+
}, className: "text-muted-foreground hover:text-foreground", "aria-label": isExpanded
|
|
36
|
+
? messages.bookingItemList.actions.collapseItem
|
|
37
|
+
: messages.bookingItemList.actions.expandItem, children: isExpanded ? (_jsx(ChevronDown, { className: "h-3.5 w-3.5" })) : (_jsx(ChevronRight, { className: "h-3.5 w-3.5" })) }) }), _jsx("td", { className: "p-2 font-medium", children: item.title }), _jsx("td", { className: "p-2", children: messages.bookingItemDialog.itemTypeLabels[item.itemType] }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: statusVariant[item.status] ?? "secondary", children: messages.bookingItemDialog.itemStatusLabels[item.status] }) }), _jsx("td", { className: "p-2 text-right font-mono", children: item.quantity }), _jsx("td", { className: "p-2 text-right font-mono", children: item.totalSellAmountCents == null
|
|
36
38
|
? messages.bookingItemList.values.totalUnavailable
|
|
37
39
|
: formatCurrency(item.totalSellAmountCents / 100, item.sellCurrency) }), _jsx("td", { className: "p-2 text-right font-mono text-muted-foreground", children: item.totalCostAmountCents == null || !item.costCurrency
|
|
38
40
|
? messages.bookingItemList.values.costUnavailable
|
|
@@ -41,12 +43,12 @@ export function BookingItemList({ bookingId }) {
|
|
|
41
43
|
e.stopPropagation();
|
|
42
44
|
setEditing(item);
|
|
43
45
|
setDialogOpen(true);
|
|
44
|
-
}, className: "text-muted-foreground hover:text-foreground", "aria-label":
|
|
46
|
+
}, className: "text-muted-foreground hover:text-foreground", "aria-label": messages.bookingItemList.actions.editItem, children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: (e) => {
|
|
45
47
|
e.stopPropagation();
|
|
46
48
|
if (confirm(messages.bookingItemList.actions.deleteConfirm)) {
|
|
47
49
|
remove.mutate(item.id);
|
|
48
50
|
}
|
|
49
|
-
}, className: "text-muted-foreground hover:text-destructive", "aria-label":
|
|
51
|
+
}, className: "text-muted-foreground hover:text-destructive", "aria-label": messages.bookingItemList.actions.deleteItem, children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }), isExpanded && (_jsxs("tr", { className: "border-b last:border-b-0 bg-muted/10", children: [_jsx("td", {}), _jsx("td", { colSpan: 8, className: "p-3", children: _jsx(ItemDetailPanel, { bookingId: bookingId, item: item }) })] }))] }, item.id));
|
|
50
52
|
}) })] }) })) }), _jsx(BookingItemDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
|
|
51
53
|
setDialogOpen(nextOpen);
|
|
52
54
|
if (!nextOpen) {
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
export declare const BOOKING_STATUS_ALL = "__all__";
|
|
2
|
+
export interface BookingListFiltersPopoverProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
activeFilterCount: number;
|
|
6
|
+
status: string;
|
|
7
|
+
onStatusChange: (status: string) => void;
|
|
8
|
+
productId: string | null;
|
|
9
|
+
onProductIdChange: (productId: string | null) => void;
|
|
10
|
+
optionId: string | null;
|
|
11
|
+
onOptionIdChange: (optionId: string | null) => void;
|
|
12
|
+
supplierId: string | null;
|
|
13
|
+
onSupplierIdChange: (supplierId: string | null) => void;
|
|
14
|
+
productCategoryId: string | null;
|
|
15
|
+
onProductCategoryIdChange: (productCategoryId: string | null) => void;
|
|
16
|
+
personId: string | null;
|
|
17
|
+
onPersonIdChange: (personId: string | null) => void;
|
|
18
|
+
organizationId: string | null;
|
|
19
|
+
onOrganizationIdChange: (organizationId: string | null) => void;
|
|
20
|
+
dateRange: {
|
|
21
|
+
from: string | null;
|
|
22
|
+
to: string | null;
|
|
23
|
+
} | null;
|
|
24
|
+
onDateRangeChange: (dateRange: {
|
|
25
|
+
from: string | null;
|
|
26
|
+
to: string | null;
|
|
27
|
+
} | null) => void;
|
|
28
|
+
paxMin: string;
|
|
29
|
+
onPaxMinChange: (paxMin: string) => void;
|
|
30
|
+
paxMax: string;
|
|
31
|
+
onPaxMaxChange: (paxMax: string) => void;
|
|
32
|
+
onFiltersChanged: () => void;
|
|
33
|
+
}
|
|
34
|
+
export declare function BookingListFiltersPopover({ open, onOpenChange, activeFilterCount, status, onStatusChange, productId, onProductIdChange, optionId, onOptionIdChange, supplierId, onSupplierIdChange, productCategoryId, onProductCategoryIdChange, personId, onPersonIdChange, organizationId, onOrganizationIdChange, dateRange, onDateRangeChange, paxMin, onPaxMinChange, paxMax, onPaxMaxChange, onFiltersChanged, }: BookingListFiltersPopoverProps): import("react/jsx-runtime").JSX.Element;
|
|
35
|
+
//# sourceMappingURL=booking-list-filters.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"booking-list-filters.d.ts","sourceRoot":"","sources":["../../src/components/booking-list-filters.tsx"],"names":[],"mappings":"AAgCA,eAAO,MAAM,kBAAkB,YAAY,CAAA;AAE3C,MAAM,WAAW,8BAA8B;IAC7C,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,iBAAiB,EAAE,MAAM,CAAA;IACzB,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACrD,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACnD,UAAU,EAAE,MAAM,GAAG,IAAI,CAAA;IACzB,kBAAkB,EAAE,CAAC,UAAU,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACvD,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAA;IAChC,yBAAyB,EAAE,CAAC,iBAAiB,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACrE,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,gBAAgB,EAAE,CAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACnD,cAAc,EAAE,MAAM,GAAG,IAAI,CAAA;IAC7B,sBAAsB,EAAE,CAAC,cAAc,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IAC/D,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,CAAA;IAC5D,iBAAiB,EAAE,CAAC,SAAS,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,EAAE,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,GAAG,IAAI,KAAK,IAAI,CAAA;IACzF,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,MAAM,EAAE,MAAM,CAAA;IACd,cAAc,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAA;IACxC,gBAAgB,EAAE,MAAM,IAAI,CAAA;CAC7B;AAED,wBAAgB,yBAAyB,CAAC,EACxC,IAAI,EACJ,YAAY,EACZ,iBAAiB,EACjB,MAAM,EACN,cAAc,EACd,SAAS,EACT,iBAAiB,EACjB,QAAQ,EACR,gBAAgB,EAChB,UAAU,EACV,kBAAkB,EAClB,iBAAiB,EACjB,yBAAyB,EACzB,QAAQ,EACR,gBAAgB,EAChB,cAAc,EACd,sBAAsB,EACtB,SAAS,EACT,iBAAiB,EACjB,MAAM,EACN,cAAc,EACd,MAAM,EACN,cAAc,EACd,gBAAgB,GACjB,EAAE,8BAA8B,2CAuShC"}
|