@voyantjs/pricing-ui 0.16.0 → 0.18.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 (67) hide show
  1. package/LICENSE +201 -109
  2. package/README.md +11 -0
  3. package/dist/components/cancellation-policy-combobox.d.ts +1 -1
  4. package/dist/components/cancellation-policy-combobox.d.ts.map +1 -1
  5. package/dist/components/cancellation-policy-combobox.js +6 -4
  6. package/dist/components/cancellation-policy-rule-dialog.d.ts.map +1 -1
  7. package/dist/components/cancellation-policy-rule-dialog.js +23 -15
  8. package/dist/components/dropoff-price-rule-dialog.d.ts.map +1 -1
  9. package/dist/components/dropoff-price-rule-dialog.js +32 -15
  10. package/dist/components/extra-price-rule-dialog.d.ts.map +1 -1
  11. package/dist/components/extra-price-rule-dialog.js +28 -14
  12. package/dist/components/option-price-rule-combobox.d.ts +1 -1
  13. package/dist/components/option-price-rule-combobox.d.ts.map +1 -1
  14. package/dist/components/option-price-rule-combobox.js +6 -4
  15. package/dist/components/option-price-rule-dialog.d.ts.map +1 -1
  16. package/dist/components/option-price-rule-dialog.js +35 -23
  17. package/dist/components/option-start-time-rule-dialog.d.ts.map +1 -1
  18. package/dist/components/option-start-time-rule-dialog.js +33 -14
  19. package/dist/components/option-unit-price-rule-dialog.d.ts.map +1 -1
  20. package/dist/components/option-unit-price-rule-dialog.js +30 -16
  21. package/dist/components/option-unit-tier-dialog.d.ts.map +1 -1
  22. package/dist/components/option-unit-tier-dialog.js +24 -10
  23. package/dist/components/pickup-price-rule-dialog.d.ts.map +1 -1
  24. package/dist/components/pickup-price-rule-dialog.js +29 -13
  25. package/dist/components/price-catalog-combobox.d.ts +1 -1
  26. package/dist/components/price-catalog-combobox.d.ts.map +1 -1
  27. package/dist/components/price-catalog-combobox.js +6 -2
  28. package/dist/components/price-schedule-combobox.d.ts.map +1 -1
  29. package/dist/components/price-schedule-combobox.js +6 -4
  30. package/dist/components/price-schedule-dialog.d.ts.map +1 -1
  31. package/dist/components/price-schedule-dialog.js +26 -15
  32. package/dist/components/pricing-category-combobox.d.ts.map +1 -1
  33. package/dist/components/pricing-category-combobox.js +4 -2
  34. package/dist/components/pricing-category-dependency-dialog.d.ts.map +1 -1
  35. package/dist/components/pricing-category-dependency-dialog.js +5 -1
  36. package/dist/components/pricing-category-dependency-form.d.ts.map +1 -1
  37. package/dist/components/pricing-category-dependency-form.js +17 -8
  38. package/dist/components/pricing-category-dialog.d.ts.map +1 -1
  39. package/dist/components/pricing-category-dialog.js +7 -3
  40. package/dist/components/pricing-category-form.d.ts.map +1 -1
  41. package/dist/components/pricing-category-form.js +20 -13
  42. package/dist/components/pricing-category-list.d.ts.map +1 -1
  43. package/dist/components/pricing-category-list.js +10 -6
  44. package/dist/components/product-combobox.d.ts +1 -1
  45. package/dist/components/product-combobox.d.ts.map +1 -1
  46. package/dist/components/product-combobox.js +6 -2
  47. package/dist/components/product-option-combobox.d.ts.map +1 -1
  48. package/dist/components/product-option-combobox.js +7 -5
  49. package/dist/i18n/en.d.ts +426 -0
  50. package/dist/i18n/en.d.ts.map +1 -0
  51. package/dist/i18n/en.js +425 -0
  52. package/dist/i18n/index.d.ts +5 -0
  53. package/dist/i18n/index.d.ts.map +1 -0
  54. package/dist/i18n/index.js +3 -0
  55. package/dist/i18n/messages.d.ts +391 -0
  56. package/dist/i18n/messages.d.ts.map +1 -0
  57. package/dist/i18n/messages.js +17 -0
  58. package/dist/i18n/provider.d.ts +874 -0
  59. package/dist/i18n/provider.d.ts.map +1 -0
  60. package/dist/i18n/provider.js +44 -0
  61. package/dist/i18n/ro.d.ts +426 -0
  62. package/dist/i18n/ro.d.ts.map +1 -0
  63. package/dist/i18n/ro.js +425 -0
  64. package/dist/index.d.ts +2 -0
  65. package/dist/index.d.ts.map +1 -1
  66. package/dist/index.js +1 -0
  67. package/package.json +33 -12
@@ -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 { usePricingUiMessagesOrDefault } from "../i18n/provider";
10
11
  import { OptionPriceRuleCombobox } from "./option-price-rule-combobox";
