feat(medusa, admin-ui, medusa-react, medusa-js): Allow toggling of manage inventory (#3435)
**What** - Toggle manage inventory in the inventory management modal **How** - Create/update/remove inventory item based on if `manage_inventory` is set and if an inventory item already exists - Move all stock location updates to when the modal is submitted - Add create-inventory-item endpoint in the core Fixes CORE-1196 Co-authored-by: Sebastian Rindom <7554214+srindom@users.noreply.github.com>
This commit is contained in:
co-authored by
Sebastian Rindom
parent
30a3203640
commit
fe9eea4c18
+8
-7
@@ -1,14 +1,15 @@
|
||||
import { Discount } from "@medusajs/medusa"
|
||||
import { parse } from "iso8601-duration"
|
||||
import { useAdminUpdateDiscount } from "medusa-react"
|
||||
import moment from "moment"
|
||||
import { ReactNode } from "react"
|
||||
import ClockIcon from "../../../../components/fundamentals/icons/clock-icon"
|
||||
import TrashIcon from "../../../../components/fundamentals/icons/trash-icon"
|
||||
|
||||
import { ActionType } from "../../../../components/molecules/actionables"
|
||||
import useNotification from "../../../../hooks/use-notification"
|
||||
import ClockIcon from "../../../../components/fundamentals/icons/clock-icon"
|
||||
import { Discount } from "@medusajs/medusa"
|
||||
import TrashIcon from "../../../../components/fundamentals/icons/trash-icon"
|
||||
import { getErrorMessage } from "../../../../utils/error-messages"
|
||||
import moment from "moment"
|
||||
import { parse } from "iso8601-duration"
|
||||
import { removeNullish } from "../../../../utils/remove-nullish"
|
||||
import { useAdminUpdateDiscount } from "medusa-react"
|
||||
import useNotification from "../../../../hooks/use-notification"
|
||||
|
||||
type displaySetting = {
|
||||
title: string
|
||||
|
||||
@@ -4,21 +4,24 @@ import {
|
||||
Order,
|
||||
StockLocationDTO,
|
||||
} from "@medusajs/medusa"
|
||||
import { useAdminStockLocations } from "medusa-react"
|
||||
import { useAdminRequestReturn, useAdminShippingOptions } from "medusa-react"
|
||||
import {
|
||||
useAdminRequestReturn,
|
||||
useAdminShippingOptions,
|
||||
useAdminStockLocations,
|
||||
} from "medusa-react"
|
||||
import React, { useContext, useEffect, useState } from "react"
|
||||
import Spinner from "../../../../components/atoms/spinner"
|
||||
import LayeredModal, {
|
||||
LayeredModalContext,
|
||||
} from "../../../../components/molecules/modal/layered-modal"
|
||||
|
||||
import Button from "../../../../components/fundamentals/button"
|
||||
import CheckIcon from "../../../../components/fundamentals/icons/check-icon"
|
||||
import EditIcon from "../../../../components/fundamentals/icons/edit-icon"
|
||||
import IconTooltip from "../../../../components/molecules/icon-tooltip"
|
||||
import Modal from "../../../../components/molecules/modal"
|
||||
import LayeredModal, {
|
||||
LayeredModalContext,
|
||||
} from "../../../../components/molecules/modal/layered-modal"
|
||||
import RMAShippingPrice from "../../../../components/molecules/rma-select-shipping"
|
||||
import Select from "../../../../components/molecules/select/next-select/select"
|
||||
// import Select from "../../../../components/molecules/select"
|
||||
import CurrencyInput from "../../../../components/organisms/currency-input"
|
||||
import RMASelectProductTable from "../../../../components/organisms/rma-select-product-table"
|
||||
import useNotification from "../../../../hooks/use-notification"
|
||||
|
||||
+1
-11
@@ -10,9 +10,7 @@ export type EditFlowVariantFormType = {
|
||||
type Props = {
|
||||
form: UseFormReturn<EditFlowVariantFormType, any>
|
||||
locationLevels: InventoryLevelDTO[]
|
||||
refetchInventory: () => void
|
||||
isLoading: boolean
|
||||
itemId: string
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -34,13 +32,7 @@ type Props = {
|
||||
* )
|
||||
* }
|
||||
*/
|
||||
const EditFlowVariantForm = ({
|
||||
form,
|
||||
isLoading,
|
||||
locationLevels,
|
||||
refetchInventory,
|
||||
itemId,
|
||||
}: Props) => {
|
||||
const EditFlowVariantForm = ({ form, isLoading, locationLevels }: Props) => {
|
||||
if (isLoading) {
|
||||
return null
|
||||
}
|
||||
@@ -49,8 +41,6 @@ const EditFlowVariantForm = ({
|
||||
<>
|
||||
<VariantStockForm
|
||||
locationLevels={locationLevels}
|
||||
refetchInventory={refetchInventory}
|
||||
itemId={itemId}
|
||||
form={nestedForm(form, "stock")}
|
||||
/>
|
||||
</>
|
||||
|
||||
+125
-117
@@ -1,13 +1,9 @@
|
||||
import React, { useMemo, useState, useContext } from "react"
|
||||
import Modal from "../../../../../components/molecules/modal"
|
||||
import { LayeredModalContext } from "../../../../../components/molecules/modal/layered-modal"
|
||||
import {
|
||||
useAdminCreateLocationLevel,
|
||||
useAdminDeleteLocationLevel,
|
||||
useAdminStockLocations,
|
||||
} from "medusa-react"
|
||||
import { useAdminStockLocations } from "medusa-react"
|
||||
import { InventoryLevelDTO, StockLocationDTO } from "@medusajs/medusa"
|
||||
import { Controller } from "react-hook-form"
|
||||
import { Controller, useFieldArray } from "react-hook-form"
|
||||
import Button from "../../../../../components/fundamentals/button"
|
||||
import Switch from "../../../../../components/atoms/switch"
|
||||
import InputField from "../../../../../components/molecules/input"
|
||||
@@ -16,55 +12,60 @@ import IconBadge from "../../../../../components/fundamentals/icon-badge"
|
||||
import BuildingsIcon from "../../../../../components/fundamentals/icons/buildings-icon"
|
||||
|
||||
export type VariantStockFormType = {
|
||||
manage_inventory: boolean
|
||||
manage_inventory?: boolean
|
||||
allow_backorder: boolean
|
||||
inventory_quantity: number | null
|
||||
sku: string | null
|
||||
ean: string | null
|
||||
upc: string | null
|
||||
barcode: string | null
|
||||
location_levels: InventoryLevelDTO[] | null
|
||||
location_levels?: Partial<InventoryLevelDTO>[] | null
|
||||
}
|
||||
|
||||
type Props = {
|
||||
itemId: string
|
||||
locationLevels: InventoryLevelDTO[]
|
||||
refetchInventory: () => void
|
||||
form: NestedForm<VariantStockFormType>
|
||||
}
|
||||
|
||||
const VariantStockForm = ({
|
||||
form,
|
||||
locationLevels,
|
||||
refetchInventory,
|
||||
itemId,
|
||||
}: Props) => {
|
||||
const VariantStockForm = ({ form, locationLevels }: Props) => {
|
||||
const locationLevelMap = useMemo(
|
||||
() => new Map(locationLevels.map((l) => [l.location_id, l])),
|
||||
[locationLevels]
|
||||
)
|
||||
|
||||
const layeredModalContext = useContext(LayeredModalContext)
|
||||
|
||||
const { stock_locations: locations, isLoading } = useAdminStockLocations()
|
||||
|
||||
const deleteLevel = useAdminDeleteLocationLevel(itemId)
|
||||
const createLevel = useAdminCreateLocationLevel(itemId)
|
||||
const { path, control, register, watch } = form
|
||||
|
||||
const { path, control, register } = form
|
||||
const manageInventory = watch(path("manage_inventory"))
|
||||
|
||||
const handleUpdateLocations = async (value) => {
|
||||
await Promise.all(
|
||||
value.removed.map(async (id) => {
|
||||
await deleteLevel.mutateAsync(id)
|
||||
})
|
||||
const {
|
||||
fields: selectedLocations,
|
||||
append,
|
||||
remove,
|
||||
} = useFieldArray({
|
||||
control,
|
||||
name: path("location_levels"),
|
||||
})
|
||||
|
||||
const handleUpdateLocations = async (data: {
|
||||
added: string[]
|
||||
removed: string[]
|
||||
}) => {
|
||||
const removed = data.removed.map((r) =>
|
||||
selectedLocations.findIndex((sl) => sl.location_id === r)
|
||||
)
|
||||
|
||||
await Promise.all(
|
||||
value.added.map(async (id) => {
|
||||
await createLevel.mutateAsync({
|
||||
stocked_quantity: 0,
|
||||
location_id: id,
|
||||
})
|
||||
})
|
||||
)
|
||||
removed.forEach((r) => remove(r))
|
||||
|
||||
refetchInventory()
|
||||
data.added.forEach((added) => {
|
||||
append({
|
||||
location_id: added,
|
||||
stocked_quantity: locationLevelMap.get(added)?.stocked_quantity ?? 0,
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
return (
|
||||
@@ -111,88 +112,95 @@ const VariantStockForm = ({
|
||||
returns are made.
|
||||
</p>
|
||||
</div>
|
||||
<div className="gap-y-2xsmall flex flex-col">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="inter-base-semibold mb-2xsmall">Allow backorders</h3>
|
||||
<Controller
|
||||
control={control}
|
||||
name={path("allow_backorder")}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
return <Switch checked={value} onCheckedChange={onChange} />
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<p className="inter-base-regular text-grey-50">
|
||||
When checked the product will be available for purchase despite the
|
||||
product being sold out
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex w-full flex-col text-base">
|
||||
<h3 className="inter-base-semibold mb-2xsmall">Quantity</h3>
|
||||
{!isLoading && locations && (
|
||||
<div className="flex w-full flex-col">
|
||||
<div className="inter-base-regular text-grey-50 flex justify-between py-3">
|
||||
<div className="">Location</div>
|
||||
<div className="">In Stock</div>
|
||||
{manageInventory && (
|
||||
<>
|
||||
<div className="gap-y-2xsmall flex flex-col">
|
||||
<div className="flex items-center justify-between">
|
||||
<h3 className="inter-base-semibold mb-2xsmall">
|
||||
Allow backorders
|
||||
</h3>
|
||||
<Controller
|
||||
control={control}
|
||||
name={path("allow_backorder")}
|
||||
render={({ field: { value, onChange } }) => {
|
||||
return <Switch checked={value} onCheckedChange={onChange} />
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
{locationLevels.map((level, i) => {
|
||||
const locationDetails = locations.find(
|
||||
(l) => l.id === level.location_id
|
||||
)
|
||||
|
||||
return (
|
||||
<div key={level.id} className="flex items-center py-3">
|
||||
<div className="inter-base-regular flex items-center">
|
||||
<IconBadge className="mr-base">
|
||||
<BuildingsIcon />
|
||||
</IconBadge>
|
||||
{locationDetails?.name}
|
||||
</div>
|
||||
<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`}
|
||||
</span>
|
||||
<span className="whitespace-nowrap text-right">{`${
|
||||
level.stocked_quantity - level.reserved_quantity
|
||||
} available`}</span>
|
||||
</div>
|
||||
<InputField
|
||||
placeholder={"0"}
|
||||
type="number"
|
||||
{...register(
|
||||
path(`location_levels.${i}.stocked_quantity`),
|
||||
{ valueAsNumber: true }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
<p className="inter-base-regular text-grey-50">
|
||||
When checked the product will be available for purchase despite
|
||||
the product being sold out
|
||||
</p>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
type="button"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
layeredModalContext.push(
|
||||
// @ts-ignore
|
||||
ManageLocationsScreen(
|
||||
layeredModalContext.pop,
|
||||
locationLevels,
|
||||
locations,
|
||||
handleUpdateLocations
|
||||
)
|
||||
)
|
||||
}}
|
||||
>
|
||||
Manage locations
|
||||
</Button>
|
||||
</div>
|
||||
<div className="flex w-full flex-col text-base">
|
||||
<h3 className="inter-base-semibold mb-2xsmall">Quantity</h3>
|
||||
{!isLoading && locations && (
|
||||
<div className="flex w-full flex-col">
|
||||
<div className="inter-base-regular text-grey-50 flex justify-between py-3">
|
||||
<div className="">Location</div>
|
||||
<div className="">In Stock</div>
|
||||
</div>
|
||||
{selectedLocations.map((level, i) => {
|
||||
console.log(level)
|
||||
const locationDetails = locations.find(
|
||||
(l: StockLocationDTO) => l.id === level.location_id
|
||||
)
|
||||
|
||||
return (
|
||||
<div key={level.id} className="flex items-center py-3">
|
||||
<div className="inter-base-regular flex items-center">
|
||||
<IconBadge className="mr-base">
|
||||
<BuildingsIcon />
|
||||
</IconBadge>
|
||||
{locationDetails?.name}
|
||||
</div>
|
||||
<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`}
|
||||
</span>
|
||||
<span className="whitespace-nowrap text-right">{`${
|
||||
level.stocked_quantity - level.reserved_quantity
|
||||
} available`}</span>
|
||||
</div>
|
||||
<InputField
|
||||
placeholder={"0"}
|
||||
type="number"
|
||||
{...register(
|
||||
path(`location_levels.${i}.stocked_quantity`),
|
||||
{ valueAsNumber: true }
|
||||
)}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
<div className="flex">
|
||||
<Button
|
||||
variant="secondary"
|
||||
size="small"
|
||||
type="button"
|
||||
className="w-full"
|
||||
onClick={() => {
|
||||
layeredModalContext.push(
|
||||
// @ts-ignore
|
||||
ManageLocationsScreen(
|
||||
layeredModalContext.pop,
|
||||
selectedLocations,
|
||||
locations,
|
||||
handleUpdateLocations
|
||||
)
|
||||
)
|
||||
}}
|
||||
>
|
||||
Manage locations
|
||||
</Button>
|
||||
</div>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
@@ -200,7 +208,7 @@ const VariantStockForm = ({
|
||||
|
||||
export const ManageLocationsScreen = (
|
||||
pop: () => void,
|
||||
levels: InventoryLevelDTO[],
|
||||
levels: Partial<InventoryLevelDTO>[],
|
||||
locations: StockLocationDTO[],
|
||||
onSubmit: (value: any) => Promise<void>
|
||||
) => {
|
||||
@@ -218,7 +226,7 @@ export const ManageLocationsScreen = (
|
||||
}
|
||||
|
||||
type ManageLocationFormProps = {
|
||||
existingLevels: InventoryLevelDTO[]
|
||||
existingLevels: Partial<InventoryLevelDTO>[]
|
||||
locationOptions: StockLocationDTO[]
|
||||
onSubmit: (value: any) => Promise<void>
|
||||
}
|
||||
@@ -232,7 +240,7 @@ const ManageLocationsForm = ({
|
||||
const { pop } = layeredModalContext
|
||||
|
||||
const existingLocations = useMemo(() => {
|
||||
return existingLevels.map((level) => level.location_id)
|
||||
return existingLevels.map((level) => level.location_id!)
|
||||
}, [existingLevels])
|
||||
|
||||
const [selectedLocations, setSelectedLocations] =
|
||||
@@ -266,7 +274,7 @@ const ManageLocationsForm = ({
|
||||
(locationId: string) => !existingLocations.includes(locationId)
|
||||
)
|
||||
const removedLevels = existingLocations.filter(
|
||||
(locationId) => !selectedLocations.includes(locationId)
|
||||
(locationId) => !!locationId && !selectedLocations.includes(locationId)
|
||||
)
|
||||
|
||||
await onSubmit({
|
||||
|
||||
@@ -11,11 +11,12 @@ import {
|
||||
useAdminUpdateProduct,
|
||||
useAdminUpdateVariant,
|
||||
} from "medusa-react"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import useImperativeDialog from "../../../../hooks/use-imperative-dialog"
|
||||
import useNotification from "../../../../hooks/use-notification"
|
||||
|
||||
import { getErrorMessage } from "../../../../utils/error-messages"
|
||||
import { removeNullish } from "../../../../utils/remove-nullish"
|
||||
import useImperativeDialog from "../../../../hooks/use-imperative-dialog"
|
||||
import { useNavigate } from "react-router-dom"
|
||||
import useNotification from "../../../../hooks/use-notification"
|
||||
|
||||
const useEditProductActions = (productId: string) => {
|
||||
const dialog = useImperativeDialog()
|
||||
@@ -70,8 +71,11 @@ const useEditProductActions = (productId: string) => {
|
||||
successMessage = "Variant was updated successfully"
|
||||
) => {
|
||||
updateVariant.mutate(
|
||||
// @ts-ignore - TODO fix type on request
|
||||
{ variant_id: id, ...removeNullish(payload) },
|
||||
{
|
||||
variant_id: id,
|
||||
...removeNullish(payload),
|
||||
manage_inventory: payload.manage_inventory,
|
||||
},
|
||||
{
|
||||
onSuccess: () => {
|
||||
notification("Success", successMessage, "success")
|
||||
|
||||
+104
-44
@@ -1,20 +1,24 @@
|
||||
import { Product, ProductVariant } from "@medusajs/medusa"
|
||||
import {
|
||||
useAdminUpdateLocationLevel,
|
||||
useAdminVariantsInventory,
|
||||
} from "medusa-react"
|
||||
import React, { useContext } from "react"
|
||||
InventoryLevelDTO,
|
||||
Product,
|
||||
ProductVariant,
|
||||
VariantInventory,
|
||||
} from "@medusajs/medusa"
|
||||
import { useMedusa } from "medusa-react"
|
||||
import { useAdminVariantsInventory } from "medusa-react"
|
||||
import { useContext } from "react"
|
||||
import { useForm } from "react-hook-form"
|
||||
import Button from "../../../../../components/fundamentals/button"
|
||||
import Modal from "../../../../../components/molecules/modal"
|
||||
import LayeredModal, {
|
||||
LayeredModalContext,
|
||||
} from "../../../../../components/molecules/modal/layered-modal"
|
||||
import EditFlowVariantForm, {
|
||||
EditFlowVariantFormType,
|
||||
} from "../../../components/variant-inventory-form/edit-flow-variant-form"
|
||||
import useEditProductActions from "../../hooks/use-edit-product-actions"
|
||||
import LayeredModal, {
|
||||
LayeredModalContext,
|
||||
} from "../../../../../components/molecules/modal/layered-modal"
|
||||
import Button from "../../../../../components/fundamentals/button"
|
||||
import Modal from "../../../../../components/molecules/modal"
|
||||
import { createUpdatePayload } from "./edit-variants-modal/edit-variant-screen"
|
||||
import useEditProductActions from "../../hooks/use-edit-product-actions"
|
||||
import { removeNullish } from "../../../../../utils/remove-nullish"
|
||||
|
||||
type Props = {
|
||||
onClose: () => void
|
||||
@@ -24,6 +28,7 @@ type Props = {
|
||||
}
|
||||
|
||||
const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
const { client } = useMedusa()
|
||||
const layeredModalContext = useContext(LayeredModalContext)
|
||||
const {
|
||||
// @ts-ignore
|
||||
@@ -32,31 +37,94 @@ const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
refetch,
|
||||
} = useAdminVariantsInventory(variant.id)
|
||||
|
||||
const itemId = variantInventory?.inventory[0]?.id
|
||||
const variantInventoryItem = variantInventory?.inventory[0]
|
||||
const itemId = variantInventoryItem?.id
|
||||
|
||||
const { mutate: updateLocationLevel } = useAdminUpdateLocationLevel(
|
||||
itemId || ""
|
||||
)
|
||||
const handleClose = () => {
|
||||
onClose()
|
||||
}
|
||||
|
||||
const { onUpdateVariant, updatingVariant } = useEditProductActions(product.id)
|
||||
|
||||
const onSubmit = async (data) => {
|
||||
const { location_levels } = data.stock
|
||||
|
||||
await Promise.all(
|
||||
location_levels.map(async (level) => {
|
||||
await updateLocationLevel({
|
||||
stockLocationId: level.location_id,
|
||||
stocked_quantity: level.stocked_quantity,
|
||||
})
|
||||
})
|
||||
)
|
||||
// / TODO: Call update location level with new values
|
||||
const onSubmit = async (data: EditFlowVariantFormType) => {
|
||||
const locationLevels = data.stock.location_levels || []
|
||||
const manageInventory = data.stock.manage_inventory
|
||||
delete data.stock.manage_inventory
|
||||
delete data.stock.location_levels
|
||||
|
||||
let inventoryItemId: string | undefined = itemId
|
||||
|
||||
const upsertPayload = removeNullish(data.stock)
|
||||
|
||||
if (variantInventoryItem) {
|
||||
// variant inventory exists and we can remove location levels
|
||||
// (it's important to do this before potentially deleting the inventory item)
|
||||
const deleteLocations = manageInventory
|
||||
? variantInventoryItem?.location_levels?.filter(
|
||||
(itemLevel: InventoryLevelDTO) => {
|
||||
return !locationLevels.find(
|
||||
(level) => level.location_id === itemLevel.location_id
|
||||
)
|
||||
}
|
||||
) ?? []
|
||||
: []
|
||||
|
||||
if (inventoryItemId) {
|
||||
await Promise.all(
|
||||
deleteLocations.map(async (location: InventoryLevelDTO) => {
|
||||
await client.admin.inventoryItems.deleteLocationLevel(
|
||||
inventoryItemId!,
|
||||
location.id
|
||||
)
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
if (!manageInventory) {
|
||||
// has an inventory item but no longer wants to manage inventory
|
||||
await client.admin.inventoryItems.delete(itemId!)
|
||||
inventoryItemId = undefined
|
||||
} else {
|
||||
// has an inventory item and wants to update inventory
|
||||
await client.admin.inventoryItems.update(itemId!, upsertPayload)
|
||||
}
|
||||
} else if (manageInventory) {
|
||||
// does not have an inventory item but wants to manage inventory
|
||||
const { inventory_item } = await client.admin.inventoryItems.create({
|
||||
variant_id: variant.id,
|
||||
...upsertPayload,
|
||||
})
|
||||
inventoryItemId = inventory_item.id
|
||||
}
|
||||
|
||||
// If some inventory Item exists update location levels
|
||||
if (inventoryItemId) {
|
||||
await Promise.all(
|
||||
locationLevels.map(async (level) => {
|
||||
if (!level.location_id) {
|
||||
return
|
||||
}
|
||||
if (level.id) {
|
||||
await client.admin.inventoryItems.updateLocationLevel(
|
||||
inventoryItemId!,
|
||||
level.location_id,
|
||||
{
|
||||
stocked_quantity: level.stocked_quantity,
|
||||
}
|
||||
)
|
||||
} else {
|
||||
await client.admin.inventoryItems.createLocationLevel(
|
||||
inventoryItemId!,
|
||||
{
|
||||
location_id: level.location_id,
|
||||
stocked_quantity: level.stocked_quantity!,
|
||||
}
|
||||
)
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
// @ts-ignore
|
||||
onUpdateVariant(variant.id, createUpdatePayload(data), () => {
|
||||
refetch()
|
||||
@@ -71,8 +139,7 @@ const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
</Modal.Header>
|
||||
{!isLoadingInventory && (
|
||||
<StockForm
|
||||
variantInventory={variantInventory}
|
||||
refetchInventory={refetch}
|
||||
variantInventory={variantInventory!}
|
||||
onSubmit={onSubmit}
|
||||
isLoadingInventory={isLoadingInventory}
|
||||
handleClose={handleClose}
|
||||
@@ -86,10 +153,15 @@ const EditVariantInventoryModal = ({ onClose, product, variant }: Props) => {
|
||||
const StockForm = ({
|
||||
variantInventory,
|
||||
onSubmit,
|
||||
refetchInventory,
|
||||
isLoadingInventory,
|
||||
handleClose,
|
||||
updatingVariant,
|
||||
}: {
|
||||
variantInventory: VariantInventory
|
||||
onSubmit: (data: EditFlowVariantFormType) => void
|
||||
isLoadingInventory: boolean
|
||||
handleClose: () => void
|
||||
updatingVariant: boolean
|
||||
}) => {
|
||||
const form = useForm<EditFlowVariantFormType>({
|
||||
defaultValues: getEditVariantDefaultValues(variantInventory),
|
||||
@@ -99,32 +171,20 @@ const StockForm = ({
|
||||
formState: { isDirty },
|
||||
handleSubmit,
|
||||
reset,
|
||||
watch,
|
||||
} = form
|
||||
|
||||
const locationLevels = watch("stock.location_levels")
|
||||
|
||||
const { location_levels } = variantInventory.inventory[0]
|
||||
|
||||
React.useEffect(() => {
|
||||
form.setValue("stock.location_levels", location_levels)
|
||||
}, [form, location_levels])
|
||||
const locationLevels = variantInventory.inventory[0]?.location_levels || []
|
||||
|
||||
const handleOnSubmit = handleSubmit((data) => {
|
||||
// @ts-ignore
|
||||
onSubmit(data)
|
||||
})
|
||||
|
||||
const itemId = variantInventory.inventory[0].id
|
||||
|
||||
return (
|
||||
<form onSubmit={handleOnSubmit} noValidate>
|
||||
<Modal.Content>
|
||||
<EditFlowVariantForm
|
||||
form={form}
|
||||
refetchInventory={refetchInventory}
|
||||
locationLevels={locationLevels || []}
|
||||
itemId={itemId}
|
||||
locationLevels={locationLevels}
|
||||
isLoading={isLoadingInventory}
|
||||
/>
|
||||
</Modal.Content>
|
||||
|
||||
@@ -106,9 +106,9 @@ const VariantsTable = ({ variants, actions }: Props) => {
|
||||
|
||||
const getTableRowActionables = (variant: ProductVariant) => {
|
||||
const inventoryManagementActions = []
|
||||
if (hasInventoryService && variant.manage_inventory) {
|
||||
if (hasInventoryService) {
|
||||
inventoryManagementActions.push({
|
||||
label: "Manage inventory", // TODO: Only add this item if variant.manageInventory is true
|
||||
label: "Manage inventory",
|
||||
icon: <BuildingsIcon size="20" />,
|
||||
onClick: () => updateVariantInventory(variant),
|
||||
})
|
||||
|
||||
Reference in New Issue
Block a user