Fix(admin-ui): multi warehouse minor fixes (#3540)
**What** 1. Enable the "create location" button in "create stock location" when a field has changed 2. Remove the "successful delete" toast when cancelling stock location creation 3. Properly update available and reserved when editing stock levels for variant 4. invalidate inventoryItemList queryKeys when changing location levels **Why** - we had the same bug with form validation when creating location levels as we had when editing them (1) - when updating location levels, listing inventory items wouldn't show the newly added location levels (4) - fixing ui bugs (2, 4)
This commit is contained in:
@@ -1,23 +1,25 @@
|
||||
import { InventoryLevelDTO, StockLocationDTO } from "@medusajs/medusa"
|
||||
import { Controller, useFieldArray } from "react-hook-form"
|
||||
import { InventoryLevelDTO, StockLocationDTO } from "@medusajs/types"
|
||||
|
||||
import clsx from "clsx"
|
||||
import { sum } from "lodash"
|
||||
import { useAdminStockLocations } from "medusa-react"
|
||||
import React from "react"
|
||||
import { useFeatureFlag } from "../../../../../providers/feature-flag-provider"
|
||||
import { NestedForm } from "../../../../../utils/nested-form"
|
||||
import Switch from "../../../../atoms/switch"
|
||||
import BuildingsIcon from "../../../../fundamentals/icons/buildings-icon"
|
||||
import Button from "../../../../fundamentals/button"
|
||||
import FeatureToggle from "../../../../fundamentals/feature-toggle"
|
||||
import IconBadge from "../../../../fundamentals/icon-badge"
|
||||
import BuildingsIcon from "../../../../fundamentals/icons/buildings-icon"
|
||||
import InputField from "../../../../molecules/input"
|
||||
import { LayeredModalContext } from "../../../../molecules/modal/layered-modal"
|
||||
import { ManageLocationsScreen } from "../../variant-inventory-form/variant-stock-form"
|
||||
import { NestedForm } from "../../../../../utils/nested-form"
|
||||
import React from "react"
|
||||
import Switch from "../../../../atoms/switch"
|
||||
import clsx from "clsx"
|
||||
import { sum } from "lodash"
|
||||
import { useAdminStockLocations } from "medusa-react"
|
||||
import { useFeatureFlag } from "../../../../../providers/feature-flag-provider"
|
||||
// import { InventoryLevelDTO, StockLocationDTO } from "@medusajs/medusa"
|
||||
|
||||
|
||||
export type VariantStockFormType = {
|
||||
manage_inventory: boolean
|
||||
manage_inventory?: boolean
|
||||
allow_backorder: boolean
|
||||
inventory_quantity: number | null
|
||||
sku: string | null
|
||||
|
||||
@@ -37,6 +37,13 @@ const VariantStockForm = ({ form, locationLevels }: Props) => {
|
||||
|
||||
const { stock_locations: locations, isLoading } = useAdminStockLocations()
|
||||
|
||||
const locationsMap = useMemo(() => {
|
||||
if (isLoading) {
|
||||
return new Map()
|
||||
}
|
||||
|
||||
return new Map(locations.map((l) => [l.id, l]))
|
||||
}, [locations, isLoading])
|
||||
const { path, control, register, watch } = form
|
||||
|
||||
const manageInventory = watch(path("manage_inventory"))
|
||||
@@ -50,6 +57,12 @@ const VariantStockForm = ({ form, locationLevels }: Props) => {
|
||||
name: path("location_levels"),
|
||||
})
|
||||
|
||||
const selectedLocationLevels = watch(path("location_levels"))
|
||||
|
||||
const levelMap = new Map(
|
||||
selectedLocationLevels?.map((l) => [l.location_id, l])
|
||||
)
|
||||
|
||||
const handleUpdateLocations = async (data: {
|
||||
added: string[]
|
||||
removed: string[]
|
||||
@@ -142,10 +155,8 @@ const VariantStockForm = ({ form, locationLevels }: Props) => {
|
||||
<div className="">In Stock</div>
|
||||
</div>
|
||||
{selectedLocations.map((level, i) => {
|
||||
console.log(level)
|
||||
const locationDetails = locations.find(
|
||||
(l: StockLocationDTO) => l.id === level.location_id
|
||||
)
|
||||
const locationDetails = locationsMap.get(level.location_id)
|
||||
const locationLevel = levelMap.get(level.location_id)
|
||||
|
||||
return (
|
||||
<div key={level.id} className="flex items-center py-3">
|
||||
@@ -158,15 +169,17 @@ const VariantStockForm = ({ form, locationLevels }: Props) => {
|
||||
<div className="ml-auto flex">
|
||||
<div className="mr-base text-small text-grey-50 flex flex-col">
|
||||
<span className="whitespace-nowrap text-right">
|
||||
{`${level.reserved_quantity} reserved`}
|
||||
{`${locationLevel!.reserved_quantity} reserved`}
|
||||
</span>
|
||||
<span className="whitespace-nowrap text-right">{`${
|
||||
level.stocked_quantity - level.reserved_quantity
|
||||
locationLevel!.stocked_quantity! -
|
||||
locationLevel!.reserved_quantity!
|
||||
} available`}</span>
|
||||
</div>
|
||||
<InputField
|
||||
placeholder={"0"}
|
||||
type="number"
|
||||
min={0}
|
||||
{...register(
|
||||
path(`location_levels.${i}.stocked_quantity`),
|
||||
{ valueAsNumber: true }
|
||||
@@ -191,7 +204,7 @@ const VariantStockForm = ({ form, locationLevels }: Props) => {
|
||||
ManageLocationsScreen(
|
||||
layeredModalContext.pop,
|
||||
selectedLocations,
|
||||
locations,
|
||||
locations || [],
|
||||
handleUpdateLocations
|
||||
)
|
||||
)
|
||||
|
||||
@@ -8,7 +8,7 @@ import useNotification from "../../hooks/use-notification"
|
||||
type DeletePromptProps = {
|
||||
heading?: string
|
||||
text?: string
|
||||
successText?: string
|
||||
successText?: string | false
|
||||
cancelText?: string
|
||||
confirmText?: string
|
||||
handleClose: () => void
|
||||
|
||||
@@ -1,23 +1,25 @@
|
||||
import {
|
||||
InventoryLevelDTO,
|
||||
Product,
|
||||
ProductVariant,
|
||||
VariantInventory,
|
||||
} from "@medusajs/medusa"
|
||||
import { useAdminVariantsInventory, useMedusa } from "medusa-react"
|
||||
import { useContext } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import useEditProductActions from "../../../hooks/use-edit-product-actions"
|
||||
import { removeNullish } from "../../../utils/remove-nullish"
|
||||
import EditFlowVariantForm, {
|
||||
EditFlowVariantFormType,
|
||||
} from "../../forms/product/variant-form/edit-flow-variant-form"
|
||||
import Button from "../../fundamentals/button"
|
||||
import Modal from "../../molecules/modal"
|
||||
import LayeredModal, {
|
||||
LayeredModalContext,
|
||||
} from "../../molecules/modal/layered-modal"
|
||||
import { Product, ProductVariant, VariantInventory } from "@medusajs/medusa"
|
||||
import {
|
||||
adminInventoryItemsKeys,
|
||||
useAdminVariantsInventory,
|
||||
useMedusa,
|
||||
} from "medusa-react"
|
||||
|
||||
import Button from "../../fundamentals/button"
|
||||
import { InventoryLevelDTO } from "@medusajs/types"
|
||||
import Modal from "../../molecules/modal"
|
||||
import { createUpdatePayload } from "./edit-variants-modal/edit-variant-screen"
|
||||
import { queryClient } from "../../../constants/query-client"
|
||||
import { removeNullish } from "../../../utils/remove-nullish"
|
||||
import { useContext } from "react"
|
||||
import useEditProductActions from "../../../hooks/use-edit-product-actions"
|
||||
import { useForm } from "react-hook-form"
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
@@ -46,14 +48,15 @@ const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
const { onUpdateVariant, updatingVariant } = useEditProductActions(product.id)
|
||||
|
||||
const onSubmit = async (data: EditFlowVariantFormType) => {
|
||||
const locationLevels = data.stock.location_levels || []
|
||||
const locationLevels = data.stock.stock_location || []
|
||||
const manageInventory = data.stock.manage_inventory
|
||||
delete data.stock.manage_inventory
|
||||
delete data.stock.location_levels
|
||||
delete data.stock.stock_location
|
||||
|
||||
let inventoryItemId: string | undefined = itemId
|
||||
|
||||
const upsertPayload = removeNullish(data.stock)
|
||||
let shouldInvalidateCache = false
|
||||
|
||||
if (variantInventoryItem) {
|
||||
// variant inventory exists and we can remove location levels
|
||||
@@ -77,12 +80,16 @@ const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
)
|
||||
})
|
||||
)
|
||||
if (deleteLocations.length) {
|
||||
shouldInvalidateCache = true
|
||||
}
|
||||
}
|
||||
|
||||
if (!manageInventory) {
|
||||
// has an inventory item but no longer wants to manage inventory
|
||||
await client.admin.inventoryItems.delete(itemId!)
|
||||
inventoryItemId = undefined
|
||||
shouldInvalidateCache = true
|
||||
} else {
|
||||
// has an inventory item and wants to update inventory
|
||||
await client.admin.inventoryItems.update(itemId!, upsertPayload)
|
||||
@@ -122,11 +129,17 @@ const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
}
|
||||
})
|
||||
)
|
||||
if (locationLevels.length) {
|
||||
shouldInvalidateCache = true
|
||||
}
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
onUpdateVariant(variant.id, createUpdatePayload(data), () => {
|
||||
refetch()
|
||||
if (shouldInvalidateCache) {
|
||||
queryClient.invalidateQueries(adminInventoryItemsKeys.lists())
|
||||
}
|
||||
handleClose()
|
||||
})
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
|
||||
})
|
||||
const {
|
||||
handleSubmit,
|
||||
formState: { isDirty, isValid },
|
||||
formState: { isDirty },
|
||||
} = form
|
||||
|
||||
const {
|
||||
@@ -63,7 +63,10 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
|
||||
const { mutateAsync: associateSalesChannel } =
|
||||
useAdminAddLocationToSalesChannel()
|
||||
|
||||
const createSalesChannelAssociationPromise = (salesChannelId, locationId) =>
|
||||
const createSalesChannelAssociationPromise = (
|
||||
salesChannelId: string,
|
||||
locationId: string
|
||||
) =>
|
||||
associateSalesChannel({
|
||||
sales_channel_id: salesChannelId,
|
||||
location_id: locationId,
|
||||
@@ -126,7 +129,7 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
|
||||
heading="Are you sure you want to cancel with unsaved changes"
|
||||
confirmText="Yes, cancel"
|
||||
cancelText="No, continue creating"
|
||||
successText={undefined}
|
||||
successText={false}
|
||||
handleClose={closeClosePrompt}
|
||||
onDelete={async () => onClose()}
|
||||
/>
|
||||
@@ -136,7 +139,7 @@ const NewLocation = ({ onClose }: { onClose: () => void }) => {
|
||||
size="small"
|
||||
variant="primary"
|
||||
type="button"
|
||||
disabled={!isDirty || !isValid}
|
||||
disabled={!isDirty}
|
||||
onClick={onSubmit()}
|
||||
>
|
||||
Add location
|
||||
|
||||
Reference in New Issue
Block a user