11
12
  const ADDON_PRICING_MODES = [
12
13
  "included",
@@ -15,22 +16,28 @@ const ADDON_PRICING_MODES = [
15
16
  "on_request",
16
17
  "unavailable",
17
18
  ];
18
- const formSchema = z.object({
19
- optionPriceRuleId: z.string().min(1, "Option price rule is required"),
20
- optionId: z.string().min(1, "Option ID is required"),
21
- productExtraId: z.string().optional().nullable(),
22
- optionExtraConfigId: z.string().optional().nullable(),
23
- pricingMode: z.enum(ADDON_PRICING_MODES),
24
- sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
25
- costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
26
- active: z.boolean(),
27
- sortOrder: z.coerce.number().int(),
28
- notes: z.string().optional().nullable(),
29
- });
19
+ function createFormSchema(messages) {
20
+ return z.object({
21
+ optionPriceRuleId: z
22
+ .string()
23
+ .min(1, messages.locationPriceRuleDialog.validation.optionPriceRuleRequired),
24
+ optionId: z.string().min(1, messages.locationPriceRuleDialog.validation.optionIdRequired),
25
+ productExtraId: z.string().optional().nullable(),
26
+ optionExtraConfigId: z.string().optional().nullable(),
27
+ pricingMode: z.enum(ADDON_PRICING_MODES),
28
+ sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
29
+ costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
30
+ active: z.boolean(),
31
+ sortOrder: z.coerce.number().int(),
32
+ notes: z.string().optional().nullable(),
33
+ });
34
+ }
30
35
  const toCents = (value) => typeof value === "number" ? Math.round(value * 100) : null;
31
36
  export function ExtraPriceRuleDialog({ open, onOpenChange, rule, onSuccess }) {
32
37
  const isEditing = !!rule;
33
38
  const { create, update } = useExtraPriceRuleMutation();
39
+ const messages = usePricingUiMessagesOrDefault();
40
+ const formSchema = createFormSchema(messages);
34
41
  const form = useForm({
35
42
  resolver: zodResolver(formSchema),
36
43
  defaultValues: {
@@ -85,8 +92,15 @@ export function ExtraPriceRuleDialog({ open, onOpenChange, rule, onSuccess }) {
85
92
  onOpenChange(false);
86
93
  };
87
94
  const isSubmitting = create.isPending || update.isPending;
88
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Extra Price Rule" : "Add Extra Price Rule" }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option price rule" }), _jsx(OptionPriceRuleCombobox, { value: form.watch("optionPriceRuleId"), onChange: (value) => form.setValue("optionPriceRuleId", value ?? "", {
95
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
96
+ ? messages.locationPriceRuleDialog.extra.titles.edit
97
+ : messages.locationPriceRuleDialog.extra.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.fields.optionPriceRule }), _jsx(OptionPriceRuleCombobox, { value: form.watch("optionPriceRuleId"), onChange: (value) => form.setValue("optionPriceRuleId", value ?? "", {
89
98
  shouldDirty: true,
90
99
  shouldValidate: true,
91
- }), disabled: isEditing }), form.formState.errors.optionPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option ID" }), _jsx(Input, { ...form.register("optionId"), placeholder: "popt_\u2026" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Product extra ID (optional)" }), _jsx(Input, { ...form.register("productExtraId"), placeholder: "pext_\u2026" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option extra config ID (optional)" }), _jsx(Input, { ...form.register("optionExtraConfigId"), placeholder: "oecf_\u2026" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Pricing mode" }), _jsxs(Select, { items: ADDON_PRICING_MODES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), value: form.watch("pricingMode"), onValueChange: (value) => form.setValue("pricingMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: ADDON_PRICING_MODES.map((mode) => (_jsx(SelectItem, { value: mode, className: "capitalize", children: mode.replace(/_/g, " ") }, mode))) })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sell amount" }), _jsx(Input, { ...form.register("sellAmount"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cost amount" }), _jsx(Input, { ...form.register("costAmount"), type: "number", step: "0.01", min: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sort order" }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] }), _jsxs("div", { className: "flex items-center gap-3 pt-6", 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: "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 Rule"] })] })] })] }) }));
100
+ }), disabled: isEditing }), form.formState.errors.optionPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.fields.optionId }), _jsx(Input, { ...form.register("optionId"), placeholder: messages.locationPriceRuleDialog.placeholders.optionId })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.extra.fields.productExtraId }), _jsx(Input, { ...form.register("productExtraId"), placeholder: messages.locationPriceRuleDialog.extra.placeholders.productExtraId })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.extra.fields.optionExtraConfigId }), _jsx(Input, { ...form.register("optionExtraConfigId"), placeholder: messages.locationPriceRuleDialog.extra.placeholders.optionExtraConfigId })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.fields.pricingMode }), _jsxs(Select, { items: ADDON_PRICING_MODES.map((mode) => ({
101
+ label: messages.common.addonPricingModeLabels[mode],
102
+ value: mode,
103
+ })), value: form.watch("pricingMode"), onValueChange: (value) => form.setValue("pricingMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: ADDON_PRICING_MODES.map((mode) => (_jsx(SelectItem, { value: mode, children: messages.common.addonPricingModeLabels[mode] }, mode))) })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.fields.sellAmount }), _jsx(Input, { ...form.register("sellAmount"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.fields.costAmount }), _jsx(Input, { ...form.register("costAmount"), type: "number", step: "0.01", min: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.fields.sortOrder }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] }), _jsxs("div", { className: "flex items-center gap-3 pt-6", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (checked) => form.setValue("active", checked) }), _jsx(Label, { children: messages.locationPriceRuleDialog.fields.active })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.locationPriceRuleDialog.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
104
+ ? messages.locationPriceRuleDialog.actions.saveRule
105
+ : messages.locationPriceRuleDialog.actions.createRule] })] })] })] }) }));
92
106
  }
@@ -4,6 +4,6 @@ type Props = {
4
4
  placeholder?: string;
5
5
  disabled?: boolean;
6
6
  };
7
- export declare function OptionPriceRuleCombobox({ value, onChange, placeholder, disabled, }: Props): import("react/jsx-runtime").JSX.Element;
7
+ export declare function OptionPriceRuleCombobox({ value, onChange, placeholder, disabled }: Props): import("react/jsx-runtime").JSX.Element;
8
8
  export {};
