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';
declare global {
interface Window {
- __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
+ __REDUX_DEVTOOLS_EXTENSION_COMPOSE__?: typeof compose;
}
}
publicFavoritesMiddleware,
collectionsContentAddress,
subprocessMiddleware,
+ treePickerSearchMiddleware
];
const reduceMiddlewaresFn: (a: Middleware[],
router: routerReducer,
snackbar: snackbarReducer,
treePicker: treePickerReducer,
+ treePickerSearch: treePickerSearchReducer,
fileUploader: fileUploaderReducer,
processPanel: processPanelReducer,
progressIndicator: progressIndicatorReducer,
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';
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 }>(),
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`,
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;
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) {
//
// 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, {
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(
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
+ });
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";
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 => ({
}))((props: SearchBarClusterFieldProps) => <Field
name='cluster'
component={NativeSelectField as any}
- items={props.clusters}/>
+ items={props.clusters} />
);
export const SearchBarProjectField = () =>
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 {
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>;
// 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;
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>
<FavoritesTreePicker pickerId={favorites} {...p} />
</div>
</div>;
-};
+});
const getRelatedTreePickers = pipe(getProjectsTreePickerIds, values);
const disableActivation = [SHARED_PROJECT_ID, FAVORITES_PROJECT_ID];
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) =>
<Typography variant='caption' color='error'>
{props.meta.error}
</Typography>}
- </div>;
\ No newline at end of file
+ </div>;
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';
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';
}
});
-
-
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';
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 {
}
});
-
-
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';