@voyantjs/bookings-ui 0.62.3 → 0.63.1

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 (35) hide show
  1. package/README.md +18 -21
  2. package/dist/components/booking-create-dialog.d.ts.map +1 -1
  3. package/dist/components/booking-create-dialog.js +8 -2
  4. package/dist/components/booking-detail-page.d.ts +32 -1
  5. package/dist/components/booking-detail-page.d.ts.map +1 -1
  6. package/dist/components/booking-detail-page.js +53 -10
  7. package/dist/components/booking-item-list.d.ts.map +1 -1
  8. package/dist/components/booking-item-list.js +18 -5
  9. package/dist/components/booking-list-filters.d.ts +7 -1
  10. package/dist/components/booking-list-filters.d.ts.map +1 -1
  11. package/dist/components/booking-list-filters.js +44 -2
  12. package/dist/components/booking-list.d.ts.map +1 -1
  13. package/dist/components/booking-list.js +21 -7
  14. package/dist/components/option-units-stepper-section.d.ts +11 -1
  15. package/dist/components/option-units-stepper-section.d.ts.map +1 -1
  16. package/dist/components/option-units-stepper-section.js +41 -14
  17. package/dist/components/supplier-status-list.d.ts.map +1 -1
  18. package/dist/components/supplier-status-list.js +46 -9
  19. package/dist/i18n/en.d.ts +10 -19
  20. package/dist/i18n/en.d.ts.map +1 -1
  21. package/dist/i18n/en.js +13 -22
  22. package/dist/i18n/messages.d.ts +10 -8
  23. package/dist/i18n/messages.d.ts.map +1 -1
  24. package/dist/i18n/provider.d.ts +20 -38
  25. package/dist/i18n/provider.d.ts.map +1 -1
  26. package/dist/i18n/ro.d.ts +10 -19
  27. package/dist/i18n/ro.d.ts.map +1 -1
  28. package/dist/i18n/ro.js +13 -22
  29. package/dist/index.d.ts +1 -2
  30. package/dist/index.d.ts.map +1 -1
  31. package/dist/index.js +0 -1
  32. package/package.json +30 -30
  33. package/dist/components/booking-workspace-page.d.ts +0 -61
  34. package/dist/components/booking-workspace-page.d.ts.map +0 -1
  35. package/dist/components/booking-workspace-page.js +0 -113
@@ -19,9 +19,10 @@ const SORTABLE_COLUMNS = {
19
19
  pax: "pax",
20
20
  startDate: "startDate",
21
21
  endDate: "endDate",
22
+ createdAt: "createdAt",
22
23
  };
23
24
  const SKELETON_ROW_COUNT = 6;
