diff --git a/packages/admin/dashboard/src/hooks/api/order-edits.tsx b/packages/admin/dashboard/src/hooks/api/order-edits.tsx index 66d50a7d79..a7ae2e7c69 100644 --- a/packages/admin/dashboard/src/hooks/api/order-edits.tsx +++ b/packages/admin/dashboard/src/hooks/api/order-edits.tsx @@ -56,6 +56,10 @@ export const useRequestOrderEdit = ( queryClient.invalidateQueries({ queryKey: ordersQueryKeys.changes(id), }) + + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.lineItems(id), + }) options?.onSuccess?.(data, variables, context) }, ...options, @@ -85,6 +89,10 @@ export const useConfirmOrderEdit = ( queryKey: ordersQueryKeys.changes(id), }) + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.lineItems(id), + }) + queryClient.invalidateQueries({ queryKey: reservationItemsQueryKeys.lists(), }) @@ -121,6 +129,10 @@ export const useCancelOrderEdit = ( queryClient.invalidateQueries({ queryKey: ordersQueryKeys.changes(orderId), }) + + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.lineItems(id), + }) options?.onSuccess?.(data, variables, context) }, ...options, diff --git a/packages/admin/dashboard/src/hooks/api/orders.tsx b/packages/admin/dashboard/src/hooks/api/orders.tsx index b76c0af3fa..832fd8a1e6 100644 --- a/packages/admin/dashboard/src/hooks/api/orders.tsx +++ b/packages/admin/dashboard/src/hooks/api/orders.tsx @@ -1,5 +1,5 @@ import { FetchError } from "@medusajs/js-sdk" -import { HttpTypes } from "@medusajs/types" +import { AdminOrderItemsFilters, HttpTypes } from "@medusajs/types" import { QueryKey, useMutation, @@ -12,13 +12,10 @@ import { queryClient } from "../../lib/query-client" import { queryKeysFactory, TQueryKey } from "../../lib/query-key-factory" const ORDERS_QUERY_KEY = "orders" as const -const _orderKeys = queryKeysFactory(ORDERS_QUERY_KEY) as TQueryKey< - "orders", - any, - string -> & { +const _orderKeys = queryKeysFactory(ORDERS_QUERY_KEY) as TQueryKey<"orders"> & { preview: (orderId: string) => any changes: (orderId: string) => any + lineItems: (orderId: string) => any } _orderKeys.preview = function (id: string) { @@ -29,6 +26,10 @@ _orderKeys.changes = function (id: string) { return [this.detail(id), "changes"] } +_orderKeys.lineItems = function (id: string) { + return [this.detail(id), "lineItems"] +} + export const ordersQueryKeys = _orderKeys export const useOrder = ( @@ -97,7 +98,7 @@ export const useOrderChanges = ( options?: Omit< UseQueryOptions< HttpTypes.AdminOrderChangesResponse, - Error, + FetchError, HttpTypes.AdminOrderChangesResponse, QueryKey >, @@ -113,6 +114,28 @@ export const useOrderChanges = ( return { ...data, ...rest } } +export const useOrderLineItems = ( + id: string, + query?: HttpTypes.AdminOrderItemsFilters, + options?: Omit< + UseQueryOptions< + HttpTypes.AdminOrderLineItemsListResponse, + FetchError, + HttpTypes.AdminOrderLineItemsListResponse, + QueryKey + >, + "queryFn" | "queryKey" + > +) => { + const { data, ...rest } = useQuery({ + queryFn: async () => sdk.admin.order.listLineItems(id, query), + queryKey: ordersQueryKeys.lineItems(id), + ...options, + }) + + return { ...data, ...rest } +} + export const useCreateOrderFulfillment = ( orderId: string, options?: UseMutationOptions< diff --git a/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx b/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx index 10d159b403..97bea76d65 100644 --- a/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx +++ b/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/activity-items.tsx @@ -54,7 +54,7 @@ function ActivityItems(props: ActivityItemsProps) {
{!!itemsToSend?.length && ( @@ -73,12 +73,10 @@ function ActivityItems(props: ActivityItemsProps) { {item.quantity}x - + - {`${originalItem?.variant?.title} · ${originalItem?.variant?.product?.title}`} + {`${originalItem?.variant_title} · ${originalItem?.product_title}`}
) @@ -105,12 +103,10 @@ function ActivityItems(props: ActivityItemsProps) { {item.quantity}x - + - {`${originalItem?.variant?.title} · ${originalItem?.variant?.product?.title}`} + {`${originalItem?.variant_title} · ${originalItem?.product_title}`} ) diff --git a/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx b/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx index 502091a814..44148617e3 100644 --- a/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx +++ b/packages/admin/dashboard/src/routes/orders/order-detail/components/order-activity-section/order-timeline.tsx @@ -16,7 +16,7 @@ import { import { useTranslation } from "react-i18next" import { AdminOrderLineItem } from "@medusajs/types" -import { useOrderChanges } from "../../../../../hooks/api" +import { useOrderChanges, useOrderLineItems } from "../../../../../hooks/api" import { useCancelClaim, useClaims } from "../../../../../hooks/api/claims" import { useCancelExchange, @@ -112,14 +112,36 @@ type Activity = { const useActivityItems = (order: AdminOrder): Activity[] => { const { t } = useTranslation() - const itemsMap = useMemo( - () => new Map(order?.items?.map((i) => [i.id, i])), - [order.items] + const { order_changes: orderChanges = [] } = useOrderChanges(order.id, { + change_type: ["edit", "claim", "exchange", "return"], + }) + + const missingLineItemIds = getMissingLineItemIds(order, orderChanges) + const { order_items: removedLineItems = [] } = useOrderLineItems( + order.id, + + { + fields: "+quantity", + item_id: missingLineItemIds, + }, + { + enabled: !!orderChanges.length, + } ) - const { order_changes: orderChanges = [] } = useOrderChanges(order.id, { - change_type: "edit", - }) + const itemsMap = useMemo(() => { + const _itemsMap = new Map(order?.items?.map((i) => [i.id, i])) + + for (const id of missingLineItemIds) { + const i = removedLineItems.find((i) => i.item.id === id) + + if (i) { + _itemsMap.set(id, { ...i.item, quantity: i.quantity }) // copy quantity from OrderItem to OrderLineItem + } + } + + return _itemsMap + }, [order.items, removedLineItems, missingLineItemIds]) const { returns = [] } = useReturns({ order_id: order.id, @@ -226,9 +248,7 @@ const useActivityItems = (order: AdminOrder): Activity[] => { items.push({ title: t("orders.activity.events.fulfillment.delivered"), timestamp: fulfillment.delivered_at, - children: ( - - ), + children: , }) } @@ -334,7 +354,7 @@ const useActivityItems = (order: AdminOrder): Activity[] => { }) } - for (const edit of orderChanges) { + for (const edit of orderChanges.filter((oc) => oc.change_type === "edit")) { const isConfirmed = edit.status === "confirmed" const isPending = edit.status === "pending" @@ -350,10 +370,10 @@ const useActivityItems = (order: AdminOrder): Activity[] => { edit.status === "requested" ? edit.requested_at : edit.status === "declined" - ? edit.declined_at - : edit.status === "canceled" - ? edit.canceled_at - : edit.created_at, + ? edit.declined_at + : edit.status === "canceled" + ? edit.canceled_at + : edit.created_at, children: isConfirmed ? ( ) : null, @@ -390,7 +410,16 @@ const useActivityItems = (order: AdminOrder): Activity[] => { } return [...sortedActivities, createdAt] - }, [order, payments, returns, exchanges, orderChanges, notes, isLoading]) + }, [ + order, + payments, + returns, + exchanges, + orderChanges, + notes, + isLoading, + itemsMap, + ]) } type OrderActivityItemProps = PropsWithChildren<{ @@ -813,14 +842,13 @@ const OrderEditBody = ({ itemsMap, }: { edit: AdminOrderChange - isRequested: boolean - itemsMap: Record + itemsMap: Map }) => { const { t } = useTranslation() const [itemsAdded, itemsRemoved] = useMemo( () => countItemsChange(edit.actions, itemsMap), - [edit] + [edit, itemsMap] ) return ( @@ -840,21 +868,24 @@ const OrderEditBody = ({ ) } +/** + * Returns count of added and removed item quantity + */ function countItemsChange( actions: AdminOrderChange["actions"], - itemsMap: Record + itemsMap: Map ) { let added = 0 let removed = 0 actions.forEach((action) => { if (action.action === "ITEM_ADD") { - added += action.details.quantity + added += action.details!.quantity as number } if (action.action === "ITEM_UPDATE") { - const newQuantity: number = action.details!.quantity + const newQuantity = action.details!.quantity as number const originalQuantity: number | undefined = itemsMap.get( - action.details!.reference_id + action.details!.reference_id as string )?.quantity if (typeof originalQuantity === "number") { @@ -872,3 +903,28 @@ function countItemsChange( return [added, removed] } + +/** + * Get IDs of missing line items that were removed from the order. + */ +function getMissingLineItemIds(order: AdminOrder, changes: AdminOrderChange[]) { + if (!changes?.length) { + return [] + } + + const retIds = new Set() + const existingItemsMap = new Map(order.items.map((item) => [item.id, true])) + + changes.forEach((change) => { + change.actions.forEach((action) => { + if ( + (action.details!.reference_id as string).startsWith("ordli_") && + !existingItemsMap.has(action.details!.reference_id as string) + ) { + retIds.add(action.details!.reference_id as string) + } + }) + }) + + return Array.from(retIds) +} diff --git a/packages/core/js-sdk/src/admin/order.ts b/packages/core/js-sdk/src/admin/order.ts index d74db17a9f..1b0dd4ae68 100644 --- a/packages/core/js-sdk/src/admin/order.ts +++ b/packages/core/js-sdk/src/admin/order.ts @@ -148,4 +148,17 @@ export class Order { headers, }) } + + async listLineItems( + id: string, + queryParams?: FindParams & HttpTypes.AdminOrderItemsFilters, + headers?: ClientHeaders + ) { + return await this.client.fetch< + PaginatedResponse + >(`/admin/orders/${id}/line-items`, { + query: queryParams, + headers, + }) + } } diff --git a/packages/core/types/src/http/order/admin/entities.ts b/packages/core/types/src/http/order/admin/entities.ts index 29215d0e3a..f2c871b499 100644 --- a/packages/core/types/src/http/order/admin/entities.ts +++ b/packages/core/types/src/http/order/admin/entities.ts @@ -39,6 +39,19 @@ export interface AdminOrderChange actions: AdminOrderChangeAction[] } +export interface AdminOrderItem { + order_id: string + item_id: string + version: number + history: { + version: { + from: number + to: number + } + } + item: AdminOrderLineItem +} + export interface AdminOrderChangeAction extends Omit { order_change: AdminOrderChange diff --git a/packages/core/types/src/http/order/admin/queries.ts b/packages/core/types/src/http/order/admin/queries.ts index 7d9c1a40c9..8aed6321ab 100644 --- a/packages/core/types/src/http/order/admin/queries.ts +++ b/packages/core/types/src/http/order/admin/queries.ts @@ -14,6 +14,13 @@ export interface AdminOrderFilters extends FindParams, BaseOrderFilters { updated_at?: OperatorMap } +export interface AdminOrderItemsFilters extends FindParams { + id?: string[] | string + item_id?: string[] | string + order_id?: string[] | string + version?: number[] | number +} + export interface AdminOrderChangesFilters extends BaseOrderChangesFilters {} export interface AdminOrderItemsFilters extends FindParams {