From eb590417bec211e6beddeb97381205e2c3269f32 Mon Sep 17 00:00:00 2001 From: Riqwan Thamir Date: Wed, 7 Aug 2024 15:27:04 +0200 Subject: [PATCH] feat(core-flows,dashboard,medusa): ability to add and remove items to claim inbound (#8480) * wip: setup UI * wip: rendering modal, adding claim items, create checks * fix: make form work after merge * fix: continuation of claim edit * chore: ability to add and remove items to claim inbound * chore: minor fixes --------- Co-authored-by: fPolic --- .../dashboard/src/hooks/api/claims.tsx | 22 ++-- .../order-create-claim/claim-create.tsx | 36 ++---- .../add-claim-items-table.tsx | 15 +-- .../claim-create-form/claim-create-form.tsx | 122 +++++++++++------- .../routes/orders/order-detail/constants.ts | 1 + .../claim/claim-request-item-return.ts | 7 +- .../claim/create-claim-shipping-method.ts | 1 - .../return/remove-item-return-action.ts | 12 +- packages/core/types/src/http/claim/common.ts | 6 +- .../[id]/inbound/items/[action_id]/route.ts | 23 ++-- .../shipping-method/[action_id]/route.ts | 8 +- 11 files changed, 140 insertions(+), 113 deletions(-) diff --git a/packages/admin-next/dashboard/src/hooks/api/claims.tsx b/packages/admin-next/dashboard/src/hooks/api/claims.tsx index 279979c9c5..d148598b89 100644 --- a/packages/admin-next/dashboard/src/hooks/api/claims.tsx +++ b/packages/admin-next/dashboard/src/hooks/api/claims.tsx @@ -1,3 +1,4 @@ +import { HttpTypes } from "@medusajs/types" import { QueryKey, useMutation, @@ -5,21 +6,25 @@ import { useQuery, UseQueryOptions, } from "@tanstack/react-query" -import { HttpTypes } from "@medusajs/types" import { sdk } from "../../lib/client" import { queryClient } from "../../lib/query-client" -import { ordersQueryKeys } from "./orders" import { queryKeysFactory } from "../../lib/query-key-factory" +import { ordersQueryKeys } from "./orders" const CLAIMS_QUERY_KEY = "claims" as const export const claimsQueryKeys = queryKeysFactory(CLAIMS_QUERY_KEY) export const useClaim = ( id: string, - query?: Record, + query?: HttpTypes.AdminClaimListParams, options?: Omit< - UseQueryOptions, + UseQueryOptions< + HttpTypes.AdminClaimResponse, + Error, + HttpTypes.AdminClaimResponse, + QueryKey + >, "queryFn" | "queryKey" > ) => { @@ -56,10 +61,7 @@ export const useClaims = ( export const useCreateClaim = ( orderId: string, options?: UseMutationOptions< - { - claim: HttpTypes.AdminClaimResponse - order: HttpTypes.AdminOrderResponse - }, + HttpTypes.AdminClaimResponse, Error, HttpTypes.AdminCreateClaim > @@ -278,6 +280,10 @@ export const useRemoveClaimInboundItem = ( queryClient.invalidateQueries({ queryKey: ordersQueryKeys.preview(orderId), }) + queryClient.invalidateQueries({ + queryKey: ordersQueryKeys.all, + }) + options?.onSuccess?.(data, variables, context) }, ...options, diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/claim-create.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/claim-create.tsx index e3f80544d5..ad40dc71bc 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/claim-create.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/claim-create.tsx @@ -1,15 +1,13 @@ -import { useEffect, useMemo, useState } from "react" +import { toast } from "@medusajs/ui" +import { useEffect, useState } from "react" import { useTranslation } from "react-i18next" import { useNavigate, useParams } from "react-router-dom" -import { toast } from "@medusajs/ui" - import { RouteFocusModal } from "../../../components/modals" -import { ClaimCreateForm } from "./components/claim-create-form" - +import { useClaim, useCreateClaim } from "../../../hooks/api/claims" import { useOrder, useOrderPreview } from "../../../hooks/api/orders" -import { useClaims, useCreateClaim } from "../../../hooks/api/claims" import { DEFAULT_FIELDS } from "../order-detail/constants" +import { ClaimCreateForm } from "./components/claim-create-form" let IS_REQUEST_RUNNING = false @@ -23,28 +21,13 @@ export const ClaimCreate = () => { }) const { order: preview } = useOrderPreview(id!) - - const [activeClaimId, setActiveClaimId] = useState() - + const [activeClaimId, setActiveClaimId] = useState() const { mutateAsync: createClaim } = useCreateClaim(order.id) - // TODO: GET /claims/:id is not implemented - // const { claim } = useClaim(activeClaimId, undefined, { - // enabled: !!activeClaimId, - // }) - - // TEMP HACK: until the endpoint above is implemented - const { claims } = useClaims(undefined, { + const { claim } = useClaim(activeClaimId!, undefined, { enabled: !!activeClaimId, - limit: 999, }) - const claim = useMemo(() => { - if (claims) { - return claims.find((c) => c.id === activeClaimId) - } - }, [claims, activeClaimId]) - useEffect(() => { async function run() { if (IS_REQUEST_RUNNING || !preview) { @@ -65,14 +48,15 @@ export const ClaimCreate = () => { IS_REQUEST_RUNNING = true try { - const { claim } = await createClaim({ + const { claim: createdClaim } = await createClaim({ order_id: preview.id, type: "replace", }) - setActiveClaimId(claim.id) + + setActiveClaimId(createdClaim.id) } catch (e) { - navigate(`/orders/${preview.id}`, { replace: true }) toast.error(e.message) + navigate(`/orders/${preview.id}`, { replace: true }) } finally { IS_REQUEST_RUNNING = false } diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/add-claim-items-table/add-claim-items-table.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/add-claim-items-table/add-claim-items-table.tsx index b3b93c03c7..2bd2ec411e 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/add-claim-items-table/add-claim-items-table.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/add-claim-items-table/add-claim-items-table.tsx @@ -1,19 +1,18 @@ -import { OnChangeFn, RowSelectionState } from "@tanstack/react-table" -import { useMemo, useState } from "react" - import { + AdminOrderLineItem, DateComparisonOperator, NumericalComparisonOperator, } from "@medusajs/types" -import { AdminOrderLineItem } from "@medusajs/types" +import { OnChangeFn, RowSelectionState } from "@tanstack/react-table" +import { useMemo, useState } from "react" +import { DataTable } from "../../../../../components/table/data-table" +import { useDataTable } from "../../../../../hooks/use-data-table" +import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" +import { getReturnableQuantity } from "../../../../../lib/rma" import { useClaimItemTableColumns } from "./use-claim-item-table-columns" import { useClaimItemTableFilters } from "./use-claim-item-table-filters" import { useClaimItemTableQuery } from "./use-claim-item-table-query" -import { useDataTable } from "../../../../../hooks/use-data-table" -import { DataTable } from "../../../../../components/table/data-table" -import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" -import { getReturnableQuantity } from "../../../../../lib/rma" const PAGE_SIZE = 50 const PREFIX = "rit" diff --git a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx index c574a71849..b7efc5d04f 100644 --- a/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx +++ b/packages/admin-next/dashboard/src/routes/orders/order-create-claim/components/claim-create-form/claim-create-form.tsx @@ -1,5 +1,6 @@ -import React, { useEffect, useMemo, useState } from "react" import { zodResolver } from "@hookform/resolvers/zod" +import { PencilSquare } from "@medusajs/icons" +import { AdminClaim, AdminOrder, InventoryLevelDTO } from "@medusajs/types" import { Alert, Button, @@ -10,10 +11,9 @@ import { Text, toast, } from "@medusajs/ui" +import { useEffect, useMemo, useState } from "react" import { useFieldArray, useForm } from "react-hook-form" import { useTranslation } from "react-i18next" -import { AdminClaim, AdminOrder, InventoryLevelDTO } from "@medusajs/types" -import { PencilSquare } from "@medusajs/icons" import { RouteFocusModal, @@ -22,25 +22,26 @@ import { useStackedModal, } from "../../../../../components/modals" -import { ClaimCreateSchema, ReturnCreateSchemaType } from "./schema" -import { AddClaimItemsTable } from "../add-claim-items-table" import { Form } from "../../../../../components/common/form" -import { ClaimInboundItem } from "./claim-inbound-item.tsx" import { Combobox } from "../../../../../components/inputs/combobox" -import { useStockLocations } from "../../../../../hooks/api/stock-locations" import { useShippingOptions } from "../../../../../hooks/api/shipping-options" +import { useStockLocations } from "../../../../../hooks/api/stock-locations" import { getStylizedAmount } from "../../../../../lib/money-amount-helpers" +import { AddClaimItemsTable } from "../add-claim-items-table" +import { ClaimInboundItem } from "./claim-inbound-item.tsx" +import { ClaimCreateSchema, ReturnCreateSchemaType } from "./schema" -import { currencies } from "../../../../../lib/data/currencies" -import { sdk } from "../../../../../lib/client" import { useAddClaimInboundItems, useAddClaimInboundShipping, + useCancelClaimRequest, useDeleteClaimInboundShipping, useRemoveClaimInboundItem, useUpdateClaimInboundItem, useUpdateClaimInboundShipping, } from "../../../../../hooks/api/claims" +import { sdk } from "../../../../../lib/client" +import { currencies } from "../../../../../lib/data/currencies" type ReturnCreateFormProps = { order: AdminOrder @@ -48,7 +49,8 @@ type ReturnCreateFormProps = { preview: AdminOrder } -let selectedItems: string[] = [] +let itemsToAdd: string[] = [] +let itemsToRemove: string[] = [] let IS_CANCELING = false @@ -87,7 +89,8 @@ export const ClaimCreateForm = ({ */ const { mutateAsync: confirmClaimRequest, isPending: isConfirming } = {} // useConfirmClaimRequest(claim.id, order.id) - const { mutateAsync: cancelClaimRequest, isPending: isCanceling } = {} // useCancelClaimRequest(claim.id, order.id) + const { mutateAsync: cancelClaimRequest, isPending: isCanceling } = + useCancelClaimRequest(claim.id, order.id) const { mutateAsync: updateClaimRequest, isPending: isUpdating } = {} // useUpdateClaim(claim.id, order.id) @@ -244,13 +247,24 @@ export const ClaimCreateForm = ({ } }) - const onItemsSelected = () => { - addInboundItem({ - items: selectedItems.map((id) => ({ - id, - quantity: 1, - })), - }) + const onItemsSelected = async () => { + itemsToAdd.length && + (await addInboundItem({ + items: itemsToAdd.map((id) => ({ + id, + quantity: 1, + })), + })) + + for (const itemToRemove of itemsToRemove) { + const actionId = previewItems + .find((i) => i.id === itemToRemove) + ?.actions?.find((a) => a.action === "RETURN_ITEM")?.id + + if (actionId) { + await removeInboundItem(actionId) + } + } setIsOpen("items", false) } @@ -393,8 +407,18 @@ export const ClaimCreateForm = ({ items={order.items!} selectedItems={items.map((i) => i.item_id)} currencyCode={order.currency_code} - onSelectionChange={(s) => (selectedItems = s)} + onSelectionChange={(finalSelection) => { + const alreadySelected = items.map((i) => i.item_id) + + itemsToAdd = finalSelection.filter( + (selection) => !alreadySelected.includes(selection) + ) + itemsToRemove = alreadySelected.filter( + (selection) => !finalSelection.includes(selection) + ) + }} /> +
@@ -413,7 +437,7 @@ export const ClaimCreateForm = ({ variant="primary" size="small" role="button" - onClick={() => onItemsSelected()} + onClick={async () => await onItemsSelected()} > {t("actions.save")} @@ -423,6 +447,7 @@ export const ClaimCreateForm = ({
+ {showPlaceholder && (
)} - {items.map((item, index) => ( - { - const actionId = previewItems - .find((i) => i.id === item.item_id) - ?.actions?.find((a) => a.action === "RETURN_ITEM")?.id - if (actionId) { - removeInboundItem(actionId) - } - }} - onUpdate={(payload) => { - const actionId = previewItems - .find((i) => i.id === item.item_id) - ?.actions?.find((a) => a.action === "RETURN_ITEM")?.id + {items.map( + (item, index) => + previewItemsMap.get(item.item_id) && ( + { + const actionId = previewItems + .find((i) => i.id === item.item_id) + ?.actions?.find((a) => a.action === "RETURN_ITEM")?.id + + if (actionId) { + removeInboundItem(actionId) + } + }} + onUpdate={(payload) => { + const actionId = previewItems + .find((i) => i.id === item.item_id) + ?.actions?.find((a) => a.action === "RETURN_ITEM")?.id + + if (actionId) { + updateInboundItem({ ...payload, actionId }) + } + }} + index={index} + /> + ) + )} - if (actionId) { - updateInboundItem({ ...payload, actionId }) - } - }} - index={index} - /> - ))} {!showPlaceholder && (
{/*LOCATION*/} 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 3f67c6516c..186696997d 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 @@ -14,6 +14,7 @@ const DEFAULT_PROPERTIES = [ "shipping_tax_total", "tax_total", "refundable_total", + "order_change", ] const DEFAULT_RELATIONS = [ diff --git a/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts b/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts index 3f4e75a72f..5b62b4034c 100644 --- a/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts +++ b/packages/core/core-flows/src/order/workflows/claim/claim-request-item-return.ts @@ -86,8 +86,8 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow( }) const orderReturn: ReturnDTO = transform( - { createdReturn, existingOrderReturn, orderClaim }, - ({ createdReturn, existingOrderReturn, orderClaim }) => { + { createdReturn, existingOrderReturn }, + ({ createdReturn, existingOrderReturn }) => { return existingOrderReturn ?? (createdReturn?.[0] as ReturnDTO) } ) @@ -102,7 +102,7 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow( const orderChange: OrderChangeDTO = useRemoteQueryStep({ entry_point: "order_change", - fields: ["id", "status"], + fields: ["id", "status", "canceled_at", "confirmed_at", "declined_at"], variables: { filters: { order_id: orderClaim.order_id, @@ -113,7 +113,6 @@ export const orderClaimRequestItemReturnWorkflow = createWorkflow( list: false, }).config({ name: "order-change-query", - status: [OrderChangeStatus.PENDING, OrderChangeStatus.REQUESTED], }) validationStep({ diff --git a/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts b/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts index c14856fc06..5586d6b8f7 100644 --- a/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts +++ b/packages/core/core-flows/src/order/workflows/claim/create-claim-shipping-method.ts @@ -6,7 +6,6 @@ import { } from "@medusajs/types" import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils" import { - WorkflowData, WorkflowResponse, createStep, createWorkflow, diff --git a/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts b/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts index 442a3cc81d..99c25d341e 100644 --- a/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts +++ b/packages/core/core-flows/src/order/workflows/return/remove-item-return-action.ts @@ -79,7 +79,17 @@ export const removeItemReturnActionWorkflow = createWorkflow( const orderChange: OrderChangeDTO = useRemoteQueryStep({ entry_point: "order_change", - fields: ["id", "status", "version", "actions.*"], + fields: [ + "id", + "status", + "version", + "return_id", + "order_id", + "actions.*", + "canceled_at", + "confirmed_at", + "declined_at", + ], variables: { filters: { order_id: orderReturn.order_id, diff --git a/packages/core/types/src/http/claim/common.ts b/packages/core/types/src/http/claim/common.ts index 1206acf7fe..9fe96fed36 100644 --- a/packages/core/types/src/http/claim/common.ts +++ b/packages/core/types/src/http/claim/common.ts @@ -1,8 +1,8 @@ import { OperatorMap } from "../../dal" -import { FindParams } from "../common" import { ClaimReason, ReturnDTO } from "../../order" -import { BaseOrder } from "../order/common" import { BigNumberRawValue } from "../../totals" +import { FindParams } from "../common" +import { BaseOrder } from "../order/common" export interface BaseClaimItem { id: string @@ -20,7 +20,7 @@ export interface BaseClaimItem { export interface BaseClaim extends Omit { order_id: string - claim_items: BaseClaimItem + claim_items: BaseClaimItem[] additional_items: any[] return?: ReturnDTO return_id?: string diff --git a/packages/medusa/src/api/admin/claims/[id]/inbound/items/[action_id]/route.ts b/packages/medusa/src/api/admin/claims/[id]/inbound/items/[action_id]/route.ts index 2df3a5bd0e..55b4133aa8 100644 --- a/packages/medusa/src/api/admin/claims/[id]/inbound/items/[action_id]/route.ts +++ b/packages/medusa/src/api/admin/claims/[id]/inbound/items/[action_id]/route.ts @@ -10,6 +10,7 @@ import { AuthenticatedMedusaRequest, MedusaResponse, } from "../../../../../../../types/routing" +import { refetchEntity } from "../../../../../../utils/refetch-entity" import { defaultAdminDetailsReturnFields } from "../../../../../returns/query-config" import { AdminPostReturnsRequestItemsActionReqSchemaType } from "../../../../../returns/validators" @@ -64,30 +65,28 @@ export const DELETE = async ( req: AuthenticatedMedusaRequest, res: MedusaResponse ) => { - const remoteQuery = req.scope.resolve(ContainerRegistrationKeys.REMOTE_QUERY) - const { id, action_id } = req.params + const claim = await refetchEntity("order_claim", id, req.scope, ["return_id"]) + const { result: orderPreview } = await removeItemReturnActionWorkflow( req.scope ).run({ input: { - return_id: id, + return_id: claim.return_id, action_id, }, }) - const queryObject = remoteQueryObjectFromString({ - entryPoint: "return", - variables: { + const orderReturn = await refetchEntity( + "return", + { + ...req.filterableFields, id, - filters: { - ...req.filterableFields, - }, }, - fields: req.remoteQueryConfig.fields, - }) - const [orderReturn] = await remoteQuery(queryObject) + req.scope, + defaultAdminDetailsReturnFields + ) res.json({ order_preview: orderPreview, diff --git a/packages/medusa/src/api/admin/claims/[id]/inbound/shipping-method/[action_id]/route.ts b/packages/medusa/src/api/admin/claims/[id]/inbound/shipping-method/[action_id]/route.ts index 6e097c0270..7c2dc2a595 100644 --- a/packages/medusa/src/api/admin/claims/[id]/inbound/shipping-method/[action_id]/route.ts +++ b/packages/medusa/src/api/admin/claims/[id]/inbound/shipping-method/[action_id]/route.ts @@ -1,5 +1,5 @@ import { - removeReturnShippingMethodWorkflow, + removeClaimShippingMethodWorkflow, updateReturnShippingMethodWorkflow, } from "@medusajs/core-flows" import { @@ -76,7 +76,7 @@ export const DELETE = async ( variables: { id, }, - fields: ["return_id"], + fields: ["id", "return_id"], }), undefined, { @@ -84,11 +84,11 @@ export const DELETE = async ( } ) - const { result: orderPreview } = await removeReturnShippingMethodWorkflow( + const { result: orderPreview } = await removeClaimShippingMethodWorkflow( req.scope ).run({ input: { - return_id: claim.return_id, + claim_id: claim.id, action_id, }, })