@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.
Files changed (55) hide show
  1. package/dist/components/booking-combobox.d.ts +13 -0
  2. package/dist/components/booking-combobox.d.ts.map +1 -0
  3. package/dist/components/booking-combobox.js +44 -0
  4. package/dist/components/booking-create-dialog.d.ts +9 -0
  5. package/dist/components/booking-create-dialog.d.ts.map +1 -1
  6. package/dist/components/booking-create-dialog.js +110 -90
  7. package/dist/components/booking-create-page.d.ts +11 -0
  8. package/dist/components/booking-create-page.d.ts.map +1 -0
  9. package/dist/components/booking-create-page.js +11 -0
  10. package/dist/components/booking-detail-page.d.ts.map +1 -1
  11. package/dist/components/booking-detail-page.js +4 -1
  12. package/dist/components/booking-item-list.d.ts.map +1 -1
  13. package/dist/components/booking-item-list.js +5 -3
  14. package/dist/components/booking-list-filters.d.ts +35 -0
  15. package/dist/components/booking-list-filters.d.ts.map +1 -0
  16. package/dist/components/booking-list-filters.js +148 -0
  17. package/dist/components/booking-list.d.ts.map +1 -1
  18. package/dist/components/booking-list.js +30 -46
  19. package/dist/components/person-picker-section.d.ts +15 -7
  20. package/dist/components/person-picker-section.d.ts.map +1 -1
  21. package/dist/components/person-picker-section.js +100 -21
  22. package/dist/components/price-breakdown-section.d.ts +16 -1
  23. package/dist/components/price-breakdown-section.d.ts.map +1 -1
  24. package/dist/components/price-breakdown-section.js +36 -5
  25. package/dist/components/product-picker-section.d.ts.map +1 -1
  26. package/dist/components/product-picker-section.js +38 -4
  27. package/dist/components/shared-room-section.d.ts +9 -8
  28. package/dist/components/shared-room-section.d.ts.map +1 -1
  29. package/dist/components/shared-room-section.js +67 -14
  30. package/dist/components/traveler-list.d.ts.map +1 -1
  31. package/dist/components/traveler-list.js +27 -16
  32. package/dist/i18n/en.d.ts +284 -1
  33. package/dist/i18n/en.d.ts.map +1 -1
  34. package/dist/i18n/en.js +300 -17
  35. package/dist/i18n/messages.d.ts +255 -1
  36. package/dist/i18n/messages.d.ts.map +1 -1
  37. package/dist/i18n/provider.d.ts +568 -2
  38. package/dist/i18n/provider.d.ts.map +1 -1
  39. package/dist/i18n/ro.d.ts +284 -1
  40. package/dist/i18n/ro.d.ts.map +1 -1
  41. package/dist/i18n/ro.js +301 -18
  42. package/dist/index.d.ts +3 -1
  43. package/dist/index.d.ts.map +1 -1
  44. package/dist/index.js +3 -1
  45. package/dist/journey/components/booking-journey.d.ts.map +1 -1
  46. package/dist/journey/components/booking-journey.js +22 -13
  47. package/dist/journey/components/contract-preview-dialog.d.ts.map +1 -1
  48. package/dist/journey/components/contract-preview-dialog.js +9 -4
  49. package/dist/journey/components/journey-steps.d.ts.map +1 -1
  50. package/dist/journey/components/journey-steps.js +94 -72
  51. package/dist/journey/components/side-panel.d.ts.map +1 -1
  52. package/dist/journey/components/side-panel.js +58 -35
  53. package/dist/journey/components/step-header.d.ts.map +1 -1
  54. package/dist/journey/components/step-header.js +3 -19
  55. 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;AA2JjC,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;;;;;;;;;;GAUG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,gBAAgB,GACjB,EAAE,wBAAwB,2CA6b1B"}
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 { usePersonMutation } from "@voyantjs/crm-react";
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 (!open) {
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
- }, [open, defaultProductId]);
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: open && Boolean(product.productId),
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: open && Boolean(slotId),
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 { create: createPerson } = usePersonMutation();
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
- try {
228
- if (person.mode === "existing") {
229
- if (!person.personId) {
230
- setError(messages.bookingCreateDialog.validation.selectPerson);
231
- return;
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
- else {
236
- if (!person.newPerson.firstName.trim() || !person.newPerson.lastName.trim()) {
237
- setError(messages.bookingCreateDialog.validation.firstAndLastNameRequired);
238
- return;
239
- }
240
- const created = await createPerson.mutateAsync({
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 paymentSchedules = paymentScheduleToRows(paymentSchedule, currency, null);
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: `${messages.bookingCreateDialog.labels.sharedRoomGeneratedLabelPrefix} - ${bookingNumber}`,
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: person.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 || createPerson.isPending || statusMutation.isPending;
319
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.bookingCreateDialog.title }) }), _jsxs(DialogBody, { className: "grid gap-4", children: [_jsx(ProductPickerSection, { value: product, onChange: setProduct, enabled: open, lockProduct: Boolean(defaultProductId), labels: {
320
- optionNone: messages.bookingCreateDialog.labels.noSpecificOption,
321
- } }), 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: open, labels: {
322
- heading: messages.bookingCreateDialog.labels.roomsHeading,
323
- noSlot: messages.bookingCreateDialog.labels.roomsNoSlot,
324
- noUnits: messages.bookingCreateDialog.labels.roomsNoUnits,
325
- remaining: messages.bookingCreateDialog.labels.roomsRemaining,
326
- unlimited: messages.bookingCreateDialog.labels.roomsUnlimited,
327
- } })) : null, _jsx(PersonPickerSection, { value: person, onChange: setPerson, enabled: open, labels: {
328
- createNewPerson: messages.bookingCreateDialog.labels.createNewPerson,
329
- selectExistingPerson: messages.bookingCreateDialog.labels.selectExistingPerson,
330
- organizationNone: messages.bookingCreateDialog.labels.organizationNone,
331
- } }), _jsx(SharedRoomSection, { value: sharedRoom, onChange: setSharedRoom, productId: product.productId || undefined, enabled: open, labels: {
332
- toggle: messages.bookingCreateDialog.labels.sharedRoomToggle,
333
- createMode: messages.bookingCreateDialog.labels.sharedRoomCreateMode,
334
- joinMode: messages.bookingCreateDialog.labels.sharedRoomJoinMode,
335
- selectPlaceholder: messages.bookingCreateDialog.labels.sharedRoomSelectPlaceholder,
336
- noGroups: messages.bookingCreateDialog.labels.sharedRoomNoGroups,
337
- createHint: messages.bookingCreateDialog.labels.sharedRoomCreateHint,
338
- } }), product.productId ? (_jsx(PassengersSection, { value: passengers, onChange: setPassengers, roomUnits: roomUnitOptions.length > 0 ? roomUnitOptions : undefined, labels: {
339
- heading: messages.bookingCreateDialog.labels.passengerHeading,
340
- addPassenger: messages.bookingCreateDialog.labels.addPassenger,
341
- role: messages.bookingCreateDialog.labels.passengerRole,
342
- roleLead: messages.bookingCreateDialog.labels.passengerLead,
343
- roleAdult: messages.bookingCreateDialog.labels.passengerAdult,
344
- roleChild: messages.bookingCreateDialog.labels.passengerChild,
345
- roleInfant: messages.bookingCreateDialog.labels.passengerInfant,
346
- room: messages.bookingCreateDialog.labels.passengerRoom,
347
- noRoom: messages.bookingCreateDialog.labels.passengerNoRoom,
348
- remove: messages.bookingCreateDialog.labels.passengerRemove,
349
- empty: messages.bookingCreateDialog.labels.passengerEmpty,
350
- } })) : null, product.productId ? (_jsx(PriceBreakdownSection, { productId: product.productId, optionId: product.optionId, unitQuantities: rooms.quantities, labels: {
351
- heading: messages.bookingCreateDialog.labels.breakdownHeading,
352
- total: messages.bookingCreateDialog.labels.breakdownTotal,
353
- onRequest: messages.bookingCreateDialog.labels.breakdownOnRequest,
354
- groupRate: messages.bookingCreateDialog.labels.breakdownGroupRate,
355
- empty: messages.bookingCreateDialog.labels.breakdownEmpty,
356
- noPricing: messages.bookingCreateDialog.labels.breakdownNoPricing,
357
- } })) : null, _jsx(VoucherPickerSection, { value: voucher, onChange: setVoucher, currency: currency, labels: {
358
- heading: messages.bookingCreateDialog.labels.voucherHeading,
359
- codePlaceholder: messages.bookingCreateDialog.labels.voucherCodePlaceholder,
360
- apply: messages.bookingCreateDialog.labels.voucherApply,
361
- clear: messages.bookingCreateDialog.labels.voucherClear,
362
- remainingLabel: messages.bookingCreateDialog.labels.voucherRemainingLabel,
363
- invalidLabel: messages.bookingCreateDialog.labels.voucherInvalidLabel,
364
- } }), _jsx(PaymentScheduleSection, { value: paymentSchedule, onChange: setPaymentSchedule, currency: currency, labels: {
365
- heading: messages.bookingCreateDialog.labels.paymentHeading,
366
- modeUnpaid: messages.bookingCreateDialog.labels.paymentModeUnpaid,
367
- modeFull: messages.bookingCreateDialog.labels.paymentModeFull,
368
- modeAdvance: messages.bookingCreateDialog.labels.paymentModeAdvance,
369
- modeSplit: messages.bookingCreateDialog.labels.paymentModeSplit,
370
- dueDate: messages.bookingCreateDialog.labels.paymentDueDate,
371
- amount: messages.bookingCreateDialog.labels.paymentAmount,
372
- firstInstallment: messages.bookingCreateDialog.labels.paymentFirstInstallment,
373
- secondInstallment: messages.bookingCreateDialog.labels.paymentSecondInstallment,
374
- preset5050: messages.bookingCreateDialog.labels.paymentPreset5050,
375
- unpaidHint: messages.bookingCreateDialog.labels.paymentUnpaidHint,
376
- } }), _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: "quickbook-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: "quickbook-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(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), 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] })] })] }) }));
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,2CAyPxB;AAED,wBAAgB,yBAAyB,CAAC,EAAE,OAAO,EAAE,EAAE;IAAE,OAAO,EAAE,aAAa,CAAA;CAAE,2CAmChF"}
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: booking.sellCurrency }), _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
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,2CAiLlE"}
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 ? "Collapse item" : "Expand item", 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
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": "Edit item", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: (e) => {
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": "Delete item", 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));
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"}