[arvados-workbench2] created: 2.4.0-362-gbe5e418e
git repository hosting
git at public.arvados.org
Fri Dec 9 04:31:38 UTC 2022
at be5e418e1bb0ff54401b71c2cf6865222d182c18 (commit)
commit be5e418e1bb0ff54401b71c2cf6865222d182c18
Author: Peter Amstutz <peter.amstutz at curii.com>
Date: Thu Dec 8 23:31:10 2022 -0500
19783: All done
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>
diff --git a/src/store/side-panel-tree/side-panel-tree-actions.ts b/src/store/side-panel-tree/side-panel-tree-actions.ts
index dd56b428..f6015fbf 100644
--- a/src/store/side-panel-tree/side-panel-tree-actions.ts
+++ b/src/store/side-panel-tree/side-panel-tree-actions.ts
@@ -20,7 +20,7 @@ import { CategoriesListReducer } from 'common/plugintypes';
import { pluginConfig } from 'plugins';
export enum SidePanelTreeCategory {
- PROJECTS = 'Projects',
+ PROJECTS = 'Home Projects',
SHARED_WITH_ME = 'Shared with me',
PUBLIC_FAVORITES = 'Public Favorites',
FAVORITES = 'My Favorites',
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index e6c4e09b..cabc9f72 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -233,7 +233,7 @@ export const initUserProject = (pickerId: string) =>
dispatch(receiveTreePickerData({
id: '',
pickerId,
- data: [{ uuid, name: 'Projects' }],
+ data: [{ uuid, name: 'Home Projects' }],
extractNodeData: value => ({
id: value.uuid,
status: TreeNodeStatus.INITIAL,
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index 13f43e81..de7362d9 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -42,11 +42,14 @@ interface ProjectsTreePickerActionProps {
onCollectionFilter: (value: string) => void;
}
-const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => ({
- projectSearch: state.treePickerSearch.projectSearchValues[props.pickerId],
- collectionFilter: state.treePickerSearch.collectionFilterValues[props.pickerId],
- ...props
-});
+const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => {
+ const { search } = getProjectsTreePickerIds(props.pickerId);
+ return {
+ ...props,
+ projectSearch: state.treePickerSearch.projectSearchValues[search],
+ collectionFilter: state.treePickerSearch.collectionFilterValues[search],
+ };
+};
const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (ProjectsTreePickerActionProps & DispatchProp) => {
const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(props.pickerId);
@@ -74,17 +77,21 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (Pr
}
};
-type CssRules = 'pickerHeight' | 'searchFlex';
+type CssRules = 'pickerHeight' | 'searchFlex' | 'scrolledBox';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
pickerHeight: {
- height: "80vh"
+ height: "70vh"
},
searchFlex: {
display: "flex",
justifyContent: "space-around",
paddingBottom: "1em"
},
+ scrolledBox: {
+ height: "100%",
+ overflow: "scroll"
+ }
});
type ProjectsTreePickerCombinedProps = ToplevelPickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps & DispatchProp & WithStyles<CssRules>;
@@ -128,34 +135,39 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
includeFiles: this.props.includeFiles,
showSelection: this.props.showSelection,
options: this.props.options,
+ toggleItemActive: this.props.toggleItemActive,
+ toggleItemSelection: this.props.toggleItemSelection,
relatedTreePickers,
disableActivation,
};
return <div className={this.props.classes.pickerHeight} >
<span className={this.props.classes.searchFlex}>
- <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
- <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
+ <SearchInput value="" label="Search all Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
+ {this.props.includeCollections &&
+ <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />}
</span>
- {this.props.projectSearch !== "" ?
- <div data-cy="projects-tree-search-picker">
- <SearchProjectsPicker {...p} pickerId={search} />
- </div>
- :
- <>
- <div data-cy="projects-tree-home-tree-picker">
- <HomeTreePicker {...p} pickerId={home} />
- </div>
- <div data-cy="projects-tree-shared-tree-picker">
- <SharedTreePicker {...p} pickerId={shared} />
- </div>
- <div data-cy="projects-tree-public-favourites-tree-picker">
- <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
- </div>
- <div data-cy="projects-tree-favourites-tree-picker">
- <FavoritesTreePicker {...p} pickerId={favorites} />
+ <div className={this.props.classes.scrolledBox}>
+ {this.props.projectSearch ?
+ <div data-cy="projects-tree-search-picker">
+ <SearchProjectsPicker {...p} pickerId={search} />
</div>
- </>}
+ :
+ <>
+ <div data-cy="projects-tree-home-tree-picker">
+ <HomeTreePicker {...p} pickerId={home} />
+ </div>
+ <div data-cy="projects-tree-shared-tree-picker">
+ <SharedTreePicker {...p} pickerId={shared} />
+ </div>
+ <div data-cy="projects-tree-public-favourites-tree-picker">
+ <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
+ </div>
+ <div data-cy="projects-tree-favourites-tree-picker">
+ <FavoritesTreePicker {...p} pickerId={favorites} />
+ </div>
+ </>}
+ </div>
</div >;
}
}));
commit 2787be4c060410a37dc3cf4b512ccb0561d5c394
Author: Peter Amstutz <peter.amstutz at curii.com>
Date: Thu Dec 8 22:03:23 2022 -0500
19783: Project search almost works
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>
diff --git a/src/store/auth/auth-action.ts b/src/store/auth/auth-action.ts
index 7fc9df77..6eb8356f 100644
--- a/src/store/auth/auth-action.ts
+++ b/src/store/auth/auth-action.ts
@@ -92,6 +92,9 @@ export const saveApiToken = (token: string) => async (dispatch: Dispatch, getSta
// If the token is from a LoginCluster federation, get user & token data
// from the token issuing cluster.
+ if (!config) {
+ return;
+ }
const lc = (config as Config).loginCluster
const tokenCluster = tokenParts.length === 3
? tokenParts[1].substring(0, 5)
@@ -127,7 +130,7 @@ export const getNewExtraToken = (reuseStored: boolean = false) =>
const client = await svc.apiClientAuthorizationService.get('current');
dispatch(authActions.SET_EXTRA_TOKEN({
extraApiToken: extraToken,
- extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt): undefined,
+ extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt) : undefined,
}));
return extraToken;
} catch (e) {
@@ -145,7 +148,7 @@ export const getNewExtraToken = (reuseStored: boolean = false) =>
const newExtraToken = getTokenV2(client);
dispatch(authActions.SET_EXTRA_TOKEN({
extraApiToken: newExtraToken,
- extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt): undefined,
+ extraApiTokenExpiration: client.expiresAt ? new Date(client.expiresAt) : undefined,
}));
return newExtraToken;
} catch {
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index b7710494..e6c4e09b 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -44,7 +44,6 @@ export interface LoadProjectParams {
includeCollections?: boolean;
includeFiles?: boolean;
includeFilterGroups?: boolean;
- loadShared?: boolean;
options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
}
@@ -60,7 +59,8 @@ export const getProjectsTreePickerIds = (pickerId: string) => ({
home: `${pickerId}_home`,
shared: `${pickerId}_shared`,
favorites: `${pickerId}_favorites`,
- publicFavorites: `${pickerId}_publicFavorites`
+ publicFavorites: `${pickerId}_publicFavorites`,
+ search: `${pickerId}_search`,
});
export const getAllNodes = <Value>(pickerId: string, filter = (node: TreeNode<Value>) => true) => (state: TreePicker) =>
@@ -88,11 +88,12 @@ export const getSelectedNodes = <Value>(pickerId: string) => (state: TreePicker)
export const initProjectsTreePicker = (pickerId: string) =>
async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
- const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
+ const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(pickerId);
dispatch<any>(initUserProject(home));
dispatch<any>(initSharedProject(shared));
dispatch<any>(initFavoritesProject(favorites));
dispatch<any>(initPublicFavoritesProject(publicFavorites));
+ dispatch<any>(initSearchProject(search));
};
interface ReceiveTreePickerDataParams<T> {
@@ -116,35 +117,37 @@ export const receiveTreePickerData = <T>(params: ReceiveTreePickerDataParams<T>)
interface LoadProjectParamsWithId extends LoadProjectParams {
id: string;
pickerId: string;
- includeCollections?: boolean;
- includeFiles?: boolean;
- includeFilterGroups?: boolean;
loadShared?: boolean;
- options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
+ searchProjects?: boolean;
}
export const loadProject = (params: LoadProjectParamsWithId) =>
async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
- const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options } = params;
+ const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options, searchProjects = false } = params;
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
- let filterB = pipe(
- (fb: FilterBuilder) => includeCollections
- ? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
- : fb.addIsA('uuid', [ResourceKind.PROJECT]),
- fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]),
- )(new FilterBuilder());
+ let filterB = new FilterBuilder();
+
+ filterB = (includeCollections && !searchProjects)
+ ? filterB.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
+ : filterB.addIsA('uuid', [ResourceKind.PROJECT]);
const state = getState();
if (state.treePickerSearch.collectionFilterValues[pickerId]) {
filterB = filterB.addILike('collections.name', state.treePickerSearch.collectionFilterValues[pickerId]);
+ } else {
+ filterB = filterB.addNotIn("collections.properties.type", ["intermediate", "log"]);
+ }
+
+ if (searchProjects && state.treePickerSearch.projectSearchValues[pickerId]) {
+ filterB = filterB.addILike('groups.name', state.treePickerSearch.projectSearchValues[pickerId]);
}
const filters = filterB.getFilters();
- const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
+ const { items, itemsAvailable } = await services.groupsService.contents((loadShared || searchProjects) ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
if (itemsAvailable > 1000) {
items.push({
@@ -292,6 +295,22 @@ export const initPublicFavoritesProject = (pickerId: string) =>
}));
};
+export const SEARCH_PROJECT_ID = 'Search all Projects';
+export const initSearchProject = (pickerId: string) =>
+ async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
+ dispatch(receiveTreePickerData({
+ id: '',
+ pickerId,
+ data: [{ uuid: SEARCH_PROJECT_ID, name: SEARCH_PROJECT_ID }],
+ extractNodeData: value => ({
+ id: value.uuid,
+ status: TreeNodeStatus.INITIAL,
+ value,
+ }),
+ }));
+ };
+
+
interface LoadFavoritesProjectParams {
pickerId: string;
includeCollections?: boolean;
diff --git a/src/views-components/projects-tree-picker/home-tree-picker.tsx b/src/views-components/projects-tree-picker/home-tree-picker.tsx
index 4e8eeda9..3133c5db 100644
--- a/src/views-components/projects-tree-picker/home-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/home-tree-picker.tsx
@@ -6,12 +6,12 @@ import { connect } from 'react-redux';
import { ProjectsTreePicker, ProjectsTreePickerProps } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
import { Dispatch } from 'redux';
import { loadUserProject } from 'store/tree-picker/tree-picker-actions';
-import { ProjectIcon } from 'components/icon/icon';
+import { ProjectsIcon } from 'components/icon/icon';
export const HomeTreePicker = connect(() => ({
- rootItemIcon: ProjectIcon,
+ rootItemIcon: ProjectsIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
dispatch<any>(loadUserProject(pickerId, includeCollections, includeFiles, options));
},
-}))(ProjectsTreePicker);
\ No newline at end of file
+}))(ProjectsTreePicker);
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index 8f21784b..13f43e81 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -10,6 +10,7 @@ import { values, pipe } from 'lodash/fp';
import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-picker';
import { SharedTreePicker } from 'views-components/projects-tree-picker/shared-tree-picker';
import { FavoritesTreePicker } from 'views-components/projects-tree-picker/favorites-tree-picker';
+import { SearchProjectsPicker } from 'views-components/projects-tree-picker/search-projects-picker';
import {
getProjectsTreePickerIds, treePickerActions, treePickerSearchActions, initProjectsTreePicker,
SHARED_PROJECT_ID, FAVORITES_PROJECT_ID
@@ -42,13 +43,13 @@ interface ProjectsTreePickerActionProps {
}
const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => ({
- projectSearch: "",
- collectionFilter: "",
+ projectSearch: state.treePickerSearch.projectSearchValues[props.pickerId],
+ collectionFilter: state.treePickerSearch.collectionFilterValues[props.pickerId],
...props
});
const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (ProjectsTreePickerActionProps & DispatchProp) => {
- const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(props.pickerId);
+ const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(props.pickerId);
const params = {
includeCollections: props.includeCollections,
includeFiles: props.includeFiles,
@@ -58,20 +59,22 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (Pr
dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: shared, params }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: favorites, params }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: publicFavorites, params }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: search, params }));
return {
- onProjectSearch: (projectSearchValue: string) => dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: props.pickerId, projectSearchValue })),
+ onProjectSearch: (projectSearchValue: string) => dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: search, projectSearchValue })),
onCollectionFilter: (collectionFilterValue: string) => {
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: search, collectionFilterValue }));
},
dispatch
}
};
-type CssRules = 'pickerHeight' | 'searchFlex' | 'searchPadding';
+type CssRules = 'pickerHeight' | 'searchFlex';
const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
pickerHeight: {
@@ -91,11 +94,12 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
class FileInputComponent extends React.Component<ProjectsTreePickerCombinedProps> {
componentDidMount() {
- const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+ const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(this.props.pickerId);
this.props.dispatch<any>(initProjectsTreePicker(this.props.pickerId));
- this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: this.props.pickerId, projectSearchValue: "" }));
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: search, projectSearchValue: "" }));
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: search, collectionFilterValue: "" }));
this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue: "" }));
this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue: "" }));
this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue: "" }));
@@ -103,9 +107,9 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
}
componentWillUnmount() {
- const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+ const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(this.props.pickerId);
// Release all the state, we don't need it to hang around forever.
- this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: this.props.pickerId }));
+ this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: search }));
this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: home }));
this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: shared }));
this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: favorites }));
@@ -117,7 +121,7 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
const onProjectSearch = this.props.onProjectSearch;
const onCollectionFilter = this.props.onCollectionFilter;
- const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
+ const { home, shared, favorites, publicFavorites, search } = getProjectsTreePickerIds(pickerId);
const relatedTreePickers = getRelatedTreePickers(pickerId);
const p = {
includeCollections: this.props.includeCollections,
@@ -132,18 +136,26 @@ export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
<SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
<SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
</span>
- <div data-cy="projects-tree-home-tree-picker">
- <HomeTreePicker {...p} pickerId={home} />
- </div>
- <div data-cy="projects-tree-shared-tree-picker">
- <SharedTreePicker {...p} pickerId={shared} />
- </div>
- <div data-cy="projects-tree-public-favourites-tree-picker">
- <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
- </div>
- <div data-cy="projects-tree-favourites-tree-picker">
- <FavoritesTreePicker {...p} pickerId={favorites} />
- </div>
+
+ {this.props.projectSearch !== "" ?
+ <div data-cy="projects-tree-search-picker">
+ <SearchProjectsPicker {...p} pickerId={search} />
+ </div>
+ :
+ <>
+ <div data-cy="projects-tree-home-tree-picker">
+ <HomeTreePicker {...p} pickerId={home} />
+ </div>
+ <div data-cy="projects-tree-shared-tree-picker">
+ <SharedTreePicker {...p} pickerId={shared} />
+ </div>
+ <div data-cy="projects-tree-public-favourites-tree-picker">
+ <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
+ </div>
+ <div data-cy="projects-tree-favourites-tree-picker">
+ <FavoritesTreePicker {...p} pickerId={favorites} />
+ </div>
+ </>}
</div >;
}
}));
diff --git a/src/views-components/projects-tree-picker/shared-tree-picker.tsx b/src/views-components/projects-tree-picker/shared-tree-picker.tsx
index 201bd118..c15df6ba 100644
--- a/src/views-components/projects-tree-picker/shared-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/shared-tree-picker.tsx
@@ -7,11 +7,12 @@ import { ProjectsTreePicker, ProjectsTreePickerProps } from 'views-components/pr
import { Dispatch } from 'redux';
import { ShareMeIcon } from 'components/icon/icon';
import { loadProject } from 'store/tree-picker/tree-picker-actions';
+import { SHARED_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
export const SharedTreePicker = connect(() => ({
rootItemIcon: ShareMeIcon,
}), (dispatch: Dispatch): Pick<ProjectsTreePickerProps, 'loadRootItem'> => ({
loadRootItem: (_, pickerId, includeCollections, includeFiles, options) => {
- dispatch<any>(loadProject({ id: 'Shared with me', pickerId, includeCollections, includeFiles, loadShared: true, options }));
+ dispatch<any>(loadProject({ id: SHARED_PROJECT_ID, pickerId, includeCollections, includeFiles, loadShared: true, options }));
},
-}))(ProjectsTreePicker);
\ No newline at end of file
+}))(ProjectsTreePicker);
commit 8a1dfbf52d1407b2773874c03db85a56143d20e6
Author: Peter Amstutz <peter.amstutz at curii.com>
Date: Thu Dec 8 20:59:09 2022 -0500
19783: Filtering collections works now
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>
diff --git a/src/store/tree-picker/tree-picker-reducer.ts b/src/store/tree-picker/tree-picker-reducer.ts
index d4b710c9..df0ee0ad 100644
--- a/src/store/tree-picker/tree-picker-reducer.ts
+++ b/src/store/tree-picker/tree-picker-reducer.ts
@@ -72,6 +72,10 @@ const receiveNodes = <V>(nodes: Array<TreeNode<V>>) => (parent: string) => (stat
newState = setNode({ ...parentNode, children: [] })(state);
}
return nodes.reduce((tree, node) => {
+ const preexistingNode = getNode(node.id)(state);
+ if (preexistingNode) {
+ node = { ...preexistingNode, value: node.value };
+ }
return setNode({ ...node, parent })(tree);
}, newState);
};
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index bddde892..8f21784b 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -4,19 +4,24 @@
import React from 'react';
import { Dispatch } from 'redux';
-import { connect } from 'react-redux';
+import { connect, DispatchProp } from 'react-redux';
import { RootState } from 'store/store';
import { values, pipe } from 'lodash/fp';
import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-picker';
import { SharedTreePicker } from 'views-components/projects-tree-picker/shared-tree-picker';
import { FavoritesTreePicker } from 'views-components/projects-tree-picker/favorites-tree-picker';
-import { getProjectsTreePickerIds, treePickerSearchActions, SHARED_PROJECT_ID, FAVORITES_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
+import {
+ getProjectsTreePickerIds, treePickerActions, treePickerSearchActions, initProjectsTreePicker,
+ SHARED_PROJECT_ID, FAVORITES_PROJECT_ID
+} from 'store/tree-picker/tree-picker-actions';
import { TreeItem } from 'components/tree/tree';
import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { PublicFavoritesTreePicker } from './public-favorites-tree-picker';
import { SearchInput } from 'components/search-input/search-input';
+import { withStyles, StyleRulesCallback, WithStyles } from '@material-ui/core';
+import { ArvadosTheme } from 'common/custom-theme';
-export interface ProjectsTreePickerProps {
+export interface ToplevelPickerProps {
pickerId: string;
includeCollections?: boolean;
includeFiles?: boolean;
@@ -36,15 +41,13 @@ interface ProjectsTreePickerActionProps {
onCollectionFilter: (value: string) => void;
}
-type ProjectsTreePickerCombinedProps = ProjectsTreePickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps;
-
-const mapStateToProps = (state: RootState, props: ProjectsTreePickerProps): ProjectsTreePickerSearchProps => ({
+const mapStateToProps = (state: RootState, props: ToplevelPickerProps): ProjectsTreePickerSearchProps => ({
projectSearch: "",
collectionFilter: "",
...props
});
-const mapDispatchToProps = (dispatch: Dispatch, props: ProjectsTreePickerProps): ProjectsTreePickerActionProps => {
+const mapDispatchToProps = (dispatch: Dispatch, props: ToplevelPickerProps): (ProjectsTreePickerActionProps & DispatchProp) => {
const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(props.pickerId);
const params = {
includeCollections: props.includeCollections,
@@ -63,37 +66,87 @@ const mapDispatchToProps = (dispatch: Dispatch, props: ProjectsTreePickerProps):
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue }));
dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue }));
- }
+ },
+ dispatch
}
};
-export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(({ pickerId, onProjectSearch, onCollectionFilter, ...props }: ProjectsTreePickerCombinedProps) => {
- const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
- const relatedTreePickers = getRelatedTreePickers(pickerId);
- const p = {
- ...props,
- relatedTreePickers,
- disableActivation,
- };
- return <div>
- <span>
- <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
- <SearchInput value="" label="Filter Collections" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
- </span>
- <div data-cy="projects-tree-home-tree-picker">
- <HomeTreePicker pickerId={home} {...p} />
- </div>
- <div data-cy="projects-tree-shared-tree-picker">
- <SharedTreePicker pickerId={shared} {...p} />
- </div>
- <div data-cy="projects-tree-public-favourites-tree-picker">
- <PublicFavoritesTreePicker pickerId={publicFavorites} {...p} />
- </div>
- <div data-cy="projects-tree-favourites-tree-picker">
- <FavoritesTreePicker pickerId={favorites} {...p} />
- </div>
- </div>;
+type CssRules = 'pickerHeight' | 'searchFlex' | 'searchPadding';
+
+const styles: StyleRulesCallback<CssRules> = (theme: ArvadosTheme) => ({
+ pickerHeight: {
+ height: "80vh"
+ },
+ searchFlex: {
+ display: "flex",
+ justifyContent: "space-around",
+ paddingBottom: "1em"
+ },
});
+type ProjectsTreePickerCombinedProps = ToplevelPickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps & DispatchProp & WithStyles<CssRules>;
+
+export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(
+ withStyles(styles)(
+ class FileInputComponent extends React.Component<ProjectsTreePickerCombinedProps> {
+
+ componentDidMount() {
+ const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+
+ this.props.dispatch<any>(initProjectsTreePicker(this.props.pickerId));
+
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: this.props.pickerId, projectSearchValue: "" }));
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue: "" }));
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue: "" }));
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue: "" }));
+ this.props.dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue: "" }));
+ }
+
+ componentWillUnmount() {
+ const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(this.props.pickerId);
+ // Release all the state, we don't need it to hang around forever.
+ this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: this.props.pickerId }));
+ this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: home }));
+ this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: shared }));
+ this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: favorites }));
+ this.props.dispatch(treePickerActions.RESET_TREE_PICKER({ pickerId: publicFavorites }));
+ }
+
+ render() {
+ const pickerId = this.props.pickerId;
+ const onProjectSearch = this.props.onProjectSearch;
+ const onCollectionFilter = this.props.onCollectionFilter;
+
+ const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
+ const relatedTreePickers = getRelatedTreePickers(pickerId);
+ const p = {
+ includeCollections: this.props.includeCollections,
+ includeFiles: this.props.includeFiles,
+ showSelection: this.props.showSelection,
+ options: this.props.options,
+ relatedTreePickers,
+ disableActivation,
+ };
+ return <div className={this.props.classes.pickerHeight} >
+ <span className={this.props.classes.searchFlex}>
+ <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
+ <SearchInput value="" label="Filter Collections inside Projects" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
+ </span>
+ <div data-cy="projects-tree-home-tree-picker">
+ <HomeTreePicker {...p} pickerId={home} />
+ </div>
+ <div data-cy="projects-tree-shared-tree-picker">
+ <SharedTreePicker {...p} pickerId={shared} />
+ </div>
+ <div data-cy="projects-tree-public-favourites-tree-picker">
+ <PublicFavoritesTreePicker {...p} pickerId={publicFavorites} />
+ </div>
+ <div data-cy="projects-tree-favourites-tree-picker">
+ <FavoritesTreePicker {...p} pickerId={favorites} />
+ </div>
+ </div >;
+ }
+ }));
+
const getRelatedTreePickers = pipe(getProjectsTreePickerIds, values);
const disableActivation = [SHARED_PROJECT_ID, FAVORITES_PROJECT_ID];
diff --git a/src/views/run-process-panel/inputs/file-input.tsx b/src/views/run-process-panel/inputs/file-input.tsx
index 099a8128..24ea0b79 100644
--- a/src/views/run-process-panel/inputs/file-input.tsx
+++ b/src/views/run-process-panel/inputs/file-input.tsx
@@ -137,7 +137,7 @@ const FileInputComponent = connect()(
color='primary'
onClick={this.submit}>Ok</Button>
</DialogActions>
- </Dialog>;
+ </Dialog >;
}
});
commit db1c206c8403ed2b874625f1d1afc7af85dde25c
Author: Peter Amstutz <peter.amstutz at curii.com>
Date: Thu Dec 8 17:41:33 2022 -0500
19783: Work in progress on filtering collections
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>
diff --git a/src/store/store.ts b/src/store/store.ts
index 94f110a0..1b9e05a9 100644
--- a/src/store/store.ts
+++ b/src/store/store.ts
@@ -26,7 +26,8 @@ import { AllProcessesPanelMiddlewareService } from "./all-processes-panel/all-pr
import { collectionPanelReducer } from './collection-panel/collection-panel-reducer';
import { dialogReducer } from './dialog/dialog-reducer';
import { ServiceRepository } from "services/services";
-import { treePickerReducer } from './tree-picker/tree-picker-reducer';
+import { treePickerReducer, treePickerSearchReducer } from './tree-picker/tree-picker-reducer';
+import { treePickerSearchMiddleware } from './tree-picker/tree-picker-middleware';
import { resourcesReducer } from 'store/resources/resources-reducer';
import { propertiesReducer } from './properties/properties-reducer';
import { fileUploaderReducer } from './file-uploader/file-uploader-reducer';
@@ -76,7 +77,7 @@ import { MiddlewareListReducer } from 'common/plugintypes';
declare global {
interface Window {
- __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
+ __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
@@ -174,6 +175,7 @@ export function configureStore(history: History, services: ServiceRepository, co
publicFavoritesMiddleware,
collectionsContentAddress,
subprocessMiddleware,
+ treePickerSearchMiddleware
];
const reduceMiddlewaresFn: (a: Middleware[],
@@ -203,6 +205,7 @@ const createRootReducer = (services: ServiceRepository) => combineReducers({
router: routerReducer,
snackbar: snackbarReducer,
treePicker: treePickerReducer,
+ treePickerSearch: treePickerSearchReducer,
fileUploader: fileUploaderReducer,
processPanel: processPanelReducer,
progressIndicator: progressIndicatorReducer,
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index 1c0b2dea..b7710494 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -14,7 +14,7 @@ import { pipe, values } from 'lodash/fp';
import { ResourceKind } from 'models/resource';
import { GroupContentsResource } from 'services/groups-service/groups-service';
import { getTreePicker, TreePicker } from './tree-picker';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from './tree-picker-middleware';
import { OrderBuilder } from 'services/api/order-builder';
import { ProjectResource } from 'models/project';
import { mapTree } from '../../models/tree';
@@ -28,6 +28,7 @@ export const treePickerActions = unionize({
LOAD_TREE_PICKER_NODE_SUCCESS: ofType<{ id: string, nodes: Array<TreeNode<any>>, pickerId: string }>(),
APPEND_TREE_PICKER_NODE_SUBTREE: ofType<{ id: string, subtree: Tree<any>, pickerId: string }>(),
TOGGLE_TREE_PICKER_NODE_COLLAPSE: ofType<{ id: string, pickerId: string }>(),
+ EXPAND_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string }>(),
ACTIVATE_TREE_PICKER_NODE: ofType<{ id: string, pickerId: string, relatedTreePickers?: string[] }>(),
DEACTIVATE_TREE_PICKER_NODE: ofType<{ pickerId: string }>(),
TOGGLE_TREE_PICKER_NODE_SELECTION: ofType<{ id: string, pickerId: string }>(),
@@ -39,6 +40,22 @@ export const treePickerActions = unionize({
export type TreePickerAction = UnionOf<typeof treePickerActions>;
+export interface LoadProjectParams {
+ includeCollections?: boolean;
+ includeFiles?: boolean;
+ includeFilterGroups?: boolean;
+ loadShared?: boolean;
+ options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
+}
+
+export const treePickerSearchActions = unionize({
+ SET_TREE_PICKER_PROJECT_SEARCH: ofType<{ pickerId: string, projectSearchValue: string }>(),
+ SET_TREE_PICKER_COLLECTION_FILTER: ofType<{ pickerId: string, collectionFilterValue: string }>(),
+ SET_TREE_PICKER_LOAD_PARAMS: ofType<{ pickerId: string, params: LoadProjectParams }>(),
+});
+
+export type TreePickerSearchAction = UnionOf<typeof treePickerSearchActions>;
+
export const getProjectsTreePickerIds = (pickerId: string) => ({
home: `${pickerId}_home`,
shared: `${pickerId}_shared`,
@@ -93,10 +110,10 @@ export const receiveTreePickerData = <T>(params: ReceiveTreePickerDataParams<T>)
nodes: data.map(item => initTreeNode(extractNodeData(item))),
pickerId,
}));
- dispatch(treePickerActions.TOGGLE_TREE_PICKER_NODE_COLLAPSE({ id, pickerId }));
+ dispatch(treePickerActions.EXPAND_TREE_PICKER_NODE({ id, pickerId }));
};
-interface LoadProjectParams {
+interface LoadProjectParamsWithId extends LoadProjectParams {
id: string;
pickerId: string;
includeCollections?: boolean;
@@ -105,20 +122,28 @@ interface LoadProjectParams {
loadShared?: boolean;
options?: { showOnlyOwned: boolean; showOnlyWritable: boolean; };
}
-export const loadProject = (params: LoadProjectParams) =>
- async (dispatch: Dispatch, _: () => RootState, services: ServiceRepository) => {
+
+export const loadProject = (params: LoadProjectParamsWithId) =>
+ async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => {
const { id, pickerId, includeCollections = false, includeFiles = false, includeFilterGroups = false, loadShared = false, options } = params;
dispatch(treePickerActions.LOAD_TREE_PICKER_NODE({ id, pickerId }));
- const filters = pipe(
+ let filterB = pipe(
(fb: FilterBuilder) => includeCollections
? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
: fb.addIsA('uuid', [ResourceKind.PROJECT]),
fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]),
- fb => fb.getFilters(),
)(new FilterBuilder());
+ const state = getState();
+
+ if (state.treePickerSearch.collectionFilterValues[pickerId]) {
+ filterB = filterB.addILike('collections.name', state.treePickerSearch.collectionFilterValues[pickerId]);
+ }
+
+ const filters = filterB.getFilters();
+
const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
if (itemsAvailable > 1000) {
diff --git a/src/store/tree-picker/tree-picker-reducer.ts b/src/store/tree-picker/tree-picker-reducer.ts
index 349240ea..d4b710c9 100644
--- a/src/store/tree-picker/tree-picker-reducer.ts
+++ b/src/store/tree-picker/tree-picker-reducer.ts
@@ -2,13 +2,15 @@
//
// SPDX-License-Identifier: AGPL-3.0
-import { createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus, expandNode, deactivateNode, selectNodes, deselectNodes } from 'models/tree';
+import {
+ createTree, TreeNode, setNode, Tree, TreeNodeStatus, setNodeStatus,
+ expandNode, deactivateNode, selectNodes, deselectNodes,
+ activateNode, getNode, toggleNodeCollapse, toggleNodeSelection, appendSubtree
+} from 'models/tree';
import { TreePicker } from "./tree-picker";
-import { treePickerActions, TreePickerAction } from "./tree-picker-actions";
+import { treePickerActions, treePickerSearchActions, TreePickerAction, TreePickerSearchAction, LoadProjectParams } from "./tree-picker-actions";
import { compose } from "redux";
-import { activateNode, getNode, toggleNodeCollapse, toggleNodeSelection } from 'models/tree';
import { pipe } from 'lodash/fp';
-import { appendSubtree } from 'models/tree';
export const treePickerReducer = (state: TreePicker = {}, action: TreePickerAction) =>
treePickerActions.match(action, {
@@ -18,12 +20,15 @@ export const treePickerReducer = (state: TreePicker = {}, action: TreePickerActi
LOAD_TREE_PICKER_NODE_SUCCESS: ({ id, nodes, pickerId }) =>
updateOrCreatePicker(state, pickerId, compose(receiveNodes(nodes)(id), setNodeStatus(id)(TreeNodeStatus.LOADED))),
- APPEND_TREE_PICKER_NODE_SUBTREE: ({ id, subtree, pickerId}) =>
+ APPEND_TREE_PICKER_NODE_SUBTREE: ({ id, subtree, pickerId }) =>
updateOrCreatePicker(state, pickerId, compose(appendSubtree(id, subtree), setNodeStatus(id)(TreeNodeStatus.LOADED))),
TOGGLE_TREE_PICKER_NODE_COLLAPSE: ({ id, pickerId }) =>
updateOrCreatePicker(state, pickerId, toggleNodeCollapse(id)),
+ EXPAND_TREE_PICKER_NODE: ({ id, pickerId }) =>
+ updateOrCreatePicker(state, pickerId, expandNode(id)),
+
ACTIVATE_TREE_PICKER_NODE: ({ id, pickerId, relatedTreePickers = [] }) =>
pipe(
() => relatedTreePickers.reduce(
@@ -70,3 +75,26 @@ const receiveNodes = <V>(nodes: Array<TreeNode<V>>) => (parent: string) => (stat
return setNode({ ...node, parent })(tree);
}, newState);
};
+
+interface TreePickerSearch {
+ projectSearchValues: { [pickerId: string]: string };
+ collectionFilterValues: { [pickerId: string]: string };
+ loadProjectParams: { [pickerId: string]: LoadProjectParams };
+}
+
+export const treePickerSearchReducer = (state: TreePickerSearch = { projectSearchValues: {}, collectionFilterValues: {}, loadProjectParams: {} }, action: TreePickerSearchAction) =>
+ treePickerSearchActions.match(action, {
+ SET_TREE_PICKER_PROJECT_SEARCH: ({ pickerId, projectSearchValue }) => ({
+ ...state, projectSearchValues: { ...state.projectSearchValues, [pickerId]: projectSearchValue }
+ }),
+
+ SET_TREE_PICKER_COLLECTION_FILTER: ({ pickerId, collectionFilterValue }) => ({
+ ...state, collectionFilterValues: { ...state.collectionFilterValues, [pickerId]: collectionFilterValue }
+ }),
+
+ SET_TREE_PICKER_LOAD_PARAMS: ({ pickerId, params }) => ({
+ ...state, loadProjectParams: { ...state.loadProjectParams, [pickerId]: params }
+ }),
+
+ default: () => state
+ });
diff --git a/src/views-components/form-fields/search-bar-form-fields.tsx b/src/views-components/form-fields/search-bar-form-fields.tsx
index 777fa824..0a6b8795 100644
--- a/src/views-components/form-fields/search-bar-form-fields.tsx
+++ b/src/views-components/form-fields/search-bar-form-fields.tsx
@@ -12,7 +12,7 @@ import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-
import { SEARCH_BAR_ADVANCED_FORM_PICKER_ID } from 'store/search-bar/search-bar-actions';
import { SearchBarAdvancedPropertiesView } from 'views-components/search-bar/search-bar-advanced-properties-view';
import { TreeItem } from "components/tree/tree";
-import { ProjectsTreePickerItem } from "views-components/projects-tree-picker/generic-projects-tree-picker";
+import { ProjectsTreePickerItem } from "store/tree-picker/tree-picker-middleware";
import { PropertyKeyField, } from 'views-components/resource-properties-form/property-key-field';
import { PropertyValueField } from 'views-components/resource-properties-form/property-value-field';
import { connect } from "react-redux";
@@ -36,7 +36,7 @@ interface SearchBarClusterFieldProps {
export const SearchBarClusterField = connect(
(state: RootState) => ({
- clusters: [{key: '', value: 'Any'}].concat(
+ clusters: [{ key: '', value: 'Any' }].concat(
state.auth.sessions
.filter(s => s.loggedIn)
.map(s => ({
@@ -46,7 +46,7 @@ export const SearchBarClusterField = connect(
}))((props: SearchBarClusterFieldProps) => <Field
name='cluster'
component={NativeSelectField as any}
- items={props.clusters}/>
+ items={props.clusters} />
);
export const SearchBarProjectField = () =>
diff --git a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
index dd6e63bf..11b51caa 100644
--- a/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/generic-projects-tree-picker.tsx
@@ -12,18 +12,12 @@ import { treePickerActions } from "store/tree-picker/tree-picker-actions";
import { ListItemTextIcon } from "components/list-item-text-icon/list-item-text-icon";
import { ProjectIcon, FileInputIcon, IconType, CollectionIcon } from 'components/icon/icon';
import { loadProject, loadCollection } from 'store/tree-picker/tree-picker-actions';
-import { GroupContentsResource } from 'services/groups-service/groups-service';
-import { CollectionDirectory, CollectionFile, CollectionFileType } from 'models/collection-file';
+import { ProjectsTreePickerItem, ProjectsTreePickerRootItem } from 'store/tree-picker/tree-picker-middleware';
import { ResourceKind } from 'models/resource';
import { TreePickerProps, TreePicker } from "views-components/tree-picker/tree-picker";
-import { LinkResource } from "models/link";
+import { CollectionFileType } from 'models/collection-file';
-export interface ProjectsTreePickerRootItem {
- id: string;
- name: string;
-}
-export type ProjectsTreePickerItem = ProjectsTreePickerRootItem | GroupContentsResource | CollectionDirectory | CollectionFile | LinkResource;
type PickedTreePickerProps = Pick<TreePickerProps<ProjectsTreePickerItem>, 'onContextMenu' | 'toggleItemActive' | 'toggleItemOpen' | 'toggleItemSelection'>;
export interface ProjectsTreePickerDataProps {
@@ -35,7 +29,7 @@ export interface ProjectsTreePickerDataProps {
disableActivation?: string[];
options?: { showOnlyOwned: boolean, showOnlyWritable: boolean };
loadRootItem: (item: TreeItem<ProjectsTreePickerRootItem>, pickerId: string,
- includeCollections?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void;
+ includeCollections?: boolean, includeFiles?: boolean, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) => void;
}
export type ProjectsTreePickerProps = ProjectsTreePickerDataProps & Partial<PickedTreePickerProps>;
diff --git a/src/views-components/projects-tree-picker/projects-tree-picker.tsx b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
index c4a6e3ab..bddde892 100644
--- a/src/views-components/projects-tree-picker/projects-tree-picker.tsx
+++ b/src/views-components/projects-tree-picker/projects-tree-picker.tsx
@@ -3,14 +3,18 @@
// SPDX-License-Identifier: AGPL-3.0
import React from 'react';
+import { Dispatch } from 'redux';
+import { connect } from 'react-redux';
+import { RootState } from 'store/store';
import { values, pipe } from 'lodash/fp';
import { HomeTreePicker } from 'views-components/projects-tree-picker/home-tree-picker';
import { SharedTreePicker } from 'views-components/projects-tree-picker/shared-tree-picker';
import { FavoritesTreePicker } from 'views-components/projects-tree-picker/favorites-tree-picker';
-import { getProjectsTreePickerIds, SHARED_PROJECT_ID, FAVORITES_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
+import { getProjectsTreePickerIds, treePickerSearchActions, SHARED_PROJECT_ID, FAVORITES_PROJECT_ID } from 'store/tree-picker/tree-picker-actions';
import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from './generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { PublicFavoritesTreePicker } from './public-favorites-tree-picker';
+import { SearchInput } from 'components/search-input/search-input';
export interface ProjectsTreePickerProps {
pickerId: string;
@@ -22,15 +26,60 @@ export interface ProjectsTreePickerProps {
toggleItemSelection?: (event: React.MouseEvent<HTMLElement>, item: TreeItem<ProjectsTreePickerItem>, pickerId: string) => void;
}
-export const ProjectsTreePicker = ({ pickerId, ...props }: ProjectsTreePickerProps) => {
+interface ProjectsTreePickerSearchProps {
+ projectSearch: string;
+ collectionFilter: string;
+}
+
+interface ProjectsTreePickerActionProps {
+ onProjectSearch: (value: string) => void;
+ onCollectionFilter: (value: string) => void;
+}
+
+type ProjectsTreePickerCombinedProps = ProjectsTreePickerProps & ProjectsTreePickerSearchProps & ProjectsTreePickerActionProps;
+
+const mapStateToProps = (state: RootState, props: ProjectsTreePickerProps): ProjectsTreePickerSearchProps => ({
+ projectSearch: "",
+ collectionFilter: "",
+ ...props
+});
+
+const mapDispatchToProps = (dispatch: Dispatch, props: ProjectsTreePickerProps): ProjectsTreePickerActionProps => {
+ const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(props.pickerId);
+ const params = {
+ includeCollections: props.includeCollections,
+ includeFiles: props.includeFiles,
+ options: props.options
+ };
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: home, params }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: shared, params }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: favorites, params }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_LOAD_PARAMS({ pickerId: publicFavorites, params }));
+
+ return {
+ onProjectSearch: (projectSearchValue: string) => dispatch(treePickerSearchActions.SET_TREE_PICKER_PROJECT_SEARCH({ pickerId: props.pickerId, projectSearchValue })),
+ onCollectionFilter: (collectionFilterValue: string) => {
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: home, collectionFilterValue }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: shared, collectionFilterValue }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: favorites, collectionFilterValue }));
+ dispatch(treePickerSearchActions.SET_TREE_PICKER_COLLECTION_FILTER({ pickerId: publicFavorites, collectionFilterValue }));
+ }
+ }
+};
+
+export const ProjectsTreePicker = connect(mapStateToProps, mapDispatchToProps)(({ pickerId, onProjectSearch, onCollectionFilter, ...props }: ProjectsTreePickerCombinedProps) => {
const { home, shared, favorites, publicFavorites } = getProjectsTreePickerIds(pickerId);
const relatedTreePickers = getRelatedTreePickers(pickerId);
const p = {
...props,
relatedTreePickers,
- disableActivation
+ disableActivation,
};
return <div>
+ <span>
+ <SearchInput value="" label="Search Projects" selfClearProp='' onSearch={onProjectSearch} debounce={200} />
+ <SearchInput value="" label="Filter Collections" selfClearProp='' onSearch={onCollectionFilter} debounce={200} />
+ </span>
<div data-cy="projects-tree-home-tree-picker">
<HomeTreePicker pickerId={home} {...p} />
</div>
@@ -44,7 +93,7 @@ export const ProjectsTreePicker = ({ pickerId, ...props }: ProjectsTreePickerPro
<FavoritesTreePicker pickerId={favorites} {...p} />
</div>
</div>;
-};
+});
const getRelatedTreePickers = pipe(getProjectsTreePickerIds, values);
const disableActivation = [SHARED_PROJECT_ID, FAVORITES_PROJECT_ID];
diff --git a/src/views-components/projects-tree-picker/tree-picker-field.tsx b/src/views-components/projects-tree-picker/tree-picker-field.tsx
index e5fecf97..d6ebb8ec 100644
--- a/src/views-components/projects-tree-picker/tree-picker-field.tsx
+++ b/src/views-components/projects-tree-picker/tree-picker-field.tsx
@@ -7,7 +7,7 @@ import { Typography } from "@material-ui/core";
import { TreeItem } from "components/tree/tree";
import { WrappedFieldProps } from 'redux-form';
import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { PickerIdProp } from 'store/tree-picker/picker-id';
export const ProjectTreePickerField = (props: WrappedFieldProps & PickerIdProp) =>
@@ -37,4 +37,4 @@ export const CollectionTreePickerField = (props: WrappedFieldProps & PickerIdPro
<Typography variant='caption' color='error'>
{props.meta.error}
</Typography>}
- </div>;
\ No newline at end of file
+ </div>;
diff --git a/src/views/run-process-panel/inputs/directory-array-input.tsx b/src/views/run-process-panel/inputs/directory-array-input.tsx
index 1b04718d..72dba752 100644
--- a/src/views/run-process-panel/inputs/directory-array-input.tsx
+++ b/src/views/run-process-panel/inputs/directory-array-input.tsx
@@ -16,7 +16,7 @@ import { GenericInputProps, GenericInput } from './generic-input';
import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
import { connect, DispatchProp } from 'react-redux';
import { initProjectsTreePicker, getSelectedNodes, treePickerActions, getProjectsTreePickerIds, getAllNodes } from 'store/tree-picker/tree-picker-actions';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { createSelector, createStructuredSelector } from 'reselect';
import { ChipsInput } from 'components/chips-input/chips-input';
import { identity, values, noop } from 'lodash';
diff --git a/src/views/run-process-panel/inputs/directory-input.tsx b/src/views/run-process-panel/inputs/directory-input.tsx
index ab1cf9d1..2120de99 100644
--- a/src/views/run-process-panel/inputs/directory-input.tsx
+++ b/src/views/run-process-panel/inputs/directory-input.tsx
@@ -17,7 +17,7 @@ import { GenericInputProps, GenericInput } from './generic-input';
import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { CollectionResource } from 'models/collection';
import { ResourceKind } from 'models/resource';
import { ERROR_MESSAGE } from 'validators/require';
@@ -141,5 +141,3 @@ const DirectoryInputComponent = connect()(
}
});
-
-
diff --git a/src/views/run-process-panel/inputs/file-array-input.tsx b/src/views/run-process-panel/inputs/file-array-input.tsx
index ddb558b9..712f6418 100644
--- a/src/views/run-process-panel/inputs/file-array-input.tsx
+++ b/src/views/run-process-panel/inputs/file-array-input.tsx
@@ -16,7 +16,7 @@ import { GenericInputProps, GenericInput } from './generic-input';
import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
import { connect, DispatchProp } from 'react-redux';
import { initProjectsTreePicker, getSelectedNodes, treePickerActions, getProjectsTreePickerIds } from 'store/tree-picker/tree-picker-actions';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { CollectionFile, CollectionFileType } from 'models/collection-file';
import { createSelector, createStructuredSelector } from 'reselect';
import { ChipsInput } from 'components/chips-input/chips-input';
diff --git a/src/views/run-process-panel/inputs/file-input.tsx b/src/views/run-process-panel/inputs/file-input.tsx
index a56d45f8..099a8128 100644
--- a/src/views/run-process-panel/inputs/file-input.tsx
+++ b/src/views/run-process-panel/inputs/file-input.tsx
@@ -18,7 +18,7 @@ import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projec
import { connect, DispatchProp } from 'react-redux';
import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { CollectionFile, CollectionFileType } from 'models/collection-file';
export interface FileInputProps {
@@ -141,5 +141,3 @@ const FileInputComponent = connect()(
}
});
-
-
diff --git a/src/views/run-process-panel/inputs/project-input.tsx b/src/views/run-process-panel/inputs/project-input.tsx
index 0c962ed8..9cf16c31 100644
--- a/src/views/run-process-panel/inputs/project-input.tsx
+++ b/src/views/run-process-panel/inputs/project-input.tsx
@@ -13,7 +13,7 @@ import { GenericInput, GenericInputProps } from './generic-input';
import { ProjectsTreePicker } from 'views-components/projects-tree-picker/projects-tree-picker';
import { initProjectsTreePicker } from 'store/tree-picker/tree-picker-actions';
import { TreeItem } from 'components/tree/tree';
-import { ProjectsTreePickerItem } from 'views-components/projects-tree-picker/generic-projects-tree-picker';
+import { ProjectsTreePickerItem } from 'store/tree-picker/tree-picker-middleware';
import { ProjectResource } from 'models/project';
import { ResourceKind } from 'models/resource';
import { RootState } from 'store/store';
commit 952c200514d8740b66377ab7d6860e3a0ef0497d
Author: Peter Amstutz <peter.amstutz at curii.com>
Date: Wed Dec 7 14:02:41 2022 -0500
19783: Add basic filters
Arvados-DCO-1.1-Signed-off-by: Peter Amstutz <peter.amstutz at curii.com>
diff --git a/src/store/tree-picker/tree-picker-actions.ts b/src/store/tree-picker/tree-picker-actions.ts
index 23f548cd..1c0b2dea 100644
--- a/src/store/tree-picker/tree-picker-actions.ts
+++ b/src/store/tree-picker/tree-picker-actions.ts
@@ -115,33 +115,59 @@ export const loadProject = (params: LoadProjectParams) =>
(fb: FilterBuilder) => includeCollections
? fb.addIsA('uuid', [ResourceKind.PROJECT, ResourceKind.COLLECTION])
: fb.addIsA('uuid', [ResourceKind.PROJECT]),
+ fb => fb.addNotIn("collections.properties.type", ["intermediate", "log"]),
fb => fb.getFilters(),
)(new FilterBuilder());
- const { items } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined });
+ const { items, itemsAvailable } = await services.groupsService.contents(loadShared ? '' : id, { filters, excludeHomeProject: loadShared || undefined, limit: 1000 });
+
+ if (itemsAvailable > 1000) {
+ items.push({
+ uuid: "more-items-available",
+ kind: ResourceKind.WORKFLOW,
+ name: "*** Not all items were loaded (limit 1000 items) ***",
+ description: "",
+ definition: "",
+ ownerUuid: "",
+ createdAt: "",
+ modifiedByClientUuid: "",
+ modifiedByUserUuid: "",
+ modifiedAt: "",
+ href: "",
+ etag: ""
+ });
+ }
+
dispatch<any>(receiveTreePickerData<GroupContentsResource>({
id,
pickerId,
data: items.filter((item) => {
- if (!includeFilterGroups && (item as GroupResource).groupClass && (item as GroupResource).groupClass === GroupClass.FILTER) {
- return false;
- }
+ if (!includeFilterGroups && (item as GroupResource).groupClass && (item as GroupResource).groupClass === GroupClass.FILTER) {
+ return false;
+ }
- if (options && options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as ProjectResource).frozenByUuid) {
- return false;
- }
+ if (options && options.showOnlyWritable && item.hasOwnProperty('frozenByUuid') && (item as ProjectResource).frozenByUuid) {
+ return false;
+ }
- return true;
- }),
- extractNodeData: item => ({
- id: item.uuid,
- value: item,
- status: item.kind === ResourceKind.PROJECT
- ? TreeNodeStatus.INITIAL
- : includeFiles
- ? TreeNodeStatus.INITIAL
- : TreeNodeStatus.LOADED
+ return true;
}),
+ extractNodeData: item => (
+ item.uuid === "more-items-available" ?
+ {
+ id: item.uuid,
+ value: item,
+ status: TreeNodeStatus.LOADED
+ }
+ : {
+ id: item.uuid,
+ value: item,
+ status: item.kind === ResourceKind.PROJECT
+ ? TreeNodeStatus.INITIAL
+ : includeFiles
+ ? TreeNodeStatus.INITIAL
+ : TreeNodeStatus.LOADED
+ }),
}));
};
@@ -188,7 +214,7 @@ export const initUserProject = (pickerId: string) =>
}));
}
};
-export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean } ) =>
+export const loadUserProject = (pickerId: string, includeCollections = false, includeFiles = false, options?: { showOnlyOwned: boolean, showOnlyWritable: boolean }) =>
async (dispatch: Dispatch<any>, getState: () => RootState, services: ServiceRepository) => {
const uuid = getUserUuid(getState());
if (uuid) {
-----------------------------------------------------------------------
hooks/post-receive
--
More information about the arvados-commits
mailing list