import React, { useContext, useEffect, useRef, useState } from "react" import { Order, OrderEdit, ProductVariant } from "@medusajs/medusa" import { useAdminCreateOrderEdit, useAdminDeleteOrderEdit, useAdminOrderEditAddLineItem, useAdminRequestOrderEditConfirmation, useAdminUpdateOrderEdit, } from "medusa-react" import clsx from "clsx" import { useTranslation } from "react-i18next" import LayeredModal, { LayeredModalContext, } from "../../../components/molecules/modal/layered-modal" import Modal from "../../../components/molecules/modal" import Button from "../../../components/fundamentals/button" import OrderEditLine from "../details/order-line/edit" import VariantsTable from "./variants-table" import SearchIcon from "../../../components/fundamentals/icons/search-icon" import { formatAmountWithSymbol } from "../../../utils/prices" import InputField from "../../../components/molecules/input" import useNotification from "../../../hooks/use-notification" import { OrderEditContext } from "./context" type TotalsSectionProps = { amountPaid: number newTotal: number differenceDue: number currencyCode: string } /** * Totals section displaying order and order edit subtotals. */ function TotalsSection(props: TotalsSectionProps) { const { t } = useTranslation() const { currencyCode, amountPaid, newTotal, differenceDue } = props return ( <>
{t("edit-amount-paid", "Amount Paid")} {formatAmountWithSymbol({ amount: amountPaid, currency: currencyCode, })} {currencyCode.toUpperCase()}
{t("edit-new-total", "New Total")} {formatAmountWithSymbol({ amount: newTotal, currency: currencyCode, })}
{t("edit-difference-due", "Difference Due")} = 0, })} > {formatAmountWithSymbol({ amount: differenceDue, currency: currencyCode, })} {currencyCode.toUpperCase()}
) } type AddProductVariantProps = { regionId: string currencyCode: string customerId: string isReplace?: boolean onSubmit: (variants: ProductVariant[]) => void } /** * Add product variant modal screen */ export function AddProductVariant(props: AddProductVariantProps) { const { t } = useTranslation() const { pop } = React.useContext(LayeredModalContext) const [selectedVariants, setSelectedVariants] = useState([]) const onSubmit = async () => { // wait until onSubmit is done to reduce list jumping await props.onSubmit(selectedVariants) pop() } const onBack = () => { setSelectedVariants([]) pop() } return ( <>
) } type OrderEditModalProps = { close: () => void orderEdit: OrderEdit currencyCode: string regionId: string customerId: string currentSubtotal: number paidTotal: number refundedTotal: number } /** * Displays layered modal for order editing. */ function OrderEditModal(props: OrderEditModalProps) { const { close, currentSubtotal, orderEdit, currencyCode, regionId, customerId, paidTotal, refundedTotal, } = props const { t } = useTranslation() const filterRef = useRef() const notification = useNotification() const [note, setNote] = useState() const [showFilter, setShowFilter] = useState(false) const [filterTerm, setFilterTerm] = useState("") const showTotals = currentSubtotal !== orderEdit.subtotal const hasChanges = !!orderEdit.changes.length const { mutateAsync: requestConfirmation, isLoading: isRequestingConfirmation, } = useAdminRequestOrderEditConfirmation(orderEdit.id) const { mutateAsync: updateOrderEdit, isLoading: isUpdating } = useAdminUpdateOrderEdit(orderEdit.id) const { mutateAsync: deleteOrderEdit } = useAdminDeleteOrderEdit(orderEdit.id) const { mutateAsync: addLineItem } = useAdminOrderEditAddLineItem( orderEdit.id ) const layeredModalContext = useContext(LayeredModalContext) const onSave = async () => { try { await requestConfirmation() if (note) { await updateOrderEdit({ internal_note: note }) } notification( t("edit-success", "Success"), t("edit-order-edit-set-as-requested", "Order edit set as requested"), "success" ) } catch (e) { notification( t("edit-error", "Error"), t( "edit-failed-to-request-confirmation", "Failed to request confirmation" ), "error" ) } close() } const onCancel = async () => { // NOTE: has to be this order of ops await deleteOrderEdit() close() } useEffect(() => { if (showFilter) { filterRef.current.focus() } }, [showFilter]) const onAddVariants = async (selectedVariants: ProductVariant[]) => { try { const promises = selectedVariants.map((v) => addLineItem({ variant_id: v.id, quantity: 1 }) ) await Promise.all(promises) notification( t("edit-success", "Success"), t("edit-added-successfully", "Added successfully"), "success" ) } catch (e) { notification( t("edit-error", "Error"), t("edit-error-occurred", "Error occurred"), "error" ) } } const hideFilter = () => { if (showFilter) { setFilterTerm("") } setShowFilter((s) => !s) } let displayItems = orderEdit.items.sort( // @ts-ignore (a, b) => new Date(a.created_at) - new Date(b.created_at) ) if (filterTerm) { displayItems = displayItems.filter( (i) => i.title.toLowerCase().includes(filterTerm) || i.variant?.sku.toLowerCase().includes(filterTerm) ) } const addProductVariantScreen = { title: t("edit-add-product-variants", "Add Product Variants"), onBack: layeredModalContext.pop, view: ( ), } return (

{t("edit-edit-order", "Edit Order")}

{t("edit-items", "Items")}
{!showFilter && ( )} {showFilter && ( setFilterTerm(e.target.value)} prefix={} /> )}
{/* ITEMS */} {displayItems.map((oi) => ( change.line_item_id === oi.id || change.original_line_item_id === oi.id )} /> ))}
{/* TOTALS */} {showTotals && ( diff_due = orderEdit_total_of_items_user_is_getting - paid_total + refunded_total orderEdit.total - paidTotal // (orderEdit_total - refunded_total) - (paidTotal - refundedTotal) } /> )} {/* NOTE */} {hasChanges && (
{t("edit-note", "Note")} setNote(e.target.value)} value={note} />
)}
) } type OrderEditModalContainerProps = { order: Order } let isRequestRunningFlag = false function OrderEditModalContainer(props: OrderEditModalContainerProps) { const { order } = props const notification = useNotification() const { hideModal, orderEdits, activeOrderEditId, setActiveOrderEdit } = useContext(OrderEditContext) const { mutateAsync: createOrderEdit } = useAdminCreateOrderEdit() const orderEdit = orderEdits?.find((oe) => oe.id === activeOrderEditId) useEffect(() => { if (activeOrderEditId || isRequestRunningFlag) { return } isRequestRunningFlag = true createOrderEdit({ order_id: order.id }) .then(({ order_edit }) => setActiveOrderEdit(order_edit.id)) .catch(() => { notification( "Error", "There is already an active order edit on this order", "error" ) hideModal() }) .finally(() => (isRequestRunningFlag = false)) }, [activeOrderEditId]) const onClose = () => { // setActiveOrderEdit(undefined) -> context will unset active edit after flag toggle hideModal() } if (!orderEdit) { return null } return ( ) } export default OrderEditModalContainer