9
9
  //# sourceMappingURL=option-price-rule-combobox.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"option-price-rule-combobox.d.ts","sourceRoot":"","sources":["../../src/components/option-price-rule-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,uBAAuB,CAAC,EACtC,KAAK,EACL,QAAQ,EACR,WAA0C,EAC1C,QAAQ,GACT,EAAE,KAAK,2CAsEP"}
1
+ {"version":3,"file":"option-price-rule-combobox.d.ts","sourceRoot":"","sources":["../../src/components/option-price-rule-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,uBAAuB,CAAC,EAAE,KAAK,EAAE,QAAQ,EAAE,WAAW,EAAE,QAAQ,EAAE,EAAE,KAAK,2CA0ExF"}
@@ -2,8 +2,10 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  import { useOptionPriceRule, useOptionPriceRules, } 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 { usePricingUiMessagesOrDefault } from "../i18n/provider";
5
6
  const PAGE_SIZE = 25;
6
- export function OptionPriceRuleCombobox({ value, onChange, placeholder = "Search option price rules…", disabled, }) {
7
+ export function OptionPriceRuleCombobox({ value, onChange, placeholder, disabled }) {
8
+ const messages = usePricingUiMessagesOrDefault();
7
9
  const [search, setSearch] = React.useState("");
8
10
  const listQuery = useOptionPriceRules({ limit: PAGE_SIZE });
9
11
  const selectedQuery = useOptionPriceRule(value, { enabled: !!value });
@@ -34,9 +36,9 @@ export function OptionPriceRuleCombobox({ value, onChange, placeholder = "Search
34
36
  const id = next ?? null;
35
37
  onChange(id);
36
38
  setInputValue(id ? (itemMap.get(id)?.name ?? "") : "");
37
- }, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
38
- ? "Loading…"
39
- : "No option price rules found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
39
+ }, children: [_jsx(ComboboxInput, { placeholder: placeholder ?? messages.comboboxes.optionPriceRule.placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
40
+ ? messages.common.loading
41
+ : messages.comboboxes.optionPriceRule.empty }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
40
42
  const item = itemMap.get(id);
41
43
  if (!item)
42
44
  return null;
@@ -1 +1 @@
1
- {"version":3,"file":"option-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA8B,MAAM,yBAAyB,CAAA;AAyDhG,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,qBAAqB,CAAA;IAC5B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAClD;AAKD,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,SAAS,GACV,EAAE,0BAA0B,2CAwR5B"}
1
+ {"version":3,"file":"option-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA8B,MAAM,yBAAyB,CAAA;AA8DhG,MAAM,WAAW,0BAA0B;IACzC,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,qBAAqB,CAAA;IAC5B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAClD;AAKD,wBAAgB,qBAAqB,CAAC,EACpC,IAAI,EACJ,YAAY,EACZ,IAAI,EACJ,SAAS,GACV,EAAE,0BAA0B,2CAwS5B"}
@@ -7,35 +7,40 @@ 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 { usePricingUiMessagesOrDefault } from "../i18n/provider";
10
11
  import { CancellationPolicyCombobox } from "./cancellation-policy-combobox";
11
12
  import { PriceCatalogCombobox } from "./price-catalog-combobox";
12
13
  import { PriceScheduleCombobox } from "./price-schedule-combobox";
13
14
  import { ProductCombobox } from "./product-combobox";
14
15
  import { ProductOptionCombobox } from "./product-option-combobox";
15
16
  const PRICING_MODES = ["per_person", "per_booking", "starting_from", "free", "on_request"];
16
- const formSchema = z.object({
17
- productId: z.string().min(1, "Product is required"),
18
- optionId: z.string().min(1, "Option is required"),
19
- priceCatalogId: z.string().min(1, "Catalog is required"),
20
- priceScheduleId: z.string().optional().nullable(),
21
- cancellationPolicyId: z.string().optional().nullable(),
22
- name: z.string().min(1, "Name is required").max(255),
23
- code: z.string().max(100).optional().nullable(),
24
- description: z.string().optional().nullable(),
25
- pricingMode: z.enum(PRICING_MODES),
26
- baseSell: z.coerce.number().min(0),
27
- baseCost: z.coerce.number().min(0),
28
- minPerBooking: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
29
- maxPerBooking: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
30
- allPricingCategories: z.boolean(),
31
- isDefault: z.boolean(),
32
- active: z.boolean(),
33
- notes: z.string().optional().nullable(),
34
- });
17
+ function createFormSchema(messages) {
18
+ return z.object({
19
+ productId: z.string().min(1, messages.optionPriceRuleDialog.validation.productRequired),
20
+ optionId: z.string().min(1, messages.optionPriceRuleDialog.validation.optionRequired),
21
+ priceCatalogId: z.string().min(1, messages.optionPriceRuleDialog.validation.catalogRequired),
22
+ priceScheduleId: z.string().optional().nullable(),
23
+ cancellationPolicyId: z.string().optional().nullable(),
24
+ name: z.string().min(1, messages.optionPriceRuleDialog.validation.nameRequired).max(255),
25
+ code: z.string().max(100).optional().nullable(),
26
+ description: z.string().optional().nullable(),
27
+ pricingMode: z.enum(PRICING_MODES),
28
+ baseSell: z.coerce.number().min(0),
29
+ baseCost: z.coerce.number().min(0),
30
+ minPerBooking: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
31
+ maxPerBooking: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
32
+ allPricingCategories: z.boolean(),
33
+ isDefault: z.boolean(),
34
+ active: z.boolean(),
35
+ notes: z.string().optional().nullable(),
36
+ });
37
+ }
35
38
  const toInt = (value) => typeof value === "number" ? value : null;
36
39
  export function OptionPriceRuleDialog({ open, onOpenChange, rule, onSuccess, }) {
37
40
  const isEditing = !!rule;
38
41
  const { create, update } = useOptionPriceRuleMutation();
42
+ const messages = usePricingUiMessagesOrDefault();
43
+ const formSchema = createFormSchema(messages);
39
44
  const form = useForm({
40
45
  resolver: zodResolver(formSchema),
41
46
  defaultValues: {
@@ -113,20 +118,27 @@ export function OptionPriceRuleDialog({ open, onOpenChange, rule, onSuccess, })
113
118
  onOpenChange(false);
114
119
  };
115
120
  const 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 Option Price Rule" : "Add Option Price Rule" }) }), _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: "Product" }), _jsx(ProductCombobox, { value: form.watch("productId"), onChange: (value) => {
121
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
122
+ ? messages.optionPriceRuleDialog.titles.edit
123
+ : messages.optionPriceRuleDialog.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.optionPriceRuleDialog.fields.product }), _jsx(ProductCombobox, { value: form.watch("productId"), onChange: (value) => {
117
124
  form.setValue("productId", value ?? "", {
118
125
  shouldDirty: true,
119
126
  shouldValidate: true,
120
127
  });
121
128
  form.setValue("optionId", "", { shouldDirty: true, shouldValidate: true });
122
- }, disabled: isEditing }), form.formState.errors.productId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.productId.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option" }), _jsx(ProductOptionCombobox, { productId: watchedProductId, value: form.watch("optionId"), onChange: (value) => form.setValue("optionId", value ?? "", {
129
+ }, disabled: isEditing }), form.formState.errors.productId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.productId.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.option }), _jsx(ProductOptionCombobox, { productId: watchedProductId, value: form.watch("optionId"), onChange: (value) => form.setValue("optionId", value ?? "", {
123
130
  shouldDirty: true,
124
131
  shouldValidate: true,
125
- }), disabled: isEditing }), form.formState.errors.optionId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionId.message })) : null] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Name" }), _jsx(Input, { ...form.register("name") }), form.formState.errors.name ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.name.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Code" }), _jsx(Input, { ...form.register("code") })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Catalog" }), _jsx(PriceCatalogCombobox, { value: form.watch("priceCatalogId"), onChange: (value) => {
132
+ }), disabled: isEditing }), form.formState.errors.optionId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionId.message })) : null] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.name }), _jsx(Input, { ...form.register("name") }), form.formState.errors.name ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.name.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.code }), _jsx(Input, { ...form.register("code") })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.catalog }), _jsx(PriceCatalogCombobox, { value: form.watch("priceCatalogId"), onChange: (value) => {
126
133
  form.setValue("priceCatalogId", value ?? "", {
127
134
  shouldDirty: true,
128
135
  shouldValidate: true,
129
136
  });
130
137
  form.setValue("priceScheduleId", "", { shouldDirty: true });
131
- } }), form.formState.errors.priceCatalogId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.priceCatalogId.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Schedule (optional)" }), _jsx(PriceScheduleCombobox, { priceCatalogId: watchedCatalogId, value: form.watch("priceScheduleId"), onChange: (value) => form.setValue("priceScheduleId", value ?? "", { shouldDirty: true }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cancellation policy (optional)" }), _jsx(CancellationPolicyCombobox, { value: form.watch("cancellationPolicyId"), onChange: (value) => form.setValue("cancellationPolicyId", value ?? "", { shouldDirty: true }) })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Pricing mode" }), _jsxs(Select, { items: PRICING_MODES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), value: form.watch("pricingMode"), onValueChange: (value) => form.setValue("pricingMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PRICING_MODES.map((mode) => (_jsx(SelectItem, { value: mode, className: "capitalize", children: mode.replace(/_/g, " ") }, mode))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Base sell" }), _jsx(Input, { ...form.register("baseSell"), type: "number", min: "0", step: "0.01" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Base cost" }), _jsx(Input, { ...form.register("baseCost"), type: "number", min: "0", step: "0.01" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Min per booking" }), _jsx(Input, { ...form.register("minPerBooking"), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Max per booking" }), _jsx(Input, { ...form.register("maxPerBooking"), type: "number", min: "0" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Description" }), _jsx(Textarea, { ...form.register("description"), rows: 2 })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("allPricingCategories"), onCheckedChange: (checked) => form.setValue("allPricingCategories", checked) }), _jsx(Label, { children: "All pricing categories" })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("isDefault"), onCheckedChange: (checked) => form.setValue("isDefault", checked) }), _jsx(Label, { children: "Default rule" })] }), _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: "Notes" }), _jsx(Textarea, { ...form.register("notes"), rows: 2 })] })] }), _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" : "Create Rule"] })] })] })] }) }));
138
+ } }), form.formState.errors.priceCatalogId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.priceCatalogId.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.schedule }), _jsx(PriceScheduleCombobox, { priceCatalogId: watchedCatalogId, value: form.watch("priceScheduleId"), onChange: (value) => form.setValue("priceScheduleId", value ?? "", { shouldDirty: true }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.cancellationPolicy }), _jsx(CancellationPolicyCombobox, { value: form.watch("cancellationPolicyId"), onChange: (value) => form.setValue("cancellationPolicyId", value ?? "", { shouldDirty: true }) })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.pricingMode }), _jsxs(Select, { items: PRICING_MODES.map((mode) => ({
139
+ label: messages.common.optionPriceRulePricingModeLabels[mode],
140
+ value: mode,
141
+ })), value: form.watch("pricingMode"), onValueChange: (value) => form.setValue("pricingMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PRICING_MODES.map((mode) => (_jsx(SelectItem, { value: mode, children: messages.common.optionPriceRulePricingModeLabels[mode] }, mode))) })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.baseSell }), _jsx(Input, { ...form.register("baseSell"), type: "number", min: "0", step: "0.01" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.baseCost }), _jsx(Input, { ...form.register("baseCost"), type: "number", min: "0", step: "0.01" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.minPerBooking }), _jsx(Input, { ...form.register("minPerBooking"), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.maxPerBooking }), _jsx(Input, { ...form.register("maxPerBooking"), type: "number", min: "0" })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.description }), _jsx(Textarea, { ...form.register("description"), rows: 2 })] }), _jsxs("div", { className: "grid grid-cols-3 gap-4", children: [_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("allPricingCategories"), onCheckedChange: (checked) => form.setValue("allPricingCategories", checked) }), _jsx(Label, { children: messages.optionPriceRuleDialog.fields.allPricingCategories })] }), _jsxs("div", { className: "flex items-center gap-2", children: [_jsx(Switch, { checked: form.watch("isDefault"), onCheckedChange: (checked) => form.setValue("isDefault", checked) }), _jsx(Label, { children: messages.optionPriceRuleDialog.fields.defaultRule })] }), _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.optionPriceRuleDialog.fields.active })] })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionPriceRuleDialog.fields.notes }), _jsx(Textarea, { ...form.register("notes"), rows: 2 })] })] }), _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
142
+ ? messages.common.saveChanges
143
+ : messages.optionPriceRuleDialog.actions.create] })] })] })] }) }));
132
144
  }
