@voyantjs/pricing-ui 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +13 -0
- package/dist/components/cancellation-policy-combobox.d.ts +9 -0
- package/dist/components/cancellation-policy-combobox.d.ts.map +1 -0
- package/dist/components/cancellation-policy-combobox.js +49 -0
- package/dist/components/cancellation-policy-rule-dialog.d.ts +11 -0
- package/dist/components/cancellation-policy-rule-dialog.d.ts.map +1 -0
- package/dist/components/cancellation-policy-rule-dialog.js +87 -0
- package/dist/components/dropoff-price-rule-dialog.d.ts +10 -0
- package/dist/components/dropoff-price-rule-dialog.d.ts.map +1 -0
- package/dist/components/dropoff-price-rule-dialog.js +96 -0
- package/dist/components/extra-price-rule-dialog.d.ts +10 -0
- package/dist/components/extra-price-rule-dialog.d.ts.map +1 -0
- package/dist/components/extra-price-rule-dialog.js +92 -0
- package/dist/components/option-price-rule-combobox.d.ts +9 -0
- package/dist/components/option-price-rule-combobox.d.ts.map +1 -0
- package/dist/components/option-price-rule-combobox.js +45 -0
- package/dist/components/option-price-rule-dialog.d.ts +9 -0
- package/dist/components/option-price-rule-dialog.d.ts.map +1 -0
- package/dist/components/option-price-rule-dialog.js +132 -0
- package/dist/components/option-start-time-rule-dialog.d.ts +10 -0
- package/dist/components/option-start-time-rule-dialog.d.ts.map +1 -0
- package/dist/components/option-start-time-rule-dialog.js +90 -0
- package/dist/components/option-unit-price-rule-dialog.d.ts +10 -0
- package/dist/components/option-unit-price-rule-dialog.d.ts.map +1 -0
- package/dist/components/option-unit-price-rule-dialog.js +103 -0
- package/dist/components/option-unit-tier-dialog.d.ts +10 -0
- package/dist/components/option-unit-tier-dialog.d.ts.map +1 -0
- package/dist/components/option-unit-tier-dialog.js +78 -0
- package/dist/components/pickup-price-rule-dialog.d.ts +10 -0
- package/dist/components/pickup-price-rule-dialog.d.ts.map +1 -0
- package/dist/components/pickup-price-rule-dialog.js +88 -0
- package/dist/components/price-catalog-combobox.d.ts +9 -0
- package/dist/components/price-catalog-combobox.d.ts.map +1 -0
- package/dist/components/price-catalog-combobox.js +45 -0
- package/dist/components/price-schedule-combobox.d.ts +10 -0
- package/dist/components/price-schedule-combobox.d.ts.map +1 -0
- package/dist/components/price-schedule-combobox.js +48 -0
- package/dist/components/price-schedule-dialog.d.ts +9 -0
- package/dist/components/price-schedule-dialog.d.ts.map +1 -0
- package/dist/components/price-schedule-dialog.js +87 -0
- package/dist/components/pricing-category-combobox.d.ts +9 -0
- package/dist/components/pricing-category-combobox.d.ts.map +1 -0
- package/dist/components/pricing-category-combobox.js +54 -0
- package/dist/components/pricing-category-dependency-dialog.d.ts +9 -0
- package/dist/components/pricing-category-dependency-dialog.d.ts.map +1 -0
- package/dist/components/pricing-category-dependency-dialog.js +11 -0
- package/dist/components/pricing-category-dependency-form.d.ts +15 -0
- package/dist/components/pricing-category-dependency-form.d.ts.map +1 -0
- package/dist/components/pricing-category-dependency-form.js +90 -0
- package/dist/components/pricing-category-dialog.d.ts +9 -0
- package/dist/components/pricing-category-dialog.d.ts.map +1 -0
- package/dist/components/pricing-category-dialog.js +13 -0
- package/dist/components/pricing-category-form.d.ts +15 -0
- package/dist/components/pricing-category-form.d.ts.map +1 -0
- package/dist/components/pricing-category-form.js +99 -0
- package/dist/components/pricing-category-list.d.ts +5 -0
- package/dist/components/pricing-category-list.d.ts.map +1 -0
- package/dist/components/pricing-category-list.js +44 -0
- package/dist/components/pricing-shared-labels.d.ts +22 -0
- package/dist/components/pricing-shared-labels.d.ts.map +1 -0
- package/dist/components/pricing-shared-labels.js +32 -0
- package/dist/components/product-combobox.d.ts +9 -0
- package/dist/components/product-combobox.d.ts.map +1 -0
- package/dist/components/product-combobox.js +41 -0
- package/dist/components/product-option-combobox.d.ts +10 -0
- package/dist/components/product-option-combobox.d.ts.map +1 -0
- package/dist/components/product-option-combobox.js +51 -0
- package/dist/index.d.ts +23 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +22 -0
- package/package.json +70 -0
package/README.md
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
# @voyantjs/pricing-ui
|
|
2
|
+
|
|
3
|
+
Importable React UI components for Voyant pricing. Bundler-consumed (Vite, Next.js, webpack, etc.).
|
|
4
|
+
|
|
5
|
+
## Install
|
|
6
|
+
|
|
7
|
+
```bash
|
|
8
|
+
pnpm add @voyantjs/pricing-ui @voyantjs/pricing-react @voyantjs/voyant-ui @tanstack/react-query react react-dom
|
|
9
|
+
```
|
|
10
|
+
|
|
11
|
+
`@voyantjs/voyant-ui` provides the design-system primitives. `@voyantjs/pricing-react` provides the data-layer hooks. Both are required peers.
|
|
12
|
+
|
|
13
|
+
All components accept a `className` prop and merge it with `cn()`. Wrap or compose to extend; use the registry copy-paste path (`npx shadcn add @voyant/...`) for components you want to fork outright.
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
value: string | null | undefined;
|
|
3
|
+
onChange: (value: string | null) => void;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function CancellationPolicyCombobox({ value, onChange, placeholder, disabled, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=cancellation-policy-combobox.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancellation-policy-combobox.d.ts","sourceRoot":"","sources":["../../src/components/cancellation-policy-combobox.tsx"],"names":[],"mappings":"AAgBA,KAAK,KAAK,GAAG;IACX,KAAK,EAAE,MAAM,GAAG,IAAI,GAAG,SAAS,CAAA;IAChC,QAAQ,EAAE,CAAC,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAA;IACxC,WAAW,CAAC,EAAE,MAAM,CAAA;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAA;CACnB,CAAA;AAID,wBAAgB,0BAA0B,CAAC,EACzC,KAAK,EACL,QAAQ,EACR,WAAiD,EACjD,QAAQ,GACT,EAAE,KAAK,2CA0EP"}
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useCancellationPolicies, useCancellationPolicy, } from "@voyantjs/pricing-react";
|
|
3
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/voyant-ui/components/combobox";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
const PAGE_SIZE = 25;
|
|
6
|
+
export function CancellationPolicyCombobox({ value, onChange, placeholder = "Search cancellation fee policies…", disabled, }) {
|
|
7
|
+
const [search, setSearch] = React.useState("");
|
|
8
|
+
const listQuery = useCancellationPolicies({ search: search || undefined, limit: PAGE_SIZE });
|
|
9
|
+
const selectedQuery = useCancellationPolicy(value, { enabled: !!value });
|
|
10
|
+
const items = React.useMemo(() => {
|
|
11
|
+
const map = new Map();
|
|
12
|
+
for (const item of listQuery.data?.data ?? [])
|
|
13
|
+
map.set(item.id, item);
|
|
14
|
+
if (selectedQuery.data)
|
|
15
|
+
map.set(selectedQuery.data.id, selectedQuery.data);
|
|
16
|
+
return Array.from(map.values());
|
|
17
|
+
}, [listQuery.data?.data, selectedQuery.data]);
|
|
18
|
+
const itemMap = React.useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);
|
|
19
|
+
const selected = value ? itemMap.get(value) : undefined;
|
|
20
|
+
const selectedLabel = selected
|
|
21
|
+
? `${selected.name}${selected.code ? ` · ${selected.code}` : ""}`
|
|
22
|
+
: "";
|
|
23
|
+
const [inputValue, setInputValue] = React.useState(selectedLabel);
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
if (selectedLabel)
|
|
26
|
+
setInputValue(selectedLabel);
|
|
27
|
+
}, [selectedLabel]);
|
|
28
|
+
return (_jsxs(Combobox, { items: items.map((item) => item.id), value: value ?? null, inputValue: inputValue, autoHighlight: true, disabled: disabled, itemToStringValue: (id) => {
|
|
29
|
+
const item = itemMap.get(id);
|
|
30
|
+
return item ? `${item.name}${item.code ? ` · ${item.code}` : ""}` : "";
|
|
31
|
+
}, onInputValueChange: (next) => {
|
|
32
|
+
setInputValue(next);
|
|
33
|
+
setSearch(next);
|
|
34
|
+
if (!next)
|
|
35
|
+
onChange(null);
|
|
36
|
+
}, onValueChange: (next) => {
|
|
37
|
+
const id = next ?? null;
|
|
38
|
+
onChange(id);
|
|
39
|
+
const item = id ? itemMap.get(id) : null;
|
|
40
|
+
setInputValue(item ? `${item.name}${item.code ? ` · ${item.code}` : ""}` : "");
|
|
41
|
+
}, children: [_jsx(ComboboxInput, { placeholder: placeholder, showClear: !!value }), _jsxs(ComboboxContent, { children: [_jsx(ComboboxEmpty, { children: listQuery.isPending || selectedQuery.isPending
|
|
42
|
+
? "Loading…"
|
|
43
|
+
: "No cancellation fee policies found." }), _jsx(ComboboxList, { children: _jsx(ComboboxCollection, { children: (id) => {
|
|
44
|
+
const item = itemMap.get(id);
|
|
45
|
+
if (!item)
|
|
46
|
+
return null;
|
|
47
|
+
return (_jsx(ComboboxItem, { value: item.id, children: _jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate font-medium", children: item.name }), item.code ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: item.code })) : null] }) }, item.id));
|
|
48
|
+
} }) })] })] }));
|
|
49
|
+
}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { type CancellationPolicyRuleRecord } from "@voyantjs/pricing-react";
|
|
2
|
+
export interface CancellationPolicyRuleDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
policyId: string;
|
|
6
|
+
rule?: CancellationPolicyRuleRecord;
|
|
7
|
+
nextSortOrder?: number;
|
|
8
|
+
onSuccess?: (rule: CancellationPolicyRuleRecord) => void;
|
|
9
|
+
}
|
|
10
|
+
export declare function CancellationPolicyRuleDialog({ open, onOpenChange, policyId, rule, nextSortOrder, onSuccess, }: CancellationPolicyRuleDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
11
|
+
//# sourceMappingURL=cancellation-policy-rule-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"cancellation-policy-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/cancellation-policy-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EACL,KAAK,4BAA4B,EAElC,MAAM,yBAAyB,CAAA;AAsChC,MAAM,WAAW,iCAAiC;IAChD,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,QAAQ,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,4BAA4B,CAAA;IACnC,aAAa,CAAC,EAAE,MAAM,CAAA;IACtB,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,4BAA4B,KAAK,IAAI,CAAA;CACzD;AAQD,wBAAgB,4BAA4B,CAAC,EAC3C,IAAI,EACJ,YAAY,EACZ,QAAQ,EACR,IAAI,EACJ,aAAa,EACb,SAAS,GACV,EAAE,iCAAiC,2CAuKnC"}
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useCancellationPolicyRuleMutation, } from "@voyantjs/pricing-react";
|
|
4
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, } from "@voyantjs/voyant-ui/components";
|
|
5
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import { useEffect } from "react";
|
|
8
|
+
import { useForm } from "react-hook-form";
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
|
+
const ruleFormSchema = z.object({
|
|
11
|
+
sortOrder: z.coerce.number().int(),
|
|
12
|
+
cutoffMinutesBefore: z.coerce.number().int().min(0).optional().or(z.literal("")).nullable(),
|
|
13
|
+
chargeType: z.enum(["none", "amount", "percentage"]),
|
|
14
|
+
chargeAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
|
|
15
|
+
chargePercent: z.coerce.number().min(0).max(100).optional().or(z.literal("")).nullable(),
|
|
16
|
+
active: z.boolean(),
|
|
17
|
+
notes: z.string().optional().nullable(),
|
|
18
|
+
});
|
|
19
|
+
const CHARGE_TYPES = [
|
|
20
|
+
{ value: "none", label: "None" },
|
|
21
|
+
{ value: "amount", label: "Amount" },
|
|
22
|
+
{ value: "percentage", label: "Percentage" },
|
|
23
|
+
];
|
|
24
|
+
export function CancellationPolicyRuleDialog({ open, onOpenChange, policyId, rule, nextSortOrder, onSuccess, }) {
|
|
25
|
+
const isEditing = !!rule;
|
|
26
|
+
const { create, update } = useCancellationPolicyRuleMutation();
|
|
27
|
+
const form = useForm({
|
|
28
|
+
resolver: zodResolver(ruleFormSchema),
|
|
29
|
+
defaultValues: {
|
|
30
|
+
sortOrder: 0,
|
|
31
|
+
cutoffMinutesBefore: "",
|
|
32
|
+
chargeType: "percentage",
|
|
33
|
+
chargeAmount: "",
|
|
34
|
+
chargePercent: "",
|
|
35
|
+
active: true,
|
|
36
|
+
notes: "",
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
useEffect(() => {
|
|
40
|
+
if (open && rule) {
|
|
41
|
+
form.reset({
|
|
42
|
+
sortOrder: rule.sortOrder,
|
|
43
|
+
cutoffMinutesBefore: rule.cutoffMinutesBefore ?? "",
|
|
44
|
+
chargeType: rule.chargeType,
|
|
45
|
+
chargeAmount: rule.chargeAmountCents != null ? rule.chargeAmountCents / 100 : "",
|
|
46
|
+
chargePercent: rule.chargePercentBasisPoints != null ? rule.chargePercentBasisPoints / 100 : "",
|
|
47
|
+
active: rule.active,
|
|
48
|
+
notes: rule.notes ?? "",
|
|
49
|
+
});
|
|
50
|
+
}
|
|
51
|
+
else if (open) {
|
|
52
|
+
form.reset({
|
|
53
|
+
sortOrder: nextSortOrder ?? 0,
|
|
54
|
+
cutoffMinutesBefore: "",
|
|
55
|
+
chargeType: "percentage",
|
|
56
|
+
chargeAmount: "",
|
|
57
|
+
chargePercent: "",
|
|
58
|
+
active: true,
|
|
59
|
+
notes: "",
|
|
60
|
+
});
|
|
61
|
+
}
|
|
62
|
+
}, [open, rule, nextSortOrder, form]);
|
|
63
|
+
const chargeType = form.watch("chargeType");
|
|
64
|
+
const onSubmit = async (values) => {
|
|
65
|
+
const payload = {
|
|
66
|
+
cancellationPolicyId: policyId,
|
|
67
|
+
sortOrder: values.sortOrder,
|
|
68
|
+
cutoffMinutesBefore: typeof values.cutoffMinutesBefore === "number" ? values.cutoffMinutesBefore : null,
|
|
69
|
+
chargeType: values.chargeType,
|
|
70
|
+
chargeAmountCents: values.chargeType === "amount" && typeof values.chargeAmount === "number"
|
|
71
|
+
? Math.round(values.chargeAmount * 100)
|
|
72
|
+
: null,
|
|
73
|
+
chargePercentBasisPoints: values.chargeType === "percentage" && typeof values.chargePercent === "number"
|
|
74
|
+
? Math.round(values.chargePercent * 100)
|
|
75
|
+
: null,
|
|
76
|
+
active: values.active,
|
|
77
|
+
notes: values.notes || null,
|
|
78
|
+
};
|
|
79
|
+
const saved = isEditing
|
|
80
|
+
? await update.mutateAsync({ id: rule.id, input: payload })
|
|
81
|
+
: await create.mutateAsync(payload);
|
|
82
|
+
onSuccess?.(saved);
|
|
83
|
+
onOpenChange(false);
|
|
84
|
+
};
|
|
85
|
+
const isSubmitting = create.isPending || update.isPending;
|
|
86
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Rule" : "Add 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: "Cutoff (minutes before)" }), _jsx(Input, { ...form.register("cutoffMinutesBefore"), type: "number", min: "0", placeholder: "2880" }), _jsx("p", { className: "text-xs text-muted-foreground", children: "48h = 2880m \u00B7 24h = 1440m" })] }), _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 flex-col gap-2", children: [_jsx(Label, { children: "Charge type" }), _jsxs(Select, { items: CHARGE_TYPES, value: form.watch("chargeType"), onValueChange: (value) => form.setValue("chargeType", value), children: [_jsx(SelectTrigger, { className: "w-full", children: _jsx(SelectValue, {}) }), _jsx(SelectContent, { children: CHARGE_TYPES.map((type) => (_jsx(SelectItem, { value: type.value, children: type.label }, type.value))) })] })] }), chargeType === "amount" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Charge amount" }), _jsx(Input, { ...form.register("chargeAmount"), type: "number", step: "0.01", min: "0" })] })) : null, chargeType === "percentage" ? (_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Charge percent (0-100)" }), _jsx(Input, { ...form.register("chargePercent"), type: "number", step: "0.01", min: "0", max: "100", placeholder: "50" })] })) : null, _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") })] })] }), _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"] })] })] })] }) }));
|
|
87
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type DropoffPriceRuleRecord } from "@voyantjs/pricing-react";
|
|
2
|
+
type Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
rule?: DropoffPriceRuleRecord;
|
|
6
|
+
onSuccess?: (rule: DropoffPriceRuleRecord) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function DropoffPriceRuleDialog({ open, onOpenChange, rule, onSuccess }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=dropoff-price-rule-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"dropoff-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/dropoff-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,sBAAsB,EAA+B,MAAM,yBAAyB,CAAA;AAqDlG,KAAK,KAAK,GAAG;IACX,IAAI,EAAE,OAAO,CAAA;IACb,YAAY,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,IAAI,CAAA;IACrC,IAAI,CAAC,EAAE,sBAAsB,CAAA;IAC7B,SAAS,CAAC,EAAE,CAAC,IAAI,EAAE,sBAAsB,KAAK,IAAI,CAAA;CACnD,CAAA;AAKD,wBAAgB,sBAAsB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAoLpF"}
|
|
@@ -0,0 +1,96 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useDropoffPriceRuleMutation } from "@voyantjs/pricing-react";
|
|
4
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, } from "@voyantjs/voyant-ui/components";
|
|
5
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import { useEffect } from "react";
|
|
8
|
+
import { useForm } from "react-hook-form";
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
|
+
import { OptionPriceRuleCombobox } from "./option-price-rule-combobox";
|
|
11
|
+
const ADDON_PRICING_MODES = [
|
|
12
|
+
"included",
|
|
13
|
+
"per_person",
|
|
14
|
+
"per_booking",
|
|
15
|
+
"on_request",
|
|
16
|
+
"unavailable",
|
|
17
|
+
];
|
|
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
|
+
facilityId: z.string().optional().nullable(),
|
|
22
|
+
dropoffCode: z.string().max(100).optional().nullable(),
|
|
23
|
+
dropoffName: z.string().min(1, "Dropoff name is required").max(255),
|
|
24
|
+
pricingMode: z.enum(ADDON_PRICING_MODES),
|
|
25
|
+
sellAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
|
|
26
|
+
costAmount: z.coerce.number().min(0).optional().or(z.literal("")).nullable(),
|
|
27
|
+
active: z.boolean(),
|
|
28
|
+
sortOrder: z.coerce.number().int(),
|
|
29
|
+
notes: z.string().optional().nullable(),
|
|
30
|
+
});
|
|
31
|
+
const toCents = (value) => typeof value === "number" ? Math.round(value * 100) : null;
|
|
32
|
+
export function DropoffPriceRuleDialog({ open, onOpenChange, rule, onSuccess }) {
|
|
33
|
+
const isEditing = !!rule;
|
|
34
|
+
const { create, update } = useDropoffPriceRuleMutation();
|
|
35
|
+
const form = useForm({
|
|
36
|
+
resolver: zodResolver(formSchema),
|
|
37
|
+
defaultValues: {
|
|
38
|
+
optionPriceRuleId: "",
|
|
39
|
+
optionId: "",
|
|
40
|
+
facilityId: "",
|
|
41
|
+
dropoffCode: "",
|
|
42
|
+
dropoffName: "",
|
|
43
|
+
pricingMode: "included",
|
|
44
|
+
sellAmount: "",
|
|
45
|
+
costAmount: "",
|
|
46
|
+
active: true,
|
|
47
|
+
sortOrder: 0,
|
|
48
|
+
notes: "",
|
|
49
|
+
},
|
|
50
|
+
});
|
|
51
|
+
useEffect(() => {
|
|
52
|
+
if (open && rule) {
|
|
53
|
+
form.reset({
|
|
54
|
+
optionPriceRuleId: rule.optionPriceRuleId,
|
|
55
|
+
optionId: rule.optionId,
|
|
56
|
+
facilityId: rule.facilityId ?? "",
|
|
57
|
+
dropoffCode: rule.dropoffCode ?? "",
|
|
58
|
+
dropoffName: rule.dropoffName,
|
|
59
|
+
pricingMode: rule.pricingMode,
|
|
60
|
+
sellAmount: rule.sellAmountCents != null ? rule.sellAmountCents / 100 : "",
|
|
61
|
+
costAmount: rule.costAmountCents != null ? rule.costAmountCents / 100 : "",
|
|
62
|
+
active: rule.active,
|
|
63
|
+
sortOrder: rule.sortOrder,
|
|
64
|
+
notes: rule.notes ?? "",
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
else if (open) {
|
|
68
|
+
form.reset();
|
|
69
|
+
}
|
|
70
|
+
}, [form, open, rule]);
|
|
71
|
+
const onSubmit = async (values) => {
|
|
72
|
+
const payload = {
|
|
73
|
+
optionPriceRuleId: values.optionPriceRuleId,
|
|
74
|
+
optionId: values.optionId,
|
|
75
|
+
facilityId: values.facilityId || null,
|
|
76
|
+
dropoffCode: values.dropoffCode || null,
|
|
77
|
+
dropoffName: values.dropoffName,
|
|
78
|
+
pricingMode: values.pricingMode,
|
|
79
|
+
sellAmountCents: toCents(values.sellAmount),
|
|
80
|
+
costAmountCents: toCents(values.costAmount),
|
|
81
|
+
active: values.active,
|
|
82
|
+
sortOrder: values.sortOrder,
|
|
83
|
+
notes: values.notes || null,
|
|
84
|
+
};
|
|
85
|
+
const saved = isEditing
|
|
86
|
+
? await update.mutateAsync({ id: rule.id, input: payload })
|
|
87
|
+
: await create.mutateAsync(payload);
|
|
88
|
+
onSuccess?.(saved);
|
|
89
|
+
onOpenChange(false);
|
|
90
|
+
};
|
|
91
|
+
const isSubmitting = create.isPending || update.isPending;
|
|
92
|
+
return (_jsx(Dialog, { open: open, onOpenChange: onOpenChange, children: _jsxs(DialogContent, { size: "lg", children: [_jsx(DialogHeader, { children: _jsx(DialogTitle, { children: isEditing ? "Edit Dropoff Price Rule" : "Add Dropoff 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 ?? "", {
|
|
93
|
+
shouldDirty: true,
|
|
94
|
+
shouldValidate: true,
|
|
95
|
+
}), 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: "Facility ID (optional)" }), _jsx(Input, { ...form.register("facilityId"), placeholder: "fac_\u2026" })] })] }), _jsxs("div", { className: "grid grid-cols-2 gap-4", children: [_jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Dropoff name" }), _jsx(Input, { ...form.register("dropoffName") })] }), _jsxs("div", { className: "flex flex-col gap-2", children: [_jsx(Label, { children: "Dropoff code (optional)" }), _jsx(Input, { ...form.register("dropoffCode") })] })] }), _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"] })] })] })] }) }));
|
|
96
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type ExtraPriceRuleRecord } from "@voyantjs/pricing-react";
|
|
2
|
+
type Props = {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
rule?: ExtraPriceRuleRecord;
|
|
6
|
+
onSuccess?: (rule: ExtraPriceRuleRecord) => void;
|
|
7
|
+
};
|
|
8
|
+
export declare function ExtraPriceRuleDialog({ open, onOpenChange, rule, onSuccess }: Props): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
export {};
|
|
10
|
+
//# sourceMappingURL=extra-price-rule-dialog.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"extra-price-rule-dialog.d.ts","sourceRoot":"","sources":["../../src/components/extra-price-rule-dialog.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,oBAAoB,EAA6B,MAAM,yBAAyB,CAAA;AAoD9F,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;AAKD,wBAAgB,oBAAoB,CAAC,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,SAAS,EAAE,EAAE,KAAK,2CAyKlF"}
|
|
@@ -0,0 +1,92 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
3
|
+
import { useExtraPriceRuleMutation } from "@voyantjs/pricing-react";
|
|
4
|
+
import { Button, Dialog, DialogBody, DialogContent, DialogFooter, DialogHeader, DialogTitle, Input, Label, Select, SelectContent, SelectItem, SelectTrigger, SelectValue, Switch, Textarea, } from "@voyantjs/voyant-ui/components";
|
|
5
|
+
import { zodResolver } from "@voyantjs/voyant-ui/lib/zod-resolver";
|
|
6
|
+
import { Loader2 } from "lucide-react";
|
|
7
|
+
import { useEffect } from "react";
|
|
8
|
+
import { useForm } from "react-hook-form";
|
|
9
|
+
import { z } from "zod/v4";
|
|
10
|
+
import { OptionPriceRuleCombobox } from "./option-price-rule-combobox";
|
|
11
|
+
const ADDON_PRICING_MODES = [
|
|
12
|
+
"included",
|
|
13
|
+
"per_person",
|
|
14
|
+
"per_booking",
|
|
15
|
+
"on_request",
|
|
16
|
+
"unavailable",
|
|
17
|
+
];
|
|
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
|
+
});
|
|
30
|
+
const toCents = (value) => typeof value === "number" ? Math.round(value * 100) : null;
|
|
31
|
+
export function ExtraPriceRuleDialog({ open, onOpenChange, rule, onSuccess }) {
|
|
32
|
+
const isEditing = !!rule;
|
|
33
|
+
const { create, update } = useExtraPriceRuleMutation();
|
|
34
|
+
const form = useForm({
|
|
35
|
+
resolver: zodResolver(formSchema),
|
|
36
|
+
defaultValues: {
|
|
37
|
+
optionPriceRuleId: "",
|
|
38
|
+
optionId: "",
|
|
39
|
+
productExtraId: "",
|
|
40
|
+
optionExtraConfigId: "",
|
|
41
|
+
pricingMode: "included",
|
|
42
|
+
sellAmount: "",
|
|
43
|
+
costAmount: "",
|
|
44
|
+
active: true,
|
|
45
|
+
sortOrder: 0,
|
|
46
|
+
notes: "",
|
|
47
|
+
},
|
|
48
|
+
});
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (open && rule) {
|
|
51
|
+
form.reset({
|
|
52
|
+
optionPriceRuleId: rule.optionPriceRuleId,
|
|
53
|
+
optionId: rule.optionId,
|
|
54
|
+
productExtraId: rule.productExtraId ?? "",
|
|
55
|
+
optionExtraConfigId: rule.optionExtraConfigId ?? "",
|
|
56
|
+
pricingMode: rule.pricingMode,
|
|
57
|
+
sellAmount: rule.sellAmountCents != null ? rule.sellAmountCents / 100 : "",
|
|
58
|
+
costAmount: rule.costAmountCents != null ? rule.costAmountCents / 100 : "",
|
|
59
|
+
active: rule.active,
|
|
60
|
+
sortOrder: rule.sortOrder,
|
|
61
|
+
notes: rule.notes ?? "",
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
else if (open) {
|
|
65
|
+
form.reset();
|
|
66
|
+
}
|
|
67
|
+
}, [form, open, rule]);
|
|
68
|
+
const onSubmit = async (values) => {
|
|
69
|
+
const payload = {
|
|
70
|
+
optionPriceRuleId: values.optionPriceRuleId,
|
|
71
|
+
optionId: values.optionId,
|
|
72
|
+
productExtraId: values.productExtraId || null,
|
|
73
|
+
optionExtraConfigId: values.optionExtraConfigId || null,
|
|
74
|
+
pricingMode: values.pricingMode,
|
|
75
|
+
sellAmountCents: toCents(values.sellAmount),
|
|
76
|
+
costAmountCents: toCents(values.costAmount),
|
|
77
|
+
active: values.active,
|
|
78
|
+
sortOrder: values.sortOrder,
|
|
79
|
+
notes: values.notes || null,
|
|
80
|
+
};
|
|
81
|
+
const saved = isEditing
|
|
82
|
+
? await update.mutateAsync({ id: rule.id, input: payload })
|
|
83
|
+
: await create.mutateAsync(payload);
|
|
84
|
+
onSuccess?.(saved);
|
|
85
|
+
onOpenChange(false);
|
|
86
|
+
};
|
|
87
|
+
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 ?? "", {
|
|
89
|
+
shouldDirty: true,
|
|
90
|
+
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"] })] })] })] }) }));
|
|
92
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
type Props = {
|
|
2
|
+
value: string | null | undefined;
|
|
3
|
+
onChange: (value: string | null) => void;
|
|
4
|
+
placeholder?: string;
|
|
5
|
+
disabled?: boolean;
|
|
6
|
+
};
|
|
7
|
+
export declare function OptionPriceRuleCombobox({ value, onChange, placeholder, disabled, }: Props): import("react/jsx-runtime").JSX.Element;
|
|
8
|
+
export {};
|
|
9
|
+
//# sourceMappingURL=option-price-rule-combobox.d.ts.map
|
|
@@ -0,0 +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"}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
import { useOptionPriceRule, useOptionPriceRules, } from "@voyantjs/pricing-react";
|
|
3
|
+
import { Combobox, ComboboxCollection, ComboboxContent, ComboboxEmpty, ComboboxInput, ComboboxItem, ComboboxList, } from "@voyantjs/voyant-ui/components/combobox";
|
|
4
|
+
import * as React from "react";
|
|
5
|
+
const PAGE_SIZE = 25;
|
|
6
|
+
export function OptionPriceRuleCombobox({ value, onChange, placeholder = "Search option price rules…", disabled, }) {
|
|
7
|
+
const [search, setSearch] = React.useState("");
|
|
8
|
+
const listQuery = useOptionPriceRules({ limit: PAGE_SIZE });
|
|
9
|
+
const selectedQuery = useOptionPriceRule(value, { enabled: !!value });
|
|
10
|
+
const items = React.useMemo(() => {
|
|
11
|
+
const map = new Map();
|
|
12
|
+
for (const item of listQuery.data?.data ?? []) {
|
|
13
|
+
if (!search || item.name.toLowerCase().includes(search.toLowerCase()))
|
|
14
|
+
map.set(item.id, item);
|
|
15
|
+
}
|
|
16
|
+
if (selectedQuery.data)
|
|
17
|
+
map.set(selectedQuery.data.id, selectedQuery.data);
|
|
18
|
+
return Array.from(map.values());
|
|
19
|
+
}, [listQuery.data?.data, search, selectedQuery.data]);
|
|
20
|
+
const itemMap = React.useMemo(() => new Map(items.map((item) => [item.id, item])), [items]);
|
|
21
|
+
const selected = value ? itemMap.get(value) : undefined;
|
|
22
|
+
const selectedLabel = selected ? selected.name : "";
|
|
23
|
+
const [inputValue, setInputValue] = React.useState(selectedLabel);
|
|
24
|
+
React.useEffect(() => {
|
|
25
|
+
if (selectedLabel)
|
|
26
|
+
setInputValue(selectedLabel);
|
|
27
|
+
}, [selectedLabel]);
|
|
28
|
+
return (_jsxs(Combobox, { items: items.map((item) => item.id), value: value ?? null, inputValue: inputValue, autoHighlight: true, disabled: disabled, itemToStringValue: (id) => itemMap.get(id)?.name ?? "", onInputValueChange: (next) => {
|
|
29
|
+
setInputValue(next);
|
|
30
|
+
setSearch(next);
|
|
31
|
+
if (!next)
|
|
32
|
+
onChange(null);
|
|
33
|
+
}, onValueChange: (next) => {
|
|
34
|
+
const id = next ?? null;
|
|
35
|
+
onChange(id);
|
|
36
|
+
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) => {
|
|
40
|
+
const item = itemMap.get(id);
|
|
41
|
+
if (!item)
|
|
42
|
+
return null;
|
|
43
|
+
return (_jsx(ComboboxItem, { value: item.id, children: _jsxs("div", { className: "flex min-w-0 flex-col", children: [_jsx("span", { className: "truncate font-medium", children: item.name }), item.code ? (_jsx("span", { className: "truncate text-xs text-muted-foreground", children: item.code })) : null] }) }, item.id));
|
|
44
|
+
} }) })] })] }));
|
|
45
|
+
}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type OptionPriceRuleRecord } from "@voyantjs/pricing-react";
|
|
2
|
+
export interface OptionPriceRuleDialogProps {
|
|
3
|
+
open: boolean;
|
|
4
|
+
onOpenChange: (open: boolean) => void;
|
|
5
|
+
rule?: OptionPriceRuleRecord;
|
|
6
|
+
onSuccess?: (rule: OptionPriceRuleRecord) => void;
|
|
7
|
+
}
|
|
8
|
+
export declare function OptionPriceRuleDialog({ open, onOpenChange, rule, onSuccess, }: OptionPriceRuleDialogProps): import("react/jsx-runtime").JSX.Element;
|
|
9
|
+
//# sourceMappingURL=option-price-rule-dialog.d.ts.map
|
|
@@ -0,0 +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"}
|