Feat/datatable core enhancements (#13193)

**What**

This PR adds core DataTable enhancements to support view configurations in the admin dashboard. This is part of a set of stacked PRs to add the feature to Medusa.

- Puts handlers in place to update the visible columns in a table and the order in which they appear.
- Adds a ViewPills component for displaying and switching between saved view configurations
- Integrated view configuration hooks (useViewConfigurations) with the DataTable

Note: Column drag-and-drop reordering and the column visibility UI controls are not included in this PR as they require additional UI library updates - which will come in the next PR.

Example of what this looks like with the feature flag turned on - note the view pills with "default" in the top. This will expand when the data table behavior adds configuration.
<img width="2492" height="758" alt="CleanShot 2025-08-13 at 2  31 35@2x" src="https://github.com/user-attachments/assets/ee770f1c-dae1-49da-b255-1a6d615789de" />
This commit is contained in:
Sebastian Rindom
2025-09-01 10:30:05 +00:00
committed by GitHub
parent fa10c78ed3
commit 5a46372afd
12 changed files with 1368 additions and 83 deletions
@@ -1,19 +1,108 @@
import { Container, Heading } from "@medusajs/ui"
import { keepPreviousData } from "@tanstack/react-query"
import { useTranslation } from "react-i18next"
import { useState, useEffect } from "react"
import { DataTable } from "../../../../../components/data-table"
import { useOrders } from "../../../../../hooks/api/orders"
import { useOrderTableColumns } from "../../../../../hooks/table/columns/use-order-table-columns"
import { useOrderTableFilters } from "../../../../../hooks/table/filters/use-order-table-filters"
import { useOrderTableQuery } from "../../../../../hooks/table/query/use-order-table-query"
import { DEFAULT_FIELDS } from "../../const"
const PAGE_SIZE = 20
export const ConfigurableOrderListTable = () => {
const { t } = useTranslation()
const [columnVisibility, setColumnVisibility] = useState<Record<string, boolean>>({})
const [columnOrder, setColumnOrder] = useState<string[]>([])
const { searchParams, raw } = useOrderTableQuery({
pageSize: PAGE_SIZE,
})
const { orders, count, isError, error, isLoading } = useOrders(
{
fields: DEFAULT_FIELDS,
...searchParams,
},
{
placeholderData: keepPreviousData,
}
)
const filters = useOrderTableFilters()
const columns = useOrderTableColumns({})
const handleViewChange = (view: any) => {
if (view) {
// Apply view configuration
const visibilityState: Record<string, boolean> = {}
const allColumns = columns.map(c => c.id!)
// Set all columns to hidden first
allColumns.forEach(col => {
visibilityState[col] = false
})
// Then show only the visible columns from the view
if (view.configuration?.visible_columns) {
view.configuration.visible_columns.forEach((col: string) => {
visibilityState[col] = true
})
}
setColumnVisibility(visibilityState)
if (view.configuration?.column_order) {
setColumnOrder(view.configuration.column_order)
}
} else {
// Reset to default (all visible)
setColumnVisibility({})
setColumnOrder([])
}
}
if (isError) {
throw error
}
return (
<Container className="divide-y p-0">
<div className="flex items-center justify-between px-6 py-4">
<Heading>{t("orders.domain")}</Heading>
</div>
<div className="px-6 py-4">
<p className="text-ui-fg-muted">
View configurations feature is enabled. Full implementation coming soon.
</p>
</div>
<DataTable
data={orders ?? []}
columns={columns}
filters={filters}
getRowId={(row) => row.id}
rowCount={count}
enablePagination
enableSearch
pageSize={PAGE_SIZE}
isLoading={isLoading}
layout="fill"
heading={t("orders.domain")}
enableColumnVisibility={true}
initialColumnVisibility={columnVisibility}
onColumnVisibilityChange={setColumnVisibility}
columnOrder={columnOrder}
onColumnOrderChange={setColumnOrder}
enableViewSelector={true}
entity="orders"
onViewChange={handleViewChange}
currentColumns={{
visible: Object.entries(columnVisibility)
.filter(([_, visible]) => visible !== false)
.map(([col]) => col),
order: columnOrder.length > 0 ? columnOrder : columns.map(c => c.id!).filter(Boolean)
}}
rowHref={(row) => `/orders/${row.id}`}
emptyState={{
message: t("orders.list.noRecordsMessage"),
}}
/>
</Container>
)
}