@voyantjs/bookings-ui 0.35.0 → 0.37.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/dist/components/booking-combobox.d.ts +13 -0
- package/dist/components/booking-combobox.d.ts.map +1 -0
- package/dist/components/booking-combobox.js +44 -0
- package/dist/components/booking-create-dialog.d.ts +9 -0
- package/dist/components/booking-create-dialog.d.ts.map +1 -1
- package/dist/components/booking-create-dialog.js +110 -90
- package/dist/components/booking-create-page.d.ts +11 -0
- package/dist/components/booking-create-page.d.ts.map +1 -0
- package/dist/components/booking-create-page.js +11 -0
- package/dist/components/booking-detail-page.d.ts.map +1 -1
- package/dist/components/booking-detail-page.js +4 -1
- package/dist/components/booking-item-list.d.ts.map +1 -1
- package/dist/components/booking-item-list.js +5 -3
- package/dist/components/booking-list-filters.d.ts +35 -0
- package/dist/components/booking-list-filters.d.ts.map +1 -0
- package/dist/components/booking-list-filters.js +148 -0
- package/dist/components/booking-list.d.ts.map +1 -1
- package/dist/components/booking-list.js +30 -46
- package/dist/components/person-picker-section.d.ts +15 -7
- package/dist/components/person-picker-section.d.ts.map +1 -1
- package/dist/components/person-picker-section.js +100 -21
- package/dist/components/price-breakdown-section.d.ts +16 -1
- package/dist/components/price-breakdown-section.d.ts.map +1 -1
- package/dist/components/price-breakdown-section.js +36 -5
- package/dist/components/product-picker-section.d.ts.map +1 -1
- package/dist/components/product-picker-section.js +38 -4
- package/dist/components/shared-room-section.d.ts +9 -8
- package/dist/components/shared-room-section.d.ts.map +1 -1
- package/dist/components/shared-room-section.js +67 -14
- package/dist/components/traveler-list.d.ts.map +1 -1
- package/dist/components/traveler-list.js +27 -16
- package/dist/i18n/en.d.ts +284 -1
- package/dist/i18n/en.d.ts.map +1 -1
- package/dist/i18n/en.js +300 -17
- package/dist/i18n/messages.d.ts +255 -1
- package/dist/i18n/messages.d.ts.map +1 -1
- package/dist/i18n/provider.d.ts +568 -2
- package/dist/i18n/provider.d.ts.map +1 -1
- package/dist/i18n/ro.d.ts +284 -1
- package/dist/i18n/ro.d.ts.map +1 -1
- package/dist/i18n/ro.js +301 -18
- package/dist/index.d.ts +3 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/journey/components/booking-journey.d.ts.map +1 -1
- package/dist/journey/components/booking-journey.js +22 -13
- package/dist/journey/components/contract-preview-dialog.d.ts.map +1 -1
- package/dist/journey/components/contract-preview-dialog.js +9 -4
- package/dist/journey/components/journey-steps.d.ts.map +1 -1
- package/dist/journey/components/journey-steps.js +94 -72
- package/dist/journey/components/side-panel.d.ts.map +1 -1
- package/dist/journey/components/side-panel.js +58 -35
- package/dist/journey/components/step-header.d.ts.map +1 -1
- package/dist/journey/components/step-header.js +3 -19
- package/package.json +24 -20
|
@@ -5,7 +5,9 @@ import { Checkbox } from "@voyantjs/ui/components/checkbox";
|
|
|
5
5
|
import { Dialog, DialogContent, DialogFooter, DialogHeader, DialogTitle, } from "@voyantjs/ui/components/dialog";
|
|
6
6
|
import { Skeleton } from "@voyantjs/ui/components/skeleton";
|
|
7
7
|
import * as React from "react";
|
|
8
|
+
import { formatMessage, useBookingsUiMessagesOrDefault } from "../../i18n/index.js";
|
|
8
9
|
export function ContractPreviewDialog({ open, onOpenChange, variables, previewUrl, acceptLanguage, onAccept, marketingLabel, termsLabel, }) {
|
|
10
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
9
11
|
const [loadState, setLoadState] = React.useState("idle");
|
|
10
12
|
const [errorMessage, setErrorMessage] = React.useState(null);
|
|
11
13
|
const [renderedHtml, setRenderedHtml] = React.useState("");
|
|
@@ -40,7 +42,9 @@ export function ContractPreviewDialog({ open, onOpenChange, variables, previewUr
|
|
|
40
42
|
})
|
|
41
43
|
.then(async (res) => {
|
|
42
44
|
if (!res.ok) {
|
|
43
|
-
throw new Error(
|
|
45
|
+
throw new Error(formatMessage(messages.bookingJourney.contract.previewRequestFailed, {
|
|
46
|
+
status: res.status,
|
|
47
|
+
}));
|
|
44
48
|
}
|
|
45
49
|
const json = (await res.json());
|
|
46
50
|
if (cancelled)
|
|
@@ -48,7 +52,7 @@ export function ContractPreviewDialog({ open, onOpenChange, variables, previewUr
|
|
|
48
52
|
const body = json.data?.rendered ?? "";
|
|
49
53
|
const tmpl = json.data?.template;
|
|
50
54
|
if (!body || !tmpl) {
|
|
51
|
-
throw new Error(
|
|
55
|
+
throw new Error(messages.bookingJourney.contract.previewMissing);
|
|
52
56
|
}
|
|
53
57
|
setRenderedHtml(body);
|
|
54
58
|
setTemplate({ id: tmpl.id, slug: tmpl.slug, name: tmpl.name });
|
|
@@ -78,8 +82,9 @@ export function ContractPreviewDialog({ open, onOpenChange, variables, previewUr
|
|
|
78
82
|
renderedHtml,
|
|
79
83
|
});
|
|
80
84
|
};
|
|
81
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "flex h-[85vh] w-[95vw] max-w-4xl flex-col gap-0 p-0", children: [_jsxs(DialogHeader, { className: "border-b p-6 pb-4", children: [_jsx(DialogTitle, { children: template?.name ??
|
|
82
|
-
|
|
85
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { className: "flex h-[85vh] w-[95vw] max-w-4xl flex-col gap-0 p-0", children: [_jsxs(DialogHeader, { className: "border-b p-6 pb-4", children: [_jsx(DialogTitle, { children: template?.name ?? messages.bookingJourney.contract.defaultTitle }), _jsx("p", { className: "text-muted-foreground text-sm", children: messages.bookingJourney.contract.description })] }), _jsxs("div", { className: "flex-1 overflow-hidden bg-muted/30", children: [loadState === "loading" ? (_jsxs("div", { className: "space-y-3 p-6", children: [_jsx(Skeleton, { className: "h-6 w-1/2" }), _jsx(Skeleton, { className: "h-4 w-full" }), _jsx(Skeleton, { className: "h-4 w-5/6" }), _jsx(Skeleton, { className: "h-4 w-4/5" }), _jsx(Skeleton, { className: "h-4 w-full" })] })) : null, loadState === "error" ? (_jsx("div", { className: "p-6", children: _jsxs("p", { className: "text-destructive text-sm", children: [messages.bookingJourney.contract.errorPrefix, " ", errorMessage] }) })) : null, loadState === "ready" ? (_jsx("iframe", { title: formatMessage(messages.bookingJourney.contract.iframeTitle, {
|
|
86
|
+
name: template?.name ?? messages.bookingJourney.contract.defaultTitle,
|
|
87
|
+
}), srcDoc: wrapPreviewHtml(renderedHtml), sandbox: "", className: "h-full w-full border-0 bg-white" })) : null] }), _jsxs(DialogFooter, { className: "border-t p-6 sm:flex-col sm:items-stretch sm:gap-3", children: [_jsxs("div", { className: "space-y-2 text-sm", children: [_jsxs("label", { className: "flex items-start gap-2", children: [_jsx(Checkbox, { checked: acceptedTerms, onCheckedChange: (v) => setAcceptedTerms(v === true), className: "mt-0.5" }), _jsx("span", { children: termsLabel ?? _jsx(_Fragment, { children: messages.bookingJourney.contract.termsLabel }) })] }), _jsxs("label", { className: "flex items-start gap-2", children: [_jsx(Checkbox, { checked: acceptedMarketing, onCheckedChange: (v) => setAcceptedMarketing(v === true), className: "mt-0.5" }), _jsx("span", { children: marketingLabel ?? messages.bookingJourney.contract.marketingLabel })] })] }), _jsxs("div", { className: "flex justify-end gap-2", children: [_jsx(Button, { type: "button", variant: "outline", onClick: () => onOpenChange(false), children: messages.bookingJourney.contract.cancel }), _jsx(Button, { type: "button", disabled: !canAccept, onClick: handleAccept, children: messages.bookingJourney.contract.acceptAndContinue })] })] })] }) }));
|
|
83
88
|
}
|
|
84
89
|
/**
|
|
85
90
|
* Wrap the rendered template body in a self-contained light-theme HTML
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"journey-steps.d.ts","sourceRoot":"","sources":["../../../src/journey/components/journey-steps.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AAYzE,OAAO,EACL,KAAK,KAAK,EASX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,aAAa,CAAA;AAEpB,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAMD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,KAAK,GACN,EAAE,eAAe,GAAG;IACnB,YAAY,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;CACrC,GAAG,KAAK,CAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"journey-steps.d.ts","sourceRoot":"","sources":["../../../src/journey/components/journey-steps.tsx"],"names":[],"mappings":"AAEA;;;;;;GAMG;AAEH,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,kCAAkC,CAAA;AAYzE,OAAO,EACL,KAAK,KAAK,EASX,MAAM,uBAAuB,CAAA;AAC9B,OAAO,KAAK,EACV,sBAAsB,EACtB,2BAA2B,EAC3B,8BAA8B,EAC9B,0BAA0B,EAC3B,MAAM,aAAa,CAAA;AAEpB,UAAU,eAAe;IACvB,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,KAAK,EAAE,iBAAiB,CAAA;CACzB;AAMD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,KAAK,GACN,EAAE,eAAe,GAAG;IACnB,YAAY,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;CACrC,GAAG,KAAK,CAAC,YAAY,CAarB;AAiWD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,uBAAuB,EACvB,YAAY,GACb,EAAE,eAAe,GAAG;IACnB,uBAAuB,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,KAAK,CAAC,SAAS,CAAA;IAC5E,YAAY,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;CACrC,GAAG,KAAK,CAAC,YAAY,CA+JrB;AAMD,wBAAgB,aAAa,CAAC,EAC5B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,2BAA2B,GAC5B,EAAE,eAAe,GAAG;IACnB,2BAA2B,CAAC,EAAE,CAAC,KAAK,EAAE,0BAA0B,KAAK,KAAK,CAAC,SAAS,CAAA;CACrF,GAAG,KAAK,CAAC,YAAY,CAgFrB;AAkUD,wBAAgB,iBAAiB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAsGjG;AAkED,wBAAgB,UAAU,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,KAAK,EAAE,EAAE,eAAe,GAAG,KAAK,CAAC,YAAY,CAkD1F;AAyDD,wBAAgB,WAAW,CAAC,EAC1B,KAAK,EACL,QAAQ,EACR,KAAK,EACL,YAAY,EACZ,kBAAkB,GACnB,EAAE,eAAe,GAAG;IACnB,YAAY,EAAE,2BAA2B,CAAA;IACzC,kBAAkB,CAAC,EAAE,CAAC,KAAK,EAAE,8BAA8B,KAAK,KAAK,CAAC,SAAS,CAAA;CAChF,GAAG,KAAK,CAAC,YAAY,CAoFrB;AAwDD,wBAAgB,UAAU,CAAC,EACzB,KAAK,EACL,QAAQ,EACR,YAAY,EACZ,SAAS,EACT,YAAY,EACZ,OAAO,GACR,EAAE;IACD,KAAK,EAAE,KAAK,CAAA;IACZ,QAAQ,EAAE,CAAC,IAAI,EAAE,KAAK,KAAK,IAAI,CAAA;IAC/B,YAAY,EAAE,OAAO,CAAA;IACrB,SAAS,EAAE,MAAM,IAAI,CAAA;IACrB,YAAY,CAAC,EAAE,MAAM,KAAK,CAAC,SAAS,CAAA;IACpC;;;;;OAKG;IACH,OAAO,CAAC,EAAE,OAAO,GAAG,QAAQ,CAAA;CAC7B,GAAG,KAAK,CAAC,YAAY,CAgErB"}
|
|
@@ -10,27 +10,36 @@ import { PhoneInput } from "@voyantjs/ui/components/phone-input";
|
|
|
10
10
|
import { RadioGroup, RadioGroupItem } from "@voyantjs/ui/components/radio-group";
|
|
11
11
|
import { Textarea } from "@voyantjs/ui/components/textarea";
|
|
12
12
|
import { Loader2 } from "lucide-react";
|
|
13
|
+
import { formatMessage, useBookingsUiMessagesOrDefault } from "../../i18n/index.js";
|
|
13
14
|
import { patchBilling, patchConfigure, patchPaxCount, setAccommodation, setAddons, setPayment, setTravelers, totalPax, } from "../lib/draft-state.js";
|
|
14
15
|
// ─────────────────────────────────────────────────────────────────
|
|
15
16
|
// Configure
|
|
16
17
|
// ─────────────────────────────────────────────────────────────────
|
|
17
18
|
export function ConfigureStep({ draft, setDraft, shape, }) {
|
|
18
|
-
|
|
19
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
20
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.steps.configure }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsx(PaxBands, { draft: draft, setDraft: setDraft, shape: shape }), _jsx(DepartureFields, { draft: draft, setDraft: setDraft, shape: shape })] })] }));
|
|
19
21
|
}
|
|
20
22
|
function PaxBands({ draft, setDraft, shape }) {
|
|
21
|
-
|
|
23
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
24
|
+
return (_jsxs("div", { className: "space-y-3", children: [_jsx(Label, { children: messages.bookingJourney.configure.travelers }), _jsx("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-3", children: shape.paxBands.map((band) => {
|
|
22
25
|
const value = draft.configure.pax?.[band.code] ?? 0;
|
|
23
|
-
return (_jsxs("div", { className: "flex items-center justify-between rounded border p-3", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: band.label }), band.minAge != null || band.maxAge != null ? (_jsx("div", { className: "text-muted-foreground text-xs", children: ageHint(band.minAge, band.maxAge) })) : null] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", type: "button", disabled: value <= band.minCount, onClick: () => setDraft(patchPaxCount(draft, band.code, value - 1)), children: "\u2212" }), _jsx("span", { className: "min-w-6 text-center", children: value }), _jsx(Button, { variant: "outline", size: "sm", type: "button", disabled: value >= band.maxCount, onClick: () => setDraft(patchPaxCount(draft, band.code, value + 1)), children: "+" })] })] }, band.code));
|
|
26
|
+
return (_jsxs("div", { className: "flex items-center justify-between rounded border p-3", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: band.label }), band.minAge != null || band.maxAge != null ? (_jsx("div", { className: "text-muted-foreground text-xs", children: ageHint(band.minAge, band.maxAge, messages) })) : null] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", type: "button", disabled: value <= band.minCount, onClick: () => setDraft(patchPaxCount(draft, band.code, value - 1)), children: "\u2212" }), _jsx("span", { className: "min-w-6 text-center", children: value }), _jsx(Button, { variant: "outline", size: "sm", type: "button", disabled: value >= band.maxCount, onClick: () => setDraft(patchPaxCount(draft, band.code, value + 1)), children: "+" })] })] }, band.code));
|
|
24
27
|
}) }), _jsx(PaxValidation, { draft: draft, shape: shape })] }));
|
|
25
28
|
}
|
|
26
29
|
function PaxValidation({ draft, shape, }) {
|
|
30
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
27
31
|
const total = totalPax(draft);
|
|
28
32
|
const { min, max } = shape.paxBandsAllowedTotal;
|
|
29
33
|
if (total < min) {
|
|
30
|
-
return (
|
|
34
|
+
return (_jsx("p", { className: "text-sm text-amber-600", children: formatMessage(messages.bookingJourney.validation.addAtLeastTravelers, {
|
|
35
|
+
count: min,
|
|
36
|
+
plural: min === 1 ? "" : "s",
|
|
37
|
+
}) }));
|
|
31
38
|
}
|
|
32
39
|
if (total > max) {
|
|
33
|
-
return
|
|
40
|
+
return (_jsx("p", { className: "text-sm text-destructive", children: formatMessage(messages.bookingJourney.validation.maxTravelersPerBooking, {
|
|
41
|
+
count: max,
|
|
42
|
+
}) }));
|
|
34
43
|
}
|
|
35
44
|
return null;
|
|
36
45
|
}
|
|
@@ -64,52 +73,60 @@ function DepartureFields({ draft, setDraft, shape }) {
|
|
|
64
73
|
}) }));
|
|
65
74
|
}
|
|
66
75
|
function DepartureBasic({ draft, setDraft, }) {
|
|
67
|
-
|
|
76
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
77
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(DateField, { id: "bj-departure-date", label: messages.bookingJourney.configure.departureDate, value: draft.configure.departureDate ?? "", onChange: (v) => setDraft(patchConfigure(draft, { departureDate: v })), range: "future" }), _jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "bj-departure-time", children: messages.bookingJourney.configure.timeOptional }), _jsx(Input, { id: "bj-departure-time", type: "time", value: draft.configure.departureTime ?? "", onChange: (e) => setDraft(patchConfigure(draft, { departureTime: e.target.value })) })] })] }));
|
|
68
78
|
}
|
|
69
79
|
function DateRangeFields({ draft, setDraft, minNights, maxNights, }) {
|
|
80
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
70
81
|
const range = draft.configure.dateRange;
|
|
71
|
-
return (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(DateField, { id: "bj-checkin", label:
|
|
82
|
+
return (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(DateField, { id: "bj-checkin", label: messages.bookingJourney.configure.checkIn, value: range?.checkIn ?? "", onChange: (v) => setDraft(patchConfigure(draft, {
|
|
72
83
|
dateRange: { checkIn: v, checkOut: range?.checkOut ?? "" },
|
|
73
|
-
})), range: "future" }), _jsx(DateField, { id: "bj-checkout", label:
|
|
84
|
+
})), range: "future" }), _jsx(DateField, { id: "bj-checkout", label: formatMessage(messages.bookingJourney.configure.checkOutWithNights, {
|
|
85
|
+
minNights,
|
|
86
|
+
maxNights,
|
|
87
|
+
}), value: range?.checkOut ?? "", onChange: (v) => setDraft(patchConfigure(draft, {
|
|
74
88
|
dateRange: { checkIn: range?.checkIn ?? "", checkOut: v },
|
|
75
89
|
})), range: "future" })] }));
|
|
76
90
|
}
|
|
77
91
|
function CabinCategoryFields({ draft, setDraft, categories, }) {
|
|
78
|
-
|
|
92
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
93
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.cabinCategory }), _jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-2", children: categories.map((cat) => {
|
|
79
94
|
const selected = draft.configure.cabinCategoryId === cat.id;
|
|
80
95
|
return (_jsxs("button", { type: "button", className: `rounded border p-3 text-left ${selected ? "border-primary ring-2 ring-primary" : ""}`, onClick: () => setDraft(patchConfigure(draft, { cabinCategoryId: cat.id, cabinNumberId: undefined })), children: [_jsx("div", { className: "font-medium", children: cat.name }), cat.description ? (_jsx("div", { className: "text-muted-foreground text-xs", children: cat.description })) : null] }, cat.id));
|
|
81
96
|
}) })] }));
|
|
82
97
|
}
|
|
83
98
|
function AirArrangementFields({ draft, setDraft, }) {
|
|
99
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
84
100
|
const current = draft.configure.airArrangement;
|
|
85
101
|
const options = [
|
|
86
102
|
{
|
|
87
103
|
value: "cruise_line",
|
|
88
|
-
label:
|
|
89
|
-
description:
|
|
104
|
+
label: messages.bookingJourney.configure.airOptions.cruise_line.label,
|
|
105
|
+
description: messages.bookingJourney.configure.airOptions.cruise_line.description,
|
|
90
106
|
},
|
|
91
107
|
{
|
|
92
108
|
value: "independent",
|
|
93
|
-
label:
|
|
94
|
-
description:
|
|
109
|
+
label: messages.bookingJourney.configure.airOptions.independent.label,
|
|
110
|
+
description: messages.bookingJourney.configure.airOptions.independent.description,
|
|
95
111
|
},
|
|
96
112
|
{
|
|
97
113
|
value: "none",
|
|
98
|
-
label:
|
|
99
|
-
description:
|
|
114
|
+
label: messages.bookingJourney.configure.airOptions.none.label,
|
|
115
|
+
description: messages.bookingJourney.configure.airOptions.none.description,
|
|
100
116
|
},
|
|
101
117
|
];
|
|
102
|
-
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children:
|
|
118
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.airArrangements }), _jsx("div", { className: "grid grid-cols-1 gap-2 sm:grid-cols-3", children: options.map((opt) => {
|
|
103
119
|
const selected = current === opt.value;
|
|
104
120
|
return (_jsxs("button", { type: "button", className: `rounded border p-3 text-left text-sm ${selected ? "border-primary ring-2 ring-primary" : ""}`, onClick: () => setDraft(patchConfigure(draft, { airArrangement: opt.value })), children: [_jsx("div", { className: "font-medium", children: opt.label }), _jsx("div", { className: "text-muted-foreground text-xs", children: opt.description })] }, opt.value));
|
|
105
121
|
}) })] }));
|
|
106
122
|
}
|
|
107
123
|
function CabinNumberFields({ draft, setDraft, perCategory, }) {
|
|
124
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
108
125
|
const catId = draft.configure.cabinCategoryId;
|
|
109
126
|
if (!catId)
|
|
110
127
|
return null;
|
|
111
128
|
const cabins = perCategory[catId] ?? [];
|
|
112
|
-
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children:
|
|
129
|
+
return (_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.configure.cabinNumber }), _jsx("div", { className: "grid grid-cols-2 gap-2 sm:grid-cols-4", children: cabins.map((cabin) => {
|
|
113
130
|
const selected = draft.configure.cabinNumberId === cabin.id;
|
|
114
131
|
return (_jsx("button", { type: "button", className: `rounded border p-2 text-sm ${selected ? "border-primary ring-2 ring-primary" : ""}`, onClick: () => setDraft(patchConfigure(draft, { cabinNumberId: cabin.id })), children: cabin.label }, cabin.id));
|
|
115
132
|
}) })] }));
|
|
@@ -118,6 +135,7 @@ function CabinNumberFields({ draft, setDraft, perCategory, }) {
|
|
|
118
135
|
// Billing
|
|
119
136
|
// ─────────────────────────────────────────────────────────────────
|
|
120
137
|
export function BillingStep({ draft, setDraft, renderLeadContactPicker, renderExtras, }) {
|
|
138
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
121
139
|
const billing = draft.billing;
|
|
122
140
|
const apply = (contact) => {
|
|
123
141
|
setDraft(patchBilling(draft, {
|
|
@@ -129,11 +147,11 @@ export function BillingStep({ draft, setDraft, renderLeadContactPicker, renderEx
|
|
|
129
147
|
},
|
|
130
148
|
}));
|
|
131
149
|
};
|
|
132
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children:
|
|
150
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.billing.title }) }), _jsxs(CardContent, { className: "space-y-6", children: [_jsxs("div", { className: "space-y-2", children: [_jsx(Label, { children: messages.bookingJourney.billing.buyerType }), _jsxs(RadioGroup, { value: billing.buyerType, onValueChange: (v) => setDraft(patchBilling(draft, { buyerType: v })), className: "flex gap-4", children: [_jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(RadioGroupItem, { value: "B2C" }), " ", messages.bookingJourney.billing.individual] }), _jsxs("label", { className: "flex items-center gap-2 text-sm", children: [_jsx(RadioGroupItem, { value: "B2B" }), " ", messages.bookingJourney.billing.company] })] })] }), renderLeadContactPicker ? _jsx("div", { children: renderLeadContactPicker({ apply }) }) : null, _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-firstName", label: messages.bookingJourney.billing.firstName, value: billing.contact.firstName, onChange: (v) => setDraft(patchBilling(draft, { contact: { ...billing.contact, firstName: v } })) }), _jsx(Field, { id: "bj-billing-lastName", label: messages.bookingJourney.billing.lastName, value: billing.contact.lastName, onChange: (v) => setDraft(patchBilling(draft, { contact: { ...billing.contact, lastName: v } })) }), _jsx(Field, { id: "bj-billing-email", label: messages.bookingJourney.billing.email, type: "email", value: billing.contact.email, onChange: (v) => setDraft(patchBilling(draft, { contact: { ...billing.contact, email: v } })) }), _jsx(PhoneField, { id: "bj-billing-phone", label: messages.bookingJourney.billing.phone, value: billing.contact.phone ?? "", onChange: (v) => setDraft(patchBilling(draft, { contact: { ...billing.contact, phone: v } })) })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-line1", label: messages.bookingJourney.billing.addressLine1, value: billing.address.line1 ?? "", onChange: (v) => setDraft(patchBilling(draft, { address: { ...billing.address, line1: v } })) }), _jsx(Field, { id: "bj-billing-line2", label: messages.bookingJourney.billing.addressLine2Optional, value: billing.address.line2 ?? "", onChange: (v) => setDraft(patchBilling(draft, { address: { ...billing.address, line2: v } })) }), _jsx(Field, { id: "bj-billing-city", label: messages.bookingJourney.billing.city, value: billing.address.city ?? "", onChange: (v) => setDraft(patchBilling(draft, { address: { ...billing.address, city: v } })) }), _jsx(Field, { id: "bj-billing-postal", label: messages.bookingJourney.billing.postalCode, value: billing.address.postal ?? "", onChange: (v) => setDraft(patchBilling(draft, { address: { ...billing.address, postal: v } })) }), _jsxs("div", { className: "space-y-1 sm:col-span-2", children: [_jsx(Label, { htmlFor: "bj-billing-country", children: messages.bookingJourney.billing.country }), _jsx(CountryCombobox, { value: billing.address.country ?? null, onChange: (code) => setDraft(patchBilling(draft, {
|
|
133
151
|
address: { ...billing.address, country: code ?? "" },
|
|
134
|
-
})) })] })] }), billing.buyerType === "B2B" ? (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-companyName", label:
|
|
152
|
+
})) })] })] }), billing.buyerType === "B2B" ? (_jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: "bj-billing-companyName", label: messages.bookingJourney.billing.companyName, value: billing.company?.name ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
135
153
|
company: { ...(billing.company ?? { name: "" }), name: v },
|
|
136
|
-
})) }), _jsx(Field, { id: "bj-billing-vatId", label:
|
|
154
|
+
})) }), _jsx(Field, { id: "bj-billing-vatId", label: messages.bookingJourney.billing.vatId, value: billing.company?.vatId ?? "", onChange: (v) => setDraft(patchBilling(draft, {
|
|
137
155
|
company: { ...(billing.company ?? { name: "" }), vatId: v },
|
|
138
156
|
})) })] })) : null, renderExtras ? _jsx("div", { children: renderExtras() }) : null] })] }));
|
|
139
157
|
}
|
|
@@ -141,6 +159,7 @@ export function BillingStep({ draft, setDraft, renderLeadContactPicker, renderEx
|
|
|
141
159
|
// Travelers
|
|
142
160
|
// ─────────────────────────────────────────────────────────────────
|
|
143
161
|
export function TravelersStep({ draft, setDraft, shape, renderTravelerContactPicker, }) {
|
|
162
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
144
163
|
const total = totalPax(draft);
|
|
145
164
|
// Auto-resize the travelers list to match pax counts. Newly-added
|
|
146
165
|
// rows pick a band based on the lowest-count band that's not yet
|
|
@@ -174,7 +193,7 @@ export function TravelersStep({ draft, setDraft, shape, renderTravelerContactPic
|
|
|
174
193
|
return;
|
|
175
194
|
setDraft(patchPaxCount(draft, defaultAddBand.code, current + 1));
|
|
176
195
|
};
|
|
177
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children:
|
|
196
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.travelers.title }) }), _jsxs(CardContent, { className: "space-y-4", children: [ensured.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: messages.bookingJourney.travelers.empty })) : null, ensured.map((traveler, idx) => {
|
|
178
197
|
const apply = (contact) => {
|
|
179
198
|
const next = [...ensured];
|
|
180
199
|
next[idx] = {
|
|
@@ -190,7 +209,7 @@ export function TravelersStep({ draft, setDraft, shape, renderTravelerContactPic
|
|
|
190
209
|
const currentBandCount = draft.configure.pax?.[traveler.band] ?? 0;
|
|
191
210
|
const canRemove = !bandSpec || currentBandCount > bandSpec.minCount;
|
|
192
211
|
return (_jsx(TravelerCard, { idx: idx, traveler: traveler, shape: shape, draft: draft, setDraft: setDraft, renderTravelerContactPicker: renderTravelerContactPicker, apply: apply, onRemove: canRemove ? () => removeTraveler(idx) : undefined }, traveler.rowId ?? idx));
|
|
193
|
-
}), canAddMore ? (_jsx("div", { className: "border-t pt-3", children: _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addTraveler, children:
|
|
212
|
+
}), canAddMore ? (_jsx("div", { className: "border-t pt-3", children: _jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: addTraveler, children: messages.bookingJourney.travelers.addTraveler }) })) : null] })] }));
|
|
194
213
|
}
|
|
195
214
|
/**
|
|
196
215
|
* One traveler block — name, optional contact, age, and any
|
|
@@ -204,6 +223,7 @@ export function TravelersStep({ draft, setDraft, shape, renderTravelerContactPic
|
|
|
204
223
|
* touching the wizard.
|
|
205
224
|
*/
|
|
206
225
|
function TravelerCard({ idx, traveler, shape, draft, setDraft, renderTravelerContactPicker, apply, onRemove, }) {
|
|
226
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
207
227
|
// The band is bookkeeping — only show fields that apply to this
|
|
208
228
|
// band per the descriptor, but never expose the band tag in the UI.
|
|
209
229
|
// The user just sees a flat traveler list.
|
|
@@ -264,13 +284,24 @@ function TravelerCard({ idx, traveler, shape, draft, setDraft, renderTravelerCon
|
|
|
264
284
|
phone: billingContact.phone || undefined,
|
|
265
285
|
});
|
|
266
286
|
};
|
|
267
|
-
return (_jsxs("div", { className: "rounded border p-4 space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("div", { className: "space-y-0.5", children: _jsxs("div", { className: "font-medium", children: [
|
|
287
|
+
return (_jsxs("div", { className: "rounded border p-4 space-y-3", children: [_jsxs("div", { className: "flex flex-wrap items-center justify-between gap-2", children: [_jsx("div", { className: "space-y-0.5", children: _jsxs("div", { className: "font-medium", children: [formatMessage(messages.bookingJourney.travelers.travelerNumber, {
|
|
288
|
+
number: idx + 1,
|
|
289
|
+
}), computedAge != null ? (_jsxs("span", { className: "text-muted-foreground font-normal", children: [" ", "\u00B7", " ", formatMessage(messages.bookingJourney.travelers.ageLabel, {
|
|
290
|
+
age: computedAge,
|
|
291
|
+
})] })) : null] }) }), _jsxs("div", { className: "flex items-center gap-2", children: [canCopyFromBilling ? (_jsx(Button, { type: "button", variant: "outline", size: "sm", onClick: copyFromBilling, children: messages.bookingJourney.travelers.copyFromBilling })) : null, renderTravelerContactPicker
|
|
268
292
|
? renderTravelerContactPicker({ rowIndex: idx, apply })
|
|
269
|
-
: null, onRemove ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "text-destructive hover:text-destructive", onClick: onRemove, children:
|
|
293
|
+
: null, onRemove ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", className: "text-destructive hover:text-destructive", onClick: onRemove, children: messages.bookingJourney.travelers.remove })) : null] })] }), _jsxs("div", { className: "grid grid-cols-1 gap-3 sm:grid-cols-2", children: [_jsx(Field, { id: `bj-trav-${idx}-first`, label: messages.bookingJourney.billing.firstName, value: traveler.firstName, onChange: (v) => updateTraveler(draft, setDraft, idx, { firstName: v }) }), _jsx(Field, { id: `bj-trav-${idx}-last`, label: messages.bookingJourney.billing.lastName, value: traveler.lastName, onChange: (v) => updateTraveler(draft, setDraft, idx, { lastName: v }) }), applicableFields.some((f) => f.key === "email") ? (_jsx(Field, { id: `bj-trav-${idx}-email`, label: messages.bookingJourney.billing.email, type: "email", value: traveler.email ?? "", onChange: (v) => updateTraveler(draft, setDraft, idx, { email: v }) })) : null, phoneField ? (_jsx(PhoneField, { id: `bj-trav-${idx}-phone`,
|
|
294
|
+
// i18n-literal-ok Required marker appended to a descriptor-supplied field label.
|
|
295
|
+
label: phoneField.label + (phoneField.required ? " *" : ""), value: traveler.phone ?? "", onChange: (v) => updateTraveler(draft, setDraft, idx, { phone: v }) })) : null, dobField ? (_jsxs("div", { className: "space-y-1", children: [_jsx(DateField, { id: `bj-trav-${idx}-dob`,
|
|
296
|
+
// i18n-literal-ok Required marker appended to a descriptor-supplied field label.
|
|
297
|
+
label: dobField.label + (dobField.required ? " *" : ""), value: traveler.dateOfBirth ?? "", onChange: onDobChange, range: "past" }), ageOutOfBounds ? (_jsxs("p", { className: "text-amber-600 text-xs dark:text-amber-400", children: ["\u26A0", " ", formatMessage(messages.bookingJourney.validation.ageOutOfRange, {
|
|
298
|
+
age: computedAge,
|
|
299
|
+
})] })) : null] })) : null, dynamicFields.map((field) => {
|
|
270
300
|
const value = traveler.documents?.[field.key] ?? "";
|
|
271
301
|
const onFieldChange = (v) => updateTraveler(draft, setDraft, idx, {
|
|
272
302
|
documents: { ...traveler.documents, [field.key]: v },
|
|
273
303
|
});
|
|
304
|
+
// i18n-literal-ok Required marker appended to a descriptor-supplied field label.
|
|
274
305
|
const labelText = field.label + (field.required ? " *" : "");
|
|
275
306
|
if (field.type === "select" && field.options) {
|
|
276
307
|
return (_jsx(SelectField, { id: `bj-trav-${idx}-${field.key}`, label: labelText, value: value, options: field.options, onChange: onFieldChange }, field.key));
|
|
@@ -347,10 +378,11 @@ function rebandTraveler(draft, idx, fromBand, toBand) {
|
|
|
347
378
|
// Accommodation
|
|
348
379
|
// ─────────────────────────────────────────────────────────────────
|
|
349
380
|
export function AccommodationStep({ draft, setDraft, shape }) {
|
|
381
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
350
382
|
const subSteps = shape.accommodation?.subSteps ?? [];
|
|
351
383
|
const rooms = shape.accommodation?.roomOptions ?? [];
|
|
352
384
|
const accommodation = draft.accommodation ?? { rooms: [], travelerAssignments: {} };
|
|
353
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children:
|
|
385
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.accommodation.title }) }), _jsx(CardContent, { className: "space-y-4", children: rooms.length === 0 && subSteps.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: messages.bookingJourney.accommodation.empty })) : (_jsxs("div", { className: "space-y-3", children: [rooms.map((room) => {
|
|
354
386
|
const current = accommodation.rooms.find((r) => r.optionUnitId === room.id);
|
|
355
387
|
const ratePlans = room.ratePlans ?? [];
|
|
356
388
|
return (_jsxs("div", { className: "space-y-3 rounded border p-3", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: room.name }), room.description ? (_jsx("div", { className: "text-muted-foreground text-xs", children: room.description })) : null] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", type: "button", onClick: () => {
|
|
@@ -377,26 +409,31 @@ export function AccommodationStep({ draft, setDraft, shape }) {
|
|
|
377
409
|
const list = accommodation.rooms.map((r) => r.optionUnitId === room.id ? { ...r, ratePlanId: planId } : r);
|
|
378
410
|
setDraft(setAccommodation(draft, { ...accommodation, rooms: list }));
|
|
379
411
|
} })) : null] }, room.id));
|
|
380
|
-
}), subSteps.map((sub) => sub.kind === "extensions" ? (
|
|
412
|
+
}), subSteps.map((sub) => sub.kind === "extensions" ? (_jsx("div", { className: "rounded border p-3 text-muted-foreground text-sm", children: formatMessage(messages.bookingJourney.accommodation.extensionsAvailable, {
|
|
413
|
+
count: sub.options.length,
|
|
414
|
+
plural: sub.options.length === 1 ? "" : "s",
|
|
415
|
+
}) }, "extensions")) : null)] })) })] }));
|
|
381
416
|
}
|
|
382
417
|
function RatePlanPicker({ roomId, ratePlans, selected, onSelect, }) {
|
|
383
|
-
|
|
418
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
419
|
+
return (_jsxs("div", { className: "space-y-2 border-t pt-3", children: [_jsx(Label, { htmlFor: `bj-rate-plan-${roomId}`, children: messages.bookingJourney.accommodation.ratePlan }), _jsx("div", { className: "space-y-2", children: ratePlans.map((plan) => {
|
|
384
420
|
const isSelected = plan.id === selected;
|
|
385
|
-
return (_jsxs("button", { type: "button", onClick: () => onSelect(plan.id), className: `w-full rounded border p-2 text-left text-sm ${isSelected ? "border-primary ring-2 ring-primary" : ""}`, children: [_jsx("div", { className: "font-medium", children: plan.name }), plan.description ? (_jsx("div", { className: "text-muted-foreground text-xs", children: plan.description })) : null, plan.cancellationPolicy ? (_jsxs("div", { className: "text-muted-foreground text-xs", children: ["
|
|
421
|
+
return (_jsxs("button", { type: "button", onClick: () => onSelect(plan.id), className: `w-full rounded border p-2 text-left text-sm ${isSelected ? "border-primary ring-2 ring-primary" : ""}`, children: [_jsx("div", { className: "font-medium", children: plan.name }), plan.description ? (_jsx("div", { className: "text-muted-foreground text-xs", children: plan.description })) : null, plan.cancellationPolicy ? (_jsxs("div", { className: "text-muted-foreground text-xs", children: [messages.bookingJourney.accommodation.cancellationPrefix, " ", plan.cancellationPolicy] })) : null, plan.inclusions && plan.inclusions.length > 0 ? (_jsxs("div", { className: "text-muted-foreground text-xs", children: [messages.bookingJourney.accommodation.includesPrefix, " ", plan.inclusions.join(", ")] })) : null] }, plan.id));
|
|
386
422
|
}) })] }));
|
|
387
423
|
}
|
|
388
424
|
// ─────────────────────────────────────────────────────────────────
|
|
389
425
|
// Add-ons
|
|
390
426
|
// ─────────────────────────────────────────────────────────────────
|
|
391
427
|
export function AddonsStep({ draft, setDraft, shape }) {
|
|
428
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
392
429
|
const flat = shape.addons?.catalog ?? [];
|
|
393
430
|
const groups = shape.addons?.groups ?? [];
|
|
394
431
|
const all = [...flat, ...groups.flatMap((g) => g.items)];
|
|
395
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children:
|
|
432
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.addons.title }) }), _jsxs(CardContent, { className: "space-y-4", children: [all.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: messages.bookingJourney.addons.empty })) : null, groups.map((group) => {
|
|
396
433
|
// Group by port/day when the descriptor asks — cruise
|
|
397
434
|
// excursions arrive grouped by port name.
|
|
398
435
|
const buckets = group.groupBy === "port" || group.groupBy === "day"
|
|
399
|
-
? bucketBy(group.items, (i) => i.groupKey ??
|
|
436
|
+
? bucketBy(group.items, (i) => i.groupKey ?? messages.bookingJourney.addons.otherBucket)
|
|
400
437
|
: new Map([["", group.items]]);
|
|
401
438
|
return (_jsxs("div", { className: "space-y-3", children: [_jsx("div", { className: "font-medium text-sm", children: group.label }), [...buckets.entries()].map(([bucket, items]) => (_jsxs("div", { className: "space-y-2", children: [bucket ? (_jsx("div", { className: "text-muted-foreground text-xs uppercase", children: bucket })) : null, items.map((item) => (_jsx(AddonRow, { draft: draft, setDraft: setDraft, item: item }, item.id)))] }, bucket || "all")))] }, group.label));
|
|
402
439
|
}), flat.length > 0 && groups.length === 0 ? (_jsx("div", { className: "space-y-2", children: flat.map((item) => (_jsx(AddonRow, { draft: draft, setDraft: setDraft, item: item }, item.id))) })) : null] })] }));
|
|
@@ -420,6 +457,7 @@ function AddonRow({ draft, setDraft, item, }) {
|
|
|
420
457
|
// Payment
|
|
421
458
|
// ─────────────────────────────────────────────────────────────────
|
|
422
459
|
export function PaymentStep({ draft, setDraft, shape, capabilities, renderProviderStep, }) {
|
|
460
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
423
461
|
// The descriptor lists what the *engine* supports; capabilities
|
|
424
462
|
// narrow further to what the *deployment* turned on. Both must
|
|
425
463
|
// accept an intent for the user to see it.
|
|
@@ -431,8 +469,8 @@ export function PaymentStep({ draft, setDraft, shape, capabilities, renderProvid
|
|
|
431
469
|
if (allowed.length > 0 && !allowed.includes(intent)) {
|
|
432
470
|
setDraft(setPayment(draft, { ...draft.payment, intent: allowed[0] }));
|
|
433
471
|
}
|
|
434
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children:
|
|
435
|
-
const meta = intentMeta(i);
|
|
472
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.payment.title }) }), _jsxs(CardContent, { className: "space-y-4", children: [allowed.length === 0 ? (_jsx("p", { className: "text-muted-foreground text-sm", children: messages.bookingJourney.payment.empty })) : (_jsx(RadioGroup, { value: intent, onValueChange: (v) => setDraft(setPayment(draft, { ...draft.payment, intent: v })), className: "grid grid-cols-1 gap-2", children: allowed.map((i) => {
|
|
473
|
+
const meta = intentMeta(i, messages);
|
|
436
474
|
const selected = i === intent;
|
|
437
475
|
return (_jsxs("label", { className: "flex cursor-pointer items-start gap-3 rounded border p-3 text-sm transition-colors " +
|
|
438
476
|
(selected ? "border-primary bg-primary/5" : "border-input hover:bg-muted/50"), children: [_jsx(RadioGroupItem, { value: i, className: "mt-0.5" }), _jsxs("div", { className: "space-y-0.5", children: [_jsx("div", { className: "font-medium", children: meta.label }), _jsx("div", { className: "text-muted-foreground text-xs", children: meta.description })] })] }, i));
|
|
@@ -440,13 +478,14 @@ export function PaymentStep({ draft, setDraft, shape, capabilities, renderProvid
|
|
|
440
478
|
intent,
|
|
441
479
|
schedule: draft.payment.schedule,
|
|
442
480
|
capabilities,
|
|
443
|
-
}) })) : (_jsx("p", { className: "text-muted-foreground text-sm", children:
|
|
481
|
+
}) })) : (_jsx("p", { className: "text-muted-foreground text-sm", children: messages.bookingJourney.payment.redirectedAfterConfirm }))) : null, intent === "bank_transfer" ? _jsx(BankTransferDetails, { capabilities: capabilities }) : null, intent === "inquiry" ? (_jsx("p", { className: "rounded border border-amber-300 bg-amber-50 p-3 text-amber-900 text-xs dark:border-amber-700 dark:bg-amber-950 dark:text-amber-100", children: messages.bookingJourney.payment.inquiryNotice })) : null] })] }));
|
|
444
482
|
}
|
|
445
483
|
function BankTransferDetails({ capabilities, }) {
|
|
484
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
446
485
|
const note = capabilities.config?.bankTransferNote;
|
|
447
|
-
return (_jsxs("div", { className: "rounded border bg-muted/30 p-3 text-sm", children: [_jsx("p", { className: "font-medium", children:
|
|
486
|
+
return (_jsxs("div", { className: "rounded border bg-muted/30 p-3 text-sm", children: [_jsx("p", { className: "font-medium", children: messages.bookingJourney.payment.bankTransferInstructions }), _jsx("p", { className: "text-muted-foreground text-xs", children: typeof note === "string" && note.length > 0
|
|
448
487
|
? note
|
|
449
|
-
:
|
|
488
|
+
: messages.bookingJourney.payment.bankTransferDefaultNote })] }));
|
|
450
489
|
}
|
|
451
490
|
function isCapabilityEnabled(intent, capabilities) {
|
|
452
491
|
switch (intent) {
|
|
@@ -462,41 +501,19 @@ function isCapabilityEnabled(intent, capabilities) {
|
|
|
462
501
|
return capabilities.acceptsInquiry === true;
|
|
463
502
|
}
|
|
464
503
|
}
|
|
465
|
-
function intentMeta(intent) {
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
description: "Charged immediately. Inventory is reserved on confirmation.",
|
|
471
|
-
};
|
|
472
|
-
case "bank_transfer":
|
|
473
|
-
return {
|
|
474
|
-
label: "Bank transfer",
|
|
475
|
-
description: "We'll send you bank details and a reference. Inventory is held while we wait for the transfer.",
|
|
476
|
-
};
|
|
477
|
-
case "hold":
|
|
478
|
-
return {
|
|
479
|
-
label: "Hold for now",
|
|
480
|
-
description: "Reserve inventory without paying. The operator follows up to collect payment.",
|
|
481
|
-
};
|
|
482
|
-
case "ticket_on_credit":
|
|
483
|
-
return {
|
|
484
|
-
label: "Agency credit account",
|
|
485
|
-
description: "Charge against an agency's credit line. Operator surfaces only.",
|
|
486
|
-
};
|
|
487
|
-
case "inquiry":
|
|
488
|
-
return {
|
|
489
|
-
label: "Send as inquiry",
|
|
490
|
-
description: "No payment, no inventory hold. The operator gets back to you with availability and a quote.",
|
|
491
|
-
};
|
|
492
|
-
}
|
|
504
|
+
function intentMeta(intent, messages) {
|
|
505
|
+
return {
|
|
506
|
+
label: messages.bookingJourney.payment.intentLabels[intent],
|
|
507
|
+
description: messages.bookingJourney.payment.intentDescriptions[intent],
|
|
508
|
+
};
|
|
493
509
|
}
|
|
494
510
|
// ─────────────────────────────────────────────────────────────────
|
|
495
511
|
// Review
|
|
496
512
|
// ─────────────────────────────────────────────────────────────────
|
|
497
513
|
export function ReviewStep({ draft, setDraft, isCommitting, onConfirm, renderExtras, surface, }) {
|
|
514
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
498
515
|
const isPublic = surface === "public";
|
|
499
|
-
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children:
|
|
516
|
+
return (_jsxs(Card, { children: [_jsx(CardHeader, { children: _jsx(CardTitle, { children: messages.bookingJourney.review.title }) }), _jsxs(CardContent, { className: "space-y-4", children: [_jsxs("div", { children: [_jsx("div", { className: "font-medium", children: messages.bookingJourney.review.leadContact }), _jsxs("div", { className: "text-muted-foreground text-sm", children: [draft.billing.contact.firstName, " ", draft.billing.contact.lastName, " \u00B7", " ", draft.billing.contact.email] })] }), _jsxs("div", { children: [_jsx("div", { className: "font-medium", children: messages.bookingJourney.review.travelers }), _jsx("ul", { className: "text-muted-foreground text-sm", children: draft.travelers.map((t, i) => (_jsxs("li", { children: [t.firstName, " ", t.lastName, " (", t.band, ")"] }, t.rowId ?? i))) })] }), isPublic ? (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "bj-customer-notes", children: messages.bookingJourney.review.customerNotes }), _jsx(Textarea, { id: "bj-customer-notes", placeholder: messages.bookingJourney.review.customerNotesPlaceholder, value: draft.customerNotes ?? "", onChange: (e) => setDraft({ ...draft, customerNotes: e.target.value }) })] })) : (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: "bj-internal-notes", children: messages.bookingJourney.review.internalNotes }), _jsx(Textarea, { id: "bj-internal-notes", value: draft.internalNotes ?? "", onChange: (e) => setDraft({ ...draft, internalNotes: e.target.value }) })] })), renderExtras ? _jsx("div", { children: renderExtras() }) : null, _jsx(Button, { onClick: onConfirm, disabled: isCommitting, children: isCommitting ? (_jsxs(_Fragment, { children: [_jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), messages.bookingJourney.review.confirming] })) : (messages.bookingJourney.review.confirmBooking) })] })] }));
|
|
500
517
|
}
|
|
501
518
|
// ─────────────────────────────────────────────────────────────────
|
|
502
519
|
// Helpers
|
|
@@ -534,7 +551,8 @@ function DateField({ id, label, value, onChange, range = "future", }) {
|
|
|
534
551
|
return (_jsxs("div", { className: "space-y-1", id: id, children: [_jsx(Label, { children: label }), _jsx(DatePicker, { value: value || null, onChange: (v) => onChange(v ?? ""), captionLayout: "dropdown", startMonth: startMonth, endMonth: endMonth, defaultMonth: defaultMonth, displayFormat: "PPP" })] }));
|
|
535
552
|
}
|
|
536
553
|
function SelectField({ id, label, value, options, onChange, }) {
|
|
537
|
-
|
|
554
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
555
|
+
return (_jsxs("div", { className: "space-y-1", children: [_jsx(Label, { htmlFor: id, children: label }), _jsxs("select", { id: id, className: "flex h-9 w-full rounded-md border border-input bg-transparent px-3 py-1 text-sm shadow-sm transition-colors focus-visible:outline-none focus-visible:ring-1 focus-visible:ring-ring disabled:cursor-not-allowed disabled:opacity-50", value: value, onChange: (e) => onChange(e.target.value), children: [_jsx("option", { value: "", children: messages.bookingJourney.values.selectPlaceholder }), options.map((opt) => (_jsx("option", { value: opt.value, children: opt.label }, opt.value)))] })] }));
|
|
538
556
|
}
|
|
539
557
|
/**
|
|
540
558
|
* Years between an ISO date-of-birth and today. Returns `null` for
|
|
@@ -552,15 +570,19 @@ function computeAge(dob) {
|
|
|
552
570
|
age -= 1;
|
|
553
571
|
return age >= 0 ? age : null;
|
|
554
572
|
}
|
|
555
|
-
function ageHint(min, max) {
|
|
556
|
-
if (min != null && max != null)
|
|
557
|
-
return
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
573
|
+
function ageHint(min, max, messages) {
|
|
574
|
+
if (min != null && max != null) {
|
|
575
|
+
return formatMessage(messages.bookingJourney.configure.ageHintRange, { min, max });
|
|
576
|
+
}
|
|
577
|
+
if (min != null) {
|
|
578
|
+
return formatMessage(messages.bookingJourney.configure.ageHintMinimum, { min });
|
|
579
|
+
}
|
|
580
|
+
if (max != null) {
|
|
581
|
+
return formatMessage(messages.bookingJourney.configure.ageHintMaximum, { max });
|
|
582
|
+
}
|
|
562
583
|
return "";
|
|
563
584
|
}
|
|
585
|
+
// i18n-literal-ok Generic helper type signature, not user-visible copy.
|
|
564
586
|
function bucketBy(items, keyFn) {
|
|
565
587
|
const map = new Map();
|
|
566
588
|
for (const item of items) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"side-panel.d.ts","sourceRoot":"","sources":["../../../src/journey/components/side-panel.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAqC,cAAc,EAAE,MAAM,aAAa,CAAA;AAEpF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,SAAS,EACT,aAAa,EACb,aAAa,EACb,WAAW,EACX,KAAK,EACL,KAAK,EACL,SAAS,GACV,EAAE,cAAc,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,
|
|
1
|
+
{"version":3,"file":"side-panel.d.ts","sourceRoot":"","sources":["../../../src/journey/components/side-panel.tsx"],"names":[],"mappings":"AAWA,OAAO,KAAK,EAAqC,cAAc,EAAE,MAAM,aAAa,CAAA;AAEpF;;;;;;GAMG;AACH,wBAAgB,cAAc,CAAC,EAC7B,OAAO,EACP,SAAS,EACT,aAAa,EACb,aAAa,EACb,WAAW,EACX,KAAK,EACL,KAAK,EACL,SAAS,GACV,EAAE,cAAc,GAAG;IAAE,SAAS,CAAC,EAAE,MAAM,CAAA;CAAE,GAAG,KAAK,CAAC,YAAY,CAuD9D"}
|