@voyantjs/hospitality-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/cancellation-policy-combobox.d.ts +1 -1
- package/dist/components/cancellation-policy-combobox.d.ts.map +1 -1
- package/dist/components/cancellation-policy-combobox.js +6 -4
- package/dist/components/maintenance-block-dialog.d.ts.map +1 -1
- package/dist/components/maintenance-block-dialog.js +24 -12
- package/dist/components/maintenance-blocks-tab.d.ts.map +1 -1
- package/dist/components/maintenance-blocks-tab.js +10 -4
- package/dist/components/meal-plan-combobox.d.ts +1 -1
- package/dist/components/meal-plan-combobox.d.ts.map +1 -1
- package/dist/components/meal-plan-combobox.js +6 -2
- package/dist/components/meal-plan-dialog.d.ts.map +1 -1
- package/dist/components/meal-plan-dialog.js +19 -12
- package/dist/components/meal-plans-tab.d.ts.map +1 -1
- package/dist/components/meal-plans-tab.js +10 -8
- package/dist/components/pagination-footer.d.ts.map +1 -1
- package/dist/components/pagination-footer.js +7 -2
- package/dist/components/price-catalog-combobox.d.ts +1 -1
- package/dist/components/price-catalog-combobox.d.ts.map +1 -1
- package/dist/components/price-catalog-combobox.js +6 -2
- package/dist/components/rate-plan-combobox.d.ts +1 -1
- package/dist/components/rate-plan-combobox.d.ts.map +1 -1
- package/dist/components/rate-plan-combobox.js +6 -2
- package/dist/components/rate-plan-dialog.d.ts.map +1 -1
- package/dist/components/rate-plan-dialog.js +33 -19
- package/dist/components/rate-plans-tab.d.ts.map +1 -1
- package/dist/components/rate-plans-tab.js +11 -5
- package/dist/components/room-block-dialog.d.ts.map +1 -1
- package/dist/components/room-block-dialog.js +23 -13
- package/dist/components/room-blocks-tab.d.ts.map +1 -1
- package/dist/components/room-blocks-tab.js +10 -4
- package/dist/components/room-inventory-dialog.d.ts.map +1 -1
- package/dist/components/room-inventory-dialog.js +36 -15
- package/dist/components/room-inventory-tab.d.ts.map +1 -1
- package/dist/components/room-inventory-tab.js +8 -6
- package/dist/components/room-type-combobox.d.ts +1 -1
- package/dist/components/room-type-combobox.d.ts.map +1 -1
- package/dist/components/room-type-combobox.js +6 -2
- package/dist/components/room-type-dialog.d.ts.map +1 -1
- package/dist/components/room-type-dialog.js +28 -18
- package/dist/components/room-types-tab.d.ts.map +1 -1
- package/dist/components/room-types-tab.js +5 -3
- package/dist/components/room-unit-combobox.d.ts +1 -1
- package/dist/components/room-unit-combobox.d.ts.map +1 -1
- package/dist/components/room-unit-combobox.js +6 -2
- package/dist/components/room-unit-dialog.d.ts.map +1 -1
- package/dist/components/room-unit-dialog.js +23 -13
- package/dist/components/room-units-tab.d.ts.map +1 -1
- package/dist/components/room-units-tab.js +5 -3
- package/dist/components/stay-rule-dialog.d.ts.map +1 -1
- package/dist/components/stay-rule-dialog.js +35 -22
- package/dist/components/stay-rules-tab.d.ts.map +1 -1
- package/dist/components/stay-rules-tab.js +7 -5
- package/dist/i18n/en.d.ts +473 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +472 -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 +448 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +12 -0
- package/dist/i18n/provider.d.ts +968 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro.d.ts +473 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +472 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +25 -9
package/README.md
CHANGED
|
@@ -11,3 +11,14 @@ pnpm add @voyantjs/hospitality-ui @voyantjs/hospitality-react @voyantjs/ui @tans
|
|
|
11
11
|
`@voyantjs/ui` provides the design-system primitives. `@voyantjs/hospitality-react` provides the data-layer hooks. Both are required peers.
|
|
12
12
|
|
|
13
13
|
All components accept a `className` prop and merge it with `cn()`. Wrap or compose to extend; use the registry copy-paste path (`npx shadcn add @voyant/...`) for components you want to fork outright.
|
|
14
|
+
|
|
15
|
+
## I18n
|
|
16
|
+
|
|
17
|
+
Components render English by default. To localize them, wrap your UI in
|
|
18
|
+
`HospitalityUiMessagesProvider` and import only the locales your app supports.
|
|
19
|
+
|
|
20
|
+
```tsx
|
|
21
|
+
import { HospitalityUiMessagesProvider } from "@voyantjs/hospitality-ui"
|
|
22
|
+
import { hospitalityUiEn } from "@voyantjs/hospitality-ui/i18n/en"
|
|
23
|
+
import { hospitalityUiRo } from "@voyantjs/hospitality-ui/i18n/ro"
|
|
24
|
+
```
|
|
@@ -4,6 +4,6 @@ type Props = {
|
|
|
4
4
|
placeholder?: string;
|
|
5
5
|
disabled?: boolean;
|
|
6
6
|
};
|
|
7
|
-
export declare function CancellationPolicyCombobox({ value, onChange, placeholder, disabled
|
|
7
|
+
export declare function CancellationPolicyCombobox({ value, onChange, placeholder, disabled }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=cancellation-policy-combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cancellation-policy-combobox.d.ts","sourceRoot":"","sources":["../../src/components/cancellation-policy-combobox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"cancellation-policy-combobox.d.ts","sourceRoot":"","sources":["../../src/components/cancellation-policy-combobox.tsx"],"names":[],"mappings":"AAkBA,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAID,wBAAgB,0BAA0B,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,KAAK,2CAwE3F"}
|
|
@@ -2,8 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useCancellationPolicies, useCancellationPolicy, } from "@voyantjs/pricing-react";
|
|
3
3
|
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/ui/components/combobox";
|
|
4
4
|
import * as React from "react";
|
|
5
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
5
6
|
const PAGE_SIZE = 25;
|
|
6
|
-
export function CancellationPolicyCombobox({ value, onChange, placeholder
|
|
7
|
+
export function CancellationPolicyCombobox({ value, onChange, placeholder, disabled }) {
|
|
8
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
7
9
|
const [search, setSearch] = React.useState("");
|
|
8
10
|
const listQuery = useCancellationPolicies({ search: search || undefined, limit: PAGE_SIZE });
|
|
9
11
|
const selectedQuery = useCancellationPolicy(value, { enabled: !!value });
|
|
@@ -38,9 +40,9 @@ export function CancellationPolicyCombobox({ value, onChange, placeholder = "Sea
|
|
|
38
40
|
onChange(id);
|
|
39
41
|
const item = id ? itemMap.get(id) : null;
|
|
40
42
|
setInputValue(item ? `${item.name}${item.code ? ` · ${item.code}` : ""}` : "");
|
|
41
|
-
}, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
42
|
-
?
|
|
43
|
-
:
|
|
43
|
+
}, children: [_jsx(ComboboxInput, { placeholder: placeholder ?? messages.comboboxes.cancellationPolicy.placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
44
|
+
? messages.common.loading
|
|
45
|
+
: messages.comboboxes.cancellationPolicy.empty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
44
46
|
const item = itemMap.get(id);
|
|
45
47
|
if (!item)
|
|
46
48
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance-block-dialog.d.ts","sourceRoot":"","sources":["../../src/components/maintenance-block-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"maintenance-block-dialog.d.ts","sourceRoot":"","sources":["../../src/components/maintenance-block-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,sBAAsB,EAE5B,MAAM,6BAA6B,CAAA;AA6BpC,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,CAAA;AAqBzD,MAAM,WAAW,2BAA2B;IAC1C,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,CAAC,EAAE,sBAAsB,CAAA;IAC9B,SAAS,CAAC,EAAE,CAAC,KAAK,EAAE,sBAAsB,KAAK,IAAI,CAAA;CACpD;AAED,wBAAgB,sBAAsB,CAAC,EACrC,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,KAAK,EACL,SAAS,GACV,EAAE,2BAA2B,2CA2L7B"}
|
|
@@ -7,21 +7,26 @@ 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 { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
10
11
|
import { RoomTypeCombobox } from "./room-type-combobox";
|
|
11
12
|
import { RoomUnitCombobox } from "./room-unit-combobox";
|
|
12
13
|
const STATUSES = ["open", "in_progress", "resolved", "cancelled"];
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
14
|
+
function createFormSchema(messages) {
|
|
15
|
+
return z.object({
|
|
16
|
+
roomTypeId: z.string().optional().nullable(),
|
|
17
|
+
roomUnitId: z.string().optional().nullable(),
|
|
18
|
+
startsOn: z.string().min(1, messages.maintenanceBlockDialog.validation.startsOnRequired),
|
|
19
|
+
endsOn: z.string().min(1, messages.maintenanceBlockDialog.validation.endsOnRequired),
|
|
20
|
+
status: z.enum(STATUSES),
|
|
21
|
+
reason: z.string().optional().nullable(),
|
|
22
|
+
notes: z.string().optional().nullable(),
|
|
23
|
+
});
|
|
24
|
+
}
|
|
22
25
|
export function MaintenanceBlockDialog({ open, onOpenChange, propertyId, block, onSuccess, }) {
|
|
23
26
|
const isEditing = Boolean(block);
|
|
24
27
|
const { create, update } = useMaintenanceBlockMutation();
|
|
28
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
29
|
+
const formSchema = createFormSchema(messages);
|
|
25
30
|
const form = useForm({
|
|
26
31
|
resolver: zodResolver(formSchema),
|
|
27
32
|
defaultValues: {
|
|
@@ -76,11 +81,18 @@ export function MaintenanceBlockDialog({ open, onOpenChange, propertyId, block,
|
|
|
76
81
|
onSuccess?.(saved);
|
|
77
82
|
};
|
|
78
83
|
const isSubmitting = form.formState.isSubmitting || create.isPending || update.isPending;
|
|
79
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
84
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
85
|
+
? messages.maintenanceBlockDialog.titles.edit
|
|
86
|
+
: messages.maintenanceBlockDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), 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.maintenanceBlockDialog.fields.roomType }), _jsx(RoomTypeCombobox, { propertyId: propertyId, value: form.watch("roomTypeId"), onChange: (value) => form.setValue("roomTypeId", value ?? ""), placeholder: messages.maintenanceBlockDialog.placeholders.roomType, disabled: !open })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.maintenanceBlockDialog.fields.roomUnit }), _jsx(RoomUnitCombobox, { propertyId: propertyId, value: form.watch("roomUnitId"), onChange: (value) => form.setValue("roomUnitId", value ?? ""), placeholder: messages.maintenanceBlockDialog.placeholders.roomUnit, disabled: !open })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.maintenanceBlockDialog.fields.startsOn }), _jsx(DatePicker, { value: form.watch("startsOn") || null, onChange: (next) => form.setValue("startsOn", next ?? "", {
|
|
80
87
|
shouldValidate: true,
|
|
81
88
|
shouldDirty: true,
|
|
82
|
-
}), placeholder:
|
|
89
|
+
}), placeholder: messages.maintenanceBlockDialog.placeholders.startsOn, className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.maintenanceBlockDialog.fields.endsOn }), _jsx(DatePicker, { value: form.watch("endsOn") || null, onChange: (next) => form.setValue("endsOn", next ?? "", {
|
|
83
90
|
shouldValidate: true,
|
|
84
91
|
shouldDirty: true,
|
|
85
|
-
}), placeholder:
|
|
92
|
+
}), placeholder: messages.maintenanceBlockDialog.placeholders.endsOn, className: "w-full" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.maintenanceBlockDialog.fields.status }), _jsxs(Select, { items: STATUSES.map((status) => ({
|
|
93
|
+
label: messages.common.maintenanceBlockStatusLabels[status],
|
|
94
|
+
value: status,
|
|
95
|
+
})), value: form.watch("status"), onValueChange: (value) => form.setValue("status", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: STATUSES.map((status) => (_jsx(SelectItem, { value: status, children: messages.common.maintenanceBlockStatusLabels[status] }, status))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.maintenanceBlockDialog.fields.reason }), _jsx(Input, { ...form.register("reason"), placeholder: messages.maintenanceBlockDialog.placeholders.reason })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.maintenanceBlockDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes") })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing
|
|
96
|
+
? messages.common.saveChanges
|
|
97
|
+
: messages.maintenanceBlockDialog.actions.create] })] })] })] }) }));
|
|
86
98
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"maintenance-blocks-tab.d.ts","sourceRoot":"","sources":["../../src/components/maintenance-blocks-tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"maintenance-blocks-tab.d.ts","sourceRoot":"","sources":["../../src/components/maintenance-blocks-tab.tsx"],"names":[],"mappings":"AAoBA,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAA;CACnB;AAGD,wBAAgB,oBAAoB,CAAC,EAAE,UAAU,EAAE,EAAE,yBAAyB,2CAwJ7E"}
|
|
@@ -6,10 +6,12 @@ import { Badge } from "@voyantjs/ui/components/badge";
|
|
|
6
6
|
import { Button } from "@voyantjs/ui/components/button";
|
|
7
7
|
import { Loader2, Pencil, Plus, Trash2 } from "lucide-react";
|
|
8
8
|
import * as React from "react";
|
|
9
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
9
10
|
import { MaintenanceBlockDialog } from "./maintenance-block-dialog";
|
|
10
11
|
import { PaginationFooter } from "./pagination-footer";
|
|
11
12
|
const PAGE_SIZE = 25;
|
|
12
13
|
export function MaintenanceBlocksTab({ propertyId }) {
|
|
14
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
13
15
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
14
16
|
const [editing, setEditing] = React.useState(undefined);
|
|
15
17
|
const [pageIndex, setPageIndex] = React.useState(0);
|
|
@@ -31,19 +33,23 @@ export function MaintenanceBlocksTab({ propertyId }) {
|
|
|
31
33
|
});
|
|
32
34
|
const roomTypeById = new Map(roomTypeQueries.flatMap((query) => (query.data ? [[query.data.id, query.data]] : [])));
|
|
33
35
|
const roomUnitById = new Map(roomUnitQueries.flatMap((query) => (query.data ? [[query.data.id, query.data]] : [])));
|
|
34
|
-
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-sm text-muted-foreground", children:
|
|
36
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: messages.maintenanceBlocksTab.description }), _jsxs(Button, { size: "sm", onClick: () => {
|
|
35
37
|
setEditing(undefined);
|
|
36
38
|
setDialogOpen(true);
|
|
37
|
-
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }),
|
|
39
|
+
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.maintenanceBlocksTab.add] })] }), isPending ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })) : rows.length === 0 ? (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center", children: _jsx("p", { className: "text-sm text-muted-foreground", children: messages.maintenanceBlocksTab.empty }) })) : (_jsx("div", { className: "rounded-md 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-3 text-left font-medium", children: messages.maintenanceBlocksTab.columns.dates }), _jsx("th", { className: "p-3 text-left font-medium", children: messages.maintenanceBlocksTab.columns.roomTypeUnit }), _jsx("th", { className: "p-3 text-left font-medium", children: messages.maintenanceBlocksTab.columns.reason }), _jsx("th", { className: "p-3 text-left font-medium", children: messages.maintenanceBlocksTab.columns.status }), _jsx("th", { className: "w-20 p-3" })] }) }), _jsx("tbody", { children: rows.map((row) => {
|
|
38
40
|
const roomType = row.roomTypeId ? roomTypeById.get(row.roomTypeId)?.name : null;
|
|
39
41
|
const roomUnit = row.roomUnitId
|
|
40
42
|
? roomUnitById.get(row.roomUnitId)?.roomNumber
|
|
41
43
|
: null;
|
|
42
|
-
return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsxs("td", { className: "p-3 font-mono text-xs", children: [row.startsOn, " \u2192 ", row.endsOn] }), _jsx("td", { className: "p-3 text-muted-foreground", children: roomType ??
|
|
44
|
+
return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsxs("td", { className: "p-3 font-mono text-xs", children: [row.startsOn, " \u2192 ", row.endsOn] }), _jsx("td", { className: "p-3 text-muted-foreground", children: roomType ??
|
|
45
|
+
roomUnit ??
|
|
46
|
+
row.roomTypeId ??
|
|
47
|
+
row.roomUnitId ??
|
|
48
|
+
messages.common.none }), _jsx("td", { className: "p-3 text-muted-foreground", children: row.reason ?? messages.common.none }), _jsx("td", { className: "p-3", children: _jsx(Badge, { variant: "outline", children: messages.common.maintenanceBlockStatusLabels[row.status] }) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
|
|
43
49
|
setEditing(row);
|
|
44
50
|
setDialogOpen(true);
|
|
45
51
|
}, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
|
|
46
|
-
if (confirm(
|
|
52
|
+
if (confirm(messages.maintenanceBlocksTab.deleteConfirm)) {
|
|
47
53
|
remove.mutate(row.id);
|
|
48
54
|
}
|
|
49
55
|
}, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, row.id));
|
|
@@ -5,6 +5,6 @@ type Props = {
|
|
|
5
5
|
placeholder?: string;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
};
|
|
8
|
-
export declare function MealPlanCombobox({ propertyId, value, onChange, placeholder, disabled
|
|
8
|
+
export declare function MealPlanCombobox({ propertyId, value, onChange, placeholder, disabled }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
export {};
|
|
10
10
|
//# sourceMappingURL=meal-plan-combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meal-plan-combobox.d.ts","sourceRoot":"","sources":["../../src/components/meal-plan-combobox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"meal-plan-combobox.d.ts","sourceRoot":"","sources":["../../src/components/meal-plan-combobox.tsx"],"names":[],"mappings":"AAcA,KAAK,KAAK,GAAG;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAID,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA0E7F"}
|
|
@@ -2,8 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useMealPlan, useMealPlans } from "@voyantjs/hospitality-react";
|
|
3
3
|
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/ui/components/combobox";
|
|
4
4
|
import * as React from "react";
|
|
5
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
5
6
|
const PAGE_SIZE = 25;
|
|
6
|
-
export function MealPlanCombobox({ propertyId, value, onChange, placeholder
|
|
7
|
+
export function MealPlanCombobox({ propertyId, value, onChange, placeholder, disabled }) {
|
|
8
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
7
9
|
const [search, setSearch] = React.useState("");
|
|
8
10
|
const listQuery = useMealPlans({
|
|
9
11
|
propertyId,
|
|
@@ -41,7 +43,9 @@ export function MealPlanCombobox({ propertyId, value, onChange, placeholder = "S
|
|
|
41
43
|
onChange(id);
|
|
42
44
|
const item = id ? itemMap.get(id) : null;
|
|
43
45
|
setInputValue(item ? `${item.name} · ${item.code}` : "");
|
|
44
|
-
}, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
46
|
+
}, children: [_jsx(ComboboxInput, { placeholder: placeholder ?? messages.comboboxes.mealPlan.placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
47
|
+
? messages.common.loading
|
|
48
|
+
: messages.comboboxes.mealPlan.empty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
45
49
|
const item = itemMap.get(id);
|
|
46
50
|
if (!item)
|
|
47
51
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meal-plan-dialog.d.ts","sourceRoot":"","sources":["../../src/components/meal-plan-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"meal-plan-dialog.d.ts","sourceRoot":"","sources":["../../src/components/meal-plan-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,6BAA6B,CAAA;AAwCtF,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAA;CAC/C;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,2CAgKrB"}
|
|
@@ -6,20 +6,25 @@ import { Loader2 } from "lucide-react";
|
|
|
6
6
|
import { useEffect } from "react";
|
|
7
7
|
import { useForm } from "react-hook-form";
|
|
8
8
|
import { z } from "zod/v4";
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
9
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
10
|
+
function createFormSchema(messages) {
|
|
11
|
+
return z.object({
|
|
12
|
+
code: z.string().min(1, messages.mealPlanDialog.validation.codeRequired).max(50),
|
|
13
|
+
name: z.string().min(1, messages.mealPlanDialog.validation.nameRequired).max(255),
|
|
14
|
+
description: z.string().optional().nullable(),
|
|
15
|
+
includesBreakfast: z.boolean(),
|
|
16
|
+
includesLunch: z.boolean(),
|
|
17
|
+
includesDinner: z.boolean(),
|
|
18
|
+
includesDrinks: z.boolean(),
|
|
19
|
+
active: z.boolean(),
|
|
20
|
+
sortOrder: z.coerce.number().int(),
|
|
21
|
+
});
|
|
22
|
+
}
|
|
20
23
|
export function MealPlanDialog({ open, onOpenChange, propertyId, mealPlan, onSuccess, }) {
|
|
21
24
|
const isEditing = Boolean(mealPlan);
|
|
22
25
|
const { create, update } = useMealPlanMutation();
|
|
26
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
27
|
+
const formSchema = createFormSchema(messages);
|
|
23
28
|
const form = useForm({
|
|
24
29
|
resolver: zodResolver(formSchema),
|
|
25
30
|
defaultValues: {
|
|
@@ -82,5 +87,7 @@ export function MealPlanDialog({ open, onOpenChange, propertyId, mealPlan, onSuc
|
|
|
82
87
|
onSuccess?.(saved);
|
|
83
88
|
};
|
|
84
89
|
const isSubmitting = form.formState.isSubmitting || create.isPending || update.isPending;
|
|
85
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
90
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
91
|
+
? messages.mealPlanDialog.titles.edit
|
|
92
|
+
: messages.mealPlanDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), 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.mealPlanDialog.fields.code }), _jsx(Input, { ...form.register("code"), placeholder: messages.mealPlanDialog.placeholders.code })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.mealPlanDialog.fields.name }), _jsx(Input, { ...form.register("name"), placeholder: messages.mealPlanDialog.placeholders.name })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.mealPlanDialog.fields.description }), _jsx(Textarea, { ...form.register("description") })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.mealPlanDialog.fields.sortOrder }), _jsx(Input, { ...form.register("sortOrder"), type: "number", className: "w-32" })] }), _jsxs("div", { className: "grid grid-cols-2 gap-3", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("includesBreakfast"), onCheckedChange: (checked) => form.setValue("includesBreakfast", checked) }), _jsx(Label, { children: messages.mealPlanDialog.fields.breakfast })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("includesLunch"), onCheckedChange: (checked) => form.setValue("includesLunch", checked) }), _jsx(Label, { children: messages.mealPlanDialog.fields.lunch })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("includesDinner"), onCheckedChange: (checked) => form.setValue("includesDinner", checked) }), _jsx(Label, { children: messages.mealPlanDialog.fields.dinner })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("includesDrinks"), onCheckedChange: (checked) => form.setValue("includesDrinks", checked) }), _jsx(Label, { children: messages.mealPlanDialog.fields.drinks })] })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (checked) => form.setValue("active", checked) }), _jsx(Label, { children: messages.mealPlanDialog.fields.active })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? messages.common.saveChanges : messages.mealPlanDialog.actions.create] })] })] })] }) }));
|
|
86
93
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"meal-plans-tab.d.ts","sourceRoot":"","sources":["../../src/components/meal-plans-tab.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"meal-plans-tab.d.ts","sourceRoot":"","sources":["../../src/components/meal-plans-tab.tsx"],"names":[],"mappings":"AAYA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB;AAGD,wBAAgB,YAAY,CAAC,EAAE,UAAU,EAAE,EAAE,iBAAiB,2CA2I7D"}
|
|
@@ -5,10 +5,12 @@ import { Badge } from "@voyantjs/ui/components/badge";
|
|
|
5
5
|
import { Button } from "@voyantjs/ui/components/button";
|
|
6
6
|
import { Loader2, Pencil, Plus, Trash2 } from "lucide-react";
|
|
7
7
|
import * as React from "react";
|
|
8
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
8
9
|
import { MealPlanDialog } from "./meal-plan-dialog";
|
|
9
10
|
import { PaginationFooter } from "./pagination-footer";
|
|
10
11
|
const PAGE_SIZE = 25;
|
|
11
12
|
export function MealPlansTab({ propertyId }) {
|
|
13
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
12
14
|
const [dialogOpen, setDialogOpen] = React.useState(false);
|
|
13
15
|
const [editing, setEditing] = React.useState(undefined);
|
|
14
16
|
const [pageIndex, setPageIndex] = React.useState(0);
|
|
@@ -19,24 +21,24 @@ export function MealPlansTab({ propertyId }) {
|
|
|
19
21
|
});
|
|
20
22
|
const { remove } = useMealPlanMutation();
|
|
21
23
|
const rows = (data?.data ?? []).slice().sort((a, b) => a.sortOrder - b.sortOrder);
|
|
22
|
-
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-sm text-muted-foreground", children:
|
|
24
|
+
return (_jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "flex items-center justify-between", children: [_jsx("p", { className: "text-sm text-muted-foreground", children: messages.mealPlansTab.description }), _jsxs(Button, { size: "sm", onClick: () => {
|
|
23
25
|
setEditing(undefined);
|
|
24
26
|
setDialogOpen(true);
|
|
25
|
-
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }),
|
|
27
|
+
}, children: [_jsx(Plus, { className: "mr-2 h-4 w-4" }), messages.mealPlansTab.add] })] }), isPending ? (_jsx("div", { className: "flex items-center justify-center py-12", children: _jsx(Loader2, { className: "h-6 w-6 animate-spin text-muted-foreground" }) })) : rows.length === 0 ? (_jsx("div", { className: "rounded-md border border-dashed p-8 text-center", children: _jsx("p", { className: "text-sm text-muted-foreground", children: messages.mealPlansTab.empty }) })) : (_jsx("div", { className: "rounded-md 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-3 text-left font-medium", children: messages.mealPlansTab.columns.code }), _jsx("th", { className: "p-3 text-left font-medium", children: messages.mealPlansTab.columns.name }), _jsx("th", { className: "p-3 text-left font-medium", children: messages.mealPlansTab.columns.includes }), _jsx("th", { className: "p-3 text-left font-medium", children: messages.mealPlansTab.columns.status }), _jsx("th", { className: "w-20 p-3" })] }) }), _jsx("tbody", { children: rows.map((row) => {
|
|
26
28
|
const includes = [];
|
|
27
29
|
if (row.includesBreakfast)
|
|
28
|
-
includes.push(
|
|
30
|
+
includes.push(messages.common.mealInclusions.breakfast);
|
|
29
31
|
if (row.includesLunch)
|
|
30
|
-
includes.push(
|
|
32
|
+
includes.push(messages.common.mealInclusions.lunch);
|
|
31
33
|
if (row.includesDinner)
|
|
32
|
-
includes.push(
|
|
34
|
+
includes.push(messages.common.mealInclusions.dinner);
|
|
33
35
|
if (row.includesDrinks)
|
|
34
|
-
includes.push(
|
|
35
|
-
return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-3 font-mono text-xs", children: row.code }), _jsx("td", { className: "p-3 font-medium", children: row.name }), _jsx("td", { className: "p-3", children: _jsx("div", { className: "flex flex-wrap gap-1", children: includes.length === 0 ? (_jsx("span", { className: "text-xs text-muted-foreground", children:
|
|
36
|
+
includes.push(messages.common.mealInclusions.drinks);
|
|
37
|
+
return (_jsxs("tr", { className: "border-b last:border-b-0", children: [_jsx("td", { className: "p-3 font-mono text-xs", children: row.code }), _jsx("td", { className: "p-3 font-medium", children: row.name }), _jsx("td", { className: "p-3", children: _jsx("div", { className: "flex flex-wrap gap-1", children: includes.length === 0 ? (_jsx("span", { className: "text-xs text-muted-foreground", children: messages.common.none })) : (includes.map((label) => (_jsx(Badge, { variant: "secondary", children: label }, label)))) }) }), _jsx("td", { className: "p-3", children: _jsx(Badge, { variant: row.active ? "default" : "outline", children: row.active ? messages.common.active : messages.common.inactive }) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
|
|
36
38
|
setEditing(row);
|
|
37
39
|
setDialogOpen(true);
|
|
38
40
|
}, className: "text-muted-foreground hover:text-foreground", children: _jsx(Pencil, { className: "h-3.5 w-3.5" }) }), _jsx("button", { type: "button", onClick: () => {
|
|
39
|
-
if (confirm(
|
|
41
|
+
if (confirm(messages.mealPlansTab.deleteConfirm.replace("{name}", row.name))) {
|
|
40
42
|
remove.mutate(row.id);
|
|
41
43
|
}
|
|
42
44
|
}, className: "text-muted-foreground hover:text-destructive", children: _jsx(Trash2, { className: "h-3.5 w-3.5" }) })] }) })] }, row.id));
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pagination-footer.d.ts","sourceRoot":"","sources":["../../src/components/pagination-footer.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"pagination-footer.d.ts","sourceRoot":"","sources":["../../src/components/pagination-footer.tsx"],"names":[],"mappings":"AAMA,KAAK,qBAAqB,GAAG;IAC3B,SAAS,EAAE,MAAM,CAAA;IACjB,QAAQ,EAAE,MAAM,CAAA;IAChB,KAAK,EAAE,MAAM,CAAA;IACb,iBAAiB,EAAE,CAAC,SAAS,EAAE,MAAM,KAAK,IAAI,CAAA;CAC/C,CAAA;AAED,wBAAgB,gBAAgB,CAAC,EAC/B,SAAS,EACT,QAAQ,EACR,KAAK,EACL,iBAAiB,GAClB,EAAE,qBAAqB,kDAuCvB"}
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
"use client";
|
|
2
|
-
import {
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { Button } from "@voyantjs/ui/components/button";
|
|
4
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
4
5
|
export function PaginationFooter({ pageIndex, pageSize, total, onPageIndexChange, }) {
|
|
6
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
5
7
|
const pageCount = Math.max(1, Math.ceil(total / pageSize));
|
|
6
8
|
const canPreviousPage = pageIndex > 0;
|
|
7
9
|
const canNextPage = pageIndex + 1 < pageCount;
|
|
8
10
|
if (total <= pageSize)
|
|
9
11
|
return null;
|
|
10
|
-
return (_jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [
|
|
12
|
+
return (_jsxs("div", { className: "flex items-center justify-between text-sm text-muted-foreground", children: [_jsx("span", { children: messages.common.showingRange
|
|
13
|
+
.replace("{start}", String(pageIndex * pageSize + 1))
|
|
14
|
+
.replace("{end}", String(Math.min((pageIndex + 1) * pageSize, total)))
|
|
15
|
+
.replace("{total}", String(total)) }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: !canPreviousPage, onClick: () => onPageIndexChange(pageIndex - 1), children: messages.common.previous }), _jsxs("span", { children: [messages.common.page, " ", pageIndex + 1, " / ", pageCount] }), _jsx(Button, { variant: "outline", size: "sm", disabled: !canNextPage, onClick: () => onPageIndexChange(pageIndex + 1), children: messages.common.next })] })] }));
|
|
11
16
|
}
|
|
@@ -4,6 +4,6 @@ type Props = {
|
|
|
4
4
|
placeholder?: string;
|
|
5
5
|
disabled?: boolean;
|
|
6
6
|
};
|
|
7
|
-
export declare function PriceCatalogCombobox({ value, onChange, placeholder, disabled
|
|
7
|
+
export declare function PriceCatalogCombobox({ value, onChange, placeholder, disabled }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
8
|
export {};
|
|
9
9
|
//# sourceMappingURL=price-catalog-combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"price-catalog-combobox.d.ts","sourceRoot":"","sources":["../../src/components/price-catalog-combobox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"price-catalog-combobox.d.ts","sourceRoot":"","sources":["../../src/components/price-catalog-combobox.tsx"],"names":[],"mappings":"AAcA,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAID,wBAAgB,oBAAoB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,KAAK,2CAqErF"}
|
|
@@ -2,8 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { usePriceCatalog, usePriceCatalogs } from "@voyantjs/pricing-react";
|
|
3
3
|
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/ui/components/combobox";
|
|
4
4
|
import * as React from "react";
|
|
5
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
5
6
|
const PAGE_SIZE = 25;
|
|
6
|
-
export function PriceCatalogCombobox({ value, onChange, placeholder
|
|
7
|
+
export function PriceCatalogCombobox({ value, onChange, placeholder, disabled }) {
|
|
8
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
7
9
|
const [search, setSearch] = React.useState("");
|
|
8
10
|
const listQuery = usePriceCatalogs({ search: search || undefined, limit: PAGE_SIZE });
|
|
9
11
|
const selectedQuery = usePriceCatalog(value, { enabled: !!value });
|
|
@@ -36,7 +38,9 @@ export function PriceCatalogCombobox({ value, onChange, placeholder = "Search pr
|
|
|
36
38
|
onChange(id);
|
|
37
39
|
const item = id ? itemMap.get(id) : null;
|
|
38
40
|
setInputValue(item ? `${item.name} · ${item.code}` : "");
|
|
39
|
-
}, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
41
|
+
}, children: [_jsx(ComboboxInput, { placeholder: placeholder ?? messages.comboboxes.priceCatalog.placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
42
|
+
? messages.common.loading
|
|
43
|
+
: messages.comboboxes.priceCatalog.empty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
40
44
|
const item = itemMap.get(id);
|
|
41
45
|
if (!item)
|
|
42
46
|
return null;
|
|
@@ -5,6 +5,6 @@ type Props = {
|
|
|
5
5
|
placeholder?: string;
|
|
6
6
|
disabled?: boolean;
|
|
7
7
|
};
|
|
8
|
-
export declare function RatePlanCombobox({ propertyId, value, onChange, placeholder, disabled
|
|
8
|
+
export declare function RatePlanCombobox({ propertyId, value, onChange, placeholder, disabled }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
9
|
export {};
|
|
10
10
|
//# sourceMappingURL=rate-plan-combobox.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-plan-combobox.d.ts","sourceRoot":"","sources":["../../src/components/rate-plan-combobox.tsx"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"rate-plan-combobox.d.ts","sourceRoot":"","sources":["../../src/components/rate-plan-combobox.tsx"],"names":[],"mappings":"AAcA,KAAK,KAAK,GAAG;IACX,UAAU,EAAE,MAAM,CAAA;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAID,wBAAgB,gBAAgB,CAAC,EAAE,UAAU,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA0E7F"}
|
|
@@ -2,8 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
2
2
|
import { useRatePlan, useRatePlans } from "@voyantjs/hospitality-react";
|
|
3
3
|
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/ui/components/combobox";
|
|
4
4
|
import * as React from "react";
|
|
5
|
+
import { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
5
6
|
const PAGE_SIZE = 25;
|
|
6
|
-
export function RatePlanCombobox({ propertyId, value, onChange, placeholder
|
|
7
|
+
export function RatePlanCombobox({ propertyId, value, onChange, placeholder, disabled }) {
|
|
8
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
7
9
|
const [search, setSearch] = React.useState("");
|
|
8
10
|
const listQuery = useRatePlans({
|
|
9
11
|
propertyId,
|
|
@@ -41,7 +43,9 @@ export function RatePlanCombobox({ propertyId, value, onChange, placeholder = "S
|
|
|
41
43
|
onChange(id);
|
|
42
44
|
const item = id ? itemMap.get(id) : null;
|
|
43
45
|
setInputValue(item ? `${item.name} · ${item.code}` : "");
|
|
44
|
-
}, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
46
|
+
}, children: [_jsx(ComboboxInput, { placeholder: placeholder ?? messages.comboboxes.ratePlan.placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
47
|
+
? messages.common.loading
|
|
48
|
+
: messages.comboboxes.ratePlan.empty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
45
49
|
const item = itemMap.get(id);
|
|
46
50
|
if (!item)
|
|
47
51
|
return null;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rate-plan-dialog.d.ts","sourceRoot":"","sources":["../../src/components/rate-plan-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,6BAA6B,CAAA;
|
|
1
|
+
{"version":3,"file":"rate-plan-dialog.d.ts","sourceRoot":"","sources":["../../src/components/rate-plan-dialog.tsx"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,cAAc,EAAuB,MAAM,6BAA6B,CAAA;AAkCtF,MAAM,MAAM,YAAY,GAAG,cAAc,CAAA;AAqCzC,MAAM,WAAW,mBAAmB;IAClC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,UAAU,EAAE,MAAM,CAAA;IAClB,QAAQ,CAAC,EAAE,cAAc,CAAA;IACzB,SAAS,CAAC,EAAE,CAAC,QAAQ,EAAE,cAAc,KAAK,IAAI,CAAA;CAC/C;AAED,wBAAgB,cAAc,CAAC,EAC7B,IAAI,EACJ,YAAY,EACZ,UAAU,EACV,QAAQ,EACR,SAAS,GACV,EAAE,mBAAmB,2CAwQrB"}
|
|
@@ -7,6 +7,7 @@ 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 { useHospitalityUiMessagesOrDefault } from "../i18n";
|
|
10
11
|
import { CancellationPolicyCombobox } from "./cancellation-policy-combobox";
|
|
11
12
|
import { MealPlanCombobox } from "./meal-plan-combobox";
|
|
12
13
|
import { PriceCatalogCombobox } from "./price-catalog-combobox";
|
|
@@ -17,24 +18,29 @@ const CHARGE_FREQUENCIES = [
|
|
|
17
18
|
"per_person_per_stay",
|
|
18
19
|
];
|
|
19
20
|
const GUARANTEE_MODES = ["none", "deposit", "on_request", "card_hold", "full_prepay"];
|
|
20
|
-
const
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
21
|
+
const DEFAULT_RATE_PLAN_CURRENCY = "EUR"; /* i18n-literal-ok domain default currency */
|
|
22
|
+
function createFormSchema(messages) {
|
|
23
|
+
return z.object({
|
|
24
|
+
code: z.string().min(1, messages.ratePlanDialog.validation.codeRequired).max(50),
|
|
25
|
+
name: z.string().min(1, messages.ratePlanDialog.validation.nameRequired).max(255),
|
|
26
|
+
description: z.string().optional().nullable(),
|
|
27
|
+
mealPlanId: z.string().optional().nullable(),
|
|
28
|
+
priceCatalogId: z.string().optional().nullable(),
|
|
29
|
+
cancellationPolicyId: z.string().optional().nullable(),
|
|
30
|
+
currencyCode: z.string().length(3, messages.ratePlanDialog.validation.currencyLength),
|
|
31
|
+
chargeFrequency: z.enum(CHARGE_FREQUENCIES),
|
|
32
|
+
guaranteeMode: z.enum(GUARANTEE_MODES),
|
|
33
|
+
commissionable: z.boolean(),
|
|
34
|
+
refundable: z.boolean(),
|
|
35
|
+
active: z.boolean(),
|
|
36
|
+
sortOrder: z.coerce.number().int(),
|
|
37
|
+
});
|
|
38
|
+
}
|
|
35
39
|
export function RatePlanDialog({ open, onOpenChange, propertyId, ratePlan, onSuccess, }) {
|
|
36
40
|
const isEditing = Boolean(ratePlan);
|
|
37
41
|
const { create, update } = useRatePlanMutation();
|
|
42
|
+
const messages = useHospitalityUiMessagesOrDefault();
|
|
43
|
+
const formSchema = createFormSchema(messages);
|
|
38
44
|
const form = useForm({
|
|
39
45
|
resolver: zodResolver(formSchema),
|
|
40
46
|
defaultValues: {
|
|
@@ -44,7 +50,7 @@ export function RatePlanDialog({ open, onOpenChange, propertyId, ratePlan, onSuc
|
|
|
44
50
|
mealPlanId: "",
|
|
45
51
|
priceCatalogId: "",
|
|
46
52
|
cancellationPolicyId: "",
|
|
47
|
-
currencyCode:
|
|
53
|
+
currencyCode: DEFAULT_RATE_PLAN_CURRENCY,
|
|
48
54
|
chargeFrequency: "per_night",
|
|
49
55
|
guaranteeMode: "none",
|
|
50
56
|
commissionable: true,
|
|
@@ -79,7 +85,7 @@ export function RatePlanDialog({ open, onOpenChange, propertyId, ratePlan, onSuc
|
|
|
79
85
|
mealPlanId: "",
|
|
80
86
|
priceCatalogId: "",
|
|
81
87
|
cancellationPolicyId: "",
|
|
82
|
-
currencyCode:
|
|
88
|
+
currencyCode: DEFAULT_RATE_PLAN_CURRENCY,
|
|
83
89
|
chargeFrequency: "per_night",
|
|
84
90
|
guaranteeMode: "none",
|
|
85
91
|
commissionable: true,
|
|
@@ -113,8 +119,16 @@ export function RatePlanDialog({ open, onOpenChange, propertyId, ratePlan, onSuc
|
|
|
113
119
|
onSuccess?.(saved);
|
|
114
120
|
};
|
|
115
121
|
const isSubmitting = form.formState.isSubmitting || create.isPending || update.isPending;
|
|
116
|
-
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
122
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
|
|
123
|
+
? messages.ratePlanDialog.titles.edit
|
|
124
|
+
: messages.ratePlanDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), 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.ratePlanDialog.fields.code }), _jsx(Input, { ...form.register("code"), placeholder: messages.ratePlanDialog.placeholders.code })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.name }), _jsx(Input, { ...form.register("name"), placeholder: messages.ratePlanDialog.placeholders.name })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.description }), _jsx(Textarea, { ...form.register("description") })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.currency }), _jsx(CurrencyCombobox, { value: form.watch("currencyCode") || null, onChange: (next) => form.setValue("currencyCode", next ?? DEFAULT_RATE_PLAN_CURRENCY, {
|
|
117
125
|
shouldValidate: true,
|
|
118
126
|
shouldDirty: true,
|
|
119
|
-
}) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children:
|
|
127
|
+
}) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.chargeFrequency }), _jsxs(Select, { items: CHARGE_FREQUENCIES.map((frequency) => ({
|
|
128
|
+
label: messages.common.chargeFrequencyLabels[frequency],
|
|
129
|
+
value: frequency,
|
|
130
|
+
})), value: form.watch("chargeFrequency"), onValueChange: (value) => form.setValue("chargeFrequency", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CHARGE_FREQUENCIES.map((frequency) => (_jsx(SelectItem, { value: frequency, children: messages.common.chargeFrequencyLabels[frequency] }, frequency))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.guarantee }), _jsxs(Select, { items: GUARANTEE_MODES.map((mode) => ({
|
|
131
|
+
label: messages.common.guaranteeModeLabels[mode],
|
|
132
|
+
value: mode,
|
|
133
|
+
})), value: form.watch("guaranteeMode"), onValueChange: (value) => form.setValue("guaranteeMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: GUARANTEE_MODES.map((mode) => (_jsx(SelectItem, { value: mode, children: messages.common.guaranteeModeLabels[mode] }, mode))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.mealPlan }), _jsx(MealPlanCombobox, { propertyId: propertyId, value: form.watch("mealPlanId"), onChange: (value) => form.setValue("mealPlanId", value ?? ""), placeholder: messages.ratePlanDialog.placeholders.mealPlan, disabled: !open })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.priceCatalog }), _jsx(PriceCatalogCombobox, { value: form.watch("priceCatalogId"), onChange: (value) => form.setValue("priceCatalogId", value ?? ""), placeholder: messages.ratePlanDialog.placeholders.priceCatalog, disabled: !open })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.cancellationPolicy }), _jsx(CancellationPolicyCombobox, { value: form.watch("cancellationPolicyId"), onChange: (value) => form.setValue("cancellationPolicyId", value ?? ""), placeholder: messages.ratePlanDialog.placeholders.cancellationPolicy, disabled: !open })] })] }), _jsxs("div", { className: "flex gap-6", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("commissionable"), onCheckedChange: (checked) => form.setValue("commissionable", checked) }), _jsx(Label, { children: messages.ratePlanDialog.fields.commissionable })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("refundable"), onCheckedChange: (checked) => form.setValue("refundable", checked) }), _jsx(Label, { children: messages.ratePlanDialog.fields.refundable })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (checked) => form.setValue("active", checked) }), _jsx(Label, { children: messages.ratePlanDialog.fields.active })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.ratePlanDialog.fields.sortOrder }), _jsx(Input, { ...form.register("sortOrder"), type: "number", className: "w-32" })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: messages.common.cancel }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? messages.common.saveChanges : messages.ratePlanDialog.actions.create] })] })] })] }) }));
|
|
120
134
|
}
|