Skip to content

Commit

Permalink
fix(dashboard,admin-shared): Fixes to inventory page (#8941)
Browse files Browse the repository at this point in the history
**What**
- Adds qty column to reservations table.
- Adds missing injections zones
- Fixes layout on long description field.

Resolves CC-121
  • Loading branch information
kasperkristensen committed Sep 2, 2024
1 parent 29555ae commit c6ff9ef
Show file tree
Hide file tree
Showing 5 changed files with 138 additions and 87 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,15 @@ const RETURN_REASON_INJECTION_ZONES = [
"return_reason.list.after",
] as const

const INVENTORY_ITEM_INJECTION_ZONES = [
"inventory_item.details.before",
"inventory_item.details.after",
"inventory_item.details.side.before",
"inventory_item.details.side.after",
"inventory_item.list.before",
"inventory_item.list.after",
] as const

/**
* All valid injection zones in the admin panel. An injection zone is a specific place
* in the admin panel where a plugin can inject custom widgets.
Expand Down Expand Up @@ -220,4 +229,5 @@ export const INJECTION_ZONES = [
...PRODUCT_TYPE_INJECTION_ZONES,
...PRODUCT_TAG_INJECTION_ZONES,
...RETURN_REASON_INJECTION_ZONES,
...INVENTORY_ITEM_INJECTION_ZONES,
] as const
Original file line number Diff line number Diff line change
@@ -1,28 +1,48 @@
import { clx } from "@medusajs/ui"
import { ConditionalTooltip } from "../../../../common/conditional-tooltip"
import { PlaceholderCell } from "../placeholder-cell"

type CellProps = {
text?: string | number
align?: "left" | "center" | "right"
maxWidth?: number
}

type HeaderProps = {
text: string
align?: "left" | "center" | "right"
}

export const TextCell = ({ text }: CellProps) => {
export const TextCell = ({ text, align = "left", maxWidth = 220 }: CellProps) => {
if (!text) {
return <PlaceholderCell />
}

const stringLength = text.toString().length

return (
<div className="flex h-full w-full items-center gap-x-3 overflow-hidden">
<ConditionalTooltip content={text} showTooltip={stringLength > 20}>
<div className={clx("flex h-full w-full items-center gap-x-3 overflow-hidden", {
"justify-start text-start": align === "left",
"justify-center text-center": align === "center",
"justify-end text-end": align === "right",
})}
style={{
maxWidth: maxWidth,
}}>
<span className="truncate">{text}</span>
</div>
</ConditionalTooltip>
)
}

export const TextHeader = ({ text }: HeaderProps) => {
export const TextHeader = ({ text, align = "left" }: HeaderProps) => {
return (
<div className="flex h-full w-full items-center">
<div className={clx("flex h-full w-full items-center", {
"justify-start text-start": align === "left",
"justify-center text-center": align === "center",
"justify-end text-end": align === "right",
})}>
<span className="truncate">{text}</span>
</div>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { Container, Heading, IconButton } from "@medusajs/ui"
import { TriangleRightMini } from "@medusajs/icons"
import { Container, Heading } from "@medusajs/ui"
import { useTranslation } from "react-i18next"
import { ArrowUpRightOnBox } from "@medusajs/icons"
import { useNavigate } from "react-router-dom"
import { Link } from "react-router-dom"

import { ProductVariantDTO } from "@medusajs/types"
import { Thumbnail } from "../../../../../components/common/thumbnail"
Expand All @@ -14,7 +14,6 @@ export const InventoryItemVariantsSection = ({
variants,
}: InventoryItemVariantsSectionProps) => {
const { t } = useTranslation()
const navigate = useNavigate()

if (!variants?.length) {
return null
Expand All @@ -27,38 +26,46 @@ export const InventoryItemVariantsSection = ({
</div>

<div className="txt-small flex flex-col gap-2 px-2 pb-2">
{variants.map((variant) => (
<div
key={variant.id}
className="shadow-elevation-card-rest bg-ui-bg-component rounded-md px-4 py-2"
>
<div className="flex items-center gap-3">
<div className="shadow-elevation-card-rest rounded-md">
<Thumbnail src={variant.product?.thumbnail} />
</div>
<div className="flex flex-1 flex-col">
<span className="text-ui-fg-base font-medium">
{variant.title}
</span>
<span className="text-ui-fg-subtle">
{variant.options.map((o) => o.value).join(" ⋅ ")}
</span>
{variants.map((variant) => {
const link = variant.product
? `/products/${variant.product.id}/variants/${variant.id}`
: null

const Inner = (
<div className="shadow-elevation-card-rest bg-ui-bg-component rounded-md px-4 py-2 transition-colors">
<div className="flex items-center gap-3">
<div className="shadow-elevation-card-rest rounded-md">
<Thumbnail src={variant.product?.thumbnail} />
</div>
<div className="flex flex-1 flex-col">
<span className="text-ui-fg-base font-medium">
{variant.title}
</span>
<span className="text-ui-fg-subtle">
{variant.options.map((o) => o.value).join(" ⋅ ")}
</span>
</div>
<div className="size-7 flex items-center justify-center">
<TriangleRightMini className="text-ui-fg-muted" />
</div>
</div>
<IconButton
size="2xsmall"
variant="transparent"
type="button"
onClick={() =>
navigate(
`/products/${variant.product.id}/variants/${variant.id}`
)
}
>
<ArrowUpRightOnBox className="text-ui-fg-muted" />
</IconButton>
</div>
</div>
))}
)

if (!link) {
return <div key={variant.id}>{Inner}</div>
}

return (
<Link
to={link}
key={variant.id}
className="outline-none focus-within:shadow-borders-interactive-with-focus rounded-md [&:hover>div]:bg-ui-bg-component-hover"
>
{Inner}
</Link>
)
})}
</div>
</Container>
)
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import { HttpTypes } from "@medusajs/types"
import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell"
import { ReservationActions } from "./reservation-actions"
import { createColumnHelper } from "@tanstack/react-table"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { CreatedAtCell } from "../../../../../components/table/table-cells/common/created-at-cell"
import { PlaceholderCell } from "../../../../../components/table/table-cells/common/placeholder-cell"
import { TextCell, TextHeader } from "../../../../../components/table/table-cells/common/text-cell"
import { ReservationActions } from "./reservation-actions"

/**
* Adds missing properties to the InventoryItemDTO type.
Expand All @@ -22,17 +23,16 @@ export const useReservationTableColumn = ({ sku }: { sku: string }) => {
return useMemo(
() => [
columnHelper.display({
header: t("fields.sku"),
id: "sku",
header: () => <TextHeader text={t("fields.sku")} />,
cell: () => {
return (
<div className="flex size-full items-center overflow-hidden">
<span className="truncate">{sku}</span>
</div>
<TextCell text={sku} />
)
},
}),
columnHelper.accessor("line_item.order_id", {
header: t("inventory.reservation.orderID"),
header: () => <TextHeader text={t("inventory.reservation.orderID")} />,
cell: ({ getValue }) => {
const orderId = getValue()

Expand All @@ -41,14 +41,12 @@ export const useReservationTableColumn = ({ sku }: { sku: string }) => {
}

return (
<div className="flex size-full items-center overflow-hidden">
<span className="truncate">{orderId}</span>
</div>
<TextCell text={orderId} />
)
},
}),
columnHelper.accessor("description", {
header: t("fields.description"),
header: () => <TextHeader text={t("fields.description")} />,
cell: ({ getValue }) => {
const description = getValue()

Expand All @@ -57,14 +55,12 @@ export const useReservationTableColumn = ({ sku }: { sku: string }) => {
}

return (
<div className="flex size-full items-center overflow-hidden">
<span className="truncate">{description}</span>
</div>
<TextCell text={description} />
)
},
}),
columnHelper.accessor("location.name", {
header: t("inventory.reservation.location"),
header: () => <TextHeader text={t("inventory.reservation.location")} />,
cell: ({ getValue }) => {
const location = getValue()

Expand All @@ -73,16 +69,20 @@ export const useReservationTableColumn = ({ sku }: { sku: string }) => {
}

return (
<div className="flex size-full items-center overflow-hidden">
<span className="truncate">{location}</span>
</div>
<TextCell text={location} />
)
},
}),
columnHelper.accessor("created_at", {
header: t("fields.createdAt"),
header: () => <TextHeader text={t("fields.createdAt")} />,
cell: ({ getValue }) => <CreatedAtCell date={getValue()} />,
}),
columnHelper.accessor("quantity", {
header: () => <TextHeader text={t("fields.quantity")} align="right" />,
cell: ({ getValue }) => {
return <TextCell text={getValue()} align="right" />
},
}),
columnHelper.display({
id: "actions",
cell: ({ row }) => <ReservationActions reservation={row.original} />,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,14 +1,20 @@
import { Outlet, json, useLoaderData, useParams } from "react-router-dom"
import { useLoaderData, useParams } from "react-router-dom"

import { TwoColumnPageSkeleton } from "../../../components/common/skeleton"
import { TwoColumnPage } from "../../../components/layout/pages"
import { useInventoryItem } from "../../../hooks/api/inventory"
import { InventoryItemAttributeSection } from "./components/inventory-item-attributes/attributes-section"
import { InventoryItemGeneralSection } from "./components/inventory-item-general-section"
import { InventoryItemLocationLevelsSection } from "./components/inventory-item-location-levels"
import { InventoryItemReservationsSection } from "./components/inventory-item-reservations"
import { JsonViewSection } from "../../../components/common/json-view-section"
import { useInventoryItem } from "../../../hooks/api/inventory"
import { InventoryItemVariantsSection } from "./components/inventory-item-variants/variants-section"
import { inventoryItemLoader } from "./loader"

import after from "virtual:medusa/widgets/inventory_item/details/after"
import before from "virtual:medusa/widgets/inventory_item/details/before"
import sideAfter from "virtual:medusa/widgets/inventory_item/details/side/after"
import sideBefore from "virtual:medusa/widgets/inventory_item/details/side/before"

export const InventoryDetail = () => {
const { id } = useParams()

Expand All @@ -31,37 +37,45 @@ export const InventoryDetail = () => {
}
)

if (isLoading) {
return <div>Loading...</div>
if (isLoading || !inventory_item) {
return (
<TwoColumnPageSkeleton
showJSON
showMetadata
mainSections={3}
sidebarSections={2}
/>
)
}
if (isError || !inventory_item) {
if (error) {
throw error
}

throw json("An unknown error occurred", 500)
if (isError) {
throw error
}

return (
<div className="flex flex-col gap-y-2">
<div className="flex flex-col gap-x-4 lg:flex-row lg:items-start">
<div className="flex w-full flex-col gap-y-3">
<InventoryItemGeneralSection inventoryItem={inventory_item} />
<InventoryItemLocationLevelsSection inventoryItem={inventory_item} />
<InventoryItemReservationsSection inventoryItem={inventory_item} />
<div className="hidden lg:block">
<JsonViewSection data={inventory_item} />
</div>
<Outlet />
</div>
<div className="mt-2 flex w-full max-w-[100%] flex-col gap-y-2 lg:mt-0 lg:max-w-[400px]">
<InventoryItemVariantsSection variants={inventory_item.variants} />
<InventoryItemAttributeSection inventoryItem={inventory_item} />
<div className="lg:hidden">
<JsonViewSection data={inventory_item} />
</div>
</div>
</div>
</div>
<TwoColumnPage
widgets={{
after,
before,
sideAfter,
sideBefore,
}}
data={inventory_item}
showJSON
showMetadata
hasOutlet
>
<TwoColumnPage.Main>
<InventoryItemGeneralSection inventoryItem={inventory_item} />
<InventoryItemLocationLevelsSection inventoryItem={inventory_item} />
<InventoryItemReservationsSection inventoryItem={inventory_item} />
</TwoColumnPage.Main>
<TwoColumnPage.Sidebar>
<InventoryItemVariantsSection
variants={(inventory_item as any).variants}
/>
<InventoryItemAttributeSection inventoryItem={inventory_item as any} />
</TwoColumnPage.Sidebar>
</TwoColumnPage>
)
}

0 comments on commit c6ff9ef

Please sign in to comment.