feat(dashboard, js-sdk): customer page transfer order + cancel request in timeline (#10250)
**What** - request order transfer from admin customers details page - cancel transfer request from order timeline --- CLOSES CMRC-730
This commit is contained in:
1 parent
344a6c9ea0
commit
1bf60c7a7d
15 files changed
+126
-379
No files matched your search
@@ -1 +0,0 @@
|
|||||||
export * from "./transfer-ownership-form"
|
|
||||||
-264
@@ -1,264 +0,0 @@
|
|||||||
import { Select, Text, clx } from "@medusajs/ui"
|
|
||||||
import { useInfiniteQuery } from "@tanstack/react-query"
|
|
||||||
import { format } from "date-fns"
|
|
||||||
import { debounce } from "lodash"
|
|
||||||
import { PropsWithChildren, useCallback, useEffect, useState } from "react"
|
|
||||||
import { Control, useWatch } from "react-hook-form"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { z } from "zod"
|
|
||||||
|
|
||||||
import { useCustomer } from "../../../hooks/api/customers"
|
|
||||||
import { sdk } from "../../../lib/client"
|
|
||||||
import { getStylizedAmount } from "../../../lib/money-amount-helpers"
|
|
||||||
import {
|
|
||||||
getOrderFulfillmentStatus,
|
|
||||||
getOrderPaymentStatus,
|
|
||||||
} from "../../../lib/order-helpers"
|
|
||||||
import { TransferOwnershipSchema } from "../../../lib/schemas"
|
|
||||||
import { Form } from "../../common/form"
|
|
||||||
import { Skeleton } from "../../common/skeleton"
|
|
||||||
import { Combobox } from "../../inputs/combobox"
|
|
||||||
import { HttpTypes } from "@medusajs/types"
|
|
||||||
|
|
||||||
type TransferOwnerShipFieldValues = z.infer<typeof TransferOwnershipSchema>
|
|
||||||
|
|
||||||
type TransferOwnerShipFormProps = {
|
|
||||||
/**
|
|
||||||
* The Order or DraftOrder to transfer ownership of.
|
|
||||||
*/
|
|
||||||
order: HttpTypes.AdminOrder
|
|
||||||
/**
|
|
||||||
* React Hook Form control object.
|
|
||||||
*/
|
|
||||||
control: Control<TransferOwnerShipFieldValues>
|
|
||||||
}
|
|
||||||
|
|
||||||
const isOrder = (
|
|
||||||
order: HttpTypes.AdminOrder
|
|
||||||
): order is HttpTypes.AdminOrder => {
|
|
||||||
return "customer" in order
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TransferOwnerShipForm = ({
|
|
||||||
order,
|
|
||||||
control,
|
|
||||||
}: TransferOwnerShipFormProps) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const [query, setQuery] = useState("")
|
|
||||||
const [debouncedQuery, setDebouncedQuery] = useState("")
|
|
||||||
|
|
||||||
const isOrderType = isOrder(order)
|
|
||||||
const currentOwnerId = useWatch({
|
|
||||||
control,
|
|
||||||
name: "current_owner_id",
|
|
||||||
})
|
|
||||||
|
|
||||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
||||||
const debouncedUpdate = useCallback(
|
|
||||||
debounce((query) => setDebouncedQuery(query), 300),
|
|
||||||
[]
|
|
||||||
)
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
debouncedUpdate(query)
|
|
||||||
|
|
||||||
return () => debouncedUpdate.cancel()
|
|
||||||
}, [query, debouncedUpdate])
|
|
||||||
|
|
||||||
const {
|
|
||||||
customer: owner,
|
|
||||||
isLoading: isLoadingOwner,
|
|
||||||
isError: isOwnerError,
|
|
||||||
error: ownerError,
|
|
||||||
} = useCustomer(currentOwnerId)
|
|
||||||
|
|
||||||
const { data, fetchNextPage, isFetchingNextPage } = useInfiniteQuery({
|
|
||||||
queryKey: ["customers", debouncedQuery],
|
|
||||||
queryFn: async ({ pageParam = 0 }) => {
|
|
||||||
const res = await sdk.admin.customer.list({
|
|
||||||
q: debouncedQuery,
|
|
||||||
limit: 10,
|
|
||||||
offset: pageParam,
|
|
||||||
has_account: true, // Only show customers with confirmed accounts
|
|
||||||
})
|
|
||||||
return res
|
|
||||||
},
|
|
||||||
initialPageParam: 0,
|
|
||||||
getNextPageParam: (lastPage) => {
|
|
||||||
const moreCustomersExist =
|
|
||||||
lastPage.count > lastPage.offset + lastPage.limit
|
|
||||||
return moreCustomersExist ? lastPage.offset + lastPage.limit : undefined
|
|
||||||
},
|
|
||||||
})
|
|
||||||
|
|
||||||
const createLabel = (customer?: HttpTypes.AdminCustomer) => {
|
|
||||||
if (!customer) {
|
|
||||||
return ""
|
|
||||||
}
|
|
||||||
|
|
||||||
const { first_name, last_name, email } = customer
|
|
||||||
|
|
||||||
const name = [first_name, last_name].filter(Boolean).join(" ")
|
|
||||||
|
|
||||||
if (name) {
|
|
||||||
return `${name} (${email})`
|
|
||||||
}
|
|
||||||
|
|
||||||
return email
|
|
||||||
}
|
|
||||||
|
|
||||||
const ownerReady = !isLoadingOwner && owner
|
|
||||||
|
|
||||||
const options =
|
|
||||||
data?.pages
|
|
||||||
.map((p) =>
|
|
||||||
p.customers.map((c) => ({
|
|
||||||
label: createLabel(c),
|
|
||||||
value: c.id,
|
|
||||||
}))
|
|
||||||
)
|
|
||||||
.flat() || []
|
|
||||||
|
|
||||||
if (isOwnerError) {
|
|
||||||
throw ownerError
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<div className="flex flex-col gap-y-8">
|
|
||||||
<div className="flex flex-col gap-y-2">
|
|
||||||
<Text size="small" leading="compact" weight="plus">
|
|
||||||
{isOrderType
|
|
||||||
? t("transferOwnership.details.order")
|
|
||||||
: t("transferOwnership.details.draft")}
|
|
||||||
</Text>
|
|
||||||
{isOrderType ? (
|
|
||||||
<OrderDetailsTable order={order} />
|
|
||||||
) : (
|
|
||||||
<DraftOrderDetailsTable draft={order} />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<div className="flex flex-col gap-y-2">
|
|
||||||
<div>
|
|
||||||
<Text size="small" leading="compact" weight="plus">
|
|
||||||
{t("transferOwnership.currentOwner.label")}
|
|
||||||
</Text>
|
|
||||||
<Text size="small" className="text-ui-fg-subtle">
|
|
||||||
{t("transferOwnership.currentOwner.hint")}
|
|
||||||
</Text>
|
|
||||||
</div>
|
|
||||||
{ownerReady ? (
|
|
||||||
<Select defaultValue={owner.id} disabled>
|
|
||||||
<Select.Trigger>
|
|
||||||
<Select.Value />
|
|
||||||
</Select.Trigger>
|
|
||||||
<Select.Content>
|
|
||||||
<Select.Item value={owner.id}>{createLabel(owner)}</Select.Item>
|
|
||||||
</Select.Content>
|
|
||||||
</Select>
|
|
||||||
) : (
|
|
||||||
<Skeleton className="h-8 w-full rounded-md" />
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
<Form.Field
|
|
||||||
control={control}
|
|
||||||
name="new_owner_id"
|
|
||||||
render={({ field }) => {
|
|
||||||
return (
|
|
||||||
<Form.Item>
|
|
||||||
<div className="flex flex-col">
|
|
||||||
<Form.Label>{t("transferOwnership.newOwner.label")}</Form.Label>
|
|
||||||
<Form.Hint>{t("transferOwnership.newOwner.hint")}</Form.Hint>
|
|
||||||
</div>
|
|
||||||
<Form.Control>
|
|
||||||
<Combobox
|
|
||||||
{...field}
|
|
||||||
searchValue={query}
|
|
||||||
onSearchValueChange={setQuery}
|
|
||||||
fetchNextPage={fetchNextPage}
|
|
||||||
isFetchingNextPage={isFetchingNextPage}
|
|
||||||
options={options}
|
|
||||||
/>
|
|
||||||
</Form.Control>
|
|
||||||
<Form.ErrorMessage />
|
|
||||||
</Form.Item>
|
|
||||||
)
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const OrderDetailsTable = ({ order }: { order: HttpTypes.AdminOrder }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const { label: fulfillmentLabel } = getOrderFulfillmentStatus(
|
|
||||||
t,
|
|
||||||
order.fulfillment_status
|
|
||||||
)
|
|
||||||
|
|
||||||
const { label: paymentLabel } = getOrderPaymentStatus(t, order.payment_status)
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table>
|
|
||||||
<Row label={t("fields.order")} value={`#${order.display_id}`} />
|
|
||||||
<DateRow date={order.created_at} />
|
|
||||||
<Row label={t("fields.fulfillment")} value={fulfillmentLabel} />
|
|
||||||
<Row label={t("fields.payment")} value={paymentLabel} />
|
|
||||||
<TotalRow total={order.total || 0} currencyCode={order.currency_code} />
|
|
||||||
</Table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: Create type for Draft Order when we have it
|
|
||||||
const DraftOrderDetailsTable = ({ draft }: { draft: HttpTypes.AdminOrder }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
return (
|
|
||||||
<Table>
|
|
||||||
<Row label={t("fields.draft")} value={`#${draft.display_id}`} />
|
|
||||||
<DateRow date={draft.created_at} />
|
|
||||||
<Row
|
|
||||||
label={t("fields.status")}
|
|
||||||
value={t(`draftOrders.status.${draft.status}`)}
|
|
||||||
/>
|
|
||||||
|
|
||||||
{/* TODO: This will likely change. We don't use carts for draft orders any longer. */}
|
|
||||||
{/* <TotalRow
|
|
||||||
total={draft.cart.total || 0}
|
|
||||||
currencyCode={draft.cart.region.currency_code}
|
|
||||||
/> */}
|
|
||||||
</Table>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const DateRow = ({ date }: { date: string | Date }) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
|
|
||||||
const formattedDate = format(new Date(date), "dd MMM yyyy")
|
|
||||||
|
|
||||||
return <Row label={t("fields.date")} value={formattedDate} />
|
|
||||||
}
|
|
||||||
|
|
||||||
const TotalRow = ({
|
|
||||||
total,
|
|
||||||
currencyCode,
|
|
||||||
}: {
|
|
||||||
total: number
|
|
||||||
currencyCode: string
|
|
||||||
}) => {
|
|
||||||
return <Row label="Total" value={getStylizedAmount(total, currencyCode)} />
|
|
||||||
}
|
|
||||||
|
|
||||||
const Row = ({ label, value }: { label: string; value: string }) => {
|
|
||||||
return (
|
|
||||||
<div className="txt-compact-small grid grid-cols-2 divide-x">
|
|
||||||
<div className="text-ui-fg-muted px-2 py-1.5">{label}</div>
|
|
||||||
<div className="text-ui-fg-subtle px-2 py-1.5">{value}</div>
|
|
||||||
</div>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
const Table = ({ children }: PropsWithChildren) => {
|
|
||||||
return <div className={clx("divide-y rounded-lg border")}>{children}</div>
|
|
||||||
}
|
|
||||||
@@ -286,6 +286,31 @@ export const useRequestTransferOrder = (
|
|||||||
queryKey: ordersQueryKeys.preview(orderId),
|
queryKey: ordersQueryKeys.preview(orderId),
|
||||||
})
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.changes(orderId),
|
||||||
|
})
|
||||||
|
|
||||||
|
options?.onSuccess?.(data, variables, context)
|
||||||
|
},
|
||||||
|
...options,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const useCancelOrderTransfer = (
|
||||||
|
orderId: string,
|
||||||
|
options?: UseMutationOptions<any, FetchError, void>
|
||||||
|
) => {
|
||||||
|
return useMutation({
|
||||||
|
mutationFn: () => sdk.admin.order.cancelTransfer(orderId),
|
||||||
|
onSuccess: (data: any, variables: any, context: any) => {
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.preview(orderId),
|
||||||
|
})
|
||||||
|
|
||||||
|
queryClient.invalidateQueries({
|
||||||
|
queryKey: ordersQueryKeys.changes(orderId),
|
||||||
|
})
|
||||||
|
|
||||||
options?.onSuccess?.(data, variables, context)
|
options?.onSuccess?.(data, variables, context)
|
||||||
},
|
},
|
||||||
...options,
|
...options,
|
||||||
|
|||||||
@@ -515,6 +515,9 @@
|
|||||||
},
|
},
|
||||||
"import": {
|
"import": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"cannotUndo": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
@@ -558,7 +561,8 @@
|
|||||||
"logout",
|
"logout",
|
||||||
"hide",
|
"hide",
|
||||||
"export",
|
"export",
|
||||||
"import"
|
"import",
|
||||||
|
"cannotUndo"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
},
|
},
|
||||||
@@ -5327,11 +5331,15 @@
|
|||||||
},
|
},
|
||||||
"confirmed": {
|
"confirmed": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
|
},
|
||||||
|
"declined": {
|
||||||
|
"type": "string"
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"required": [
|
"required": [
|
||||||
"requested",
|
"requested",
|
||||||
"confirmed"
|
"confirmed",
|
||||||
|
"declined"
|
||||||
],
|
],
|
||||||
"additionalProperties": false
|
"additionalProperties": false
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -136,7 +136,8 @@
|
|||||||
"logout": "Logout",
|
"logout": "Logout",
|
||||||
"hide": "Hide",
|
"hide": "Hide",
|
||||||
"export": "Export",
|
"export": "Export",
|
||||||
"import": "Import"
|
"import": "Import",
|
||||||
|
"cannotUndo": "This action cannot be undone"
|
||||||
},
|
},
|
||||||
"operators": {
|
"operators": {
|
||||||
"in": "In"
|
"in": "In"
|
||||||
@@ -1293,7 +1294,8 @@
|
|||||||
},
|
},
|
||||||
"transfer": {
|
"transfer": {
|
||||||
"requested": "Order transfer #{{transferId}} requested",
|
"requested": "Order transfer #{{transferId}} requested",
|
||||||
"confirmed": "Order transfer #{{transferId}} confirmed"
|
"confirmed": "Order transfer #{{transferId}} confirmed",
|
||||||
|
"declined": "Order transfer #{{transferId}} declined"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -603,6 +603,11 @@ export const RouteMap: RouteObject[] = [
|
|||||||
"../../routes/customers/customers-add-customer-group"
|
"../../routes/customers/customers-add-customer-group"
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
path: ":order_id/transfer",
|
||||||
|
lazy: () =>
|
||||||
|
import("../../routes/orders/order-request-transfer"),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
path: "metadata/edit",
|
path: "metadata/edit",
|
||||||
lazy: () =>
|
lazy: () =>
|
||||||
|
|||||||
+4
-5
@@ -120,11 +120,10 @@ const useColumns = () => {
|
|||||||
return useMemo(
|
return useMemo(
|
||||||
() => [
|
() => [
|
||||||
...base,
|
...base,
|
||||||
// TODO: REENABLE WHEN TRANSFER OWNERSHIP IS IMPLEMENTED
|
columnHelper.display({
|
||||||
// columnHelper.display({
|
id: "actions",
|
||||||
// id: "actions",
|
cell: ({ row }) => <CustomerOrderActions order={row.original} />,
|
||||||
// cell: ({ row }) => <CustomerOrderActions order={row.original} />,
|
}),
|
||||||
// }),
|
|
||||||
],
|
],
|
||||||
[base]
|
[base]
|
||||||
)
|
)
|
||||||
|
|||||||
-1
@@ -1 +0,0 @@
|
|||||||
export * from "./transfer-customer-order-ownership-form"
|
|
||||||
-72
@@ -1,72 +0,0 @@
|
|||||||
import { zodResolver } from "@hookform/resolvers/zod"
|
|
||||||
import { HttpTypes } from "@medusajs/types"
|
|
||||||
import { Button } from "@medusajs/ui"
|
|
||||||
import { useForm } from "react-hook-form"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { z } from "zod"
|
|
||||||
import { TransferOwnerShipForm } from "../../../../../components/forms/transfer-ownership-form"
|
|
||||||
import { RouteDrawer, useRouteModal } from "../../../../../components/modals"
|
|
||||||
import { KeyboundForm } from "../../../../../components/utilities/keybound-form"
|
|
||||||
import { TransferOwnershipSchema } from "../../../../../lib/schemas"
|
|
||||||
|
|
||||||
type TransferCustomerOrderOwnershipFormProps = {
|
|
||||||
order: HttpTypes.AdminOrder
|
|
||||||
}
|
|
||||||
|
|
||||||
export const TransferCustomerOrderOwnershipForm = ({
|
|
||||||
order,
|
|
||||||
}: TransferCustomerOrderOwnershipFormProps) => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { handleSuccess } = useRouteModal()
|
|
||||||
|
|
||||||
const form = useForm<z.infer<typeof TransferOwnershipSchema>>({
|
|
||||||
defaultValues: {
|
|
||||||
current_owner_id: order.customer_id ?? undefined,
|
|
||||||
new_owner_id: "",
|
|
||||||
},
|
|
||||||
resolver: zodResolver(TransferOwnershipSchema),
|
|
||||||
})
|
|
||||||
|
|
||||||
const { mutateAsync, isLoading } = {
|
|
||||||
mutateAsync: async (args: any) => {},
|
|
||||||
isLoading: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
const handleSubmit = form.handleSubmit(async (values) => {
|
|
||||||
mutateAsync(
|
|
||||||
{
|
|
||||||
customer_id: values.new_owner_id,
|
|
||||||
}
|
|
||||||
// {
|
|
||||||
// onSuccess: () => {
|
|
||||||
// handleSuccess()
|
|
||||||
// },
|
|
||||||
// }
|
|
||||||
)
|
|
||||||
})
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RouteDrawer.Form form={form}>
|
|
||||||
<KeyboundForm
|
|
||||||
onSubmit={handleSubmit}
|
|
||||||
className="flex size-full flex-col overflow-hidden"
|
|
||||||
>
|
|
||||||
<RouteDrawer.Body className="size-full flex-1 overflow-auto">
|
|
||||||
<TransferOwnerShipForm order={order} control={form.control} />
|
|
||||||
</RouteDrawer.Body>
|
|
||||||
<RouteDrawer.Footer>
|
|
||||||
<div className="flex items-center justify-end gap-x-2">
|
|
||||||
<RouteDrawer.Close asChild>
|
|
||||||
<Button variant="secondary" size="small">
|
|
||||||
{t("actions.cancel")}
|
|
||||||
</Button>
|
|
||||||
</RouteDrawer.Close>
|
|
||||||
<Button type="submit" isLoading={isLoading} size="small">
|
|
||||||
{t("actions.save")}
|
|
||||||
</Button>
|
|
||||||
</div>
|
|
||||||
</RouteDrawer.Footer>
|
|
||||||
</KeyboundForm>
|
|
||||||
</RouteDrawer.Form>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
-29
@@ -1,29 +0,0 @@
|
|||||||
import { Heading } from "@medusajs/ui"
|
|
||||||
import { useTranslation } from "react-i18next"
|
|
||||||
import { useParams } from "react-router-dom"
|
|
||||||
|
|
||||||
import { RouteDrawer } from "../../../components/modals"
|
|
||||||
import { useOrder } from "../../../hooks/api/orders"
|
|
||||||
import { TransferCustomerOrderOwnershipForm } from "./components/transfer-customer-order-ownership-form"
|
|
||||||
|
|
||||||
export const CustomerTransferOwnership = () => {
|
|
||||||
const { t } = useTranslation()
|
|
||||||
const { order_id } = useParams()
|
|
||||||
|
|
||||||
const { order, isLoading, isError, error } = useOrder(order_id!)
|
|
||||||
|
|
||||||
const ready = !isLoading && order
|
|
||||||
|
|
||||||
if (isError) {
|
|
||||||
throw error
|
|
||||||
}
|
|
||||||
|
|
||||||
return (
|
|
||||||
<RouteDrawer>
|
|
||||||
<RouteDrawer.Header>
|
|
||||||
<Heading>{t("transferOwnership.header")}</Heading>
|
|
||||||
</RouteDrawer.Header>
|
|
||||||
{ready && <TransferCustomerOrderOwnershipForm order={order} />}
|
|
||||||
</RouteDrawer>
|
|
||||||
)
|
|
||||||
}
|
|
||||||
@@ -1 +0,0 @@
|
|||||||
export { CustomerTransferOwnership as Component } from "./customer-transfer-ownership"
|
|
||||||
+41
@@ -17,6 +17,7 @@ import { useTranslation } from "react-i18next"
|
|||||||
|
|
||||||
import { AdminOrderLineItem } from "@medusajs/types"
|
import { AdminOrderLineItem } from "@medusajs/types"
|
||||||
import {
|
import {
|
||||||
|
useCancelOrderTransfer,
|
||||||
useCustomer,
|
useCustomer,
|
||||||
useOrderChanges,
|
useOrderChanges,
|
||||||
useOrderLineItems,
|
useOrderLineItems,
|
||||||
@@ -407,6 +408,14 @@ const useActivityItems = (order: AdminOrder): Activity[] => {
|
|||||||
timestamp: transfer.confirmed_at,
|
timestamp: transfer.confirmed_at,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
if (transfer.declined_at) {
|
||||||
|
items.push({
|
||||||
|
title: t(`orders.activity.events.transfer.declined`, {
|
||||||
|
transferId: transfer.id.slice(-7),
|
||||||
|
}),
|
||||||
|
timestamp: transfer.declined_at,
|
||||||
|
})
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// for (const note of notes || []) {
|
// for (const note of notes || []) {
|
||||||
@@ -896,11 +905,33 @@ const TransferOrderRequestBody = ({
|
|||||||
}: {
|
}: {
|
||||||
transfer: AdminOrderChange
|
transfer: AdminOrderChange
|
||||||
}) => {
|
}) => {
|
||||||
|
const prompt = usePrompt()
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
|
|
||||||
const action = transfer.actions[0]
|
const action = transfer.actions[0]
|
||||||
const { customer } = useCustomer(action.reference_id)
|
const { customer } = useCustomer(action.reference_id)
|
||||||
|
|
||||||
|
const isCompleted = !!transfer.confirmed_at
|
||||||
|
|
||||||
|
const { mutateAsync: cancelTransfer } = useCancelOrderTransfer(
|
||||||
|
transfer.order_id
|
||||||
|
)
|
||||||
|
|
||||||
|
const handleDelete = async () => {
|
||||||
|
const res = await prompt({
|
||||||
|
title: t("general.areYouSure"),
|
||||||
|
description: t("actions.cannotUndo"),
|
||||||
|
confirmText: t("actions.delete"),
|
||||||
|
cancelText: t("actions.cancel"),
|
||||||
|
})
|
||||||
|
|
||||||
|
if (!res) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
await cancelTransfer()
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* TODO: change original_email to customer info when action details is changed
|
* TODO: change original_email to customer info when action details is changed
|
||||||
*/
|
*/
|
||||||
@@ -917,6 +948,16 @@ const TransferOrderRequestBody = ({
|
|||||||
? `${customer?.first_name} ${customer?.last_name}`
|
? `${customer?.first_name} ${customer?.last_name}`
|
||||||
: customer?.email}
|
: customer?.email}
|
||||||
</Text>
|
</Text>
|
||||||
|
{!isCompleted && (
|
||||||
|
<Button
|
||||||
|
onClick={handleDelete}
|
||||||
|
className="text-ui-fg-subtle h-auto px-0 leading-none hover:bg-transparent"
|
||||||
|
variant="transparent"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{t("actions.cancel")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -72,7 +72,9 @@ export function CreateOrderTransferForm({
|
|||||||
>
|
>
|
||||||
<RouteDrawer.Body className="flex-1 overflow-auto">
|
<RouteDrawer.Body className="flex-1 overflow-auto">
|
||||||
<div className="flex flex-col gap-y-8">
|
<div className="flex flex-col gap-y-8">
|
||||||
|
<div className="flex justify-center">
|
||||||
<TransferHeader />
|
<TransferHeader />
|
||||||
|
</div>
|
||||||
<Form.Field
|
<Form.Field
|
||||||
control={form.control}
|
control={form.control}
|
||||||
name="current_customer_details"
|
name="current_customer_details"
|
||||||
|
|||||||
+10
-2
@@ -10,17 +10,25 @@ import { CreateOrderTransferForm } from "./components/create-order-transfer-form
|
|||||||
export const OrderRequestTransfer = () => {
|
export const OrderRequestTransfer = () => {
|
||||||
const { t } = useTranslation()
|
const { t } = useTranslation()
|
||||||
const params = useParams()
|
const params = useParams()
|
||||||
const { order } = useOrder(params.id!, {
|
|
||||||
|
// Form is rendered bot on the order details page and customer page so we need to pick the correct param from URL
|
||||||
|
const orderId = (params.order_id || params.id) as string
|
||||||
|
|
||||||
|
const { order, isPending, isError } = useOrder(orderId, {
|
||||||
fields: DEFAULT_FIELDS,
|
fields: DEFAULT_FIELDS,
|
||||||
})
|
})
|
||||||
|
|
||||||
|
if (!isPending && isError) {
|
||||||
|
throw new Error("Order not found")
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<RouteDrawer>
|
<RouteDrawer>
|
||||||
<RouteDrawer.Header>
|
<RouteDrawer.Header>
|
||||||
<Heading>{t("orders.transfer.title")}</Heading>
|
<Heading>{t("orders.transfer.title")}</Heading>
|
||||||
</RouteDrawer.Header>
|
</RouteDrawer.Header>
|
||||||
|
|
||||||
<CreateOrderTransferForm order={order} />
|
{order && <CreateOrderTransferForm order={order} />}
|
||||||
</RouteDrawer>
|
</RouteDrawer>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@@ -211,6 +211,31 @@ export class Order {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* This method cancels an order transfer request. It sends a request to the
|
||||||
|
* [Cancel Order Transfer Request](https://docs.medusajs.com/api/admin#orders_postordersidcanceltransferrequest)
|
||||||
|
* API route.
|
||||||
|
*
|
||||||
|
* @param id - The order's ID.
|
||||||
|
* @param headers - Headers to pass in the request.
|
||||||
|
* @returns The order's details.
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* sdk.admin.order.cancelTransfer("order_123")
|
||||||
|
* .then(({ order }) => {
|
||||||
|
* console.log(order)
|
||||||
|
* })
|
||||||
|
*/
|
||||||
|
async cancelTransfer(id: string, headers?: ClientHeaders) {
|
||||||
|
return await this.client.fetch<HttpTypes.AdminOrderResponse>(
|
||||||
|
`/admin/orders/${id}/transfer/cancel`,
|
||||||
|
{
|
||||||
|
method: "POST",
|
||||||
|
headers,
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This method creates a fulfillment for an order. It sends a request to the
|
* This method creates a fulfillment for an order. It sends a request to the
|
||||||
* [Create Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillments)
|
* [Create Fulfillment](https://docs.medusajs.com/api/admin#orders_postordersidfulfillments)
|
||||||
|
|||||||
Reference in new issue
Block a user