feat(dashboard): refactor location list UI to use data table (#13571)

* wip: convert location list to a table

* chore: changeset

* fix: rm search bluring on loading change

* feat: translations and palceholders, cleanup, make content more compact

* fix: delete message

* chore: optimise use memo

* fix: update toast

* feat: make stock location address searchable

* fix: search input blur on load finish
This commit is contained in:
Frane Polić
2025-09-24 10:29:13 +02:00
committed by GitHub
parent 6e806942c7
commit 10787c865f
15 changed files with 373 additions and 134 deletions

View File

@@ -31,26 +31,26 @@ type DataTableActionProps = {
label: string
disabled?: boolean
} & (
| {
| {
to: string
}
| {
| {
onClick: () => void
}
)
)
type DataTableActionMenuActionProps = {
label: string
icon: ReactNode
disabled?: boolean
} & (
| {
| {
to: string
}
| {
| {
onClick: () => void
}
)
)
type DataTableActionMenuGroupProps = {
actions: DataTableActionMenuActionProps[]
@@ -138,15 +138,18 @@ export const DataTable = <TData,>({
const isViewConfigEnabled = useFeatureFlag("view_configurations")
// If view config is disabled, don't use column visibility features
const effectiveEnableColumnVisibility = isViewConfigEnabled && enableColumnVisibility
const effectiveEnableColumnVisibility =
isViewConfigEnabled && enableColumnVisibility
const effectiveEnableViewSelector = isViewConfigEnabled && enableViewSelector
const enableFiltering = filters && filters.length > 0
const showFilterMenu = enableFilterMenu !== undefined ? enableFilterMenu : enableFiltering
const showFilterMenu =
enableFilterMenu !== undefined ? enableFilterMenu : enableFiltering
const enableCommands = commands && commands.length > 0
const enableSorting = columns.some((column) => column.enableSorting)
const [columnVisibility, setColumnVisibility] = React.useState<VisibilityState>(initialColumnVisibility)
const [columnVisibility, setColumnVisibility] =
React.useState<VisibilityState>(initialColumnVisibility)
// Update column visibility when initial visibility changes
React.useEffect(() => {
@@ -154,9 +157,12 @@ export const DataTable = <TData,>({
const currentKeys = Object.keys(columnVisibility).sort()
const newKeys = Object.keys(initialColumnVisibility).sort()
const hasChanged = currentKeys.length !== newKeys.length ||
const hasChanged =
currentKeys.length !== newKeys.length ||
currentKeys.some((key, index) => key !== newKeys[index]) ||
Object.entries(initialColumnVisibility).some(([key, value]) => columnVisibility[key] !== value)
Object.entries(initialColumnVisibility).some(
([key, value]) => columnVisibility[key] !== value
)
if (hasChanged) {
setColumnVisibility(initialColumnVisibility)
@@ -164,10 +170,13 @@ export const DataTable = <TData,>({
}, [initialColumnVisibility])
// Wrapper function to handle column visibility changes
const handleColumnVisibilityChange = React.useCallback((visibility: VisibilityState) => {
setColumnVisibility(visibility)
onColumnVisibilityChange?.(visibility)
}, [onColumnVisibilityChange])
const handleColumnVisibilityChange = React.useCallback(
(visibility: VisibilityState) => {
setColumnVisibility(visibility)
onColumnVisibilityChange?.(visibility)
},
[onColumnVisibilityChange]
)
// Extract filter IDs for query param management
const filterIds = useMemo(() => filters?.map((f) => f.id) ?? [], [filters])
@@ -231,7 +240,7 @@ export const DataTable = <TData,>({
Array.from(prev.keys()).forEach((key) => {
if (prefixedFilterIds.includes(key)) {
// Extract the unprefixed key
const unprefixedKey = prefix ? key.replace(`${prefix}_`, '') : key
const unprefixedKey = prefix ? key.replace(`${prefix}_`, "") : key
if (!(unprefixedKey in value)) {
prev.delete(key)
}
@@ -257,11 +266,14 @@ export const DataTable = <TData,>({
}, [order])
// Memoize current configuration to prevent infinite loops
const currentConfiguration = useMemo(() => ({
filters: filtering,
sorting: sorting,
search: search,
}), [filtering, sorting, search])
const currentConfiguration = useMemo(
() => ({
filters: filtering,
sorting: sorting,
search: search,
}),
[filtering, sorting, search]
)
const handleSortingChange = (value: DataTableSortingState) => {
setSearchParams((prev) => {
@@ -315,42 +327,43 @@ export const DataTable = <TData,>({
onRowClick: rowHref ? onRowClick : undefined,
pagination: enablePagination
? {
state: pagination,
onPaginationChange: handlePaginationChange,
}
state: pagination,
onPaginationChange: handlePaginationChange,
}
: undefined,
filtering: enableFiltering
? {
state: filtering,
onFilteringChange: handleFilteringChange,
}
state: filtering,
onFilteringChange: handleFilteringChange,
}
: undefined,
sorting: enableSorting
? {
state: sorting,
onSortingChange: handleSortingChange,
}
state: sorting,
onSortingChange: handleSortingChange,
}
: undefined,
search: enableSearch
? {
state: search,
onSearchChange: handleSearchChange,
}
state: search,
onSearchChange: handleSearchChange,
}
: undefined,
rowSelection,
isLoading,
columnVisibility: effectiveEnableColumnVisibility
? {
state: columnVisibility,
onColumnVisibilityChange: handleColumnVisibilityChange,
}
: undefined,
columnOrder: effectiveEnableColumnVisibility && columnOrder && onColumnOrderChange
? {
state: columnOrder,
onColumnOrderChange: onColumnOrderChange,
}
state: columnVisibility,
onColumnVisibilityChange: handleColumnVisibilityChange,
}
: undefined,
columnOrder:
effectiveEnableColumnVisibility && columnOrder && onColumnOrderChange
? {
state: columnOrder,
onColumnOrderChange: onColumnOrderChange,
}
: undefined,
})
const shouldRenderHeading = heading || subHeading
@@ -358,7 +371,9 @@ export const DataTable = <TData,>({
return (
<UiDataTable
instance={instance}
className={layout === "fill" ? "h-full [&_tr]:last-of-type:!border-b" : undefined}
className={
layout === "fill" ? "h-full [&_tr]:last-of-type:!border-b" : undefined
}
>
<UiDataTable.Toolbar
className="flex flex-col items-start justify-between gap-2 md:flex-row md:items-center"
@@ -397,7 +412,9 @@ export const DataTable = <TData,>({
</div>
)}
{actionMenu && <ActionMenu variant="primary" {...actionMenu} />}
{actions && actions.length > 0 && <DataTableActions actions={actions} />}
{actions && actions.length > 0 && (
<DataTableActions actions={actions} />
)}
{!actions && action && <DataTableAction {...action} />}
</div>
</div>
@@ -407,7 +424,9 @@ export const DataTable = <TData,>({
<UiDataTable.Pagination translations={paginationTranslations} />
)}
{enableCommands && (
<UiDataTable.CommandBar selectedLabel={(count) => `${count} selected`} />
<UiDataTable.CommandBar
selectedLabel={(count) => `${count} selected`}
/>
)}
</UiDataTable>
)
@@ -520,4 +539,3 @@ const DataTableActions = ({ actions }: { actions: DataTableActionProps[] }) => {
</div>
)
}