fix(dashboard): promotion decimal value definition (#13371)

* fix(dashboard): pormotion decimal value definition

* fix: typo

* fix: fixed value decimals

* fix: translation
This commit is contained in:
Frane Polić authored and GitHub committed 2025-09-04 16:07:29 +02:00
1 parent 21c31a2ab2
commit b7fef5b7ef
6 files changed
+52 -15

No files matched your search

+5
View File
@@ -0,0 +1,5 @@
---
"@medusajs/dashboard": patch
---
fix(dashboard): promotion decimal value definition
@@ -7997,9 +7997,12 @@
"properties": { "properties": {
"title": { "title": {
"type": "string" "type": "string"
},
"invalid": {
"type": "string"
} }
}, },
"required": ["title"], "required": ["title", "invalid"],
"additionalProperties": false "additionalProperties": false
}, },
"value_type": { "value_type": {
@@ -2151,7 +2151,8 @@
"description": "The code your customers will enter during checkout." "description": "The code your customers will enter during checkout."
}, },
"value": { "value": {
"title": "Promotion Value" "title": "Promotion Value",
"invalid": "Invalid promotion value"
}, },
"value_type": { "value_type": {
"fixed": { "fixed": {
@@ -36,7 +36,10 @@ import {
import { KeyboundForm } from "../../../../../components/utilities/keybound-form" import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCampaigns } from "../../../../../hooks/api/campaigns" import { useCampaigns } from "../../../../../hooks/api/campaigns"
import { useCreatePromotion } from "../../../../../hooks/api/promotions" import { useCreatePromotion } from "../../../../../hooks/api/promotions"
import { getCurrencySymbol } from "../../../../../lib/data/currencies" import {
currencies,
getCurrencySymbol,
} from "../../../../../lib/data/currencies"
import { DEFAULT_CAMPAIGN_VALUES } from "../../../../campaigns/common/constants" import { DEFAULT_CAMPAIGN_VALUES } from "../../../../campaigns/common/constants"
import { RulesFormField } from "../../../common/edit-rules/components/rules-form-field" import { RulesFormField } from "../../../common/edit-rules/components/rules-form-field"
import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form" import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form"
@@ -142,6 +145,7 @@ export const CreatePromotionForm = () => {
application_method: { application_method: {
...applicationMethodData, ...applicationMethodData,
...applicationMethodRuleData, ...applicationMethodRuleData,
value: parseFloat(applicationMethodData.value as string) as number,
target_rules: buildRulesData(targetRulesData), target_rules: buildRulesData(targetRulesData),
buy_rules: buildRulesData(buyRulesData), buy_rules: buildRulesData(buyRulesData),
}, },
@@ -742,6 +746,9 @@ export const CreatePromotionForm = () => {
const currencyCode = const currencyCode =
form.getValues().application_method.currency_code form.getValues().application_method.currency_code
const currencyInfo =
currencies[currencyCode?.toUpperCase() || "USD"]
return ( return (
<Form.Item className="basis-1/2"> <Form.Item className="basis-1/2">
<Form.Label <Form.Label
@@ -759,10 +766,16 @@ export const CreatePromotionForm = () => {
<CurrencyInput <CurrencyInput
{...field} {...field}
min={0} min={0}
onValueChange={(value) => {
onChange(value ? parseInt(value) : "")
}}
code={currencyCode || "USD"} code={currencyCode || "USD"}
onValueChange={(_value, _name, values) =>
onChange(values?.value)
}
decimalScale={
currencyInfo?.decimal_digits ?? 2
}
decimalsLimit={
currencyInfo?.decimal_digits ?? 2
}
symbol={ symbol={
currencyCode currencyCode
? getCurrencySymbol(currencyCode) ? getCurrencySymbol(currencyCode)
@@ -783,7 +796,7 @@ export const CreatePromotionForm = () => {
onChange( onChange(
e.target.value === "" e.target.value === ""
? null ? null
: parseInt(e.target.value) : parseFloat(e.target.value)
) )
}} }}
/> />
@@ -30,7 +30,7 @@ export const CreatePromotionSchema = z
is_tax_inclusive: z.boolean().optional(), is_tax_inclusive: z.boolean().optional(),
application_method: z.object({ application_method: z.object({
allocation: z.enum(["each", "across"]), allocation: z.enum(["each", "across"]),
value: z.number().min(0), value: z.number().min(0).or(z.string().min(1)),
currency_code: z.string().optional(), currency_code: z.string().optional(),
max_quantity: z.number().optional().nullable(), max_quantity: z.number().optional().nullable(),
target_rules: RuleSchema, target_rules: RuleSchema,
@@ -11,7 +11,10 @@ import { DeprecatedPercentageInput } from "../../../../../components/inputs/perc
import { RouteDrawer, useRouteModal } from "../../../../../components/modals" import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form" import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useUpdatePromotion } from "../../../../../hooks/api/promotions" import { useUpdatePromotion } from "../../../../../hooks/api/promotions"
import { getCurrencySymbol } from "../../../../../lib/data/currencies" import {
currencies,
getCurrencySymbol,
} from "../../../../../lib/data/currencies"
import { SwitchBox } from "../../../../../components/common/switch-box" import { SwitchBox } from "../../../../../components/common/switch-box"
type EditPromotionFormProps = { type EditPromotionFormProps = {
@@ -24,7 +27,7 @@ const EditPromotionSchema = zod.object({
is_tax_inclusive: zod.boolean().optional(), is_tax_inclusive: zod.boolean().optional(),
status: zod.enum(["active", "inactive", "draft"]), status: zod.enum(["active", "inactive", "draft"]),
value_type: zod.enum(["fixed", "percentage"]), value_type: zod.enum(["fixed", "percentage"]),
value: zod.number(), value: zod.number().min(0).or(zod.string().min(1)),
allocation: zod.enum(["each", "across"]), allocation: zod.enum(["each", "across"]),
target_type: zod.enum(["order", "shipping_methods", "items"]), target_type: zod.enum(["order", "shipping_methods", "items"]),
}) })
@@ -59,6 +62,13 @@ export const EditPromotionDetailsForm = ({
const { mutateAsync, isPending } = useUpdatePromotion(promotion.id) const { mutateAsync, isPending } = useUpdatePromotion(promotion.id)
const handleSubmit = form.handleSubmit(async (data) => { const handleSubmit = form.handleSubmit(async (data) => {
const value = parseFloat(data.value)
if (isNaN(value) || value < 0) {
form.setError("value", { message: t("promotions.form.value.invalid") })
return
}
await mutateAsync( await mutateAsync(
{ {
is_automatic: data.is_automatic === "true", is_automatic: data.is_automatic === "true",
@@ -66,7 +76,7 @@ export const EditPromotionDetailsForm = ({
status: data.status, status: data.status,
is_tax_inclusive: data.is_tax_inclusive, is_tax_inclusive: data.is_tax_inclusive,
application_method: { application_method: {
value: data.value, value: parseFloat(data.value),
type: data.value_type as any, type: data.value_type as any,
allocation: data.allocation as any, allocation: data.allocation as any,
}, },
@@ -269,6 +279,9 @@ export const EditPromotionDetailsForm = ({
const currencyCode = const currencyCode =
promotion.application_method?.currency_code ?? "USD" promotion.application_method?.currency_code ?? "USD"
const currencyInfo =
currencies[currencyCode?.toUpperCase() || "USD"]
return ( return (
<Form.Item> <Form.Item>
<Form.Label> <Form.Label>
@@ -280,9 +293,11 @@ export const EditPromotionDetailsForm = ({
{isFixedValueType ? ( {isFixedValueType ? (
<CurrencyInput <CurrencyInput
min={0} min={0}
onValueChange={(val) => onValueChange={(val) => onChange(val)}
onChange(val ? parseInt(val) : null) decimalSeparator="."
} groupSeparator=","
decimalScale={currencyInfo.decimal_digits}
decimalsLimit={currencyInfo.decimal_digits}
code={currencyCode} code={currencyCode}
symbol={getCurrencySymbol(currencyCode)} symbol={getCurrencySymbol(currencyCode)}
{...field} {...field}
@@ -299,7 +314,7 @@ export const EditPromotionDetailsForm = ({
onChange( onChange(
e.target.value === "" e.target.value === ""
? null ? null
: parseInt(e.target.value) : parseFloat(e.target.value)
) )
}} }}
/> />