feat(dashboard,types): split damaged activity from received (#8859)
what: - split damaged activity from received - adds a popover to view return details <img width="1661" alt="Screenshot 2024-08-28 at 23 23 10" src="https://github.com/user-attachments/assets/064e982c-f850-452d-a60d-e5c267d0075a"> RESOLVES CC-363
This commit is contained in:
@@ -924,8 +924,12 @@
|
||||
"inboundShippingHint": "Choose which method you want to use.",
|
||||
"returnableQuantityLabel": "Returnable quantity",
|
||||
"refundableAmountLabel": "Refundable amount",
|
||||
"returnRequestedInfo": "{{requestedItemsCount}}x item return requested",
|
||||
"returnReceivedInfo": "{{requestedItemsCount}}x item return received",
|
||||
"returnRequestedInfo": "{{requestedItemsCount}}x items return requested",
|
||||
"returnReceivedInfo": "{{requestedItemsCount}}x items return received",
|
||||
"itemReceived": "Items received",
|
||||
"returnRequested": "Return requested",
|
||||
"damagedItemReceived": "Damaged items received",
|
||||
"damagedItemsReturned": "{{quantity}}x damaged items returned",
|
||||
"activeChangeError": "There is an active order change in progress on this order. Please finish or discard the change first.",
|
||||
"cancel": {
|
||||
"title": "Cancel Return",
|
||||
|
||||
@@ -44,6 +44,7 @@ function ActivityItems(props: ActivityItemsProps) {
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
autoFocus={false}
|
||||
className="focus-visible:outline-none"
|
||||
>
|
||||
<Text size="small" leading="compact" weight="plus">
|
||||
{title}
|
||||
@@ -53,7 +54,7 @@ function ActivityItems(props: ActivityItemsProps) {
|
||||
<Popover.Content
|
||||
align="center"
|
||||
side="top"
|
||||
className="bg-ui-bg-component p-0 max-w-[200px]"
|
||||
className="bg-ui-bg-component p-0 max-w-[200px] focus-visible:outline-none"
|
||||
>
|
||||
<div className="flex flex-col">
|
||||
{!!itemsToSend?.length && (
|
||||
|
||||
@@ -52,6 +52,7 @@ import {
|
||||
import { getTotalCaptured } from "../../../../../lib/payment"
|
||||
import { getReturnableQuantity } from "../../../../../lib/rma"
|
||||
import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link"
|
||||
import ReturnInfoPopover from "./return-info-popover"
|
||||
|
||||
type OrderSummarySectionProps = {
|
||||
order: AdminOrder
|
||||
@@ -565,6 +566,61 @@ const CostBreakdown = ({ order }: { order: AdminOrder }) => {
|
||||
)
|
||||
}
|
||||
|
||||
const ReturnBreakdownWithDamages = ({
|
||||
orderReturn,
|
||||
itemId,
|
||||
}: {
|
||||
orderReturn: AdminReturn
|
||||
itemId: string
|
||||
}) => {
|
||||
const { t } = useTranslation()
|
||||
|
||||
const item = orderReturn?.items?.find((ri) => ri.item_id === itemId)
|
||||
const damagedQuantity = item?.damaged_quantity || 0
|
||||
|
||||
return (
|
||||
item && (
|
||||
<div
|
||||
key={orderReturn.id}
|
||||
className="txt-compact-small-plus text-ui-fg-subtle bg-ui-bg-subtle flex flex-row justify-between gap-y-2 border-t-2 border-dotted px-6 py-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowDownRightMini className="text-ui-fg-muted" />
|
||||
<Text size="small">
|
||||
{t(`orders.returns.damagedItemsReturned`, {
|
||||
quantity: damagedQuantity,
|
||||
})}
|
||||
</Text>
|
||||
|
||||
{item?.note && (
|
||||
<Tooltip content={item.note}>
|
||||
<DocumentText className="text-ui-tag-neutral-icon ml-1 inline" />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{item?.reason && (
|
||||
<Badge
|
||||
size="2xsmall"
|
||||
className="cursor-default select-none capitalize"
|
||||
rounded="full"
|
||||
>
|
||||
{item?.reason?.label}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<Text size="small" leading="compact" className="text-ui-fg-muted">
|
||||
{t(`orders.returns.damagedItemReceived`)}
|
||||
|
||||
<span className="ml-2">
|
||||
<ReturnInfoPopover orderReturn={orderReturn} />
|
||||
</span>
|
||||
</Text>
|
||||
</div>
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
const ReturnBreakdown = ({
|
||||
orderReturn,
|
||||
itemId,
|
||||
@@ -585,52 +641,72 @@ const ReturnBreakdown = ({
|
||||
|
||||
const isRequested = orderReturn.status === "requested"
|
||||
const item = orderReturn?.items?.find((ri) => ri.item_id === itemId)
|
||||
const damagedQuantity = item?.damaged_quantity || 0
|
||||
|
||||
return (
|
||||
item && (
|
||||
<div
|
||||
key={orderReturn.id}
|
||||
className="txt-compact-small-plus text-ui-fg-subtle bg-ui-bg-subtle flex flex-row justify-between gap-y-2 border-t-2 border-dotted px-6 py-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowDownRightMini className="text-ui-fg-muted" />
|
||||
<Text>
|
||||
{t(
|
||||
`orders.returns.${
|
||||
isRequested ? "returnRequestedInfo" : "returnReceivedInfo"
|
||||
}`,
|
||||
{
|
||||
requestedItemsCount:
|
||||
item?.[isRequested ? "quantity" : "received_quantity"],
|
||||
}
|
||||
)}
|
||||
</Text>
|
||||
<>
|
||||
{damagedQuantity > 0 && (
|
||||
<ReturnBreakdownWithDamages
|
||||
orderReturn={orderReturn}
|
||||
itemId={itemId}
|
||||
/>
|
||||
)}
|
||||
<div
|
||||
key={item.id}
|
||||
className="txt-compact-small-plus text-ui-fg-subtle bg-ui-bg-subtle flex flex-row justify-between gap-y-2 border-t-2 border-dotted px-6 py-4"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowDownRightMini className="text-ui-fg-muted" />
|
||||
<Text size="small">
|
||||
{t(
|
||||
`orders.returns.${
|
||||
isRequested ? "returnRequestedInfo" : "returnReceivedInfo"
|
||||
}`,
|
||||
{
|
||||
requestedItemsCount:
|
||||
item?.[isRequested ? "quantity" : "received_quantity"],
|
||||
}
|
||||
)}
|
||||
</Text>
|
||||
|
||||
{item?.note && (
|
||||
<Tooltip content={item.note}>
|
||||
<DocumentText className="text-ui-tag-neutral-icon ml-1 inline" />
|
||||
</Tooltip>
|
||||
{item?.note && (
|
||||
<Tooltip content={item.note}>
|
||||
<DocumentText className="text-ui-tag-neutral-icon ml-1 inline" />
|
||||
</Tooltip>
|
||||
)}
|
||||
|
||||
{item?.reason && (
|
||||
<Badge
|
||||
size="2xsmall"
|
||||
className="cursor-default select-none capitalize"
|
||||
rounded="full"
|
||||
>
|
||||
{item?.reason?.label}
|
||||
</Badge>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{orderReturn && isRequested && (
|
||||
<Text size="small" leading="compact" className="text-ui-fg-muted">
|
||||
{getRelativeDate(orderReturn.created_at)}
|
||||
<span className="ml-2">
|
||||
<ReturnInfoPopover orderReturn={orderReturn} />
|
||||
</span>
|
||||
</Text>
|
||||
)}
|
||||
|
||||
{item?.reason && (
|
||||
<Badge
|
||||
size="2xsmall"
|
||||
className="cursor-default select-none capitalize"
|
||||
rounded="full"
|
||||
>
|
||||
{item?.reason?.label}
|
||||
</Badge>
|
||||
{orderReturn && !isRequested && (
|
||||
<Text size="small" leading="compact" className="text-ui-fg-muted">
|
||||
{t(`orders.returns.itemReceived`)}
|
||||
|
||||
<span className="ml-2">
|
||||
<ReturnInfoPopover orderReturn={orderReturn} />
|
||||
</span>
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{orderReturn && (
|
||||
<Text size="small" leading="compact" className="text-ui-fg-muted">
|
||||
{getRelativeDate(
|
||||
isRequested ? orderReturn.created_at : orderReturn.received_at
|
||||
)}
|
||||
</Text>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
)
|
||||
)
|
||||
}
|
||||
@@ -657,7 +733,7 @@ const ClaimBreakdown = ({
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowDownRightMini className="text-ui-fg-muted" />
|
||||
|
||||
<Text>
|
||||
<Text size="small">
|
||||
{t(`orders.claims.outboundItemAdded`, {
|
||||
itemsCount: items.reduce(
|
||||
(acc, item) => (acc = acc + item.quantity),
|
||||
@@ -696,7 +772,7 @@ const ExchangeBreakdown = ({
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<ArrowDownRightMini className="text-ui-fg-muted" />
|
||||
<Text>
|
||||
<Text size="small">
|
||||
{t(`orders.exchanges.outboundItemAdded`, {
|
||||
itemsCount: items.reduce(
|
||||
(acc, item) => (acc = acc + item.quantity),
|
||||
@@ -720,19 +796,39 @@ const Total = ({ order }: { order: AdminOrder }) => {
|
||||
return (
|
||||
<div className=" flex flex-col gap-y-2 px-6 py-4">
|
||||
<div className="text-ui-fg-base flex items-center justify-between">
|
||||
<Text className="text-ui-fg-subtle" size="small" leading="compact">
|
||||
<Text
|
||||
weight="plus"
|
||||
className="text-ui-fg-subtle"
|
||||
size="small"
|
||||
leading="compact"
|
||||
>
|
||||
{t("fields.total")}
|
||||
</Text>
|
||||
<Text className="text-ui-fg-subtle" size="small" leading="compact">
|
||||
<Text
|
||||
weight="plus"
|
||||
className="text-ui-fg-subtle"
|
||||
size="small"
|
||||
leading="compact"
|
||||
>
|
||||
{getStylizedAmount(order.total, order.currency_code)}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
<div className="text-ui-fg-base flex items-center justify-between">
|
||||
<Text className="text-ui-fg-subtle" size="small" leading="compact">
|
||||
<Text
|
||||
weight="plus"
|
||||
className="text-ui-fg-subtle"
|
||||
size="small"
|
||||
leading="compact"
|
||||
>
|
||||
{t("fields.paidTotal")}
|
||||
</Text>
|
||||
<Text className="text-ui-fg-subtle" size="small" leading="compact">
|
||||
<Text
|
||||
weight="plus"
|
||||
className="text-ui-fg-subtle"
|
||||
size="small"
|
||||
leading="compact"
|
||||
>
|
||||
{getStylizedAmount(
|
||||
getTotalCaptured(order.payment_collections || []),
|
||||
order.currency_code
|
||||
@@ -745,6 +841,7 @@ const Total = ({ order }: { order: AdminOrder }) => {
|
||||
className="text-ui-fg-subtle text-semibold"
|
||||
size="small"
|
||||
leading="compact"
|
||||
weight="plus"
|
||||
>
|
||||
{t("orders.returns.outstandingAmount")}
|
||||
</Text>
|
||||
@@ -752,6 +849,7 @@ const Total = ({ order }: { order: AdminOrder }) => {
|
||||
className="text-ui-fg-subtle text-bold"
|
||||
size="small"
|
||||
leading="compact"
|
||||
weight="plus"
|
||||
>
|
||||
{getStylizedAmount(
|
||||
order.summary.pending_difference || 0,
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
import { InformationCircleSolid } from "@medusajs/icons"
|
||||
import { AdminReturn } from "@medusajs/types"
|
||||
import { Badge, Popover, Text } from "@medusajs/ui"
|
||||
import { useState } from "react"
|
||||
import { useTranslation } from "react-i18next"
|
||||
import { formatDate } from "../../../../../components/common/date"
|
||||
|
||||
type ReturnInfoPopoverProps = {
|
||||
orderReturn: AdminReturn
|
||||
}
|
||||
|
||||
function ReturnInfoPopover({ orderReturn }: ReturnInfoPopoverProps) {
|
||||
const { t } = useTranslation()
|
||||
const [open, setOpen] = useState(false)
|
||||
|
||||
const handleMouseEnter = () => {
|
||||
setOpen(true)
|
||||
}
|
||||
|
||||
const handleMouseLeave = () => {
|
||||
setOpen(false)
|
||||
}
|
||||
|
||||
let returnType = "Return"
|
||||
let returnTypeId = orderReturn.id
|
||||
|
||||
if (orderReturn.claim_id) {
|
||||
returnType = "Claim"
|
||||
returnTypeId = orderReturn.claim_id
|
||||
}
|
||||
|
||||
if (orderReturn.exchange_id) {
|
||||
returnType = "Exchange"
|
||||
returnTypeId = orderReturn.exchange_id
|
||||
}
|
||||
|
||||
if (typeof orderReturn !== "object") {
|
||||
return
|
||||
}
|
||||
|
||||
return (
|
||||
<Popover open={open}>
|
||||
<Popover.Trigger
|
||||
onMouseEnter={handleMouseEnter}
|
||||
onMouseLeave={handleMouseLeave}
|
||||
autoFocus={false}
|
||||
className="focus-visible:outline-none align-sub"
|
||||
>
|
||||
<InformationCircleSolid />
|
||||
</Popover.Trigger>
|
||||
|
||||
<Popover.Content
|
||||
align="center"
|
||||
side="top"
|
||||
className="bg-ui-bg-component focus-visible:outline-none p-2"
|
||||
>
|
||||
<div className="">
|
||||
<Badge size="2xsmall" className="mb-2" rounded="full">
|
||||
{returnType}: #{returnTypeId.slice(-7)}
|
||||
</Badge>
|
||||
|
||||
<Text size="xsmall">
|
||||
<span className="text-ui-fg-subtle">
|
||||
{t(`orders.returns.returnRequested`)}
|
||||
</span>
|
||||
{" · "}
|
||||
{formatDate(orderReturn.requested_at)}
|
||||
</Text>
|
||||
|
||||
<Text size="xsmall">
|
||||
<span className="text-ui-fg-subtle">
|
||||
{t(`orders.returns.itemReceived`)}
|
||||
</span>
|
||||
{" · "}
|
||||
{orderReturn.received_at
|
||||
? formatDate(orderReturn.received_at)
|
||||
: "-"}
|
||||
</Text>
|
||||
</div>
|
||||
</Popover.Content>
|
||||
</Popover>
|
||||
)
|
||||
}
|
||||
|
||||
export default ReturnInfoPopover
|
||||
@@ -2,6 +2,7 @@ export interface BaseReturnItem {
|
||||
id: string
|
||||
quantity: number
|
||||
received_quantity: number
|
||||
damaged_quantity: number
|
||||
reason_id?: string
|
||||
note?: string
|
||||
item_id: string
|
||||
|
||||
Reference in New Issue
Block a user