feat(dashboard): DataTable component (#6297)

This commit is contained in:
Kasper Fabricius Kristensen
2024-02-02 21:56:55 +01:00
committed by GitHub
parent a7be5d7b6d
commit 8cbf6c60fe
62 changed files with 2722 additions and 397 deletions
@@ -0,0 +1,133 @@
import { Order } from "@medusajs/medusa"
import {
ColumnDef,
ColumnDefBase,
createColumnHelper,
} from "@tanstack/react-table"
import { useMemo } from "react"
import {
DateCell,
DateHeader,
} from "../../../components/table/table-cells/common/date-cell"
import {
DisplayIdCell,
DisplayIdHeader,
} from "../../../components/table/table-cells/order/display-id-cell"
import {
FulfillmentStatusCell,
FulfillmentStatusHeader,
} from "../../../components/table/table-cells/order/fulfillment-status-cell"
import {
ItemsCell,
ItemsHeader,
} from "../../../components/table/table-cells/order/items-cell"
import {
PaymentStatusCell,
PaymentStatusHeader,
} from "../../../components/table/table-cells/order/payment-status-cell"
import {
SalesChannelCell,
SalesChannelHeader,
} from "../../../components/table/table-cells/order/sales-channel-cell"
import {
TotalCell,
TotalHeader,
} from "../../../components/table/table-cells/order/total-cell"
// We have to use any here, as the type of Order is so complex that it lags the TS server
const columnHelper = createColumnHelper<Order>()
type UseOrderTableColumnsProps = {
exclude?: string[]
}
export const useOrderTableColumns = (props: UseOrderTableColumnsProps) => {
const { exclude = [] } = props ?? {}
const columns = useMemo(
() => [
columnHelper.accessor("display_id", {
header: () => <DisplayIdHeader />,
cell: ({ getValue }) => {
const id = getValue()
return <DisplayIdCell displayId={id} />
},
}),
columnHelper.accessor("created_at", {
header: () => <DateHeader />,
cell: ({ getValue }) => {
const date = new Date(getValue())
return <DateCell date={date} />
},
}),
columnHelper.accessor("sales_channel", {
header: () => <SalesChannelHeader />,
cell: ({ getValue }) => {
const channel = getValue()
return <SalesChannelCell channel={channel} />
},
}),
columnHelper.accessor("payment_status", {
header: () => <PaymentStatusHeader />,
cell: ({ getValue }) => {
const status = getValue()
return <PaymentStatusCell status={status} />
},
}),
columnHelper.accessor("fulfillment_status", {
header: () => <FulfillmentStatusHeader />,
cell: ({ getValue }) => {
const status = getValue()
return <FulfillmentStatusCell status={status} />
},
}),
columnHelper.accessor("items", {
header: () => <ItemsHeader />,
cell: ({ getValue }) => {
const items = getValue()
return <ItemsCell items={items} />
},
}),
columnHelper.accessor("total", {
header: () => <TotalHeader />,
cell: ({ getValue, row }) => {
const total = getValue()
const currencyCode = row.original.currency_code
return <TotalCell currencyCode={currencyCode} total={total} />
},
}),
],
[]
)
const isAccessorColumnDef = (
c: any
): c is ColumnDef<Order> & { accessorKey: string } => {
return c.accessorKey !== undefined
}
const isDisplayColumnDef = (
c: any
): c is ColumnDef<Order> & { id: string } => {
return c.id !== undefined
}
const shouldExclude = <TDef extends ColumnDefBase<Order, any>>(c: TDef) => {
if (isAccessorColumnDef(c)) {
return exclude.includes(c.accessorKey)
} else if (isDisplayColumnDef(c)) {
return exclude.includes(c.id)
}
return false
}
return columns.filter((c) => !shouldExclude(c)) as ColumnDef<Order>[]
}
@@ -0,0 +1,154 @@
import { useAdminRegions, useAdminSalesChannels } from "medusa-react"
import { useTranslation } from "react-i18next"
import type { Filter } from "../../../components/table/data-table"
export const useOrderTableFilters = (): Filter[] => {
const { t } = useTranslation()
const { regions } = useAdminRegions({
limit: 1000,
fields: "id,name",
expand: "",
})
const { sales_channels } = useAdminSalesChannels({
limit: 1000,
fields: "id,name",
expand: "",
})
let filters: Filter[] = []
if (regions) {
const regionFilter: Filter = {
key: "region_id",
label: t("fields.region"),
type: "select",
options: regions.map((r) => ({
label: r.name,
value: r.id,
})),
multiple: true,
searchable: true,
}
filters = [...filters, regionFilter]
}
if (sales_channels) {
const salesChannelFilter: Filter = {
key: "sales_channel_id",
label: t("fields.salesChannel"),
type: "select",
multiple: true,
searchable: true,
options: sales_channels.map((s) => ({
label: s.name,
value: s.id,
})),
}
filters = [...filters, salesChannelFilter]
}
const paymentStatusFilter: Filter = {
key: "payment_status",
label: t("orders.paymentStatusLabel"),
type: "select",
multiple: true,
options: [
{
label: t("orders.paymentStatus.notPaid"),
value: "not_paid",
},
{
label: t("orders.paymentStatus.awaiting"),
value: "awaiting",
},
{
label: t("orders.paymentStatus.captured"),
value: "captured",
},
{
label: t("orders.paymentStatus.refunded"),
value: "refunded",
},
{
label: t("orders.paymentStatus.partiallyRefunded"),
value: "partially_refunded",
},
{
label: t("orders.paymentStatus.canceled"),
value: "canceled",
},
{
label: t("orders.paymentStatus.requresAction"),
value: "requires_action",
},
],
}
const fulfillmentStatusFilter: Filter = {
key: "fulfillment_status",
label: t("orders.fulfillmentStatusLabel"),
type: "select",
multiple: true,
options: [
{
label: t("orders.fulfillmentStatus.notFulfilled"),
value: "not_fulfilled",
},
{
label: t("orders.fulfillmentStatus.fulfilled"),
value: "fulfilled",
},
{
label: t("orders.fulfillmentStatus.partiallyFulfilled"),
value: "partially_fulfilled",
},
{
label: t("orders.fulfillmentStatus.returned"),
value: "returned",
},
{
label: t("orders.fulfillmentStatus.partiallyReturned"),
value: "partially_returned",
},
{
label: t("orders.fulfillmentStatus.shipped"),
value: "shipped",
},
{
label: t("orders.fulfillmentStatus.partiallyShipped"),
value: "partially_shipped",
},
{
label: t("orders.fulfillmentStatus.canceled"),
value: "canceled",
},
{
label: t("orders.fulfillmentStatus.requresAction"),
value: "requires_action",
},
],
}
const dateFilters: Filter[] = [
{ label: "Created At", key: "created_at" },
{ label: "Updated At", key: "updated_at" },
].map((f) => ({
key: f.key,
label: f.label,
type: "date",
}))
filters = [
...filters,
paymentStatusFilter,
fulfillmentStatusFilter,
...dateFilters,
]
return filters
}
@@ -0,0 +1,58 @@
import { AdminGetOrdersParams } from "@medusajs/medusa"
import { useQueryParams } from "../../use-query-params"
type UseOrderTableQueryProps = {
prefix?: string
pageSize?: number
}
/**
* TODO: Enable `order` query param when staging is updated
*/
export const useOrderTableQuery = ({
prefix,
pageSize = 50,
}: UseOrderTableQueryProps) => {
const queryObject = useQueryParams(
[
"offset",
"q",
"created_at",
"updated_at",
"region_id",
"sales_channel_id",
"payment_status",
"fulfillment_status",
],
prefix
)
const {
offset,
sales_channel_id,
created_at,
updated_at,
fulfillment_status,
payment_status,
region_id,
q,
} = queryObject
const searchParams: AdminGetOrdersParams = {
limit: pageSize,
offset: offset ? Number(offset) : 0,
sales_channel_id: sales_channel_id?.split(","),
fulfillment_status: fulfillment_status?.split(","),
payment_status: payment_status?.split(","),
created_at: created_at ? JSON.parse(created_at) : undefined,
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
region_id: region_id?.split(","),
q,
}
return {
searchParams,
raw: queryObject,
}
}