@@ -1 +1 @@
1
- {"version":3,"file":"option-start-time-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-start-time-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,yBAAyB,CAAA;AAiDhC,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,yBAAyB,CAAA;IAChC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,yBAAyB,KAAK,IAAI,CAAA;CACtD,CAAA;AAOD,wBAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAuMvF"}
1
+ {"version":3,"file":"option-start-time-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-start-time-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,yBAAyB,CAAA;AAyDhC,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,yBAAyB,CAAA;IAChC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,yBAAyB,KAAK,IAAI,CAAA;CACtD,CAAA;AAOD,wBAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAgOvF"}
@@ -7,26 +7,35 @@ 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 { usePricingUiMessagesOrDefault } from "../i18n/provider";
10
11
  import { OptionPriceRuleCombobox } from "./option-price-rule-combobox";
11
12
  const RULE_MODES = ["included", "excluded", "override", "adjustment"];
12
13
  const ADJUSTMENT_TYPES = ["fixed", "percentage"];
13
- const formSchema = z.object({
14
- optionPriceRuleId: z.string().min(1, "Option price rule is required"),
15
- optionId: z.string().min(1, "Option ID is required"),
16
- startTimeId: z.string().min(1, "Start time ID is required"),
17
- ruleMode: z.enum(RULE_MODES),
18
- adjustmentType: z.enum(ADJUSTMENT_TYPES).optional().nullable(),
19
- sellAdjustment: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
20
- costAdjustment: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
21
- adjustmentPercent: z.coerce.number().min(0).max(100).optional().or(z.literal("")).nullable(),
22
- active: z.boolean(),
23
- notes: z.string().optional().nullable(),
24
- });
14
+ function createFormSchema(messages) {
15
+ return z.object({
16
+ optionPriceRuleId: z
17
+ .string()
18
+ .min(1, messages.optionStartTimeRuleDialog.validation.optionPriceRuleRequired),
19
+ optionId: z.string().min(1, messages.optionStartTimeRuleDialog.validation.optionIdRequired),
20
+ startTimeId: z
21
+ .string()
22
+ .min(1, messages.optionStartTimeRuleDialog.validation.startTimeIdRequired),
23
+ ruleMode: z.enum(RULE_MODES),
24
+ adjustmentType: z.enum(ADJUSTMENT_TYPES).optional().nullable(),
25
+ sellAdjustment: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
26
+ costAdjustment: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
27
+ adjustmentPercent: z.coerce.number().min(0).max(100).optional().or(z.literal("")).nullable(),
28
+ active: z.boolean(),
29
+ notes: z.string().optional().nullable(),
30
+ });
31
+ }
25
32
  const toCents = (value) => typeof value === "number" ? Math.round(value * 100) : null;
