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 <mainacc.polic@gmail.com>
This commit is contained in:
@@ -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<string, any>,
|
||||
query?: HttpTypes.AdminClaimListParams,
|
||||
options?: Omit<
|
||||
UseQueryOptions<any, Error, any, QueryKey>,
|
||||
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,
|
||||
|
||||
@@ -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<string>()
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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)
|
||||
)
|
||||
}}
|
||||
/>
|
||||
|
||||
<StackedFocusModal.Footer>
|
||||
<div className="flex w-full items-center justify-end gap-x-4">
|
||||
<div className="flex items-center justify-end gap-x-2">
|
||||
@@ -413,7 +437,7 @@ export const ClaimCreateForm = ({
|
||||
variant="primary"
|
||||
size="small"
|
||||
role="button"
|
||||
onClick={() => onItemsSelected()}
|
||||
onClick={async () => await onItemsSelected()}
|
||||
>
|
||||
{t("actions.save")}
|
||||
</Button>
|
||||
@@ -423,6 +447,7 @@ export const ClaimCreateForm = ({
|
||||
</StackedFocusModal.Content>
|
||||
</StackedFocusModal>
|
||||
</div>
|
||||
|
||||
{showPlaceholder && (
|
||||
<div
|
||||
style={{
|
||||
@@ -432,34 +457,39 @@ export const ClaimCreateForm = ({
|
||||
className="bg-ui-bg-field mt-4 block h-[56px] w-full rounded-lg border border-dashed"
|
||||
/>
|
||||
)}
|
||||
{items.map((item, index) => (
|
||||
<ClaimInboundItem
|
||||
key={item.id}
|
||||
item={itemsMap.get(item.item_id)!}
|
||||
previewItem={previewItemsMap.get(item.item_id)!}
|
||||
currencyCode={order.currency_code}
|
||||
form={form}
|
||||
onRemove={() => {
|
||||
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) && (
|
||||
<ClaimInboundItem
|
||||
key={item.id}
|
||||
item={itemsMap.get(item.item_id)!}
|
||||
previewItem={previewItemsMap.get(item.item_id)!}
|
||||
currencyCode={order.currency_code}
|
||||
form={form}
|
||||
onRemove={() => {
|
||||
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 && (
|
||||
<div className="mt-8 flex flex-col gap-y-4">
|
||||
{/*LOCATION*/}
|
||||
|
||||
@@ -14,6 +14,7 @@ const DEFAULT_PROPERTIES = [
|
||||
"shipping_tax_total",
|
||||
"tax_total",
|
||||
"refundable_total",
|
||||
"order_change",
|
||||
]
|
||||
|
||||
const DEFAULT_RELATIONS = [
|
||||
|
||||
@@ -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({
|
||||
|
||||
@@ -6,7 +6,6 @@ import {
|
||||
} from "@medusajs/types"
|
||||
import { ChangeActionType, OrderChangeStatus } from "@medusajs/utils"
|
||||
import {
|
||||
WorkflowData,
|
||||
WorkflowResponse,
|
||||
createStep,
|
||||
createWorkflow,
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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<BaseOrder, "status" | "version" | "items"> {
|
||||
order_id: string
|
||||
claim_items: BaseClaimItem
|
||||
claim_items: BaseClaimItem[]
|
||||
additional_items: any[]
|
||||
return?: ReturnDTO
|
||||
return_id?: string
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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,
|
||||
},
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user