import { EllipseGreenSolid, EllipseGreySolid, EllipsisHorizontal, ExclamationCircle, PencilSquare, Spinner, Trash, } from "@medusajs/icons" import { DateComparisonOperator, PriceList } from "@medusajs/medusa" import { Container, DropdownMenu, Heading, IconButton, Input, StatusBadge, Table, Text, clx, usePrompt, } from "@medusajs/ui" import { Row, createColumnHelper, flexRender, getCoreRowModel, getPaginationRowModel, useReactTable, } from "@tanstack/react-table" import { useAdminDeletePriceList, useAdminPriceLists, useAdminUpdatePriceList, } from "medusa-react" import * as React from "react" import { useNavigate, useSearchParams } from "react-router-dom" import Spacer from "../../../components/atoms/spacer" import WidgetContainer from "../../../components/extensions/widget-container" import { FilterMenu } from "../../../components/molecules/filter-menu" import { useDebouncedSearchParam } from "../../../hooks/use-debounced-search-param" import useNotification from "../../../hooks/use-notification" import { useWidgets } from "../../../providers/widget-provider" import { getErrorMessage } from "../../../utils/error-messages" import { getDateComparisonOperatorFromSearchParams, getStringArrayFromSearchParams, getStringFromSearchParams, } from "../../../utils/search-param-utils" import { PriceListStatus } from "../forms/price-list-details-form" import { PriceListNew } from "../new" const PAGE_SIZE = 10 const TABLE_HEIGHT = (PAGE_SIZE + 1) * 48 const PriceListTableFilters = () => { const navigate = useNavigate() const [searchParams] = useSearchParams() const onStatusChange = (status: string[]) => { const current = new URLSearchParams(searchParams) if (status.length === 0) { current.delete("status") navigate({ search: current.toString() }, { replace: true }) return } if (current.has("status")) { current.delete("status") } current.set("status", status.join(",")) navigate({ search: current.toString() }, { replace: true }) } const onDateChange = ( key: "updated_at" | "created_at", value?: DateComparisonOperator ) => { const current = new URLSearchParams(searchParams) if (value) { current.set(key, JSON.stringify(value)) } else { current.delete(key) } navigate({ search: current.toString() }, { replace: true }) } const onClearFilters = () => { const reset = new URLSearchParams() navigate({ search: reset.toString() }, { replace: true }) } return ( onDateChange("created_at", dc)} /> onDateChange("updated_at", dc)} /> ) } const PriceListOverview = () => { const { getWidgets } = useWidgets() const [searchParams] = useSearchParams() const { query, setQuery } = useDebouncedSearchParam() const navigate = useNavigate() const { price_lists, count, isLoading, isError } = useAdminPriceLists( { limit: PAGE_SIZE, status: getStringArrayFromSearchParams("status", searchParams) as | PriceListStatus[] | undefined, created_at: getDateComparisonOperatorFromSearchParams( "created_at", searchParams ), updated_at: getDateComparisonOperatorFromSearchParams( "updated_at", searchParams ), q: getStringFromSearchParams("q", searchParams), expand: "customer_groups", }, { keepPreviousData: true, } ) const table = useReactTable({ data: price_lists ?? [], columns, getCoreRowModel: getCoreRowModel(), getPaginationRowModel: getPaginationRowModel(), }) if (isLoading) { return ( ) } if (isError) { return (
An error occurred while loading the price lists. Try to reload the page or try again later.
) } return (
{getWidgets("price_list.list.before").map((w, i) => { return ( ) })}
Price Lists
{ setQuery(e.target.value) }} />
{table.getHeaderGroups().map((headerGroup) => { return ( {headerGroup.headers.map((header) => { return ( {flexRender( header.column.columnDef.header, header.getContext() )} ) })} ) })} {table.getRowModel().rows.map((row) => ( { navigate(`/a/pricing/${row.original.id}`) }} > {row.getVisibleCells().map((cell) => ( {flexRender( cell.column.columnDef.cell, cell.getContext() )} ))} ))}
{getWidgets("price_list.list.after").map((w, i) => { return ( ) })}
) } const columnHelper = createColumnHelper() const columns = [ columnHelper.accessor("name", { header: "Name", cell: (info) => info.getValue(), }), columnHelper.accessor("description", { header: "Description", cell: (info) => ( {info.getValue()} ), }), columnHelper.accessor("status", { header: "Status", cell: ({ getValue, row }) => { const startsAt = row.original.starts_at const endsAt = row.original.ends_at const isExpired = endsAt ? new Date(endsAt) < new Date() : false const isScheduled = startsAt ? new Date(startsAt) > new Date() : false const isDraft = getValue() === PriceListStatus.DRAFT const color = isExpired ? "red" : isDraft ? "grey" : isScheduled ? "orange" : "green" const text = isExpired ? "Expired" : isDraft ? "Draft" : isScheduled ? "Scheduled" : "Active" return ( {text} ) }, }), columnHelper.accessor("customer_groups", { header: "Customer Groups", cell: (info) => info.getValue()?.length || "-", }), columnHelper.display({ id: "actions", cell: (info) => { return }, }), ] type PriceListTableRowActionsProps = { row: Row } const PriceListTableRowActions = ({ row }: PriceListTableRowActionsProps) => { const { mutateAsync: deleteFn } = useAdminDeletePriceList(row.original.id) const { mutateAsync: updateFn } = useAdminUpdatePriceList(row.original.id) const prompt = usePrompt() const notification = useNotification() const navigate = useNavigate() const handleDelete = async (e: React.MouseEvent) => { e.stopPropagation() const response = await prompt({ title: "Are you sure?", description: "This will permanently delete the price list", verificationText: row.original.name, }) if (!response) { return } return deleteFn(undefined, { onSuccess: () => { notification( "Price list deleted", `Successfully deleted ${row.original.name}`, "success" ) }, onError: (err) => { notification("An error occurred", getErrorMessage(err), "error") }, }) } const toggleStatus = async (e: React.MouseEvent) => { e.stopPropagation() return updateFn( { status: row.original.status === "active" ? PriceListStatus.DRAFT : PriceListStatus.ACTIVE, }, { onSuccess: () => { notification( "Price list updated", `Successfully updated ${row.original.name}`, "success" ) }, onError: (err) => { notification("An error occurred", getErrorMessage(err), "error") }, } ) } const handleNavigate = (e: React.MouseEvent) => { e.stopPropagation() navigate(`/a/pricing/${row.original.id}`) } const isExpired = row.original.ends_at ? new Date(row.original.ends_at) < new Date() : false const isActive = row.original.status === "active" return ( Edit {!isExpired && ( {isActive ? : } {isActive ? "Mark as draft" : "Mark as active"} )} Delete ) } export { PriceListOverview }