feat(dashboard,core-flows,js-sdk,types,link-modules,payment): ability to copy payment link (#8630)
what: - enables a button to create a payment link when a payment delta is present - api to delete order payment collection - adds a pending amount to payment collections Note: Not the happiest with the decision on when to create a payment collection and when not to. The code should programatically create or delete payment collections currently to generate the right collection for the payment delta. Adding a more specific flow to create and manage a payment collection will help reduce this burden from the code path and onto CX/merchant. Another issue I found is that the payment collection status doesn't get updated when payment is complete as it still gets stuck to "authorized" state https://github.com/user-attachments/assets/037a10f9-3621-43c2-94ba-1ada4b0a041b
This commit is contained in:
@@ -13,6 +13,7 @@ export * from "./inventory"
|
||||
export * from "./invites"
|
||||
export * from "./notification"
|
||||
export * from "./orders"
|
||||
export * from "./payment-collections"
|
||||
export * from "./payments"
|
||||
export * from "./price-lists"
|
||||
export * from "./product-types"
|
||||
|
||||
@@ -89,6 +89,11 @@ export const useCreateOrderFulfillment = (
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.details(),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.preview(orderId),
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
@@ -107,6 +112,11 @@ export const useCancelOrderFulfillment = (
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.details(),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.preview(orderId),
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
@@ -129,6 +139,11 @@ export const useCreateOrderShipment = (
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.details(),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.preview(orderId),
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
@@ -136,18 +151,15 @@ export const useCreateOrderShipment = (
|
||||
}
|
||||
|
||||
export const useCancelOrder = (
|
||||
orderId: string,
|
||||
options?: UseMutationOptions<any, Error, any>
|
||||
) => {
|
||||
return useMutation({
|
||||
mutationFn: () => sdk.admin.order.cancel(orderId),
|
||||
mutationFn: (id) => sdk.admin.order.cancel(id),
|
||||
onSuccess: (data: any, variables: any, context: any) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.details(),
|
||||
})
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.lists(),
|
||||
queryKey: ordersQueryKeys.all,
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
|
||||
@@ -0,0 +1,63 @@
|
||||
import { FetchError } from "@medusajs/js-sdk"
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { useMutation, UseMutationOptions } from "@tanstack/react-query"
|
||||
import { sdk } from "../../lib/client"
|
||||
import { queryClient } from "../../lib/query-client"
|
||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||
import { ordersQueryKeys } from "./orders"
|
||||
|
||||
const PAYMENT_COLLECTION_QUERY_KEY = "payment-collection" as const
|
||||
export const paymentCollectionQueryKeys = queryKeysFactory(
|
||||
PAYMENT_COLLECTION_QUERY_KEY
|
||||
)
|
||||
|
||||
export const useCreatePaymentCollection = (
|
||||
options?: UseMutationOptions<
|
||||
HttpTypes.AdminPaymentCollectionResponse,
|
||||
Error,
|
||||
HttpTypes.AdminCreatePaymentCollection
|
||||
>
|
||||
) => {
|
||||
return useMutation({
|
||||
mutationFn: (payload) => sdk.admin.paymentCollection.create(payload),
|
||||
onSuccess: (data, variables, context) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.all,
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: paymentCollectionQueryKeys.all,
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
})
|
||||
}
|
||||
|
||||
export const useDeletePaymentCollection = (
|
||||
options?: Omit<
|
||||
UseMutationOptions<
|
||||
HttpTypes.AdminDeletePaymentCollectionResponse,
|
||||
FetchError,
|
||||
string
|
||||
>,
|
||||
"mutationFn"
|
||||
>
|
||||
) => {
|
||||
return useMutation({
|
||||
mutationFn: (id: string) => sdk.admin.paymentCollection.delete(id),
|
||||
onSuccess: (data, variables, context) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.all,
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: paymentCollectionQueryKeys.all,
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
},
|
||||
...options,
|
||||
})
|
||||
}
|
||||
@@ -70,11 +70,7 @@ export const useCapturePayment = (
|
||||
mutationFn: (payload) => sdk.admin.payment.capture(paymentId, payload),
|
||||
onSuccess: (data, variables, context) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.details(),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.lists(),
|
||||
queryKey: ordersQueryKeys.all,
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
@@ -95,11 +91,7 @@ export const useRefundPayment = (
|
||||
mutationFn: (payload) => sdk.admin.payment.refund(paymentId, payload),
|
||||
onSuccess: (data, variables, context) => {
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.details(),
|
||||
})
|
||||
|
||||
queryClient.invalidateQueries({
|
||||
queryKey: ordersQueryKeys.lists(),
|
||||
queryKey: ordersQueryKeys.all,
|
||||
})
|
||||
|
||||
options?.onSuccess?.(data, variables, context)
|
||||
|
||||
@@ -4,7 +4,7 @@ import { sdk } from "../../lib/client"
|
||||
import { queryKeysFactory } from "../../lib/query-key-factory"
|
||||
|
||||
const REFUND_REASON_QUERY_KEY = "refund-reason" as const
|
||||
export const paymentQueryKeys = queryKeysFactory(REFUND_REASON_QUERY_KEY)
|
||||
export const refundReasonQueryKeys = queryKeysFactory(REFUND_REASON_QUERY_KEY)
|
||||
|
||||
export const useRefundReasons = (
|
||||
query?: HttpTypes.RefundReasonFilters,
|
||||
|
||||
@@ -830,7 +830,9 @@
|
||||
"capturePaymentSuccess": "Payment of {{amount}} successfully captured",
|
||||
"createRefund": "Create Refund",
|
||||
"refundPaymentSuccess": "Refund of amount {{amount}} successful",
|
||||
"createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}"
|
||||
"createRefundWrongQuantity": "Quantity should be a number between 1 and {{number}}",
|
||||
"refundAmount": "Refund {{ amount }}",
|
||||
"paymentLink": "Copy payment link for {{ amount }}"
|
||||
},
|
||||
|
||||
"edits": {
|
||||
|
||||
@@ -10,3 +10,12 @@ export const getTotalCaptured = (
|
||||
(paymentCollection.refunded_amount as number))
|
||||
return acc
|
||||
}, 0)
|
||||
|
||||
export const getTotalPending = (paymentCollections: AdminPaymentCollection[]) =>
|
||||
paymentCollections.reduce((acc, paymentCollection) => {
|
||||
acc +=
|
||||
(paymentCollection.amount as number) -
|
||||
(paymentCollection.captured_amount as number)
|
||||
|
||||
return acc
|
||||
}, 0)
|
||||
|
||||
+12
-1
@@ -7,6 +7,7 @@ import { AdminOrder } from "@medusajs/types"
|
||||
import { Alert, Button, Select, Switch, toast } from "@medusajs/ui"
|
||||
import { useForm, useWatch } from "react-hook-form"
|
||||
|
||||
import { OrderLineItemDTO } from "@medusajs/types"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import {
|
||||
RouteFocusModal,
|
||||
@@ -118,8 +119,18 @@ export function OrderCreateFulfillmentForm({
|
||||
|
||||
if (itemsToFulfill?.length) {
|
||||
setFulfillableItems(itemsToFulfill)
|
||||
|
||||
const quantityMap = fulfillableItems.reduce(
|
||||
(acc, item) => {
|
||||
acc[item.id] = getFulfillableQuantity(item as OrderLineItemDTO)
|
||||
return acc
|
||||
},
|
||||
{} as Record<string, number>
|
||||
)
|
||||
|
||||
form.setValue("quantity", quantityMap)
|
||||
}
|
||||
}, [order.items])
|
||||
}, [order.items?.length])
|
||||
|
||||
return (
|
||||
<RouteFocusModal.Form form={form}>
|
||||
|
||||
+3
-3
@@ -11,8 +11,8 @@ import {
|
||||
RouteFocusModal,
|
||||
useRouteModal,
|
||||
} from "../../../../../components/modals"
|
||||
import { CreateShipmentSchema } from "./constants"
|
||||
import { useCreateOrderShipment } from "../../../../../hooks/api"
|
||||
import { CreateShipmentSchema } from "./constants"
|
||||
|
||||
type OrderCreateFulfillmentFormProps = {
|
||||
order: AdminOrder
|
||||
@@ -27,7 +27,7 @@ export function OrderCreateShipmentForm({
|
||||
const { handleSuccess } = useRouteModal()
|
||||
|
||||
const { mutateAsync: createShipment, isPending: isMutating } =
|
||||
useCreateOrderShipment(order.id, fulfillment.id)
|
||||
useCreateOrderShipment(order.id, fulfillment?.id)
|
||||
|
||||
const form = useForm<zod.infer<typeof CreateShipmentSchema>>({
|
||||
defaultValues: {
|
||||
@@ -44,7 +44,7 @@ export function OrderCreateShipmentForm({
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
await createShipment(
|
||||
{
|
||||
items: fulfillment.items.map((i) => ({
|
||||
items: fulfillment?.items?.map((i) => ({
|
||||
id: i.line_item_id,
|
||||
quantity: i.quantity,
|
||||
})),
|
||||
|
||||
+132
@@ -0,0 +1,132 @@
|
||||
import { CheckCircleSolid, SquareTwoStack } from "@medusajs/icons"
|
||||
import { AdminOrder } from "@medusajs/types"
|
||||
import { Button, toast, Tooltip } from "@medusajs/ui"
|
||||
import copy from "copy-to-clipboard"
|
||||
import React, { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import {
|
||||
useCreatePaymentCollection,
|
||||
useDeletePaymentCollection,
|
||||
} from "../../../../../hooks/api"
|
||||
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
|
||||
|
||||
export const MEDUSA_BACKEND_URL = __STOREFRONT_URL__ ?? "http://localhost:8000"
|
||||
|
||||
type CopyPaymentLinkProps = {
|
||||
order: AdminOrder
|
||||
}
|
||||
|
||||
/**
|
||||
* This component is based on the `button` element and supports all of its props
|
||||
*/
|
||||
const CopyPaymentLink = React.forwardRef<any, CopyPaymentLinkProps>(
|
||||
({ order }: CopyPaymentLinkProps, ref) => {
|
||||
const [isCreating, setIsCreating] = useState(false)
|
||||
const [url, setUrl] = useState("")
|
||||
const [done, setDone] = useState(false)
|
||||
const [open, setOpen] = useState(false)
|
||||
const [text, setText] = useState("CopyPaymentLink")
|
||||
const { t } = useTranslation()
|
||||
const { mutateAsync: createPaymentCollection } =
|
||||
useCreatePaymentCollection()
|
||||
|
||||
const { mutateAsync: deletePaymentCollection } =
|
||||
useDeletePaymentCollection()
|
||||
|
||||
const copyToClipboard = async (
|
||||
e:
|
||||
| React.MouseEvent<HTMLElement, MouseEvent>
|
||||
| React.MouseEvent<HTMLButtonElement, MouseEvent>
|
||||
) => {
|
||||
e.stopPropagation()
|
||||
|
||||
if (!url?.length) {
|
||||
const activePaymentCollection = order.payment_collections.find(
|
||||
(pc) =>
|
||||
pc.status === "not_paid" &&
|
||||
pc.amount === order.summary?.pending_difference
|
||||
)
|
||||
|
||||
if (!activePaymentCollection) {
|
||||
setIsCreating(true)
|
||||
|
||||
const paymentCollectionsToDelete = order.payment_collections.filter(
|
||||
(pc) => pc.status === "not_paid"
|
||||
)
|
||||
|
||||
const promises = paymentCollectionsToDelete.map((paymentCollection) =>
|
||||
deletePaymentCollection(paymentCollection.id)
|
||||
)
|
||||
|
||||
await Promise.all(promises)
|
||||
|
||||
await createPaymentCollection(
|
||||
{ order_id: order.id },
|
||||
{
|
||||
onSuccess: (data) => {
|
||||
setUrl(
|
||||
`${MEDUSA_BACKEND_URL}/payment-collection/${data.payment_collection.id}`
|
||||
)
|
||||
},
|
||||
onError: (err) => {
|
||||
toast.error(err.message)
|
||||
},
|
||||
onSettled: () => setIsCreating(false),
|
||||
}
|
||||
)
|
||||
} else {
|
||||
setUrl(
|
||||
`${MEDUSA_BACKEND_URL}/payment-collection/${activePaymentCollection.id}`
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
setDone(true)
|
||||
copy(url)
|
||||
|
||||
setTimeout(() => {
|
||||
setDone(false)
|
||||
setUrl("")
|
||||
}, 2000)
|
||||
}
|
||||
|
||||
React.useEffect(() => {
|
||||
if (done) {
|
||||
setText("Copied")
|
||||
return
|
||||
}
|
||||
|
||||
setTimeout(() => {
|
||||
setText("CopyPaymentLink")
|
||||
}, 500)
|
||||
}, [done])
|
||||
|
||||
return (
|
||||
<Tooltip content={text} open={done || open} onOpenChange={setOpen}>
|
||||
<Button
|
||||
ref={ref}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
aria-label="CopyPaymentLink code snippet"
|
||||
onClick={copyToClipboard}
|
||||
isLoading={isCreating}
|
||||
>
|
||||
{done ? (
|
||||
<CheckCircleSolid className="inline" />
|
||||
) : (
|
||||
<SquareTwoStack className="inline" />
|
||||
)}
|
||||
{t("orders.payment.paymentLink", {
|
||||
amount: getStylizedAmount(
|
||||
order?.summary?.pending_difference,
|
||||
order?.currency_code
|
||||
),
|
||||
})}
|
||||
</Button>
|
||||
</Tooltip>
|
||||
)
|
||||
}
|
||||
)
|
||||
CopyPaymentLink.displayName = "CopyPaymentLink"
|
||||
|
||||
export { CopyPaymentLink }
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./copy-payment-link"
|
||||
+7
-2
@@ -1,5 +1,10 @@
|
||||
import { Buildings, XCircle } from "@medusajs/icons"
|
||||
import { AdminOrder, FulfillmentDTO, OrderLineItemDTO } from "@medusajs/types"
|
||||
import {
|
||||
AdminOrder,
|
||||
AdminOrderFulfillment,
|
||||
HttpTypes,
|
||||
OrderLineItemDTO,
|
||||
} from "@medusajs/types"
|
||||
import {
|
||||
Button,
|
||||
Container,
|
||||
@@ -152,7 +157,7 @@ const Fulfillment = ({
|
||||
order,
|
||||
index,
|
||||
}: {
|
||||
fulfillment: FulfillmentDTO
|
||||
fulfillment: AdminOrderFulfillment
|
||||
order: AdminOrder
|
||||
index: number
|
||||
}) => {
|
||||
|
||||
+3
-3
@@ -11,11 +11,11 @@ import {
|
||||
import { format } from "date-fns"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { ActionMenu } from "../../../../../components/common/action-menu"
|
||||
import { useCancelOrder } from "../../../../../hooks/api/orders"
|
||||
import {
|
||||
getOrderFulfillmentStatus,
|
||||
getOrderPaymentStatus,
|
||||
} from "../../../../../lib/order-helpers"
|
||||
import { useCancelOrder } from "../../../../../hooks/api/orders"
|
||||
|
||||
type OrderGeneralSectionProps = {
|
||||
order: Order
|
||||
@@ -25,7 +25,7 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => {
|
||||
const { t } = useTranslation()
|
||||
const prompt = usePrompt()
|
||||
|
||||
const { mutateAsync } = useCancelOrder(order.id)
|
||||
const { mutateAsync: cancelOrder } = useCancelOrder()
|
||||
|
||||
const handleCancel = async () => {
|
||||
const res = await prompt({
|
||||
@@ -41,7 +41,7 @@ export const OrderGeneralSection = ({ order }: OrderGeneralSectionProps) => {
|
||||
return
|
||||
}
|
||||
|
||||
await mutateAsync()
|
||||
await cancelOrder(order.id)
|
||||
}
|
||||
|
||||
return (
|
||||
|
||||
+27
-8
@@ -25,7 +25,7 @@ import {
|
||||
getStylizedAmount,
|
||||
} from "../../../../../lib/money-amount-helpers"
|
||||
import { getOrderPaymentStatus } from "../../../../../lib/order-helpers"
|
||||
import { getTotalCaptured } from "../../../../../lib/payment"
|
||||
import { getTotalCaptured, getTotalPending } from "../../../../../lib/payment"
|
||||
|
||||
type OrderPaymentSectionProps = {
|
||||
order: HttpTypes.AdminOrder
|
||||
@@ -321,15 +321,34 @@ const Total = ({
|
||||
currencyCode: string
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
const totalPending = getTotalPending(paymentCollections)
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{t("orders.payment.totalPaidByCustomer")}
|
||||
</Text>
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{getStylizedAmount(getTotalCaptured(paymentCollections), currencyCode)}
|
||||
</Text>
|
||||
<div>
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{t("orders.payment.totalPaidByCustomer")}
|
||||
</Text>
|
||||
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{getStylizedAmount(
|
||||
getTotalCaptured(paymentCollections),
|
||||
currencyCode
|
||||
)}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{totalPending > 0 && (
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
Total pending
|
||||
</Text>
|
||||
|
||||
<Text size="small" weight="plus" leading="compact">
|
||||
{getStylizedAmount(totalPending, currencyCode)}
|
||||
</Text>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
+54
-34
@@ -45,6 +45,7 @@ import {
|
||||
} from "../../../../../lib/money-amount-helpers"
|
||||
import { getTotalCaptured } from "../../../../../lib/payment.ts"
|
||||
import { getReturnableQuantity } from "../../../../../lib/rma.ts"
|
||||
import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link.tsx"
|
||||
|
||||
type OrderSummarySectionProps = {
|
||||
order: AdminOrder
|
||||
@@ -54,9 +55,12 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
|
||||
const { t } = useTranslation()
|
||||
const navigate = useNavigate()
|
||||
|
||||
const { reservations } = useReservationItems({
|
||||
line_item_id: order.items.map((i) => i.id),
|
||||
})
|
||||
const { reservations } = useReservationItems(
|
||||
{
|
||||
line_item_id: order?.items?.map((i) => i.id),
|
||||
},
|
||||
{ enabled: Array.isArray(order?.items) }
|
||||
)
|
||||
|
||||
const { order: orderPreview } = useOrderPreview(order.id!)
|
||||
|
||||
@@ -96,6 +100,20 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
|
||||
return false
|
||||
}, [reservations])
|
||||
|
||||
// TODO: We need a way to link payment collections to a change order to
|
||||
// accurately differentiate order payments and order change payments
|
||||
// This fix should be temporary.
|
||||
const authorizedPaymentCollection = order.payment_collections.find(
|
||||
(pc) =>
|
||||
pc.status === "authorized" &&
|
||||
pc.amount === order.summary?.pending_difference
|
||||
)
|
||||
|
||||
const showPayment =
|
||||
typeof authorizedPaymentCollection === "undefined" &&
|
||||
(order?.summary?.pending_difference || 0) > 0
|
||||
const showRefund = (order?.summary?.pending_difference || 0) < 0
|
||||
|
||||
return (
|
||||
<Container className="divide-y divide-dashed p-0">
|
||||
<Header order={order} orderPreview={orderPreview} />
|
||||
@@ -103,38 +121,40 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
|
||||
<CostBreakdown order={order} />
|
||||
<Total order={order} />
|
||||
|
||||
{showAllocateButton ||
|
||||
(showReturns && (
|
||||
<div className="bg-ui-bg-subtle flex items-center justify-end rounded-b-xl px-4 py-4">
|
||||
{showReturns && (
|
||||
<ButtonMenu
|
||||
groups={[
|
||||
{
|
||||
actions: returns.map((r) => ({
|
||||
label: t("orders.returns.receive.receive", {
|
||||
label: `#${r.id.slice(-7)}`,
|
||||
}),
|
||||
icon: <ArrowLongRight />,
|
||||
to: `/orders/${order.id}/returns/${r.id}/receive`,
|
||||
})),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Button variant="secondary" size="small">
|
||||
{t("orders.returns.receive.action")}
|
||||
</Button>
|
||||
</ButtonMenu>
|
||||
)}
|
||||
{showAllocateButton && (
|
||||
<Button
|
||||
onClick={() => navigate(`./allocate-items`)}
|
||||
variant="secondary"
|
||||
>
|
||||
{t("orders.allocateItems.action")}
|
||||
{(showAllocateButton || showReturns || showPayment) && (
|
||||
<div className="bg-ui-bg-subtle flex items-center justify-end rounded-b-xl px-4 py-4 gap-x-2">
|
||||
{showReturns && (
|
||||
<ButtonMenu
|
||||
groups={[
|
||||
{
|
||||
actions: returns.map((r) => ({
|
||||
label: t("orders.returns.receive.receive", {
|
||||
label: `#${r.id.slice(-7)}`,
|
||||
}),
|
||||
icon: <ArrowLongRight />,
|
||||
to: `/orders/${order.id}/returns/${r.id}/receive`,
|
||||
})),
|
||||
},
|
||||
]}
|
||||
>
|
||||
<Button variant="secondary" size="small">
|
||||
{t("orders.returns.receive.action")}
|
||||
</Button>
|
||||
)}
|
||||
</div>
|
||||
))}
|
||||
</ButtonMenu>
|
||||
)}
|
||||
|
||||
{showAllocateButton && (
|
||||
<Button
|
||||
onClick={() => navigate(`./allocate-items`)}
|
||||
variant="secondary"
|
||||
>
|
||||
{t("orders.allocateItems.action")}
|
||||
</Button>
|
||||
)}
|
||||
|
||||
{showPayment && <CopyPaymentLink order={order} />}
|
||||
</div>
|
||||
)}
|
||||
</Container>
|
||||
)
|
||||
}
|
||||
|
||||
+9
-9
@@ -1,18 +1,16 @@
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { AdminOrder, AdminReturn } from "@medusajs/types"
|
||||
import { Alert, Button, Input, Switch, Text, toast } from "@medusajs/ui"
|
||||
import React, { useEffect, useMemo } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { zodResolver } from "@hookform/resolvers/zod"
|
||||
import { ArrrowRight } from "@medusajs/icons"
|
||||
import { AdminOrder, AdminReturn } from "@medusajs/types"
|
||||
import { Alert, Button, Input, Switch, Text, toast } from "@medusajs/ui"
|
||||
import { useEffect, useMemo } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import * as zod from "zod"
|
||||
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import { Thumbnail } from "../../../../../components/common/thumbnail"
|
||||
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
||||
import { useStockLocation } from "../../../../../hooks/api"
|
||||
import { ReceiveReturnSchema } from "./constants"
|
||||
import { Form } from "../../../../../components/common/form"
|
||||
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
|
||||
import {
|
||||
useAddReceiveItems,
|
||||
useCancelReceiveReturn,
|
||||
@@ -20,6 +18,8 @@ import {
|
||||
useRemoveReceiveItems,
|
||||
useUpdateReceiveItem,
|
||||
} from "../../../../../hooks/api/returns"
|
||||
import { getStylizedAmount } from "../../../../../lib/money-amount-helpers"
|
||||
import { ReceiveReturnSchema } from "./constants"
|
||||
import WrittenOffQuantity from "./written-off-quantity"
|
||||
|
||||
type OrderAllocateItemsFormProps = {
|
||||
@@ -318,7 +318,7 @@ export function OrderReceiveReturnForm({
|
||||
</span>
|
||||
<span className="txt-small font-medium">
|
||||
{getStylizedAmount(
|
||||
preview.summary.difference_sum || 0,
|
||||
preview.summary.pending_difference || 0,
|
||||
order.currency_code
|
||||
)}
|
||||
</span>
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
|
||||
interface ImportMetaEnv {
|
||||
readonly VITE_MEDUSA_ADMIN_BACKEND_URL: string
|
||||
readonly VITE_MEDUSA_STOREFRONT_URL: string
|
||||
readonly VITE_MEDUSA_V2: "true" | "false"
|
||||
}
|
||||
|
||||
@@ -10,4 +11,5 @@ interface ImportMeta {
|
||||
}
|
||||
|
||||
declare const __BACKEND_URL__: string | undefined
|
||||
declare const __STOREFRONT_URL__: string | undefined
|
||||
declare const __BASE__: string
|
||||
|
||||
Reference in New Issue
Block a user