feat(dashboard,types): add active order change panel - claims, exchanges & returns (#8738)
* feat(dashboard,types): add active order change panel - claims, exchanges & returns * Apply suggestions from code review
This commit is contained in:
@@ -912,6 +912,14 @@
|
||||
"errorNegativeValue": "Quantity cannot be a negative value.",
|
||||
"errorLargeDamagedValue": "Damaged items quantity + non damaged received quantity exceeds total item quantity on the return. Please decrease quantity of non damaged items."
|
||||
}
|
||||
},
|
||||
"toast": {
|
||||
"canceledSuccessfully": "Return canceled successfully",
|
||||
"confirmedSuccessfully": "Return confirmed successfully"
|
||||
},
|
||||
"panel": {
|
||||
"title": "Return initiated",
|
||||
"description": "There is an open return request to be completed"
|
||||
}
|
||||
},
|
||||
"claims": {
|
||||
@@ -931,6 +939,14 @@
|
||||
},
|
||||
"tooltips": {
|
||||
"onlyReturnShippingOptions": "This list will consist of only return shipping options."
|
||||
},
|
||||
"toast": {
|
||||
"canceledSuccessfully": "Claim canceled successfully",
|
||||
"confirmedSuccessfully": "Claim confirmed successfully"
|
||||
},
|
||||
"panel": {
|
||||
"title": "Claim initiated",
|
||||
"description": "There is an open claim request to be completed"
|
||||
}
|
||||
},
|
||||
"exchanges": {
|
||||
@@ -950,6 +966,14 @@
|
||||
},
|
||||
"tooltips": {
|
||||
"onlyReturnShippingOptions": "This list will consist of only return shipping options."
|
||||
},
|
||||
"toast": {
|
||||
"canceledSuccessfully": "Exchange canceled successfully",
|
||||
"confirmedSuccessfully": "Exchange confirmed successfully"
|
||||
},
|
||||
"panel": {
|
||||
"title": "Exchange initiated",
|
||||
"description": "There is an open exchange request to be completed"
|
||||
}
|
||||
},
|
||||
"reservations": {
|
||||
|
||||
+12
-8
@@ -307,15 +307,19 @@ export const ClaimCreateForm = ({
|
||||
const shippingOptionId = form.watch("inbound_option_id")
|
||||
|
||||
const handleSubmit = form.handleSubmit(async (data) => {
|
||||
try {
|
||||
await confirmClaimRequest({ no_notification: !data.send_notification })
|
||||
await confirmClaimRequest(
|
||||
{ no_notification: !data.send_notification },
|
||||
{
|
||||
onSuccess: () => {
|
||||
toast.success(t("orders.claims.toast.confirmedSuccessfully"))
|
||||
|
||||
handleSuccess()
|
||||
} catch (e) {
|
||||
toast.error(t("general.error"), {
|
||||
description: e.message,
|
||||
})
|
||||
}
|
||||
handleSuccess()
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
}
|
||||
)
|
||||
})
|
||||
|
||||
const onItemsSelected = async () => {
|
||||
|
||||
+79
@@ -0,0 +1,79 @@
|
||||
import { ExclamationCircle } from "@medusajs/icons"
|
||||
import { Button, Container, Heading, Text, toast } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useCancelClaimRequest } from "../../../../../hooks/api/claims"
|
||||
|
||||
type ActiveOrderClaimSectionProps = {
|
||||
orderPreview: HttpTypes.AdminOrderPreview
|
||||
}
|
||||
|
||||
export const ActiveOrderClaimSection = ({
|
||||
orderPreview,
|
||||
}: ActiveOrderClaimSectionProps) => {
|
||||
const { t } = useTranslation()
|
||||
const claimId = orderPreview?.order_change?.claim_id
|
||||
|
||||
const { mutateAsync: cancelClaim } = useCancelClaimRequest(
|
||||
claimId,
|
||||
orderPreview.id
|
||||
)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const onContinueClaim = async () => {
|
||||
navigate(`/orders/${orderPreview.id}/claims`)
|
||||
}
|
||||
|
||||
const onCancelClaim = async () => {
|
||||
await cancelClaim(undefined, {
|
||||
onSuccess: () => {
|
||||
toast.success(t("orders.claims.toast.canceledSuccessfully"))
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!claimId) {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background:
|
||||
"repeating-linear-gradient(-45deg, rgb(212, 212, 216, 0.15), rgb(212, 212, 216,.15) 10px, transparent 10px, transparent 20px)",
|
||||
}}
|
||||
className="-m-4 mb-1 border-b p-4"
|
||||
>
|
||||
<Container className="flex items-center justify-between p-0">
|
||||
<div className="flex w-full flex-row divide-y divide-dashed justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 px-6 pt-4 mb-2">
|
||||
<ExclamationCircle className="text-ui-fg-subtle" />
|
||||
<Heading level="h2">{t("orders.claims.panel.title")}</Heading>
|
||||
</div>
|
||||
|
||||
<div className="gap-2 px-6 pb-4">
|
||||
<Text>{t("orders.claims.panel.description")}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-end gap-x-2 rounded-b-xl px-4 py-4">
|
||||
<Button size="small" variant="secondary" onClick={onCancelClaim}>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
|
||||
<Button size="small" variant="secondary" onClick={onContinueClaim}>
|
||||
{t("actions.continue")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./active-order-claim-section"
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
import { ArrowPath } from "@medusajs/icons"
|
||||
import { Button, Container, Heading, Text, toast } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useCancelExchangeRequest } from "../../../../../hooks/api/exchanges"
|
||||
|
||||
type ActiveOrderExchangeSectionProps = {
|
||||
orderPreview: HttpTypes.AdminOrderPreview
|
||||
}
|
||||
|
||||
export const ActiveOrderExchangeSection = ({
|
||||
orderPreview,
|
||||
}: ActiveOrderExchangeSectionProps) => {
|
||||
const { t } = useTranslation()
|
||||
const exchangeId = orderPreview?.order_change?.exchange_id
|
||||
|
||||
const { mutateAsync: cancelExchange } = useCancelExchangeRequest(
|
||||
exchangeId,
|
||||
orderPreview.id
|
||||
)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const onContinueExchange = async () => {
|
||||
navigate(`/orders/${orderPreview.id}/exchanges`)
|
||||
}
|
||||
|
||||
const onCancelExchange = async () => {
|
||||
await cancelExchange(undefined, {
|
||||
onSuccess: () => {
|
||||
toast.success(t("orders.exchanges.toast.canceledSuccessfully"))
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!exchangeId) {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background:
|
||||
"repeating-linear-gradient(-45deg, rgb(212, 212, 216, 0.15), rgb(212, 212, 216,.15) 10px, transparent 10px, transparent 20px)",
|
||||
}}
|
||||
className="-m-4 mb-1 border-b p-4"
|
||||
>
|
||||
<Container className="flex items-center justify-between p-0">
|
||||
<div className="flex w-full flex-row divide-y divide-dashed justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 px-6 pt-4 mb-2">
|
||||
<ArrowPath className="text-ui-fg-subtle" />
|
||||
<Heading level="h2">{t("orders.exchanges.panel.title")}</Heading>
|
||||
</div>
|
||||
|
||||
<div className="gap-2 px-6 pb-4">
|
||||
<Text>{t("orders.exchanges.panel.description")}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-end gap-x-2 rounded-b-xl px-4 py-4">
|
||||
<Button size="small" variant="secondary" onClick={onCancelExchange}>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
size="small"
|
||||
variant="secondary"
|
||||
onClick={onContinueExchange}
|
||||
>
|
||||
{t("actions.continue")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./active-order-exchange-section"
|
||||
+82
@@ -0,0 +1,82 @@
|
||||
import { ArrowUturnLeft } from "@medusajs/icons"
|
||||
import { Button, Container, Heading, Text, toast } from "@medusajs/ui"
|
||||
import { useTranslation } from "react-i18next"
|
||||
|
||||
import { HttpTypes } from "@medusajs/types"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import { useCancelReturnRequest } from "../../../../../hooks/api/returns"
|
||||
|
||||
type ActiveOrderReturnSectionProps = {
|
||||
orderPreview: HttpTypes.AdminOrderPreview
|
||||
}
|
||||
|
||||
export const ActiveOrderReturnSection = ({
|
||||
orderPreview,
|
||||
}: ActiveOrderReturnSectionProps) => {
|
||||
const { t } = useTranslation()
|
||||
const orderChange = orderPreview?.order_change
|
||||
const returnId = orderChange?.return_id
|
||||
const isReturnRequest =
|
||||
orderChange?.change_type === "return_request" && !!orderChange.return_id
|
||||
|
||||
const { mutateAsync: cancelReturn } = useCancelReturnRequest(
|
||||
returnId,
|
||||
orderPreview.id
|
||||
)
|
||||
|
||||
const navigate = useNavigate()
|
||||
|
||||
const onContinueReturn = async () => {
|
||||
navigate(`/orders/${orderPreview.id}/returns`)
|
||||
}
|
||||
|
||||
const onCancelReturn = async () => {
|
||||
await cancelReturn(undefined, {
|
||||
onSuccess: () => {
|
||||
toast.success(t("orders.returns.toast.canceledSuccessfully"))
|
||||
},
|
||||
onError: (error) => {
|
||||
toast.error(error.message)
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
if (!returnId || !isReturnRequest) {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<div
|
||||
style={{
|
||||
background:
|
||||
"repeating-linear-gradient(-45deg, rgb(212, 212, 216, 0.15), rgb(212, 212, 216,.15) 10px, transparent 10px, transparent 20px)",
|
||||
}}
|
||||
className="-m-4 mb-1 border-b p-4"
|
||||
>
|
||||
<Container className="flex items-center justify-between p-0">
|
||||
<div className="flex w-full flex-row divide-y divide-dashed justify-between">
|
||||
<div>
|
||||
<div className="flex items-center gap-2 px-6 pt-4 mb-2">
|
||||
<ArrowUturnLeft className="text-ui-fg-subtle" />
|
||||
<Heading level="h2">{t("orders.returns.panel.title")}</Heading>
|
||||
</div>
|
||||
|
||||
<div className="gap-2 px-6 pb-4">
|
||||
<Text>{t("orders.returns.panel.description")}</Text>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex items-center justify-end gap-x-2 rounded-b-xl px-4 py-4">
|
||||
<Button size="small" variant="secondary" onClick={onCancelReturn}>
|
||||
{t("actions.cancel")}
|
||||
</Button>
|
||||
|
||||
<Button size="small" variant="secondary" onClick={onContinueReturn}>
|
||||
{t("actions.continue")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</Container>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
+1
@@ -0,0 +1 @@
|
||||
export * from "./active-order-return-section"
|
||||
+5
@@ -249,6 +249,8 @@ const Header = ({
|
||||
(i) => !(getReturnableQuantity(i) > 0)
|
||||
)
|
||||
|
||||
const isOrderEditActive = orderPreview?.order_change?.change_type === "edit"
|
||||
|
||||
return (
|
||||
<div className="flex items-center justify-between px-6 py-4">
|
||||
<Heading level="h2">{t("fields.summary")}</Heading>
|
||||
@@ -281,6 +283,7 @@ const Header = ({
|
||||
icon: <ArrowUturnLeft />,
|
||||
disabled:
|
||||
shouldDisableReturn ||
|
||||
isOrderEditActive ||
|
||||
!!orderPreview?.order_change?.exchange_id ||
|
||||
!!orderPreview?.order_change?.claim_id,
|
||||
},
|
||||
@@ -294,6 +297,7 @@ const Header = ({
|
||||
icon: <ArrowPath />,
|
||||
disabled:
|
||||
shouldDisableReturn ||
|
||||
isOrderEditActive ||
|
||||
(!!orderPreview?.order_change?.return_id &&
|
||||
!!!orderPreview?.order_change?.exchange_id) ||
|
||||
!!orderPreview?.order_change?.claim_id,
|
||||
@@ -308,6 +312,7 @@ const Header = ({
|
||||
icon: <ExclamationCircle />,
|
||||
disabled:
|
||||
shouldDisableReturn ||
|
||||
isOrderEditActive ||
|
||||
(!!orderPreview?.order_change?.return_id &&
|
||||
!!!orderPreview?.order_change?.claim_id) ||
|
||||
!!orderPreview?.order_change?.exchange_id,
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { Outlet, useLoaderData, useParams } from "react-router-dom"
|
||||
|
||||
import { JsonViewSection } from "../../../components/common/json-view-section"
|
||||
import { useOrder } from "../../../hooks/api/orders"
|
||||
import { useOrder, useOrderPreview } from "../../../hooks/api/orders"
|
||||
import { OrderActivitySection } from "./components/order-activity-section"
|
||||
import { OrderCustomerSection } from "./components/order-customer-section"
|
||||
import { OrderFulfillmentSection } from "./components/order-fulfillment-section"
|
||||
@@ -15,6 +15,9 @@ import after from "virtual:medusa/widgets/order/details/after"
|
||||
import before from "virtual:medusa/widgets/order/details/before"
|
||||
import sideAfter from "virtual:medusa/widgets/order/details/side/after"
|
||||
import sideBefore from "virtual:medusa/widgets/order/details/side/before"
|
||||
import { ActiveOrderClaimSection } from "./components/active-order-claim-section"
|
||||
import { ActiveOrderExchangeSection } from "./components/active-order-exchange-section"
|
||||
import { ActiveOrderReturnSection } from "./components/active-order-return-section"
|
||||
import { OrderActiveEditSection } from "./components/order-active-edit-section"
|
||||
|
||||
export const OrderDetail = () => {
|
||||
@@ -31,8 +34,11 @@ export const OrderDetail = () => {
|
||||
initialData,
|
||||
}
|
||||
)
|
||||
const { order: orderPreview, isLoading: isPreviewLoading } = useOrderPreview(
|
||||
id!
|
||||
)
|
||||
|
||||
if (isLoading || !order) {
|
||||
if (isLoading || !order || isPreviewLoading) {
|
||||
return <div>Loading...</div>
|
||||
}
|
||||
|
||||
@@ -52,6 +58,9 @@ export const OrderDetail = () => {
|
||||
<div className="flex flex-col gap-x-4 lg:flex-row xl:items-start">
|
||||
<div className="flex w-full flex-col gap-y-3">
|
||||
<OrderActiveEditSection order={order} />
|
||||
<ActiveOrderClaimSection orderPreview={orderPreview!} />
|
||||
<ActiveOrderExchangeSection orderPreview={orderPreview!} />
|
||||
<ActiveOrderReturnSection orderPreview={orderPreview!} />
|
||||
<OrderGeneralSection order={order} />
|
||||
<OrderSummarySection order={order} />
|
||||
<OrderPaymentSection order={order} />
|
||||
|
||||
@@ -321,7 +321,7 @@ export interface BaseOrderChange {
|
||||
/**
|
||||
* The type of the order change
|
||||
*/
|
||||
change_type?: "return" | "exchange" | "claim" | "edit"
|
||||
change_type?: "return" | "exchange" | "claim" | "edit" | "return_request"
|
||||
|
||||
/**
|
||||
* The ID of the associated order
|
||||
|
||||
Reference in New Issue
Block a user