Newer
Older
import React from 'react'
import PropTypes from 'prop-types'
import intl from 'react-intl-universal'
import { makeStyles } from '@material-ui/core/styles'
import { withRouter, Route, Redirect, Switch } from 'react-router-dom'
import useMediaQuery from '@material-ui/core/useMediaQuery'
import { MuiPickersUtilsProvider } from '@material-ui/pickers'
import moment from 'moment'
import MomentUtils from '@date-io/moment'
import 'moment/locale/fi'
import InfoHeader from '../components/main_layout/InfoHeader'
import TextPage from '../components/main_layout/TextPage'
import Message from '../components/main_layout/Message'
import Main from '../components/main_layout/Main'
import FacetBar from '../components/facet_bar/FacetBar'
// ** General components end **
// ** Portal specific components and configs **
import FacetedSearchPerspective from '../components/perspectives/sampo/FacetedSearchPerspective'
import FullTextSearch from '../components/perspectives/sampo/FullTextSearch'
import ClientFSPerspective from '../components/perspectives/sampo/client_fs/ClientFSPerspective'
import ClientFSMain from '../components/perspectives/sampo/client_fs/ClientFSMain'
import InstanceHomePage from '../components/perspectives/sampo/InstanceHomePage'
import Footer from '../components/perspectives/sampo/Footer'
import KnowledgeGraphMetadataTable from '../components/perspectives/sampo/KnowledgeGraphMetadataTable'
import { perspectiveConfig } from '../configs/sampo/PerspectiveConfig'
import { perspectiveConfigOnlyInfoPages } from '../configs/sampo/PerspectiveConfigOnlyInfoPages'
// ** Portal specific components and configs end **
fetchPaginatedResults,
esikkala
committed
clearFacet,
updatePage,
updateRowsPerPage,
showError,
updatePerspectiveHeaderExpanded,
animateMap,
clientFSToggleDataset,
clientFSFetchResults,
clientFSSortResults,
clientFSClearResults,
clientFSUpdateQuery,
clientFSUpdateFacet,
fetchKnowledgeGraphMetadata
import { filterResults } from '../selectors'
root: {
flexGrow: 1,
// Set app height for different screen sizes
height: 'auto',
[theme.breakpoints.up('md')]: {
},
/* Background color of the app.
In order to use both 'auto' and '100%' heights, bg-color
needs to be defined also in index.html (for #app and #root elements)
*/
backgroundColor: '#bdbdbd'
height: 'calc(100% - 64px)' // 100% - app bar - padding
mainContainerClientFS: {
height: 'auto',
[theme.breakpoints.up('md')]: {
height: 'calc(100% - 144px)' // 100% - app bar - padding * 2
},
[theme.breakpoints.down('sm')]: {
},
[theme.breakpoints.up('sm')]: {
marginTop: 72 // app bar + padding
}
},
paddingBottom: theme.spacing(1),
paddingLeft: theme.spacing(1),
paddingRight: theme.spacing(1),
[theme.breakpoints.up('md')]: {
height: 'calc(100% - 80px)'
},
[theme.breakpoints.down('sm')]: {
marginTop: 64 // app bar + padding
},
[theme.breakpoints.up('sm')]: {
marginTop: 72 // app bar + padding
}
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
},
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
},
[theme.breakpoints.up('sm')]: {
height: 'auto',
[theme.breakpoints.up('md')]: {
overflow: 'auto',
paddingTop: '0px !important',
paddingBottom: '0px !important'
},
facetBarContainerClientFS: {
height: 'auto',
[theme.breakpoints.down('md')]: {
marginBottom: theme.spacing(1)
},
paddingLeft: theme.spacing(0.5),
paddingRight: theme.spacing(0.5)
height: 'auto',
[theme.breakpoints.up('md')]: {
paddingBottom: '0px !important',
[theme.breakpoints.down('sm')]: {
marginTop: theme.spacing(1)
height: 800,
[theme.breakpoints.down('md')]: {
marginBottom: 8,
width: 'calc(100% - 2px)'
},
[theme.breakpoints.up('md')]: {
height: '100%'
},
paddingTop: '0px !important',
paddingBottom: '0px !important',
paddingRight: theme.spacing(0.5),
paddingLeft: theme.spacing(0.5)
// [theme.breakpoints.down('sm')]: {
// marginTop: theme.spacing(1)
// }
instancePageContainer: {
height: 'auto',
[theme.breakpoints.up('md')]: {
},
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
}
},
instancePageContainerHeaderExpanded: {
height: 'auto',
[theme.breakpoints.up('md')]: {
},
padding: theme.spacing(1),
[theme.breakpoints.down('sm')]: {
height: 'auto',
[theme.breakpoints.up('md')]: {
paddingTop: '0px !important',
paddingBottom: '0px !important'
}
/**
* A top-level container component, which connects all Sampo-UI components to the Redux store. Also
* the main routes of the portal are defined here using React Router. Currently it is not possible to
* render this component in Storybook.
*/
const { error } = props
const classes = useStyles(props)
const xsScreen = useMediaQuery(theme => theme.breakpoints.down('xs'))
const smScreen = useMediaQuery(theme => theme.breakpoints.between('sm', 'md'))
const mdScreen = useMediaQuery(theme => theme.breakpoints.between('md', 'lg'))
const lgScreen = useMediaQuery(theme => theme.breakpoints.between('lg', 'xl'))
const xlScreen = useMediaQuery(theme => theme.breakpoints.up('xl'))
let screenSize = ''
if (xsScreen) { screenSize = 'xs' }
if (smScreen) { screenSize = 'sm' }
if (mdScreen) { screenSize = 'md' }
if (lgScreen) { screenSize = 'lg' }
if (xlScreen) { screenSize = 'xl' }
const rootUrlWithLang = `${rootUrl}/${props.options.currentLocale}`
const noResults = props.clientFS.results == null
<MuiPickersUtilsProvider libInstance={moment} utils={MomentUtils} locale={props.options.currentLocale}>
<div className={classes.root}>
<div className={classes.appFrame}>
<Message error={error} />
<>
<TopBar
search={props.fullTextSearch}
fetchFullTextResults={props.fetchFullTextResults}
clearResults={props.clearResults}
perspectives={perspectiveConfig}
currentLocale={props.options.currentLocale}
availableLocales={props.options.availableLocales}
loadLocales={props.loadLocales}
xsScreen={xsScreen}
<Route exact path={`${rootUrl}/`}>
<Redirect to={rootUrlWithLang} />
</Route>
render={() =>
<Grid container spacing={1} className={classes.mainContainer}>
<Main
perspectives={perspectiveConfig}
screenSize={screenSize}
<Footer />
</Grid>}
/>
{/* https://stackoverflow.com/a/41024944 */}
<Route
if (typeof window.ga === 'function') {
window.ga('set', 'page', location.pathname + location.search)
window.ga('send', 'pageview')
}
return null
}}
/>
{/* route for full text search results */}
<Route
path={`${rootUrlWithLang}/full-text-search`}
render={routeProps =>
<Grid container spacing={1} className={classes.mainContainer}>
<Grid item xs={12} className={classes.resultsContainer}>
<FullTextSearch
fullTextSearch={props.fullTextSearch}
{/* routes for faceted search perspectives */}
if (!has(perspective, 'externalUrl') && perspective.id !== 'placesClientFS') {
render={routeProps => {
return (
<>
<InfoHeader
resultClass={perspective.id}
pageType='facetResults'
expanded={props[perspective.id].facetedSearchHeaderExpanded}
updateExpanded={props.updatePerspectiveHeaderExpanded}
descriptionHeight={perspective.perspectiveDescHeight}
/>
<Grid
container spacing={1} className={props[perspective.id].facetedSearchHeaderExpanded
? classes.perspectiveContainerHeaderExpanded
: classes.perspectiveContainer}
>
<Grid item xs={12} md={3} className={classes.facetBarContainer}>
<FacetBar
facetData={props[`${perspective.id}Facets`]}
facetDataConstrainSelf={has(props, `${perspective.id}FacetsConstrainSelf`)
? props[`${perspective.id}FacetsConstrainSelf`]
: null}
facetResults={props[`${perspective.id}`]}
facetClass={perspective.id}
resultClass={perspective.id}
fetchingResultCount={props[perspective.id].fetchingResultCount}
resultCount={props[perspective.id].resultCount}
fetchFacet={props.fetchFacet}
fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
esikkala
committed
clearFacet={props.clearFacet}
fetchResultCount={props.fetchResultCount}
updateFacetOption={props.updateFacetOption}
defaultActiveFacets={perspective.defaultActiveFacets}
/>
</Grid>
<Grid item xs={12} md={9} className={classes.resultsContainer}>
<FacetedSearchPerspective
facetResults={props[`${perspective.id}`]}
placesResults={props.places}
facetData={props[`${perspective.id}Facets`]}
facetDataConstrainSelf={has(props, `${perspective.id}FacetsConstrainSelf`)
? props[`${perspective.id}FacetsConstrainSelf`]
: null}
leafletMap={props.leafletMap}
fetchPaginatedResults={props.fetchPaginatedResults}
fetchResults={props.fetchResults}
fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
fetchGeoJSONLayers={props.fetchGeoJSONLayers}
fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
clearGeoJSONLayers={props.clearGeoJSONLayers}
fetchByURI={props.fetchByURI}
updatePage={props.updatePage}
updateRowsPerPage={props.updateRowsPerPage}
updateFacetOption={props.updateFacetOption}
sortResults={props.sortResults}
routeProps={routeProps}
perspective={perspective}
animationValue={props.animationValue}
animateMap={props.animateMap}
screenSize={screenSize}
rootUrl={rootUrlWithLang}
/>
<Switch>
<Redirect
from={`/${perspective.id}/page/:id`}
to={`${rootUrlWithLang}/${perspective.id}/page/:id`}
/>
<Route
path={`${rootUrlWithLang}/${perspective.id}/page/:id`}
render={routeProps => {
return (
<>
<InfoHeader
resultClass={perspective.id}
pageType='instancePage'
expanded={props[perspective.id].instancePageHeaderExpanded}
updateExpanded={props.updatePerspectiveHeaderExpanded}
descriptionHeight={perspective.perspectiveDescHeight}
/>
<Grid
container spacing={1} className={props[perspective.id].instancePageHeaderExpanded
? classes.instancePageContainerHeaderExpanded
: classes.instancePageContainer}
>
<Grid item xs={12} className={classes.instancePageContent}>
<InstanceHomePage
rootUrl={rootUrlWithLang}
fetchByURI={props.fetchByURI}
fetchResults={props.fetchResults}
tableData={props[perspective.id].instanceTableData}
tableExternalData={props[perspective.id].instancePageTableExternalData}
results={props[perspective.id].results}
resultUpdateID={props[perspective.id].resultUpdateID}
tabs={perspective.instancePageTabs}
sparqlQuery={props[perspective.id].instanceSparqlQuery}
isLoading={props[perspective.id].fetching}
routeProps={routeProps}
screenSize={screenSize}
fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
fetchGeoJSONLayers={props.fetchGeoJSONLayers}
fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
clearGeoJSONLayers={props.clearGeoJSONLayers}
leafletMap={props.leafletMap}
showError={props.showError}
{/* create routes for classes that have only info pages and no faceted search perspective */}
from={`${rootUrl}/${perspective.id}/page/:id`}
to={`${rootUrlWithLang}/${perspective.id}/page/:id`}
/>
<Route
path={`${rootUrlWithLang}/${perspective.id}/page/:id`}
render={routeProps => {
return (
<>
<InfoHeader
resultClass={perspective.id}
pageType='instancePage'
expanded={props[perspective.id].instancePageHeaderExpanded}
updateExpanded={props.updatePerspectiveHeaderExpanded}
descriptionHeight={perspective.perspectiveDescHeight}
/>
<Grid
container spacing={1} className={props[perspective.id].instancePageHeaderExpanded
? classes.instancePageContainerHeaderExpanded
: classes.instancePageContainer}
>
<Grid item xs={12} className={classes.instancePageContent}>
<InstanceHomePage
rootUrl={rootUrlWithLang}
fetchByURI={props.fetchByURI}
fetchResults={props.fetchResults}
tableData={props[perspective.id].instanceTableData}
tableExternalData={props[perspective.id].instancePageTableExternalData}
results={props[perspective.id].results}
resultUpdateID={props[perspective.id].resultUpdateID}
tabs={perspective.instancePageTabs}
sparqlQuery={props[perspective.id].instanceSparqlQuery}
isLoading={props[perspective.id].fetching}
routeProps={routeProps}
screenSize={screenSize}
fetchFacetConstrainSelf={props.fetchFacetConstrainSelf}
fetchGeoJSONLayers={props.fetchGeoJSONLayers}
fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
clearGeoJSONLayers={props.clearGeoJSONLayers}
leafletMap={props.leafletMap}
showError={props.showError}
render={routeProps =>
<Grid container className={classes.mainContainerClientFS}>
<Grid item sm={12} md={4} lg={3} className={classes.facetBarContainerClientFS}>
<FacetBar
facetedSearchMode='clientFS'
facetClass='clientFSPlaces'
resultClass='clientFSPlaces'
facetData={props.clientFS}
clientFSFacetValues={props.clientFSFacetValues}
fetchingResultCount={props.clientFS.textResultsFetching}
resultCount={noResults ? 0 : props.clientFS.results.length}
clientFS={props.clientFS}
clientFSToggleDataset={props.clientFSToggleDataset}
clientFSFetchResults={props.clientFSFetchResults}
clientFSClearResults={props.clientFSClearResults}
clientFSUpdateQuery={props.clientFSUpdateQuery}
clientFSUpdateFacet={props.clientFSUpdateFacet}
defaultActiveFacets={perspectiveConfig[4].defaultActiveFacets}
leafletMap={props.leafletMap}
updateMapBounds={props.updateMapBounds}
screenSize={screenSize}
showError={props.showError}
/>
</Grid>
<Grid item sm={12} md={8} lg={9} className={classes.resultsContainerClientFS}>
{noResults && <ClientFSMain />}
{!noResults &&
<ClientFSPerspective
routeProps={routeProps}
screenSize={screenSize}
clientFS={props.clientFS}
clientFSResults={props.clientFSResults}
clientFSSortResults={props.clientFSSortResults}
leafletMap={props.leafletMap}
fetchGeoJSONLayersBackend={props.fetchGeoJSONLayersBackend}
fetchGeoJSONLayers={props.fetchGeoJSONLayers}
clearGeoJSONLayers={props.clearGeoJSONLayers}
showError={props.showError}
/>}
</Grid>
</Grid>}
/>
render={() =>
<div className={classNames(classes.mainContainer, classes.textPageContainer)}>
<TextPage>{intl.getHTML('feedback')}</TextPage>
</div>}
render={() =>
<div className={classNames(classes.mainContainer, classes.textPageContainer)}>
<TextPage>
{intl.getHTML('aboutThePortalPartOne')}
<KnowledgeGraphMetadataTable
resultClass='perspective1KnowledgeGraphMetadata'
fetchKnowledgeGraphMetadata={props.fetchKnowledgeGraphMetadata}
knowledgeGraphMetadata={props.perspective1.knowledgeGraphMetadata}
/>
{intl.getHTML('aboutThePortalPartTwo')}
render={() =>
<div className={classNames(classes.mainContainer, classes.textPageContainer)}>
<TextPage>{intl.getHTML('instructions')}</TextPage>
</div>}
/>
</>
</div>
const mapStateToProps = state => {
const { clientFSResults, clientFSFacetValues } = filterResults(state.clientSideFacetedSearch)
perspective1: state.perspective1,
perspective1Facets: state.perspective1Facets,
perspective1FacetsConstrainSelf: state.perspective1FacetsConstrainSelf,
perspective2: state.perspective2,
perspective2Facets: state.perspective2Facets,
perspective2FacetsConstrainSelf: state.perspective2FacetsConstrainSelf,
perspective3: state.perspective3,
perspective3Facets: state.perspective3Facets,
perspective3FacetsConstrainSelf: state.perspective3FacetsConstrainSelf,
manuscripts: state.manuscripts,
works: state.works,
events: state.events,
actors: state.actors,
expressions: state.expressions,
collections: state.collections,
finds: state.finds,
findsFacets: state.findsFacets,
findsFacetsConstrainSelf: state.findsFacetsConstrainSelf,
emloActors: state.emloActors,
emloActorsFacets: state.emloActorsFacets,
emloActorsFacetsConstrainSelf: state.emloActorsFacetsConstrainSelf,
hellerau: state.hellerau,
hellerauFacets: state.hellerauFacets,
hellerauFacetsConstrainSelf: state.hellerauFacetsConstrainSelf,
clientFS: state.clientSideFacetedSearch,
clientFSResults,
clientFSFacetValues,
animationValue: state.animation.value,
const mapDispatchToProps = ({
fetchPaginatedResults,
esikkala
committed
clearFacet,
updatePage,
updateRowsPerPage,
animateMap,
clientFSToggleDataset,
clientFSFetchResults,
clientFSClearResults,
clientFSSortResults,
clientFSUpdateQuery,
clientFSUpdateFacet,
fetchKnowledgeGraphMetadata
/**
* General options considering the whole semantic portal, e.g. language.
*/
/**
* Errors shown with react-redux-toastr.
*/
/**
* Faceted search configs and results of 'Perspective 1'.
*/
/**
* Facet configs and values of 'Perspective 1'.
*/
perspective1Facets: PropTypes.object.isRequired,
/**
* Facet configs and values for facets that restrict themselves of 'Perspective 1'.
*/
perspective1FacetsConstrainSelf: PropTypes.object.isRequired,
/**
* Faceted search configs and results of 'Perspective 2'.
*/
/**
* Facet configs and values of 'Perspective 2'.
*/
perspective2Facets: PropTypes.object.isRequired,
/**
* Faceted search configs and results of 'Perspective 3'.
*/
/**
* Facet configs and values of 'Perspective 3'.
*/
perspective3Facets: PropTypes.object.isRequired,
/**
* Faceted search configs and results of 'Places'.
*/
/**
* Leaflet map config and external layers.
*/
leafletMap: PropTypes.object.isRequired,
/**
* State of the animation, used by TemporalMap.
*/
animationValue: PropTypes.array.isRequired,
/**
* Redux action for fetching all faceted search results.
*/
/**
* Redux action for fetching the total count faceted search results.
*/
/**
* Redux action for full text search results.
*/
fetchFullTextResults: PropTypes.func.isRequired,
/**
* Redux action for fetching paginated faceted search results.
*/
fetchPaginatedResults: PropTypes.func.isRequired,
/**
* Redux action for fetching information about a single entity.
*/
/**
* Redux action for loading external GeoJSON layers.
*/
/**
* Redux action for clearing external GeoJSON layers.
*/
clearGeoJSONLayers: PropTypes.func.isRequired,
/**
* Redux action for loading external GeoJSON layers via the backend.
* Useful when the API or similar needs to be hidden.
*/
fetchGeoJSONLayersBackend: PropTypes.func.isRequired,
/**
* Redux action for sorting the paginated results.
*/
sortResults: PropTypes.func.isRequired,
/**
* Redux action for clearing the full text results.
*/
/**
* Redux action for updating the page of paginated faceted search results.
*/
updatePage: PropTypes.func.isRequired,
/**
* Redux action for updating the rows per page of paginated faceted search results.
*/
updateRowsPerPage: PropTypes.func.isRequired,
/**
* Redux action for updating the active selection or config of a facet.
*/
updateFacetOption: PropTypes.func.isRequired,
/**
* Redux action for fetching the values of a facet.
*/
esikkala
committed
/**
* Redux action for resetting a facet.
*/
clearFacet: PropTypes.func.isRequired,
/**
* Redux action for displaying an error message.
*/
/**
* Redux action expanding and collapsing the header of perspective.
*/
updatePerspectiveHeaderExpanded: PropTypes.func.isRequired,
/**
* Redux action for updating the bounds of a Leaflet map.
*/
updateMapBounds: PropTypes.func.isRequired,
/**
* Redux action for loading translations from JavaScript objects.
*/
loadLocales: PropTypes.func.isRequired,
/**
* Redux action for animating TemporalMap.
*/
/**
* State for client-side faceted search.
*/
/**
* Redux action for updating the dataset selections in client-side faceted search.
*/
clientFSToggleDataset: PropTypes.func.isRequired,
/**
* Redux action for the fetching the initial result set in client-side faceted search.
*/
clientFSFetchResults: PropTypes.func.isRequired,
/**
* Redux action for the clearing the initial result set in client-side faceted search.
*/
clientFSClearResults: PropTypes.func.isRequired,
/**
* Redux action for sorting results in client-side faceted search.
*/
clientFSSortResults: PropTypes.func.isRequired,
/**
* Redux action for updating the initial query in client-side faceted search.
*/
clientFSUpdateQuery: PropTypes.func.isRequired,
/**
* Redux action for updating a facet in client-side faceted search.
*/
clientFSUpdateFacet: PropTypes.func.isRequired
export const SemanticPortalComponent = SemanticPortal
export default compose(
connect(
mapStateToProps,
mapDispatchToProps