diff --git a/src/pages/discount-codes/discount-code-form/DiscountCodeFormPage.tsx b/src/pages/discount-codes/discount-code-form/DiscountCodeFormPage.tsx index 2814cc5..79b6b93 100644 --- a/src/pages/discount-codes/discount-code-form/DiscountCodeFormPage.tsx +++ b/src/pages/discount-codes/discount-code-form/DiscountCodeFormPage.tsx @@ -17,7 +17,12 @@ const schema = yup.object({ name: yup.string().min(1, 'نام الزامی است').max(100, 'نام نباید بیشتر از ۱۰۰ کاراکتر باشد').required('نام الزامی است'), description: yup.string().max(500, 'توضیحات نباید بیشتر از ۵۰۰ کاراکتر باشد').nullable(), type: yup.mixed<'percentage' | 'fixed' | 'fee_percentage'>().oneOf(['percentage', 'fixed', 'fee_percentage']).required('نوع الزامی است'), - value: yup.number().typeError('مقدار نامعتبر است').required('مقدار الزامی است').min(0.01, 'مقدار باید بیشتر از صفر باشد'), + value: yup + .number() + .transform((val, original) => parseFormattedNumber(original) as any) + .typeError('مقدار نامعتبر است') + .required('مقدار الزامی است') + .min(0.01, 'مقدار باید بیشتر از صفر باشد'), status: yup.mixed<'active' | 'inactive'>().oneOf(['active', 'inactive']).required('وضعیت الزامی است'), application_level: yup.mixed<'invoice' | 'category' | 'product' | 'shipping' | 'product_fee'>().oneOf(['invoice', 'category', 'product', 'shipping', 'product_fee']).required('سطح اعمال الزامی است'), min_purchase_amount: yup @@ -196,6 +201,8 @@ const DiscountCodeFormPage = () => { step="0.01" placeholder="20" error={errors.value?.message as string} + thousandSeparator + numeric {...register('value')} data-testid="discount-value-input" /> diff --git a/src/utils/numberUtils.ts b/src/utils/numberUtils.ts index 1d34a05..b45dd55 100644 --- a/src/utils/numberUtils.ts +++ b/src/utils/numberUtils.ts @@ -86,13 +86,16 @@ export const createOptionalNumberTransform = () => { export const formatWithThousands = (value: string | number): string => { if (value === null || value === undefined) return ""; - const str = persianToEnglish(value.toString()); - if (str === "") return ""; - const parts = str.replace(/[^\d.]/g, "").split("."); + const raw = persianToEnglish(value.toString()); + if (raw === "") return ""; + const hasTrailingDot = /\.$/.test(raw); + const sanitized = raw.replace(/[^\d.]/g, ""); + const parts = sanitized.split("."); const integerPart = parts[0]; const decimalPart = parts.length > 1 ? parts[1] : ""; const withCommas = integerPart.replace(/\B(?=(\d{3})+(?!\d))/g, ","); - return decimalPart ? `${withCommas}.${decimalPart}` : withCommas; + if (decimalPart) return `${withCommas}.${decimalPart}`; + return hasTrailingDot ? `${withCommas}.` : withCommas; }; export const parseFormattedNumber = (value: any): number | undefined => {