@voyantjs/hospitality-ui 0.15.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.
Files changed (72) hide show
  1. package/README.md +11 -0
  2. package/dist/components/cancellation-policy-combobox.d.ts +1 -1
  3. package/dist/components/cancellation-policy-combobox.d.ts.map +1 -1
  4. package/dist/components/cancellation-policy-combobox.js +6 -4
  5. package/dist/components/maintenance-block-dialog.d.ts.map +1 -1
  6. package/dist/components/maintenance-block-dialog.js +24 -12
  7. package/dist/components/maintenance-blocks-tab.d.ts.map +1 -1
  8. package/dist/components/maintenance-blocks-tab.js +10 -4
  9. package/dist/components/meal-plan-combobox.d.ts +1 -1
  10. package/dist/components/meal-plan-combobox.d.ts.map +1 -1
  11. package/dist/components/meal-plan-combobox.js +6 -2
  12. package/dist/components/meal-plan-dialog.d.ts.map +1 -1
  13. package/dist/components/meal-plan-dialog.js +19 -12
  14. package/dist/components/meal-plans-tab.d.ts.map +1 -1
  15. package/dist/components/meal-plans-tab.js +10 -8
  16. package/dist/components/pagination-footer.d.ts.map +1 -1
  17. package/dist/components/pagination-footer.js +7 -2
  18. package/dist/components/price-catalog-combobox.d.ts +1 -1
  19. package/dist/components/price-catalog-combobox.d.ts.map +1 -1
  20. package/dist/components/price-catalog-combobox.js +6 -2
  21. package/dist/components/rate-plan-combobox.d.ts +1 -1
  22. package/dist/components/rate-plan-combobox.d.ts.map +1 -1
  23. package/dist/components/rate-plan-combobox.js +6 -2
  24. package/dist/components/rate-plan-dialog.d.ts.map +1 -1
  25. package/dist/components/rate-plan-dialog.js +33 -19
  26. package/dist/components/rate-plans-tab.d.ts.map +1 -1
  27. package/dist/components/rate-plans-tab.js +11 -5
  28. package/dist/components/room-block-dialog.d.ts.map +1 -1
  29. package/dist/components/room-block-dialog.js +23 -13
  30. package/dist/components/room-blocks-tab.d.ts.map +1 -1
  31. package/dist/components/room-blocks-tab.js +10 -4
  32. package/dist/components/room-inventory-dialog.d.ts.map +1 -1
  33. package/dist/components/room-inventory-dialog.js +36 -15
  34. package/dist/components/room-inventory-tab.d.ts.map +1 -1
  35. package/dist/components/room-inventory-tab.js +8 -6
  36. package/dist/components/room-type-combobox.d.ts +1 -1
  37. package/dist/components/room-type-combobox.d.ts.map +1 -1
  38. package/dist/components/room-type-combobox.js +6 -2
  39. package/dist/components/room-type-dialog.d.ts.map +1 -1
  40. package/dist/components/room-type-dialog.js +28 -18
  41. package/dist/components/room-types-tab.d.ts.map +1 -1
  42. package/dist/components/room-types-tab.js +5 -3
  43. package/dist/components/room-unit-combobox.d.ts +1 -1
  44. package/dist/components/room-unit-combobox.d.ts.map +1 -1
  45. package/dist/components/room-unit-combobox.js +6 -2
  46. package/dist/components/room-unit-dialog.d.ts.map +1 -1
  47. package/dist/components/room-unit-dialog.js +23 -13
  48. package/dist/components/room-units-tab.d.ts.map +1 -1
  49. package/dist/components/room-units-tab.js +5 -3
  50. package/dist/components/stay-rule-dialog.d.ts.map +1 -1
  51. package/dist/components/stay-rule-dialog.js +35 -22
  52. package/dist/components/stay-rules-tab.d.ts.map +1 -1
  53. package/dist/components/stay-rules-tab.js +7 -5
  54. package/dist/i18n/en.d.ts +473 -0
  55. package/dist/i18n/en.d.ts.map +1 -0
  56. package/dist/i18n/en.js +472 -0
  57. package/dist/i18n/index.d.ts +5 -0
  58. package/dist/i18n/index.d.ts.map +1 -0
  59. package/dist/i18n/index.js +3 -0
  60. package/dist/i18n/messages.d.ts +448 -0
  61. package/dist/i18n/messages.d.ts.map +1 -0
  62. package/dist/i18n/messages.js +12 -0
  63. package/dist/i18n/provider.d.ts +968 -0
  64. package/dist/i18n/provider.d.ts.map +1 -0
  65. package/dist/i18n/provider.js +44 -0
  66. package/dist/i18n/ro.d.ts +473 -0
  67. package/dist/i18n/ro.d.ts.map +1 -0
  68. package/dist/i18n/ro.js +472 -0
  69. package/dist/index.d.ts +2 -0
  70. package/dist/index.d.ts.map +1 -1
  71. package/dist/index.js +1 -0
  72. 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, }: Props): import("react/jsx-runtime").JSX.Element;
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":"AAgBA,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,EACzC,KAAK,EACL,QAAQ,EACR,WAA6C,EAC7C,QAAQ,GACT,EAAE,KAAK,2CAoEP"}
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 = "Search cancellation policies…", disabled, }) {
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
- ? "Loading…"
43
- : "No cancellation policies found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
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;AA2BpC,MAAM,MAAM,oBAAoB,GAAG,sBAAsB,CAAA;AAkBzD,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,2CA2K7B"}
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
- const formSchema = z.object({
14
- roomTypeId: z.string().optional().nullable(),
15
- roomUnitId: z.string().optional().nullable(),
16
- startsOn: z.string().min(1, "Start date is required"),
17
- endsOn: z.string().min(1, "End date is required"),
18
- status: z.enum(STATUSES),
19
- reason: z.string().optional().nullable(),
20
- notes: z.string().optional().nullable(),
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 ? "Edit Maintenance Block" : "Add Maintenance Block" }) }), _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: "Room type (optional)" }), _jsx(RoomTypeCombobox, { propertyId: propertyId, value: form.watch("roomTypeId"), onChange: (value) => form.setValue("roomTypeId", value ?? ""), placeholder: "None", disabled: !open })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Room unit (optional)" }), _jsx(RoomUnitCombobox, { propertyId: propertyId, value: form.watch("roomUnitId"), onChange: (value) => form.setValue("roomUnitId", value ?? ""), placeholder: "None", disabled: !open })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Starts on" }), _jsx(DatePicker, { value: form.watch("startsOn") || null, onChange: (next) => form.setValue("startsOn", next ?? "", {
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: "Select start date", className: "w-full" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Ends on" }), _jsx(DatePicker, { value: form.watch("endsOn") || null, onChange: (next) => form.setValue("endsOn", next ?? "", {
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: "Select end date", 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: "Status" }), _jsxs(Select, { items: STATUSES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), 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, className: "capitalize", children: status.replace(/_/g, " ") }, status))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Reason" }), _jsx(Input, { ...form.register("reason"), placeholder: "HVAC failure" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Notes" }), _jsx(Textarea, { ...form.register("notes") })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? "Save Changes" : "Add Block"] })] })] })] }) }));
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":"AAkBA,MAAM,WAAW,yBAAyB;IACxC,UAAU,EAAE,MAAM,CAAA;CACnB;AAGD,wBAAgB,oBAAoB,CAAC,EAAE,UAAU,EAAE,EAAE,yBAAyB,2CAuI7E"}
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: "Take rooms out of service for repairs or upkeep." }), _jsxs(Button, { size: "sm", onClick: () => {
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" }), "Add Block"] })] }), 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: "No maintenance blocks yet." }) })) : (_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: "Dates" }), _jsx("th", { className: "p-3 text-left font-medium", children: "Room type / unit" }), _jsx("th", { className: "p-3 text-left font-medium", children: "Reason" }), _jsx("th", { className: "p-3 text-left font-medium", children: "Status" }), _jsx("th", { className: "w-20 p-3" })] }) }), _jsx("tbody", { children: rows.map((row) => {
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 ?? roomUnit ?? row.roomTypeId ?? row.roomUnitId ?? "—" }), _jsx("td", { className: "p-3 text-muted-foreground", children: row.reason ?? "—" }), _jsx("td", { className: "p-3", children: _jsx(Badge, { variant: "outline", className: "capitalize", children: row.status.replace(/_/g, " ") }) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
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("Delete block?")) {
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, }: Props): import("react/jsx-runtime").JSX.Element;
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":"AAYA,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,EAC/B,UAAU,EACV,KAAK,EACL,QAAQ,EACR,WAAkC,EAClC,QAAQ,GACT,EAAE,KAAK,2CAoEP"}
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 = "Search meal plans…", disabled, }) {
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 ? "Loading…" : "No meal plans found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
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;AAmCtF,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,2CAoJrB"}
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
- const formSchema = z.object({
10
- code: z.string().min(1, "Code is required").max(50),
11
- name: z.string().min(1, "Name is required").max(255),
12
- description: z.string().optional().nullable(),
13
- includesBreakfast: z.boolean(),
14
- includesLunch: z.boolean(),
15
- includesDinner: z.boolean(),
16
- includesDrinks: z.boolean(),
17
- active: z.boolean(),
18
- sortOrder: z.coerce.number().int(),
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 ? "Edit Meal Plan" : "Add Meal Plan" }) }), _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: "Code" }), _jsx(Input, { ...form.register("code"), placeholder: "BB" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { ...form.register("name"), placeholder: "Bed & Breakfast" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Description" }), _jsx(Textarea, { ...form.register("description") })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sort order" }), _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: "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: "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: "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: "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: "Active" })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? "Save Changes" : "Add Meal Plan"] })] })] })] }) }));
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":"AAUA,MAAM,WAAW,iBAAiB;IAChC,UAAU,EAAE,MAAM,CAAA;CACnB;AAGD,wBAAgB,YAAY,CAAC,EAAE,UAAU,EAAE,EAAE,iBAAiB,2CAkI7D"}
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: "Meal inclusions like BB, HB, FB, all-inclusive, and room-only." }), _jsxs(Button, { size: "sm", onClick: () => {
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" }), "Add Meal Plan"] })] }), 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: "No meal plans yet." }) })) : (_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: "Code" }), _jsx("th", { className: "p-3 text-left font-medium", children: "Name" }), _jsx("th", { className: "p-3 text-left font-medium", children: "Includes" }), _jsx("th", { className: "p-3 text-left font-medium", children: "Status" }), _jsx("th", { className: "w-20 p-3" })] }) }), _jsx("tbody", { children: rows.map((row) => {
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("Breakfast");
30
+ includes.push(messages.common.mealInclusions.breakfast);
29
31
  if (row.includesLunch)
30
- includes.push("Lunch");
32
+ includes.push(messages.common.mealInclusions.lunch);
31
33
  if (row.includesDinner)
32
- includes.push("Dinner");
34
+ includes.push(messages.common.mealInclusions.dinner);
33
35
  if (row.includesDrinks)
34
- includes.push("Drinks");
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: "\u2014" })) : (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 ? "Active" : "Inactive" }) }), _jsx("td", { className: "p-3", children: _jsxs("div", { className: "flex items-center gap-1", children: [_jsx("button", { type: "button", onClick: () => {
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(`Delete meal plan "${row.name}"?`)) {
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":"AAIA,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,kDAmCvB"}
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 { jsxs as _jsxs, jsx as _jsx } from "react/jsx-runtime";
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: [_jsxs("span", { children: ["Showing ", pageIndex * pageSize + 1, "-", Math.min((pageIndex + 1) * pageSize, total), " of ", total] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Button, { variant: "outline", size: "sm", disabled: !canPreviousPage, onClick: () => onPageIndexChange(pageIndex - 1), children: "Previous" }), _jsxs("span", { children: ["Page ", pageIndex + 1, " / ", pageCount] }), _jsx(Button, { variant: "outline", size: "sm", disabled: !canNextPage, onClick: () => onPageIndexChange(pageIndex + 1), children: "Next" })] })] }));
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, }: Props): import("react/jsx-runtime").JSX.Element;
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":"AAYA,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,EACnC,KAAK,EACL,QAAQ,EACR,WAAsC,EACtC,QAAQ,GACT,EAAE,KAAK,2CA+DP"}
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 = "Search price catalogs…", disabled, }) {
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 ? "Loading…" : "No price catalogs found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
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, }: Props): import("react/jsx-runtime").JSX.Element;
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":"AAYA,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,EAC/B,UAAU,EACV,KAAK,EACL,QAAQ,EACR,WAAkC,EAClC,QAAQ,GACT,EAAE,KAAK,2CAoEP"}
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 = "Search rate plans…", disabled, }) {
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 ? "Loading…" : "No rate plans found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
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;AA6BtF,MAAM,MAAM,YAAY,GAAG,cAAc,CAAA;AAgCzC,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,2CA+OrB"}
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 formSchema = z.object({
21
- code: z.string().min(1, "Code is required").max(50),
22
- name: z.string().min(1, "Name is required").max(255),
23
- description: z.string().optional().nullable(),
24
- mealPlanId: z.string().optional().nullable(),
25
- priceCatalogId: z.string().optional().nullable(),
26
- cancellationPolicyId: z.string().optional().nullable(),
27
- currencyCode: z.string().length(3, "Currency must be 3 chars"),
28
- chargeFrequency: z.enum(CHARGE_FREQUENCIES),
29
- guaranteeMode: z.enum(GUARANTEE_MODES),
30
- commissionable: z.boolean(),
31
- refundable: z.boolean(),
32
- active: z.boolean(),
33
- sortOrder: z.coerce.number().int(),
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: "EUR",
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: "EUR",
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 ? "Edit Rate Plan" : "Add Rate Plan" }) }), _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: "Code" }), _jsx(Input, { ...form.register("code"), placeholder: "FLEX" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { ...form.register("name"), placeholder: "Flexible Rate" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "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: "Currency" }), _jsx(CurrencyCombobox, { value: form.watch("currencyCode") || null, onChange: (next) => form.setValue("currencyCode", next ?? "EUR", {
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: "Charge frequency" }), _jsxs(Select, { items: CHARGE_FREQUENCIES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), 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, className: "capitalize", children: frequency.replace(/_/g, " ") }, frequency))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Guarantee" }), _jsxs(Select, { items: GUARANTEE_MODES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), 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, className: "capitalize", children: mode.replace(/_/g, " ") }, mode))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Meal plan" }), _jsx(MealPlanCombobox, { propertyId: propertyId, value: form.watch("mealPlanId"), onChange: (value) => form.setValue("mealPlanId", value ?? ""), placeholder: "None", disabled: !open })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Price catalog" }), _jsx(PriceCatalogCombobox, { value: form.watch("priceCatalogId"), onChange: (value) => form.setValue("priceCatalogId", value ?? ""), placeholder: "None", disabled: !open })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cancellation policy" }), _jsx(CancellationPolicyCombobox, { value: form.watch("cancellationPolicyId"), onChange: (value) => form.setValue("cancellationPolicyId", value ?? ""), placeholder: "None", 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: "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: "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: "Active" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sort order" }), _jsx(Input, { ...form.register("sortOrder"), type: "number", className: "w-32" })] })] }), _jsxs(DialogFooter, { children: [_jsx(Button, { type: "button", variant: "ghost", onClick: () => onOpenChange(false), children: "Cancel" }), _jsxs(Button, { type: "submit", disabled: isSubmitting, children: [isSubmitting ? _jsx(Loader2, { className: "mr-2 h-4 w-4 animate-spin" }) : null, isEditing ? "Save Changes" : "Add Rate Plan"] })] })] })] }) }));
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
  }