feat(ui,dashboard): Migrate SC tables to DataTable (#11106)

This commit is contained in:
Kasper Fabricius Kristensen
2025-02-07 15:26:49 +01:00
committed by GitHub
parent d588073cea
commit fcd3e2226e
34 changed files with 847 additions and 858 deletions

View File

@@ -0,0 +1,35 @@
import { clx } from "@medusajs/ui"
import { PropsWithChildren } from "react"
type DataTableStatusCellProps = PropsWithChildren<{
color?: "green" | "red" | "blue" | "orange" | "grey" | "purple"
}>
export const DataTableStatusCell = ({
color,
children,
}: DataTableStatusCellProps) => {
return (
<div className="txt-compact-small text-ui-fg-subtle flex h-full w-full items-center gap-x-2 overflow-hidden">
<div
role="presentation"
className="flex h-5 w-2 items-center justify-center"
>
<div
className={clx(
"h-2 w-2 rounded-sm shadow-[0px_0px_0px_1px_rgba(0,0,0,0.12)_inset]",
{
"bg-ui-tag-neutral-icon": color === "grey",
"bg-ui-tag-green-icon": color === "green",
"bg-ui-tag-red-icon": color === "red",
"bg-ui-tag-blue-icon": color === "blue",
"bg-ui-tag-orange-icon": color === "orange",
"bg-ui-tag-purple-icon": color === "purple",
}
)}
/>
</div>
<span className="truncate">{children}</span>
</div>
)
}

View File

@@ -1,15 +1,18 @@
import {
Button,
clx,
DataTableColumnDef,
DataTableCommand,
DataTableEmptyStateProps,
DataTableFilter,
DataTableFilteringState,
DataTablePaginationState,
DataTableRow,
DataTableRowSelectionState,
DataTableSortingState,
Heading,
DataTable as Primitive,
Text,
useDataTable,
} from "@medusajs/ui"
import React, { ReactNode, useCallback, useState } from "react"
@@ -66,14 +69,17 @@ interface DataTableProps<TData> {
autoFocusSearch?: boolean
rowHref?: (row: TData) => string
emptyState?: DataTableEmptyStateProps
heading: string
heading?: string
subHeading?: string
prefix?: string
pageSize?: number
isLoading?: boolean
rowSelection?: {
state: DataTableRowSelectionState
onRowSelectionChange: (value: DataTableRowSelectionState) => void
enableRowSelection?: boolean | ((row: DataTableRow<TData>) => boolean)
}
layout?: "fill" | "auto"
}
export const DataTable = <TData,>({
@@ -90,11 +96,13 @@ export const DataTable = <TData,>({
autoFocusSearch = false,
rowHref,
heading,
subHeading,
prefix,
pageSize = 10,
emptyState,
rowSelection,
isLoading = false,
layout = "auto",
}: DataTableProps<TData>) => {
const { t } = useTranslation()
@@ -258,14 +266,30 @@ export const DataTable = <TData,>({
isLoading,
})
const shouldRenderHeading = heading || subHeading
return (
<Primitive instance={instance}>
<Primitive
instance={instance}
className={clx({
"h-full [&_tr]:last-of-type:!border-b": layout === "fill",
})}
>
<Primitive.Toolbar
className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center"
translations={toolbarTranslations}
>
<div className="flex w-full items-center justify-between">
<Heading>{heading}</Heading>
<div className="flex w-full items-center justify-between gap-2">
{shouldRenderHeading && (
<div>
{heading && <Heading>{heading}</Heading>}
{subHeading && (
<Text size="small" className="text-ui-fg-subtle">
{subHeading}
</Text>
)}
</div>
)}
<div className="flex items-center justify-end gap-x-2 md:hidden">
{enableFiltering && (
<Primitive.FilterMenu tooltip={t("filters.filterLabel")} />

View File

@@ -0,0 +1,61 @@
import {
createDataTableColumnHelper,
DataTableColumnDef,
Tooltip,
} from "@medusajs/ui"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { useDate } from "../../../../hooks/use-date"
type EntityWithDates = {
created_at: string
updated_at: string
}
const columnHelper = createDataTableColumnHelper<EntityWithDates>()
export const useDataTableDateColumns = <TData extends EntityWithDates>() => {
const { t } = useTranslation()
const { getFullDate } = useDate()
return useMemo(() => {
return [
columnHelper.accessor("created_at", {
header: t("fields.createdAt"),
cell: ({ row }) => {
return (
<Tooltip
content={getFullDate({
date: row.original.created_at,
includeTime: true,
})}
>
<span>{getFullDate({ date: row.original.created_at })}</span>
</Tooltip>
)
},
enableSorting: true,
sortAscLabel: t("filters.sorting.dateAsc"),
sortDescLabel: t("filters.sorting.dateDesc"),
}),
columnHelper.accessor("updated_at", {
header: t("fields.updatedAt"),
cell: ({ row }) => {
return (
<Tooltip
content={getFullDate({
date: row.original.updated_at,
includeTime: true,
})}
>
<span>{getFullDate({ date: row.original.updated_at })}</span>
</Tooltip>
)
},
enableSorting: true,
sortAscLabel: t("filters.sorting.dateAsc"),
sortDescLabel: t("filters.sorting.dateDesc"),
}),
] as DataTableColumnDef<TData>[]
}, [t, getFullDate])
}

View File

@@ -0,0 +1,4 @@
export * from "./use-sales-channel-table-columns"
export * from "./use-sales-channel-table-empty-state"
export * from "./use-sales-channel-table-filters"
export * from "./use-sales-channel-table-query"

View File

@@ -0,0 +1,61 @@
import { HttpTypes } from "@medusajs/types"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { createDataTableColumnHelper, Tooltip } from "@medusajs/ui"
import { DataTableStatusCell } from "../../components/data-table-status-cell/data-table-status-cell"
import { useDataTableDateColumns } from "../general/use-data-table-date-columns"
const columnHelper = createDataTableColumnHelper<HttpTypes.AdminSalesChannel>()
export const useSalesChannelTableColumns = () => {
const { t } = useTranslation()
const dateColumns = useDataTableDateColumns<HttpTypes.AdminSalesChannel>()
return useMemo(
() => [
columnHelper.accessor("name", {
header: () => t("fields.name"),
enableSorting: true,
sortLabel: t("fields.name"),
sortAscLabel: t("filters.sorting.alphabeticallyAsc"),
sortDescLabel: t("filters.sorting.alphabeticallyDesc"),
}),
columnHelper.accessor("description", {
header: () => t("fields.description"),
cell: ({ getValue }) => {
return (
<Tooltip content={getValue()}>
<div className="flex h-full w-full items-center overflow-hidden">
<span className="truncate">{getValue()}</span>
</div>
</Tooltip>
)
},
enableSorting: true,
sortLabel: t("fields.description"),
sortAscLabel: t("filters.sorting.alphabeticallyAsc"),
sortDescLabel: t("filters.sorting.alphabeticallyDesc"),
maxSize: 250,
minSize: 100,
}),
columnHelper.accessor("is_disabled", {
header: () => t("fields.status"),
enableSorting: true,
sortLabel: t("fields.status"),
sortAscLabel: t("filters.sorting.alphabeticallyAsc"),
sortDescLabel: t("filters.sorting.alphabeticallyDesc"),
cell: ({ getValue }) => {
const value = getValue()
return (
<DataTableStatusCell color={value ? "grey" : "green"}>
{value ? t("general.disabled") : t("general.enabled")}
</DataTableStatusCell>
)
},
}),
...dateColumns,
],
[t, dateColumns]
)
}

View File

@@ -0,0 +1,22 @@
import { DataTableEmptyStateProps } from "@medusajs/ui"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
export const useSalesChannelTableEmptyState = (): DataTableEmptyStateProps => {
const { t } = useTranslation()
return useMemo(() => {
const content: DataTableEmptyStateProps = {
empty: {
heading: t("salesChannels.list.empty.heading"),
description: t("salesChannels.list.empty.description"),
},
filtered: {
heading: t("salesChannels.list.filtered.heading"),
description: t("salesChannels.list.filtered.description"),
},
}
return content
}, [t])
}

View File

@@ -0,0 +1,33 @@
import { HttpTypes } from "@medusajs/types"
import { createDataTableFilterHelper } from "@medusajs/ui"
import { useMemo } from "react"
import { useTranslation } from "react-i18next"
import { useDataTableDateFilters } from "../general/use-data-table-date-filters"
const filterHelper = createDataTableFilterHelper<HttpTypes.AdminSalesChannel>()
export const useSalesChannelTableFilters = () => {
const { t } = useTranslation()
const dateFilters = useDataTableDateFilters()
return useMemo(
() => [
filterHelper.accessor("is_disabled", {
label: t("fields.status"),
type: "radio",
options: [
{
label: t("general.enabled"),
value: "false",
},
{
label: t("general.disabled"),
value: "true",
},
],
}),
...dateFilters,
],
[dateFilters, t]
)
}

View File

@@ -0,0 +1,30 @@
import { HttpTypes } from "@medusajs/types"
import { useQueryParams } from "../../../../hooks/use-query-params"
type UseSalesChannelTableQueryProps = {
prefix?: string
pageSize?: number
}
export const useSalesChannelTableQuery = ({
prefix,
pageSize = 20,
}: UseSalesChannelTableQueryProps) => {
const queryObject = useQueryParams(
["offset", "q", "order", "created_at", "updated_at", "is_disabled"],
prefix
)
const { offset, created_at, updated_at, is_disabled, ...rest } = queryObject
const searchParams: HttpTypes.AdminSalesChannelListParams = {
limit: pageSize,
offset: offset ? Number(offset) : 0,
created_at: created_at ? JSON.parse(created_at) : undefined,
updated_at: updated_at ? JSON.parse(updated_at) : undefined,
is_disabled: is_disabled ? JSON.parse(is_disabled) : undefined,
...rest,
}
return searchParams
}