26
33
  const toBasisPoints = (value) => typeof value === "number" ? Math.round(value * 100) : null;
27
34
  export function OptionStartTimeRuleDialog({ open, onOpenChange, rule, onSuccess }) {
28
35
  const isEditing = !!rule;
29
36
  const { create, update } = useOptionStartTimeRuleMutation();
37
+ const messages = usePricingUiMessagesOrDefault();
38
+ const formSchema = createFormSchema(messages);
30
39
  const form = useForm({
31
40
  resolver: zodResolver(formSchema),
32
41
  defaultValues: {
@@ -83,8 +92,18 @@ export function OptionStartTimeRuleDialog({ open, onOpenChange, rule, onSuccess
83
92
  onOpenChange(false);
84
93
  };
85
94
  const isSubmitting = create.isPending || update.isPending;
86
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Start Time Rule" : "Add Start Time Rule" }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option price rule" }), _jsx(OptionPriceRuleCombobox, { value: form.watch("optionPriceRuleId"), onChange: (value) => form.setValue("optionPriceRuleId", value ?? "", {
95
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
96
+ ? messages.optionStartTimeRuleDialog.titles.edit
97
+ : messages.optionStartTimeRuleDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.optionPriceRule }), _jsx(OptionPriceRuleCombobox, { value: form.watch("optionPriceRuleId"), onChange: (value) => form.setValue("optionPriceRuleId", value ?? "", {
87
98
  shouldDirty: true,
88
99
  shouldValidate: true,
89
- }), disabled: isEditing }), form.formState.errors.optionPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option ID" }), _jsx(Input, { ...form.register("optionId"), placeholder: "popt_\u2026" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Start time ID" }), _jsx(Input, { ...form.register("startTimeId"), placeholder: "pst_\u2026" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Rule mode" }), _jsxs(Select, { items: RULE_MODES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), value: form.watch("ruleMode"), onValueChange: (value) => form.setValue("ruleMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: RULE_MODES.map((mode) => (_jsx(SelectItem, { value: mode, className: "capitalize", children: mode.replace(/_/g, " ") }, mode))) })] })] }), showAdjustment ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Adjustment type" }), _jsxs(Select, { items: ADJUSTMENT_TYPES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), value: form.watch("adjustmentType") ?? "", onValueChange: (value) => form.setValue("adjustmentType", (value || null)), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: "Select\u2026" }) }), _jsx(SelectContent, { children: ADJUSTMENT_TYPES.map((type) => (_jsx(SelectItem, { value: type, className: "capitalize", children: type }, type))) })] })] })) : null] }), showAdjustment ? (_jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sell adjustment" }), _jsx(Input, { ...form.register("sellAdjustment"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cost adjustment" }), _jsx(Input, { ...form.register("costAdjustment"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Adjustment (%)" }), _jsx(Input, { ...form.register("adjustmentPercent"), type: "number", step: "0.01", min: "0", max: "100" })] })] })) : null, _jsxs("div", { className: "flex items-center gap-3", 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: "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 Rule"] })] })] })] }) }));
100
+ }), disabled: isEditing }), form.formState.errors.optionPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.optionId }), _jsx(Input, { ...form.register("optionId"), placeholder: messages.optionStartTimeRuleDialog.placeholders.optionId })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.startTimeId }), _jsx(Input, { ...form.register("startTimeId"), placeholder: messages.optionStartTimeRuleDialog.placeholders.startTimeId })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.ruleMode }), _jsxs(Select, { items: RULE_MODES.map((mode) => ({
101
+ label: messages.common.startTimeRuleModeLabels[mode],
102
+ value: mode,
103
+ })), value: form.watch("ruleMode"), onValueChange: (value) => form.setValue("ruleMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: RULE_MODES.map((mode) => (_jsx(SelectItem, { value: mode, children: messages.common.startTimeRuleModeLabels[mode] }, mode))) })] })] }), showAdjustment ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.adjustmentType }), _jsxs(Select, { items: ADJUSTMENT_TYPES.map((type) => ({
104
+ label: messages.common.adjustmentTypeLabels[type],
105
+ value: type,
106
+ })), value: form.watch("adjustmentType") ?? "", onValueChange: (value) => form.setValue("adjustmentType", (value || null)), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, { placeholder: messages.optionStartTimeRuleDialog.placeholders.select }) }), _jsx(SelectContent, { children: ADJUSTMENT_TYPES.map((type) => (_jsx(SelectItem, { value: type, children: messages.common.adjustmentTypeLabels[type] }, type))) })] })] })) : null] }), showAdjustment ? (_jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.sellAdjustment }), _jsx(Input, { ...form.register("sellAdjustment"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.costAdjustment }), _jsx(Input, { ...form.register("costAdjustment"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.adjustmentPercent }), _jsx(Input, { ...form.register("adjustmentPercent"), type: "number", step: "0.01", min: "0", max: "100" })] })] })) : null, _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (checked) => form.setValue("active", checked) }), _jsx(Label, { children: messages.optionStartTimeRuleDialog.fields.active })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionStartTimeRuleDialog.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
107
+ ? messages.common.saveChanges
108
+ : messages.optionStartTimeRuleDialog.actions.create] })] })] })] }) }));
90
109
  }
