@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.
- package/README.md +18 -21
- package/dist/components/booking-create-dialog.d.ts.map +1 -1
- package/dist/components/booking-create-dialog.js +8 -2
- package/dist/components/booking-detail-page.d.ts +32 -1
- package/dist/components/booking-detail-page.d.ts.map +1 -1
- package/dist/components/booking-detail-page.js +53 -10
- package/dist/components/booking-item-list.d.ts.map +1 -1
- package/dist/components/booking-item-list.js +18 -5
- package/dist/components/booking-list-filters.d.ts +7 -1
- package/dist/components/booking-list-filters.d.ts.map +1 -1
- package/dist/components/booking-list-filters.js +44 -2
- package/dist/components/booking-list.d.ts.map +1 -1
- package/dist/components/booking-list.js +21 -7
- package/dist/components/option-units-stepper-section.d.ts +11 -1
- package/dist/components/option-units-stepper-section.d.ts.map +1 -1
- package/dist/components/option-units-stepper-section.js +41 -14
- package/dist/components/supplier-status-list.d.ts.map +1 -1
- package/dist/components/supplier-status-list.js +46 -9
- package/dist/i18n/en.d.ts +10 -19
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +13 -22
- package/dist/i18n/messages.d.ts +10 -8
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/provider.d.ts +20 -38
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/ro.d.ts +10 -19
- package/dist/i18n/ro.d.ts.map +1 -1
- package/dist/i18n/ro.js +13 -22
- package/dist/index.d.ts +1 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +0 -1
- package/package.json +30 -30
- package/dist/components/booking-workspace-page.d.ts +0 -61
- package/dist/components/booking-workspace-page.d.ts.map +0 -1
- 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 =
|
|
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:
|
|
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
|
-
|
|
162
|
-
|
|
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;
|
|
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
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
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
|
-
}, [
|
|
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
|
-
|
|
91
|
-
|
|
92
|
-
|
|
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 =
|
|
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,
|
|
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:
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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;
|
package/dist/i18n/en.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"en.d.ts","sourceRoot":"","sources":["../../src/i18n/en.ts"],"names":[],"mappings":"AAEA,eAAO,MAAM,YAAY
|
|
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: "
|
|
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: "
|
|
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: "
|
|
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",
|
package/dist/i18n/messages.d.ts
CHANGED
|
@@ -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;
|