From 4c2e9a3239f43773cc8f477f45ccc4ee8db2f1b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Frane=20Poli=C4=87?= <16856471+fPolic@users.noreply.github.com> Date: Fri, 12 Jul 2024 09:57:00 +0200 Subject: [PATCH] feat(dashboard): allocate items (#8021) **What** - add Allocate items flow --- https://github.com/medusajs/medusa/assets/16856471/8485c8dc-b1f2-4239-bb22-996345d5d2ad --- .../dashboard/src/i18n/translations/en.json | 23 +- .../providers/router-provider/route-map.tsx | 5 + .../inventory-item-general-section.tsx | 4 +- .../constants.ts | 6 + .../order-create-fulfillment-form/index.ts | 1 + .../order-allocate-items-form.tsx | 330 +++++++++++++++++ .../order-allocate-items-item.tsx | 348 ++++++++++++++++++ .../orders/order-allocate-items/index.ts | 1 + .../order-allocate-items.tsx | 26 ++ .../order-summary-section.tsx | 95 ++++- .../routes/orders/order-detail/constants.ts | 1 + .../reservation-general-section.tsx | 7 +- 12 files changed, 820 insertions(+), 27 deletions(-) create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/constants.ts create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/index.ts create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-item.tsx create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-allocate-items/index.ts create mode 100644 packages/admin-next/dashboard/src/routes/orders/order-allocate-items/order-allocate-items.tsx diff --git a/packages/admin-next/dashboard/src/i18n/translations/en.json b/packages/admin-next/dashboard/src/i18n/translations/en.json index c153077b12..ff9e735362 100644 --- a/packages/admin-next/dashboard/src/i18n/translations/en.json +++ b/packages/admin-next/dashboard/src/i18n/translations/en.json @@ -578,7 +578,7 @@ "reservation": { "header": "Reservation of {{itemName}}", "editItemDetails": "Edit reservation", - "orderID": "Order ID", + "lineItemId": "Line item ID", "description": "Description", "location": "Location", "inStockAtLocation": "In stock at this location", @@ -765,6 +765,22 @@ "allocatedLabel": "Allocated", "notAllocatedLabel": "Not allocated" }, + "allocateItems": { + "action": "Allocate items", + "title": "Allocate order items", + "locationDescription": "Choose which location you want to allocate from.", + "itemsToAllocate": "Items to allocate", + "itemsToAllocateDesc": "Select the number of items you wish to allocate", + "search": "Search items", + "consistsOf": "Consists of {{num}}x inventory items", + "requires": "Requires {{num}} per variant", + "toast": { + "created": "Items successfully allocated" + }, + "error": { + "quantityNotAllocated": "There are unallocated items." + } + }, "shipment": { "title": "Mark fulfillment shipped", "trackingNumber": "Tracking number", @@ -2055,7 +2071,9 @@ }, "labels": { "productVariant": "Product Variant", - "prices": "Prices" + "prices": "Prices", + "available": "Available", + "inStock": "In stock" }, "fields": { "amount": "Amount", @@ -2071,6 +2089,7 @@ "inventoryItems": "Inventory items", "inventoryItem": "Inventory item", "requiredQuantity": "Required quantity", + "qty": "Qty", "description": "Description", "email": "Email", "password": "Password", diff --git a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx index f8b5327bcd..21a05636ef 100644 --- a/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx +++ b/packages/admin-next/dashboard/src/providers/router-provider/route-map.tsx @@ -217,6 +217,11 @@ export const RouteMap: RouteObject[] = [ lazy: () => import("../../routes/orders/order-create-fulfillment"), }, + { + path: "allocate-items", + lazy: () => + import("../../routes/orders/order-allocate-items"), + }, { path: ":f_id/create-shipment", lazy: () => diff --git a/packages/admin-next/dashboard/src/routes/inventory/inventory-detail/components/inventory-item-general-section.tsx b/packages/admin-next/dashboard/src/routes/inventory/inventory-detail/components/inventory-item-general-section.tsx index a6aa2e7d7f..67b8fdb0f0 100644 --- a/packages/admin-next/dashboard/src/routes/inventory/inventory-detail/components/inventory-item-general-section.tsx +++ b/packages/admin-next/dashboard/src/routes/inventory/inventory-detail/components/inventory-item-general-section.tsx @@ -1,13 +1,13 @@ import { Container, Heading } from "@medusajs/ui" +import { AdminInventoryItemResponse } from "@medusajs/types" import { ActionMenu } from "../../../../components/common/action-menu" -import { InventoryItemRes } from "../../../../types/api-responses" import { PencilSquare } from "@medusajs/icons" import { SectionRow } from "../../../../components/common/section" import { useTranslation } from "react-i18next" type InventoryItemGeneralSectionProps = { - inventoryItem: InventoryItemRes["inventory_item"] + inventoryItem: AdminInventoryItemResponse["inventory_item"] } export const InventoryItemGeneralSection = ({ inventoryItem, diff --git a/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/constants.ts b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/constants.ts new file mode 100644 index 0000000000..73f62339c8 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/constants.ts @@ -0,0 +1,6 @@ +import { z } from "zod" + +export const AllocateItemsSchema = z.object({ + location_id: z.string(), + quantity: z.record(z.string(), z.number().or(z.string())), +}) diff --git a/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/index.ts b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/index.ts new file mode 100644 index 0000000000..779dd5dade --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/index.ts @@ -0,0 +1 @@ +export * from "./order-allocate-items-form" diff --git a/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx new file mode 100644 index 0000000000..ed16d857c2 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-form.tsx @@ -0,0 +1,330 @@ +import { zodResolver } from "@hookform/resolvers/zod" +import { useEffect, useMemo, useState } from "react" +import { useTranslation } from "react-i18next" +import * as zod from "zod" + +import { AdminOrder, InventoryItemDTO, OrderLineItemDTO } from "@medusajs/types" +import { Alert, Button, Heading, Input, Select, toast } from "@medusajs/ui" +import { useForm, useWatch } from "react-hook-form" + +import { + RouteFocusModal, + useRouteModal, +} from "../../../../../components/modals" +import { Form } from "../../../../../components/common/form" +import { useStockLocations } from "../../../../../hooks/api/stock-locations" +import { useCreateReservationItem } from "../../../../../hooks/api/reservations" +import { OrderAllocateItemsItem } from "./order-allocate-items-item" +import { AllocateItemsSchema } from "./constants" +import { queryClient } from "../../../../../lib/query-client" +import { ordersQueryKeys } from "../../../../../hooks/api/orders" + +type OrderAllocateItemsFormProps = { + order: AdminOrder +} + +export function OrderAllocateItemsForm({ order }: OrderAllocateItemsFormProps) { + const { t } = useTranslation() + const { handleSuccess } = useRouteModal() + + const [disableSubmit, setDisableSubmit] = useState(false) + const [filterTerm, setFilterTerm] = useState("") + + const { mutateAsync: allocateItems, isPending: isMutating } = + useCreateReservationItem() + + const itemsToAllocate = useMemo( + () => + order.items.filter( + (item) => + item.variant.manage_inventory && + item.variant.inventory.length && + item.quantity - item.detail.fulfilled_quantity > 0 + ), + [order.items] + ) + + const filteredItems = useMemo(() => { + return itemsToAllocate.filter( + (i) => + i.variant.title.toLowerCase().includes(filterTerm) || + i.variant.product.title.toLowerCase().includes(filterTerm) + ) + }, [itemsToAllocate, filterTerm]) + + // TODO - empty state UI + const noItemsToAllocate = !itemsToAllocate.length + + const form = useForm>({ + defaultValues: { + location_id: "", + quantity: defaultAllocations(itemsToAllocate), + }, + resolver: zodResolver(AllocateItemsSchema), + }) + + const { stock_locations = [] } = useStockLocations() + + const handleSubmit = form.handleSubmit(async (data) => { + try { + const payload = Object.entries(data.quantity) + .filter(([key]) => !key.endsWith("-")) + .map(([key, quantity]) => [...key.split("-"), quantity]) + + if (payload.some((d) => d[2] === "")) { + form.setError("root.quantityNotAllocated", { + type: "manual", + message: t("orders.allocateItems.error.quantityNotAllocated"), + }) + + return + } + + const promises = payload.map(([itemId, inventoryId, quantity]) => + allocateItems({ + location_id: data.location_id, + inventory_item_id: inventoryId, + line_item_id: itemId, + quantity, + }) + ) + + /** + * TODO: we should have bulk endpoint for this so this is executed in a workflow and can be reverted + */ + await Promise.all(promises) + + // invalidate order details so we get new item.variant.inventory items + await queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.details(), + }) + + handleSuccess(`/orders/${order.id}`) + + toast.success(t("general.success"), { + description: t("orders.allocateItems.toast.created"), + dismissLabel: t("actions.close"), + }) + } catch (e) { + toast.error(t("general.error"), { + description: e.message, + dismissLabel: t("actions.close"), + }) + } + }) + + const onQuantityChange = ( + inventoryItem: InventoryItemDTO, + lineItem: OrderLineItemDTO, + hasInventoryKit: boolean, + value: number | null, + isRoot?: boolean + ) => { + let shouldDisableSubmit = false + + const key = + isRoot && hasInventoryKit + ? `quantity.${lineItem.id}-` + : `quantity.${lineItem.id}-${inventoryItem.id}` + + form.setValue(key, value) + + if (value) { + const location = inventoryItem.location_levels.find( + (l) => l.location_id === selectedLocationId + ) + if (location) { + if (location.available_quantity < value) { + shouldDisableSubmit = true + } + } + } + + if (hasInventoryKit && !isRoot) { + // changed subitem in the kit -> we need to set parent to "-" + form.resetField(`quantity.${lineItem.id}-`, { defaultValue: "" }) + } + + if (hasInventoryKit && isRoot) { + // changed root -> we need to set items to parent quantity x required_quantity + + const item = itemsToAllocate.find((i) => i.id === lineItem.id) + + item.variant.inventory_items.forEach((ii, ind) => { + const num = value || 0 + const inventory = item.variant.inventory[ind] + + form.setValue( + `quantity.${lineItem.id}-${inventory.id}`, + num * ii.required_quantity + ) + + if (value) { + const location = inventory.location_levels.find( + (l) => l.location_id === selectedLocationId + ) + if (location) { + if (location.available_quantity < value) { + shouldDisableSubmit = true + } + } + } + }) + } + + form.clearErrors("root.quantityNotAllocated") + setDisableSubmit(shouldDisableSubmit) + } + + const selectedLocationId = useWatch({ + name: "location_id", + control: form.control, + }) + + useEffect(() => { + if (selectedLocationId) { + form.setValue("quantity", defaultAllocations(itemsToAllocate)) + } + }, [selectedLocationId]) + + const allocationError = + form.formState.errors?.root?.quantityNotAllocated?.message + + return ( + +
+ +
+ + + + +
+
+ +
+
+
+ {t("orders.allocateItems.title")} +
+ { + return ( + +
+
+ {t("fields.location")} + + {t("orders.allocateItems.locationDescription")} + +
+
+ + + +
+
+ +
+ ) + }} + /> + + +
+
+ + {t("orders.allocateItems.itemsToAllocate")} + + + {t("orders.allocateItems.itemsToAllocateDesc")} + +
+
+ setFilterTerm(e.target.value)} + placeholder={t("orders.allocateItems.search")} + autoComplete="off" + type="search" + /> +
+
+ + {allocationError && ( + + {allocationError} + + )} + +
+ {filteredItems.map((item) => ( + + ))} +
+
+
+
+
+
+
+
+
+ ) +} + +function defaultAllocations(items: OrderLineItemDTO) { + const ret = {} + + items.forEach((item) => { + const hasInventoryKit = item.variant.inventory_items.length > 1 + + ret[ + hasInventoryKit + ? `${item.id}-` + : `${item.id}-${item.variant.inventory[0].id}` + ] = "" + + if (hasInventoryKit) { + item.variant.inventory.forEach((i) => { + ret[`${item.id}-${i.id}`] = "" + }) + } + }) + + return ret +} diff --git a/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-item.tsx b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-item.tsx new file mode 100644 index 0000000000..bf6c7aa03d --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/components/order-create-fulfillment-form/order-allocate-items-item.tsx @@ -0,0 +1,348 @@ +import { useMemo, useState } from "react" +import { useTranslation } from "react-i18next" +import { InventoryItemDTO, OrderLineItemDTO } from "@medusajs/types" +import { + Component, + ExclamationCircleSolid, + TriangleDownMini, +} from "@medusajs/icons" +import { UseFormReturn, useWatch } from "react-hook-form" +import { Input, Text, clx } from "@medusajs/ui" +import * as zod from "zod" + +import { Thumbnail } from "../../../../../components/common/thumbnail" +import { getFulfillableQuantity } from "../../../../../lib/order-item" +import { Form } from "../../../../../components/common/form" +import { AllocateItemsSchema } from "./constants" + +type OrderEditItemProps = { + item: OrderLineItemDTO + locationId?: string + form: UseFormReturn> + onQuantityChange: ( + inventoryItem: InventoryItemDTO, + lineItem: OrderLineItemDTO, + hasInventoryKit: boolean, + value: number | null, + isRoot?: boolean + ) => {} +} + +export function OrderAllocateItemsItem({ + item, + form, + locationId, + onQuantityChange, +}: OrderEditItemProps) { + const { t } = useTranslation() + + const variant = item.variant + const inventory = item.variant.inventory + + const [isOpen, setIsOpen] = useState(false) + + const quantityField = useWatch({ + control: form.control, + name: "quantity", + }) + + const hasInventoryKit = + !!variant?.inventory_items.length && variant?.inventory_items.length > 1 + + const { availableQuantity, inStockQuantity } = useMemo(() => { + if (!variant || !locationId) { + return {} + } + + const { inventory } = variant + + const locationInventory = inventory[0]?.location_levels?.find( + (inv) => inv.location_id === locationId + ) + + if (!locationInventory) { + return {} + } + + return { + availableQuantity: locationInventory.available_quantity, + inStockQuantity: locationInventory.stocked_quantity, + } + }, [variant, locationId]) + + const hasQuantityError = + !hasInventoryKit && + availableQuantity && + quantityField[`${item.id}-${item.variant.inventory[0].id}`] && + quantityField[`${item.id}-${item.variant.inventory[0].id}`] > + availableQuantity + + const minValue = 0 + const maxValue = Math.min( + getFulfillableQuantity(item), + availableQuantity || Number.MAX_SAFE_INTEGER + ) + + return ( +
+
+
+
+ {hasQuantityError && ( + + )} + +
+
+ + {item.variant.product.title} + + {item.variant.sku && ( + + {" "} + ({item.variant.sku}) + + )} + {hasInventoryKit && ( + + )} +
+ + {item.title} + +
+
+
+ +
+ {!hasInventoryKit && ( + <> +
+
+ +
+ + {t("labels.available")} + + + {availableQuantity || "-"} + {availableQuantity && + !hasInventoryKit && + quantityField[ + `${item.id}-${item.variant.inventory[0].id}` + ] && ( + + - + { + quantityField[ + `${item.id}-${item.variant.inventory[0].id}` + ] + } + + )} + +
+
+ +
+
+ +
+ + {t("labels.inStock")} + + + {inStockQuantity || "-"} + +
+
+ + )} + +
+
+ +
+ { + return ( + + + { + const val = + e.target.value === "" + ? null + : Number(e.target.value) + + onQuantityChange( + item.variant.inventory[0], + item, + hasInventoryKit, + val, + true + ) + }} + /> + + + ) + }} + />{" "} + / {item.quantity} {t("fields.qty")} +
+
+
+
+ + {hasInventoryKit && ( +
+
setIsOpen((o) => !o)} + className="flex items-center gap-x-2" + > + + + {t("orders.allocateItems.consistsOf", { + num: inventory.length, + })} + +
+
+ )} + + {isOpen && + variant.inventory.map((i, ind) => { + const location = i.location_levels.find( + (l) => l.location_id === locationId + ) + + const hasQuantityError = + !!quantityField[`${item.id}-${i.id}`] && + quantityField[`${item.id}-${i.id}`] > location.available_quantity + + return ( +
+
+ {hasQuantityError && ( + + )} +
+ {i.title} + + {t("orders.allocateItems.requires", { + num: variant.inventory_items[ind].required_quantity, + })} + +
+
+ +
+
+
+ +
+ + {t("labels.available")} + + + {location?.available_quantity || "-"} + {location?.available_quantity && + quantityField[`${item.id}-${i.id}`] && ( + + -{quantityField[`${item.id}-${i.id}`]} + + )} + +
+
+ +
+
+ +
+ + {t("labels.inStock")} + + + {location?.stocked_quantity || "-"} + +
+
+ +
+
+ +
+ { + return ( + + + { + const val = + e.target.value === "" + ? null + : Number(e.target.value) + + onQuantityChange( + i, + item, + hasInventoryKit, + val + ) + }} + /> + + + ) + }} + /> + /{" "} + {item.quantity * + variant.inventory_items[ind].required_quantity}{" "} + {t("fields.qty")} +
+
+
+
+ ) + })} +
+ ) +} diff --git a/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/index.ts b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/index.ts new file mode 100644 index 0000000000..cffd2a2c63 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/index.ts @@ -0,0 +1 @@ +export { OrderAllocateItems as Component } from "./order-allocate-items" diff --git a/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/order-allocate-items.tsx b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/order-allocate-items.tsx new file mode 100644 index 0000000000..4616eccad5 --- /dev/null +++ b/packages/admin-next/dashboard/src/routes/orders/order-allocate-items/order-allocate-items.tsx @@ -0,0 +1,26 @@ +import { useParams } from "react-router-dom" + +import { useOrder } from "../../../hooks/api/orders" +import { RouteFocusModal } from "../../../components/modals" +import { OrderAllocateItemsForm } from "./components/order-create-fulfillment-form" + +export function OrderAllocateItems() { + const { id } = useParams() + + const { order, isLoading, isError, error } = useOrder(id!, { + fields: + "currency_code,*items,*items.variant,+items.variant.product.title,*items.variant.inventory,*items.variant.inventory.location_levels,*items.variant.inventory_items,*shipping_address", + }) + + if (isError) { + throw error + } + + const ready = !isLoading && order + + return ( + + {ready && } + + ) +} diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx index 2d811b57ed..41140d9d87 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/components/order-summary-section/order-summary-section.tsx @@ -3,8 +3,17 @@ import { OrderLineItemDTO, ReservationItemDTO, } from "@medusajs/types" -import { Container, Copy, Heading, StatusBadge, Text } from "@medusajs/ui" +import { + Button, + Container, + Copy, + Heading, + StatusBadge, + Text, +} from "@medusajs/ui" import { useTranslation } from "react-i18next" +import { useNavigate } from "react-router-dom" +import { useMemo } from "react" import { ActionMenu } from "../../../../../components/common/action-menu" import { Thumbnail } from "../../../../../components/common/thumbnail" @@ -12,18 +21,65 @@ import { getLocaleAmount, getStylizedAmount, } from "../../../../../lib/money-amount-helpers" +import { useReservationItems } from "../../../../../hooks/api/reservations" type OrderSummarySectionProps = { order: AdminOrder } export const OrderSummarySection = ({ order }: OrderSummarySectionProps) => { + const { t } = useTranslation() + const navigate = useNavigate() + + const { reservations } = useReservationItems({ + line_item_id: order.items.map((i) => i.id), + }) + + /** + * 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 + }, [reservations]) + return (
+ + {showAllocateButton && ( +
+ +
+ )} ) } @@ -71,6 +127,7 @@ const Item = ({ reservation?: ReservationItemDTO | null }) => { const { t } = useTranslation() + const isInventoryManaged = item.variant.manage_inventory return (
- - {reservation - ? t("orders.reservations.allocatedLabel") - : t("orders.reservations.notAllocatedLabel")} - + {isInventoryManaged && ( + + {reservation + ? t("orders.reservations.allocatedLabel") + : t("orders.reservations.notAllocatedLabel")} + + )}
@@ -133,27 +192,23 @@ const Item = ({ } const ItemBreakdown = ({ order }: { order: AdminOrder }) => { - // const { reservations, isError, error } = useAdminReservations({ - // line_item_id: order.items.map((i) => i.id), - // }) - - // if (isError) { - // throw error - // } + const { reservations } = useReservationItems({ + line_item_id: order.items.map((i) => i.id), + }) return (
{order.items.map((item) => { - // const reservation = reservations - // ? reservations.find((r) => r.line_item_id === item.id) - // : null + const reservation = reservations + ? reservations.find((r) => r.line_item_id === item.id) + : null return ( ) })} diff --git a/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts b/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts index 1ab3ceac18..7d2c681563 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts +++ b/packages/admin-next/dashboard/src/routes/orders/order-detail/constants.ts @@ -18,6 +18,7 @@ const DEFAULT_RELATIONS = [ "*customer", "*items", // -> we get LineItem here with added `quantity` and `detail` which is actually an OrderItem (which is a parent object to LineItem in the DB) "*items.variant.options", + "+items.variant.manage_inventory", "*shipping_address", "*billing_address", "*sales_channel", diff --git a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx index 1c8b6d65a6..febc33afeb 100644 --- a/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx +++ b/packages/admin-next/dashboard/src/routes/reservations/reservation-detail/components/reservation-general-section/reservation-general-section.tsx @@ -1,4 +1,4 @@ -import { AdminReservationResponse, StockLocationDTO } from "@medusajs/types" +import { AdminReservationResponse } from "@medusajs/types" import { Container, Heading } from "@medusajs/ui" import { ActionMenu } from "../../../../../components/common/action-menu" @@ -8,6 +8,7 @@ import { SectionRow } from "../../../../../components/common/section" import { useInventoryItem } from "../../../../../hooks/api/inventory" import { useStockLocation } from "../../../../../hooks/api/stock-locations" import { useTranslation } from "react-i18next" +import { useOrder } from "../../../../../hooks/api" type ReservationGeneralSectionProps = { reservation: AdminReservationResponse["reservation"] @@ -61,8 +62,8 @@ export const ReservationGeneralSection = ({ />