fix(dashboard): support more decimals for tx rates (#13407)
* Percentage formatter set maximum digits to 4 * Modify Tax Region Forms to allow 4 digits tax rates * Add changeset * Revert placeholder value to use only 2 decimals * fix(dashboard): support more decimals for tx rates * changeset * bypass scale * refactor * fix removal of deprecated input * cleanup deprecated percentage input * small mistake in onchange * add deprecated percentage input back * import * remove file extension * frane comment --------- Co-authored-by: AmbroziuBaban <ambroziubaban@gmail.com>
This commit is contained in:
5
.changeset/large-months-yawn.md
Normal file
5
.changeset/large-months-yawn.md
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
"@medusajs/dashboard": patch
|
||||
---
|
||||
|
||||
fix(dashboard): support more decimals for tx rates
|
||||
@@ -1,10 +1,22 @@
|
||||
import { Input, Text, clx } from "@medusajs/ui"
|
||||
import { clx, Input, Text } from "@medusajs/ui"
|
||||
import { getNumberOfDecimalPlaces } from "../../../lib/number-helper"
|
||||
import { ComponentProps, ElementRef, forwardRef } from "react"
|
||||
import Primitive from "react-currency-input-field"
|
||||
|
||||
/**
|
||||
* @deprecated Use `PercentageInput` instead
|
||||
*/
|
||||
const MIN_DECIMAL_SCALE = 2
|
||||
|
||||
function resolveDecimalScale(
|
||||
value: string | readonly string[] | number | undefined | null
|
||||
): number | undefined {
|
||||
if (value == null || Array.isArray(value)) {
|
||||
return MIN_DECIMAL_SCALE
|
||||
}
|
||||
return Math.max(
|
||||
getNumberOfDecimalPlaces(parseFloat(value.toString())),
|
||||
MIN_DECIMAL_SCALE
|
||||
)
|
||||
}
|
||||
|
||||
export const DeprecatedPercentageInput = forwardRef<
|
||||
ElementRef<typeof Input>,
|
||||
Omit<ComponentProps<typeof Input>, "type">
|
||||
@@ -38,38 +50,56 @@ DeprecatedPercentageInput.displayName = "PercentageInput"
|
||||
export const PercentageInput = forwardRef<
|
||||
ElementRef<"input">,
|
||||
ComponentProps<typeof Primitive>
|
||||
>(({ min = 0, decimalScale = 2, className, ...props }, ref) => {
|
||||
return (
|
||||
<div className="relative">
|
||||
<Primitive
|
||||
ref={ref as any} // dependency is typed incorrectly
|
||||
min={min}
|
||||
autoComplete="off"
|
||||
decimalScale={decimalScale}
|
||||
decimalsLimit={decimalScale}
|
||||
{...props}
|
||||
className={clx(
|
||||
"caret-ui-fg-base bg-ui-bg-field shadow-buttons-neutral transition-fg txt-compact-small flex w-full select-none appearance-none items-center justify-between rounded-md px-2 py-1.5 pr-10 text-right outline-none",
|
||||
"placeholder:text-ui-fg-muted text-ui-fg-base",
|
||||
"hover:bg-ui-bg-field-hover",
|
||||
"focus-visible:shadow-borders-interactive-with-active data-[state=open]:!shadow-borders-interactive-with-active",
|
||||
"aria-[invalid=true]:border-ui-border-error aria-[invalid=true]:shadow-borders-error",
|
||||
"invalid::border-ui-border-error invalid:shadow-borders-error",
|
||||
"disabled:!bg-ui-bg-disabled disabled:!text-ui-fg-disabled",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
<div className="absolute inset-y-0 right-0 z-10 flex w-8 items-center justify-center border-l">
|
||||
<Text
|
||||
className="text-ui-fg-muted"
|
||||
size="small"
|
||||
leading="compact"
|
||||
weight="plus"
|
||||
>
|
||||
%
|
||||
</Text>
|
||||
>(
|
||||
(
|
||||
{
|
||||
min = 0,
|
||||
max = 100,
|
||||
decimalScale,
|
||||
decimalsLimit,
|
||||
value,
|
||||
className,
|
||||
...props
|
||||
},
|
||||
ref
|
||||
) => {
|
||||
const resolvedDecimalScale = decimalScale ?? resolveDecimalScale(value)
|
||||
const resolvedDecimalsLimit = decimalsLimit ?? resolvedDecimalScale
|
||||
|
||||
return (
|
||||
<div className="relative">
|
||||
<Primitive
|
||||
ref={ref as any} // dependency is typed incorrectly
|
||||
min={min}
|
||||
max={max}
|
||||
autoComplete="off"
|
||||
decimalScale={resolvedDecimalScale}
|
||||
decimalsLimit={resolvedDecimalsLimit}
|
||||
value={value}
|
||||
{...props}
|
||||
className={clx(
|
||||
"caret-ui-fg-base bg-ui-bg-field shadow-buttons-neutral transition-fg txt-compact-small flex w-full select-none appearance-none items-center justify-between rounded-md px-2 py-1.5 pl-10 text-left outline-none",
|
||||
"placeholder:text-ui-fg-muted text-ui-fg-base",
|
||||
"hover:bg-ui-bg-field-hover",
|
||||
"focus-visible:shadow-borders-interactive-with-active data-[state=open]:!shadow-borders-interactive-with-active",
|
||||
"aria-[invalid=true]:border-ui-border-error aria-[invalid=true]:shadow-borders-error",
|
||||
"invalid::border-ui-border-error invalid:shadow-borders-error",
|
||||
"disabled:!bg-ui-bg-disabled disabled:!text-ui-fg-disabled",
|
||||
className
|
||||
)}
|
||||
/>
|
||||
<div className="absolute inset-y-0 left-0 z-10 flex w-8 items-center justify-center border-r">
|
||||
<Text
|
||||
className="text-ui-fg-muted"
|
||||
size="small"
|
||||
leading="compact"
|
||||
weight="plus"
|
||||
>
|
||||
%
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
)
|
||||
PercentageInput.displayName = "PercentageInput"
|
||||
|
||||
19
packages/admin/dashboard/src/lib/number-helper.ts
Normal file
19
packages/admin/dashboard/src/lib/number-helper.ts
Normal file
@@ -0,0 +1,19 @@
|
||||
/**
|
||||
* Gets the number of decimal places in a number
|
||||
* @param num - The number for which we are getting the number of decimal places
|
||||
* @returns The number of decimal places
|
||||
*
|
||||
* @example
|
||||
* getDecimalPlaces(123.456) // 3
|
||||
* getDecimalPlaces(42) // 0
|
||||
* getDecimalPlaces(10.0000) // 0
|
||||
*/
|
||||
export function getNumberOfDecimalPlaces(num: number): number {
|
||||
// Convert to string and check if it contains a decimal point
|
||||
const str = num.toString()
|
||||
if (str.indexOf(".") === -1) {
|
||||
return 0
|
||||
}
|
||||
// Return the length of the part after the decimal point
|
||||
return str.split(".")[1].length
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
const formatter = new Intl.NumberFormat([], {
|
||||
style: "percent",
|
||||
minimumFractionDigits: 2,
|
||||
maximumFractionDigits: 4,
|
||||
})
|
||||
|
||||
/**
|
||||
|
||||
@@ -3,7 +3,7 @@ import { useForm } from "react-hook-form"
|
||||
import { z } from "zod"
|
||||
|
||||
import { InformationCircleSolid } from "@medusajs/icons"
|
||||
import { Button, Heading, Input, Text, Tooltip, toast } from "@medusajs/ui"
|
||||
import { Button, Heading, Input, Text, toast, Tooltip } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import { CountrySelect } from "../../../../../components/inputs/country-select"
|
||||
@@ -221,6 +221,7 @@ export const TaxRegionCreateForm = ({ parentId }: TaxRegionCreateFormProps) => {
|
||||
<PercentageInput
|
||||
{...field}
|
||||
value={value?.value}
|
||||
decimalsLimit={4}
|
||||
onValueChange={(value, _name, values) =>
|
||||
onChange({
|
||||
value: value,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { InformationCircleSolid } from "@medusajs/icons"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { Button, Heading, Input, Text, Tooltip, toast } from "@medusajs/ui"
|
||||
import { Button, Heading, Input, Text, toast, Tooltip } from "@medusajs/ui"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { z } from "zod"
|
||||
@@ -189,6 +189,7 @@ export const TaxRegionProvinceCreateForm = ({
|
||||
<PercentageInput
|
||||
{...field}
|
||||
value={value?.value}
|
||||
decimalsLimit={4}
|
||||
onValueChange={(value, _name, values) =>
|
||||
onChange({
|
||||
value: value,
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import {
|
||||
Button,
|
||||
clx,
|
||||
Divider,
|
||||
Heading,
|
||||
Hint,
|
||||
@@ -8,7 +9,6 @@ import {
|
||||
Label,
|
||||
Select,
|
||||
Text,
|
||||
clx,
|
||||
toast,
|
||||
} from "@medusajs/ui"
|
||||
import { useFieldArray, useForm, useWatch } from "react-hook-form"
|
||||
@@ -414,6 +414,7 @@ export const TaxRegionCreateTaxOverrideForm = ({
|
||||
<PercentageInput
|
||||
{...field}
|
||||
placeholder="0.00"
|
||||
decimalsLimit={4}
|
||||
value={value?.value}
|
||||
onValueChange={(value, _name, values) =>
|
||||
onChange({
|
||||
|
||||
@@ -3,6 +3,7 @@ import { MagnifyingGlass } from "@medusajs/icons"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import {
|
||||
Button,
|
||||
clx,
|
||||
Divider,
|
||||
Heading,
|
||||
Hint,
|
||||
@@ -10,7 +11,6 @@ import {
|
||||
Label,
|
||||
Select,
|
||||
Text,
|
||||
clx,
|
||||
toast,
|
||||
} from "@medusajs/ui"
|
||||
import { useFieldArray, useForm, useWatch } from "react-hook-form"
|
||||
@@ -414,6 +414,7 @@ export const TaxRegionTaxOverrideEditForm = ({
|
||||
<PercentageInput
|
||||
{...field}
|
||||
value={value?.value}
|
||||
decimalsLimit={4}
|
||||
onValueChange={(value, _name, values) =>
|
||||
onChange({
|
||||
value: value,
|
||||
|
||||
@@ -119,6 +119,7 @@ export const TaxRegionTaxRateCreateForm = ({
|
||||
<PercentageInput
|
||||
{...field}
|
||||
value={value?.value}
|
||||
decimalsLimit={4}
|
||||
onValueChange={(value, _name, values) =>
|
||||
onChange({
|
||||
value: value,
|
||||
|
||||
@@ -117,6 +117,7 @@ export const TaxRegionTaxRateEditForm = ({
|
||||
<PercentageInput
|
||||
{...field}
|
||||
value={value?.value}
|
||||
decimalsLimit={4}
|
||||
onValueChange={(value, _name, values) =>
|
||||
onChange({
|
||||
value: value,
|
||||
|
||||
Reference in New Issue
Block a user