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