@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.
- package/LICENSE +201 -109
- package/README.md +11 -0
- package/dist/components/cancellation-policy-combobox.d.ts +1 -1
- package/dist/components/cancellation-policy-combobox.d.ts.map +1 -1
- package/dist/components/cancellation-policy-combobox.js +6 -4
- package/dist/components/cancellation-policy-rule-dialog.d.ts.map +1 -1
- package/dist/components/cancellation-policy-rule-dialog.js +23 -15
- package/dist/components/dropoff-price-rule-dialog.d.ts.map +1 -1
- package/dist/components/dropoff-price-rule-dialog.js +32 -15
- package/dist/components/extra-price-rule-dialog.d.ts.map +1 -1
- package/dist/components/extra-price-rule-dialog.js +28 -14
- package/dist/components/option-price-rule-combobox.d.ts +1 -1
- package/dist/components/option-price-rule-combobox.d.ts.map +1 -1
- package/dist/components/option-price-rule-combobox.js +6 -4
- package/dist/components/option-price-rule-dialog.d.ts.map +1 -1
- package/dist/components/option-price-rule-dialog.js +35 -23
- package/dist/components/option-start-time-rule-dialog.d.ts.map +1 -1
- package/dist/components/option-start-time-rule-dialog.js +33 -14
- package/dist/components/option-unit-price-rule-dialog.d.ts.map +1 -1
- package/dist/components/option-unit-price-rule-dialog.js +30 -16
- package/dist/components/option-unit-tier-dialog.d.ts.map +1 -1
- package/dist/components/option-unit-tier-dialog.js +24 -10
- package/dist/components/pickup-price-rule-dialog.d.ts.map +1 -1
- package/dist/components/pickup-price-rule-dialog.js +29 -13
- package/dist/components/price-catalog-combobox.d.ts +1 -1
- package/dist/components/price-catalog-combobox.d.ts.map +1 -1
- package/dist/components/price-catalog-combobox.js +6 -2
- package/dist/components/price-schedule-combobox.d.ts.map +1 -1
- package/dist/components/price-schedule-combobox.js +6 -4
- package/dist/components/price-schedule-dialog.d.ts.map +1 -1
- package/dist/components/price-schedule-dialog.js +26 -15
- package/dist/components/pricing-category-combobox.d.ts.map +1 -1
- package/dist/components/pricing-category-combobox.js +4 -2
- package/dist/components/pricing-category-dependency-dialog.d.ts.map +1 -1
- package/dist/components/pricing-category-dependency-dialog.js +5 -1
- package/dist/components/pricing-category-dependency-form.d.ts.map +1 -1
- package/dist/components/pricing-category-dependency-form.js +17 -8
- package/dist/components/pricing-category-dialog.d.ts.map +1 -1
- package/dist/components/pricing-category-dialog.js +7 -3
- package/dist/components/pricing-category-form.d.ts.map +1 -1
- package/dist/components/pricing-category-form.js +20 -13
- package/dist/components/pricing-category-list.d.ts.map +1 -1
- package/dist/components/pricing-category-list.js +10 -6
- package/dist/components/product-combobox.d.ts +1 -1
- package/dist/components/product-combobox.d.ts.map +1 -1
- package/dist/components/product-combobox.js +6 -2
- package/dist/components/product-option-combobox.d.ts.map +1 -1
- package/dist/components/product-option-combobox.js +7 -5
- package/dist/i18n/en.d.ts +426 -0
- package/dist/i18n/en.d.ts.map +1 -0
- package/dist/i18n/en.js +425 -0
- package/dist/i18n/index.d.ts +5 -0
- package/dist/i18n/index.d.ts.map +1 -0
- package/dist/i18n/index.js +3 -0
- package/dist/i18n/messages.d.ts +391 -0
- package/dist/i18n/messages.d.ts.map +1 -0
- package/dist/i18n/messages.js +17 -0
- package/dist/i18n/provider.d.ts +874 -0
- package/dist/i18n/provider.d.ts.map +1 -0
- package/dist/i18n/provider.js +44 -0
- package/dist/i18n/ro.d.ts +426 -0
- package/dist/i18n/ro.d.ts.map +1 -0
- package/dist/i18n/ro.js +425 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/package.json +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
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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
|
|
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:
|
|
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
|
|
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":"
|
|
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
|
|
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
|
-
?
|
|
39
|
-
:
|
|
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;
|
|
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
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
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
|
|
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:
|
|
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:
|
|
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:
|
|
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;
|
|
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
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
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
|
|
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:
|
|
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;
|
|
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
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
|
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:
|
|
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;
|
|
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
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
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;
|
|
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"}
|