import { ReactNode, useMemo, useState } from "react"
import { useTranslation } from "react-i18next"
import { Link } from "react-router-dom"
import {
ArrowDownRightMini,
ArrowLongRight,
ArrowPath,
ArrowUturnLeft,
DocumentText,
ExclamationCircle,
PencilSquare,
ReceiptPercent,
TriangleDownMini,
} from "@medusajs/icons"
import {
AdminClaim,
AdminExchange,
AdminOrder,
AdminOrderLineItem,
AdminOrderPreview,
AdminPaymentCollection,
AdminPlugin,
AdminRegion,
AdminReturn,
AdminReturnItem,
AdminReturnReason,
} from "@medusajs/types"
import {
Badge,
Button,
clx,
Container,
Copy,
Heading,
StatusBadge,
Text,
toast,
Tooltip,
usePrompt,
} from "@medusajs/ui"
import { AdminReservation } from "@medusajs/types/src/http"
import { ActionMenu } from "../../../../../components/common/action-menu"
import DisplayId from "../../../../../components/common/display-id/display-id"
import { Thumbnail } from "../../../../../components/common/thumbnail"
import { useClaims } from "../../../../../hooks/api/claims"
import { useExchanges } from "../../../../../hooks/api/exchanges"
import { useOrderPreview } from "../../../../../hooks/api/orders"
import { useMarkPaymentCollectionAsPaid } from "../../../../../hooks/api/payment-collections"
import { useReservationItems } from "../../../../../hooks/api/reservations"
import { useReturns } from "../../../../../hooks/api/returns"
import { useDate } from "../../../../../hooks/use-date"
import { getTotalCreditLines } from "../../../../../lib/credit-line"
import { formatCurrency } from "../../../../../lib/format-currency"
import { getReservationsLimitCount } from "../../../../../lib/orders"
import {
getLocaleAmount,
getStylizedAmount,
isAmountLessThenRoundingError,
} from "../../../../../lib/money-amount-helpers"
import { getTotalCaptured } from "../../../../../lib/payment"
import { getLoyaltyPlugin } from "../../../../../lib/plugins"
import { getReturnableQuantity } from "../../../../../lib/rma"
import { CopyPaymentLink } from "../copy-payment-link/copy-payment-link"
import ReturnInfoPopover from "./return-info-popover"
import ShippingInfoPopover from "./shipping-info-popover"
import { formatPercentage } from "../../../../../lib/percentage-helpers.ts"
type OrderSummarySectionProps = {
order: AdminOrder
plugins: AdminPlugin[]
}
export const OrderSummarySection = ({
order,
plugins,
}: OrderSummarySectionProps) => {
const { t } = useTranslation()
const prompt = usePrompt()
const { reservations } = useReservationItems(
{
line_item_id: order?.items?.map((i) => i.id),
limit: getReservationsLimitCount(order),
},
{ enabled: Array.isArray(order?.items) }
)
const { order: orderPreview } = useOrderPreview(order.id!)
const { returns = [] } = useReturns({
status: "requested",
order_id: order.id,
fields: "+received_at",
})
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
*/
const showAllocateButton = useMemo(() => {
if (!reservations) {
return false
}
const reservationsMap = new Map(
reservations.map((r) => [r.line_item_id, r.id])
)
for (const item of order.items) {
// Inventory is managed
if (item.variant?.manage_inventory) {
// There are items that are unfulfilled
if (item.quantity - item.detail.fulfilled_quantity > 0) {
// Reservation for this item doesn't exist
if (!reservationsMap.has(item.id)) {
return true
}
}
}
}
return false
}, [order.items, reservations])
const unpaidPaymentCollection = order.payment_collections.find(
(pc) => pc.status === "not_paid"
)
const { mutateAsync: markAsPaid } = useMarkPaymentCollectionAsPaid(
order.id,
unpaidPaymentCollection?.id!
)
const pendingDifference = order.summary?.pending_difference || 0
const isAmountSignificant = !isAmountLessThenRoundingError(
pendingDifference,
order.currency_code
)
const showPayment =
unpaidPaymentCollection && pendingDifference > 0 && isAmountSignificant
const showRefund = pendingDifference < 0 && isAmountSignificant
const handleMarkAsPaid = async (
paymentCollection: AdminPaymentCollection
) => {
const res = await prompt({
title: t("orders.payment.markAsPaid"),
description: t("orders.payment.markAsPaidPayment", {
amount: formatCurrency(
paymentCollection.amount as number,
order.currency_code
),
}),
confirmText: t("actions.confirm"),
cancelText: t("actions.cancel"),
variant: "confirmation",
})
if (!res) {
return
}
await markAsPaid(
{ order_id: order.id },
{
onSuccess: () => {
toast.success(
t("orders.payment.markAsPaidPaymentSuccess", {
amount: formatCurrency(
paymentCollection.amount as number,
order.currency_code
),
})
)
},
onError: (error) => {
toast.error(error.message)
},
}
)
}
return (
{(showAllocateButton || showReturns || showPayment || showRefund) && (
{showReturns &&
(receivableReturns.length === 1 ? (
) : (
{
let id = r.id
let returnType = "Return"
if (r.exchange_id) {
id = r.exchange_id
returnType = "Exchange"
}
if (r.claim_id) {
id = r.claim_id
returnType = "Claim"
}
return {
label: t("orders.returns.receive.receiveItems", {
id: `#${id.slice(-7)}`,
returnType,
}),
icon: ,
to: `/orders/${order.id}/returns/${r.id}/receive`,
}
}),
},
]}
>
))}
{showAllocateButton && (
)}
{showPayment && (
)}
{showPayment && (
)}
{showRefund && (
)}
)}
)
}
const Header = ({
order,
orderPreview,
}: {
order: AdminOrder
orderPreview?: AdminOrderPreview
}) => {
const { t } = useTranslation()
// is ture if there is no shipped items ATM
const shouldDisableReturn = order.items.every(
(i) => !(getReturnableQuantity(i) > 0)
)
const isOrderEditActive = orderPreview?.order_change?.change_type === "edit"
// State where creation of order edit was interrupted i.e. order edit is drafted but not confirmed
const isOrderEditPending =
orderPreview?.order_change?.change_type === "edit" &&
orderPreview?.order_change?.status === "pending"
return (