From 956da0a3141be31119f6310734a59f4b79c254e3 Mon Sep 17 00:00:00 2001 From: Pawel Kowalczyk Date: Mon, 15 Oct 2018 15:28:20 +0200 Subject: [PATCH] searching on text Feature #14277 Arvados-DCO-1.1-Signed-off-by: Pawel Kowalczyk --- src/routes/routes.ts | 2 +- src/store/navigation/navigation-action.ts | 4 +- src/store/search-bar/search-bar-actions.ts | 22 ++++++++++- .../search-results-middleware-service.ts | 15 +++---- src/store/store.ts | 6 +++ .../search-bar-autocomplete-view.tsx | 7 ++-- .../search-bar/search-bar-basic-view.tsx | 9 +++-- .../search-bar/search-bar-view.tsx | 39 +++++++++++++------ .../search-bar/search-bar.tsx | 7 ++-- .../search-results-panel-view.tsx | 2 +- .../search-results-panel.tsx | 15 +------ 11 files changed, 79 insertions(+), 49 deletions(-) diff --git a/src/routes/routes.ts b/src/routes/routes.ts index 34eea4f1..d5041976 100644 --- a/src/routes/routes.ts +++ b/src/routes/routes.ts @@ -19,7 +19,7 @@ export const Routes = { SHARED_WITH_ME: '/shared-with-me', RUN_PROCESS: '/run-process', WORKFLOWS: '/workflows', - SEARCH_RESULTS: '/q' + SEARCH_RESULTS: '/search' }; export const getResourceUrl = (uuid: string) => { diff --git a/src/store/navigation/navigation-action.ts b/src/store/navigation/navigation-action.ts index 48bd606e..b060afc4 100644 --- a/src/store/navigation/navigation-action.ts +++ b/src/store/navigation/navigation-action.ts @@ -56,4 +56,6 @@ export const navigateToRootProject = (dispatch: Dispatch, getState: () => RootSt export const navigateToSharedWithMe = push(Routes.SHARED_WITH_ME); -export const navigateToRunProcess = push(Routes.RUN_PROCESS); \ No newline at end of file +export const navigateToRunProcess = push(Routes.RUN_PROCESS); + +export const navigateToSearchResults = push(Routes.SEARCH_RESULTS); \ No newline at end of file diff --git a/src/store/search-bar/search-bar-actions.ts b/src/store/search-bar/search-bar-actions.ts index 2f30e02d..394920a5 100644 --- a/src/store/search-bar/search-bar-actions.ts +++ b/src/store/search-bar/search-bar-actions.ts @@ -11,6 +11,8 @@ import { FilterBuilder } from "~/services/api/filter-builder"; import { ResourceKind } from '~/models/resource'; import { GroupClass } from '~/models/group'; import { SearchView } from '~/store/search-bar/search-bar-reducer'; +import { navigateToSearchResults, navigateTo } from '~/store/navigation/navigation-action'; +import { snackbarActions, SnackbarKind } from '~/store/snackbar/snackbar-actions'; export const searchBarActions = unionize({ SET_CURRENT_VIEW: ofType(), @@ -54,6 +56,7 @@ export const saveQuery = (data: SearchBarAdvanceFormData) => if (data.saveQuery && data.searchQuery) { services.searchService.saveQuery(data.searchQuery); dispatch(searchBarActions.SET_SAVED_QUERIES(services.searchService.getSavedQueries())); + dispatch(snackbarActions.OPEN_SNACKBAR({ message: 'Query has been sucessfully saved', kind: SnackbarKind.SUCCESS })); } }; @@ -68,14 +71,28 @@ export const deleteSavedQuery = (id: number) => export const openSearchView = () => (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { dispatch(searchBarActions.OPEN_SEARCH_VIEW()); - dispatch(searchBarActions.SET_CURRENT_VIEW(SearchView.BASIC)); const savedSearchQueries = services.searchService.getSavedQueries(); dispatch(searchBarActions.SET_SAVED_QUERIES(savedSearchQueries)); }; +export const closeSearchView = () => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(searchBarActions.CLOSE_SEARCH_VIEW()); + dispatch(searchBarActions.SET_CURRENT_VIEW(SearchView.BASIC)); + }; +export const navigateToItem = (uuid: string) => + (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + dispatch(searchBarActions.CLOSE_SEARCH_VIEW()); + dispatch(navigateTo(uuid)); + }; + export const searchData = (searchValue: string) => async (dispatch: Dispatch, getState: () => RootState, services: ServiceRepository) => { + const currentView = getState().searchBar.currentView; + if (currentView !== SearchView.AUTOCOMPLETE) { + dispatch(searchBarActions.CLOSE_SEARCH_VIEW()); + } dispatch(searchBarActions.SET_SEARCH_VALUE(searchValue)); dispatch(searchBarActions.SET_SEARCH_RESULTS([])); if (searchValue) { @@ -87,9 +104,10 @@ export const searchData = (searchValue: string) => }); dispatch(searchBarActions.SET_SEARCH_RESULTS(items)); } + dispatch(navigateToSearchResults); }; -const getFilters = (filterName: string, searchValue: string): string => { +export const getFilters = (filterName: string, searchValue: string): string => { return new FilterBuilder() .addIsA("uuid", [ResourceKind.PROJECT, ResourceKind.COLLECTION, ResourceKind.PROCESS]) .addILike(filterName, searchValue, GroupContentsResourcePrefix.COLLECTION) diff --git a/src/store/search-results-panel/search-results-middleware-service.ts b/src/store/search-results-panel/search-results-middleware-service.ts index 2ae2dee3..924a0f09 100644 --- a/src/store/search-results-panel/search-results-middleware-service.ts +++ b/src/store/search-results-panel/search-results-middleware-service.ts @@ -16,6 +16,7 @@ import { OrderDirection, OrderBuilder } from '~/services/api/order-builder'; import { GroupContentsResource, GroupContentsResourcePrefix } from "~/services/groups-service/groups-service"; import { ListResults } from '~/services/common-service/common-resource-service'; import { searchResultsPanelActions } from '~/store/search-results-panel/search-results-panel-actions'; +import { getFilters } from '~/store/search-bar/search-bar-actions'; export class SearchResultsMiddlewareService extends DataExplorerMiddlewareService { constructor(private services: ServiceRepository, id: string) { @@ -26,8 +27,9 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic const state = api.getState(); const userUuid = state.auth.user!.uuid; const dataExplorer = getDataExplorer(state.dataExplorer, this.getId()); + const searchValue = state.searchBar.searchValue; try { - const response = await this.services.groupsService.contents(userUuid, getParams(dataExplorer)); + const response = await this.services.groupsService.contents(userUuid, getParams(dataExplorer, searchValue)); api.dispatch(updateResources(response.items)); api.dispatch(setItems(response)); } catch { @@ -36,18 +38,11 @@ export class SearchResultsMiddlewareService extends DataExplorerMiddlewareServic } } -export const getParams = (dataExplorer: DataExplorer) => ({ +export const getParams = (dataExplorer: DataExplorer, searchValue: string) => ({ ...dataExplorerToListParams(dataExplorer), - order: getOrder(dataExplorer), - filters: getFilters(dataExplorer) + filters: getFilters('name', searchValue) }); -export const getFilters = (dataExplorer: DataExplorer) => { - const filters = new FilterBuilder() - .addILike("name", dataExplorer.searchValue) - .getFilters(); - return filters; -}; export const getOrder = (dataExplorer: DataExplorer) => { const sortColumn = dataExplorer.columns.find(c => c.sortDirection !== SortDirection.NONE); diff --git a/src/store/store.ts b/src/store/store.ts index 064faeca..7a0d58f8 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -40,6 +40,8 @@ import { WorkflowMiddlewareService } from './workflow-panel/workflow-middleware- import { WORKFLOW_PANEL_ID } from './workflow-panel/workflow-panel-actions'; import { appInfoReducer } from '~/store/app-info/app-info-reducer'; import { searchBarReducer } from './search-bar/search-bar-reducer'; +import { SEARCH_RESULTS_PANEL_ID } from '~/store/search-results-panel/search-results-panel-actions'; +import { SearchResultsMiddlewareService } from './search-results-panel/search-results-middleware-service'; const composeEnhancers = (process.env.NODE_ENV === 'development' && @@ -62,6 +64,9 @@ export function configureStore(history: History, services: ServiceRepository): R const trashPanelMiddleware = dataExplorerMiddleware( new TrashPanelMiddlewareService(services, TRASH_PANEL_ID) ); + const searchResultsPanelMiddleware = dataExplorerMiddleware( + new SearchResultsMiddlewareService(services, SEARCH_RESULTS_PANEL_ID) + ); const sharedWithMePanelMiddleware = dataExplorerMiddleware( new SharedWithMeMiddlewareService(services, SHARED_WITH_ME_PANEL_ID) ); @@ -75,6 +80,7 @@ export function configureStore(history: History, services: ServiceRepository): R projectPanelMiddleware, favoritePanelMiddleware, trashPanelMiddleware, + searchResultsPanelMiddleware, sharedWithMePanelMiddleware, workflowPanelMiddleware ]; diff --git a/src/views-components/search-bar/search-bar-autocomplete-view.tsx b/src/views-components/search-bar/search-bar-autocomplete-view.tsx index 85294509..48172424 100644 --- a/src/views-components/search-bar/search-bar-autocomplete-view.tsx +++ b/src/views-components/search-bar/search-bar-autocomplete-view.tsx @@ -4,7 +4,7 @@ import * as React from 'react'; import { Paper, StyleRulesCallback, withStyles, WithStyles, List } from '@material-ui/core'; -import { RecentQueriesItem } from '~/views-components/search-bar/search-bar-view'; +import { RenderAutocompleteItems } from '~/views-components/search-bar/search-bar-view'; import { GroupContentsResource } from '~/services/groups-service/groups-service'; import Highlighter from "react-highlight-words"; @@ -24,16 +24,17 @@ const styles: StyleRulesCallback = theme => { export interface SearchBarAutocompleteViewDataProps { searchResults?: GroupContentsResource[]; searchValue?: string; + navigateTo: (uuid: string) => void; } type SearchBarAutocompleteViewProps = SearchBarAutocompleteViewDataProps & WithStyles; export const SearchBarAutocompleteView = withStyles(styles)( - ({ classes, searchResults, searchValue }: SearchBarAutocompleteViewProps) => + ({ classes, searchResults, searchValue, navigateTo }: SearchBarAutocompleteViewProps) => {searchResults && {searchResults.map((item: GroupContentsResource) => { - return ; + return ; })} } diff --git a/src/views-components/search-bar/search-bar-basic-view.tsx b/src/views-components/search-bar/search-bar-basic-view.tsx index a191b2ee..97a5d3e3 100644 --- a/src/views-components/search-bar/search-bar-basic-view.tsx +++ b/src/views-components/search-bar/search-bar-basic-view.tsx @@ -5,7 +5,7 @@ import * as React from 'react'; import { Paper, StyleRulesCallback, withStyles, WithStyles, List } from '@material-ui/core'; import { SearchView } from '~/store/search-bar/search-bar-reducer'; -import { RecentQueriesItem, RenderSavedQueries } from '~/views-components/search-bar/search-bar-view'; +import { RenderRecentQueries, RenderSavedQueries } from '~/views-components/search-bar/search-bar-view'; type CssRules = 'advanced' | 'searchQueryList' | 'list' | 'searchView'; @@ -40,18 +40,19 @@ interface SearchBarBasicViewProps { recentQueries: () => string[]; deleteSavedQuery: (id: number) => void; savedQueries: string[]; + onSearch: (searchValue: string) => void; } export const SearchBarBasicView = withStyles(styles)( - ({ classes, setView, recentQueries, deleteSavedQuery, savedQueries }: SearchBarBasicViewProps & WithStyles) => + ({ classes, setView, recentQueries, deleteSavedQuery, savedQueries, onSearch }: SearchBarBasicViewProps & WithStyles) =>
Recent search queries
- {recentQueries().map((query, index) => )} + {recentQueries().map((query, index) => )}
Saved search queries
- {savedQueries.map((query, index) => )} + {savedQueries.map((query, index) => )}
setView(SearchView.ADVANCED)}>Advanced search
diff --git a/src/views-components/search-bar/search-bar-view.tsx b/src/views-components/search-bar/search-bar-view.tsx index f394084c..51062646 100644 --- a/src/views-components/search-bar/search-bar-view.tsx +++ b/src/views-components/search-bar/search-bar-view.tsx @@ -66,6 +66,7 @@ interface SearchBarActionProps { saveQuery: (data: SearchBarAdvanceFormData) => void; deleteSavedQuery: (id: number) => void; openSearchView: () => void; + navigateTo: (uuid: string) => void; } type SearchBarProps = SearchBarDataProps & SearchBarActionProps & WithStyles; @@ -74,26 +75,41 @@ interface SearchBarState { value: string; } -interface RenderSavedQueriesProps { + + +interface RenderRecentQueriesProps { text: string | JSX.Element; - id: number; - deleteSavedQuery: (id: number) => void; + onSearch: (searchValue: string | JSX.Element) => void; } -interface RenderRecentQueriesProps { +export const RenderRecentQueries = (props: RenderRecentQueriesProps) => { + return + props.onSearch(props.text)} /> + ; +}; + +interface RenderAutocompleteItemsProps { text: string | JSX.Element; + navigateTo: (uuid: string) => void; + uuid: string; } -export const RecentQueriesItem = (props: RenderRecentQueriesProps) => { +export const RenderAutocompleteItems = (props: RenderAutocompleteItemsProps) => { return - + props.navigateTo(props.uuid)} /> ; }; +interface RenderSavedQueriesProps { + text: string | JSX.Element; + id: number; + deleteSavedQuery: (id: number) => void; + onSearch: (searchValue: string | JSX.Element) => void; +} export const RenderSavedQueries = (props: RenderSavedQueriesProps) => { return - + props.onSearch(props.text)} /> props.deleteSavedQuery(props.id)}> @@ -159,18 +175,19 @@ export const SearchBarView = withStyles(styles)( } getView = (currentView: string) => { - const { onSetView, loadRecentQueries, savedQueries, deleteSavedQuery, searchValue, searchResults, saveQuery } = this.props; + const { onSetView, loadRecentQueries, savedQueries, deleteSavedQuery, searchValue, searchResults, saveQuery, onSearch, navigateTo } = this.props; switch (currentView) { case SearchView.BASIC: - return ; + return ; case SearchView.ADVANCED: - return ; + return ; case SearchView.AUTOCOMPLETE: return ; default: - return ; + return ; } } diff --git a/src/views-components/search-bar/search-bar.tsx b/src/views-components/search-bar/search-bar.tsx index 8dcaaf84..5c42af20 100644 --- a/src/views-components/search-bar/search-bar.tsx +++ b/src/views-components/search-bar/search-bar.tsx @@ -8,7 +8,6 @@ import { Dispatch } from 'redux'; import { goToView, searchData, - searchBarActions, deleteSavedQuery, saveRecentQuery, loadRecentQueries, @@ -17,6 +16,7 @@ import { } from '~/store/search-bar/search-bar-actions'; import { SearchBarView } from '~/views-components/search-bar/search-bar-view'; import { SearchBarAdvanceFormData } from '~/store/search-bar/search-bar-actions'; +import { closeSearchView, navigateToItem } from '~/store/search-bar/search-bar-actions'; const mapStateToProps = ({ searchBar }: RootState) => { return { @@ -31,12 +31,13 @@ const mapStateToProps = ({ searchBar }: RootState) => { const mapDispatchToProps = (dispatch: Dispatch) => ({ onSearch: (valueSearch: string) => dispatch(searchData(valueSearch)), onSetView: (currentView: string) => dispatch(goToView(currentView)), - closeView: () => dispatch(searchBarActions.CLOSE_SEARCH_VIEW()), + closeView: () => dispatch(closeSearchView()), saveRecentQuery: (query: string) => dispatch(saveRecentQuery(query)), loadRecentQueries: () => dispatch(loadRecentQueries()), saveQuery: (data: SearchBarAdvanceFormData) => dispatch(saveQuery(data)), deleteSavedQuery: (id: number) => dispatch(deleteSavedQuery(id)), - openSearchView: () => dispatch(openSearchView()) + openSearchView: () => dispatch(openSearchView()), + navigateTo: (uuid: string) => dispatch(navigateToItem(uuid)) }); export const SearchBar = connect(mapStateToProps, mapDispatchToProps)(SearchBarView); \ No newline at end of file diff --git a/src/views/search-results-panel/search-results-panel-view.tsx b/src/views/search-results-panel/search-results-panel-view.tsx index 2f1f8898..a31263ab 100644 --- a/src/views/search-results-panel/search-results-panel-view.tsx +++ b/src/views/search-results-panel/search-results-panel-view.tsx @@ -62,7 +62,7 @@ export const searchResultsPanelColumns: DataColumns selected: true, configurable: true, filters: [], - render: uuid => + render: uuid => }, { name: SearchResultsPanelColumnNames.STATUS, diff --git a/src/views/search-results-panel/search-results-panel.tsx b/src/views/search-results-panel/search-results-panel.tsx index 8b01ca3f..fd322d75 100644 --- a/src/views/search-results-panel/search-results-panel.tsx +++ b/src/views/search-results-panel/search-results-panel.tsx @@ -5,23 +5,12 @@ import { Dispatch } from "redux"; import { connect } from "react-redux"; import { navigateTo } from '~/store/navigation/navigation-action'; -import { SearchResultsPanelActionProps, SearchResultsPanelDataProps } from './search-results-panel-view'; -import { RootState } from '~/store/store'; +import { SearchResultsPanelActionProps } from './search-results-panel-view'; import { openContextMenu, resourceKindToContextMenuKind } from '~/store/context-menu/context-menu-actions'; import { ResourceKind } from '~/models/resource'; import { loadDetailsPanel } from '~/store/details-panel/details-panel-action'; import { SearchResultsPanelView } from '~/views/search-results-panel/search-results-panel-view'; -const mapStateToProps = (state: RootState): SearchResultsPanelDataProps => ({ - data: { - inTrash: false, - dataFrom: '', - dataTo: '', - saveQuery: false, - searchQuery: '' - } -}); - const mapDispatchToProps = (dispatch: Dispatch): SearchResultsPanelActionProps => ({ onContextMenu: (event, resourceUuid) => { const kind = resourceKindToContextMenuKind(resourceUuid); @@ -44,4 +33,4 @@ const mapDispatchToProps = (dispatch: Dispatch): SearchResultsPanelActionProps = } }); -export const SearchResultsPanel = connect(mapStateToProps, mapDispatchToProps)(SearchResultsPanelView); \ No newline at end of file +export const SearchResultsPanel = connect(null, mapDispatchToProps)(SearchResultsPanelView); \ No newline at end of file -- 2.30.2