diff --git a/app/react/components/datatables/extend-options/mergeOptions.ts b/app/react/components/datatables/extend-options/mergeOptions.ts index 0c10ac314..b005ab97d 100644 --- a/app/react/components/datatables/extend-options/mergeOptions.ts +++ b/app/react/components/datatables/extend-options/mergeOptions.ts @@ -1,10 +1,10 @@ import { TableOptions } from '@tanstack/react-table'; -type OptionExtender = (options: TableOptions) => TableOptions; +import { OptionsExtension } from './types'; -export function mergeOptions( - ...extenders: Array> -): OptionExtender { - return (options: TableOptions) => +export function mergeOptions( + ...extenders: Array> +): OptionsExtension { + return (options: TableOptions) => extenders.reduce((acc, option) => option(acc), options); } diff --git a/app/react/components/datatables/extend-options/types.ts b/app/react/components/datatables/extend-options/types.ts new file mode 100644 index 000000000..abe676167 --- /dev/null +++ b/app/react/components/datatables/extend-options/types.ts @@ -0,0 +1,3 @@ +import { TableOptions } from '@tanstack/react-table'; + +export type OptionsExtension = (options: TableOptions) => TableOptions; diff --git a/app/react/components/datatables/extend-options/withColumnFilters.ts b/app/react/components/datatables/extend-options/withColumnFilters.ts new file mode 100644 index 000000000..a55705bd5 --- /dev/null +++ b/app/react/components/datatables/extend-options/withColumnFilters.ts @@ -0,0 +1,29 @@ +import { ColumnFiltersState, TableOptions } from '@tanstack/react-table'; + +import { applySetStateAction } from '@/react-tools/apply-set-state-action'; + +import { DefaultType } from '../types'; + +import { OptionsExtension } from './types'; + +export function withColumnFilters( + filters: ColumnFiltersState, + onChange: (filters: ColumnFiltersState) => void +): OptionsExtension { + return function extendOptions(options: TableOptions) { + return { + ...options, + state: { + ...options.state, + columnFilters: filters, + }, + onColumnFiltersChange: (updater) => { + onChange(applySetStateAction(updater, filters)); + }, + initialState: { + ...options.initialState, + columnFilters: filters, + }, + }; + }; +} diff --git a/app/react/components/datatables/extend-options/withControlledSelected.tsx b/app/react/components/datatables/extend-options/withControlledSelected.tsx index 58003ee05..f00f5f580 100644 --- a/app/react/components/datatables/extend-options/withControlledSelected.tsx +++ b/app/react/components/datatables/extend-options/withControlledSelected.tsx @@ -6,10 +6,12 @@ import { import { DefaultType } from '../types'; +import { OptionsExtension } from './types'; + export function withControlledSelected( onChange?: (value: string[]) => void, value?: string[] -) { +): OptionsExtension { return function extendTableOptions(options: TableOptions) { if (!onChange || !value) { return options; diff --git a/app/react/components/datatables/extend-options/withGlobalFilter.ts b/app/react/components/datatables/extend-options/withGlobalFilter.ts index cce2ec24b..59d29be5f 100644 --- a/app/react/components/datatables/extend-options/withGlobalFilter.ts +++ b/app/react/components/datatables/extend-options/withGlobalFilter.ts @@ -3,12 +3,14 @@ import { TableOptions } from '@tanstack/react-table'; import { defaultGlobalFilterFn } from '../Datatable'; import { DefaultType } from '../types'; +import { OptionsExtension } from './types'; + export function withGlobalFilter< D extends DefaultType, TFilter extends { search: string; }, ->(filterFn: typeof defaultGlobalFilterFn) { +>(filterFn: typeof defaultGlobalFilterFn): OptionsExtension { return function extendOptions(options: TableOptions) { return { ...options, diff --git a/app/react/components/datatables/extend-options/withMeta.ts b/app/react/components/datatables/extend-options/withMeta.ts index cddf02c3f..9434993ad 100644 --- a/app/react/components/datatables/extend-options/withMeta.ts +++ b/app/react/components/datatables/extend-options/withMeta.ts @@ -2,7 +2,11 @@ import { TableOptions } from '@tanstack/react-table'; import { DefaultType } from '../types'; -export function withMeta(meta: Record) { +import { OptionsExtension } from './types'; + +export function withMeta( + meta: Record +): OptionsExtension { return function extendOptions(options: TableOptions) { return { ...options, diff --git a/app/react/components/datatables/types.ts b/app/react/components/datatables/types.ts index 056481921..588d26ad3 100644 --- a/app/react/components/datatables/types.ts +++ b/app/react/components/datatables/types.ts @@ -1,5 +1,6 @@ import { createStore } from 'zustand'; import { persist } from 'zustand/middleware'; +import { ColumnFiltersState } from '@tanstack/react-table'; import { keyBuilder } from '@/react/hooks/useLocalStorage'; @@ -76,6 +77,22 @@ export function refreshableSettings( }; } +export interface FilteredColumnsTableSettings { + columnFilters: ColumnFiltersState; + setColumnFilters(columns: ColumnFiltersState): void; +} + +export function filteredColumnsSettings( + set: ZustandSetFunc +): FilteredColumnsTableSettings { + return { + columnFilters: [], + setColumnFilters(columns) { + set((s) => ({ ...s, columnFilters: columns })); + }, + }; +} + export interface BasicTableSettings extends SortableTableSettings, PaginationTableSettings {} diff --git a/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatable.tsx b/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatable.tsx index b7d0ba8f0..7a791a71b 100644 --- a/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatable.tsx +++ b/app/react/docker/containers/ListView/ContainersDatatable/ContainersDatatable.tsx @@ -15,6 +15,8 @@ import { } from '@@/datatables/ColumnVisibilityMenu'; import { TableSettingsProvider } from '@@/datatables/useTableSettings'; import { useTableState } from '@@/datatables/useTableState'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { useContainers } from '../../queries/containers'; @@ -92,6 +94,12 @@ export function ContainersDatatable({ )} dataset={containersQuery.data || []} emptyContentLabel="No containers found" + extendTableOptions={mergeOptions( + withColumnFilters( + tableState.columnFilters, + tableState.setColumnFilters + ) + )} /> diff --git a/app/react/docker/containers/ListView/ContainersDatatable/datatable-store.ts b/app/react/docker/containers/ListView/ContainersDatatable/datatable-store.ts index 1a45ca67e..3c4877661 100644 --- a/app/react/docker/containers/ListView/ContainersDatatable/datatable-store.ts +++ b/app/react/docker/containers/ListView/ContainersDatatable/datatable-store.ts @@ -2,6 +2,7 @@ import { refreshableSettings, hiddenColumnsSettings, createPersistedStore, + filteredColumnsSettings, } from '@@/datatables/types'; import { QuickAction, TableSettings } from './types'; @@ -12,6 +13,7 @@ export function createStore(storageKey: string) { return createPersistedStore(storageKey, 'name', (set) => ({ ...hiddenColumnsSettings(set), ...refreshableSettings(set), + ...filteredColumnsSettings(set), truncateContainerName: TRUNCATE_LENGTH, setTruncateContainerName(truncateContainerName: number) { set({ diff --git a/app/react/docker/containers/ListView/ContainersDatatable/types.ts b/app/react/docker/containers/ListView/ContainersDatatable/types.ts index b2cc66deb..ec1919034 100644 --- a/app/react/docker/containers/ListView/ContainersDatatable/types.ts +++ b/app/react/docker/containers/ListView/ContainersDatatable/types.ts @@ -1,5 +1,6 @@ import { BasicTableSettings, + FilteredColumnsTableSettings, RefreshableTableSettings, SettableColumnsTableSettings, } from '@@/datatables/types'; @@ -15,7 +16,8 @@ export interface TableSettings extends BasicTableSettings, SettableColumnsTableSettings, SettableQuickActionsTableSettings, - RefreshableTableSettings { + RefreshableTableSettings, + FilteredColumnsTableSettings { truncateContainerName: number; setTruncateContainerName: (value: number) => void; } diff --git a/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx b/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx index dfa0da397..2fd0ed8c8 100644 --- a/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx +++ b/app/react/docker/images/ListView/ImagesDatatable/ImagesDatatable.tsx @@ -10,6 +10,8 @@ import { Datatable, TableSettingsMenu } from '@@/datatables'; import { BasicTableSettings, createPersistedStore, + FilteredColumnsTableSettings, + filteredColumnsSettings, refreshableSettings, RefreshableTableSettings, } from '@@/datatables/types'; @@ -18,6 +20,8 @@ import { AddButton, Button, ButtonGroup, LoadingButton } from '@@/buttons'; import { Link } from '@@/Link'; import { ButtonWithRef } from '@@/buttons/Button'; import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { ImagesListResponse, useImages } from '../../queries/useImages'; @@ -28,13 +32,15 @@ const tableKey = 'images'; export interface TableSettings extends BasicTableSettings, - RefreshableTableSettings {} + RefreshableTableSettings, + FilteredColumnsTableSettings {} const settingsStore = createPersistedStore( tableKey, 'tags', (set) => ({ ...refreshableSettings(set), + ...filteredColumnsSettings(set), }) ); @@ -65,6 +71,9 @@ export function ImagesDatatable({ title="Images" titleIcon={List} data-cy="docker-images-datatable" + extendTableOptions={mergeOptions( + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} renderTableActions={(selectedItems) => (
diff --git a/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx b/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx index bcd533621..5d1ce33b9 100644 --- a/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx +++ b/app/react/docker/services/ItemView/TasksDatatable/TasksDatatable.tsx @@ -1,15 +1,24 @@ import { List } from 'lucide-react'; import { Datatable } from '@@/datatables'; -import { createPersistedStore } from '@@/datatables/types'; -import { useTableState } from '@@/datatables/useTableState'; +import { + BasicTableSettings, + type FilteredColumnsTableSettings, + filteredColumnsSettings, +} from '@@/datatables/types'; +import { useTableStateWithStorage } from '@@/datatables/useTableState'; import { withMeta } from '@@/datatables/extend-options/withMeta'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { useColumns } from './columns'; import { DecoratedTask } from './types'; const storageKey = 'docker-service-tasks'; -const store = createPersistedStore(storageKey); + +interface TableSettings + extends BasicTableSettings, + FilteredColumnsTableSettings {} export function TasksDatatable({ dataset, @@ -20,7 +29,13 @@ export function TasksDatatable({ isSlotColumnVisible: boolean; serviceName: string; }) { - const tableState = useTableState(store, storageKey); + const tableState = useTableStateWithStorage( + storageKey, + undefined, + (set) => ({ + ...filteredColumnsSettings(set), + }) + ); const columns = useColumns(isSlotColumnVisible); return ( @@ -31,7 +46,11 @@ export function TasksDatatable({ columns={columns} dataset={dataset} emptyContentLabel="No task available." - extendTableOptions={withMeta({ table: 'tasks', serviceName })} + extendTableOptions={mergeOptions( + withMeta({ table: 'tasks', serviceName }), + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} + disableSelect data-cy="docker-service-tasks-datatable" /> ); diff --git a/app/react/docker/stacks/ListView/StacksDatatable/StacksDatatable.tsx b/app/react/docker/stacks/ListView/StacksDatatable/StacksDatatable.tsx index 7ff0bcf15..8a660e0b6 100644 --- a/app/react/docker/stacks/ListView/StacksDatatable/StacksDatatable.tsx +++ b/app/react/docker/stacks/ListView/StacksDatatable/StacksDatatable.tsx @@ -5,22 +5,20 @@ import { useAuthorizations, useIsEdgeAdmin } from '@/react/hooks/useUser'; import { isBE } from '@/react/portainer/feature-flags/feature-flags.service'; import { Datatable } from '@@/datatables'; -import { useTableState } from '@@/datatables/useTableState'; import { useRepeater } from '@@/datatables/useRepeater'; import { defaultGlobalFilterFn } from '@@/datatables/Datatable'; import { withGlobalFilter } from '@@/datatables/extend-options/withGlobalFilter'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { isExternalStack, isOrphanedStack } from '../../view-models/utils'; import { TableActions } from './TableActions'; import { TableSettingsMenus } from './TableSettingsMenus'; -import { createStore } from './store'; +import { useStore } from './store'; import { useColumns } from './columns'; import { DecoratedStack } from './types'; -const tableKey = 'docker_stacks'; -const settingsStore = createStore(tableKey); - export function StacksDatatable({ onRemove, onReload, @@ -32,7 +30,7 @@ export function StacksDatatable({ isImageNotificationEnabled: boolean; dataset: Array; }) { - const tableState = useTableState(settingsStore, tableKey); + const tableState = useStore(); useRepeater(tableState.autoRefreshRate, onReload); const isAdminQuery = useIsEdgeAdmin(); const { authorized: canManageStacks } = useAuthorizations([ @@ -69,7 +67,10 @@ export function StacksDatatable({ tableState.hiddenColumns.map((col) => [col, false]) ), }} - extendTableOptions={withGlobalFilter(globalFilterFn)} + extendTableOptions={mergeOptions( + withGlobalFilter(globalFilterFn), + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} data-cy="docker-stacks-datatable" /> ); diff --git a/app/react/docker/stacks/ListView/StacksDatatable/store.ts b/app/react/docker/stacks/ListView/StacksDatatable/store.ts index 25f2743b3..6546be685 100644 --- a/app/react/docker/stacks/ListView/StacksDatatable/store.ts +++ b/app/react/docker/stacks/ListView/StacksDatatable/store.ts @@ -1,24 +1,30 @@ import { - BasicTableSettings, - RefreshableTableSettings, - SettableColumnsTableSettings, - createPersistedStore, + type BasicTableSettings, + type FilteredColumnsTableSettings, + type RefreshableTableSettings, + type SettableColumnsTableSettings, hiddenColumnsSettings, refreshableSettings, + filteredColumnsSettings, } from '@@/datatables/types'; +import { useTableStateWithStorage } from '@@/datatables/useTableState'; export interface TableSettings extends BasicTableSettings, SettableColumnsTableSettings, - RefreshableTableSettings { + RefreshableTableSettings, + FilteredColumnsTableSettings { showOrphanedStacks: boolean; setShowOrphanedStacks(value: boolean): void; } -export function createStore(storageKey: string) { - return createPersistedStore(storageKey, 'name', (set) => ({ +const tableKey = 'docker_stacks'; + +export function useStore() { + return useTableStateWithStorage(tableKey, 'name', (set) => ({ ...hiddenColumnsSettings(set), ...refreshableSettings(set), + ...filteredColumnsSettings(set), showOrphanedStacks: false, setShowOrphanedStacks(showOrphanedStacks) { set((s) => ({ ...s, showOrphanedStacks })); diff --git a/app/react/docker/volumes/ListView/VolumesDatatable/VolumesDatatable.tsx b/app/react/docker/volumes/ListView/VolumesDatatable/VolumesDatatable.tsx index f236562aa..186af476d 100644 --- a/app/react/docker/volumes/ListView/VolumesDatatable/VolumesDatatable.tsx +++ b/app/react/docker/volumes/ListView/VolumesDatatable/VolumesDatatable.tsx @@ -4,6 +4,8 @@ import { Datatable, TableSettingsMenu } from '@@/datatables'; import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh'; import { BasicTableSettings, + FilteredColumnsTableSettings, + filteredColumnsSettings, RefreshableTableSettings, createPersistedStore, refreshableSettings, @@ -11,13 +13,17 @@ import { import { useRepeater } from '@@/datatables/useRepeater'; import { useTableState } from '@@/datatables/useTableState'; import { withMeta } from '@@/datatables/extend-options/withMeta'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { DecoratedVolume } from '../types'; import { TableActions } from './TableActions'; import { useColumns } from './columns'; -interface TableSettings extends BasicTableSettings, RefreshableTableSettings {} +interface TableSettings + extends BasicTableSettings, + RefreshableTableSettings, + FilteredColumnsTableSettings {} const storageKey = 'docker-volumes'; const store = createPersistedStore( @@ -25,6 +31,7 @@ const store = createPersistedStore( undefined, (set) => ({ ...refreshableSettings(set), + ...filteredColumnsSettings(set), }) ); @@ -63,10 +70,16 @@ export function VolumesDatatable({ /> )} - extendTableOptions={withMeta({ - table: 'volumes', - isBrowseVisible, - })} + extendTableOptions={ + (withMeta({ + table: 'volumes', + isBrowseVisible, + }), + withColumnFilters( + tableState.columnFilters, + tableState.setColumnFilters + )) + } data-cy="docker-volumes-datatable" /> ); diff --git a/app/react/hooks/useUnauthorizedRedirect.ts b/app/react/hooks/useUnauthorizedRedirect.ts index 1a181e05d..d7b2a1d8c 100644 --- a/app/react/hooks/useUnauthorizedRedirect.ts +++ b/app/react/hooks/useUnauthorizedRedirect.ts @@ -10,7 +10,7 @@ type AuthorizationOptions = { type RedirectOptions = { to: string; - params: Record; + params?: Record; }; /** diff --git a/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx b/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx index 39cdbd1bb..8f2942920 100644 --- a/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx +++ b/app/react/kubernetes/configs/ListView/ConfigMapsDatatable/ConfigMapsDatatable.tsx @@ -5,8 +5,11 @@ import { CronJob, Job } from 'kubernetes-types/batch/v1'; import { useEnvironmentId } from '@/react/hooks/useEnvironmentId'; import { Authorized, useAuthorizations } from '@/react/hooks/useUser'; -import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings'; -import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; +import { + DefaultDatatableSettings, + TableSettings as KubeTableSettings, +} from '@/react/kubernetes/datatables/DefaultDatatableSettings'; +import { useKubeStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription'; import { pluralize } from '@/portainer/helpers/strings'; import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery'; @@ -17,9 +20,14 @@ import { useJobs } from '@/react/kubernetes/applications/useJobs'; import { useCronJobs } from '@/react/kubernetes/applications/useCronJobs'; import { Datatable, TableSettingsMenu } from '@@/datatables'; -import { useTableState } from '@@/datatables/useTableState'; +import { AddButton } from '@@/buttons'; import { DeleteButton } from '@@/buttons/DeleteButton'; -import { AddButton } from '@@/buttons/AddButton'; +import { + type FilteredColumnsTableSettings, + filteredColumnsSettings, +} from '@@/datatables/types'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { useConfigMapsForCluster, @@ -31,11 +39,20 @@ import { getIsConfigMapInUse } from './utils'; import { ConfigMapRowData } from './types'; import { columns } from './columns'; +interface TableSettings + extends KubeTableSettings, + FilteredColumnsTableSettings {} + const storageKey = 'k8sConfigMapsDatatable'; -const settingsStore = createStore(storageKey); export function ConfigMapsDatatable() { - const tableState = useTableState(settingsStore, storageKey); + const tableState = useKubeStore( + storageKey, + undefined, + (set) => ({ + ...filteredColumnsSettings(set), + }) + ); const { authorized: canWrite } = useAuthorizations(['K8sConfigMapsW']); const readOnly = !canWrite; const { authorized: canAccessSystemResources } = useAuthorizations( @@ -109,6 +126,9 @@ export function ConfigMapsDatatable() { /> } data-cy="k8s-configmaps-datatable" + extendTableOptions={mergeOptions( + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} /> ); } diff --git a/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx b/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx index 704a58c61..f0e560696 100644 --- a/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx +++ b/app/react/kubernetes/configs/ListView/SecretsDatatable/SecretsDatatable.tsx @@ -5,8 +5,11 @@ import { CronJob, Job } from 'kubernetes-types/batch/v1'; import { useEnvironmentId } from '@/react/hooks/useEnvironmentId'; import { Authorized, useAuthorizations } from '@/react/hooks/useUser'; -import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings'; -import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; +import { + DefaultDatatableSettings, + TableSettings as KubeTableSettings, +} from '@/react/kubernetes/datatables/DefaultDatatableSettings'; +import { useKubeStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription'; import { pluralize } from '@/portainer/helpers/strings'; import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery'; @@ -18,8 +21,13 @@ import { useCronJobs } from '@/react/kubernetes/applications/useCronJobs'; import { Datatable, TableSettingsMenu } from '@@/datatables'; import { AddButton } from '@@/buttons'; -import { useTableState } from '@@/datatables/useTableState'; import { DeleteButton } from '@@/buttons/DeleteButton'; +import { + type FilteredColumnsTableSettings, + filteredColumnsSettings, +} from '@@/datatables/types'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { useSecretsForCluster, @@ -32,17 +40,26 @@ import { SecretRowData } from './types'; import { columns } from './columns'; const storageKey = 'k8sSecretsDatatable'; -const settingsStore = createStore(storageKey); + +interface TableSettings + extends KubeTableSettings, + FilteredColumnsTableSettings {} export function SecretsDatatable() { - const tableState = useTableState(settingsStore, storageKey); + const tableState = useKubeStore( + storageKey, + undefined, + (set) => ({ + ...filteredColumnsSettings(set), + }) + ); + const environmentId = useEnvironmentId(); const { authorized: canWrite } = useAuthorizations(['K8sSecretsW']); const readOnly = !canWrite; const { authorized: canAccessSystemResources } = useAuthorizations( 'K8sAccessSystemNamespaces' ); - const environmentId = useEnvironmentId(); const { data: namespaces, ...namespacesQuery } = useNamespacesQuery( environmentId, { @@ -109,6 +126,9 @@ export function SecretsDatatable() { /> } data-cy="k8s-secrets-datatable" + extendTableOptions={mergeOptions( + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} /> ); } diff --git a/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx b/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx index 040e31204..cbd31eb81 100644 --- a/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx +++ b/app/react/kubernetes/ingresses/IngressDatatable/IngressDatatable.tsx @@ -4,14 +4,22 @@ import { useMemo } from 'react'; import { useEnvironmentId } from '@/react/hooks/useEnvironmentId'; import { useAuthorizations, Authorized } from '@/react/hooks/useUser'; import Route from '@/assets/ico/route.svg?c'; -import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings'; -import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; +import { + DefaultDatatableSettings, + TableSettings as KubeTableSettings, +} from '@/react/kubernetes/datatables/DefaultDatatableSettings'; +import { useKubeStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription'; import { Datatable, TableSettingsMenu } from '@@/datatables'; import { AddButton } from '@@/buttons'; -import { useTableState } from '@@/datatables/useTableState'; import { DeleteButton } from '@@/buttons/DeleteButton'; +import { + type FilteredColumnsTableSettings, + filteredColumnsSettings, +} from '@@/datatables/types'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { DeleteIngressesRequest, Ingress } from '../types'; import { useDeleteIngresses, useIngresses } from '../queries'; @@ -29,10 +37,18 @@ interface SelectedIngress { } const storageKey = 'ingressClassesNameSpace'; -const settingsStore = createStore(storageKey); +interface TableSettings + extends KubeTableSettings, + FilteredColumnsTableSettings {} export function IngressDatatable() { - const tableState = useTableState(settingsStore, storageKey); + const tableState = useKubeStore( + storageKey, + undefined, + (set) => ({ + ...filteredColumnsSettings(set), + }) + ); const environmentId = useEnvironmentId(); const { authorized: canAccessSystemResources } = useAuthorizations( @@ -91,6 +107,9 @@ export function IngressDatatable() { } disableSelect={useCheckboxes()} data-cy="k8s-ingresses-datatable" + extendTableOptions={mergeOptions( + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} /> ); diff --git a/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx b/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx index 000308852..193859fc9 100644 --- a/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx +++ b/app/react/kubernetes/services/ServicesView/ServicesDatatable/ServicesDatatable.tsx @@ -9,14 +9,23 @@ import { useEnvironmentId } from '@/react/hooks/useEnvironmentId'; import { Authorized, useAuthorizations } from '@/react/hooks/useUser'; import { notifyError, notifySuccess } from '@/portainer/services/notifications'; import { pluralize } from '@/portainer/helpers/strings'; -import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings'; +import { + DefaultDatatableSettings, + TableSettings as KubeTableSettings, +} from '@/react/kubernetes/datatables/DefaultDatatableSettings'; +import { useKubeStore } from '@/react/kubernetes/datatables/default-kube-datatable-store'; import { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription'; import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery'; import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton'; import { Datatable, Table, TableSettingsMenu } from '@@/datatables'; -import { useTableState } from '@@/datatables/useTableState'; import { DeleteButton } from '@@/buttons/DeleteButton'; +import { + type FilteredColumnsTableSettings, + filteredColumnsSettings, +} from '@@/datatables/types'; +import { mergeOptions } from '@@/datatables/extend-options/mergeOptions'; +import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters'; import { useMutationDeleteServices, @@ -25,13 +34,20 @@ import { import { Service } from '../../types'; import { columns } from './columns'; -import { createStore } from './datatable-store'; const storageKey = 'k8sServicesDatatable'; -const settingsStore = createStore(storageKey); +interface TableSettings + extends KubeTableSettings, + FilteredColumnsTableSettings {} export function ServicesDatatable() { - const tableState = useTableState(settingsStore, storageKey); + const tableState = useKubeStore( + storageKey, + undefined, + (set) => ({ + ...filteredColumnsSettings(set), + }) + ); const environmentId = useEnvironmentId(); const { data: namespaces, ...namespacesQuery } = useNamespacesQuery(environmentId); @@ -91,6 +107,9 @@ export function ServicesDatatable() { } renderRow={servicesRenderRow} data-cy="k8s-services-datatable" + extendTableOptions={mergeOptions( + withColumnFilters(tableState.columnFilters, tableState.setColumnFilters) + )} /> ); }