-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #296 from performant-software/feature/basira291_se…
…arch_history BASIRA #291 - Session search history
- Loading branch information
Showing
13 changed files
with
285 additions
and
35 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.nav-bar.ui.menu { | ||
border-radius: 0; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,28 @@ | ||
// @flow | ||
|
||
import React from 'react'; | ||
import { Header, Menu } from 'semantic-ui-react'; | ||
import LinksMenu from './LinksMenu'; | ||
import './NavBar.css'; | ||
|
||
const NavBar = () => { | ||
return ( | ||
<Menu | ||
className='nav-bar' | ||
inverted | ||
size='large' | ||
> | ||
<Menu.Item> | ||
<Header | ||
content='BASIRA' | ||
inverted | ||
/> | ||
</Menu.Item> | ||
<LinksMenu | ||
position='right' | ||
/> | ||
</Menu> | ||
); | ||
}; | ||
|
||
export default NavBar; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,45 @@ | ||
// @flow | ||
|
||
import { useCallback, useEffect, useMemo } from 'react'; | ||
import { useCurrentRefinements, useSearchBox } from 'react-instantsearch-hooks-web'; | ||
import { useLocation } from 'react-router-dom'; | ||
import _ from 'underscore'; | ||
import SearchHistoryService from '../services/SearchHistory'; | ||
import useFacetLabels from '../hooks/FacetLabels'; | ||
|
||
const SearchHistory = () => { | ||
const currentRefinements = useCurrentRefinements(); | ||
const { query } = useSearchBox(); | ||
|
||
const location = useLocation(); | ||
const { getLabel } = useFacetLabels(); | ||
|
||
const { search: url } = location; | ||
|
||
/** | ||
* Retrieves the label and facet value from the current refinements. | ||
* | ||
* @type {function(*): *} | ||
*/ | ||
const transformItem = useCallback((item) => ( | ||
_.map(item.refinements, (r) => `${getLabel(r.attribute)}: ${r.label}`) | ||
), [getLabel]); | ||
|
||
/** | ||
* Transforms the list of items into an array of label/values. | ||
*/ | ||
const items = useMemo(() => _.flatten(_.map(currentRefinements.items, transformItem)), [currentRefinements]); | ||
|
||
/** | ||
* Saves the search whenever the URL is changed. | ||
*/ | ||
useEffect(() => { | ||
if (!(_.isEmpty(query) && _.isEmpty(items))) { | ||
SearchHistoryService.saveSearch({ url, query, items, created: Date.now() }); | ||
} | ||
}, [location.search]); | ||
|
||
return null; | ||
}; | ||
|
||
export default SearchHistory; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,3 @@ | ||
.search-history > .ui.container > .search-list > .table > tbody > tr > td:first-child { | ||
white-space: nowrap; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
// @flow | ||
|
||
import { CurrentFacetLabels, EmbeddedList } from '@performant-software/semantic-components'; | ||
import { useTimer } from '@performant-software/shared-components'; | ||
import React, { useCallback, useState } from 'react'; | ||
import { Link } from 'react-router-dom'; | ||
import { useTranslation } from 'react-i18next'; | ||
import { Button, Container, Popup } from 'semantic-ui-react'; | ||
import _ from 'underscore'; | ||
import { getDateTimeView } from '../utils/Date'; | ||
import NavBar from '../components/NavBar'; | ||
import SearchHistoryService from '../services/SearchHistory'; | ||
import SearchLink from '../components/SearchLink'; | ||
import './SearchHistory.css'; | ||
|
||
const TOOLTIP_DELAY_MS = 1500; | ||
|
||
const SearchHistory = () => { | ||
const [items, setItems] = useState(_.sortBy(SearchHistoryService.getHistory(), 'created').reverse()); | ||
const [copyItem, setCopyItem] = useState(); | ||
|
||
const { t } = useTranslation(); | ||
const { clearTimer, setTimer } = useTimer(); | ||
|
||
/** | ||
* Clears the search history. | ||
* | ||
* @type {(function(): void)|*} | ||
*/ | ||
const onClear = useCallback(() => { | ||
// Clear the items on the state | ||
setItems([]); | ||
|
||
// Clear the items in session storage | ||
SearchHistoryService.clearHistory(); | ||
}, []); | ||
|
||
/** | ||
* Copies the URL for the passed item and sets the selected item on the state. | ||
* | ||
* @type {(function(*): void)|*} | ||
*/ | ||
const onCopy = useCallback((item) => { | ||
const url = `${window.location.origin}/${item.url}`; | ||
navigator.clipboard.writeText(url); | ||
|
||
setCopyItem(item); | ||
|
||
clearTimer(); | ||
setTimer(() => setCopyItem(null), TOOLTIP_DELAY_MS); | ||
}, []); | ||
|
||
return ( | ||
<Container | ||
className='search-history' | ||
fluid | ||
> | ||
<NavBar /> | ||
<Container> | ||
<EmbeddedList | ||
className='search-list' | ||
actions={[{ | ||
as: Link, | ||
asProps: (item) => ({ | ||
to: `/${item.url}` | ||
}), | ||
icon: 'arrow alternate circle right outline', | ||
name: 'link' | ||
}, { | ||
name: 'copy', | ||
render: (item) => ( | ||
<Popup | ||
content={t('SearchHistory.messages.copy')} | ||
hoverable | ||
inverted | ||
on='click' | ||
onOpen={() => onCopy(item)} | ||
open={copyItem === item} | ||
trigger={( | ||
<Button | ||
basic | ||
icon='copy outline' | ||
size='small' | ||
/> | ||
)} | ||
/> | ||
) | ||
}]} | ||
buttons={[{ | ||
render: () => <SearchLink /> | ||
}, { | ||
color: 'red', | ||
content: t('SearchHistory.buttons.clear'), | ||
icon: 'trash', | ||
onClick: onClear | ||
}]} | ||
columns={[{ | ||
name: 'created', | ||
label: t('SearchHistory.columns.created'), | ||
resolve: (item) => getDateTimeView(item.created) | ||
}, { | ||
name: 'query', | ||
label: t('SearchHistory.columns.query') | ||
}, { | ||
name: 'items', | ||
label: t('SearchHistory.columns.facets'), | ||
render: (item) => ( | ||
<CurrentFacetLabels | ||
items={_.map(item.items, (label) => ({ label }))} | ||
/> | ||
) | ||
}]} | ||
defaultSort='created' | ||
defaultSortDirection='descending' | ||
items={items} | ||
/> | ||
</Container> | ||
</Container> | ||
); | ||
}; | ||
|
||
export default SearchHistory; |
Oops, something went wrong.