Merge branch 'main' into 19462-colorscheme refs #19462
[arvados-workbench2.git] / src / store / data-explorer / data-explorer-reducer.ts
1 // Copyright (C) The Arvados Authors. All rights reserved.
2 //
3 // SPDX-License-Identifier: AGPL-3.0
4
5 import {
6   DataColumn,
7   resetSortDirection,
8   SortDirection,
9   toggleSortDirection,
10 } from 'components/data-table/data-column';
11 import {
12   DataExplorerAction,
13   dataExplorerActions,
14   DataTableRequestState,
15 } from './data-explorer-action';
16 import {
17   DataColumns,
18   DataTableFetchMode,
19 } from 'components/data-table/data-table';
20 import { DataTableFilters } from 'components/data-table-filters/data-table-filters-tree';
21
22 export interface DataExplorer {
23   fetchMode: DataTableFetchMode;
24   columns: DataColumns<any>;
25   items: any[];
26   itemsAvailable: number;
27   page: number;
28   rowsPerPage: number;
29   rowsPerPageOptions: number[];
30   searchValue: string;
31   working?: boolean;
32   requestState: DataTableRequestState;
33 }
34
35 export const initialDataExplorer: DataExplorer = {
36   fetchMode: DataTableFetchMode.PAGINATED,
37   columns: [],
38   items: [],
39   itemsAvailable: 0,
40   page: 0,
41   rowsPerPage: 50,
42   rowsPerPageOptions: [10, 20, 50, 100, 200, 500],
43   searchValue: '',
44   requestState: DataTableRequestState.IDLE,
45 };
46
47 export type DataExplorerState = Record<string, DataExplorer>;
48
49 export const dataExplorerReducer = (
50   state: DataExplorerState = {},
51   action: DataExplorerAction
52 ) => {
53   return dataExplorerActions.match(action, {
54     CLEAR: ({ id }) =>
55       update(state, id, (explorer) => ({
56         ...explorer,
57         page: 0,
58         itemsAvailable: 0,
59         items: [],
60       })),
61
62     RESET_PAGINATION: ({ id }) =>
63       update(state, id, (explorer) => ({ ...explorer, page: 0 })),
64
65     SET_FETCH_MODE: ({ id, fetchMode }) =>
66       update(state, id, (explorer) => ({ ...explorer, fetchMode })),
67
68     SET_COLUMNS: ({ id, columns }) => update(state, id, setColumns(columns)),
69
70     SET_FILTERS: ({ id, columnName, filters }) =>
71       update(state, id, mapColumns(setFilters(columnName, filters))),
72
73     SET_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
74       update(state, id, (explorer) => ({
75         ...explorer,
76         items,
77         itemsAvailable,
78         page: page || 0,
79         rowsPerPage,
80       })),
81
82     APPEND_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
83       update(state, id, (explorer) => ({
84         ...explorer,
85         items: state[id].items.concat(items),
86         itemsAvailable: state[id].itemsAvailable + itemsAvailable,
87         page,
88         rowsPerPage,
89       })),
90
91     SET_PAGE: ({ id, page }) =>
92       update(state, id, (explorer) => ({ ...explorer, page })),
93
94     SET_ROWS_PER_PAGE: ({ id, rowsPerPage }) =>
95       update(state, id, (explorer) => ({ ...explorer, rowsPerPage })),
96
97     SET_EXPLORER_SEARCH_VALUE: ({ id, searchValue }) =>
98       update(state, id, (explorer) => ({ ...explorer, searchValue })),
99
100     SET_REQUEST_STATE: ({ id, requestState }) =>
101       update(state, id, (explorer) => ({ ...explorer, requestState })),
102
103     TOGGLE_SORT: ({ id, columnName }) =>
104       update(state, id, mapColumns(toggleSort(columnName))),
105
106     TOGGLE_COLUMN: ({ id, columnName }) =>
107       update(state, id, mapColumns(toggleColumn(columnName))),
108
109     default: () => state,
110   });
111 };
112 export const getDataExplorer = (state: DataExplorerState, id: string) => {
113   const returnValue = state[id] || initialDataExplorer;
114   return returnValue;
115 };
116
117 export const getSortColumn = (dataExplorer: DataExplorer) =>
118   dataExplorer.columns.find(
119     (c: any) => !!c.sortDirection && c.sortDirection !== SortDirection.NONE
120   );
121
122 const update = (
123   state: DataExplorerState,
124   id: string,
125   updateFn: (dataExplorer: DataExplorer) => DataExplorer
126 ) => ({ ...state, [id]: updateFn(getDataExplorer(state, id)) });
127
128 const canUpdateColumns = (
129   prevColumns: DataColumns<any>,
130   nextColumns: DataColumns<any>
131 ) => {
132   if (prevColumns.length !== nextColumns.length) {
133     return true;
134   }
135   for (let i = 0; i < nextColumns.length; i++) {
136     const pc = prevColumns[i];
137     const nc = nextColumns[i];
138     if (pc.key !== nc.key || pc.name !== nc.name) {
139       return true;
140     }
141   }
142   return false;
143 };
144
145 const setColumns =
146   (columns: DataColumns<any>) => (dataExplorer: DataExplorer) => ({
147     ...dataExplorer,
148     columns: canUpdateColumns(dataExplorer.columns, columns)
149       ? columns
150       : dataExplorer.columns,
151   });
152
153 const mapColumns =
154   (mapFn: (column: DataColumn<any>) => DataColumn<any>) =>
155   (dataExplorer: DataExplorer) => ({
156     ...dataExplorer,
157     columns: dataExplorer.columns.map(mapFn),
158   });
159
160 const toggleSort = (columnName: string) => (column: DataColumn<any>) =>
161   column.name === columnName
162     ? toggleSortDirection(column)
163     : resetSortDirection(column);
164
165 const toggleColumn = (columnName: string) => (column: DataColumn<any>) =>
166   column.name === columnName
167     ? { ...column, selected: !column.selected }
168     : column;
169
170 const setFilters =
171   (columnName: string, filters: DataTableFilters) =>
172   (column: DataColumn<any>) =>
173     column.name === columnName ? { ...column, filters } : column;