refs #14348 Merge branch 'origin/14348-cluster-search'
authorDaniel Kos <daniel.kos@contractors.roche.com>
Wed, 2 Jan 2019 08:15:10 +0000 (09:15 +0100)
committerDaniel Kos <daniel.kos@contractors.roche.com>
Wed, 2 Jan 2019 08:15:55 +0000 (09:15 +0100)
Arvados-DCO-1.1-Signed-off-by: Daniel Kos <daniel.kos@contractors.roche.com>

25 files changed:
src/components/data-explorer/data-explorer.tsx
src/components/data-table/data-table.tsx
src/models/search-bar.ts
src/services/common-service/common-resource-service.ts
src/services/common-service/common-service.ts
src/services/groups-service/groups-service.ts
src/store/auth/auth-action-session.ts
src/store/data-explorer/data-explorer-action.ts
src/store/data-explorer/data-explorer-middleware-service.ts
src/store/data-explorer/data-explorer-middleware.ts
src/store/data-explorer/data-explorer-reducer.ts
src/store/resource-type-filters/resource-type-filters.ts
src/store/search-bar/search-bar-actions.test.ts
src/store/search-bar/search-bar-actions.ts
src/store/search-results-panel/search-results-middleware-service.ts
src/store/store.ts
src/store/trash-panel/trash-panel-middleware-service.ts
src/store/workbench/workbench-actions.ts
src/views-components/data-explorer/data-explorer.tsx
src/views-components/data-explorer/renderers.tsx
src/views-components/form-fields/search-bar-form-fields.tsx
src/views/compute-node-panel/compute-node-panel-root.tsx
src/views/search-results-panel/search-results-panel-view.tsx
src/views/trash-panel/trash-panel.tsx
src/views/workflow-panel/workflow-panel-view.tsx

index b2377888ea432b90bb4e64d431286a0b69f45516..878f47ff769b026741c7203b78c77bdb9317e156 100644 (file)
@@ -5,7 +5,7 @@
 import * as React from 'react';
 import { Grid, Paper, Toolbar, StyleRulesCallback, withStyles, WithStyles, TablePagination, IconButton, Tooltip, Button } from '@material-ui/core';
 import { ColumnSelector } from "~/components/column-selector/column-selector";
-import { DataTable, DataColumns } from "~/components/data-table/data-table";
+import { DataTable, DataColumns, DataTableFetchMode } from "~/components/data-table/data-table";
 import { DataColumn, SortDirection } from "~/components/data-table/data-column";
 import { SearchInput } from '~/components/search-input/search-input';
 import { ArvadosTheme } from "~/common/custom-theme";
