Cluster search fixes, infinite data explorer mode
[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 { DataExplorerAction, dataExplorerActions } from "./data-explorer-action";
12 import { DataColumns, DataTableFetchMode } from "~/components/data-table/data-table";
13 import { DataTableFilters } from "~/components/data-table-filters/data-table-filters-tree";
14
15 export interface DataExplorer {
16     fetchMode: DataTableFetchMode;
17     columns: DataColumns<any>;
18     items: any[];
19     itemsAvailable: number;
20     page: number;
21     rowsPerPage: number;
22     rowsPerPageOptions: number[];
23     searchValue: string;
24     working?: boolean;
25 }
26
27 export const initialDataExplorer: DataExplorer = {
28     fetchMode: DataTableFetchMode.PAGINATED,
29     columns: [],
30     items: [],
31     itemsAvailable: 0,
32     page: 0,
33     rowsPerPage: 10,
34     rowsPerPageOptions: [5, 10, 25, 50],
35     searchValue: ""
36 };
37
38 export type DataExplorerState = Record<string, DataExplorer>;
39
40 export const dataExplorerReducer = (state: DataExplorerState = {}, action: DataExplorerAction) =>
41     dataExplorerActions.match(action, {
42         CLEAR: ({ id }) =>
43             update(state, id, explorer => ({ ...explorer, page: 0, itemsAvailable: 0, items: [] })),
44
45         RESET_PAGINATION: ({ id }) =>
46             update(state, id, explorer => ({ ...explorer, page: 0 })),
47
48         SET_FETCH_MODE: ({ id, fetchMode }) =>
49             update(state, id, explorer => ({ ...explorer, fetchMode })),
50
51         SET_COLUMNS: ({ id, columns }) =>
52             update(state, id, setColumns(columns)),
53
54         SET_FILTERS: ({ id, columnName, filters }) =>
55             update(state, id, mapColumns(setFilters(columnName, filters))),
56
57         SET_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
58             update(state, id, explorer => ({ ...explorer, items, itemsAvailable, page, rowsPerPage })),
59
60         APPEND_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
61             update(state, id, explorer => ({
62                 ...explorer,
63                 items: state[id].items.concat(items),
64                 itemsAvailable: state[id].itemsAvailable + itemsAvailable,
65                 page,
66                 rowsPerPage
67             })),
68
69         SET_PAGE: ({ id, page }) =>
70             update(state, id, explorer => ({ ...explorer, page })),
71
72         SET_ROWS_PER_PAGE: ({ id, rowsPerPage }) =>
73             update(state, id, explorer => ({ ...explorer, rowsPerPage })),
74
75         SET_EXPLORER_SEARCH_VALUE: ({ id, searchValue }) =>
76             update(state, id, explorer => ({ ...explorer, searchValue })),
77
78         TOGGLE_SORT: ({ id, columnName }) =>
79             update(state, id, mapColumns(toggleSort(columnName))),
80
81         TOGGLE_COLUMN: ({ id, columnName }) =>
82             update(state, id, mapColumns(toggleColumn(columnName))),
83
84         default: () => state
85     });
86
87 export const getDataExplorer = (state: DataExplorerState, id: string) =>
88     state[id] || initialDataExplorer;
89
90 export const getSortColumn = (dataExplorer: DataExplorer) => dataExplorer.columns.find((c: any) =>
91     !!c.sortDirection && c.sortDirection !== SortDirection.NONE);
92
93 const update = (state: DataExplorerState, id: string, updateFn: (dataExplorer: DataExplorer) => DataExplorer) =>
94     ({ ...state, [id]: updateFn(getDataExplorer(state, id)) });
95
96 const canUpdateColumns = (prevColumns: DataColumns<any>, nextColumns: DataColumns<any>) => {
97     if (prevColumns.length !== nextColumns.length) {
98         return true;
99     }
100     for (let i = 0; i < nextColumns.length; i++) {
101         const pc = prevColumns[i];
102         const nc = nextColumns[i];
103         if (pc.key !== nc.key || pc.name !== nc.name) {
104             return true;
105         }
106     }
107     return false;
108 };
109
110 const setColumns = (columns: DataColumns<any>) =>
111     (dataExplorer: DataExplorer) =>
112         ({ ...dataExplorer, columns: canUpdateColumns(dataExplorer.columns, columns) ? columns : dataExplorer.columns });
113
114 const mapColumns = (mapFn: (column: DataColumn<any>) => DataColumn<any>) =>
115     (dataExplorer: DataExplorer) =>
116         ({ ...dataExplorer, columns: dataExplorer.columns.map(mapFn) });
117
118 const toggleSort = (columnName: string) =>
119     (column: DataColumn<any>) => column.name === columnName
120         ? toggleSortDirection(column)
121         : resetSortDirection(column);
122
123 const toggleColumn = (columnName: string) =>
124     (column: DataColumn<any>) => column.name === columnName
125         ? { ...column, selected: !column.selected }
126         : column;
127
128 const setFilters = (columnName: string, filters: DataTableFilters) =>
129     (column: DataColumn<any>) => column.name === columnName
130         ? { ...column, filters }
131         : column;