feat(dashboard): summary shipping breakdown (#8779)

**What**
- display a shipping costs breakdown in the order summary
- allow to receive only non canceled returns
- show how many items is actually received with return in the timeline

**Question**
- should we display the shipping total somewhere as well

---

CLOSES CC-356
This commit is contained in:
Frane Polić
2024-08-28 06:44:22 +00:00
committed by GitHub
parent a430339d54
commit e0a3b8fc2e
2 changed files with 33 additions and 20 deletions
@@ -242,7 +242,7 @@ const useActivityItems = (order: AdminOrder) => {
returnId: ret.id.slice(-7), returnId: ret.id.slice(-7),
}), }),
timestamp: ret.received_at, timestamp: ret.received_at,
children: <ReturnBody orderReturn={ret} />, children: <ReturnBody orderReturn={ret} isReceived />,
}) })
} }
} }
@@ -498,9 +498,11 @@ const FulfillmentCreatedBody = ({
const ReturnBody = ({ const ReturnBody = ({
orderReturn, orderReturn,
isCreated, isCreated,
isReceived,
}: { }: {
orderReturn: AdminReturn orderReturn: AdminReturn
isCreated: boolean isCreated: boolean
isReceived?: boolean
}) => { }) => {
const prompt = usePrompt() const prompt = usePrompt()
const { t } = useTranslation() const { t } = useTranslation()
@@ -526,7 +528,7 @@ const ReturnBody = ({
} }
const numberOfItems = orderReturn.items.reduce((acc, item) => { const numberOfItems = orderReturn.items.reduce((acc, item) => {
return acc + item.quantity return acc + (isReceived ? item.received_quantity : item.quantity) // TODO: revisit when we add dismissed quantity on ReturnItem
}, 0) }, 0)
return ( return (
@@ -35,23 +35,23 @@ import {
import { AdminPaymentCollection } from "../../../../../../../../core/types/dist/http/payment/admin/entities" import { AdminPaymentCollection } from "../../../../../../../../core/types/dist/http/payment/admin/entities"
import { ActionMenu } from "../../../../../components/common/action-menu" import { ActionMenu } from "../../../../../components/common/action-menu"
import { ButtonMenu } from "../../../../../components/common/button-menu/button-menu.tsx" import { ButtonMenu } from "../../../../../components/common/button-menu/button-menu"
import { Thumbnail } from "../../../../../components/common/thumbnail" import { Thumbnail } from "../../../../../components/common/thumbnail"
import { useClaims } from "../../../../../hooks/api/claims.tsx" import { useClaims } from "../../../../../hooks/api/claims"
import { useExchanges } from "../../../../../hooks/api/exchanges.tsx" import { useExchanges } from "../../../../../hooks/api/exchanges"
import { useOrderPreview } from "../../../../../hooks/api/orders.tsx" import { useOrderPreview } from "../../../../../hooks/api/orders"
import { useMarkPaymentCollectionAsPaid } from "../../../../../hooks/api/payment-collections.tsx" import { useMarkPaymentCollectionAsPaid } from "../../../../../hooks/api/payment-collections"
import { useReservationItems } from "../../../../../hooks/api/reservations" import { useReservationItems } from "../../../../../hooks/api/reservations"
import { useReturns } from "../../../../../hooks/api/returns" import { useReturns } from "../../../../../hooks/api/returns"
import { useDate } from "../../../../../hooks/use-date" import { useDate } from "../../../../../hooks/use-date"
import { formatCurrency } from "../../../../../lib/format-currency.ts" import { formatCurrency } from "../../../../../lib/format-currency"
import { import {
getLocaleAmount, getLocaleAmount,
getStylizedAmount, getStylizedAmount,
} from "../../../../../lib/money-amount-helpers" } from "../../../../../lib/money-amount-helpers"
import { getTotalCaptured } from "../../../../../lib/payment.ts" import { getTotalCaptured } from "../../../../../lib/payment"
import { getReturnableQuantity } from "../../../../../lib/rma.ts" import { getReturnableQuantity } from "../../../../../lib/rma"
import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link.tsx" import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link"
type OrderSummarySectionProps = { type OrderSummarySectionProps = {
order: AdminOrder order: AdminOrder
@@ -77,7 +77,12 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
fields: "+received_at", fields: "+received_at",
}) })
const showReturns = !!returns.length const receivableReturns = useMemo(
() => returns.filter((r) => !r.canceled_at),
[returns]
)
const showReturns = !!receivableReturns.length
/** /**
* Show Allocation button only if there are unfulfilled items that don't have reservations * Show Allocation button only if there are unfulfilled items that don't have reservations
@@ -170,11 +175,11 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
{(showAllocateButton || showReturns || showPayment || showRefund) && ( {(showAllocateButton || showReturns || showPayment || showRefund) && (
<div className="bg-ui-bg-subtle flex items-center justify-end gap-x-2 rounded-b-xl px-4 py-4"> <div className="bg-ui-bg-subtle flex items-center justify-end gap-x-2 rounded-b-xl px-4 py-4">
{showReturns && {showReturns &&
(returns.length === 1 ? ( (receivableReturns.length === 1 ? (
<Button <Button
onClick={() => onClick={() =>
navigate( navigate(
`/orders/${order.id}/returns/${returns[0].id}/receive` `/orders/${order.id}/returns/${receivableReturns[0].id}/receive`
) )
} }
variant="secondary" variant="secondary"
@@ -186,7 +191,7 @@ export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => {
<ButtonMenu <ButtonMenu
groups={[ groups={[
{ {
actions: returns.map((r) => ({ actions: receivableReturns.map((r) => ({
label: t("orders.returns.receive.receive", { label: t("orders.returns.receive.receive", {
label: `#${r.id.slice(-7)}`, label: `#${r.id.slice(-7)}`,
}), }),
@@ -529,11 +534,17 @@ const CostBreakdown = ({ order }: { order: AdminOrder }) => {
: "-" : "-"
} }
/> />
<Cost {(order.shipping_methods || [])
label={t("fields.shipping")} .sort((m1, m2) =>
secondaryValue={order.shipping_methods.map((sm) => sm.name).join(", ")} (m1.created_at as string).localeCompare(m2.created_at as string)
value={getLocaleAmount(order.shipping_total, order.currency_code)} )
/> .map((sm, i) => (
<Cost
label={t("fields.shipping") + (i ? ` ${i + 1}` : "")}
secondaryValue={sm.name}
value={getLocaleAmount(sm.total, order.currency_code)}
/>
))}
</div> </div>
) )
} }