@@ -35,6 +35,7 @@ const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
 });
 
 interface DataExplorerDataProps<T> {
+    fetchMode: DataTableFetchMode;
     items: T[];
     itemsAvailable: number;
     columns: DataColumns<T>;
@@ -63,6 +64,7 @@ interface DataExplorerActionProps<T> {
     onFiltersChange: (filters: DataTableFilters, column: DataColumn<T>) => void;
     onChangePage: (page: number) => void;
     onChangeRowsPerPage: (rowsPerPage: number) => void;
+    onLoadMore: (page: number) => void;
     extractKey?: (item: T) => React.Key;
 }
 
@@ -81,7 +83,7 @@ export const DataExplorer = withStyles(styles)(
                 rowsPerPage, rowsPerPageOptions, onColumnToggle, searchValue, onSearch,
                 items, itemsAvailable, onRowClick, onRowDoubleClick, classes,
                 dataTableDefaultView, hideColumnSelector, actions, paperProps, hideSearchInput,
-                paperKey
+                paperKey, fetchMode
             } = this.props;
             return <Paper className={classes.root} {...paperProps} key={paperKey}>
                 {(!hideColumnSelector || !hideSearchInput) && <Toolbar className={classes.toolbar}>
@@ -110,14 +112,18 @@ export const DataExplorer = withStyles(styles)(
                     defaultView={dataTableDefaultView} />
                 <Toolbar className={classes.footer}>
                     <Grid container justify="flex-end">
-                        <TablePagination
+                        {fetchMode === DataTableFetchMode.PAGINATED ? <TablePagination
                             count={itemsAvailable}
                             rowsPerPage={rowsPerPage}
                             rowsPerPageOptions={rowsPerPageOptions}
                             page={this.props.page}
                             onChangePage={this.changePage}
                             onChangeRowsPerPage={this.changeRowsPerPage}
-                            component="div" />
+                            component="div" /> : <Button
+                                variant="text"
+                                size="medium"
+                                onClick={this.loadMore}
+                                >Load more</Button>}
                     </Grid>
                 </Toolbar>
             </Paper>;
@@ -131,6 +137,10 @@ export const DataExplorer = withStyles(styles)(
             this.props.onChangeRowsPerPage(parseInt(event.target.value, 10));
         }
 
+        loadMore = () => {
+            this.props.onLoadMore(this.props.page + 1);
+        }
+
         renderContextMenuTrigger = (item: T) =>
             <Grid container justify="center">
                 <Tooltip title="More options" disableFocusListener>
index d9157a6a5d0e14851f141309c8a89f96af818c50..8298861b2473a34ab13a0b3c85099b9e1af4b4e4 100644 (file)
@@ -12,6 +12,11 @@ import { countNodes } from '~/models/tree';
 
 export type DataColumns<T> = Array<DataColumn<T>>;
 
+export enum DataTableFetchMode {
+    PAGINATED,
+    INFINITE
+}
+
 export interface DataTableDataProps<T> {
     items: T[];
     columns: DataColumns<T>;
index 798f9c8f27bd84d1aeb6fbb06aaa244771ff840b..effaeed4c0e676882e6bccc9f445e9eb732d9483 100644 (file)
@@ -6,7 +6,7 @@ import { ResourceKind } from '~/models/resource';
 
 export type SearchBarAdvanceFormData = {
     type?: ResourceKind;
-    cluster?: ClusterObjectType;
+    cluster?: string;
     projectUuid?: string;
     inTrash: boolean;
     dateFrom: string;
@@ -21,9 +21,3 @@ export interface PropertyValue {
     key: string;
     value: string;
 }
-
-export enum ClusterObjectType {
-    INDIANAPOLIS = "indianapolis",
-    KAISERAUGST = "kaiseraugst",
-    PENZBERG = "penzberg"
-}
index 471c32fa21020e8b9eb1db9ae248ad3c6aa30f8f..17c287d22ebaf6fad2d60b774d0fc400f29af5b7 100644 (file)
@@ -2,7 +2,6 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import * as _ from "lodash";
 import { AxiosInstance } from "axios";
 import { Resource } from "src/models/resource";
 import { ApiActions } from "~/services/api/api-actions";
@@ -18,11 +17,9 @@ export enum CommonResourceServiceError {
 }
 
 export class CommonResourceService<T extends Resource> extends CommonService<T> {
-
-    constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
+   constructor(serverApi: AxiosInstance, resourceType: string, actions: ApiActions) {
         super(serverApi, resourceType, actions);
     }
-    
 }
 
 export const getCommonResourceServiceError = (errorResponse: any) => {
index b301a727551135ef4d6bff23f11d90446b13b57b..07ff398a4abdd7d4409bb67dd660e02f304af359 100644 (file)
@@ -23,6 +23,7 @@ export interface ListArguments {
 }
 
 export interface ListResults<T> {
+    clusterId?: string;
     kind: string;
     offset: number;
     limit: number;
@@ -128,4 +129,4 @@ export class CommonService<T> {
             this.actions
         );
     }
-}
\ No newline at end of file
+}
index d8b33f601f94c553e79e8be078f0dc2c9c4ee62f..a676557a78afd4e44c3b5c1ae771077b63541f49 100644 (file)
@@ -5,7 +5,7 @@
 import * as _ from "lodash";
 import { CommonResourceService } from '~/services/common-service/common-resource-service';
 import { ListResults, ListArguments } from '~/services/common-service/common-service';
-import { AxiosInstance } from "axios";
+import { AxiosInstance, AxiosRequestConfig } from "axios";
 import { CollectionResource } from "~/models/collection";
 import { ProjectResource } from "~/models/project";
 import { ProcessResource } from "~/models/process";
@@ -13,6 +13,7 @@ import { ResourceKind } from '~/models/resource';
 import { TrashableResourceService } from "~/services/common-service/trashable-resource-service";
 import { ApiActions } from "~/services/api/api-actions";
 import { GroupResource } from "~/models/group";
+import { Session } from "~/models/session";
 
 export interface ContentsArguments {
     limit?: number;
@@ -39,7 +40,7 @@ export class GroupsService<T extends GroupResource = GroupResource> extends Tras
         super(serverApi, "groups", actions);
     }
 
-    async contents(uuid: string, args: ContentsArguments = {}): Promise<ListResults<GroupContentsResource>> {
+    async contents(uuid: string, args: ContentsArguments = {}, session?: Session): Promise<ListResults<GroupContentsResource>> {
         const { filters, order, ...other } = args;
         const params = {
             ...other,
@@ -48,17 +49,18 @@ export class GroupsService<T extends GroupResource = GroupResource> extends Tras
         };
 
         const pathUrl = uuid ? `${uuid}/contents` : 'contents';
+
+        const cfg: AxiosRequestConfig = { params: CommonResourceService.mapKeys(_.snakeCase)(params) };
+        if (session) {
+            cfg.baseURL = session.baseUrl;
+        }
+
         const response = await CommonResourceService.defaultResponse(
-                this.serverApi
-                    .get(this.resourceType + pathUrl, {
-                        params: CommonResourceService.mapKeys(_.snakeCase)(params)
-                    }),
-                this.actions, 
-                false
-            );
+            this.serverApi.get(this.resourceType + pathUrl, cfg), this.actions, false
+        );
 
         const { items, ...res } = response;
-        const mappedItems = items.map((item: GroupContentsResource) => {
+        const mappedItems = (items || []).map((item: GroupContentsResource) => {
             const mappedItem = TrashableResourceService.mapKeys(_.camelCase)(item);
             if (item.kind === ResourceKind.COLLECTION || item.kind === ResourceKind.PROJECT) {
                 const { properties } = item;
@@ -68,7 +70,7 @@ export class GroupsService<T extends GroupResource = GroupResource> extends Tras
             }
         });
         const mappedResponse = { ...TrashableResourceService.mapKeys(_.camelCase)(res) };
-        return { ...mappedResponse, items: mappedItems };
+        return { ...mappedResponse, items: mappedItems, clusterId: session && session.clusterId };
     }
 
     shared(params: SharedArguments = {}): Promise<ListResults<GroupContentsResource>> {
index d36d5a335c3b5d8f45f5eb7c1e6bfb14df4391d6..b32e205016a1824a8c6ed3554637ffac68b2b726 100644 (file)
@@ -56,7 +56,7 @@ const getTokenUuid = async (baseUrl: string, token: string): Promise<string> =>
         return Promise.resolve(uuid);
     }
 
-    const resp = await Axios.get(`${baseUrl}/api_client_authorizations`, {
+    const resp = await Axios.get(`${baseUrl}api_client_authorizations`, {
         headers: {
             Authorization: `OAuth2 ${token}`
         },
@@ -99,7 +99,7 @@ const clusterLogin = async (clusterId: string, baseUrl: string, activeSession: S
     };
 };
 
-const getActiveSession = (sessions: Session[]): Session | undefined => sessions.find(s => s.active);
+export const getActiveSession = (sessions: Session[]): Session | undefined => sessions.find(s => s.active);
 
 export const validateCluster = async (remoteHost: string, clusterId: string, activeSession: Session): Promise<{ user: User; token: string, baseUrl: string }> => {
     const baseUrl = await getRemoteHostBaseUrl(remoteHost);
index 7797ae6cefc27759d3f994b0d7f14f9225b5f193..546ec8f3679512e24f00c1563cf4ad67d6f9a5fc 100644 (file)
@@ -3,15 +3,18 @@
 // SPDX-License-Identifier: AGPL-3.0
 
 import { unionize, ofType, UnionOf } from "~/common/unionize";
-import { DataColumns } from "~/components/data-table/data-table";
+import { DataColumns, DataTableFetchMode } from "~/components/data-table/data-table";
 import { DataTableFilters } from '~/components/data-table-filters/data-table-filters-tree';
 
 export const dataExplorerActions = unionize({
+    CLEAR: ofType<{ id: string }>(),
     RESET_PAGINATION: ofType<{ id: string }>(),
-    REQUEST_ITEMS: ofType<{ id: string }>(),
+    REQUEST_ITEMS: ofType<{ id: string, criteriaChanged?: boolean }>(),
+    SET_FETCH_MODE: ofType<({ id: string, fetchMode: DataTableFetchMode })>(),
     SET_COLUMNS: ofType<{ id: string, columns: DataColumns<any> }>(),
     SET_FILTERS: ofType<{ id: string, columnName: string, filters: DataTableFilters }>(),
     SET_ITEMS: ofType<{ id: string, items: any[], page: number, rowsPerPage: number, itemsAvailable: number }>(),
+    APPEND_ITEMS: ofType<{ id: string, items: any[], page: number, rowsPerPage: number, itemsAvailable: number }>(),
     SET_PAGE: ofType<{ id: string, page: number }>(),
     SET_ROWS_PER_PAGE: ofType<{ id: string, rowsPerPage: number }>(),
     TOGGLE_COLUMN: ofType<{ id: string, columnName: string }>(),
@@ -22,16 +25,22 @@ export const dataExplorerActions = unionize({
 export type DataExplorerAction = UnionOf<typeof dataExplorerActions>;
 
 export const bindDataExplorerActions = (id: string) => ({
+    CLEAR: () =>
+        dataExplorerActions.CLEAR({ id }),
     RESET_PAGINATION: () =>
         dataExplorerActions.RESET_PAGINATION({ id }),
-    REQUEST_ITEMS: () =>
-        dataExplorerActions.REQUEST_ITEMS({ id }),
+    REQUEST_ITEMS: (criteriaChanged?: boolean) =>
+        dataExplorerActions.REQUEST_ITEMS({ id, criteriaChanged }),
+    SET_FETCH_MODE: (payload: { fetchMode: DataTableFetchMode }) =>
+        dataExplorerActions.SET_FETCH_MODE({ ...payload, id }),
     SET_COLUMNS: (payload: { columns: DataColumns<any> }) =>
         dataExplorerActions.SET_COLUMNS({ ...payload, id }),
     SET_FILTERS: (payload: { columnName: string, filters: DataTableFilters }) =>
         dataExplorerActions.SET_FILTERS({ ...payload, id }),
     SET_ITEMS: (payload: { items: any[], page: number, rowsPerPage: number, itemsAvailable: number }) =>
         dataExplorerActions.SET_ITEMS({ ...payload, id }),
+    APPEND_ITEMS: (payload: { items: any[], page: number, rowsPerPage: number, itemsAvailable: number }) =>
+        dataExplorerActions.APPEND_ITEMS({ ...payload, id }),
     SET_PAGE: (payload: { page: number }) =>
         dataExplorerActions.SET_PAGE({ ...payload, id }),
     SET_ROWS_PER_PAGE: (payload: { rowsPerPage: number }) =>
index 82ba5b4b8e517e9ad6541f228e53213e9ab3f81b..57fd0b59e34f759f7a2b0573613a78f9b7847a20 100644 (file)
@@ -25,7 +25,7 @@ export abstract class DataExplorerMiddlewareService {
         return getDataExplorerColumnFilters(columns, columnName);
     }
 
-    abstract requestItems(api: MiddlewareAPI<Dispatch, RootState>): void;
+    abstract requestItems(api: MiddlewareAPI<Dispatch, RootState>, criteriaChanged?: boolean): void;
 }
 
 export const getDataExplorerColumnFilters = <T>(columns: DataColumns<T>, columnName: string): DataTableFilters => {
index f90f9a6ce39d1ceddff638959544d1ec24c1d06c..e377f3410fef8ba215747fcd5dbb41f16931e10f 100644 (file)
@@ -20,24 +20,24 @@ export const dataExplorerMiddleware = (service: DataExplorerMiddlewareService):
             };
         dataExplorerActions.match(action, {
             SET_PAGE: handleAction(() => {
-                api.dispatch(actions.REQUEST_ITEMS());
+                api.dispatch(actions.REQUEST_ITEMS(false));
             }),
             SET_ROWS_PER_PAGE: handleAction(() => {
-                api.dispatch(actions.REQUEST_ITEMS());
+                api.dispatch(actions.REQUEST_ITEMS(true));
             }),
             SET_FILTERS: handleAction(() => {
                 api.dispatch(actions.RESET_PAGINATION());
-                api.dispatch(actions.REQUEST_ITEMS());
+                api.dispatch(actions.REQUEST_ITEMS(true));
             }),
             TOGGLE_SORT: handleAction(() => {
-                api.dispatch(actions.REQUEST_ITEMS());
+                api.dispatch(actions.REQUEST_ITEMS(true));
             }),
             SET_EXPLORER_SEARCH_VALUE: handleAction(() => {
                 api.dispatch(actions.RESET_PAGINATION());
-                api.dispatch(actions.REQUEST_ITEMS());
+                api.dispatch(actions.REQUEST_ITEMS(true));
             }),
-            REQUEST_ITEMS: handleAction(() => {
-                service.requestItems(api);
+            REQUEST_ITEMS: handleAction(({ criteriaChanged }) => {
+                service.requestItems(api, criteriaChanged);
             }),
             default: () => next(action)
         });
index 613bf278edd81d67ad73cc84402ab991aa8353a7..0fa329054435609745630311ffc95201716e229b 100644 (file)
@@ -2,12 +2,18 @@
 //
 // SPDX-License-Identifier: AGPL-3.0
 
-import { DataColumn, toggleSortDirection, resetSortDirection, SortDirection } from "~/components/data-table/data-column";
-import { dataExplorerActions, DataExplorerAction } from "./data-explorer-action";
-import { DataColumns } from "~/components/data-table/data-table";
+import {
+    DataColumn,
+    resetSortDirection,
+    SortDirection,
+    toggleSortDirection
+} from "~/components/data-table/data-column";
+import { DataExplorerAction, dataExplorerActions } from "./data-explorer-action";
+import { DataColumns, DataTableFetchMode } from "~/components/data-table/data-table";
 import { DataTableFilters } from "~/components/data-table-filters/data-table-filters-tree";
 
 export interface DataExplorer {
+    fetchMode: DataTableFetchMode;
     columns: DataColumns<any>;
     items: any[];
     itemsAvailable: number;
@@ -19,6 +25,7 @@ export interface DataExplorer {
 }
 
 export const initialDataExplorer: DataExplorer = {
+    fetchMode: DataTableFetchMode.PAGINATED,
     columns: [],
     items: [],
     itemsAvailable: 0,
@@ -32,9 +39,15 @@ export type DataExplorerState = Record<string, DataExplorer>;
 
 export const dataExplorerReducer = (state: DataExplorerState = {}, action: DataExplorerAction) =>
     dataExplorerActions.match(action, {
+        CLEAR: ({ id }) =>
+            update(state, id, explorer => ({ ...explorer, page: 0, itemsAvailable: 0, items: [] })),
+
         RESET_PAGINATION: ({ id }) =>
             update(state, id, explorer => ({ ...explorer, page: 0 })),
 
+        SET_FETCH_MODE: ({ id, fetchMode }) =>
+            update(state, id, explorer => ({ ...explorer, fetchMode })),
+
         SET_COLUMNS: ({ id, columns }) =>
             update(state, id, setColumns(columns)),
 
@@ -44,6 +57,15 @@ export const dataExplorerReducer = (state: DataExplorerState = {}, action: DataE
         SET_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
             update(state, id, explorer => ({ ...explorer, items, itemsAvailable, page, rowsPerPage })),
 
+        APPEND_ITEMS: ({ id, items, itemsAvailable, page, rowsPerPage }) =>
+            update(state, id, explorer => ({
+                ...explorer,
+                items: state[id].items.concat(items),
+                itemsAvailable: state[id].itemsAvailable + itemsAvailable,
+                page,
+                rowsPerPage
+            })),
+
         SET_PAGE: ({ id, page }) =>
             update(state, id, explorer => ({ ...explorer, page })),
 
index 78777be10941a0e34d8b46a186fed8a8f6bfbd4f..a6abf44b7142a70e2552f945462fcafb3eb223b7 100644 (file)
@@ -52,6 +52,14 @@ export const getInitialResourceTypeFilters = pipe(
     initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
 );
 
+export const getTrashPanelTypeFilters = pipe(
+    (): DataTableFilters => createTree<DataTableFilterItem>(),
+    initFilter(ObjectTypeFilter.PROJECT),
+    initFilter(ObjectTypeFilter.COLLECTION),
+    initFilter(CollectionTypeFilter.GENERAL_COLLECTION, ObjectTypeFilter.COLLECTION),
+    initFilter(CollectionTypeFilter.OUTPUT_COLLECTION, ObjectTypeFilter.COLLECTION),
+    initFilter(CollectionTypeFilter.LOG_COLLECTION, ObjectTypeFilter.COLLECTION),
+);
 
 const createFiltersBuilder = (filters: DataTableFilters) =>
     ({ fb: new FilterBuilder(), selectedFilters: getSelectedNodes(filters) });
index aa6e4759e0f6d7d4f015624c08c2945a4bf9a307..ea290b4d7def5fd3bc35941f39725c1bd2341ec2 100644 (file)
@@ -4,7 +4,6 @@
 
 import { getAdvancedDataFromQuery, getQueryFromAdvancedData, parseSearchQuery } from "~/store/search-bar/search-bar-actions";
 import { ResourceKind } from "~/models/resource";
-import { ClusterObjectType } from "~/models/search-bar";
 
 describe('search-bar-actions', () => {
     describe('parseSearchQuery', () => {
@@ -95,11 +94,11 @@ describe('search-bar-actions', () => {
         });
 
         it('should correctly build advanced data record from query #2', () => {
-            const r = getAdvancedDataFromQuery('document from:2017-08-01 pdf has:filesize:101mb is:trashed type:arvados#collection cluster:indianapolis');
+            const r = getAdvancedDataFromQuery('document from:2017-08-01 pdf has:filesize:101mb is:trashed type:arvados#collection cluster:c97qx');
             expect(r).toEqual({
                 searchValue: 'document pdf',
                 type: ResourceKind.COLLECTION,
-                cluster: ClusterObjectType.INDIANAPOLIS,
+                cluster: 'c97qx',
                 projectUuid: undefined,
                 inTrash: true,
                 dateFrom: '2017-08-01',
@@ -119,7 +118,7 @@ describe('search-bar-actions', () => {
             const q = getQueryFromAdvancedData({
                 searchValue: 'document pdf',
                 type: ResourceKind.COLLECTION,
-                cluster: ClusterObjectType.INDIANAPOLIS,
+                cluster: 'c97qx',
                 projectUuid: undefined,
                 inTrash: true,
                 dateFrom: '2017-08-01',
@@ -131,7 +130,7 @@ describe('search-bar-actions', () => {
                 saveQuery: false,
                 queryName: ''
             });
-            expect(q).toBe('document pdf type:arvados#collection cluster:indianapolis is:trashed from:2017-08-01 has:filesize:101mb');
+            expect(q).toBe('document pdf type:arvados#collection cluster:c97qx is:trashed from:2017-08-01 has:filesize:101mb');
         });
     });
 });
index 199ec3f95788c9f131e373862e52c07ddb9703bd..c81cba04266ad6252f7bd308abbb7f70bc090402 100644 (file)
@@ -15,11 +15,14 @@ import { GroupClass } from '~/models/group';
 import { SearchView } from '~/store/search-bar/search-bar-reducer';
 import { navigateTo, navigateToSearchResults } from '~/store/navigation/navigation-action';
 import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
-import { ClusterObjectType, PropertyValue, SearchBarAdvanceFormData } from '~/models/search-bar';
+import { PropertyValue, SearchBarAdvanceFormData } from '~/models/search-bar';
 import { debounce } from 'debounce';
 import * as _ from "lodash";
 import { getModifiedKeysValues } from "~/common/objects";
 import { activateSearchBarProject } from "~/store/search-bar/search-bar-tree-actions";
+import { Session } from "~/models/session";
+import { searchResultsPanelActions } from "~/store/search-results-panel/search-results-panel-actions";
+import { ListResults } from "~/services/common-service/common-service";
 
 export const searchBarActions = unionize({
     SET_CURRENT_VIEW: ofType<string>(),
@@ -189,6 +192,7 @@ export const submitData = (event: React.FormEvent<HTMLFormElement>) =>
         dispatch(searchBarActions.CLOSE_SEARCH_VIEW());
         dispatch(searchBarActions.SET_SEARCH_VALUE(searchValue));
         dispatch(searchBarActions.SET_SEARCH_RESULTS([]));
+        dispatch(searchResultsPanelActions.CLEAR());
         dispatch(navigateToSearchResults);
     };
 
@@ -205,12 +209,19 @@ const searchGroups = (searchValue: string, limit: number) =>
         const currentView = getState().searchBar.currentView;
 
         if (searchValue || currentView === SearchView.ADVANCED) {
-            const filters = getFilters('name', searchValue);
-            const { items } = await services.groupsService.contents('', {
-                filters,
-                limit,
-                recursive: true
-            });
+            const sq = parseSearchQuery(searchValue);
+            const clusterId = getSearchQueryFirstProp(sq, 'cluster');
+            const sessions = getSearchSessions(clusterId, getState().auth.sessions);
+            const lists: ListResults<GroupContentsResource>[] = await Promise.all(sessions.map(session => {
+                const filters = getFilters('name', searchValue, sq);
+                return services.groupsService.contents('', {
+                    filters,
+                    limit,
+                    recursive: true
+                }, session);
+            }));
+
+            const items = lists.reduce((items, list) => items.concat(list.items), [] as GroupContentsResource[]);
             dispatch(searchBarActions.SET_SEARCH_RESULTS(items));
         }
     };
@@ -288,7 +299,7 @@ export const getQueryFromAdvancedData = (data: SearchBarAdvanceFormData, prevDat
     return value;
 };
 
-export interface ParseSearchQuery {
+export class ParseSearchQuery {
     hasKeywords: boolean;
     values: string[];
     properties: {
@@ -351,9 +362,9 @@ export const parseSearchQuery: (query: string) => ParseSearchQuery = (searchValu
     return { hasKeywords: keywordsCnt > 0, values, properties };
 };
 
-const getFirstProp = (sq: ParseSearchQuery, name: string) => sq.properties[name] && sq.properties[name][0];
-const getPropValue = (sq: ParseSearchQuery, name: string, value: string) => sq.properties[name] && sq.properties[name].find((v: string) => v === value);
-const getProperties = (sq: ParseSearchQuery): PropertyValue[] => {
+export const getSearchQueryFirstProp = (sq: ParseSearchQuery, name: string) => sq.properties[name] && sq.properties[name][0];
+export const getSearchQueryPropValue = (sq: ParseSearchQuery, name: string, value: string) => sq.properties[name] && sq.properties[name].find((v: string) => v === value);
+export const getSearchQueryProperties = (sq: ParseSearchQuery): PropertyValue[] => {
     if (sq.properties.has) {
         return sq.properties.has.map((value: string) => {
             const v = value.split(':');
@@ -371,23 +382,26 @@ export const getAdvancedDataFromQuery = (query: string): SearchBarAdvanceFormDat
 
     return {
         searchValue: sq.values.join(' '),
-        type: getFirstProp(sq, 'type') as ResourceKind,
-        cluster: getFirstProp(sq, 'cluster') as ClusterObjectType,
-        projectUuid: getFirstProp(sq, 'project'),
-        inTrash: getPropValue(sq, 'is', 'trashed') !== undefined,
-        dateFrom: getFirstProp(sq, 'from'),
-        dateTo: getFirstProp(sq, 'to'),
-        properties: getProperties(sq),
+        type: getSearchQueryFirstProp(sq, 'type') as ResourceKind,
+        cluster: getSearchQueryFirstProp(sq, 'cluster'),
+        projectUuid: getSearchQueryFirstProp(sq, 'project'),
+        inTrash: getSearchQueryPropValue(sq, 'is', 'trashed') !== undefined,
+        dateFrom: getSearchQueryFirstProp(sq, 'from'),
+        dateTo: getSearchQueryFirstProp(sq, 'to'),
+        properties: getSearchQueryProperties(sq),
         saveQuery: false,
         queryName: ''
     };
 };
 
-export const getFilters = (filterName: string, searchValue: string): string => {
+export const getSearchSessions = (clusterId: string | undefined, sessions: Session[]): Session[] => {
+    return sessions.filter(s => s.loggedIn && (!clusterId || s.clusterId === clusterId));
+};
+
+export const getFilters = (filterName: string, searchValue: string, sq: ParseSearchQuery): string => {
     const filter = new FilterBuilder();
-    const sq = parseSearchQuery(searchValue);
 
-    const resourceKind = getFirstProp(sq, 'type') as ResourceKind;
+    const resourceKind = getSearchQueryFirstProp(sq, 'type') as ResourceKind;
 
     let prefix = '';
     switch (resourceKind) {
@@ -402,11 +416,16 @@ export const getFilters = (filterName: string, searchValue: string): string => {
             break;
     }
 
+    const isTrashed = getSearchQueryPropValue(sq, 'is', 'trashed');
+
     if (!sq.hasKeywords) {
         filter
             .addILike(filterName, searchValue, GroupContentsResourcePrefix.COLLECTION)
-            .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS)
             .addILike(filterName, searchValue, GroupContentsResourcePrefix.PROJECT);
+
+        if (isTrashed) {
+            filter.addILike(filterName, searchValue, GroupContentsResourcePrefix.PROCESS);
+        }
     } else {
         if (prefix) {
             sq.values.forEach(v =>
@@ -416,33 +435,38 @@ export const getFilters = (filterName: string, searchValue: string): string => {
             sq.values.forEach(v => {
                 filter
                     .addILike(filterName, v, GroupContentsResourcePrefix.COLLECTION)
-                    .addILike(filterName, v, GroupContentsResourcePrefix.PROCESS)
                     .addILike(filterName, v, GroupContentsResourcePrefix.PROJECT);
+
+                if (isTrashed) {
+                    filter.addILike(filterName, v, GroupContentsResourcePrefix.PROCESS);
+                }
             });
         }
 
-        if (getPropValue(sq, 'is', 'trashed')) {
+        if (isTrashed) {
             filter.addEqual("is_trashed", true);
         }
 
-        const projectUuid = getFirstProp(sq, 'project');
+        const projectUuid = getSearchQueryFirstProp(sq, 'project');
         if (projectUuid) {
             filter.addEqual('uuid', projectUuid, prefix);
         }
 
-        const dateFrom = getFirstProp(sq, 'from');
+        const dateFrom = getSearchQueryFirstProp(sq, 'from');
         if (dateFrom) {
             filter.addGte('modified_at', buildDateFilter(dateFrom));
         }
 
-        const dateTo = getFirstProp(sq, 'to');
+        const dateTo = getSearchQueryFirstProp(sq, 'to');
         if (dateTo) {
             filter.addLte('modified_at', buildDateFilter(dateTo));
         }
 
-        const props = getProperties(sq);
+        const props = getSearchQueryProperties(sq);
         props.forEach(p => {
-            // filter.addILike(`properties.${p.key}`, p.value);
+            if (p.value) {
+                filter.addILike(`properties.${p.key}`, p.value);
+            }
             filter.addExists(p.key);
         });
     }
index d8b4d7e76f7e6c6616f6b69b1d7779fd9cbd2238..de3252221ef8bd6ec61280735c6e8f118092870e 100644 (file)
@@ -15,7 +15,12 @@ import { OrderDirection, OrderBuilder } from '~/services/api/order-builder';
 import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/groups-service/groups-service";
 import { ListResults } from '~/services/common-service/common-service';
 import { searchResultsPanelActions } from '~/store/search-results-panel/search-results-panel-actions';
-import { getFilters } from '~/store/search-bar/search-bar-actions';
+import {
+    getFilters,
+    getSearchQueryFirstProp,
+    getSearchSessions, ParseSearchQuery,
+    parseSearchQuery
+} from '~/store/search-bar/search-bar-actions';
 import { getSortColumn } from "~/store/data-explorer/data-explorer-reducer";
 
 export class SearchResultsMiddlewareService extends DataExplorerMiddlewareService {
@@ -23,24 +28,53 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic
         super(id);
     }
 
-    async requestItems(api: MiddlewareAPI<Dispatch, RootState>) {
+    async requestItems(api: MiddlewareAPI<Dispatch, RootState>, criteriaChanged?: boolean) {
         const state = api.getState();
         const userUuid = state.auth.user!.uuid;
         const dataExplorer = getDataExplorer(state.dataExplorer, this.getId());
         const searchValue = state.searchBar.searchValue;
+        const sq = parseSearchQuery(searchValue);
+        const clusterId = getSearchQueryFirstProp(sq, 'cluster');
+        const sessions = getSearchSessions(clusterId, state.auth.sessions);
+
+        if (searchValue.trim() === '') {
+            return;
+        }
+
         try {
-            const response = await this.services.groupsService.contents('', getParams(dataExplorer, searchValue));
-            api.dispatch(updateResources(response.items));
-            api.dispatch(setItems(response));
+            const params = getParams(dataExplorer, searchValue, sq);
+            const lists: ListResults<GroupContentsResource>[] = await Promise.all(sessions.map(session =>
+                this.services.groupsService.contents('', params, session)
+            ));
+
+            const items = lists
+                .reduce((items, list) => items.concat(list.items), [] as GroupContentsResource[]);
+
+            const itemsAvailable = lists
+                .reduce((itemsAvailable, list) => itemsAvailable + list.itemsAvailable, 0);
+
+            const list: ListResults<GroupContentsResource> = {
+                ...params,
+                kind: '',
+                items,
+                itemsAvailable
+            };
+
+            api.dispatch(updateResources(list.items));
+            api.dispatch(criteriaChanged
+                ? setItems(list)
+                : appendItems(list)
+            );
+
         } catch {
-            api.dispatch(couldNotFetchWorkflows());
+            api.dispatch(couldNotFetchSearchResults());
         }
     }
 }
 
-export const getParams = (dataExplorer: DataExplorer, searchValue: string) => ({
+export const getParams = (dataExplorer: DataExplorer, searchValue: string, sq: ParseSearchQuery) => ({
     ...dataExplorerToListParams(dataExplorer),
-    filters: getFilters('name', searchValue),
+    filters: getFilters('name', searchValue, sq),
     order: getOrder(dataExplorer)
 });
 
@@ -69,8 +103,14 @@ export const setItems = (listResults: ListResults<GroupContentsResource>) =>
         items: listResults.items.map(resource => resource.uuid),
     });
 
-const couldNotFetchWorkflows = () =>
+export const appendItems = (listResults: ListResults<GroupContentsResource>) =>
+    searchResultsPanelActions.APPEND_ITEMS({
+        ...listResultsToDataExplorerItemsMeta(listResults),
+        items: listResults.items.map(resource => resource.uuid),
+    });
+
+const couldNotFetchSearchResults = () =>
     snackbarActions.OPEN_SNACKBAR({
-        message: 'Could not fetch workflows.',
+        message: `Could not fetch search results for some sessions.`,
         kind: SnackbarKind.ERROR
     });
index 60a4b4f007ef3640b18f8c4e9028c490fc5050a6..f842b0c6efadc59f43b812bea337ee32663ee996 100644 (file)
@@ -61,7 +61,7 @@ import { ApiClientAuthorizationMiddlewareService } from '~/store/api-client-auth
 
 const composeEnhancers =
     (process.env.NODE_ENV === 'development' &&
-        window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__) ||
+        window && window.__REDUX_DEVTOOLS_EXTENSION_COMPOSE__({trace: true, traceLimit: 25})) ||
     compose;
 
 export type RootState = ReturnType<ReturnType<typeof createRootReducer>>;
index f52421a1d6581581cdf97f9613d848d6b3f74e15..3708f073b9033b60922348b180f14425dd35fc6f 100644 (file)
@@ -41,7 +41,7 @@ export class TrashPanelMiddlewareService extends DataExplorerMiddlewareService {
 
         const otherFilters = new FilterBuilder()
             .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.COLLECTION)
-            .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROCESS)
+            // .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROCESS)
             .addILike("name", dataExplorer.searchValue, GroupContentsResourcePrefix.PROJECT)
             .addEqual("is_trashed", true)
             .getFilters();
index b12ebb5c469e6fc6424b3603430e7cc3e6e27569..8f5bb605179045b21570b8ad7495a42d4cd27954 100644 (file)
@@ -5,21 +5,37 @@
 import { Dispatch } from 'redux';
 import { RootState } from "~/store/store";
 import { loadDetailsPanel } from '~/store/details-panel/details-panel-action';
-import { snackbarActions } from '~/store/snackbar/snackbar-actions';
-import { loadFavoritePanel } from '~/store/favorite-panel/favorite-panel-action';
-import { openProjectPanel, projectPanelActions, setIsProjectPanelTrashed } from '~/store/project-panel/project-panel-action';
-import { activateSidePanelTreeItem, initSidePanelTree, SidePanelTreeCategory, loadSidePanelTreeProjects } from '~/store/side-panel-tree/side-panel-tree-actions';
+import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions';
+import { favoritePanelActions, loadFavoritePanel } from '~/store/favorite-panel/favorite-panel-action';
+import {
+    getProjectPanelCurrentUuid,
+    openProjectPanel,
+    projectPanelActions,
+    setIsProjectPanelTrashed
+} from '~/store/project-panel/project-panel-action';
+import {
+    activateSidePanelTreeItem,
+    initSidePanelTree,
+    loadSidePanelTreeProjects,
+    SidePanelTreeCategory
+} from '~/store/side-panel-tree/side-panel-tree-actions';
 import { loadResource, updateResources } from '~/store/resources/resources-actions';
-import { favoritePanelActions } from '~/store/favorite-panel/favorite-panel-action';
 import { projectPanelColumns } from '~/views/project-panel/project-panel';
 import { favoritePanelColumns } from '~/views/favorite-panel/favorite-panel';
 import { matchRootRoute } from '~/routes/routes';
-import { setSidePanelBreadcrumbs, setProcessBreadcrumbs, setSharedWithMeBreadcrumbs, setTrashBreadcrumbs, setBreadcrumbs, setGroupDetailsBreadcrumbs, setGroupsBreadcrumbs } from '~/store/breadcrumbs/breadcrumbs-actions';
+import {
+    setBreadcrumbs,
+    setGroupDetailsBreadcrumbs,
+    setGroupsBreadcrumbs,
+    setProcessBreadcrumbs,
+    setSharedWithMeBreadcrumbs,
+    setSidePanelBreadcrumbs,
+    setTrashBreadcrumbs
+} from '~/store/breadcrumbs/breadcrumbs-actions';
 import { navigateToProject } from '~/store/navigation/navigation-action';
 import { MoveToFormDialogData } from '~/store/move-to-dialog/move-to-dialog';
 import { ServiceRepository } from '~/services/services';
 import { getResource } from '~/store/resources/resources';
-import { getProjectPanelCurrentUuid } from '~/store/project-panel/project-panel-action';
 import * as projectCreateActions from '~/store/projects/project-create-actions';
 import * as projectMoveActions from '~/store/projects/project-move-actions';
 import * as projectUpdateActions from '~/store/projects/project-update-actions';
@@ -35,8 +51,10 @@ import { trashPanelColumns } from "~/views/trash-panel/trash-panel";
 import { loadTrashPanel, trashPanelActions } from "~/store/trash-panel/trash-panel-action";
 import { initProcessLogsPanel } from '~/store/process-logs-panel/process-logs-panel-actions';
 import { loadProcessPanel } from '~/store/process-panel/process-panel-actions';
-import { sharedWithMePanelActions } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
-import { loadSharedWithMePanel } from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
+import {
+    loadSharedWithMePanel,
+    sharedWithMePanelActions
+} from '~/store/shared-with-me-panel/shared-with-me-panel-actions';
 import { CopyFormDialogData } from '~/store/copy-dialog/copy-dialog';
 import { loadWorkflowPanel, workflowPanelActions } from '~/store/workflow-panel/workflow-panel-actions';
 import { loadSshKeysPanel } from '~/store/auth/auth-action-ssh';
@@ -45,23 +63,25 @@ import { loadSiteManagerPanel } from '~/store/auth/auth-action-session';
 import { workflowPanelColumns } from '~/views/workflow-panel/workflow-panel-view';
 import { progressIndicatorActions } from '~/store/progress-indicator/progress-indicator-actions';
 import { getProgressIndicator } from '~/store/progress-indicator/progress-indicator-reducer';
-import { ResourceKind, extractUuidKind } from '~/models/resource';
+import { extractUuidKind, ResourceKind } from '~/models/resource';
 import { FilterBuilder } from '~/services/api/filter-builder';
 import { GroupContentsResource } from '~/services/groups-service/groups-service';
-import { unionize, ofType, UnionOf, MatchCases } from '~/common/unionize';
+import { MatchCases, ofType, unionize, UnionOf } from '~/common/unionize';
 import { loadRunProcessPanel } from '~/store/run-process-panel/run-process-panel-actions';
 import { loadCollectionFiles } from '~/store/collection-panel/collection-panel-files/collection-panel-files-actions';
-import { SnackbarKind } from '~/store/snackbar/snackbar-actions';
 import { collectionPanelActions } from "~/store/collection-panel/collection-panel-action";
 import { CollectionResource } from "~/models/collection";
-import { searchResultsPanelActions, loadSearchResultsPanel } from '~/store/search-results-panel/search-results-panel-actions';
+import {
+    loadSearchResultsPanel,
+    searchResultsPanelActions
+} from '~/store/search-results-panel/search-results-panel-actions';
 import { searchResultsPanelColumns } from '~/views/search-results-panel/search-results-panel-view';
 import { loadVirtualMachinesPanel } from '~/store/virtual-machines/virtual-machines-actions';
 import { loadRepositoriesPanel } from '~/store/repositories/repositories-actions';
 import { loadKeepServicesPanel } from '~/store/keep-services/keep-services-actions';
 import { loadUsersPanel, userBindedActions } from '~/store/users/users-actions';
-import { loadLinkPanel, linkPanelActions } from '~/store/link-panel/link-panel-actions';
-import { loadComputeNodesPanel, computeNodesActions } from '~/store/compute-nodes/compute-nodes-actions';
+import { linkPanelActions, loadLinkPanel } from '~/store/link-panel/link-panel-actions';
+import { computeNodesActions, loadComputeNodesPanel } from '~/store/compute-nodes/compute-nodes-actions';
 import { linkPanelColumns } from '~/views/link-panel/link-panel-root';
 import { userPanelColumns } from '~/views/user-panel/user-panel';
 import { computeNodePanelColumns } from '~/views/compute-node-panel/compute-node-panel-root';
@@ -71,6 +91,7 @@ import * as groupPanelActions from '~/store/groups-panel/groups-panel-actions';
 import { groupsPanelColumns } from '~/views/groups-panel/groups-panel';
 import * as groupDetailsPanelActions from '~/store/group-details-panel/group-details-panel-actions';
 import { groupDetailsPanelColumns } from '~/views/group-details-panel/group-details-panel';
+import { DataTableFetchMode } from "~/components/data-table/data-table";
 
 export const WORKBENCH_LOADING_SCREEN = 'workbenchLoadingScreen';
 
@@ -103,6 +124,7 @@ export const loadWorkbench = () =>
                 dispatch(trashPanelActions.SET_COLUMNS({ columns: trashPanelColumns }));
                 dispatch(sharedWithMePanelActions.SET_COLUMNS({ columns: projectPanelColumns }));
                 dispatch(workflowPanelActions.SET_COLUMNS({ columns: workflowPanelColumns }));
+                dispatch(searchResultsPanelActions.SET_FETCH_MODE({ fetchMode: DataTableFetchMode.INFINITE }));
                 dispatch(searchResultsPanelActions.SET_COLUMNS({ columns: searchResultsPanelColumns }));
                 dispatch(userBindedActions.SET_COLUMNS({ columns: userPanelColumns }));
                 dispatch(groupPanelActions.GroupsPanelActions.SET_COLUMNS({ columns: groupsPanelColumns }));
index 8cddf3ba1a5eea67880519a292a46d5146c58e5f..8c95355f6265990773ef20a58965dd1050063676 100644 (file)
@@ -57,6 +57,10 @@ const mapDispatchToProps = () => {
             dispatch(dataExplorerActions.SET_ROWS_PER_PAGE({ id, rowsPerPage }));
         },
 
+        onLoadMore: (page: number) => {
+            dispatch(dataExplorerActions.SET_PAGE({ id, page }));
+        },
+
         onRowClick,
 
         onRowDoubleClick,
index 0637676c03836bf8b389a4f81dcf24dfd763f028..6905e96084083861c25d4842599039721d772c0c 100644 (file)
@@ -81,7 +81,7 @@ const renderWorkflowName = (item: { name: string; uuid: string, kind: string, ow
         </Grid>
     </Grid>;
 
-export const RosurceWorkflowName = connect(
+export const ResourceWorkflowName = connect(
     (state: RootState, props: { uuid: string }) => {
         const resource = getResource<WorkflowResource>(props.uuid)(state.resources);
         return resource || { name: '', uuid: '', kind: '', ownerUuid: '' };
@@ -224,6 +224,29 @@ const renderNodeInfo = (data: string) => {
     return <Typography>{JSON.stringify(data, null, 4)}</Typography>;
 };
 
+const clusterColors = [
+    ['#f44336', '#fff'],
+    ['#2196f3', '#fff'],
+    ['#009688', '#fff'],
+    ['#cddc39', '#fff'],
+    ['#ff9800', '#fff']
+];
+
+export const ResourceCluster = (props: { uuid: string }) => {
+    const CLUSTER_ID_LENGTH = 5;
+    const pos = props.uuid.indexOf('-');
+    const clusterId = pos >= CLUSTER_ID_LENGTH ? props.uuid.substr(0, pos) : '';
+    const ci = pos >= CLUSTER_ID_LENGTH ? (props.uuid.charCodeAt(0) + props.uuid.charCodeAt(1)) % clusterColors.length : 0;
+    return <Typography>
+        <div style={{
+            backgroundColor: clusterColors[ci][0],
+            color: clusterColors[ci][1],
+            padding: "2px 7px",
+            borderRadius: 3
+        }}>{clusterId}</div>
+    </Typography>;
+};
+
 export const ComputeNodeInfo = withResourceData('info', renderNodeInfo);
 
 export const ComputeNodeDomain = withResourceData('domain', renderCommonData);
index 85abbe19f3f266769bb70b9f1da0e03d847399f1..8de48ea744a8494ae2575a5ee51358a0c591902f 100644 (file)
@@ -8,7 +8,6 @@ import { TextField, DateTextField } from "~/components/text-field/text-field";
 import { CheckboxField } from '~/components/checkbox-field/checkbox-field';
 import { NativeSelectField } from '~/components/select-field/select-field';
 import { ResourceKind } from '~/models/resource';
-import { ClusterObjectType } from '~/models/search-bar';
 import { HomeTreePicker } from '~/views-components/projects-tree-picker/home-tree-picker';
 import { SEARCH_BAR_ADVANCE_FORM_PICKER_ID } from '~/store/search-bar/search-bar-actions';
 import { SearchBarAdvancedPropertiesView } from '~/views-components/search-bar/search-bar-advanced-properties-view';
@@ -18,6 +17,8 @@ import { PropertyKeyInput } from '~/views-components/resource-properties-form/pr
 import { PropertyValueInput, PropertyValueFieldProps } from '~/views-components/resource-properties-form/property-value-field';
 import { VocabularyProp, connectVocabulary } from '~/views-components/resource-properties-form/property-field-common';
 import { compose } from 'redux';
+import { connect } from "react-redux";
+import { RootState } from "~/store/store";
 
 export const SearchBarTypeField = () =>
     <Field
@@ -30,16 +31,25 @@ export const SearchBarTypeField = () =>
             { key: ResourceKind.PROCESS, value: 'Process' }
         ]} />;
 
-export const SearchBarClusterField = () =>
-    <Field
+
+interface SearchBarClusterFieldProps {
+    clusters: { key: string, value: string }[];
+}
+
+export const SearchBarClusterField = connect(
+    (state: RootState) => ({
+        clusters: [{key: '', value: 'Any'}].concat(
+            state.auth.sessions
+                .filter(s => s.loggedIn)
+                .map(s => ({
+                    key: s.clusterId,
+                    value: s.clusterId
+                })))
+    }))((props: SearchBarClusterFieldProps) => <Field
         name='cluster'
         component={NativeSelectField}
-        items={[
-            { key: '', value: 'Any' },
-            { key: ClusterObjectType.INDIANAPOLIS, value: 'Indianapolis' },
-            { key: ClusterObjectType.KAISERAUGST, value: 'Kaiseraugst' },
-            { key: ClusterObjectType.PENZBERG, value: 'Penzberg' }
-        ]} />;
+        items={props.clusters}/>
+    );
 
 export const SearchBarProjectField = () =>
     <Field
index e49fc79d0fbef7df23e05aa1aa72b5f09af4cfcf..6770e61a00faaa4b809415935abf3639e7da417a 100644 (file)
@@ -10,9 +10,9 @@ import { COMPUTE_NODE_PANEL_ID } from '~/store/compute-nodes/compute-nodes-actio
 import { DataColumns } from '~/components/data-table/data-table';
 import { SortDirection } from '~/components/data-table/data-column';
 import { createTree } from '~/models/tree';
-import { 
-    CommonUuid, ComputeNodeInfo, ComputeNodeDomain, ComputeNodeHostname, ComputeNodeJobUuid,
-    ComputeNodeFirstPingAt, ComputeNodeLastPingAt, ComputeNodeIpAddress
+import {
+    ComputeNodeInfo, ComputeNodeDomain, ComputeNodeHostname, ComputeNodeJobUuid,
+    ComputeNodeFirstPingAt, ComputeNodeLastPingAt, ComputeNodeIpAddress, CommonUuid
 } from '~/views-components/data-explorer/renderers';
 import { ResourcesState } from '~/store/resources/resources';
 
@@ -115,4 +115,4 @@ export const ComputeNodePanelRoot = (props: ComputeNodePanelRootProps) => {
                 icon={ShareMeIcon}
                 messages={[DEFAULT_MESSAGE]} />
         } />;
-};
\ No newline at end of file
+};
index 7bfc2bfef6caa70aa952f654a8d15423dd234d8a..6827a00288c4327c7b30f05c94b5ffea38362521 100644 (file)
@@ -12,7 +12,7 @@ import { SearchBarAdvanceFormData } from '~/models/search-bar';
 import { SEARCH_RESULTS_PANEL_ID } from '~/store/search-results-panel/search-results-panel-actions';
 import { DataExplorer } from '~/views-components/data-explorer/data-explorer';
 import {
-    ProcessStatus,
+    ProcessStatus, ResourceCluster,
     ResourceFileSize,
     ResourceLastModifiedDate,
     ResourceName,
@@ -23,6 +23,7 @@ import { createTree } from '~/models/tree';
 import { getInitialResourceTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
 
 export enum SearchResultsPanelColumnNames {
+    CLUSTER = "Cluster",
     NAME = "Name",
     PROJECT = "Project",
     STATUS = "Status",
@@ -50,6 +51,13 @@ export interface WorkflowPanelFilter extends DataTableFilterItem {
 }
 
 export const searchResultsPanelColumns: DataColumns<string> = [
+    {
+        name: SearchResultsPanelColumnNames.CLUSTER,
+        selected: true,
+        configurable: true,
+        filters: createTree(),
+        render: (uuid: string) => <ResourceCluster uuid={uuid} />
+    },
     {
         name: SearchResultsPanelColumnNames.NAME,
         selected: true,
@@ -110,4 +118,4 @@ export const SearchResultsPanelView = (props: SearchResultsPanelProps) => {
         onRowDoubleClick={props.onItemDoubleClick}
         onContextMenu={props.onContextMenu}
         contextMenuColumn={true} />;
-};
\ No newline at end of file
+};
index bcc661144fe64f0f0aee9faf0499a7d2c3ff06b4..7a319ef70286ac08d8ba1b5282fc603680b6c73c 100644 (file)
@@ -32,7 +32,10 @@ import { ContextMenuKind } from "~/views-components/context-menu/context-menu";
 import { Dispatch } from "redux";
 import { DataTableDefaultView } from '~/components/data-table-default-view/data-table-default-view';
 import { createTree } from '~/models/tree';
-import { getInitialResourceTypeFilters } from '~/store/resource-type-filters/resource-type-filters';
+import {
+    getInitialResourceTypeFilters,
+    getTrashPanelTypeFilters
+} from '~/store/resource-type-filters/resource-type-filters';
 
 type CssRules = "toolbar" | "button";
 
@@ -93,7 +96,7 @@ export const trashPanelColumns: DataColumns<string> = [
         selected: true,
         configurable: true,
         sortDirection: SortDirection.NONE,
-        filters: getInitialResourceTypeFilters(),
+        filters: getTrashPanelTypeFilters(),
         render: uuid => <ResourceType uuid={uuid} />,
     },
     {
index da8a0c4bd303d5de5a66c86d6b4fb1adb8a6861c..d8fa100e73d9d8f0061279c11407e6f2ead0a6fa 100644 (file)
@@ -9,7 +9,7 @@ import { DataTableDefaultView } from '~/components/data-table-default-view/data-
 import { WORKFLOW_PANEL_ID } from '~/store/workflow-panel/workflow-panel-actions';
 import {
     ResourceLastModifiedDate,
-    RosurceWorkflowName,
+    ResourceWorkflowName,
     ResourceWorkflowStatus,
     ResourceShare,
     ResourceRunProcess
@@ -70,7 +70,7 @@ export const workflowPanelColumns: DataColumns<string> = [
         configurable: true,
         sortDirection: SortDirection.ASC,
         filters: createTree(),
-        render: (uuid: string) => <RosurceWorkflowName uuid={uuid} />
+        render: (uuid: string) => <ResourceWorkflowName uuid={uuid} />
     },
     {
         name: WorkflowPanelColumnNames.AUTHORISATION,