feat(dashboard): DataTable component (#6297)
This commit is contained in:
committed by
GitHub
parent
a7be5d7b6d
commit
8cbf6c60fe
@@ -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,
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user