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
@@ -12,7 +12,7 @@ medusaIntegrationTestRunner({
let inventoryItem2
let stockLocation1
let stockLocation2
let stockLocation3
beforeEach(async () => {
await createAdminUser(dbConnection, adminHeaders, getContainer())
@@ -24,6 +24,10 @@ medusaIntegrationTestRunner({
await api.post(`/admin/stock-locations`, { name: "loc2" }, adminHeaders)
).data.stock_location
stockLocation3 = (
await api.post(`/admin/stock-locations`, { name: "loc3" }, adminHeaders)
).data.stock_location
inventoryItem1 = (
await api.post(
`/admin/inventory-items`,
@@ -122,9 +126,152 @@ medusaIntegrationTestRunner({
})
})
describe("POST /admin/inventory-items/:id/location-levels/batch", () => {
describe("POST /admin/inventory-items/location-levels/batch", () => {
let locationLevel1
let locationLevel2
beforeEach(async () => {
await api.post(
const seed = await api.post(
`/admin/inventory-items/${inventoryItem1.id}/location-levels/batch`,
{
create: [
{
location_id: stockLocation1.id,
stocked_quantity: 0,
},
{
location_id: stockLocation2.id,
stocked_quantity: 10,
},
],
},
adminHeaders
)
locationLevel1 = seed.data.created[0]
locationLevel2 = seed.data.created[1]
})
it("should batch update the inventory levels", async () => {
const result = await api.post(
`/admin/inventory-items/location-levels/batch`,
{
update: [
{
location_id: stockLocation1.id,
inventory_item_id: inventoryItem1.id,
stocked_quantity: 10,
},
{
location_id: stockLocation2.id,
inventory_item_id: inventoryItem1.id,
stocked_quantity: 20,
},
],
},
adminHeaders
)
expect(result.status).toEqual(200)
expect(result.data).toEqual(
expect.objectContaining({
updated: expect.arrayContaining([
expect.objectContaining({
location_id: stockLocation1.id,
inventory_item_id: inventoryItem1.id,
stocked_quantity: 10,
}),
expect.objectContaining({
location_id: stockLocation2.id,
inventory_item_id: inventoryItem1.id,
stocked_quantity: 20,
}),
]),
})
)
})
it("should batch create the inventory levels", async () => {
const result = await api.post(
`/admin/inventory-items/location-levels/batch`,
{
create: [
{
location_id: stockLocation3.id,
inventory_item_id: inventoryItem1.id,
stocked_quantity: 10,
},
],
},
adminHeaders
)
expect(result.status).toEqual(200)
expect(result.data).toEqual(
expect.objectContaining({
created: expect.arrayContaining([
expect.objectContaining({
location_id: stockLocation3.id,
inventory_item_id: inventoryItem1.id,
stocked_quantity: 10,
}),
]),
})
)
})
it("should batch delete the inventory levels when stocked quantity is 0 and force is false", async () => {
const result = await api.post(
`/admin/inventory-items/location-levels/batch`,
{ delete: [locationLevel1.id] },
adminHeaders
)
expect(result.status).toEqual(200)
expect(result.data).toEqual(
expect.objectContaining({
deleted: [locationLevel1.id],
})
)
})
it("should not delete the inventory levels when stocked quantity is greater than 0 and force is false", async () => {
const error = await api
.post(
`/admin/inventory-items/location-levels/batch`,
{ delete: [locationLevel2.id] },
adminHeaders
)
.catch((e) => e)
expect(error.response.status).toEqual(400)
expect(error.response.data).toEqual({
type: "not_allowed",
message: `Cannot remove Inventory Levels for ${stockLocation2.id} because there are stocked items at the locations. Use force flag to delete anyway.`,
})
})
it("should delete the inventory levels when stocked quantity is greater than 0 and force is true", async () => {
const result = await api.post(
`/admin/inventory-items/location-levels/batch`,
{ delete: [locationLevel2.id], force: true },
adminHeaders
)
expect(result.status).toEqual(200)
expect(result.data).toEqual(
expect.objectContaining({
deleted: [locationLevel2.id],
})
)
})
})
describe("POST /admin/inventory-items/:id/location-levels/batch", () => {
let locationLevel1
beforeEach(async () => {
const seed = await api.post(
`/admin/inventory-items/${inventoryItem1.id}/location-levels`,
{
location_id: stockLocation1.id,
@@ -132,6 +279,8 @@ medusaIntegrationTestRunner({
},
adminHeaders
)
locationLevel1 = seed.data.inventory_item.location_levels[0]
})
it("should delete an inventory location level and create a new one", async () => {
@@ -139,7 +288,8 @@ medusaIntegrationTestRunner({
`/admin/inventory-items/${inventoryItem1.id}/location-levels/batch`,
{
create: [{ location_id: "location_2" }],
delete: [stockLocation1.id],
delete: [locationLevel1.id],
force: true,
},
adminHeaders
)
@@ -154,7 +304,7 @@ medusaIntegrationTestRunner({
expect(levelsListResult.data.inventory_levels).toHaveLength(1)
})
it("should not delete an inventory location level when there is stocked items", async () => {
it("should not delete an inventory location level when there is stocked items without force", async () => {
await api.post(
`/admin/inventory-items/${inventoryItem1.id}/location-levels/${stockLocation1.id}`,
{ stocked_quantity: 10 },
@@ -164,7 +314,7 @@ medusaIntegrationTestRunner({
const { response } = await api
.post(
`/admin/inventory-items/${inventoryItem1.id}/location-levels/batch`,
{ delete: [stockLocation1.id] },
{ delete: [locationLevel1.id] },
adminHeaders
)
.catch((e) => e)
@@ -172,7 +322,7 @@ medusaIntegrationTestRunner({
expect(response.status).toEqual(400)
expect(response.data).toEqual({
type: "not_allowed",
message: `Cannot remove Inventory Levels for ${stockLocation1.id} because there are stocked or reserved items at the locations`,
message: `Cannot remove Inventory Levels for ${stockLocation1.id} because there are stocked items at the locations. Use force flag to delete anyway.`,
})
})