mirror of https://github.com/portainer/portainer
fix(stacks): store filter state [EE-5159] (#11637)
parent
84fe3cf2a2
commit
1261887c9e
|
@ -1,10 +1,10 @@
|
||||||
import { TableOptions } from '@tanstack/react-table';
|
import { TableOptions } from '@tanstack/react-table';
|
||||||
|
|
||||||
type OptionExtender<T> = (options: TableOptions<T>) => TableOptions<T>;
|
import { OptionsExtension } from './types';
|
||||||
|
|
||||||
export function mergeOptions<T>(
|
export function mergeOptions<D>(
|
||||||
...extenders: Array<OptionExtender<T>>
|
...extenders: Array<OptionsExtension<D>>
|
||||||
): OptionExtender<T> {
|
): OptionsExtension<D> {
|
||||||
return (options: TableOptions<T>) =>
|
return (options: TableOptions<D>) =>
|
||||||
extenders.reduce((acc, option) => option(acc), options);
|
extenders.reduce((acc, option) => option(acc), options);
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,3 @@
|
||||||
|
import { TableOptions } from '@tanstack/react-table';
|
||||||
|
|
||||||
|
export type OptionsExtension<D> = (options: TableOptions<D>) => TableOptions<D>;
|
|
@ -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<D extends DefaultType>(
|
||||||
|
filters: ColumnFiltersState,
|
||||||
|
onChange: (filters: ColumnFiltersState) => void
|
||||||
|
): OptionsExtension<D> {
|
||||||
|
return function extendOptions(options: TableOptions<D>) {
|
||||||
|
return {
|
||||||
|
...options,
|
||||||
|
state: {
|
||||||
|
...options.state,
|
||||||
|
columnFilters: filters,
|
||||||
|
},
|
||||||
|
onColumnFiltersChange: (updater) => {
|
||||||
|
onChange(applySetStateAction(updater, filters));
|
||||||
|
},
|
||||||
|
initialState: {
|
||||||
|
...options.initialState,
|
||||||
|
columnFilters: filters,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
};
|
||||||
|
}
|
|
@ -6,10 +6,12 @@ import {
|
||||||
|
|
||||||
import { DefaultType } from '../types';
|
import { DefaultType } from '../types';
|
||||||
|
|
||||||
|
import { OptionsExtension } from './types';
|
||||||
|
|
||||||
export function withControlledSelected<D extends DefaultType>(
|
export function withControlledSelected<D extends DefaultType>(
|
||||||
onChange?: (value: string[]) => void,
|
onChange?: (value: string[]) => void,
|
||||||
value?: string[]
|
value?: string[]
|
||||||
) {
|
): OptionsExtension<D> {
|
||||||
return function extendTableOptions(options: TableOptions<D>) {
|
return function extendTableOptions(options: TableOptions<D>) {
|
||||||
if (!onChange || !value) {
|
if (!onChange || !value) {
|
||||||
return options;
|
return options;
|
||||||
|
|
|
@ -3,12 +3,14 @@ import { TableOptions } from '@tanstack/react-table';
|
||||||
import { defaultGlobalFilterFn } from '../Datatable';
|
import { defaultGlobalFilterFn } from '../Datatable';
|
||||||
import { DefaultType } from '../types';
|
import { DefaultType } from '../types';
|
||||||
|
|
||||||
|
import { OptionsExtension } from './types';
|
||||||
|
|
||||||
export function withGlobalFilter<
|
export function withGlobalFilter<
|
||||||
D extends DefaultType,
|
D extends DefaultType,
|
||||||
TFilter extends {
|
TFilter extends {
|
||||||
search: string;
|
search: string;
|
||||||
},
|
},
|
||||||
>(filterFn: typeof defaultGlobalFilterFn<D, TFilter>) {
|
>(filterFn: typeof defaultGlobalFilterFn<D, TFilter>): OptionsExtension<D> {
|
||||||
return function extendOptions(options: TableOptions<D>) {
|
return function extendOptions(options: TableOptions<D>) {
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
|
|
|
@ -2,7 +2,11 @@ import { TableOptions } from '@tanstack/react-table';
|
||||||
|
|
||||||
import { DefaultType } from '../types';
|
import { DefaultType } from '../types';
|
||||||
|
|
||||||
export function withMeta<D extends DefaultType>(meta: Record<string, unknown>) {
|
import { OptionsExtension } from './types';
|
||||||
|
|
||||||
|
export function withMeta<D extends DefaultType>(
|
||||||
|
meta: Record<string, unknown>
|
||||||
|
): OptionsExtension<D> {
|
||||||
return function extendOptions(options: TableOptions<D>) {
|
return function extendOptions(options: TableOptions<D>) {
|
||||||
return {
|
return {
|
||||||
...options,
|
...options,
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { createStore } from 'zustand';
|
import { createStore } from 'zustand';
|
||||||
import { persist } from 'zustand/middleware';
|
import { persist } from 'zustand/middleware';
|
||||||
|
import { ColumnFiltersState } from '@tanstack/react-table';
|
||||||
|
|
||||||
import { keyBuilder } from '@/react/hooks/useLocalStorage';
|
import { keyBuilder } from '@/react/hooks/useLocalStorage';
|
||||||
|
|
||||||
|
@ -76,6 +77,22 @@ export function refreshableSettings<T extends RefreshableTableSettings>(
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface FilteredColumnsTableSettings {
|
||||||
|
columnFilters: ColumnFiltersState;
|
||||||
|
setColumnFilters(columns: ColumnFiltersState): void;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function filteredColumnsSettings<T extends FilteredColumnsTableSettings>(
|
||||||
|
set: ZustandSetFunc<T>
|
||||||
|
): FilteredColumnsTableSettings {
|
||||||
|
return {
|
||||||
|
columnFilters: [],
|
||||||
|
setColumnFilters(columns) {
|
||||||
|
set((s) => ({ ...s, columnFilters: columns }));
|
||||||
|
},
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
export interface BasicTableSettings
|
export interface BasicTableSettings
|
||||||
extends SortableTableSettings,
|
extends SortableTableSettings,
|
||||||
PaginationTableSettings {}
|
PaginationTableSettings {}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import {
|
||||||
} from '@@/datatables/ColumnVisibilityMenu';
|
} from '@@/datatables/ColumnVisibilityMenu';
|
||||||
import { TableSettingsProvider } from '@@/datatables/useTableSettings';
|
import { TableSettingsProvider } from '@@/datatables/useTableSettings';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
import { useTableState } from '@@/datatables/useTableState';
|
||||||
|
import { mergeOptions } from '@@/datatables/extend-options/mergeOptions';
|
||||||
|
import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters';
|
||||||
|
|
||||||
import { useContainers } from '../../queries/containers';
|
import { useContainers } from '../../queries/containers';
|
||||||
|
|
||||||
|
@ -92,6 +94,12 @@ export function ContainersDatatable({
|
||||||
)}
|
)}
|
||||||
dataset={containersQuery.data || []}
|
dataset={containersQuery.data || []}
|
||||||
emptyContentLabel="No containers found"
|
emptyContentLabel="No containers found"
|
||||||
|
extendTableOptions={mergeOptions(
|
||||||
|
withColumnFilters(
|
||||||
|
tableState.columnFilters,
|
||||||
|
tableState.setColumnFilters
|
||||||
|
)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
</TableSettingsProvider>
|
</TableSettingsProvider>
|
||||||
</RowProvider>
|
</RowProvider>
|
||||||
|
|
|
@ -2,6 +2,7 @@ import {
|
||||||
refreshableSettings,
|
refreshableSettings,
|
||||||
hiddenColumnsSettings,
|
hiddenColumnsSettings,
|
||||||
createPersistedStore,
|
createPersistedStore,
|
||||||
|
filteredColumnsSettings,
|
||||||
} from '@@/datatables/types';
|
} from '@@/datatables/types';
|
||||||
|
|
||||||
import { QuickAction, TableSettings } from './types';
|
import { QuickAction, TableSettings } from './types';
|
||||||
|
@ -12,6 +13,7 @@ export function createStore(storageKey: string) {
|
||||||
return createPersistedStore<TableSettings>(storageKey, 'name', (set) => ({
|
return createPersistedStore<TableSettings>(storageKey, 'name', (set) => ({
|
||||||
...hiddenColumnsSettings(set),
|
...hiddenColumnsSettings(set),
|
||||||
...refreshableSettings(set),
|
...refreshableSettings(set),
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
truncateContainerName: TRUNCATE_LENGTH,
|
truncateContainerName: TRUNCATE_LENGTH,
|
||||||
setTruncateContainerName(truncateContainerName: number) {
|
setTruncateContainerName(truncateContainerName: number) {
|
||||||
set({
|
set({
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import {
|
import {
|
||||||
BasicTableSettings,
|
BasicTableSettings,
|
||||||
|
FilteredColumnsTableSettings,
|
||||||
RefreshableTableSettings,
|
RefreshableTableSettings,
|
||||||
SettableColumnsTableSettings,
|
SettableColumnsTableSettings,
|
||||||
} from '@@/datatables/types';
|
} from '@@/datatables/types';
|
||||||
|
@ -15,7 +16,8 @@ export interface TableSettings
|
||||||
extends BasicTableSettings,
|
extends BasicTableSettings,
|
||||||
SettableColumnsTableSettings,
|
SettableColumnsTableSettings,
|
||||||
SettableQuickActionsTableSettings<QuickAction>,
|
SettableQuickActionsTableSettings<QuickAction>,
|
||||||
RefreshableTableSettings {
|
RefreshableTableSettings,
|
||||||
|
FilteredColumnsTableSettings {
|
||||||
truncateContainerName: number;
|
truncateContainerName: number;
|
||||||
setTruncateContainerName: (value: number) => void;
|
setTruncateContainerName: (value: number) => void;
|
||||||
}
|
}
|
||||||
|
|
|
@ -10,6 +10,8 @@ import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
import {
|
import {
|
||||||
BasicTableSettings,
|
BasicTableSettings,
|
||||||
createPersistedStore,
|
createPersistedStore,
|
||||||
|
FilteredColumnsTableSettings,
|
||||||
|
filteredColumnsSettings,
|
||||||
refreshableSettings,
|
refreshableSettings,
|
||||||
RefreshableTableSettings,
|
RefreshableTableSettings,
|
||||||
} from '@@/datatables/types';
|
} from '@@/datatables/types';
|
||||||
|
@ -18,6 +20,8 @@ import { AddButton, Button, ButtonGroup, LoadingButton } from '@@/buttons';
|
||||||
import { Link } from '@@/Link';
|
import { Link } from '@@/Link';
|
||||||
import { ButtonWithRef } from '@@/buttons/Button';
|
import { ButtonWithRef } from '@@/buttons/Button';
|
||||||
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
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';
|
import { ImagesListResponse, useImages } from '../../queries/useImages';
|
||||||
|
|
||||||
|
@ -28,13 +32,15 @@ const tableKey = 'images';
|
||||||
|
|
||||||
export interface TableSettings
|
export interface TableSettings
|
||||||
extends BasicTableSettings,
|
extends BasicTableSettings,
|
||||||
RefreshableTableSettings {}
|
RefreshableTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
const settingsStore = createPersistedStore<TableSettings>(
|
const settingsStore = createPersistedStore<TableSettings>(
|
||||||
tableKey,
|
tableKey,
|
||||||
'tags',
|
'tags',
|
||||||
(set) => ({
|
(set) => ({
|
||||||
...refreshableSettings(set),
|
...refreshableSettings(set),
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -65,6 +71,9 @@ export function ImagesDatatable({
|
||||||
title="Images"
|
title="Images"
|
||||||
titleIcon={List}
|
titleIcon={List}
|
||||||
data-cy="docker-images-datatable"
|
data-cy="docker-images-datatable"
|
||||||
|
extendTableOptions={mergeOptions(
|
||||||
|
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)
|
||||||
|
)}
|
||||||
renderTableActions={(selectedItems) => (
|
renderTableActions={(selectedItems) => (
|
||||||
<div className="flex items-center gap-2">
|
<div className="flex items-center gap-2">
|
||||||
<RemoveButtonMenu selectedItems={selectedItems} onRemove={onRemove} />
|
<RemoveButtonMenu selectedItems={selectedItems} onRemove={onRemove} />
|
||||||
|
|
|
@ -1,15 +1,24 @@
|
||||||
import { List } from 'lucide-react';
|
import { List } from 'lucide-react';
|
||||||
|
|
||||||
import { Datatable } from '@@/datatables';
|
import { Datatable } from '@@/datatables';
|
||||||
import { createPersistedStore } from '@@/datatables/types';
|
import {
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
BasicTableSettings,
|
||||||
|
type FilteredColumnsTableSettings,
|
||||||
|
filteredColumnsSettings,
|
||||||
|
} from '@@/datatables/types';
|
||||||
|
import { useTableStateWithStorage } from '@@/datatables/useTableState';
|
||||||
import { withMeta } from '@@/datatables/extend-options/withMeta';
|
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 { useColumns } from './columns';
|
||||||
import { DecoratedTask } from './types';
|
import { DecoratedTask } from './types';
|
||||||
|
|
||||||
const storageKey = 'docker-service-tasks';
|
const storageKey = 'docker-service-tasks';
|
||||||
const store = createPersistedStore(storageKey);
|
|
||||||
|
interface TableSettings
|
||||||
|
extends BasicTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
export function TasksDatatable({
|
export function TasksDatatable({
|
||||||
dataset,
|
dataset,
|
||||||
|
@ -20,7 +29,13 @@ export function TasksDatatable({
|
||||||
isSlotColumnVisible: boolean;
|
isSlotColumnVisible: boolean;
|
||||||
serviceName: string;
|
serviceName: string;
|
||||||
}) {
|
}) {
|
||||||
const tableState = useTableState(store, storageKey);
|
const tableState = useTableStateWithStorage<TableSettings>(
|
||||||
|
storageKey,
|
||||||
|
undefined,
|
||||||
|
(set) => ({
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
|
})
|
||||||
|
);
|
||||||
const columns = useColumns(isSlotColumnVisible);
|
const columns = useColumns(isSlotColumnVisible);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
@ -31,7 +46,11 @@ export function TasksDatatable({
|
||||||
columns={columns}
|
columns={columns}
|
||||||
dataset={dataset}
|
dataset={dataset}
|
||||||
emptyContentLabel="No task available."
|
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"
|
data-cy="docker-service-tasks-datatable"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -5,22 +5,20 @@ import { useAuthorizations, useIsEdgeAdmin } from '@/react/hooks/useUser';
|
||||||
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
import { isBE } from '@/react/portainer/feature-flags/feature-flags.service';
|
||||||
|
|
||||||
import { Datatable } from '@@/datatables';
|
import { Datatable } from '@@/datatables';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
|
||||||
import { useRepeater } from '@@/datatables/useRepeater';
|
import { useRepeater } from '@@/datatables/useRepeater';
|
||||||
import { defaultGlobalFilterFn } from '@@/datatables/Datatable';
|
import { defaultGlobalFilterFn } from '@@/datatables/Datatable';
|
||||||
import { withGlobalFilter } from '@@/datatables/extend-options/withGlobalFilter';
|
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 { isExternalStack, isOrphanedStack } from '../../view-models/utils';
|
||||||
|
|
||||||
import { TableActions } from './TableActions';
|
import { TableActions } from './TableActions';
|
||||||
import { TableSettingsMenus } from './TableSettingsMenus';
|
import { TableSettingsMenus } from './TableSettingsMenus';
|
||||||
import { createStore } from './store';
|
import { useStore } from './store';
|
||||||
import { useColumns } from './columns';
|
import { useColumns } from './columns';
|
||||||
import { DecoratedStack } from './types';
|
import { DecoratedStack } from './types';
|
||||||
|
|
||||||
const tableKey = 'docker_stacks';
|
|
||||||
const settingsStore = createStore(tableKey);
|
|
||||||
|
|
||||||
export function StacksDatatable({
|
export function StacksDatatable({
|
||||||
onRemove,
|
onRemove,
|
||||||
onReload,
|
onReload,
|
||||||
|
@ -32,7 +30,7 @@ export function StacksDatatable({
|
||||||
isImageNotificationEnabled: boolean;
|
isImageNotificationEnabled: boolean;
|
||||||
dataset: Array<DecoratedStack>;
|
dataset: Array<DecoratedStack>;
|
||||||
}) {
|
}) {
|
||||||
const tableState = useTableState(settingsStore, tableKey);
|
const tableState = useStore();
|
||||||
useRepeater(tableState.autoRefreshRate, onReload);
|
useRepeater(tableState.autoRefreshRate, onReload);
|
||||||
const isAdminQuery = useIsEdgeAdmin();
|
const isAdminQuery = useIsEdgeAdmin();
|
||||||
const { authorized: canManageStacks } = useAuthorizations([
|
const { authorized: canManageStacks } = useAuthorizations([
|
||||||
|
@ -69,7 +67,10 @@ export function StacksDatatable({
|
||||||
tableState.hiddenColumns.map((col) => [col, false])
|
tableState.hiddenColumns.map((col) => [col, false])
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
extendTableOptions={withGlobalFilter(globalFilterFn)}
|
extendTableOptions={mergeOptions(
|
||||||
|
withGlobalFilter(globalFilterFn),
|
||||||
|
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)
|
||||||
|
)}
|
||||||
data-cy="docker-stacks-datatable"
|
data-cy="docker-stacks-datatable"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,24 +1,30 @@
|
||||||
import {
|
import {
|
||||||
BasicTableSettings,
|
type BasicTableSettings,
|
||||||
RefreshableTableSettings,
|
type FilteredColumnsTableSettings,
|
||||||
SettableColumnsTableSettings,
|
type RefreshableTableSettings,
|
||||||
createPersistedStore,
|
type SettableColumnsTableSettings,
|
||||||
hiddenColumnsSettings,
|
hiddenColumnsSettings,
|
||||||
refreshableSettings,
|
refreshableSettings,
|
||||||
|
filteredColumnsSettings,
|
||||||
} from '@@/datatables/types';
|
} from '@@/datatables/types';
|
||||||
|
import { useTableStateWithStorage } from '@@/datatables/useTableState';
|
||||||
|
|
||||||
export interface TableSettings
|
export interface TableSettings
|
||||||
extends BasicTableSettings,
|
extends BasicTableSettings,
|
||||||
SettableColumnsTableSettings,
|
SettableColumnsTableSettings,
|
||||||
RefreshableTableSettings {
|
RefreshableTableSettings,
|
||||||
|
FilteredColumnsTableSettings {
|
||||||
showOrphanedStacks: boolean;
|
showOrphanedStacks: boolean;
|
||||||
setShowOrphanedStacks(value: boolean): void;
|
setShowOrphanedStacks(value: boolean): void;
|
||||||
}
|
}
|
||||||
|
|
||||||
export function createStore(storageKey: string) {
|
const tableKey = 'docker_stacks';
|
||||||
return createPersistedStore<TableSettings>(storageKey, 'name', (set) => ({
|
|
||||||
|
export function useStore() {
|
||||||
|
return useTableStateWithStorage<TableSettings>(tableKey, 'name', (set) => ({
|
||||||
...hiddenColumnsSettings(set),
|
...hiddenColumnsSettings(set),
|
||||||
...refreshableSettings(set),
|
...refreshableSettings(set),
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
showOrphanedStacks: false,
|
showOrphanedStacks: false,
|
||||||
setShowOrphanedStacks(showOrphanedStacks) {
|
setShowOrphanedStacks(showOrphanedStacks) {
|
||||||
set((s) => ({ ...s, showOrphanedStacks }));
|
set((s) => ({ ...s, showOrphanedStacks }));
|
||||||
|
|
|
@ -4,6 +4,8 @@ import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
import { TableSettingsMenuAutoRefresh } from '@@/datatables/TableSettingsMenuAutoRefresh';
|
||||||
import {
|
import {
|
||||||
BasicTableSettings,
|
BasicTableSettings,
|
||||||
|
FilteredColumnsTableSettings,
|
||||||
|
filteredColumnsSettings,
|
||||||
RefreshableTableSettings,
|
RefreshableTableSettings,
|
||||||
createPersistedStore,
|
createPersistedStore,
|
||||||
refreshableSettings,
|
refreshableSettings,
|
||||||
|
@ -11,13 +13,17 @@ import {
|
||||||
import { useRepeater } from '@@/datatables/useRepeater';
|
import { useRepeater } from '@@/datatables/useRepeater';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
import { useTableState } from '@@/datatables/useTableState';
|
||||||
import { withMeta } from '@@/datatables/extend-options/withMeta';
|
import { withMeta } from '@@/datatables/extend-options/withMeta';
|
||||||
|
import { withColumnFilters } from '@@/datatables/extend-options/withColumnFilters';
|
||||||
|
|
||||||
import { DecoratedVolume } from '../types';
|
import { DecoratedVolume } from '../types';
|
||||||
|
|
||||||
import { TableActions } from './TableActions';
|
import { TableActions } from './TableActions';
|
||||||
import { useColumns } from './columns';
|
import { useColumns } from './columns';
|
||||||
|
|
||||||
interface TableSettings extends BasicTableSettings, RefreshableTableSettings {}
|
interface TableSettings
|
||||||
|
extends BasicTableSettings,
|
||||||
|
RefreshableTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
const storageKey = 'docker-volumes';
|
const storageKey = 'docker-volumes';
|
||||||
const store = createPersistedStore<TableSettings>(
|
const store = createPersistedStore<TableSettings>(
|
||||||
|
@ -25,6 +31,7 @@ const store = createPersistedStore<TableSettings>(
|
||||||
undefined,
|
undefined,
|
||||||
(set) => ({
|
(set) => ({
|
||||||
...refreshableSettings(set),
|
...refreshableSettings(set),
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
|
@ -63,10 +70,16 @@ export function VolumesDatatable({
|
||||||
/>
|
/>
|
||||||
</TableSettingsMenu>
|
</TableSettingsMenu>
|
||||||
)}
|
)}
|
||||||
extendTableOptions={withMeta({
|
extendTableOptions={
|
||||||
table: 'volumes',
|
(withMeta({
|
||||||
isBrowseVisible,
|
table: 'volumes',
|
||||||
})}
|
isBrowseVisible,
|
||||||
|
}),
|
||||||
|
withColumnFilters(
|
||||||
|
tableState.columnFilters,
|
||||||
|
tableState.setColumnFilters
|
||||||
|
))
|
||||||
|
}
|
||||||
data-cy="docker-volumes-datatable"
|
data-cy="docker-volumes-datatable"
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,7 +10,7 @@ type AuthorizationOptions = {
|
||||||
|
|
||||||
type RedirectOptions = {
|
type RedirectOptions = {
|
||||||
to: string;
|
to: string;
|
||||||
params: Record<string, unknown>;
|
params?: Record<string, unknown>;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -5,8 +5,11 @@ import { CronJob, Job } from 'kubernetes-types/batch/v1';
|
||||||
|
|
||||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||||
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||||
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
|
import {
|
||||||
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
|
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 { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
|
||||||
import { pluralize } from '@/portainer/helpers/strings';
|
import { pluralize } from '@/portainer/helpers/strings';
|
||||||
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
|
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 { useCronJobs } from '@/react/kubernetes/applications/useCronJobs';
|
||||||
|
|
||||||
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
import { AddButton } from '@@/buttons';
|
||||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
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 {
|
import {
|
||||||
useConfigMapsForCluster,
|
useConfigMapsForCluster,
|
||||||
|
@ -31,11 +39,20 @@ import { getIsConfigMapInUse } from './utils';
|
||||||
import { ConfigMapRowData } from './types';
|
import { ConfigMapRowData } from './types';
|
||||||
import { columns } from './columns';
|
import { columns } from './columns';
|
||||||
|
|
||||||
|
interface TableSettings
|
||||||
|
extends KubeTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
const storageKey = 'k8sConfigMapsDatatable';
|
const storageKey = 'k8sConfigMapsDatatable';
|
||||||
const settingsStore = createStore(storageKey);
|
|
||||||
|
|
||||||
export function ConfigMapsDatatable() {
|
export function ConfigMapsDatatable() {
|
||||||
const tableState = useTableState(settingsStore, storageKey);
|
const tableState = useKubeStore<TableSettings>(
|
||||||
|
storageKey,
|
||||||
|
undefined,
|
||||||
|
(set) => ({
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
|
})
|
||||||
|
);
|
||||||
const { authorized: canWrite } = useAuthorizations(['K8sConfigMapsW']);
|
const { authorized: canWrite } = useAuthorizations(['K8sConfigMapsW']);
|
||||||
const readOnly = !canWrite;
|
const readOnly = !canWrite;
|
||||||
const { authorized: canAccessSystemResources } = useAuthorizations(
|
const { authorized: canAccessSystemResources } = useAuthorizations(
|
||||||
|
@ -109,6 +126,9 @@ export function ConfigMapsDatatable() {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
data-cy="k8s-configmaps-datatable"
|
data-cy="k8s-configmaps-datatable"
|
||||||
|
extendTableOptions={mergeOptions(
|
||||||
|
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,8 +5,11 @@ import { CronJob, Job } from 'kubernetes-types/batch/v1';
|
||||||
|
|
||||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||||
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||||
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
|
import {
|
||||||
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
|
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 { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
|
||||||
import { pluralize } from '@/portainer/helpers/strings';
|
import { pluralize } from '@/portainer/helpers/strings';
|
||||||
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
|
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 { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
import { AddButton } from '@@/buttons';
|
import { AddButton } from '@@/buttons';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
|
||||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
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 {
|
import {
|
||||||
useSecretsForCluster,
|
useSecretsForCluster,
|
||||||
|
@ -32,17 +40,26 @@ import { SecretRowData } from './types';
|
||||||
import { columns } from './columns';
|
import { columns } from './columns';
|
||||||
|
|
||||||
const storageKey = 'k8sSecretsDatatable';
|
const storageKey = 'k8sSecretsDatatable';
|
||||||
const settingsStore = createStore(storageKey);
|
|
||||||
|
interface TableSettings
|
||||||
|
extends KubeTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
export function SecretsDatatable() {
|
export function SecretsDatatable() {
|
||||||
const tableState = useTableState(settingsStore, storageKey);
|
const tableState = useKubeStore<TableSettings>(
|
||||||
|
storageKey,
|
||||||
|
undefined,
|
||||||
|
(set) => ({
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
|
})
|
||||||
|
);
|
||||||
|
const environmentId = useEnvironmentId();
|
||||||
const { authorized: canWrite } = useAuthorizations(['K8sSecretsW']);
|
const { authorized: canWrite } = useAuthorizations(['K8sSecretsW']);
|
||||||
const readOnly = !canWrite;
|
const readOnly = !canWrite;
|
||||||
const { authorized: canAccessSystemResources } = useAuthorizations(
|
const { authorized: canAccessSystemResources } = useAuthorizations(
|
||||||
'K8sAccessSystemNamespaces'
|
'K8sAccessSystemNamespaces'
|
||||||
);
|
);
|
||||||
|
|
||||||
const environmentId = useEnvironmentId();
|
|
||||||
const { data: namespaces, ...namespacesQuery } = useNamespacesQuery(
|
const { data: namespaces, ...namespacesQuery } = useNamespacesQuery(
|
||||||
environmentId,
|
environmentId,
|
||||||
{
|
{
|
||||||
|
@ -109,6 +126,9 @@ export function SecretsDatatable() {
|
||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
data-cy="k8s-secrets-datatable"
|
data-cy="k8s-secrets-datatable"
|
||||||
|
extendTableOptions={mergeOptions(
|
||||||
|
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,14 +4,22 @@ import { useMemo } from 'react';
|
||||||
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||||
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
|
import { useAuthorizations, Authorized } from '@/react/hooks/useUser';
|
||||||
import Route from '@/assets/ico/route.svg?c';
|
import Route from '@/assets/ico/route.svg?c';
|
||||||
import { DefaultDatatableSettings } from '@/react/kubernetes/datatables/DefaultDatatableSettings';
|
import {
|
||||||
import { createStore } from '@/react/kubernetes/datatables/default-kube-datatable-store';
|
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 { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
|
||||||
|
|
||||||
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
import { Datatable, TableSettingsMenu } from '@@/datatables';
|
||||||
import { AddButton } from '@@/buttons';
|
import { AddButton } from '@@/buttons';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
|
||||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
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 { DeleteIngressesRequest, Ingress } from '../types';
|
||||||
import { useDeleteIngresses, useIngresses } from '../queries';
|
import { useDeleteIngresses, useIngresses } from '../queries';
|
||||||
|
@ -29,10 +37,18 @@ interface SelectedIngress {
|
||||||
}
|
}
|
||||||
const storageKey = 'ingressClassesNameSpace';
|
const storageKey = 'ingressClassesNameSpace';
|
||||||
|
|
||||||
const settingsStore = createStore(storageKey);
|
interface TableSettings
|
||||||
|
extends KubeTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
export function IngressDatatable() {
|
export function IngressDatatable() {
|
||||||
const tableState = useTableState(settingsStore, storageKey);
|
const tableState = useKubeStore<TableSettings>(
|
||||||
|
storageKey,
|
||||||
|
undefined,
|
||||||
|
(set) => ({
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
|
})
|
||||||
|
);
|
||||||
const environmentId = useEnvironmentId();
|
const environmentId = useEnvironmentId();
|
||||||
|
|
||||||
const { authorized: canAccessSystemResources } = useAuthorizations(
|
const { authorized: canAccessSystemResources } = useAuthorizations(
|
||||||
|
@ -91,6 +107,9 @@ export function IngressDatatable() {
|
||||||
}
|
}
|
||||||
disableSelect={useCheckboxes()}
|
disableSelect={useCheckboxes()}
|
||||||
data-cy="k8s-ingresses-datatable"
|
data-cy="k8s-ingresses-datatable"
|
||||||
|
extendTableOptions={mergeOptions(
|
||||||
|
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -9,14 +9,23 @@ import { useEnvironmentId } from '@/react/hooks/useEnvironmentId';
|
||||||
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
import { Authorized, useAuthorizations } from '@/react/hooks/useUser';
|
||||||
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
import { notifyError, notifySuccess } from '@/portainer/services/notifications';
|
||||||
import { pluralize } from '@/portainer/helpers/strings';
|
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 { SystemResourceDescription } from '@/react/kubernetes/datatables/SystemResourceDescription';
|
||||||
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
|
import { useNamespacesQuery } from '@/react/kubernetes/namespaces/queries/useNamespacesQuery';
|
||||||
import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
|
import { CreateFromManifestButton } from '@/react/kubernetes/components/CreateFromManifestButton';
|
||||||
|
|
||||||
import { Datatable, Table, TableSettingsMenu } from '@@/datatables';
|
import { Datatable, Table, TableSettingsMenu } from '@@/datatables';
|
||||||
import { useTableState } from '@@/datatables/useTableState';
|
|
||||||
import { DeleteButton } from '@@/buttons/DeleteButton';
|
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 {
|
import {
|
||||||
useMutationDeleteServices,
|
useMutationDeleteServices,
|
||||||
|
@ -25,13 +34,20 @@ import {
|
||||||
import { Service } from '../../types';
|
import { Service } from '../../types';
|
||||||
|
|
||||||
import { columns } from './columns';
|
import { columns } from './columns';
|
||||||
import { createStore } from './datatable-store';
|
|
||||||
|
|
||||||
const storageKey = 'k8sServicesDatatable';
|
const storageKey = 'k8sServicesDatatable';
|
||||||
const settingsStore = createStore(storageKey);
|
interface TableSettings
|
||||||
|
extends KubeTableSettings,
|
||||||
|
FilteredColumnsTableSettings {}
|
||||||
|
|
||||||
export function ServicesDatatable() {
|
export function ServicesDatatable() {
|
||||||
const tableState = useTableState(settingsStore, storageKey);
|
const tableState = useKubeStore<TableSettings>(
|
||||||
|
storageKey,
|
||||||
|
undefined,
|
||||||
|
(set) => ({
|
||||||
|
...filteredColumnsSettings(set),
|
||||||
|
})
|
||||||
|
);
|
||||||
const environmentId = useEnvironmentId();
|
const environmentId = useEnvironmentId();
|
||||||
const { data: namespaces, ...namespacesQuery } =
|
const { data: namespaces, ...namespacesQuery } =
|
||||||
useNamespacesQuery(environmentId);
|
useNamespacesQuery(environmentId);
|
||||||
|
@ -91,6 +107,9 @@ export function ServicesDatatable() {
|
||||||
}
|
}
|
||||||
renderRow={servicesRenderRow}
|
renderRow={servicesRenderRow}
|
||||||
data-cy="k8s-services-datatable"
|
data-cy="k8s-services-datatable"
|
||||||
|
extendTableOptions={mergeOptions(
|
||||||
|
withColumnFilters(tableState.columnFilters, tableState.setColumnFilters)
|
||||||
|
)}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue