import { useAdminVariants, useAdminVariantsInventory } from "medusa-react" import React, { useEffect, useMemo, useState } from "react" import { usePagination, useRowSelect, useTable } from "react-table" import { InventoryLevelDTO, ProductVariant } from "@medusajs/medusa" import clsx from "clsx" import pluralize from "pluralize" import { useTranslation } from "react-i18next" import { useDebounce } from "../../../hooks/use-debounce" import ImagePlaceholder from "../../../components/fundamentals/image-placeholder" import Table from "../../../components/molecules/table" import IndeterminateCheckbox from "../../../components/molecules/indeterminate-checkbox" import { formatAmountWithSymbol } from "../../../utils/prices" import TableContainer from "../../../components/organisms/table-container" import Tooltip from "../../../components/atoms/tooltip" import useStockLocations from "../../../hooks/use-stock-locations" import Skeleton from "../../../components/atoms/skeleton" const PAGE_SIZE = 12 type Props = { isReplace?: boolean regionId: string customerId: string currencyCode: string setSelectedVariants: (selectedIds: ProductVariant[]) => void } const VariantsTable: React.FC = (props) => { const { t } = useTranslation() const { isReplace, regionId, currencyCode, customerId, setSelectedVariants } = props const [query, setQuery] = useState("") const [offset, setOffset] = useState(0) const [numPages, setNumPages] = useState(0) const [currentPage, setCurrentPage] = useState(0) const debouncedSearchTerm = useDebounce(query, 500) const { isLoading, count, variants } = useAdminVariants({ q: debouncedSearchTerm, limit: PAGE_SIZE, offset, region_id: regionId, customer_id: customerId, }) useEffect(() => { if (typeof count !== "undefined") { setNumPages(Math.ceil(count / PAGE_SIZE)) } }, [count]) const VariantInventoryCell = ({ row: { original } }) => { const { getLocationNameById } = useStockLocations() const { variant, isLoading } = useAdminVariantsInventory(original.id) if (isLoading) { return (
) } if (!isLoading && !variant?.inventory?.length) { return
{original.inventory_quantity}
} const { inventory } = variant const total = inventory[0].location_levels.reduce( (sum: number, location_level: InventoryLevelDTO) => (sum += location_level.stocked_quantity), 0 ) const LocationTooltip = ( <> {inventory[0].location_levels.map( (location_level: InventoryLevelDTO) => (
{location_level.stocked_quantity} {t("variants-table-location", " in {{location}}", { location: getLocationNameById(location_level.location_id), })}
) )} ) return (
{total} in {inventory[0].location_levels.length}{" "} {pluralize("location", inventory[0].location_levels.length)}
) } const ProductCell = ({ row: { original } }) => { return (
{original.product.thumbnail ? ( ) : ( )}
{original.product.title} } maxWidth={400} >
{original.sku ?? original.product.title}
{original.title}
) } const columns = useMemo(() => { return [ { Header: (
{t("edit-product", "Product")}
), accessor: "sku", Cell: ProductCell, }, { Header: (
{t("edit-in-stock", "In Stock")}
), accessor: "inventory_quantity", Cell: VariantInventoryCell, }, { Header: (
{t("edit-price", "Price")}
), accessor: "amount", Cell: ({ row: { original } }) => { if (!original.original_price_incl_tax) { return null } const showOriginal = original.calculated_price_type !== "default" return (
{showOriginal && ( {formatAmountWithSymbol({ amount: original.original_price_incl_tax, currency: currencyCode, })} )} {formatAmountWithSymbol({ amount: original.calculated_price_incl_tax, currency: currencyCode, })}
{" "} {currencyCode.toUpperCase()}
) }, }, ] }, []) const table = useTable( { columns, data: variants || [], manualPagination: true, initialState: { pageIndex: currentPage, pageSize: PAGE_SIZE, selectedRowIds: {}, }, pageCount: numPages, autoResetSelectedRows: false, autoResetPage: false, getRowId: (row) => row.id, }, usePagination, useRowSelect, (hooks) => { hooks.visibleColumns.push((columns) => [ { id: "selection", Header: ({ getToggleAllRowsSelectedProps }) => { if (isReplace) { return null } return (
) }, Cell: ({ row, toggleAllRowsSelected, toggleRowSelected }) => { const currentState = row.getToggleRowSelectedProps() const selectProps = row.getToggleRowSelectedProps() return (
{ toggleAllRowsSelected(false) toggleRowSelected(row.id, !currentState.checked) } : selectProps.onChange } />
) }, }, ...columns, ]) } ) useEffect(() => { if (!variants) { return } const selected = variants.filter((v) => table.state.selectedRowIds[v.id]) setSelectedVariants(selected) }, [table.state.selectedRowIds, variants]) const handleNext = () => { if (table.canNextPage) { setOffset((old) => old + table.state.pageSize) setCurrentPage((old) => old + 1) table.nextPage() } } const handlePrev = () => { if (table.canPreviousPage) { setOffset((old) => Math.max(old - table.state.pageSize, 0)) setCurrentPage((old) => old - 1) table.previousPage() } } const handleSearch = (q) => { setOffset(0) setCurrentPage(0) setQuery(q) } return ( {table.headerGroups.map((headerGroup) => ( {headerGroup.headers.map((col) => ( {col.render("Header")} ))} ))} {table.rows.map((row) => { table.prepareRow(row) return ( {row.cells.map((cell) => { return ( {cell.render("Cell")} ) })} ) })}
) } export default VariantsTable