24
- const TABLE_COLUMN_COUNT = 7;
25
+ const TABLE_COLUMN_COUNT = 9;
25
26
  export function BookingList({ pageSize = 25, onSelectBooking, onCreateBooking, headerActions, } = {}) {
26
27
  const [search, setSearch] = React.useState("");
27
28
  const [status, setStatus] = React.useState(BOOKING_STATUS_ALL);
@@ -31,6 +32,7 @@ export function BookingList({ pageSize = 25, onSelectBooking, onCreateBooking, h
31
32
  const [productCategoryId, setProductCategoryId] = React.useState(null);
32
33
  const [personId, setPersonId] = React.useState(null);
33
34
  const [organizationId, setOrganizationId] = React.useState(null);
35
+ const [availabilitySlotId, setAvailabilitySlotId] = React.useState(null);
34
36
  const [dateRange, setDateRange] = React.useState(null);
35
37
  const [paxMin, setPaxMin] = React.useState("");
36
38
  const [paxMax, setPaxMax] = React.useState("");
@@ -49,6 +51,7 @@ export function BookingList({ pageSize = 25, onSelectBooking, onCreateBooking, h
49
51
  status: status === BOOKING_STATUS_ALL ? undefined : status,
50
52
  productId: productId ?? undefined,
51
53
  optionId: optionId ?? undefined,
54
+ availabilitySlotId: availabilitySlotId ?? undefined,
52
55
  supplierId: supplierId ?? undefined,
53
56
  productCategoryId: productCategoryId ?? undefined,
54
57
  personId: personId ?? undefined,
@@ -93,6 +96,7 @@ export function BookingList({ pageSize = 25, onSelectBooking, onCreateBooking, h
93
96
  const activeFilterCount = (status !== BOOKING_STATUS_ALL ? 1 : 0) +
94
97
  (productId !== null ? 1 : 0) +
95
98
  (optionId !== null ? 1 : 0) +
99
+ (availabilitySlotId !== null ? 1 : 0) +
96
100
  (supplierId !== null ? 1 : 0) +
97
101
  (productCategoryId !== null ? 1 : 0) +
98
102
  (personId !== null ? 1 : 0) +
@@ -105,6 +109,7 @@ export function BookingList({ pageSize = 25, onSelectBooking, onCreateBooking, h
105
109
  setStatus(BOOKING_STATUS_ALL);
106
110
  setProductId(null);
107
111
  setOptionId(null);
112
+ setAvailabilitySlotId(null);
108
113
  setSupplierId(null);
109
114
  setProductCategoryId(null);
110
115
  setPersonId(null);
@@ -120,19 +125,23 @@ export function BookingList({ pageSize = 25, onSelectBooking, onCreateBooking, h
120
125
  return (_jsxs("div", { "data-slot": "booking-list", className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex flex-wrap items-center gap-2", children: [_jsxs("div", { className: "relative min-w-[14rem] flex-1", children: [_jsx(Label, { htmlFor: "bookings-search", className: "sr-only", children: messages.bookingList.searchPlaceholder }), _jsx(Search, { className: "absolute left-3 top-1/2 size-4 -translate-y-1/2 text-muted-foreground" }), _jsx(Input, { id: "bookings-search", placeholder: messages.bookingList.searchPlaceholder, value: search, onChange: (event) => {
121
126
  setSearch(event.target.value);
122
127
  resetOffset();
123
- }, className: "pl-9" })] }), _jsx(BookingListFiltersPopover, { open: filterPopoverOpen, onOpenChange: setFilterPopoverOpen, activeFilterCount: activeFilterCount, status: status, onStatusChange: setStatus, productId: productId, onProductIdChange: setProductId, optionId: optionId, onOptionIdChange: setOptionId, supplierId: supplierId, onSupplierIdChange: setSupplierId, productCategoryId: productCategoryId, onProductCategoryIdChange: setProductCategoryId, personId: personId, onPersonIdChange: setPersonId, organizationId: organizationId, onOrganizationIdChange: setOrganizationId, dateRange: dateRange, onDateRangeChange: setDateRange, paxMin: paxMin, onPaxMinChange: setPaxMin, paxMax: paxMax, onPaxMaxChange: setPaxMax, onFiltersChanged: resetOffset }), hasActiveFilters && (_jsxs(Button, { variant: "ghost", size: "sm", onClick: clearFilters, children: [_jsx(X, { className: "mr-1 size-4" }), filterMessages.clear] })), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [headerActions, _jsxs(Button, { onClick: () => {
128
+ }, className: "pl-9" })] }), _jsx(BookingListFiltersPopover, { open: filterPopoverOpen, onOpenChange: setFilterPopoverOpen, activeFilterCount: activeFilterCount, status: status, onStatusChange: setStatus, productId: productId, onProductIdChange: (next) => {
129
+ setProductId(next);
130
+ // Slot picker is product-scoped; clear when the product changes.
131
+ setAvailabilitySlotId(null);
132
+ }, optionId: optionId, onOptionIdChange: setOptionId, availabilitySlotId: availabilitySlotId, onAvailabilitySlotIdChange: setAvailabilitySlotId, supplierId: supplierId, onSupplierIdChange: setSupplierId, productCategoryId: productCategoryId, onProductCategoryIdChange: setProductCategoryId, personId: personId, onPersonIdChange: setPersonId, organizationId: organizationId, onOrganizationIdChange: setOrganizationId, dateRange: dateRange, onDateRangeChange: setDateRange, paxMin: paxMin, onPaxMinChange: setPaxMin, paxMax: paxMax, onPaxMaxChange: setPaxMax, onFiltersChanged: resetOffset }), hasActiveFilters && (_jsxs(Button, { variant: "ghost", size: "sm", onClick: clearFilters, children: [_jsx(X, { className: "mr-1 size-4" }), filterMessages.clear] })), _jsxs("div", { className: "ml-auto flex items-center gap-2", children: [headerActions, _jsxs(Button, { onClick: () => {
124
133
  if (onCreateBooking) {
125
134
  onCreateBooking();
126
135
  return;
127
136
  }
128
137
  setEditing(undefined);
129
138
  setDialogOpen(true);
130
- }, children: [_jsx(Plus, { className: "mr-2 size-4" }), messages.bookingList.newBooking] })] })] }), _jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.bookingNumber, field: SORTABLE_COLUMNS.bookingNumber, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: columnMessages.whatBooked }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.status, field: SORTABLE_COLUMNS.status, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.sellAmount, field: SORTABLE_COLUMNS.sellAmount, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.pax, field: SORTABLE_COLUMNS.pax, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.startDate, field: SORTABLE_COLUMNS.startDate, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.endDate, field: SORTABLE_COLUMNS.endDate, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) })] }) }), _jsx(TableBody, { children: showSkeleton ? (_jsx(BookingTableSkeleton, { rows: SKELETON_ROW_COUNT })) : isError ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: TABLE_COLUMN_COUNT, className: "h-24 text-center text-sm text-destructive", children: messages.bookingList.loadingError }) })) : bookings.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: TABLE_COLUMN_COUNT, className: "h-24 text-center text-sm text-muted-foreground", children: messages.bookingList.empty }) })) : (bookings.map((booking) => (_jsxs(TableRow, { onClick: () => handleSelect(booking), className: "cursor-pointer", children: [_jsx(TableCell, { className: "font-medium", children: booking.bookingNumber }), _jsx(TableCell, { children: formatBookingItems(booking, messages.bookingList.itemsMore) }), _jsx(TableCell, { children: _jsx(Badge, { variant: bookingStatusBadgeVariant[booking.status], children: statusLabels[booking.status] }) }), _jsx(TableCell, { children: booking.sellAmountCents == null
139
+ }, children: [_jsx(Plus, { className: "mr-2 size-4" }), messages.bookingList.newBooking] })] })] }), _jsx("div", { className: "rounded-md border", children: _jsxs(Table, { children: [_jsx(TableHeader, { children: _jsxs(TableRow, { children: [_jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.bookingNumber, field: SORTABLE_COLUMNS.bookingNumber, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: columnMessages.whatBooked }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.status, field: SORTABLE_COLUMNS.status, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.sellAmount, field: SORTABLE_COLUMNS.sellAmount, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.pax, field: SORTABLE_COLUMNS.pax, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.startDate, field: SORTABLE_COLUMNS.startDate, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.endDate, field: SORTABLE_COLUMNS.endDate, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) }), _jsx(TableHead, { children: columnMessages.lead }), _jsx(TableHead, { children: _jsx(SortHeader, { label: columnMessages.createdAt, field: SORTABLE_COLUMNS.createdAt, sortBy: sortBy, sortDir: sortDir, onSort: handleSort }) })] }) }), _jsx(TableBody, { children: showSkeleton ? (_jsx(BookingTableSkeleton, { rows: SKELETON_ROW_COUNT })) : isError ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: TABLE_COLUMN_COUNT, className: "h-24 text-center text-sm text-destructive", children: messages.bookingList.loadingError }) })) : bookings.length === 0 ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: TABLE_COLUMN_COUNT, className: "h-24 text-center text-sm text-muted-foreground", children: messages.bookingList.empty }) })) : (bookings.map((booking) => (_jsxs(TableRow, { onClick: () => handleSelect(booking), className: "cursor-pointer", children: [_jsx(TableCell, { className: "font-medium", children: booking.bookingNumber }), _jsx(TableCell, { children: formatBookingItems(booking, messages.bookingList.itemsMore) }), _jsx(TableCell, { children: _jsx(Badge, { variant: bookingStatusBadgeVariant[booking.status], children: statusLabels[booking.status] }) }), _jsx(TableCell, { children: booking.sellAmountCents == null
131
140
  ? "—"
132
141
  : `${formatNumber(booking.sellAmountCents / 100, {
133
142
  minimumFractionDigits: 2,
134
143
  maximumFractionDigits: 2,
135
- })} ${booking.sellCurrency}` }), _jsx(TableCell, { children: booking.pax ?? "—" }), _jsx(TableCell, { children: formatBookingDateTime(booking.startsAt ?? booking.startDate, formatDateTime) }), _jsx(TableCell, { children: formatBookingDateTime(booking.endsAt ?? booking.endDate, formatDateTime) })] }, booking.id)))) })] }) }), _jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [_jsx("span", { children: formatMessage(messages.bookingList.showingSummary, {
144
+ })} ${booking.sellCurrency}` }), _jsx(TableCell, { children: booking.pax ?? "—" }), _jsx(TableCell, { children: formatBookingDateTime(booking.startsAt ?? booking.startDate, formatDateTime) }), _jsx(TableCell, { children: formatBookingDateTime(booking.endsAt ?? booking.endDate, formatDateTime) }), _jsx(TableCell, { children: formatLead(booking) }), _jsx(TableCell, { children: formatBookingDateTime(booking.createdAt, formatDateTime) })] }, booking.id)))) })] }) }), _jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [_jsx("span", { children: formatMessage(messages.bookingList.showingSummary, {
136
145
  count: bookings.length,
137
146
  total,
138
147
  }) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: offset === 0, onClick: () => setOffset((prev) => Math.max(0, prev - pageSize)), children: messages.bookingList.previousPage }), _jsx("span", { children: formatMessage(messages.bookingList.pageSummary, {
@@ -158,9 +167,8 @@ function formatBookingItems(booking, moreTemplate) {
158
167
  if (!first)
159
168
  return _jsx("span", { className: "text-muted-foreground", children: "\u2014" });
160
169
  const label = first.productName ?? first.title;
161
- if (rest.length === 0)
162
- return label;
163
- return (_jsxs("span", { children: [label, _jsx("span", { className: "ml-1 text-xs text-muted-foreground", children: formatMessage(moreTemplate, { count: rest.length }) })] }));
170
+ const moreSuffix = rest.length === 0 ? "" : ` ${formatMessage(moreTemplate, { count: rest.length })}`;
171
+ return (_jsxs("div", { className: "max-w-[320px] truncate", title: `${label}${moreSuffix}`, children: [label, rest.length > 0 ? (_jsx("span", { className: "ml-1 text-xs text-muted-foreground", children: formatMessage(moreTemplate, { count: rest.length }) })) : null] }));
164
172
  }
165
173
  function formatBookingDateTime(value, formatDateTime) {
166
174
  if (!value)
@@ -170,3 +178,9 @@ function formatBookingDateTime(value, formatDateTime) {
170
178
  }
171
179
  return formatDateTime(value);
172
180
  }
181
+ function formatLead(booking) {
182
+ const name = [booking.contactFirstName, booking.contactLastName].filter(Boolean).join(" ").trim();
183
+ if (name)
184
+ return name;
185
+ return booking.contactEmail ?? "—";
186
+ }
@@ -44,7 +44,9 @@ export interface OptionUnitsStepperSectionProps {
44
44
  noUnits?: string;
45
45
  remaining?: string;
46
46
  unlimited?: string;
47
+ fillsSlotCapacity?: string;
47
48
  };
49
+ slotHasFiniteCapacity?: boolean;
48
50
  }
49
51
  /**
50
52
  * Rooms / per-unit stepper for booking-create flows. Drives
@@ -66,7 +68,15 @@ export interface OptionUnitsStepperSectionProps {
66
68
  * disables the "+" button — we don't let the UI submit a request that
67
69
  * would 409 at insert time.
68
70
  */
69
- export declare function OptionUnitsStepperSection({ value, onChange, productId, slotId, optionId, enabled, onUnitsChange, labels, }: OptionUnitsStepperSectionProps): import("react/jsx-runtime").JSX.Element;
71
+ export declare function OptionUnitsStepperSection({ value, onChange, productId, slotId, optionId, enabled, onUnitsChange, labels, slotHasFiniteCapacity, }: OptionUnitsStepperSectionProps): import("react/jsx-runtime").JSX.Element;
72
+ export declare function resolveOptionRemainingLabel({ totalRemaining, units, slotHasFiniteCapacity, remaining, unlimited, fillsSlotCapacity, }: {
73
+ totalRemaining: number | null;
74
+ units: ReadonlyArray<Pick<OptionUnitsStepperUnit, "unitType">>;
75
+ slotHasFiniteCapacity: boolean;
76
+ remaining: string;
77
+ unlimited: string;
78
+ fillsSlotCapacity?: string;
79
+ }): string;
70
80
  /**
71
81
  * Returns the `optionId` the slot is bound to, derived from the first
72
82
  * slot-availability row whose `optionUnitId` we can map to a known
@@ -1 +1 @@
1
- {"version":3,"file":"option-units-stepper-section.d.ts","sourceRoot":"","sources":["../../src/components/option-units-stepper-section.tsx"],"names":[],"mappings":"AAgBA,iEAAiE;AACjE,MAAM,WAAW,uBAAuB;IACtC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,eAAO,MAAM,4BAA4B,EAAE,uBAA4C,CAAA;AAEvF,MAAM,WAAW,sBAAsB;IACrC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,YAAY,EAAE,MAAM,CAAA;IACpB,QAAQ,EAAE,MAAM,CAAA;IAChB,yFAAyF;IACzF,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,gEAAgE;IAChE,MAAM,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,0FAA0F;IAC1F,QAAQ,CAAC,EAAE,QAAQ,GAAG,OAAO,GAAG,MAAM,GAAG,SAAS,GAAG,SAAS,GAAG,OAAO,GAAG,IAAI,CAAA;IAC/E,YAAY,EAAE,MAAM,GAAG,IAAI,CAAA;IAC3B,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;IACtB,QAAQ,EAAE,MAAM,CAAA;IAChB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,8BAA8B;IAC7C,KAAK,EAAE,uBAAuB,CAAA;IAC9B,QAAQ,EAAE,CAAC,KAAK,EAAE,uBAAuB,KAAK,IAAI,CAAA;IAClD,kEAAkE;IAClE,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB;;;OAGG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf;;;OAGG;IACH,QAAQ,CAAC,EAAE,MAAM,GAAG,IAAI,CAAA;IACxB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,EAAE,KAAK,IAAI,CAAA;IACzD,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,yBAAyB,CAAC,EACxC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,MAAM,EACN,QAAQ,EACR,OAAc,EACd,aAAa,EACb,MAAM,GACP,EAAE,8BAA8B,2CA+MhC;AAED;;;;;;;GAOG;AACH,wBAAgB,mBAAmB,CACjC,QAAQ,EAAE,aAAa,CAAC;IAAE,YAAY,EAAE,MAAM,CAAA;CAAE,CAAC,EACjD,cAAc,EAAE,WAAW,CAAC,MAAM,EAAE,MAAM,CAAC,EAC3C,gBAAgB,EAAE,MAAM,GAAG,IAAI,GAC9B,MAAM,GAAG,IAAI,CAMf;AAED;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,QAAQ,EAAE,aAAa,CAAC,sBAAsB,CAAC,EAC/C,WAAW,EAAE,aAAa,CAAC,sBAAsB,CAAC,EAClD,YAAY,EAAE,MAAM,GAAG,IAAI,EAC3B,OAAO,EAAE,OAAO,GACf,sBAAsB,EAAE,CAM1B"}
1
+ {"version":3,"file":"option-units-stepper-section.d.ts","sourceRoot":"","sources":["../../src/components/option-units-stepper-section.tsx"],"names":[],"mappings":"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"}
@@ -28,7 +28,7 @@ export const emptyOptionUnitsStepperValue = { quantities: {} };
28
28
  * disables the "+" button — we don't let the UI submit a request that
29
29
  * would 409 at insert time.
30
30
  */
31
- export function OptionUnitsStepperSection({ value, onChange, productId, slotId, optionId, enabled = true, onUnitsChange, labels, }) {
31
+ export function OptionUnitsStepperSection({ value, onChange, productId, slotId, optionId, enabled = true, onUnitsChange, labels, slotHasFiniteCapacity = false, }) {
32
32
  const productsClient = useVoyantProductsContext();
33
33
  const messages = useBookingsUiMessagesOrDefault();
34
34
  const merged = { ...messages.roomsStepperSection.labels, ...labels };
@@ -74,22 +74,30 @@ export function OptionUnitsStepperSection({ value, onChange, productId, slotId,
74
74
  // row, so we look it up from the units we already fetched per option.
75
75
  const optionByUnitId = React.useMemo(() => {
76
76
  const map = new Map();
77
- productOptions.forEach((option, index) => {
78
- const units = optionUnitQueries[index]?.data?.data ?? [];
79
- for (const unit of units) {
80
- map.set(unit.id, option.id);
81
- }
82
- });
77
+ for (const unit of optionUnitRows) {
78
+ if (unit.optionId)
79
+ map.set(unit.optionUnitId, unit.optionId);
80
+ }
83
81
  return map;
84
- }, [productOptions, optionUnitQueries]);
82
+ }, [optionUnitRows]);
83
+ const productUnitById = React.useMemo(() => {
84
+ return new Map(optionUnitRows.map((unit) => [unit.optionUnitId, unit]));
85
+ }, [optionUnitRows]);
85
86
  // The slot's bound option, derived from the first availability row.
86
87
  // `null` when the slot is product-level (no option_id) — that path goes
87
88
  // through the product-level fallback below.
88
89
  const slotOptionId = React.useMemo(() => resolveSlotOptionId(availability.data?.data ?? [], optionByUnitId, optionId ?? null), [availability.data?.data, optionByUnitId, optionId]);
89
- const availabilityUnitRows = React.useMemo(() => (availability.data?.data ?? []).map((unit) => ({
90
- ...unit,
91
- optionId: slotOptionId ?? optionId ?? null,
92
- })), [availability.data?.data, slotOptionId, optionId]);
90
+ const availabilityUnitRows = React.useMemo(() => (availability.data?.data ?? []).map((unit) => {
91
+ const productUnit = productUnitById.get(unit.optionUnitId);
92
+ return {
93
+ ...unit,
94
+ optionId: productUnit?.optionId ?? slotOptionId ?? optionId ?? null,
95
+ unitCode: productUnit?.unitCode ?? null,
96
+ minAge: productUnit?.minAge ?? null,
97
+ maxAge: productUnit?.maxAge ?? null,
98
+ unitType: productUnit?.unitType ?? null,
99
+ };
100
+ }), [availability.data?.data, productUnitById, slotOptionId, optionId]);
93
101
  // Slot-bound per-unit availability stays authoritative for the slot's
94
102
  // option (real-time `remaining` from active bookings). For *other*
95
103
  // options the same product offers, fall back to the product-level
@@ -136,6 +144,7 @@ export function OptionUnitsStepperSection({ value, onChange, productId, slotId,
136
144
  optionKey,
137
145
  optionName,
138
146
  primary: group.primary,
147
+ allUnits: group.allUnits,
139
148
  totalRemaining,
140
149
  };
141
150
  });
@@ -162,13 +171,28 @@ export function OptionUnitsStepperSection({ value, onChange, productId, slotId,
162
171
  }
163
172
  onChange({ quantities: next });
164
173
  };
165
- return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: merged.heading }), _jsx("div", { className: "flex flex-col gap-2", children: optionRows.map(({ optionKey, optionName, primary, totalRemaining }) => {
174
+ return (_jsxs("div", { className: "flex flex-col gap-2 rounded-md border p-3", children: [_jsx(Label, { children: merged.heading }), _jsx("div", { className: "flex flex-col gap-2", children: optionRows.map(({ optionKey, optionName, primary, allUnits, totalRemaining }) => {
166
175
  const qty = value.quantities[primary.optionUnitId] ?? 0;
167
- const remainingLabel = totalRemaining === null ? merged.unlimited : `${totalRemaining} ${merged.remaining}`;
176
+ const remainingLabel = resolveOptionRemainingLabel({
177
+ totalRemaining,
178
+ units: allUnits,
179
+ slotHasFiniteCapacity,
180
+ remaining: merged.remaining,
181
+ unlimited: merged.unlimited,
182
+ fillsSlotCapacity: merged.fillsSlotCapacity,
183
+ });
168
184
  const atMax = totalRemaining !== null && qty >= totalRemaining;
169
185
  return (_jsxs("div", { className: "flex items-center gap-3 rounded-md border px-3 py-2", children: [_jsxs("div", { className: "flex-1", children: [_jsx("div", { className: "text-sm font-medium", children: optionName }), _jsx("div", { className: "text-xs text-muted-foreground", children: remainingLabel })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setQuantity(primary.optionUnitId, Math.max(0, qty - 1)), disabled: qty <= 0, "aria-label": `${merged.decreaseUnitPrefix} ${optionName}`, children: _jsx(Minus, { className: "h-3.5 w-3.5" }) }), _jsx("span", { className: "min-w-[1.5rem] text-center text-sm tabular-nums", children: qty }), _jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "h-7 w-7 p-0", onClick: () => setQuantity(primary.optionUnitId, qty + 1), disabled: atMax, "aria-label": `${merged.increaseUnitPrefix} ${optionName}`, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })] }, optionKey));
170
186
  }) })] }));
171
187
  }
188
+ export function resolveOptionRemainingLabel({ totalRemaining, units, slotHasFiniteCapacity, remaining, unlimited, fillsSlotCapacity, }) {
189
+ if (totalRemaining !== null)
190
+ return `${totalRemaining} ${remaining}`;
191
+ if (slotHasFiniteCapacity && units.length > 0 && units.every(isPersonUnit)) {
192
+ return fillsSlotCapacity ?? unlimited;
193
+ }
194
+ return unlimited;
195
+ }
172
196
  /**
173
197
  * Returns the `optionId` the slot is bound to, derived from the first
174
198
  * slot-availability row whose `optionUnitId` we can map to a known
@@ -206,6 +230,9 @@ function isAdultUnit(unit) {
206
230
  // when the upstream code isn't surfaced.
207
231
  return /\badult\b/i.test(unit.unitName);
208
232
  }
233
+ function isPersonUnit(unit) {
234
+ return unit.unitType === "person";
235
+ }
209
236
  function optionUnitToStepperUnit(option, unit, unitCount) {
210
237
  return {
211
238
  optionId: option.id,
@@ -1 +1 @@
1
- {"version":3,"file":"supplier-status-list.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-list.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CA+GxE"}
1
+ {"version":3,"file":"supplier-status-list.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-list.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CAyIxE"}
@@ -19,18 +19,31 @@ export function SupplierStatusList({ bookingId }) {
19
19
  const { formatCurrency, formatDate } = useBookingsUiI18nOrDefault();
20
20
  const messages = useBookingsUiMessagesOrDefault();
21
21
  const statuses = data?.data ?? [];
22
+ // `bookingSupplierStatuses` gets one row per `product_day_services`
23
+ // entry — so a 2-day itinerary that includes the same service on
24
+ // both days lands two visually-identical rows. The operator only
25
+ // cares about the per-service total, so collapse identical rows
26
+ // (same service id, name, status, cost) into one with a `× N`
27
+ // badge. The edit pencil opens the first row of the group; for true
28
+ // duplicates that's a no-op-distinction.
29
+ const groupedStatuses = groupSupplierStatuses(statuses);
22
30
  return (_jsxs(Card, { "data-slot": "supplier-status-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { children: messages.supplierStatusList.title }), _jsxs(Button, { size: "sm", onClick: () => {
23
31
  setEditing(undefined);
24
32
  setDialogOpen(true);
25
- }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.supplierStatusList.addSupplier] })] }), _jsx(CardContent, { children: statuses.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.supplierStatusList.empty })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.service }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.status }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.cost }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.reference }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.confirmed }), _jsx("th", { className: "w-12 p-2" })] }) }), _jsx("tbody", { children: statuses.map((status) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: status.serviceName }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: supplierStatusVariant[status.status] ?? "secondary", children: messages.common.supplierStatusLabels[status.status] }) }), _jsx("td", { className: "p-2 font-mono", children: status.costAmountCents == null || !status.costCurrency
26
- ? messages.supplierStatusList.values.costUnavailable
27
- : formatCurrency(status.costAmountCents / 100, status.costCurrency) }), _jsx("td", { className: "p-2", children: status.supplierReference ??
28
- messages.supplierStatusList.values.referenceUnavailable }), _jsx("td", { className: "p-2", children: status.confirmedAt
29
- ? formatDate(status.confirmedAt)
30
- : messages.supplierStatusList.values.confirmedUnavailable }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
31
- setEditing(status);
32
- setDialogOpen(true);
33
- }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }) })] }, status.id))) })] }) })) }), _jsx(SupplierStatusDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
33
+ }, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.supplierStatusList.addSupplier] })] }), _jsx(CardContent, { children: statuses.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.supplierStatusList.empty })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.service }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.status }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.cost }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.reference }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.confirmed }), _jsx("th", { className: "w-12 p-2" })] }) }), _jsx("tbody", { children: groupedStatuses.map((group) => {
34
+ const head = group.statuses[0];
35
+ const totalCostCents = group.statuses.reduce((sum, s) => sum + (s.costAmountCents ?? 0), 0);
36
+ const reference = group.statuses.find((s) => s.supplierReference)?.supplierReference ?? null;
37
+ const confirmedAt = group.statuses.find((s) => s.confirmedAt)?.confirmedAt ?? null;
38
+ return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: _jsxs("span", { className: "inline-flex items-center gap-1.5", children: [head.serviceName, group.statuses.length > 1 ? (_jsxs("span", { className: "rounded bg-muted px-1.5 py-0.5 text-[10px] text-muted-foreground", children: ["\u00D7 ", group.statuses.length] })) : null] }) }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: supplierStatusVariant[head.status] ?? "secondary", children: messages.common.supplierStatusLabels[head.status] }) }), _jsx("td", { className: "p-2 font-mono", children: totalCostCents === 0 || !head.costCurrency
39
+ ? messages.supplierStatusList.values.costUnavailable
40
+ : formatCurrency(totalCostCents / 100, head.costCurrency) }), _jsx("td", { className: "p-2", children: reference ?? messages.supplierStatusList.values.referenceUnavailable }), _jsx("td", { className: "p-2", children: confirmedAt
41
+ ? formatDate(confirmedAt)
42
+ : messages.supplierStatusList.values.confirmedUnavailable }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
43
+ setEditing(head);
44
+ setDialogOpen(true);
45
+ }, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }) })] }, group.key));
46
+ }) })] }) })) }), _jsx(SupplierStatusDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
34
47
  setDialogOpen(nextOpen);
35
48
  if (!nextOpen) {
36
49
  setEditing(undefined);
@@ -39,3 +52,27 @@ export function SupplierStatusList({ bookingId }) {
39
52
  setEditing(undefined);
40
53
  } })] }));
41
54
  }
55
+ function groupSupplierStatuses(statuses) {
56
+ const groups = new Map();
57
+ for (const status of statuses) {
58
+ // Visually-identical rows collapse together. Reference/confirmed
59
+ // timestamps and `id` are intentionally excluded — those differ
60
+ // between sibling rows that the operator nonetheless sees as one
61
+ // line of business.
62
+ const key = [
63
+ status.supplierServiceId ?? "",
64
+ status.serviceName,
65
+ status.status,
66
+ status.costCurrency ?? "",
67
+ status.costAmountCents ?? "",
68
+ ].join("|");
69
+ const existing = groups.get(key);
70
+ if (existing) {
71
+ existing.statuses.push(status);
72
+ }
73
+ else {
74
+ groups.set(key, { key, statuses: [status] });
75
+ }
76
+ }
77
+ return Array.from(groups.values());
78
+ }
package/dist/i18n/en.d.ts CHANGED
@@ -44,6 +44,7 @@ export declare const bookingsUiEn: {
44
44
  deleteAction: string;
45
45
  deleteConfirm: string;
46
46
  collectPaymentAction: string;
47
+ recordPaymentAction: string;
47
48
  noValue: string;
48
49
  tbd: string;
49
50
  summarySell: string;
@@ -58,9 +59,11 @@ export declare const bookingsUiEn: {
58
59
  tabOverview: string;
59
60
  tabTravelers: string;
60
61
  tabFinance: string;
62
+ tabInvoices: string;
61
63
  tabSuppliers: string;
62
64
  tabDocuments: string;
63
65
  tabActivity: string;
66
+ tabLedger: string;
64
67
  internalNotesLabel: string;
65
68
  billingPayer: string;
66
69
  billingEmail: string;
@@ -68,25 +71,6 @@ export declare const bookingsUiEn: {
68
71
  billingAddress: string;
69
72
  documentsSlotEmpty: string;
70
73
  };
71
- bookingWorkspacePage: {
72
- description: string;
73
- loadingTitle: string;
74
- notFoundTitle: string;
75
- notFoundDescription: string;
76
- tabs: {
77
- booking: string;
78
- finance: string;
79
- legal: string;
80
- travelers: string;
81
- activity: string;
82
- };
83
- empty: {
84
- finance: string;
85
- legal: string;
86
- travelers: string;
87
- activity: string;
88
- };
89
- };
90
74
  travelerDialog: {
91
75
  titles: {
92
76
  create: string;
@@ -452,6 +436,7 @@ export declare const bookingsUiEn: {
452
436
  noUnits: string;
453
437
  remaining: string;
454
438
  unlimited: string;
439
+ fillsSlotCapacity: string;
455
440
  decreaseUnitPrefix: string;
456
441
  increaseUnitPrefix: string;
457
442
  };
@@ -1180,6 +1165,8 @@ export declare const bookingsUiEn: {
1180
1165
  pax: string;
1181
1166
  startDate: string;
1182
1167
  endDate: string;
1168
+ lead: string;
1169
+ createdAt: string;
1183
1170
  };
1184
1171
  filters: {
1185
1172
  button: string;
@@ -1204,6 +1191,10 @@ export declare const bookingsUiEn: {
1204
1191
  organizationLabel: string;
1205
1192
  organization: string;
1206
1193
  organizationEmpty: string;
1194
+ departureLabel: string;
1195
+ departure: string;
1196
+ departureEmpty: string;
1197
+ departureNeedsProduct: string;
1207
1198
  dateRangeLabel: string;
1208
1199
  dateRange: string;
1209
1200
  paxLabel: string;
@@ -1 +1 @@
1
- {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAwxCK,CAAA"}
1
+ {"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CA+wCK,CAAA"}
package/dist/i18n/en.js CHANGED
@@ -43,7 +43,8 @@ export const bookingsUiEn = {
43
43
  cancelBookingAction: "Cancel booking",
44
44
  deleteAction: "Delete",
45
45
  deleteConfirm: "Delete this booking?",
46
- collectPaymentAction: "Collect payment",
46
+ collectPaymentAction: "Generate payment link",
47
+ recordPaymentAction: "Record payment",
47
48
  noValue: "-",
48
49
  tbd: "TBD",
49
50
  summarySell: "Sell",
@@ -57,10 +58,12 @@ export const bookingsUiEn = {
57
58
  summaryUpdated: "Updated",
58
59
  tabOverview: "Overview",
59
60
  tabTravelers: "Travelers",
60
- tabFinance: "Finance",
61
+ tabFinance: "Payments",
62
+ tabInvoices: "Invoices",
61
63
  tabSuppliers: "Suppliers",
62
64
  tabDocuments: "Documents",
63
65
  tabActivity: "Activity",
66
+ tabLedger: "Ledger",
64
67
  internalNotesLabel: "Internal notes",
65
68
  billingPayer: "Payer",
66
69
  billingEmail: "Email",
@@ -68,25 +71,6 @@ export const bookingsUiEn = {
68
71
  billingAddress: "Address",
69
72
  documentsSlotEmpty: "Provide a documents slot to render booking documents.",
70
73
  },
71
- bookingWorkspacePage: {
72
- description: "Coordinate booking, finance, legal, traveler, and activity work.",
73
- loadingTitle: "Booking workspace",
74
- notFoundTitle: "Booking workspace",
75
- notFoundDescription: "The booking workspace could not find this booking.",
76
- tabs: {
77
- booking: "Booking",
78
- finance: "Finance",
79
- legal: "Legal",
80
- travelers: "Travelers",
81
- activity: "Activity",
82
- },
83
- empty: {
84
- finance: "Provide a finance tab slot to render invoices, payment status, or collections.",
85
- legal: "Provide a legal tab slot to render contracts, waivers, or compliance tasks.",
86
- travelers: "Provide traveler tab extensions for batch edits and operational checks.",
87
- activity: "Provide an activity tab slot to render the operator timeline or audit trail.",
88
- },
89
- },
90
74
  travelerDialog: {
91
75
  titles: {
92
76
  create: "Add traveler",
@@ -452,6 +436,7 @@ export const bookingsUiEn = {
452
436
  noUnits: "This departure has no per-unit availability configured.",
453
437
  remaining: "left",
454
438
  unlimited: "unlimited",
439
+ fillsSlotCapacity: "fills slot capacity",
455
440
  decreaseUnitPrefix: "Decrease",
456
441
  increaseUnitPrefix: "Increase",
457
442
  },
@@ -1174,12 +1159,14 @@ export const bookingsUiEn = {
1174
1159
  newBooking: "New booking",
1175
1160
  columns: {
1176
1161
  bookingNumber: "Booking #",
1177
- whatBooked: "What",
1162
+ whatBooked: "Items",
1178
1163
  status: "Status",
1179
1164
  sellAmount: "Sell amount",
1180
1165
  pax: "Pax",
1181
1166
  startDate: "Start date/time",
1182
1167
  endDate: "End date/time",
1168
+ lead: "Lead",
1169
+ createdAt: "Created",
1183
1170
  },
1184
1171
  filters: {
1185
1172
  button: "Filters",
@@ -1204,6 +1191,10 @@ export const bookingsUiEn = {
1204
1191
  organizationLabel: "Organization",
1205
1192
  organization: "Search organizations...",
1206
1193
  organizationEmpty: "No organizations found.",
1194
+ departureLabel: "Departure",
1195
+ departure: "Search departures...",
1196
+ departureEmpty: "No departures found.",
1197
+ departureNeedsProduct: "Select a product first.",
1207
1198
  dateRangeLabel: "Start date",
1208
1199
  dateRange: "Any date",
1209
1200
  paxLabel: "Pax",
@@ -30,6 +30,7 @@ export type BookingsUiMessages = {
30
30
  deleteAction: string;
31
31
  deleteConfirm: string;
32
32
  collectPaymentAction: string;
33
+ recordPaymentAction: string;
33
34
  noValue: string;
34
35
  tbd: string;
35
36
  summarySell: string;
@@ -44,9 +45,11 @@ export type BookingsUiMessages = {
44
45
  tabOverview: string;
45
46
  tabTravelers: string;
46
47
  tabFinance: string;
48
+ tabInvoices: string;
47
49
  tabSuppliers: string;
48
50
  tabDocuments: string;
49
51
  tabActivity: string;
52
+ tabLedger: string;
50
53
  internalNotesLabel: string;
51
54
  billingPayer: string;
52
55
  billingEmail: string;
@@ -54,14 +57,6 @@ export type BookingsUiMessages = {
54
57
  billingAddress: string;
55
58
  documentsSlotEmpty: string;
56
59
  };
57
- bookingWorkspacePage: {
58
- description: string;
59
- loadingTitle: string;
60
- notFoundTitle: string;
61
- notFoundDescription: string;
62
- tabs: Record<"booking" | "finance" | "legal" | "travelers" | "activity", string>;
63
- empty: Record<"finance" | "legal" | "travelers" | "activity", string>;
64
- };
65
60
  travelerDialog: {
66
61
  titles: {
67
62
  create: string;
@@ -381,6 +376,7 @@ export type BookingsUiMessages = {
381
376
  noUnits: string;
382
377
  remaining: string;
383
378
  unlimited: string;
379
+ fillsSlotCapacity: string;
384
380
  decreaseUnitPrefix: string;
385
381
  increaseUnitPrefix: string;
386
382
  };
@@ -1054,6 +1050,8 @@ export type BookingsUiMessages = {
1054
1050
  pax: string;
1055
1051
  startDate: string;
1056
1052
  endDate: string;
1053
+ lead: string;
1054
+ createdAt: string;
1057
1055
  };
1058
1056
  filters: {
1059
1057
  button: string;
@@ -1078,6 +1076,10 @@ export type BookingsUiMessages = {
1078
1076
  organizationLabel: string;
1079
1077
  organization: string;
1080
1078
  organizationEmpty: string;
1079
+ departureLabel: string;
1080
+ departure: string;
1081
+ departureEmpty: string;
1082
+ departureNeedsProduct: string;
1081
1083
  dateRangeLabel: string;
1082
1084
  dateRange: string;
1083
1085
  paxLabel: string;