From 4e86caba3060508a2512de17e8021ae1fb9697b9 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Frane=20Poli=C4=87?=
<16856471+fPolic@users.noreply.github.com>
Date: Mon, 17 Jun 2024 11:23:18 +0200
Subject: [PATCH] feat(dashboard): display inventory levels in variants table
(#7694)
* feat: display inventory levels in variants table
* fix: display conditions and translations
* fix: invalidate inventory lists when products are created
* fix: translation, fix link definition
* fix: revert link
* feat: navigation actions
* fix: action, refactor
* fix: refactor, add check for manage quantity flag
* fix: update label
---
.../dashboard/src/hooks/api/products.tsx | 5 +
.../dashboard/src/i18n/translations/en.json | 8 ++
.../components/use-inventory-table-query.tsx | 19 +++-
.../product-variant-section.tsx | 2 +-
.../use-variant-table-columns.tsx | 107 ++++++++++++++++--
.../types/src/http/inventory/admin/queries.ts | 1 +
6 files changed, 128 insertions(+), 14 deletions(-)
diff --git a/packages/admin-next/dashboard/src/hooks/api/products.tsx b/packages/admin-next/dashboard/src/hooks/api/products.tsx
index f25353eb22..eb1bdd85c3 100644
--- a/packages/admin-next/dashboard/src/hooks/api/products.tsx
+++ b/packages/admin-next/dashboard/src/hooks/api/products.tsx
@@ -9,6 +9,7 @@ import {
import { sdk } from "../../lib/client"
import { queryClient } from "../../lib/query-client"
import { queryKeysFactory } from "../../lib/query-key-factory"
+import { inventoryItemsQueryKeys } from "./inventory.tsx"
const PRODUCTS_QUERY_KEY = "products" as const
export const productsQueryKeys = queryKeysFactory(PRODUCTS_QUERY_KEY)
@@ -254,6 +255,10 @@ export const useCreateProduct = (
sdk.admin.product.create(payload),
onSuccess: (data: any, variables: any, context: any) => {
queryClient.invalidateQueries({ queryKey: productsQueryKeys.lists() })
+ // if `manage_inventory` is true on created variants that will create inventory items automatically
+ queryClient.invalidateQueries({
+ queryKey: inventoryItemsQueryKeys.lists(),
+ })
options?.onSuccess?.(data, variables, context)
},
...options,
diff --git a/packages/admin-next/dashboard/src/i18n/translations/en.json b/packages/admin-next/dashboard/src/i18n/translations/en.json
index cb6186c910..ebb2bce571 100644
--- a/packages/admin-next/dashboard/src/i18n/translations/en.json
+++ b/packages/admin-next/dashboard/src/i18n/translations/en.json
@@ -324,7 +324,15 @@
"create": {
"header": "Create Variant"
},
+ "tableItemAvailable": "{{availableCount}} available",
+ "tableItem_one": "{{availableCount}} available at {{locationCount}} location",
+ "tableItem_other": "{{availableCount}} available at {{locationCount}} locations",
"inventory": {
+ "notManaged": "Not managed",
+ "actions": {
+ "inventoryItems": "Go to inventory item",
+ "inventoryKit": "Show inventory items"
+ },
"header": "Stock & Inventory",
"editItemDetails": "Edit item details",
"manageInventoryLabel": "Manage inventory",
diff --git a/packages/admin-next/dashboard/src/routes/inventory/inventory-list/components/use-inventory-table-query.tsx b/packages/admin-next/dashboard/src/routes/inventory/inventory-list/components/use-inventory-table-query.tsx
index d02f52f891..acf886a72c 100644
--- a/packages/admin-next/dashboard/src/routes/inventory/inventory-list/components/use-inventory-table-query.tsx
+++ b/packages/admin-next/dashboard/src/routes/inventory/inventory-list/components/use-inventory-table-query.tsx
@@ -1,4 +1,5 @@
-import { AdminGetInventoryItemsParams } from "@medusajs/medusa"
+import { HttpTypes } from "@medusajs/types"
+
import { useQueryParams } from "../../../../hooks/use-query-params"
export const useInventoryTableQuery = ({
@@ -10,14 +11,17 @@ export const useInventoryTableQuery = ({
}) => {
const raw = useQueryParams(
[
+ "id",
"location_id",
"q",
"order",
"requires_shipping",
"offset",
"sku",
+ "origin_country",
"material",
"mid_code",
+ "hs_code",
"order",
"weight",
"width",
@@ -37,7 +41,7 @@ export const useInventoryTableQuery = ({
...params
} = raw
- const searchParams: AdminGetInventoryItemsParams = {
+ const searchParams: HttpTypes.AdminInventoryItemParams = {
limit: pageSize,
offset: offset ? parseInt(offset) : undefined,
weight: weight ? JSON.parse(weight) : undefined,
@@ -47,7 +51,16 @@ export const useInventoryTableQuery = ({
requires_shipping: requires_shipping
? JSON.parse(requires_shipping)
: undefined,
- ...params,
+ q: params.q,
+ sku: params.sku,
+ order: params.order,
+ mid_code: params.mid_code,
+ hs_code: params.hs_code,
+ material: params.material,
+ location_levels: {
+ location_id: params.location_id || [],
+ },
+ id: params.id ? params.id.split(",") : undefined,
}
return {
diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx b/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx
index 6444b40234..80ac2bab6f 100644
--- a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx
+++ b/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/product-variant-section.tsx
@@ -2,6 +2,7 @@ import { PencilSquare, Plus } from "@medusajs/icons"
import { Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { keepPreviousData } from "@tanstack/react-query"
+import { HttpTypes } from "@medusajs/types"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { DataTable } from "../../../../../components/table/data-table"
@@ -10,7 +11,6 @@ import { useProductVariantTableColumns } from "./use-variant-table-columns"
import { useProductVariantTableFilters } from "./use-variant-table-filters"
import { useProductVariantTableQuery } from "./use-variant-table-query"
import { useProductVariants } from "../../../../../hooks/api/products"
-import { HttpTypes } from "@medusajs/types"
type ProductVariantSectionProps = {
product: HttpTypes.AdminProduct
diff --git a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx b/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx
index 51126447c3..bb6e8419de 100644
--- a/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx
+++ b/packages/admin-next/dashboard/src/routes/products/product-detail/components/product-variant-section/use-variant-table-columns.tsx
@@ -1,25 +1,31 @@
-import { PencilSquare, Trash } from "@medusajs/icons"
-import { Badge, usePrompt } from "@medusajs/ui"
+import { Buildings, Component, PencilSquare, Trash } from "@medusajs/icons"
+import { Badge, usePrompt, clx } from "@medusajs/ui"
import { createColumnHelper } from "@tanstack/react-table"
-import { useMemo } from "react"
+import { HttpTypes, InventoryItemDTO } from "@medusajs/types"
import { useTranslation } from "react-i18next"
+import { useMemo } from "react"
import { ActionMenu } from "../../../../../components/common/action-menu"
import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell"
import { useDeleteVariant } from "../../../../../hooks/api/products"
-import { HttpTypes } from "@medusajs/types"
const VariantActions = ({
variant,
product,
}: {
- variant: HttpTypes.AdminProductVariant
+ variant: HttpTypes.AdminProductVariant & {
+ inventory_items: { inventory: InventoryItemDTO }[]
+ }
product: HttpTypes.AdminProduct
}) => {
const { mutateAsync } = useDeleteVariant(product.id, variant.id)
const { t } = useTranslation()
const prompt = usePrompt()
+ const inventoryItemsCount = variant.inventory_items?.length || 0
+ const hasInventoryItem = inventoryItemsCount === 1
+ const hasInventoryKit = inventoryItemsCount > 1
+
const handleDelete = async () => {
const res = await prompt({
title: t("general.areYouSure"),
@@ -37,6 +43,23 @@ const VariantActions = ({
await mutateAsync()
}
+ const [inventoryItemLink, inventoryKitLink] = useMemo(() => {
+ if (!variant.inventory_items?.length) {
+ return ["", ""]
+ }
+
+ const itemId = variant.inventory_items![0].inventory.id
+ const itemLink = `/inventory/${itemId}`
+
+ const itemIds = variant.inventory_items!.map((i) => i.inventory.id)
+ const params = { id: itemIds }
+ const query = new URLSearchParams(params).toString()
+
+ const kitLink = `/inventory?${query}`
+
+ return [itemLink, kitLink]
+ }, [variant.inventory_items])
+
return (