diff --git a/.changeset/new-cooks-count.md b/.changeset/new-cooks-count.md
new file mode 100644
index 0000000000..a6c798313d
--- /dev/null
+++ b/.changeset/new-cooks-count.md
@@ -0,0 +1,5 @@
+---
+"@medusajs/dashboard": patch
+---
+
+fix(dashboard): promotion decimal value definition
diff --git a/packages/admin/dashboard/src/i18n/translations/$schema.json b/packages/admin/dashboard/src/i18n/translations/$schema.json
index 33b50f67bf..643fb44273 100644
--- a/packages/admin/dashboard/src/i18n/translations/$schema.json
+++ b/packages/admin/dashboard/src/i18n/translations/$schema.json
@@ -7997,9 +7997,12 @@
"properties": {
"title": {
"type": "string"
+ },
+ "invalid": {
+ "type": "string"
}
},
- "required": ["title"],
+ "required": ["title", "invalid"],
"additionalProperties": false
},
"value_type": {
diff --git a/packages/admin/dashboard/src/i18n/translations/en.json b/packages/admin/dashboard/src/i18n/translations/en.json
index 3b936b121c..4a2ea633ee 100644
--- a/packages/admin/dashboard/src/i18n/translations/en.json
+++ b/packages/admin/dashboard/src/i18n/translations/en.json
@@ -2151,7 +2151,8 @@
"description": "The code your customers will enter during checkout."
},
"value": {
- "title": "Promotion Value"
+ "title": "Promotion Value",
+ "invalid": "Invalid promotion value"
},
"value_type": {
"fixed": {
diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx b/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx
index 7a430990fd..b76b773240 100644
--- a/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx
+++ b/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/create-promotion-form.tsx
@@ -36,7 +36,10 @@ import {
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
import { useCampaigns } from "../../../../../hooks/api/campaigns"
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 { RulesFormField } from "../../../common/edit-rules/components/rules-form-field"
import { AddCampaignPromotionFields } from "../../../promotion-add-campaign/components/add-campaign-promotion-form"
@@ -142,6 +145,7 @@ export const CreatePromotionForm = () => {
application_method: {
...applicationMethodData,
...applicationMethodRuleData,
+ value: parseFloat(applicationMethodData.value as string) as number,
target_rules: buildRulesData(targetRulesData),
buy_rules: buildRulesData(buyRulesData),
},
@@ -742,6 +746,9 @@ export const CreatePromotionForm = () => {
const currencyCode =
form.getValues().application_method.currency_code
+ const currencyInfo =
+ currencies[currencyCode?.toUpperCase() || "USD"]
+
return (
{
{
- onChange(value ? parseInt(value) : "")
- }}
code={currencyCode || "USD"}
+ onValueChange={(_value, _name, values) =>
+ onChange(values?.value)
+ }
+ decimalScale={
+ currencyInfo?.decimal_digits ?? 2
+ }
+ decimalsLimit={
+ currencyInfo?.decimal_digits ?? 2
+ }
symbol={
currencyCode
? getCurrencySymbol(currencyCode)
@@ -783,7 +796,7 @@ export const CreatePromotionForm = () => {
onChange(
e.target.value === ""
? null
- : parseInt(e.target.value)
+ : parseFloat(e.target.value)
)
}}
/>
diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/form-schema.ts b/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/form-schema.ts
index 4cc69f24e9..9dbb81bc11 100644
--- a/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/form-schema.ts
+++ b/packages/admin/dashboard/src/routes/promotions/promotion-create/components/create-promotion-form/form-schema.ts
@@ -30,7 +30,7 @@ export const CreatePromotionSchema = z
is_tax_inclusive: z.boolean().optional(),
application_method: z.object({
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(),
max_quantity: z.number().optional().nullable(),
target_rules: RuleSchema,
diff --git a/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx b/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx
index 96294aeadf..f9a570679e 100644
--- a/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx
+++ b/packages/admin/dashboard/src/routes/promotions/promotion-edit-details/components/edit-promotion-form/edit-promotion-details-form.tsx
@@ -11,7 +11,10 @@ import { DeprecatedPercentageInput } from "../../../../../components/inputs/perc
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
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"
type EditPromotionFormProps = {
@@ -24,7 +27,7 @@ const EditPromotionSchema = zod.object({
is_tax_inclusive: zod.boolean().optional(),
status: zod.enum(["active", "inactive", "draft"]),
value_type: zod.enum(["fixed", "percentage"]),
- value: zod.number(),
+ value: zod.number().min(0).or(zod.string().min(1)),
allocation: zod.enum(["each", "across"]),
target_type: zod.enum(["order", "shipping_methods", "items"]),
})
@@ -59,6 +62,13 @@ export const EditPromotionDetailsForm = ({
const { mutateAsync, isPending } = useUpdatePromotion(promotion.id)
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(
{
is_automatic: data.is_automatic === "true",
@@ -66,7 +76,7 @@ export const EditPromotionDetailsForm = ({
status: data.status,
is_tax_inclusive: data.is_tax_inclusive,
application_method: {
- value: data.value,
+ value: parseFloat(data.value),
type: data.value_type as any,
allocation: data.allocation as any,
},
@@ -269,6 +279,9 @@ export const EditPromotionDetailsForm = ({
const currencyCode =
promotion.application_method?.currency_code ?? "USD"
+ const currencyInfo =
+ currencies[currencyCode?.toUpperCase() || "USD"]
+
return (
@@ -280,9 +293,11 @@ export const EditPromotionDetailsForm = ({
{isFixedValueType ? (
- onChange(val ? parseInt(val) : null)
- }
+ onValueChange={(val) => onChange(val)}
+ decimalSeparator="."
+ groupSeparator=","
+ decimalScale={currencyInfo.decimal_digits}
+ decimalsLimit={currencyInfo.decimal_digits}
code={currencyCode}
symbol={getCurrencySymbol(currencyCode)}
{...field}
@@ -299,7 +314,7 @@ export const EditPromotionDetailsForm = ({
onChange(
e.target.value === ""
? null
- : parseInt(e.target.value)
+ : parseFloat(e.target.value)
)
}}
/>