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:
Riqwan Thamir
2024-08-07 15:27:04 +02:00
committed by GitHub
co-authored by fPolic
parent 17567b9f0a
commit eb590417be
11 changed files with 140 additions and 113 deletions
@@ -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 = [