feat(dashboard) admin 3.0 order edit (#6665)

**What**
- added Order Edit creation flow

**NOTES**
- since the state is managed on the server upon changing input / adding items a request is fired to update the edit
- on save we only confirm the edit

---

**TODO**
- [x] item removal functionality

---

https://github.com/medusajs/medusa/assets/16856471/01aa85ea-1fb1-4dff-9cf4-d8d79029c2cc
This commit is contained in:
Frane Polić
2024-03-20 08:54:22 +01:00
committed by GitHub
parent 873c21355c
commit 189b03c485
16 changed files with 917 additions and 7 deletions

View File

@@ -6,6 +6,8 @@
"add": "Add",
"start": "Start",
"end": "End",
"open": "Open",
"close": "Close",
"apply": "Apply",
"range": "Range",
"search": "Search",
@@ -55,6 +57,7 @@
"cancel": "Cancel",
"save": "Save",
"continue": "Continue",
"confirm": "Confirm",
"edit": "Edit",
"download": "Download",
"clearAll": "Clear all",
@@ -257,6 +260,16 @@
"requiresAction": "Requires action"
}
},
"edits": {
"title": "Edit order",
"currentItems": "Current items",
"currentItemsDescription": "Adjust item quantity or remove.",
"addItemsDescription": "You can add new items to the order.",
"addItems": "Add items",
"amountPaid": "Amount paid",
"newTotal": "New total",
"differenceDue": "Difference due"
},
"reservations": {
"allocatedLabel": "Allocated",
"notAllocatedLabel": "Not allocated"
@@ -736,6 +749,7 @@
"availability": "Availability",
"inventory": "Inventory",
"optional": "Optional",
"note": "Note",
"taxInclusivePricing": "Tax inclusive pricing",
"taxRate": "Tax Rate",
"taxCode": "Tax Code",

View File

@@ -6,14 +6,16 @@ type MoneyAmountCellProps = {
currencyCode: string
amount?: number | null
align?: "left" | "right"
className?: string
}
export const MoneyAmountCell = ({
currencyCode,
amount,
align = "left",
className,
}: MoneyAmountCellProps) => {
if (!amount) {
if (typeof amount === "undefined" || amount === null) {
return <PlaceholderCell />
}
@@ -21,10 +23,14 @@ export const MoneyAmountCell = ({
return (
<div
className={clx("flex h-full w-full items-center overflow-hidden", {
"justify-start text-left": align === "left",
"justify-end text-right": align === "right",
})}
className={clx(
"flex h-full w-full items-center overflow-hidden",
{
"justify-start text-left": align === "left",
"justify-end text-right": align === "right",
},
className
)}
>
<span className="truncate">{formatted}</span>
</div>

View File

@@ -120,6 +120,10 @@ export const v1Routes: RouteObject[] = [
lazy: () =>
import("../../routes/orders/order-transfer-ownership"),
},
{
path: "edit",
lazy: () => import("../../routes/orders/order-edit"),
},
],
},
],

View File

@@ -38,7 +38,7 @@ const Header = ({ order }: { order: Order }) => {
actions: [
{
label: t("orders.summary.editItems"),
to: "#", // TODO: Open modal to edit items
to: `/orders/${order.id}/edit`,
icon: <PencilSquare />,
},
{

View File

@@ -1,2 +1,2 @@
export const orderExpand =
"items,items.variant,items.variant.options,sales_channel,shipping_methods,shipping_methods.shipping_option,discounts,payments,customer,shipping_address,shipping_address.country,billing_address,billing_address.country,fulfillments,fulfillments.items,fulfillments.items.item,fulfillments.tracking_links,refunds"
"items,items.variant,items.variant.options,sales_channel,shipping_methods,shipping_methods.shipping_option,discounts,payments,customer,shipping_address,shipping_address.country,billing_address,billing_address.country,fulfillments,fulfillments.items,fulfillments.items.item,fulfillments.tracking_links,refunds,edits,edits.items,edits.items.variant,edits.items.variant.product"

View File

@@ -0,0 +1 @@
export * from "./order-edit-form"

View File

@@ -0,0 +1,359 @@
import React, { useEffect, useMemo, useState } from "react"
import { useForm } from "react-hook-form"
import * as zod from "zod"
import { useTranslation } from "react-i18next"
import { useSearchParams } from "react-router-dom"
import {
adminOrderEditsKeys,
adminOrderKeys,
useAdminCancelOrderEdit,
useAdminConfirmOrderEdit,
useAdminOrderEditAddLineItem,
useAdminUpdateOrderEdit,
} from "medusa-react"
import { Button, clx, Heading, Text, Textarea } from "@medusajs/ui"
import { Order, OrderEdit } from "@medusajs/medusa"
import {
RouteFocusModal,
useRouteModal,
} from "../../../../../components/route-modal"
import { SplitView } from "../../../../../components/layout/split-view"
import { VariantTable } from "../variant-table"
import { medusa, queryClient } from "../../../../../lib/medusa.ts"
import { MoneyAmountCell } from "../../../../../components/table/table-cells/common/money-amount-cell"
import { Form } from "../../../../../components/common/form"
import { OrderEditItem } from "./order-edit-item"
type OrderEditFormProps = {
order: Order
orderEdit: OrderEdit
}
const QuantitiesSchema = zod.union(
zod.record(zod.string(), zod.number().optional()),
zod.object({ note: zod.string().optional() })
)
export function OrderEditForm({ order, orderEdit }: OrderEditFormProps) {
const { t } = useTranslation()
const { handleSuccess } = useRouteModal()
const [, setSearchParams] = useSearchParams()
/**
* STATE
*/
const [open, setOpen] = useState(false)
const [isSubmitting, setIsSubmitting] = useState(false)
const [isAddingItems, setIsAddingItems] = useState(false)
/**
* FORM
*/
const form = useForm<zod.infer<typeof QuantitiesSchema>>({
defaultValues: order.items.reduce(
(acc, i) => {
acc[i.id] = i.quantity
return acc
},
{ note: orderEdit.internal_note || "" }
),
})
/**
* CRUD HOOKS
*/
const { mutateAsync: confirmOrderEdit } = useAdminConfirmOrderEdit(
orderEdit.id
)
const { mutateAsync: cancelOrderEdit } = useAdminCancelOrderEdit(orderEdit.id)
const { mutateAsync: addLineItemToOrderEdit } = useAdminOrderEditAddLineItem(
orderEdit.id
)
const { mutateAsync: updateOrderEdit } = useAdminUpdateOrderEdit(orderEdit.id)
const onQuantityChangeComplete = async (itemId: string) => {
setIsSubmitting(true)
const quantity = form.getValues()[itemId]
// if (form.getValues()[itemId] === 0) {
// await medusa.admin.orderEdits.removeLineItem(orderEdit.id, itemId)
// } else
if (quantity > 0) {
await medusa.admin.orderEdits.updateLineItem(orderEdit.id, itemId, {
quantity,
})
}
await queryClient.invalidateQueries(
adminOrderEditsKeys.detail(orderEdit.id)
)
setIsSubmitting(false)
}
const currentItems = useMemo(
() =>
orderEdit?.items
.sort((i1, i2) => i1.id.localeCompare(i2.id))
.filter((i) => i.original_item_id) || [],
[orderEdit]
)
const addedItems = useMemo(
() =>
orderEdit?.items
.sort((i1, i2) => i1.id.localeCompare(i2.id))
.filter((i) => !i.original_item_id) || [],
[orderEdit]
)
/**
* EFFECTS
*/
useEffect(() => {
if (orderEdit) {
orderEdit?.items.forEach((i) => {
form.setValue(i.id, i.quantity)
})
}
}, [orderEdit?.items.length])
/**
* HANDLERS
*/
const handleOpenChange = (open: boolean) => {
if (!open) {
setSearchParams(
{},
{
replace: true,
}
)
}
setOpen(open)
}
const onVariantsSelect = async (variantIds: string[]) => {
setIsAddingItems(true)
try {
await Promise.all(
variantIds.map((id) =>
addLineItemToOrderEdit({ variant_id: id, quantity: 1 })
)
)
await queryClient.invalidateQueries(adminOrderKeys.detail(order.id))
} finally {
setIsAddingItems(false)
}
setOpen(false)
}
const onItemRemove = async (itemId: string) => {
setIsSubmitting(true)
const change = orderEdit.changes.find(
(change) =>
change.line_item_id === itemId ||
change.original_line_item_id === itemId
)
try {
if (change) {
if (change.type === "item_add") {
await medusa.admin.orderEdits.deleteItemChange(
orderEdit.id,
change.id
)
}
if (change.type === "item_update") {
await medusa.admin.orderEdits.deleteItemChange(
orderEdit.id,
change.id
)
await medusa.admin.orderEdits.removeLineItem(orderEdit.id, itemId)
}
} else {
await medusa.admin.orderEdits.removeLineItem(orderEdit.id, itemId)
}
await queryClient.invalidateQueries(
adminOrderEditsKeys.detail(orderEdit.id)
)
} finally {
setIsSubmitting(false)
}
}
const handleSubmit = form.handleSubmit(async (data) => {
setIsSubmitting(true)
try {
if (data.note !== orderEdit?.internal_note) {
await updateOrderEdit({ internal_note: data.note })
}
await confirmOrderEdit() // TODO error notification if fails
} finally {
setIsSubmitting(false)
}
handleSuccess(`/orders/${order.id}`)
})
// TODO pass on "cancel" close
const handlCancel = async () => {
await cancelOrderEdit()
handleSuccess(`/orders/${order.id}`)
}
return (
<RouteFocusModal.Form form={form}>
<form className="flex h-full flex-col" onSubmit={handleSubmit}>
<RouteFocusModal.Header>
<div className="flex h-full items-center justify-end gap-x-2">
<RouteFocusModal.Close asChild>
<Button variant="secondary" size="small">
{t("actions.cancel")}
</Button>
</RouteFocusModal.Close>
<Button type="submit" size="small" isLoading={isSubmitting}>
{t("actions.confirm")}
</Button>
</div>
</RouteFocusModal.Header>
<RouteFocusModal.Body className="flex h-full w-full flex-col items-center overflow-hidden">
<SplitView open={open} onOpenChange={handleOpenChange}>
<SplitView.Content>
<div className="conatiner mx-auto max-w-[720px]">
<div className={clx("flex h-full w-full flex-col pt-16")}>
<Heading level="h1" className="pb-8 text-left">
{t("orders.edits.title")}
</Heading>
<Text className="text-ui-fg-base mb-1" weight="plus">
{t("orders.edits.currentItems")}
</Text>
<Text className="text-ui-fg-subtle mb-4">
{t("orders.edits.currentItemsDescription")}
</Text>
{currentItems.map((item) => (
<OrderEditItem
key={item.id}
item={item}
form={form}
currencyCode={order.currency_code}
onRemove={onItemRemove}
onQuantityChangeComplete={onQuantityChangeComplete}
/>
))}
<div className="border-b border-dashed pb-10 ">
<Text className="text-ui-fg-base mb-1 mt-8" weight="plus">
{t("orders.edits.addItems")}
</Text>
<Text className="text-ui-fg-subtle mb-2">
{t("orders.edits.addItemsDescription")}
</Text>
{!!addedItems.length &&
addedItems.map((item) => (
<div key={item.id} className="pb-4 pt-2">
<OrderEditItem
item={item}
form={form}
currencyCode={order.currency_code}
onRemove={onItemRemove}
onQuantityChangeComplete={onQuantityChangeComplete}
/>
</div>
))}
<div className="m mt-2 flex justify-end">
<Button
variant="secondary"
type="button"
onClick={() => setOpen(true)}
>
{t("orders.edits.addItems")}
</Button>
</div>
</div>
<div className="flex flex-col gap-y-2 border-b border-dashed py-10">
<div className="text-ui-fg-subtle flex justify-between text-sm">
<Text className="min-w-[50%] ">
{t("orders.edits.amountPaid")}
</Text>
<MoneyAmountCell
align="right"
currencyCode={order.currency_code}
amount={order.paid_total - order.refunded_total}
/>
</div>
<div className="text-ui-fg-subtle flex justify-between text-sm ">
<Text className="min-w-[50%] ">
{t("orders.edits.newTotal")}
</Text>
<MoneyAmountCell
align="right"
currencyCode={order.currency_code}
amount={orderEdit.total}
/>
</div>
<div className="text-ui-fg-base flex justify-between text-sm">
<Text className="min-w-[50%]" weight="plus">
{t("orders.edits.differenceDue")}
</Text>
<MoneyAmountCell
align="right"
currencyCode={order.currency_code}
amount={orderEdit.total - order.paid_total}
/>
</div>
</div>
<div className="py-10">
<Form.Field
control={form.control}
name="note"
render={({ field }) => {
return (
<Form.Item>
<Text
className="text-ui-fg-base mb-1"
weight="plus"
>
{t("fields.note")}
</Text>
<Form.Control>
<Textarea {...field} />
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
</div>
</div>
</SplitView.Content>
<SplitView.Drawer>
<VariantTable
isAddingItems={isAddingItems}
onSave={onVariantsSelect}
order={order}
/>
</SplitView.Drawer>
</SplitView>
</RouteFocusModal.Body>
</form>
</RouteFocusModal.Form>
)
}

View File

@@ -0,0 +1,112 @@
import React from "react"
import { useTranslation } from "react-i18next"
import { UseFormReturn } from "react-hook-form"
import { Input, Text } from "@medusajs/ui"
import { LineItem } from "@medusajs/medusa"
import { MoneyAmountCell } from "../../../../../components/table/table-cells/common/money-amount-cell"
import { Thumbnail } from "../../../../../components/common/thumbnail"
import { Trash } from "@medusajs/icons"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { Form } from "../../../../../components/common/form"
type OrderEditItemProps = {
item: LineItem
currencyCode: string
form: UseFormReturn<Record<string, number>>
onQuantityChangeComplete: (id: string) => void
onRemove: (id: string) => void
}
function OrderEditItem({
item,
currencyCode,
form,
onQuantityChangeComplete,
onRemove,
}: OrderEditItemProps) {
const { t } = useTranslation()
const thumbnail = item.thumbnail
return (
<div className="bg-ui-bg-subtle shadow-elevation-card-rest my-2 rounded-xl">
<div className="flex gap-x-2 border-b p-3 text-sm">
<div className="flex flex-grow items-center gap-x-3">
<Thumbnail src={thumbnail} />
<div className="flex flex-col">
<div>
<Text as="span" weight="plus">
{item.title}
</Text>
{item.variant.sku && <span>(${item.variant.sku})</span>}
</div>
<Text as="div" className="text-ui-fg-subtle">
{item.variant.title}
</Text>
</div>
</div>
<div className="text-ui-fg-subtle flex flex-shrink-0">
<MoneyAmountCell currencyCode={currencyCode} amount={item.total} />
</div>
<div className="flex items-center">
<ActionMenu
groups={[
{
actions: [
{
label: t("actions.remove"),
icon: <Trash />,
onClick: () => onRemove(item.id),
},
],
},
]}
/>
</div>
</div>
<div className="block p-3 text-sm">
<Text weight="plus" className="mb-2">
{t("fields.quantity")}
</Text>
<Form.Field
control={form.control}
name={item.id}
render={({ field }) => {
return (
<Form.Item>
<Form.Control>
<Input
className="bg-ui-bg-base w-full rounded-lg"
min={1}
type="number"
{...field}
onChange={(e) => {
const val = e.target.value
field.onChange(val === "" ? null : Number(val))
}}
onBlur={() => {
if (typeof form.getValues()[item.id] === "undefined") {
field.onChange(1)
}
onQuantityChangeComplete(item.id)
}}
/>
</Form.Control>
<Form.ErrorMessage />
</Form.Item>
)
}}
/>
</div>
</div>
)
}
export { OrderEditItem }

View File

@@ -0,0 +1 @@
export * from "./variant-table"

View File

@@ -0,0 +1,97 @@
import React, { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { createColumnHelper } from "@tanstack/react-table"
import { Checkbox } from "@medusajs/ui"
import {
ProductCell,
ProductHeader,
} from "../../../../../components/table/table-cells/product/product-cell"
import { MoneyAmountCell } from "../../../../../components/table/table-cells/common/money-amount-cell"
import { PricedVariant } from "@medusajs/client-types"
const columnHelper = createColumnHelper<PricedVariant>()
export const useVariantTableColumns = (currencyCode: string) => {
const { t } = useTranslation()
return useMemo(
() => [
columnHelper.display({
id: "select",
header: ({ table }) => {
return (
<Checkbox
checked={
table.getIsSomePageRowsSelected()
? "indeterminate"
: table.getIsAllPageRowsSelected()
}
onCheckedChange={(value) =>
table.toggleAllPageRowsSelected(!!value)
}
/>
)
},
cell: ({ row }) => {
return (
<Checkbox
checked={row.getIsSelected()}
onCheckedChange={(value) => row.toggleSelected(!!value)}
onClick={(e) => {
e.stopPropagation()
}}
/>
)
},
}),
columnHelper.display({
id: "product",
header: () => <ProductHeader />,
cell: ({ row }) => <ProductCell product={row.original.product} />,
}),
columnHelper.accessor("sku", {
header: t("fields.sku"),
}),
columnHelper.accessor("title", {
header: t("fields.variant"),
}),
columnHelper.display({
id: "amount",
header: () => (
<div className="text-small text-right font-semibold text-gray-500">
{t("fields.price")}
</div>
),
cell: ({ row: { original } }) => {
if (!original.original_price_incl_tax) {
return null
}
const showOriginal = original.calculated_price_type !== "default"
return (
<div className="flex items-center justify-end gap-2">
<div className="flex flex-col items-end">
{showOriginal && (
<span className="text-gray-400 line-through">
<MoneyAmountCell
currencyCode={currencyCode}
amount={original.original_price_incl_tax}
/>
</span>
)}
<span>
<MoneyAmountCell
currencyCode={currencyCode}
amount={original.calculated_price_incl_tax}
/>
</span>
</div>
</div>
)
},
}),
],
[t]
)
}

View File

@@ -0,0 +1,17 @@
import { useTranslation } from "react-i18next"
import { Filter } from "../../../../../components/table/data-table"
export const useVariantTableFilters = () => {
const { t } = useTranslation()
const filters: Filter[] = [
{ label: t("fields.createdAt"), key: "created_at" },
{ label: t("fields.updatedAt"), key: "updated_at" },
].map((f) => ({
key: f.key,
label: f.label,
type: "date",
}))
return filters
}

View File

@@ -0,0 +1,32 @@
import { AdminGetVariantsParams } from "@medusajs/medusa"
import { useQueryParams } from "../../../../../hooks/use-query-params"
export const useVariantTableQuery = ({
pageSize = 50,
prefix,
}: {
pageSize?: number
prefix: string
}) => {
const raw = useQueryParams(
["offset", "q", "title", "customer_id", "inventory_quantity"],
prefix
)
const searchParams: AdminGetVariantsParams = {
limit: pageSize,
offset: raw.offset ? Number(raw.offset) : 0,
q: raw.q,
title: raw.title,
customer_id: raw.customer_id,
inventory_quantity: raw.inventory_quantity
? Number(raw.inventory_quantity)
: undefined,
}
return {
searchParams,
raw,
}
}

View File

@@ -0,0 +1,163 @@
import { PricedVariant } from "@medusajs/client-types"
import { useTranslation } from "react-i18next"
import { OnChangeFn, RowSelectionState } from "@tanstack/react-table"
import { useState } from "react"
import { useAdminVariants } from "medusa-react"
import { Button } from "@medusajs/ui"
import { Order } from "@medusajs/medusa"
import { DataTable } from "../../../../../components/table/data-table"
import { useDataTable } from "../../../../../hooks/use-data-table.tsx"
import { SplitView } from "../../../../../components/layout/split-view"
import { useVariantTableQuery } from "./use-variant-table-query"
import { useVariantTableColumns } from "./use-variant-table-columns"
import { useVariantTableFilters } from "./use-variant-table-filters"
const PAGE_SIZE = 50
export type Option = {
value: string
label: string
}
const Footer = ({
onSave,
isAddingItems,
}: {
isAddingItems: boolean
onSave: () => void
}) => {
const { t } = useTranslation()
return (
<div className="flex items-center justify-end gap-x-2 border-t p-4">
<SplitView.Close type="button" asChild>
<Button variant="secondary" size="small">
{t("general.close")}
</Button>
</SplitView.Close>
<Button
isLoading={isAddingItems}
size="small"
type="button"
onClick={onSave}
>
{t("general.add")}
</Button>
</div>
)
}
type VariantTableProps = {
onSave: (ids: string[]) => Promise<void>
isAddingItems: boolean
order: Order
}
export const VariantTable = ({
onSave,
order,
isAddingItems,
}: VariantTableProps) => {
const [rowSelection, setRowSelection] = useState<RowSelectionState>({})
const [intermediate, setIntermediate] = useState<string[]>([])
const { searchParams, raw } = useVariantTableQuery({
pageSize: PAGE_SIZE,
})
const { variants, count, isLoading, isError, error } = useAdminVariants(
{
...searchParams,
region_id: order.region_id,
cart_id: order.cart_id,
customer_id: order.customer_id,
currency_code: order.currency_code,
},
{
keepPreviousData: true,
}
)
const updater: OnChangeFn<RowSelectionState> = (fn) => {
const newState: RowSelectionState =
typeof fn === "function" ? fn(rowSelection) : fn
const added = Object.keys(newState).filter(
(k) => newState[k] !== rowSelection[k]
)
if (added.length) {
const addedProducts = (variants?.filter((v) => added.includes(v.id!)) ??
[]) as PricedVariant[]
if (addedProducts.length > 0) {
const newConditions = addedProducts.map((p) => p.id!)
setIntermediate((prev) => {
const filteredPrev = prev.filter((p) => newState[p])
return Array.from(new Set([...filteredPrev, ...newConditions]))
})
}
setRowSelection(newState)
}
const removed = Object.keys(rowSelection).filter(
(k) => newState[k] !== rowSelection[k]
)
if (removed.length) {
setIntermediate((prev) => {
return prev.filter((p) => !removed.includes(p))
})
setRowSelection(newState)
}
}
const handleSave = () => {
onSave(intermediate)
}
const columns = useVariantTableColumns(order.currency_code)
const filters = useVariantTableFilters()
const { table } = useDataTable({
data: (variants ?? []) as PricedVariant[],
columns: columns,
count,
enablePagination: true,
getRowId: (row) => row.id,
pageSize: PAGE_SIZE,
enableRowSelection: true,
rowSelection: {
state: rowSelection,
updater,
},
})
if (isError) {
throw error
}
return (
<div className="flex size-full flex-col overflow-hidden">
<DataTable
table={table}
columns={columns}
pageSize={PAGE_SIZE}
count={count}
queryObject={raw}
pagination
search
filters={filters}
isLoading={isLoading}
layout="fill"
orderBy={["title", "created_at", "updated_at"]}
/>
<Footer isAddingItems={isAddingItems} onSave={handleSave} />
</div>
)
}

View File

@@ -0,0 +1,2 @@
export { orderLoader as loader } from "./loader"
export { OrderEdit as Component } from "./order-edit"

View File

@@ -0,0 +1,25 @@
import { AdminOrdersRes } from "@medusajs/medusa"
import { Response } from "@medusajs/medusa-js"
import { adminOrderKeys } from "medusa-react"
import { LoaderFunctionArgs } from "react-router-dom"
import { medusa, queryClient } from "../../../lib/medusa"
import { orderExpand } from "../order-detail/constants"
const orderEditQuery = (id: string) => ({
queryKey: adminOrderKeys.detail(id),
queryFn: async () =>
medusa.admin.orders.retrieve(id, {
expand: orderExpand,
}),
})
export const orderLoader = async ({ params }: LoaderFunctionArgs) => {
const id = params.id
const query = orderEditQuery(id!)
return (
queryClient.getQueryData<Response<AdminOrdersRes>>(query.queryKey) ??
(await queryClient.fetchQuery(query))
)
}

View File

@@ -0,0 +1,77 @@
import {
useAdminCreateOrderEdit,
useAdminOrder,
useAdminOrderEdit,
} from "medusa-react"
import { useLoaderData, useParams } from "react-router-dom"
import { useEffect } from "react"
import { orderLoader } from "./loader"
import { orderExpand } from "../order-detail/constants"
import { RouteFocusModal } from "../../../components/route-modal"
import { OrderEditForm } from "./components/order-edit-form"
/**
* Flag to ensure OE creation in useEffect is only executed once
*/
let isOECreationRunning = false
export const OrderEdit = () => {
const { mutateAsync: createOrderEdit } = useAdminCreateOrderEdit()
const initialData = useLoaderData() as Awaited<ReturnType<typeof orderLoader>>
const { id } = useParams()
const { order, isLoading, isError, error } = useAdminOrder(
id!,
{
expand: orderExpand,
},
{
initialData,
}
)
// find created OE - there should exist only one per Order
const _orderEdit = order?.edits.find((oe) => oe.status === "created")
const { order_edit: orderEdit } = useAdminOrderEdit(
_orderEdit?.id as unknown as string,
{
expand: "changes,items,items.variant",
},
{ enabled: !!_orderEdit?.id }
)
useEffect(() => {
;(async () => {
if (!order || !!_orderEdit || isOECreationRunning) {
return
}
isOECreationRunning = true
await createOrderEdit({
order_id: order.id,
// created_by: // TODO
})
isOECreationRunning = false
})()
}, [order])
if (isLoading || !order || !orderEdit) {
// TODO: Add loader
return null
}
if (isError) {
throw error
}
return (
<RouteFocusModal>
{!isLoading && order && (
<OrderEditForm order={order} orderEdit={orderEdit} />
)}
</RouteFocusModal>
)
}