fix(dashboard): fix currency input locale formatting (#12812)
* fix: refund forms and format currency util * fix: claim form * fix: return form * fix: exchange form
This commit is contained in:
@@ -1,5 +1,5 @@
|
||||
export const formatCurrency = (amount: number, currency: string) => {
|
||||
return new Intl.NumberFormat("en-US", {
|
||||
return new Intl.NumberFormat(undefined, {
|
||||
style: "currency",
|
||||
currency,
|
||||
signDisplay: "auto",
|
||||
|
||||
@@ -5,7 +5,6 @@ import {
|
||||
clx,
|
||||
CurrencyInput,
|
||||
Divider,
|
||||
Input,
|
||||
Label,
|
||||
RadioGroup,
|
||||
Select,
|
||||
@@ -15,8 +14,10 @@ import {
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { formatValue } from "react-currency-input-field"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import * as zod from "zod"
|
||||
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
||||
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
|
||||
@@ -33,13 +34,19 @@ const OrderBalanceSettlementSchema = zod.object({
|
||||
settlement_type: zod.enum(["credit_line", "refund"]),
|
||||
refund: zod
|
||||
.object({
|
||||
amount: zod.string().or(zod.number()).optional(),
|
||||
amount: zod.object({
|
||||
value: zod.string().or(zod.number()).optional(),
|
||||
float: zod.number().or(zod.null()),
|
||||
}),
|
||||
note: zod.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
credit_line: zod
|
||||
.object({
|
||||
amount: zod.string().or(zod.number()).optional(),
|
||||
amount: zod.object({
|
||||
value: zod.string().or(zod.number()).optional(),
|
||||
float: zod.number().or(zod.null()),
|
||||
}),
|
||||
note: zod.string().optional(),
|
||||
})
|
||||
.optional(),
|
||||
@@ -51,19 +58,30 @@ export const OrderBalanceSettlementForm = ({
|
||||
order: AdminOrder
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const [searchParams] = useSearchParams()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
const [activePayment, setActivePayment] = useState<AdminPayment | null>(null)
|
||||
const paymentId = searchParams.get("paymentId")
|
||||
const payments = getPaymentsFromOrder(order)
|
||||
const pendingDifference = order.summary.pending_difference * -1
|
||||
|
||||
const [activePayment, setActivePayment] = useState<AdminPayment | null>(
|
||||
paymentId ? payments.find((p) => p.id === paymentId) || null : null
|
||||
)
|
||||
|
||||
const form = useForm<zod.infer<typeof OrderBalanceSettlementSchema>>({
|
||||
defaultValues: {
|
||||
settlement_type: "refund",
|
||||
refund: {
|
||||
amount: 0,
|
||||
amount: {
|
||||
value: "",
|
||||
float: null,
|
||||
},
|
||||
},
|
||||
credit_line: {
|
||||
amount: 0,
|
||||
amount: {
|
||||
value: "",
|
||||
float: null,
|
||||
},
|
||||
},
|
||||
},
|
||||
resolver: zodResolver(OrderBalanceSettlementSchema),
|
||||
@@ -79,9 +97,14 @@ export const OrderBalanceSettlementForm = ({
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
if (data.settlement_type === "credit_line") {
|
||||
if (data.credit_line?.amount.float === null) {
|
||||
return
|
||||
}
|
||||
await createCreditLine(
|
||||
{
|
||||
amount: parseFloat(data.credit_line!.amount! as string) * -1,
|
||||
amount: data.credit_line!.amount.float! * -1,
|
||||
reference: "refund",
|
||||
reference_id: order.id,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
@@ -97,9 +120,12 @@ export const OrderBalanceSettlementForm = ({
|
||||
}
|
||||
|
||||
if (data.settlement_type === "refund") {
|
||||
if (data.refund?.amount.float === null) {
|
||||
return
|
||||
}
|
||||
await createRefund(
|
||||
{
|
||||
amount: parseFloat(data.refund!.amount! as string),
|
||||
amount: data.refund!.amount!.float!,
|
||||
note: data.refund!.note,
|
||||
},
|
||||
{
|
||||
@@ -107,7 +133,7 @@ export const OrderBalanceSettlementForm = ({
|
||||
toast.success(
|
||||
t("orders.payment.refundPaymentSuccess", {
|
||||
amount: formatCurrency(
|
||||
parseFloat(data.refund!.amount! as string),
|
||||
data.refund!.amount!.float!,
|
||||
order.currency_code!
|
||||
),
|
||||
})
|
||||
@@ -131,18 +157,23 @@ export const OrderBalanceSettlementForm = ({
|
||||
useEffect(() => {
|
||||
form.clearErrors()
|
||||
|
||||
const minimum = activePayment?.amount
|
||||
const _minimum = activePayment?.amount
|
||||
? Math.min(pendingDifference, activePayment.amount)
|
||||
: pendingDifference
|
||||
|
||||
const minimum = {
|
||||
value: _minimum.toFixed(currency.decimal_digits),
|
||||
float: _minimum,
|
||||
}
|
||||
|
||||
if (settlementType === "refund") {
|
||||
form.setValue("refund.amount", activePayment ? minimum : 0)
|
||||
form.setValue("refund.amount", minimum)
|
||||
}
|
||||
|
||||
if (settlementType === "credit_line") {
|
||||
form.setValue("credit_line.amount", minimum)
|
||||
}
|
||||
}, [settlementType, activePayment, pendingDifference, form])
|
||||
}, [settlementType, activePayment, pendingDifference, form, currency])
|
||||
|
||||
return (
|
||||
<RouteDrawer.Form form={form}>
|
||||
@@ -194,6 +225,7 @@ export const OrderBalanceSettlementForm = ({
|
||||
<>
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<Select
|
||||
defaultValue={activePayment?.id}
|
||||
onValueChange={(value) => {
|
||||
setActivePayment(payments.find((p) => p.id === value)!)
|
||||
}}
|
||||
@@ -260,9 +292,12 @@ export const OrderBalanceSettlementForm = ({
|
||||
decimalScale={currency.decimal_digits}
|
||||
symbol={currency.symbol_native}
|
||||
code={currency.code}
|
||||
value={field.value}
|
||||
value={field.value.value}
|
||||
onValueChange={(_value, _name, values) =>
|
||||
onChange(values?.value ? values?.value : "")
|
||||
onChange({
|
||||
value: values?.value,
|
||||
float: values?.float || null,
|
||||
})
|
||||
}
|
||||
autoFocus
|
||||
/>
|
||||
@@ -315,10 +350,13 @@ export const OrderBalanceSettlementForm = ({
|
||||
decimalScale={currency.decimal_digits}
|
||||
symbol={currency.symbol_native}
|
||||
code={currency.code}
|
||||
value={field.value}
|
||||
onValueChange={(_value, _name, values) =>
|
||||
onChange(values?.value ? values?.value : "")
|
||||
}
|
||||
value={field.value.value}
|
||||
onValueChange={(_value, _name, values) => {
|
||||
onChange({
|
||||
value: values?.value,
|
||||
float: values?.float || null,
|
||||
})
|
||||
}}
|
||||
autoFocus
|
||||
/>
|
||||
</Form.Control>
|
||||
|
||||
@@ -87,10 +87,16 @@ export const ClaimCreateForm = ({
|
||||
useState(false)
|
||||
|
||||
const [customInboundShippingAmount, setCustomInboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
useState<{ value: string; float: number | null }>({
|
||||
value: "0",
|
||||
float: 0,
|
||||
})
|
||||
|
||||
const [customOutboundShippingAmount, setCustomOutboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
useState<{ value: string; float: number | null }>({
|
||||
value: "0",
|
||||
float: 0,
|
||||
})
|
||||
|
||||
const [inventoryMap, setInventoryMap] = useState<
|
||||
Record<string, InventoryLevelDTO[]>
|
||||
@@ -263,13 +269,23 @@ export const ClaimCreateForm = ({
|
||||
|
||||
useEffect(() => {
|
||||
if (inboundShipping) {
|
||||
setCustomInboundShippingAmount(inboundShipping.total)
|
||||
setCustomInboundShippingAmount({
|
||||
value: inboundShipping.total.toFixed(
|
||||
currencies[order.currency_code.toUpperCase()].decimal_digits
|
||||
),
|
||||
float: inboundShipping.total,
|
||||
})
|
||||
}
|
||||
}, [inboundShipping])
|
||||
|
||||
useEffect(() => {
|
||||
if (outboundShipping) {
|
||||
setCustomOutboundShippingAmount(outboundShipping.total)
|
||||
setCustomOutboundShippingAmount({
|
||||
value: outboundShipping.total.toFixed(
|
||||
currencies[order.currency_code.toUpperCase()].decimal_digits
|
||||
),
|
||||
float: outboundShipping.total,
|
||||
})
|
||||
}
|
||||
}, [outboundShipping])
|
||||
|
||||
@@ -519,6 +535,7 @@ export const ClaimCreateForm = ({
|
||||
).variants
|
||||
|
||||
variants.forEach((variant) => {
|
||||
// TODO: fix this for inventory kits
|
||||
ret[variant.id] = variant.inventory?.[0]?.location_levels || []
|
||||
})
|
||||
|
||||
@@ -560,6 +577,15 @@ export const ClaimCreateForm = ({
|
||||
return (method?.total as number) || 0
|
||||
}, [preview.shipping_methods])
|
||||
|
||||
const outboundShippingTotal = useMemo(() => {
|
||||
const method = preview.shipping_methods.find(
|
||||
(sm) =>
|
||||
!!sm.actions?.find((a) => a.action === "SHIPPING_ADD" && !a.return_id)
|
||||
)
|
||||
|
||||
return (method?.total as number) || 0
|
||||
}, [preview.shipping_methods])
|
||||
|
||||
return (
|
||||
<RouteFocusModal.Form form={form}>
|
||||
<KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
|
||||
@@ -866,10 +892,7 @@ export const ClaimCreateForm = ({
|
||||
}
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customInboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customInboundShippingAmount)
|
||||
const customPrice = customInboundShippingAmount.float
|
||||
|
||||
if (actionId) {
|
||||
updateInboundShipping(
|
||||
@@ -891,8 +914,13 @@ export const ClaimCreateForm = ({
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomInboundShippingAmount}
|
||||
value={customInboundShippingAmount}
|
||||
onValueChange={(value, _name, values) => {
|
||||
setCustomInboundShippingAmount({
|
||||
value: values?.value || "",
|
||||
float: values?.float || null,
|
||||
})
|
||||
}}
|
||||
value={customInboundShippingAmount.value}
|
||||
disabled={showInboundItemsPlaceholder}
|
||||
/>
|
||||
) : (
|
||||
@@ -937,10 +965,7 @@ export const ClaimCreateForm = ({
|
||||
}
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customOutboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customOutboundShippingAmount)
|
||||
const customPrice = customOutboundShippingAmount.float
|
||||
|
||||
if (actionId) {
|
||||
updateOutboundShipping(
|
||||
@@ -962,13 +987,18 @@ export const ClaimCreateForm = ({
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomOutboundShippingAmount}
|
||||
value={customOutboundShippingAmount}
|
||||
onValueChange={(value, _name, values) => {
|
||||
setCustomOutboundShippingAmount({
|
||||
value: values?.value || "",
|
||||
float: values?.float || null,
|
||||
})
|
||||
}}
|
||||
value={customOutboundShippingAmount.value}
|
||||
disabled={showOutboundItemsPlaceholder}
|
||||
/>
|
||||
) : (
|
||||
getStylizedAmount(
|
||||
outboundShipping?.amount ?? 0,
|
||||
outboundShippingTotal,
|
||||
order.currency_code
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -60,10 +60,18 @@ export const ExchangeCreateForm = ({
|
||||
useState(false)
|
||||
const [isOutboundShippingPriceEdit, setIsOutboundShippingPriceEdit] =
|
||||
useState(false)
|
||||
|
||||
const [customInboundShippingAmount, setCustomInboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
useState<{ value: string; float: number | null }>({
|
||||
value: "0",
|
||||
float: 0,
|
||||
})
|
||||
|
||||
const [customOutboundShippingAmount, setCustomOutboundShippingAmount] =
|
||||
useState<number | string>(0)
|
||||
useState<{ value: string; float: number | null }>({
|
||||
value: "0",
|
||||
float: 0,
|
||||
})
|
||||
|
||||
/**
|
||||
* MUTATIONS
|
||||
@@ -252,6 +260,15 @@ export const ExchangeCreateForm = ({
|
||||
return (method?.total as number) || 0
|
||||
}, [preview.shipping_methods])
|
||||
|
||||
const outboundShippingTotal = useMemo(() => {
|
||||
const method = preview.shipping_methods.find(
|
||||
(sm) =>
|
||||
!!sm.actions?.find((a) => a.action === "SHIPPING_ADD" && !a.return_id)
|
||||
)
|
||||
|
||||
return (method?.total as number) || 0
|
||||
}, [preview.shipping_methods])
|
||||
|
||||
return (
|
||||
<RouteFocusModal.Form form={form}>
|
||||
<KeyboundForm onSubmit={handleSubmit} className="flex h-full flex-col">
|
||||
@@ -356,10 +373,7 @@ export const ExchangeCreateForm = ({
|
||||
}
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customInboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customInboundShippingAmount)
|
||||
const customPrice = customInboundShippingAmount.float
|
||||
|
||||
if (actionId) {
|
||||
updateInboundShipping(
|
||||
@@ -381,8 +395,13 @@ export const ExchangeCreateForm = ({
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomInboundShippingAmount}
|
||||
value={customInboundShippingAmount}
|
||||
onValueChange={(value, name, values) =>
|
||||
setCustomInboundShippingAmount({
|
||||
value: values?.value || "",
|
||||
float: values?.float || null,
|
||||
})
|
||||
}
|
||||
value={customInboundShippingAmount.value}
|
||||
disabled={!inboundPreviewItems?.length}
|
||||
/>
|
||||
) : (
|
||||
@@ -427,10 +446,7 @@ export const ExchangeCreateForm = ({
|
||||
}
|
||||
})
|
||||
|
||||
const customPrice =
|
||||
customOutboundShippingAmount === ""
|
||||
? null
|
||||
: parseFloat(customOutboundShippingAmount)
|
||||
const customPrice = customOutboundShippingAmount.float
|
||||
|
||||
if (actionId) {
|
||||
updateOutboundShipping(
|
||||
@@ -452,13 +468,18 @@ export const ExchangeCreateForm = ({
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={setCustomOutboundShippingAmount}
|
||||
value={customOutboundShippingAmount}
|
||||
onValueChange={(value, name, values) =>
|
||||
setCustomOutboundShippingAmount({
|
||||
value: values?.value || "",
|
||||
float: values?.float || null,
|
||||
})
|
||||
}
|
||||
value={customOutboundShippingAmount.value}
|
||||
disabled={!outboundPreviewItems?.length}
|
||||
/>
|
||||
) : (
|
||||
getStylizedAmount(
|
||||
outboundShipping?.amount ?? 0,
|
||||
outboundShippingTotal,
|
||||
order.currency_code
|
||||
)
|
||||
)}
|
||||
|
||||
@@ -8,11 +8,11 @@ import {
|
||||
Textarea,
|
||||
toast,
|
||||
} from "@medusajs/ui"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import { useEffect, useMemo, useState } from "react"
|
||||
import { formatValue } from "react-currency-input-field"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { useNavigate, useSearchParams } from "react-router-dom"
|
||||
import { useSearchParams } from "react-router-dom"
|
||||
import * as zod from "zod"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
||||
@@ -29,16 +29,21 @@ type CreateRefundFormProps = {
|
||||
}
|
||||
|
||||
const CreateRefundSchema = zod.object({
|
||||
amount: zod.string().or(zod.number()),
|
||||
amount: zod.object({
|
||||
value: zod.string().or(zod.number()),
|
||||
float: zod.number().or(zod.null()),
|
||||
}),
|
||||
note: zod.string().optional(),
|
||||
})
|
||||
|
||||
export const CreateRefundForm = ({ order }: CreateRefundFormProps) => {
|
||||
const { t } = useTranslation()
|
||||
const { handleSuccess } = useRouteModal()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const [searchParams] = useSearchParams()
|
||||
const paymentId = searchParams.get("paymentId")
|
||||
const [paymentId, setPaymentId] = useState<string | undefined>(
|
||||
searchParams.get("paymentId") || undefined
|
||||
)
|
||||
const payments = getPaymentsFromOrder(order)
|
||||
const payment = payments.find((p) => p.id === paymentId)!
|
||||
const paymentAmount = payment?.amount || 0
|
||||
@@ -50,7 +55,10 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => {
|
||||
|
||||
const form = useForm<zod.infer<typeof CreateRefundSchema>>({
|
||||
defaultValues: {
|
||||
amount: paymentAmount,
|
||||
amount: {
|
||||
value: paymentAmount.toFixed(currency.decimal_digits),
|
||||
float: paymentAmount,
|
||||
},
|
||||
note: "",
|
||||
},
|
||||
resolver: zodResolver(CreateRefundSchema),
|
||||
@@ -61,21 +69,24 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => {
|
||||
const paymentAmount = (payment?.amount || 0) as number
|
||||
const pendingAmount =
|
||||
pendingDifference < 0
|
||||
? Math.min(pendingDifference, paymentAmount)
|
||||
? Math.min(Math.abs(pendingDifference), paymentAmount)
|
||||
: paymentAmount
|
||||
|
||||
const normalizedAmount =
|
||||
pendingAmount < 0 ? pendingAmount * -1 : pendingAmount
|
||||
|
||||
form.setValue("amount", normalizedAmount as number)
|
||||
}, [payment])
|
||||
form.setValue("amount", {
|
||||
value: normalizedAmount.toFixed(currency.decimal_digits),
|
||||
float: normalizedAmount,
|
||||
})
|
||||
}, [payment?.id || ""])
|
||||
|
||||
const { mutateAsync, isPending } = useRefundPayment(order.id, payment?.id!)
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
await mutateAsync(
|
||||
{
|
||||
amount: parseFloat(data.amount as string),
|
||||
amount: data.amount.float!,
|
||||
note: data.note,
|
||||
},
|
||||
{
|
||||
@@ -83,7 +94,7 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => {
|
||||
toast.success(
|
||||
t("orders.payment.refundPaymentSuccess", {
|
||||
amount: formatCurrency(
|
||||
data.amount as number,
|
||||
data.amount.float!,
|
||||
payment?.currency_code!
|
||||
),
|
||||
})
|
||||
@@ -107,11 +118,9 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => {
|
||||
<RouteDrawer.Body className="flex-1 overflow-auto">
|
||||
<div className="flex flex-col gap-y-4">
|
||||
<Select
|
||||
value={payment?.id}
|
||||
value={paymentId}
|
||||
onValueChange={(value) => {
|
||||
navigate(`/orders/${order.id}/refund?paymentId=${value}`, {
|
||||
replace: true,
|
||||
})
|
||||
setPaymentId(value)
|
||||
}}
|
||||
>
|
||||
<Label className="txt-compact-small mb-[-6px] font-sans font-medium">
|
||||
@@ -179,9 +188,12 @@ export const CreateRefundForm = ({ order }: CreateRefundFormProps) => {
|
||||
decimalScale={currency.decimal_digits}
|
||||
symbol={currency.symbol_native}
|
||||
code={currency.code}
|
||||
value={field.value}
|
||||
value={field.value.value}
|
||||
onValueChange={(_value, _name, values) =>
|
||||
onChange(values?.value ? values?.value : "")
|
||||
onChange({
|
||||
value: values?.value,
|
||||
float: values?.float || null,
|
||||
})
|
||||
}
|
||||
autoFocus
|
||||
/>
|
||||
|
||||
@@ -94,7 +94,13 @@ export const ReturnCreateForm = ({
|
||||
*/
|
||||
const { setIsOpen } = useStackedModal()
|
||||
const [isShippingPriceEdit, setIsShippingPriceEdit] = useState(false)
|
||||
const [customShippingAmount, setCustomShippingAmount] = useState(0)
|
||||
const [customShippingAmount, setCustomShippingAmount] = useState<{
|
||||
value: string
|
||||
float: number | null
|
||||
}>({
|
||||
value: "0",
|
||||
float: 0,
|
||||
})
|
||||
const [inventoryMap, setInventoryMap] = useState<
|
||||
Record<string, InventoryLevelDTO[]>
|
||||
>({})
|
||||
@@ -671,10 +677,7 @@ export const ReturnCreateForm = ({
|
||||
if (actionId) {
|
||||
updateReturnShipping({
|
||||
actionId,
|
||||
custom_amount:
|
||||
typeof customShippingAmount === "string"
|
||||
? null
|
||||
: customShippingAmount,
|
||||
custom_amount: customShippingAmount.float,
|
||||
})
|
||||
}
|
||||
setIsShippingPriceEdit(false)
|
||||
@@ -684,10 +687,13 @@ export const ReturnCreateForm = ({
|
||||
.symbol_native
|
||||
}
|
||||
code={order.currency_code}
|
||||
onValueChange={(value) =>
|
||||
setCustomShippingAmount(value ? parseFloat(value) : "")
|
||||
onValueChange={(value, name, values) =>
|
||||
setCustomShippingAmount({
|
||||
value: values?.value || "",
|
||||
float: values?.float || null,
|
||||
})
|
||||
}
|
||||
value={customShippingAmount}
|
||||
value={customShippingAmount.value}
|
||||
disabled={showPlaceholder}
|
||||
/>
|
||||
) : (
|
||||
|
||||
Reference in New Issue
Block a user