feat(inventory,dashboard,types,core-flows,js-sdk,medusa): Improve inventory UX (#10630)

* feat(dashboard): Add UI for bulk editing inventory stock (#10556)

* progress

* cleanup types

* add changeset

* fix 0 values

* format schema

* add delete event and allow copy/pasting enabled for some fields

* add response types

* add tests

* work on fixing setValue behaviour

* cleanup toggle logic

* add loading state

* format schema

* add support for bidirectional actions in DataGrid and update Checkbox and RadioGroup

* update lock

* lint

* fix 404

* address feedback

* update cursor on bidirectional select
This commit is contained in:
Kasper Fabricius Kristensen
2025-01-12 19:07:14 -05:00
committed by GitHub
parent c5915451b8
commit bc22b81cdf
82 changed files with 2720 additions and 289 deletions
@@ -1,39 +1,35 @@
import {
AdminCreateInventoryLocationLevelType,
AdminUpdateInventoryLocationLevelType,
} from "../../../validators"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework/http"
import { AdminBatchInventoryItemLocationsLevelType } from "../../../validators"
import { bulkCreateDeleteLevelsWorkflow } from "@medusajs/core-flows"
import { BatchMethodRequest } from "@medusajs/framework/types"
import { batchInventoryItemLevelsWorkflow } from "@medusajs/core-flows"
export const POST = async (
req: MedusaRequest<
BatchMethodRequest<
AdminCreateInventoryLocationLevelType,
AdminUpdateInventoryLocationLevelType
>
>,
res: MedusaResponse<{ inventory_item: {} }>
req: MedusaRequest<AdminBatchInventoryItemLocationsLevelType>,
res: MedusaResponse
) => {
const { id } = req.params
// TODO: Normalize workflow and response, and add support for updates
const workflow = bulkCreateDeleteLevelsWorkflow(req.scope)
await workflow.run({
const workflow = batchInventoryItemLevelsWorkflow(req.scope)
const output = await workflow.run({
input: {
deletes:
req.validatedBody.delete?.map((location_id) => ({
location_id,
inventory_item_id: id,
})) ?? [],
creates:
delete: req.validatedBody.delete ?? [],
create:
req.validatedBody.create?.map((c) => ({
...c,
inventory_item_id: id,
})) ?? [],
update:
req.validatedBody.update?.map((u) => ({
...u,
inventory_item_id: id,
})) ?? [],
force: req.validatedBody.force ?? false,
},
})
res.status(200).json({ inventory_item: {} })
res.status(200).json({
created: output.result.created,
updated: output.result.updated,
deleted: output.result.deleted,
})
}
@@ -0,0 +1,20 @@
import { batchInventoryItemLevelsWorkflow } from "@medusajs/core-flows"
import { MedusaRequest, MedusaResponse } from "@medusajs/framework"
import { AdminBatchInventoryItemLevelsType } from "../../validators"
export const POST = async (
req: MedusaRequest<AdminBatchInventoryItemLevelsType>,
res: MedusaResponse
) => {
const body = req.validatedBody
const output = await batchInventoryItemLevelsWorkflow(req.scope).run({
input: body,
})
res.json({
created: output.result.created,
updated: output.result.updated,
deleted: output.result.deleted,
})
}
@@ -4,9 +4,10 @@ import {
} from "@medusajs/framework"
import { MiddlewareRoute, unlessPath } from "@medusajs/framework/http"
import { DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT } from "../../../utils/middlewares"
import { createBatchBody } from "../../utils/validators"
import * as QueryConfig from "./query-config"
import {
AdminBatchInventoryItemLevels,
AdminBatchInventoryItemLocationsLevel,
AdminCreateInventoryItem,
AdminCreateInventoryLocationLevel,
AdminGetInventoryItemParams,
@@ -49,6 +50,22 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [
),
],
},
{
method: ["POST"],
matcher: "/admin/inventory-items/batch",
bodyParser: {
sizeLimit: DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT,
},
middlewares: [validateAndTransformBody(AdminBatchInventoryItemLevels)],
},
{
method: ["POST"],
matcher: "/admin/inventory-items/location-levels/batch",
bodyParser: {
sizeLimit: DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT,
},
middlewares: [validateAndTransformBody(AdminBatchInventoryItemLevels)],
},
{
method: ["POST"],
matcher: "/admin/inventory-items/:id",
@@ -88,12 +105,7 @@ export const adminInventoryRoutesMiddlewares: MiddlewareRoute[] = [
sizeLimit: DEFAULT_BATCH_ENDPOINTS_SIZE_LIMIT,
},
middlewares: [
validateAndTransformBody(
createBatchBody(
AdminCreateInventoryLocationLevel,
AdminUpdateInventoryLocationLevel
)
),
validateAndTransformBody(AdminBatchInventoryItemLocationsLevel),
validateAndTransformQuery(
AdminGetInventoryLocationLevelParams,
QueryConfig.retrieveLocationLevelsTransformQueryConfig
@@ -75,6 +75,30 @@ export const AdminCreateInventoryLocationLevel = z
})
.strict()
export type AdminUpdateInventoryLocationLevelBatchType = z.infer<
typeof AdminUpdateInventoryLocationLevelBatch
>
export const AdminUpdateInventoryLocationLevelBatch = z
.object({
id: z.string().optional(),
location_id: z.string(),
stocked_quantity: z.number().min(0).optional(),
incoming_quantity: z.number().min(0).optional(),
})
.strict()
export type AdminBatchInventoryItemLocationsLevelType = z.infer<
typeof AdminBatchInventoryItemLocationsLevel
>
export const AdminBatchInventoryItemLocationsLevel = z.object({
create: z.array(AdminCreateInventoryLocationLevel).optional(),
update: z.array(AdminUpdateInventoryLocationLevelBatch).optional(),
delete: z.array(z.string()).optional(),
force: z.boolean().optional(),
})
export type AdminUpdateInventoryLocationLevelType = z.infer<
typeof AdminUpdateInventoryLocationLevel
>
@@ -129,3 +153,23 @@ export const AdminUpdateInventoryItem = z
metadata: z.record(z.unknown()).nullish(),
})
.strict()
const AdminBatchInventoryLocationLevel = z.object({
inventory_item_id: z.string(),
location_id: z.string(),
stocked_quantity: z.number().min(0).optional(),
incoming_quantity: z.number().min(0).optional(),
})
export const AdminBatchInventoryItemLevels = z
.object({
create: z.array(AdminBatchInventoryLocationLevel).optional(),
update: z.array(AdminBatchInventoryLocationLevel).optional(),
delete: z.array(z.string()).optional(),
force: z.boolean().optional(),
})
.strict()
export type AdminBatchInventoryItemLevelsType = z.infer<
typeof AdminBatchInventoryItemLevels
>