@voyantjs/bookings-ui 0.16.0 → 0.17.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -0
- package/dist/components/booking-activity-timeline.d.ts.map +1 -1
- package/dist/components/booking-activity-timeline.js +34 -14
- package/dist/components/booking-cancellation-dialog.d.ts.map +1 -1
- package/dist/components/booking-cancellation-dialog.js +15 -16
- package/dist/components/booking-create-dialog.d.ts.map +1 -1
- package/dist/components/booking-create-dialog.js +77 -13
- package/dist/components/booking-dialog.d.ts.map +1 -1
- package/dist/components/booking-dialog.js +27 -21
- package/dist/components/booking-document-dialog.d.ts.map +1 -1
- package/dist/components/booking-document-dialog.js +27 -13
- package/dist/components/booking-document-list.d.ts.map +1 -1
- package/dist/components/booking-document-list.js +9 -4
- package/dist/components/booking-group-link-dialog.d.ts.map +1 -1
- package/dist/components/booking-group-link-dialog.js +17 -6
- package/dist/components/booking-group-section.d.ts.map +1 -1
- package/dist/components/booking-group-section.js +8 -2
- package/dist/components/booking-guarantee-dialog.d.ts.map +1 -1
- package/dist/components/booking-guarantee-dialog.js +30 -14
- package/dist/components/booking-guarantee-list.d.ts.map +1 -1
- package/dist/components/booking-guarantee-list.js +11 -8
- package/dist/components/booking-item-dialog.d.ts.map +1 -1
- package/dist/components/booking-item-dialog.js +34 -20
- package/dist/components/booking-item-list.d.ts.map +1 -1
- package/dist/components/booking-item-list.js +10 -9
- package/dist/components/booking-item-travelers.d.ts.map +1 -1
- package/dist/components/booking-item-travelers.js +9 -4
- package/dist/components/booking-list.d.ts.map +1 -1
- package/dist/components/booking-list.js +17 -8
- package/dist/components/booking-notes.d.ts.map +1 -1
- package/dist/components/booking-notes.js +5 -2
- package/dist/components/booking-payment-schedule-dialog.d.ts.map +1 -1
- package/dist/components/booking-payment-schedule-dialog.js +31 -12
- package/dist/components/booking-payment-schedule-list.d.ts.map +1 -1
- package/dist/components/booking-payment-schedule-list.js +7 -6
- package/dist/components/booking-payments-summary.d.ts.map +1 -1
- package/dist/components/booking-payments-summary.js +7 -4
- package/dist/components/file-dropzone.d.ts.map +1 -1
- package/dist/components/file-dropzone.js +25 -15
- package/dist/components/passengers-section.d.ts.map +1 -1
- package/dist/components/passengers-section.js +3 -17
- package/dist/components/payment-schedule-section.d.ts.map +1 -1
- package/dist/components/payment-schedule-section.js +3 -14
- package/dist/components/person-picker-section.d.ts.map +1 -1
- package/dist/components/person-picker-section.js +3 -19
- package/dist/components/price-breakdown-section.d.ts.map +1 -1
- package/dist/components/price-breakdown-section.js +15 -18
- package/dist/components/product-picker-section.d.ts.map +1 -1
- package/dist/components/product-picker-section.js +3 -8
- package/dist/components/rooms-stepper-section.d.ts.map +1 -1
- package/dist/components/rooms-stepper-section.js +4 -9
- package/dist/components/shared-room-section.d.ts.map +1 -1
- package/dist/components/shared-room-section.js +3 -9
- package/dist/components/status-change-dialog.d.ts.map +1 -1
- package/dist/components/status-change-dialog.js +6 -1
- package/dist/components/supplier-status-dialog.d.ts.map +1 -1
- package/dist/components/supplier-status-dialog.js +31 -15
- package/dist/components/supplier-status-list.d.ts.map +1 -1
- package/dist/components/supplier-status-list.js +10 -2
- package/dist/components/traveler-dialog.d.ts.map +1 -1
- package/dist/components/traveler-dialog.js +17 -8
- package/dist/components/traveler-list.d.ts.map +1 -1
- package/dist/components/traveler-list.js +5 -3
- package/dist/components/voucher-picker-section.d.ts.map +1 -1
- package/dist/components/voucher-picker-section.js +11 -26
- package/dist/i18n/en.d.ts +797 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +796 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/messages.d.ts +684 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +1 -0
- package/dist/i18n/provider.d.ts +1617 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +45 -0
- package/dist/i18n/ro.d.ts +797 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +796 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +32 -17
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"product-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/product-picker-section.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"product-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/product-picker-section.tsx"],"names":[],"mappings":"AAiBA,MAAM,WAAW,kBAAkB;IACjC,SAAS,EAAE,MAAM,CAAA;IACjB,uFAAuF;IACvF,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;CACxB;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C,mEAAmE;IACnE,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,qGAAqG;IACrG,WAAW,CAAC,EAAE,OAAO,CAAA;IACrB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,wBAAwB,CAAC,EAAE,MAAM,CAAA;QACjC,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;;;GAIG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,OAAc,EACd,WAAmB,EACnB,MAAM,GACP,EAAE,yBAAyB,2CAkF3B"}
|
|
@@ -3,14 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-run
|
|
|
3
3
|
import { useProductOptions, useProducts } from "@voyantjs/products-react";
|
|
4
4
|
import { Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
|
|
5
5
|
import * as React from "react";
|
|
6
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
6
7
|
const OPTION_NONE = "__none__";
|
|
7
|
-
const DEFAULT_LABELS = {
|
|
8
|
-
product: "Product",
|
|
9
|
-
productSearchPlaceholder: "Search products...",
|
|
10
|
-
productSelectPlaceholder: "Select a product...",
|
|
11
|
-
option: "Option",
|
|
12
|
-
optionNone: "No specific option",
|
|
13
|
-
};
|
|
14
8
|
/**
|
|
15
9
|
* Controlled product + option picker. Splits `value` + `onChange` so apps can
|
|
16
10
|
* replace the whole section (e.g., with a typeahead against a custom catalog)
|
|
@@ -18,7 +12,8 @@ const DEFAULT_LABELS = {
|
|
|
18
12
|
*/
|
|
19
13
|
export function ProductPickerSection({ value, onChange, enabled = true, lockProduct = false, labels, }) {
|
|
20
14
|
const [productSearch, setProductSearch] = React.useState("");
|
|
21
|
-
const
|
|
15
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
16
|
+
const merged = { ...messages.productPickerSection.labels, ...labels };
|
|
22
17
|
const { data: productsData } = useProducts({
|
|
23
18
|
search: productSearch || undefined,
|
|
24
19
|
limit: 20,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rooms-stepper-section.d.ts","sourceRoot":"","sources":["../../src/components/rooms-stepper-section.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rooms-stepper-section.d.ts","sourceRoot":"","sources":["../../src/components/rooms-stepper-section.tsx"],"names":[],"mappings":"AAOA,iEAAiE;AACjE,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAA;CACnC;AAED,eAAO,MAAM,sBAAsB,EAAE,iBAAsC,CAAA;AAE3E,MAAM,WAAW,wBAAwB;IACvC,KAAK,EAAE,iBAAiB,CAAA;IACxB,QAAQ,EAAE,CAAC,KAAK,EAAE,iBAAiB,KAAK,IAAI,CAAA;IAC5C;;;;OAIG;IACH,MAAM,CAAC,EAAE,MAAM,CAAA;IACf,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,SAAS,CAAC,EAAE,MAAM,CAAA;QAClB,SAAS,CAAC,EAAE,MAAM,CAAA;KACnB,CAAA;CACF;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH,wBAAgB,mBAAmB,CAAC,EAClC,KAAK,EACL,QAAQ,EACR,MAAM,EACN,OAAc,EACd,MAAM,GACP,EAAE,wBAAwB,2CAoF1B"}
|
|
@@ -3,14 +3,8 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { useSlotUnitAvailability } from "@voyantjs/availability-react";
|
|
4
4
|
import { Button, Label } from "@voyantjs/ui/components";
|
|
5
5
|
import { Minus, Plus } from "lucide-react";
|
|
6
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
6
7
|
export const emptyRoomsStepperValue = { quantities: {} };
|
|
7
|
-
const DEFAULT_LABELS = {
|
|
8
|
-
heading: "Rooms",
|
|
9
|
-
noSlot: "Pick a departure first to see available rooms.",
|
|
10
|
-
noUnits: "This departure has no per-unit availability configured.",
|
|
11
|
-
remaining: "left",
|
|
12
|
-
unlimited: "unlimited",
|
|
13
|
-
};
|
|
14
8
|
/**
|
|
15
9
|
* Rooms / per-unit stepper for booking-create flows. Drives
|
|
16
10
|
* `GET /v1/availability/slots/:id/unit-availability` from #235 so the
|
|
@@ -32,7 +26,8 @@ const DEFAULT_LABELS = {
|
|
|
32
26
|
* would 409 at insert time.
|
|
33
27
|
*/
|
|
34
28
|
export function RoomsStepperSection({ value, onChange, slotId, enabled = true, labels, }) {
|
|
35
|
-
const
|
|
29
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
30
|
+
const merged = { ...messages.roomsStepperSection.labels, ...labels };
|
|
36
31
|
const availability = useSlotUnitAvailability({ slotId, enabled: enabled && Boolean(slotId) });
|
|
37
32
|
const units = availability.data?.data ?? [];
|
|
38
33
|
if (!slotId) {
|
|
@@ -55,6 +50,6 @@ export function RoomsStepperSection({ value, onChange, slotId, enabled = true, l
|
|
|
55
50
|
const qty = value.quantities[unit.optionUnitId] ?? 0;
|
|
56
51
|
const remainingLabel = unit.remaining === null ? merged.unlimited : `${unit.remaining} ${merged.remaining}`;
|
|
57
52
|
const atMax = unit.remaining !== null && qty >= unit.remaining;
|
|
58
|
-
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: unit.unitName }), _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(unit.optionUnitId, Math.max(0, qty - 1)), disabled: qty <= 0, "aria-label":
|
|
53
|
+
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: unit.unitName }), _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(unit.optionUnitId, Math.max(0, qty - 1)), disabled: qty <= 0, "aria-label": `${merged.decreaseUnitPrefix} ${unit.unitName}`, 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(unit.optionUnitId, qty + 1), disabled: atMax, "aria-label": `${merged.increaseUnitPrefix} ${unit.unitName}`, children: _jsx(Plus, { className: "h-3.5 w-3.5" }) })] })] }, unit.optionUnitId));
|
|
59
54
|
}) })] }));
|
|
60
55
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shared-room-section.d.ts","sourceRoot":"","sources":["../../src/components/shared-room-section.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shared-room-section.d.ts","sourceRoot":"","sources":["../../src/components/shared-room-section.tsx"],"names":[],"mappings":"AAiBA,MAAM,MAAM,cAAc,GAAG,QAAQ,GAAG,MAAM,CAAA;AAE9C,MAAM,WAAW,eAAe;IAC9B,OAAO,EAAE,OAAO,CAAA;IAChB,IAAI,EAAE,cAAc,CAAA;IACpB,sCAAsC;IACtC,OAAO,EAAE,MAAM,CAAA;CAChB;AAED,eAAO,MAAM,oBAAoB,EAAE,eAIlC,CAAA;AAED,MAAM,WAAW,sBAAsB;IACrC,KAAK,EAAE,eAAe,CAAA;IACtB,QAAQ,EAAE,CAAC,KAAK,EAAE,eAAe,KAAK,IAAI,CAAA;IAC1C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,OAAO,CAAC,EAAE,OAAO,CAAA;IACjB,MAAM,CAAC,EAAE;QACP,MAAM,CAAC,EAAE,MAAM,CAAA;QACf,UAAU,CAAC,EAAE,MAAM,CAAA;QACnB,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,iBAAiB,CAAC,EAAE,MAAM,CAAA;QAC1B,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAED;;;;;;;;GAQG;AACH,wBAAgB,iBAAiB,CAAC,EAChC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,OAAc,EACd,MAAM,GACP,EAAE,sBAAsB,2CAoFxB"}
|
|
@@ -2,20 +2,13 @@
|
|
|
2
2
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { useBookingGroups } from "@voyantjs/bookings-react";
|
|
4
4
|
import { Button, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, } from "@voyantjs/ui/components";
|
|
5
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
5
6
|
const GROUP_NONE = "__none__";
|
|
6
7
|
export const emptySharedRoomValue = {
|
|
7
8
|
enabled: false,
|
|
8
9
|
mode: "create",
|
|
9
10
|
groupId: "",
|
|
10
11
|
};
|
|
11
|
-
const DEFAULT_LABELS = {
|
|
12
|
-
toggle: "Link to a shared-room group",
|
|
13
|
-
createMode: "Create new group",
|
|
14
|
-
joinMode: "Join existing",
|
|
15
|
-
selectPlaceholder: "Select a group...",
|
|
16
|
-
noGroups: "No existing groups for this product",
|
|
17
|
-
createHint: "A new group will be created with this booking as the primary member.",
|
|
18
|
-
};
|
|
19
12
|
/**
|
|
20
13
|
* Shared-room (partaj) attachment section. Operators use it to either create a
|
|
21
14
|
* new `booking_groups` row at booking-create time or join an existing group
|
|
@@ -26,7 +19,8 @@ const DEFAULT_LABELS = {
|
|
|
26
19
|
* insert (we need the new booking id to attach).
|
|
27
20
|
*/
|
|
28
21
|
export function SharedRoomSection({ value, onChange, productId, enabled = true, labels, }) {
|
|
29
|
-
const
|
|
22
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
23
|
+
const merged = { ...messages.sharedRoomSection.labels, ...labels };
|
|
30
24
|
const { data: groupsData } = useBookingGroups({
|
|
31
25
|
productId: productId || undefined,
|
|
32
26
|
limit: 50,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"status-change-dialog.d.ts","sourceRoot":"","sources":["../../src/components/status-change-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"status-change-dialog.d.ts","sourceRoot":"","sources":["../../src/components/status-change-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,aAAa,EAInB,MAAM,0BAA0B,CAAA;AAgCjC,MAAM,WAAW,uBAAuB;IACtC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,aAAa,EAAE,aAAa,CAAC,QAAQ,CAAC,CAAA;IACtC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,kBAAkB,CAAC,EACjC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,aAAa,EACb,SAAS,GACV,EAAE,uBAAuB,2CA+FzB"}
|
|
@@ -7,12 +7,14 @@ import { Loader2 } from "lucide-react";
|
|
|
7
7
|
import { useEffect } from "react";
|
|
8
8
|
import { useForm } from "react-hook-form";
|
|
9
9
|
import { z } from "zod/v4";
|
|
10
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
10
11
|
const statusChangeFormSchema = z.object({
|
|
11
12
|
status: bookingStatusSchema,
|
|
12
13
|
note: z.string().optional().nullable(),
|
|
13
14
|
});
|
|
14
15
|
export function StatusChangeDialog({ open, onOpenChange, bookingId, currentStatus, onSuccess, }) {
|
|
15
16
|
const mutation = useBookingStatusMutation(bookingId);
|
|
17
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
16
18
|
const form = useForm({
|
|
17
19
|
resolver: zodResolver(statusChangeFormSchema),
|
|
18
20
|
defaultValues: {
|
|
@@ -37,5 +39,8 @@ export function StatusChangeDialog({ open, onOpenChange, bookingId, currentStatu
|
|
|
37
39
|
onOpenChange(false);
|
|
38
40
|
onSuccess?.();
|
|
39
41
|
};
|
|
40
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children:
|
|
42
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: messages.statusChangeDialog.title }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.statusChangeDialog.fields.status }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), items: bookingStatusOptions.map((status) => ({
|
|
43
|
+
...status,
|
|
44
|
+
label: messages.common.bookingStatusLabels[status.value],
|
|
45
|
+
})), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: bookingStatusOptions.map((status) => (_jsx(SelectItem, { value: status.value, children: messages.common.bookingStatusLabels[status.value] }, status.value))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.statusChangeDialog.fields.note }), _jsx(Textarea, { ...form.register("note"), placeholder: messages.statusChangeDialog.placeholders.note })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", size: "sm", disabled: mutation.isPending, children: [mutation.isPending && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), messages.statusChangeDialog.actions.updateStatus] })] })] })] }) }));
|
|
41
46
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supplier-status-dialog.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,2BAA2B,EAEjC,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"supplier-status-dialog.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,2BAA2B,EAEjC,MAAM,0BAA0B,CAAA;AA6CjC,MAAM,WAAW,yBAAyB;IACxC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,cAAc,CAAC,EAAE,2BAA2B,CAAA;IAC5C,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAUD,wBAAgB,oBAAoB,CAAC,EACnC,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,cAAc,EACd,SAAS,GACV,EAAE,yBAAyB,2CAoK3B"}
|
|
@@ -8,30 +8,39 @@ import { Loader2 } from "lucide-react";
|
|
|
8
8
|
import { useEffect } from "react";
|
|
9
9
|
import { useForm } from "react-hook-form";
|
|
10
10
|
import { z } from "zod/v4";
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
11
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
12
|
+
function createSupplierStatusFormSchema(messages) {
|
|
13
|
+
return z.object({
|
|
14
|
+
serviceName: z.string().min(1, messages.supplierStatusDialog.validation.serviceNameRequired),
|
|
15
|
+
status: z.enum(["pending", "confirmed", "rejected", "cancelled"]),
|
|
16
|
+
supplierReference: z.string().optional().nullable(),
|
|
17
|
+
costCurrency: z
|
|
18
|
+
.string()
|
|
19
|
+
.min(3)
|
|
20
|
+
.max(3, messages.supplierStatusDialog.validation.costCurrencyInvalid),
|
|
21
|
+
costAmountCents: z.coerce.number().int().min(0),
|
|
22
|
+
notes: z.string().optional().nullable(),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
19
25
|
const CONFIRMATION_STATUSES = [
|
|
20
|
-
{ value: "pending"
|
|
21
|
-
{ value: "confirmed"
|
|
22
|
-
{ value: "rejected"
|
|
23
|
-
{ value: "cancelled"
|
|
26
|
+
{ value: "pending" },
|
|
27
|
+
{ value: "confirmed" },
|
|
28
|
+
{ value: "rejected" },
|
|
29
|
+
{ value: "cancelled" },
|
|
24
30
|
];
|
|
31
|
+
const DEFAULT_CURRENCY = "EUR"; // i18n-literal-ok ISO default currency
|
|
25
32
|
export function SupplierStatusDialog({ open, onOpenChange, bookingId, supplierStatus, onSuccess, }) {
|
|
26
33
|
const isEditing = Boolean(supplierStatus);
|
|
27
34
|
const { create, update } = useSupplierStatusMutation(bookingId);
|
|
35
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
36
|
+
const supplierStatusFormSchema = createSupplierStatusFormSchema(messages);
|
|
28
37
|
const form = useForm({
|
|
29
38
|
resolver: zodResolver(supplierStatusFormSchema),
|
|
30
39
|
defaultValues: {
|
|
31
40
|
serviceName: "",
|
|
32
41
|
status: "pending",
|
|
33
42
|
supplierReference: "",
|
|
34
|
-
costCurrency:
|
|
43
|
+
costCurrency: DEFAULT_CURRENCY,
|
|
35
44
|
costAmountCents: 0,
|
|
36
45
|
notes: "",
|
|
37
46
|
},
|
|
@@ -70,8 +79,15 @@ export function SupplierStatusDialog({ open, onOpenChange, bookingId, supplierSt
|
|
|
70
79
|
onSuccess?.();
|
|
71
80
|
};
|
|
72
81
|
const isSubmitting = create.isPending || update.isPending;
|
|
73
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
82
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
83
|
+
? messages.supplierStatusDialog.titles.edit
|
|
84
|
+
: messages.supplierStatusDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.serviceName }), _jsx(Input, { ...form.register("serviceName"), placeholder: messages.supplierStatusDialog.placeholders.serviceName, disabled: isEditing }), form.formState.errors.serviceName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.serviceName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.status }), _jsxs(Select, { value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), items: CONFIRMATION_STATUSES.map((status) => ({
|
|
85
|
+
...status,
|
|
86
|
+
label: messages.common.supplierStatusLabels[status.value],
|
|
87
|
+
})), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CONFIRMATION_STATUSES.map((status) => (_jsx(SelectItem, { value: status.value, children: messages.common.supplierStatusLabels[status.value] }, status.value))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.costCurrency }), _jsx(CurrencyCombobox, { value: form.watch("costCurrency") || null, onChange: (next) => form.setValue("costCurrency", next ?? DEFAULT_CURRENCY, {
|
|
74
88
|
shouldValidate: true,
|
|
75
89
|
shouldDirty: true,
|
|
76
|
-
}) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children:
|
|
90
|
+
}) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.costAmountCents }), _jsx(Input, { ...form.register("costAmountCents", { valueAsNumber: true }), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.supplierReference }), _jsx(Input, { ...form.register("supplierReference"), placeholder: messages.supplierStatusDialog.placeholders.supplierReference })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.supplierStatusDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), placeholder: messages.supplierStatusDialog.placeholders.notes })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", size: "sm", disabled: isSubmitting, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing
|
|
91
|
+
? messages.common.saveChanges
|
|
92
|
+
: messages.supplierStatusDialog.actions.addSupplierStatus] })] })] })] }) }));
|
|
77
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"supplier-status-list.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-list.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"supplier-status-list.d.ts","sourceRoot":"","sources":["../../src/components/supplier-status-list.tsx"],"names":[],"mappings":"AAUA,MAAM,WAAW,uBAAuB;IACtC,SAAS,EAAE,MAAM,CAAA;CAClB;AASD,wBAAgB,kBAAkB,CAAC,EAAE,SAAS,EAAE,EAAE,uBAAuB,2CA+GxE"}
|
|
@@ -4,6 +4,7 @@ import { useSupplierStatuses } from "@voyantjs/bookings-react";
|
|
|
4
4
|
import { Badge, Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
|
|
5
5
|
import { Pencil, Plus } from "lucide-react";
|
|
6
6
|
import * as React from "react";
|
|
7
|
+
import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
7
8
|
import { SupplierStatusDialog } from "./supplier-status-dialog";
|
|
8
9
|
const supplierStatusVariant = {
|
|
9
10
|
pending: "outline",
|
|
@@ -15,11 +16,18 @@ export function SupplierStatusList({ bookingId }) {
|
|
|
15
16
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
16
17
|
const [editing, setEditing] = React.useState(undefined);
|
|
17
18
|
const { data } = useSupplierStatuses(bookingId);
|
|
19
|
+
const { formatCurrency, formatDate } = useBookingsUiI18nOrDefault();
|
|
20
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
18
21
|
const statuses = data?.data ?? [];
|
|
19
|
-
return (_jsxs(Card, { "data-slot": "supplier-status-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsx(CardTitle, { children:
|
|
22
|
+
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: () => {
|
|
20
23
|
setEditing(undefined);
|
|
21
24
|
setDialogOpen(true);
|
|
22
|
-
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }),
|
|
25
|
+
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.supplierStatusList.addSupplier] })] }), _jsx(CardContent, { children: statuses.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.supplierStatusList.empty })) : (_jsx("div", { className: "rounded border bg-background", children: _jsxs("table", { className: "w-full text-sm", children: [_jsx("thead", { children: _jsxs("tr", { className: "border-b text-muted-foreground", children: [_jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.service }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.status }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.cost }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.reference }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.supplierStatusList.columns.confirmed }), _jsx("th", { className: "w-12 p-2" })] }) }), _jsx("tbody", { children: statuses.map((status) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-2", children: status.serviceName }), _jsx("td", { className: "p-2", children: _jsx(Badge, { variant: supplierStatusVariant[status.status] ?? "secondary", children: messages.common.supplierStatusLabels[status.status] }) }), _jsx("td", { className: "p-2 font-mono", children: status.costAmountCents == null || !status.costCurrency
|
|
26
|
+
? messages.supplierStatusList.values.costUnavailable
|
|
27
|
+
: formatCurrency(status.costAmountCents / 100, status.costCurrency) }), _jsx("td", { className: "p-2", children: status.supplierReference ??
|
|
28
|
+
messages.supplierStatusList.values.referenceUnavailable }), _jsx("td", { className: "p-2", children: status.confirmedAt
|
|
29
|
+
? formatDate(status.confirmedAt)
|
|
30
|
+
: messages.supplierStatusList.values.confirmedUnavailable }), _jsx("td", { className: "p-2", children: _jsx("button", { type: "button", onClick: () => {
|
|
23
31
|
setEditing(status);
|
|
24
32
|
setDialogOpen(true);
|
|
25
33
|
}, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }) })] }, status.id))) })] }) })) }), _jsx(SupplierStatusDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traveler-dialog.d.ts","sourceRoot":"","sources":["../../src/components/traveler-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAAuB,MAAM,0BAA0B,CAAA;
|
|
1
|
+
{"version":3,"file":"traveler-dialog.d.ts","sourceRoot":"","sources":["../../src/components/traveler-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAAuB,MAAM,0BAA0B,CAAA;AAkC1F,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,CAAC,EAAE,qBAAqB,CAAA;IAChC,SAAS,CAAC,EAAE,MAAM,IAAI,CAAA;CACvB;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,SAAS,EACT,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,2CAwIrB"}
|
|
@@ -7,16 +7,21 @@ import { Loader2 } from "lucide-react";
|
|
|
7
7
|
import { useEffect } from "react";
|
|
8
8
|
import { useForm } from "react-hook-form";
|
|
9
9
|
import { z } from "zod/v4";
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
10
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
11
|
+
function createTravelerFormSchema(messages) {
|
|
12
|
+
return z.object({
|
|
13
|
+
firstName: z.string().min(1, messages.travelerDialog.validation.firstNameRequired),
|
|
14
|
+
lastName: z.string().min(1, messages.travelerDialog.validation.lastNameRequired),
|
|
15
|
+
email: z.string().email().optional().or(z.literal("")).nullable(),
|
|
16
|
+
phone: z.string().optional().nullable(),
|
|
17
|
+
specialRequests: z.string().optional().nullable(),
|
|
18
|
+
});
|
|
19
|
+
}
|
|
17
20
|
export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSuccess, }) {
|
|
18
21
|
const isEditing = Boolean(traveler);
|
|
19
22
|
const { create, update } = useTravelerMutation(bookingId);
|
|
23
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
24
|
+
const travelerFormSchema = createTravelerFormSchema(messages);
|
|
20
25
|
const form = useForm({
|
|
21
26
|
resolver: zodResolver(travelerFormSchema),
|
|
22
27
|
defaultValues: {
|
|
@@ -60,5 +65,9 @@ export function TravelerDialog({ open, onOpenChange, bookingId, traveler, onSucc
|
|
|
60
65
|
onSuccess?.();
|
|
61
66
|
};
|
|
62
67
|
const isSubmitting = create.isPending || update.isPending;
|
|
63
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
68
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
69
|
+
? messages.travelerDialog.titles.edit
|
|
70
|
+
: messages.travelerDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), className: "flex flex-1 flex-col overflow-hidden", children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.firstName }), _jsx(Input, { ...form.register("firstName"), placeholder: messages.travelerDialog.placeholders.firstName }), form.formState.errors.firstName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.firstName.message }))] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.lastName }), _jsx(Input, { ...form.register("lastName"), placeholder: messages.travelerDialog.placeholders.lastName }), form.formState.errors.lastName && (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.lastName.message }))] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.email }), _jsx(Input, { ...form.register("email"), type: "email", placeholder: messages.travelerDialog.placeholders.email })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.phone }), _jsx(Input, { ...form.register("phone"), placeholder: messages.travelerDialog.placeholders.phone })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.travelerDialog.fields.specialRequests }), _jsx(Textarea, { ...form.register("specialRequests"), placeholder: messages.travelerDialog.placeholders.specialRequests })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", size: "sm", disabled: isSubmitting, children: [isSubmitting && _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isEditing
|
|
71
|
+
? messages.common.saveChanges
|
|
72
|
+
: messages.travelerDialog.actions.addTraveler] })] })] })] }) }));
|
|
64
73
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"traveler-list.d.ts","sourceRoot":"","sources":["../../src/components/traveler-list.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"traveler-list.d.ts","sourceRoot":"","sources":["../../src/components/traveler-list.tsx"],"names":[],"mappings":"AAcA,MAAM,WAAW,iBAAiB;IAChC,SAAS,EAAE,MAAM,CAAA;CAClB;AAED,wBAAgB,YAAY,CAAC,EAAE,SAAS,EAAE,EAAE,iBAAiB,2CA8G5D"}
|
|
@@ -4,21 +4,23 @@ import { useTravelerMutation, useTravelers, } from "@voyantjs/bookings-react";
|
|
|
4
4
|
import { Button, Card, CardContent, CardHeader, CardTitle } from "@voyantjs/ui/components";
|
|
5
5
|
import { Pencil, Plus, Trash2, Users } from "lucide-react";
|
|
6
6
|
import * as React from "react";
|
|
7
|
+
import { useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
7
8
|
import { TravelerDialog } from "./traveler-dialog";
|
|
8
9
|
export function TravelerList({ bookingId }) {
|
|
9
10
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
10
11
|
const [editing, setEditing] = React.useState(undefined);
|
|
11
12
|
const { data } = useTravelers(bookingId);
|
|
12
13
|
const { remove } = useTravelerMutation(bookingId);
|
|
14
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
13
15
|
const travelers = data?.data ?? [];
|
|
14
|
-
return (_jsxs(Card, { "data-slot": "traveler-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4" }),
|
|
16
|
+
return (_jsxs(Card, { "data-slot": "traveler-list", children: [_jsxs(CardHeader, { className: "flex flex-row items-center justify-between", children: [_jsxs(CardTitle, { className: "flex items-center gap-2", children: [_jsx(Users, { className: "h-4 w-4" }), messages.travelerList.title] }), _jsxs(Button, { size: "sm", onClick: () => {
|
|
15
17
|
setEditing(undefined);
|
|
16
18
|
setDialogOpen(true);
|
|
17
|
-
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }),
|
|
19
|
+
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.travelerList.addTraveler] })] }), _jsx(CardContent, { children: travelers.length === 0 ? (_jsx("p", { className: "py-4 text-center text-sm text-muted-foreground", children: messages.travelerList.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.travelerList.columns.name }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.travelerList.columns.email }), _jsx("th", { className: "p-2 text-left font-medium", children: messages.travelerList.columns.phone }), _jsx("th", { className: "w-20 p-2" })] }) }), _jsx("tbody", { children: travelers.map((traveler) => (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsxs("td", { className: "p-2", children: [traveler.firstName, " ", traveler.lastName] }), _jsx("td", { className: "p-2", children: traveler.email ?? messages.travelerList.values.emailUnavailable }), _jsx("td", { className: "p-2", children: traveler.phone ?? messages.travelerList.values.phoneUnavailable }), _jsx("td", { className: "p-2", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
|
|
18
20
|
setEditing(traveler);
|
|
19
21
|
setDialogOpen(true);
|
|
20
22
|
}, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
|
|
21
|
-
if (confirm(
|
|
23
|
+
if (confirm(messages.travelerList.actions.deleteConfirm)) {
|
|
22
24
|
remove.mutate(traveler.id);
|
|
23
25
|
}
|
|
24
26
|
}, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, traveler.id))) })] }) })) }), _jsx(TravelerDialog, { open: dialogOpen, onOpenChange: (nextOpen) => {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"voucher-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/voucher-picker-section.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"voucher-picker-section.d.ts","sourceRoot":"","sources":["../../src/components/voucher-picker-section.tsx"],"names":[],"mappings":"AAOA,mDAAmD;AACnD,MAAM,WAAW,aAAa;IAC5B,EAAE,EAAE,MAAM,CAAA;IACV,IAAI,EAAE,MAAM,CAAA;IACZ,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;IACpB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;IACvB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAA;IACnC,SAAS,EAAE,MAAM,GAAG,IAAI,CAAA;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,qFAAqF;IACrF,IAAI,EAAE,MAAM,CAAA;IACZ,4DAA4D;IAC5D,MAAM,EAAE,aAAa,GAAG,IAAI,CAAA;IAC5B,mFAAmF;IACnF,KAAK,EAAE,MAAM,GAAG,IAAI,CAAA;CACrB;AAED,eAAO,MAAM,uBAAuB,EAAE,kBAIrC,CAAA;AAED,MAAM,WAAW,yBAAyB;IACxC,KAAK,EAAE,kBAAkB,CAAA;IACzB,QAAQ,EAAE,CAAC,KAAK,EAAE,kBAAkB,KAAK,IAAI,CAAA;IAC7C;;;OAGG;IACH,SAAS,CAAC,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAA;IACjB,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,MAAM,CAAC,EAAE;QACP,OAAO,CAAC,EAAE,MAAM,CAAA;QAChB,eAAe,CAAC,EAAE,MAAM,CAAA;QACxB,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,KAAK,CAAC,EAAE,MAAM,CAAA;QACd,cAAc,CAAC,EAAE,MAAM,CAAA;QACvB,YAAY,CAAC,EAAE,MAAM,CAAA;KACtB,CAAA;CACF;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,oBAAoB,CAAC,EACnC,KAAK,EACL,QAAQ,EACR,SAAS,EACT,QAAQ,EACR,WAAW,EACX,MAAM,GACP,EAAE,yBAAyB,2CAiH3B"}
|
|
@@ -3,33 +3,12 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
3
3
|
import { usePublicVoucherValidationMutation } from "@voyantjs/finance-react";
|
|
4
4
|
import { Button, Input, Label } from "@voyantjs/ui/components";
|
|
5
5
|
import { CheckCircle2, Loader2, XCircle } from "lucide-react";
|
|
6
|
+
import { useBookingsUiI18nOrDefault, useBookingsUiMessagesOrDefault } from "../i18n/provider";
|
|
6
7
|
export const emptyVoucherPickerValue = {
|
|
7
8
|
code: "",
|
|
8
9
|
picked: null,
|
|
9
10
|
error: null,
|
|
10
11
|
};
|
|
11
|
-
const DEFAULT_LABELS = {
|
|
12
|
-
heading: "Voucher (optional)",
|
|
13
|
-
codePlaceholder: "Enter voucher code...",
|
|
14
|
-
apply: "Apply",
|
|
15
|
-
clear: "Clear",
|
|
16
|
-
remainingLabel: "Remaining balance:",
|
|
17
|
-
invalidLabel: "This voucher can't be applied:",
|
|
18
|
-
};
|
|
19
|
-
const REASON_MESSAGES = {
|
|
20
|
-
not_found: "Voucher code not found.",
|
|
21
|
-
inactive: "Voucher is not active.",
|
|
22
|
-
not_started: "Voucher is not yet valid.",
|
|
23
|
-
expired: "Voucher has expired.",
|
|
24
|
-
booking_mismatch: "Voucher is assigned to a different booking.",
|
|
25
|
-
currency_mismatch: "Voucher currency does not match the booking.",
|
|
26
|
-
insufficient_balance: "Voucher balance is too low for the selected amount.",
|
|
27
|
-
};
|
|
28
|
-
function formatAmount(cents, currency) {
|
|
29
|
-
if (cents == null)
|
|
30
|
-
return "—";
|
|
31
|
-
return `${(cents / 100).toFixed(2)}${currency ? ` ${currency}` : ""}`;
|
|
32
|
-
}
|
|
33
12
|
/**
|
|
34
13
|
* Voucher picker for booking-create flows. Operator enters a code, clicks
|
|
35
14
|
* Apply, and the server-side `/v1/public/vouchers/validate` runs all the
|
|
@@ -42,7 +21,9 @@ function formatAmount(cents, currency) {
|
|
|
42
21
|
* again without leaving a trail.
|
|
43
22
|
*/
|
|
44
23
|
export function VoucherPickerSection({ value, onChange, bookingId, currency, amountCents, labels, }) {
|
|
45
|
-
const
|
|
24
|
+
const { formatCurrency } = useBookingsUiI18nOrDefault();
|
|
25
|
+
const messages = useBookingsUiMessagesOrDefault();
|
|
26
|
+
const merged = { ...messages.voucherPickerSection.labels, ...labels };
|
|
46
27
|
const validate = usePublicVoucherValidationMutation();
|
|
47
28
|
const handleApply = async () => {
|
|
48
29
|
const code = value.code.trim();
|
|
@@ -73,14 +54,16 @@ export function VoucherPickerSection({ value, onChange, bookingId, currency, amo
|
|
|
73
54
|
onChange({
|
|
74
55
|
code,
|
|
75
56
|
picked: null,
|
|
76
|
-
error:
|
|
57
|
+
error: messages.voucherPickerSection.reasonMessages[data.reason] ?? messages.voucherPickerSection.validation.invalid,
|
|
77
58
|
});
|
|
78
59
|
}
|
|
79
60
|
catch (err) {
|
|
80
61
|
onChange({
|
|
81
62
|
code,
|
|
82
63
|
picked: null,
|
|
83
|
-
error: err instanceof Error
|
|
64
|
+
error: err instanceof Error
|
|
65
|
+
? err.message
|
|
66
|
+
: messages.voucherPickerSection.validation.lookupFailed,
|
|
84
67
|
});
|
|
85
68
|
}
|
|
86
69
|
};
|
|
@@ -90,5 +73,7 @@ export function VoucherPickerSection({ value, onChange, bookingId, currency, amo
|
|
|
90
73
|
e.preventDefault();
|
|
91
74
|
void handleApply();
|
|
92
75
|
}
|
|
93
|
-
}, placeholder: merged.codePlaceholder, disabled: validate.isPending || Boolean(value.picked) }), value.picked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleClear, children: merged.clear })) : (_jsxs(Button, { type: "button", size: "sm", onClick: () => void handleApply(), disabled: validate.isPending || !value.code.trim(), children: [validate.isPending && _jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }), merged.apply] }))] }), value.picked && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), _jsxs("span", { children: [merged.remainingLabel, " ", _jsx("strong", { children:
|
|
76
|
+
}, placeholder: merged.codePlaceholder, disabled: validate.isPending || Boolean(value.picked) }), value.picked ? (_jsx(Button, { type: "button", variant: "ghost", size: "sm", onClick: handleClear, children: merged.clear })) : (_jsxs(Button, { type: "button", size: "sm", onClick: () => void handleApply(), disabled: validate.isPending || !value.code.trim(), children: [validate.isPending && _jsx(Loader2, { className: "mr-1 h-3.5 w-3.5 animate-spin" }), merged.apply] }))] }), value.picked && (_jsxs("div", { className: "flex items-center gap-2 text-sm", children: [_jsx(CheckCircle2, { className: "h-4 w-4 text-emerald-600" }), _jsxs("span", { children: [merged.remainingLabel, " ", _jsx("strong", { children: value.picked.remainingAmountCents == null || !value.picked.currency
|
|
77
|
+
? messages.voucherPickerSection.validation.amountUnavailable
|
|
78
|
+
: formatCurrency(value.picked.remainingAmountCents / 100, value.picked.currency) })] })] })), value.error && (_jsxs("div", { className: "flex items-start gap-2 text-sm text-destructive", children: [_jsx(XCircle, { className: "mt-0.5 h-4 w-4" }), _jsxs("span", { children: [merged.invalidLabel, " ", value.error] })] }))] }));
|
|
94
79
|
}
|