@@ -1 +1 @@
1
- {"version":3,"file":"option-unit-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-unit-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,yBAAyB,CAAA;AAwDhC,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,yBAAyB,CAAA;IAChC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,yBAAyB,KAAK,IAAI,CAAA;CACtD,CAAA;AAOD,wBAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAgMvF"}
1
+ {"version":3,"file":"option-unit-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-unit-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,yBAAyB,EAE/B,MAAM,yBAAyB,CAAA;AA+DhC,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,yBAAyB,CAAA;IAChC,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,yBAAyB,KAAK,IAAI,CAAA;CACtD,CAAA;AAOD,wBAAgB,yBAAyB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAgNvF"}
@@ -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 { usePricingUiMessagesOrDefault } from "../i18n/provider";
10
11
  import { OptionPriceRuleCombobox } from "./option-price-rule-combobox";
11
12
  import { PricingCategoryCombobox } from "./pricing-category-combobox";
12
13
  const PRICING_MODES = [
@@ -17,25 +18,31 @@ const PRICING_MODES = [
17
18
  "free",
18
19
  "on_request",
19
20
  ];
20
- const formSchema = z.object({
21
- optionPriceRuleId: z.string().min(1, "Option price rule is required"),
22
- optionId: z.string().min(1, "Option ID is required"),
23
- unitId: z.string().min(1, "Unit ID is required"),
24
- pricingCategoryId: z.string().optional().nullable(),
25
- pricingMode: z.enum(PRICING_MODES),
26
- sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
27
- costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
28
- minQuantity: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
29
- maxQuantity: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
30
- sortOrder: z.coerce.number().int(),
31
- active: z.boolean(),
32
- notes: z.string().optional().nullable(),
33
- });
21
+ function createFormSchema(messages) {
22
+ return z.object({
23
+ optionPriceRuleId: z
24
+ .string()
25
+ .min(1, messages.optionUnitPriceRuleDialog.validation.optionPriceRuleRequired),
26
+ optionId: z.string().min(1, messages.optionUnitPriceRuleDialog.validation.optionIdRequired),
27
+ unitId: z.string().min(1, messages.optionUnitPriceRuleDialog.validation.unitIdRequired),
28
+ pricingCategoryId: z.string().optional().nullable(),
29
+ pricingMode: z.enum(PRICING_MODES),
30
+ sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
31
+ costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
32
+ minQuantity: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
33
+ maxQuantity: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
34
+ sortOrder: z.coerce.number().int(),
35
+ active: z.boolean(),
36
+ notes: z.string().optional().nullable(),
37
+ });
38
+ }
34
39
  const toInt = (value) => typeof value === "number" ? value : null;
35
40
  const toCents = (value) => typeof value === "number" ? Math.round(value * 100) : null;
36
41
  export function OptionUnitPriceRuleDialog({ open, onOpenChange, rule, onSuccess }) {
37
42
  const isEditing = !!rule;
38
43
  const { create, update } = useOptionUnitPriceRuleMutation();
44
+ const messages = usePricingUiMessagesOrDefault();
45
+ const formSchema = createFormSchema(messages);
39
46
  const form = useForm({
40
47
  resolver: zodResolver(formSchema),
41
48
  defaultValues: {
@@ -96,8 +103,15 @@ export function OptionUnitPriceRuleDialog({ open, onOpenChange, rule, onSuccess
96
103
  onOpenChange(false);
97
104
  };
98
105
  const isSubmitting = create.isPending || update.isPending;
99
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Option Unit Price Rule" : "Add Option Unit Price Rule" }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option price rule" }), _jsx(OptionPriceRuleCombobox, { value: form.watch("optionPriceRuleId"), onChange: (value) => form.setValue("optionPriceRuleId", value ?? "", {
106
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
107
+ ? messages.optionUnitPriceRuleDialog.titles.edit
108
+ : messages.optionUnitPriceRuleDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.optionPriceRule }), _jsx(OptionPriceRuleCombobox, { value: form.watch("optionPriceRuleId"), onChange: (value) => form.setValue("optionPriceRuleId", value ?? "", {
100
109
  shouldDirty: true,
101
110
  shouldValidate: true,
102
- }), disabled: isEditing }), form.formState.errors.optionPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option ID" }), _jsx(Input, { ...form.register("optionId"), placeholder: "popt_\u2026" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Unit ID" }), _jsx(Input, { ...form.register("unitId"), placeholder: "punit_\u2026" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Pricing category (optional)" }), _jsx(PricingCategoryCombobox, { value: form.watch("pricingCategoryId"), onChange: (value) => form.setValue("pricingCategoryId", value ?? "", { shouldDirty: true }) })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Pricing mode" }), _jsxs(Select, { items: PRICING_MODES.map((x) => ({ label: x.replace(/_/g, " "), value: x })), value: form.watch("pricingMode"), onValueChange: (value) => form.setValue("pricingMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PRICING_MODES.map((mode) => (_jsx(SelectItem, { value: mode, className: "capitalize", children: mode.replace(/_/g, " ") }, mode))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sell amount" }), _jsx(Input, { ...form.register("sellAmount"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cost amount" }), _jsx(Input, { ...form.register("costAmount"), type: "number", step: "0.01", min: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Min quantity" }), _jsx(Input, { ...form.register("minQuantity"), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Max quantity" }), _jsx(Input, { ...form.register("maxQuantity"), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sort order" }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] })] }), _jsxs("div", { className: "flex items-center gap-3", 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: "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 Rule"] })] })] })] }) }));
111
+ }), disabled: isEditing }), form.formState.errors.optionPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.optionId }), _jsx(Input, { ...form.register("optionId"), placeholder: messages.optionUnitPriceRuleDialog.placeholders.optionId })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.unitId }), _jsx(Input, { ...form.register("unitId"), placeholder: messages.optionUnitPriceRuleDialog.placeholders.unitId })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.pricingCategory }), _jsx(PricingCategoryCombobox, { value: form.watch("pricingCategoryId"), onChange: (value) => form.setValue("pricingCategoryId", value ?? "", { shouldDirty: true }), placeholder: messages.optionUnitPriceRuleDialog.placeholders.pricingCategory })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.pricingMode }), _jsxs(Select, { items: PRICING_MODES.map((mode) => ({
112
+ label: messages.common.unitPricingModeLabels[mode],
113
+ value: mode,
114
+ })), value: form.watch("pricingMode"), onValueChange: (value) => form.setValue("pricingMode", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: PRICING_MODES.map((mode) => (_jsx(SelectItem, { value: mode, children: messages.common.unitPricingModeLabels[mode] }, mode))) })] })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.sellAmount }), _jsx(Input, { ...form.register("sellAmount"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.costAmount }), _jsx(Input, { ...form.register("costAmount"), type: "number", step: "0.01", min: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-3 gap-3", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.minQuantity }), _jsx(Input, { ...form.register("minQuantity"), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.maxQuantity }), _jsx(Input, { ...form.register("maxQuantity"), type: "number", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.sortOrder }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] })] }), _jsxs("div", { className: "flex items-center gap-3", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (checked) => form.setValue("active", checked) }), _jsx(Label, { children: messages.optionUnitPriceRuleDialog.fields.active })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitPriceRuleDialog.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
115
+ ? messages.common.saveChanges
116
+ : messages.optionUnitPriceRuleDialog.actions.create] })] })] })] }) }));
103
117
  }
