21700: Install Bundler system-wide in Rails postinst
[arvados.git] / services / workbench2 / 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, 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     isNotFound: boolean;
34 }
35
36 export const initialDataExplorer: DataExplorer = {
37     fetchMode: DataTableFetchMode.PAGINATED,
38     columns: [],
39     items: [],
40     itemsAvailable: 0,
41     page: 0,
42     rowsPerPage: 50,
43     rowsPerPageOptions: [10, 20, 50, 100, 200, 500],
44     searchValue: '',
45     requestState: DataTableRequestState.IDLE,
46     isNotFound: false,
47 };
48
49 export type DataExplorerState = Record<string, DataExplorer>;
50
51 export const dataExplorerReducer = (
52     state: DataExplorerState = {},
53     action: DataExplorerAction
54 ) => {
55     return dataExplorerActions.match(action, {
56         CLEAR: ({ id }) =>
57             update(state, id, (explorer) => ({
58                 ...explorer,
59                 page: 0,
60                 itemsAvailable: 0,
61                 items: [],
62             })),
63
64         RESET_PAGINATION: ({ id }) =>
65             update(state, id, (explorer) => ({ ...explorer, page: 0 })),
66
67         SET_FETCH_MODE: ({ id, fetchMode }) =>
68             update(state, id, (explorer) => ({ ...explorer, fetchMode })),
69
70         SET_COLUMNS: ({ id, columns }) => update(state, id, setColumns(columns)),
71
72         SET_FILTERS: ({ id, columnName, filters }) =>
73             update(state, id, mapColumns(setFilters(columnName, filters))),
74
75         SET_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) => (
76             update(state, id, (explorer) => {
77                 // Reject updates to pages other than current,
78                 //  DataExplorer middleware should retry
79                 const updatedPage = page || 0;
80                 if (explorer.page === updatedPage) {
81                     return {
82                         ...explorer,
83                         items,
84                         itemsAvailable,
85                         page: updatedPage,
86                         rowsPerPage,
87                     }
88                 } else {
89                     return explorer;
90                 }
91             })
92         ),
93
94         APPEND_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
95             update(state, id, (explorer) => ({
96                 ...explorer,
97                 items: state[id].items.concat(items),
98                 itemsAvailable: state[id].itemsAvailable + itemsAvailable,
99                 page,
100                 rowsPerPage,
101             })),
102
103         SET_PAGE: ({ id, page }) =>
104             update(state, id, (explorer) => ({ ...explorer, page })),
105
106         SET_ROWS_PER_PAGE: ({ id, rowsPerPage }) =>
107             update(state, id, (explorer) => ({ ...explorer, rowsPerPage })),
108
109         SET_EXPLORER_SEARCH_VALUE: ({ id, searchValue }) =>
110             update(state, id, (explorer) => ({ ...explorer, searchValue })),
111
112         RESET_EXPLORER_SEARCH_VALUE: ({ id }) =>
113             update(state, id, (explorer) => ({ ...explorer, searchValue: '' })),
114
115         SET_REQUEST_STATE: ({ id, requestState }) =>
116             update(state, id, (explorer) => ({ ...explorer, requestState })),
117
118         TOGGLE_SORT: ({ id, columnName }) =>
119             update(state, id, mapColumns(toggleSort(columnName))),
120
121         TOGGLE_COLUMN: ({ id, columnName }) =>
122             update(state, id, mapColumns(toggleColumn(columnName))),
123
124         SET_IS_NOT_FOUND: ({ id, isNotFound }) =>
125             update(state, id, (explorer) => ({ ...explorer, isNotFound })),
126
127         default: () => state,
128     });
129 };
130 export const getDataExplorer = (state: DataExplorerState, id: string) => {
131     const returnValue = state[id] || initialDataExplorer;
132     return returnValue;
133 };
134
135 export const getSortColumn = <R>(dataExplorer: DataExplorer): DataColumn<any, R> | undefined =>
136     dataExplorer.columns.find(
137         (c: DataColumn<any, R>) => !!c.sort && c.sort.direction !== SortDirection.NONE
138     );
139
140 const update = (
141     state: DataExplorerState,
142     id: string,
143     updateFn: (dataExplorer: DataExplorer) => DataExplorer
144 ) => ({ ...state, [id]: updateFn(getDataExplorer(state, id)) });
145
146 const canUpdateColumns = (
147     prevColumns: DataColumns<any, any>,
148     nextColumns: DataColumns<any, any>
149 ) => {
150     if (prevColumns.length !== nextColumns.length) {
151         return true;
152     }
153     for (let i = 0; i < nextColumns.length; i++) {
154         const pc = prevColumns[i];
155         const nc = nextColumns[i];
156         if (pc.key !== nc.key || pc.name !== nc.name) {
157             return true;
158         }
159     }
160     return false;
161 };
162
163 const setColumns =
164     (columns: DataColumns<any, any>) => (dataExplorer: DataExplorer) => ({
165         ...dataExplorer,
166         columns: canUpdateColumns(dataExplorer.columns, columns)
167             ? columns
168             : dataExplorer.columns,
169     });
170
171 const mapColumns =
172     (mapFn: (column: DataColumn<any, any>) => DataColumn<any, any>) =>
173         (dataExplorer: DataExplorer) => ({
174             ...dataExplorer,
175             columns: dataExplorer.columns.map(mapFn),
176         });
177
178 const toggleSort = (columnName: string) => (column: DataColumn<any, any>) =>
179     column.name === columnName
180         ? toggleSortDirection(column)
181         : resetSortDirection(column);
182
183 const toggleColumn = (columnName: string) => (column: DataColumn<any, any>) =>
184     column.name === columnName
185         ? { ...column, selected: !column.selected }
186         : column;
187
188 const setFilters =
189     (columnName: string, filters: DataTableFilters) =>
190         (column: DataColumn<any, any>) =>
191             column.name === columnName ? { ...column, filters } : column;