@@ -1 +1 @@
1
- {"version":3,"file":"option-unit-tier-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-unit-tier-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,oBAAoB,EAA6B,MAAM,yBAAyB,CAAA;AAgC9F,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAA;CACjD,CAAA;AAOD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CA0IlF"}
1
+ {"version":3,"file":"option-unit-tier-dialog.d.ts","sourceRoot":"","sources":["../../src/components/option-unit-tier-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,oBAAoB,EAA6B,MAAM,yBAAyB,CAAA;AA0C9F,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,oBAAoB,CAAA;IAC3B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,oBAAoB,KAAK,IAAI,CAAA;CACjD,CAAA;AAOD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAkJlF"}
@@ -7,20 +7,30 @@ 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
- const formSchema = z.object({
11
- optionUnitPriceRuleId: z.string().min(1, "Option unit price rule is required"),
12
- minQuantity: z.coerce.number().int().min(1, "Min quantity must be at least 1"),
13
- maxQuantity: z.coerce.number().int().min(1).optional().or(z.literal("")).nullable(),
14
- sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
15
- costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
16
- active: z.boolean(),
17
- sortOrder: z.coerce.number().int(),
18
- });
10
+ import { usePricingUiMessagesOrDefault } from "../i18n/provider";
11
+ function createFormSchema(messages) {
12
+ return z.object({
13
+ optionUnitPriceRuleId: z
14
+ .string()
15
+ .min(1, messages.optionUnitTierDialog.validation.optionUnitPriceRuleIdRequired),
16
+ minQuantity: z.coerce
17
+ .number()
18
+ .int()
19
+ .min(1, messages.optionUnitTierDialog.validation.minQuantityMin),
20
+ maxQuantity: z.coerce.number().int().min(1).optional().or(z.literal("")).nullable(),
21
+ sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
22
+ costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
23
+ active: z.boolean(),
24
+ sortOrder: z.coerce.number().int(),
25
+ });
26
+ }
19
27
  const toCents = (value) => typeof value === "number" ? Math.round(value * 100) : null;
20
28
  const toInt = (value) => typeof value === "number" ? value : null;
21
29
  export function OptionUnitTierDialog({ open, onOpenChange, tier, onSuccess }) {
22
30
  const isEditing = !!tier;
23
31
  const { create, update } = useOptionUnitTierMutation();
32
+ const messages = usePricingUiMessagesOrDefault();
33
+ const formSchema = createFormSchema(messages);
24
34
  const form = useForm({
25
35
  resolver: zodResolver(formSchema),
26
36
  defaultValues: {
@@ -74,5 +84,9 @@ export function OptionUnitTierDialog({ open, onOpenChange, tier, onSuccess }) {
74
84
  onOpenChange(false);
75
85
  };
76
86
  const isSubmitting = create.isPending || update.isPending;
77
- return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Unit Tier" : "Add Unit Tier" }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Option unit price rule ID" }), _jsx(Input, { ...form.register("optionUnitPriceRuleId"), placeholder: "oupr_\u2026", disabled: isEditing }), form.formState.errors.optionUnitPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionUnitPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Min quantity" }), _jsx(Input, { ...form.register("minQuantity"), type: "number", min: "1" }), form.formState.errors.minQuantity ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.minQuantity.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Max quantity" }), _jsx(Input, { ...form.register("maxQuantity"), type: "number", min: "1" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sell amount" }), _jsx(Input, { ...form.register("sellAmount"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Cost amount" }), _jsx(Input, { ...form.register("costAmount"), type: "number", step: "0.01", min: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Sort order" }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] }), _jsxs("div", { className: "flex items-center gap-3 pt-6", 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 Tier"] })] })] })] }) }));
87
+ return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing
88
+ ? messages.optionUnitTierDialog.titles.edit
89
+ : messages.optionUnitTierDialog.titles.create }) }), _jsxs("form", { onSubmit: form.handleSubmit(onSubmit), children: [_jsxs(DialogBody, { className: "grid gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitTierDialog.fields.optionUnitPriceRuleId }), _jsx(Input, { ...form.register("optionUnitPriceRuleId"), placeholder: messages.optionUnitTierDialog.placeholders.optionUnitPriceRuleId, disabled: isEditing }), form.formState.errors.optionUnitPriceRuleId ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.optionUnitPriceRuleId.message })) : null] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitTierDialog.fields.minQuantity }), _jsx(Input, { ...form.register("minQuantity"), type: "number", min: "1" }), form.formState.errors.minQuantity ? (_jsx("p", { className: "text-xs text-destructive", children: form.formState.errors.minQuantity.message })) : null] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitTierDialog.fields.maxQuantity }), _jsx(Input, { ...form.register("maxQuantity"), type: "number", min: "1" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitTierDialog.fields.sellAmount }), _jsx(Input, { ...form.register("sellAmount"), type: "number", step: "0.01", min: "0" })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitTierDialog.fields.costAmount }), _jsx(Input, { ...form.register("costAmount"), type: "number", step: "0.01", min: "0" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: messages.optionUnitTierDialog.fields.sortOrder }), _jsx(Input, { ...form.register("sortOrder"), type: "number" })] }), _jsxs("div", { className: "flex items-center gap-3 pt-6", children: [_jsx(Switch, { checked: form.watch("active"), onCheckedChange: (checked) => form.setValue("active", checked) }), _jsx(Label, { children: messages.optionUnitTierDialog.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
90
+ ? messages.common.saveChanges
91
+ : messages.optionUnitTierDialog.actions.create] })] })] })] }) }));
78
92
  }
@@ -1 +1 @@
1
- {"version":3,"file":"pickup-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/pickup-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA8B,MAAM,yBAAyB,CAAA;AAmDhG,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,qBAAqB,CAAA;IAC5B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAClD,CAAA;AAKD,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAmKnF"}
1
+ {"version":3,"file":"pickup-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/pickup-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,qBAAqB,EAA8B,MAAM,yBAAyB,CAAA;AAyDhG,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,qBAAqB,CAAA;IAC5B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,qBAAqB,KAAK,IAAI,CAAA;CAClD,CAAA;AAKD,wBAAgB,qBAAqB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAoLnF"}