diff --git a/src/client/components/main_layout/TopBar.js b/src/client/components/main_layout/TopBar.js index ba27a133346e61b7e5bec8a32bfa11e5ed5a9abc..2fcb7529c48fffeffd2054212f11f40550815d75 100644 --- a/src/client/components/main_layout/TopBar.js +++ b/src/client/components/main_layout/TopBar.js @@ -86,19 +86,21 @@ const useStyles = makeStyles(theme => ({ mainLogoButtonLabel: { justifyContent: 'left' }, - mainLogoTypography: { + mainLogoTypography: props => ({ // set color and background explicitly to keep Google Lighthouse happy color: '#fff', background: theme.palette.primary.main, whiteSpace: 'nowrap', + textTransform: props.layoutConfig.topBar.logoTextTransform, [theme.breakpoints.down('sm')]: { - fontSize: '1.25rem', - fontWeight: 600 - } - // [theme.breakpoints.down('xs')]: { - // display: 'none' - // } - }, + fontSize: '1.5rem' + }, + ...(props.layoutConfig.topBar.hideLogoTextOnMobile && { + [theme.breakpoints.down('xs')]: { + display: 'none' + } + }) + }), mobileMenuButton: { padding: 12 } @@ -111,18 +113,34 @@ const useStyles = makeStyles(theme => ({ const TopBar = props => { const [mobileMoreAnchorEl, setMobileMoreAnchorEl] = React.useState(null) const isMobileMenuOpen = Boolean(mobileMoreAnchorEl) - const { perspectives, currentLocale, availableLocales, rootUrl } = props + const { perspectives, currentLocale, availableLocales, rootUrl, layoutConfig } = props + const { topBar } = layoutConfig const classes = useStyles(props) const handleMobileMenuOpen = event => setMobileMoreAnchorEl(event.currentTarget) const handleMobileMenuClose = () => setMobileMoreAnchorEl(null) const clientFSMode = props.location.pathname.indexOf('clientFS') !== -1 + let showSearchField = true + if (has(layoutConfig.topBar, 'showSearchField')) { + showSearchField = layoutConfig.topBar.showSearchField + } // https://material-ui.com/components/buttons/#third-party-routing-library const AdapterLink = React.forwardRef((props, ref) => <Link innerRef={ref} {...props} />) const AdapterNavLink = React.forwardRef((props, ref) => <NavLink innerRef={ref} {...props} />) + const getInternalLink = perspective => { + const searchMode = has(perspective, 'searchMode') ? perspective.searchMode : 'faceted-search' + let link = null + if (searchMode === 'dummy-internal') { + link = `${props.rootUrl}${perspective.internalLink}` + } + if (searchMode !== 'dummy-internal') { + link = `${props.rootUrl}/${perspective.id}/${searchMode}` + } + return link + } + const renderMobileMenuItem = perspective => { - const { searchMode } = perspective if (has(perspective, 'externalUrl')) { return ( <a @@ -144,7 +162,7 @@ const TopBar = props => { <MenuItem key={perspective.id} component={AdapterLink} - to={`${props.rootUrl}/${perspective.id}/${searchMode}`} + to={getInternalLink(perspective)} onClick={handleMobileMenuClose} > {intl.get(`perspectives.${perspective.id}.label`).toUpperCase()} @@ -154,7 +172,6 @@ const TopBar = props => { } const renderDesktopTopMenuItem = perspective => { - const { searchMode } = perspective if (has(perspective, 'externalUrl')) { return ( <a @@ -179,7 +196,7 @@ const TopBar = props => { key={perspective.id} className={classes.appBarButton} component={AdapterNavLink} - to={`${props.rootUrl}/${perspective.id}/${searchMode}`} + to={getInternalLink(perspective)} isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/${perspective.id}`)} activeClassName={classes.appBarButtonActive} > @@ -189,58 +206,73 @@ const TopBar = props => { } } - const renderMobileMenu = perspectives => - <Menu - anchorEl={mobileMoreAnchorEl} - anchorOrigin={{ vertical: 'top', horizontal: 'right' }} - transformOrigin={{ vertical: 'top', horizontal: 'right' }} - open={isMobileMenuOpen} - onClose={handleMobileMenuClose} - > - {perspectives.map(perspective => perspective.hideTopPerspectiveButton ? null : renderMobileMenuItem(perspective))} - <Divider /> - {renderMobileMenuItem({ - id: 'feedback', - externalUrl: props.layoutConfig.topBar.feedbackLink, - label: intl.get('topBar.feedback') - })} - {/* <MenuItem - key='feedback' - component={AdapterLink} - to={`${props.rootUrl}/feedback`} - onClick={handleMobileMenuClose} - > - {intl.get('topBar.feedback').toUpperCase()} - </MenuItem> */} - <MenuItem - key={0} - component={AdapterLink} - to={`${props.rootUrl}/about`} - onClick={handleMobileMenuClose} - > - {intl.get('topBar.info.aboutThePortal').toUpperCase()} - </MenuItem> - <a - className={classes.link} - key={1} - href={intl.get('topBar.info.blogUrl')} - target='_blank' - rel='noopener noreferrer' - onClick={handleMobileMenuClose} - > - <MenuItem> - {intl.get('topBar.info.blog').toUpperCase()} + const renderInfoItem = item => { + let jsx + if (item.externalLink) { + jsx = ( + <a + className={classes.link} + key={item.id} + href={intl.get(`topBar.info.${item.translatedUrl}`)} + target='_blank' + rel='noopener noreferrer' + > + <MenuItem onClick={handleMobileMenuClose}> + {intl.get(`topBar.info.${item.translatedText}`).toUpperCase()} + </MenuItem> + </a> + ) + } else { + jsx = ( + <MenuItem + key={item.id} + component={AdapterLink} + to={`${props.rootUrl}${item.internalLink}`} + onClick={handleMobileMenuClose} + > + {intl.get(`topBar.info.${item.translatedText}`).toUpperCase()} </MenuItem> - </a> - <MenuItem - key='info' - component={AdapterLink} - to={`${props.rootUrl}/instructions`} - onClick={handleMobileMenuClose} + ) + } + return jsx + } + + const renderMobileMenu = perspectives => { + const { infoDropdown } = props.layoutConfig.topBar + return ( + <Menu + anchorEl={mobileMoreAnchorEl} + anchorOrigin={{ vertical: 'top', horizontal: 'right' }} + transformOrigin={{ vertical: 'top', horizontal: 'right' }} + open={isMobileMenuOpen} + onClose={handleMobileMenuClose} > - {intl.get('topBar.instructions').toUpperCase()} - </MenuItem> - </Menu> + {perspectives.map(perspective => perspective.hideTopPerspectiveButton ? null : renderMobileMenuItem(perspective))} + <Divider /> + {renderMobileMenuItem({ + id: 'feedback', + externalUrl: props.layoutConfig.topBar.feedbackLink, + label: intl.get('topBar.feedback') + })} + {infoDropdown.map(item => renderInfoItem(item))} + {topBar.externalInstructions && renderMobileMenuItem({ + id: 'instructions', + externalUrl: intl.get('topBar.instructionsUrl'), + label: intl.get('topBar.instructions') + })} + {!topBar.externalInstructions && + <Button + className={classes.appBarButton} + component={AdapterNavLink} + to={`${props.rootUrl}/instructions`} + isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/instructions`)} + activeClassName={classes.appBarButtonActive} + > + {intl.get('topBar.instructions')} + </Button>} + </Menu> + ) + } return ( <div className={classes.root}> @@ -257,12 +289,17 @@ const TopBar = props => { }} onClick={() => clientFSMode ? props.clientFSClearResults() : null} > - {/* <img className={classes.mainLogo} src={} /> */} - <Typography className={classes.mainLogoTypography} variant='h6'> + {topBar.logoImage && + <img + className={classes.mainLogo} + src={topBar.logoImage} + alt={`${intl.get('appTitle.short')} logo`} + />} + <Typography className={classes.mainLogoTypography} variant='h5'> {props.xsScreen ? intl.get('appTitle.mobile') : intl.get('appTitle.short')} </Typography> </Button> - {!clientFSMode && + {showSearchField && <TopBarSearchField fetchFullTextResults={props.fetchFullTextResults} clearResults={props.clearResults} @@ -278,16 +315,22 @@ const TopBar = props => { externalUrl: props.layoutConfig.topBar.feedbackLink, label: intl.get('topBar.feedback') })} - <TopBarInfoButton rootUrl={props.rootUrl} /> - <Button - className={classes.appBarButton} - component={AdapterNavLink} - to={`${props.rootUrl}/instructions`} - isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/instructions`)} - activeClassName={classes.appBarButtonActive} - > - {intl.get('topBar.instructions')} - </Button> + <TopBarInfoButton rootUrl={props.rootUrl} layoutConfig={layoutConfig} /> + {topBar.externalInstructions && renderDesktopTopMenuItem({ + id: 'instructions', + externalUrl: intl.get('topBar.instructionsUrl'), + label: intl.get('topBar.instructions') + })} + {!topBar.externalInstructions && + <Button + className={classes.appBarButton} + component={AdapterNavLink} + to={`${props.rootUrl}/instructions`} + isActive={(match, location) => location.pathname.startsWith(`${props.rootUrl}/instructions`)} + activeClassName={classes.appBarButtonActive} + > + {intl.get('topBar.instructions')} + </Button>} {props.layoutConfig.topBar.showLanguageButton && <TopBarLanguageButton currentLocale={currentLocale} diff --git a/src/client/reducers/index.js b/src/client/reducers/index.js index b74750b1358fdaa1284d535f3433b06bb045656e..4092b58d09c09240383b536f55c371342534a284 100644 --- a/src/client/reducers/index.js +++ b/src/client/reducers/index.js @@ -10,6 +10,7 @@ import { createFullTextSearchReducer } from './general/fullTextSearch' import error from './general/error' import options from './general/options' import animation from './general/animation' +import videoPlayer from './general/videoPlayer' import leafletMap from './general/leafletMap' import { resultsInitialState, @@ -21,6 +22,7 @@ import { const reducers = { leafletMap, animation, + videoPlayer, options, error, toastr: toastrReducer @@ -65,7 +67,11 @@ for (const perspective of perspectiveConfig) { reducers[perspectiveID] = fullTextSearchReducer } else if (perspective.searchMode && perspective.searchMode === 'faceted-search') { const { resultClasses, properties, facets, maps } = perspective - const { paginatedResultsConfig } = resultClasses[perspectiveID] + const { paginatedResultsConfig, instanceConfig } = resultClasses[perspectiveID] + let instancePageResultClasses = {} + if (instanceConfig && instanceConfig.instancePageResultClasses) { + instancePageResultClasses = instanceConfig.instancePageResultClasses + } const resultsInitialStateFull = { ...resultsInitialState, ...paginatedResultsConfig, @@ -77,7 +83,9 @@ for (const perspective of perspectiveConfig) { ...facetsInitialState, facets } - const resultsReducer = createResultsReducer(resultsInitialStateFull, new Set(Object.keys(resultClasses))) + const resultsReducer = createResultsReducer( + resultsInitialStateFull, + new Set(Object.keys({ ...resultClasses, ...instancePageResultClasses }))) const facetsReducer = createFacetsReducer(facetsInitialStateFull, perspectiveID) const facetsConstrainSelfReducer = createFacetsConstrainSelfReducer(facetsInitialStateFull, perspectiveID) reducers[perspectiveID] = resultsReducer diff --git a/src/server/sparql/Utils.js b/src/server/sparql/Utils.js index 7a90c123e8344cacd0aac863513dcf20e88fd6e4..00639c28e9e300b903a3ce7912e336c3c8205e70 100644 --- a/src/server/sparql/Utils.js +++ b/src/server/sparql/Utils.js @@ -1,13 +1,16 @@ import { readFile } from 'fs/promises' import { has } from 'lodash' -// import { backendSearchConfig as oldBackendSearchConfig } from './findsampo/BackendSearchConfig' -// import { findsPerspectiveConfig } from './findsampo/perspective_configs/FindsPerspectiveConfig' -// import { typesPerspectiveConfig } from './perspective_configs/TypesPerspectiveConfig' -// import { periodsPerspectiveConfig } from './perspective_configs/PeriodsPerspectiveConfig' + +// import { backendSearchConfig as oldBackendSearchConfig } from './veterans/BackendSearchConfig' + +// import { videosConfig } from './veterans/perspective_configs/VideosConfig' +// import { clipsConfig } from './veterans/perspective_configs/ClipsConfig' +// import { entitiesConfig } from './veterans/perspective_configs/EntitiesConfig' // import { coinsPerspectiveConfig } from './perspective_configs/CoinsPerspectiveConfig' -// import { INITIAL_STATE } from '../../client/reducers/findsampo/findsFacets' -// import { INITIAL_STATE } from '../../client/reducers/findsampo/finds' +// import { INITIAL_STATE } from '../../client/reducers/veterans/videosFacets' +// import { INITIAL_STATE } from '../../client/reducers/veterans/clipsFacets' +// import { INITIAL_STATE } from '../../client/reducers/veterans/entitiesFacets' export const createBackendSearchConfig = async () => { const portalConfigJSON = await readFile('src/configs/portalConfig.json') @@ -31,48 +34,31 @@ export const createBackendSearchConfig = async () => { // handle default resultClass which is same as perspectiveID const { paginatedResultsConfig, instanceConfig } = perspectiveConfig.resultClasses[perspectiveID] const paginatedResultsPropertiesQueryBlockID = paginatedResultsConfig.propertiesQueryBlock - const instancePagePropertiesQueryBlockID = instanceConfig.propertiesQueryBlock const paginatedResultsPropertiesQueryBlock = sparqlQueries[paginatedResultsPropertiesQueryBlockID] - const instancePagePropertiesQueryBlock = sparqlQueries[instancePagePropertiesQueryBlockID] paginatedResultsConfig.propertiesQueryBlock = paginatedResultsPropertiesQueryBlock - instanceConfig.propertiesQueryBlock = instancePagePropertiesQueryBlock - if (has(instanceConfig, 'instancePageResultClasses')) { - for (const instancePageResultClass in instanceConfig.instancePageResultClasses) { - const instancePageResultClassConfig = instanceConfig.instancePageResultClasses[instancePageResultClass] - if (instancePageResultClassConfig.sparqlQuery) { - instancePageResultClassConfig.sparqlQuery = sparqlQueries[instancePageResultClassConfig.sparqlQuery] - } - if (instancePageResultClassConfig.sparqlQueryNodes) { - instancePageResultClassConfig.sparqlQueryNodes = sparqlQueries[instancePageResultClassConfig.sparqlQueryNodes] + if (paginatedResultsConfig.postprocess) { + paginatedResultsConfig.postprocess.func = resultMappers[paginatedResultsConfig.postprocess.func] + } + if (instanceConfig) { + const instancePagePropertiesQueryBlockID = instanceConfig.propertiesQueryBlock + const instancePagePropertiesQueryBlock = sparqlQueries[instancePagePropertiesQueryBlockID] + instanceConfig.propertiesQueryBlock = instancePagePropertiesQueryBlock + if (instanceConfig.postprocess) { + instanceConfig.postprocess.func = resultMappers[instanceConfig.postprocess.func] + } + if (has(instanceConfig, 'instancePageResultClasses')) { + for (const instancePageResultClass in instanceConfig.instancePageResultClasses) { + const instancePageResultClassConfig = instanceConfig.instancePageResultClasses[instancePageResultClass] + processResultClassConfig(instancePageResultClassConfig, sparqlQueries, resultMappers) } + hasInstancePageResultClasses = true } - hasInstancePageResultClasses = true } // handle other resultClasses for (const resultClass in perspectiveConfig.resultClasses) { if (resultClass === perspectiveID) { continue } const resultClassConfig = perspectiveConfig.resultClasses[resultClass] - if (resultClassConfig.sparqlQuery) { - resultClassConfig.sparqlQuery = sparqlQueries[resultClassConfig.sparqlQuery] - } - if (resultClassConfig.sparqlQueryNodes) { - resultClassConfig.sparqlQueryNodes = sparqlQueries[resultClassConfig.sparqlQueryNodes] - } - if (resultClassConfig.instanceConfig) { - const { instanceConfig } = resultClassConfig - if (instanceConfig.propertiesQueryBlock) { - instanceConfig.propertiesQueryBlock = sparqlQueries[instanceConfig.propertiesQueryBlock] - } - if (instanceConfig.relatedInstances) { - instanceConfig.relatedInstances = sparqlQueries[instanceConfig.relatedInstances] - } - } - if (resultClassConfig.resultMapper) { - resultClassConfig.resultMapper = resultMappers[resultClassConfig.resultMapper] - } - if (resultClassConfig.postprocess) { - resultClassConfig.postprocess.func = resultMappers[resultClassConfig.postprocess.func] - } + processResultClassConfig(resultClassConfig, sparqlQueries, resultMappers) } // merge facet results and instance page result classes if (hasInstancePageResultClasses) { @@ -103,16 +89,14 @@ export const createBackendSearchConfig = async () => { const instancePagePropertiesQueryBlockID = instanceConfig.propertiesQueryBlock const instancePagePropertiesQueryBlock = sparqlQueries[instancePagePropertiesQueryBlockID] instanceConfig.propertiesQueryBlock = instancePagePropertiesQueryBlock + if (instanceConfig.postprocess) { + instanceConfig.postprocess.func = resultMappers[instanceConfig.postprocess.func] + } let hasInstancePageResultClasses = false if (has(instanceConfig, 'instancePageResultClasses')) { for (const instancePageResultClass in instanceConfig.instancePageResultClasses) { const instancePageResultClassConfig = instanceConfig.instancePageResultClasses[instancePageResultClass] - if (instancePageResultClassConfig.sparqlQuery) { - instancePageResultClassConfig.sparqlQuery = sparqlQueries[instancePageResultClassConfig.sparqlQuery] - } - if (instancePageResultClassConfig.sparqlQueryNodes) { - instancePageResultClassConfig.sparqlQueryNodes = sparqlQueries[instancePageResultClassConfig.sparqlQueryNodes] - } + processResultClassConfig(instancePageResultClassConfig, sparqlQueries, resultMappers) } hasInstancePageResultClasses = true } @@ -131,6 +115,30 @@ export const createBackendSearchConfig = async () => { return backendSearchConfig } +const processResultClassConfig = (resultClassConfig, sparqlQueries, resultMappers) => { + if (resultClassConfig.sparqlQuery) { + resultClassConfig.sparqlQuery = sparqlQueries[resultClassConfig.sparqlQuery] + } + if (resultClassConfig.sparqlQueryNodes) { + resultClassConfig.sparqlQueryNodes = sparqlQueries[resultClassConfig.sparqlQueryNodes] + } + if (resultClassConfig.instanceConfig) { + const { instanceConfig } = resultClassConfig + if (instanceConfig.propertiesQueryBlock) { + instanceConfig.propertiesQueryBlock = sparqlQueries[instanceConfig.propertiesQueryBlock] + } + if (instanceConfig.relatedInstances) { + instanceConfig.relatedInstances = sparqlQueries[instanceConfig.relatedInstances] + } + } + if (resultClassConfig.resultMapper) { + resultClassConfig.resultMapper = resultMappers[resultClassConfig.resultMapper] + } + if (resultClassConfig.postprocess) { + resultClassConfig.postprocess.func = resultMappers[resultClassConfig.postprocess.func] + } +} + export const mergeFacetConfigs = (clientFacets, serverFacets) => { const mergedFacets = {} for (const clientFacetID in clientFacets) { @@ -197,6 +205,9 @@ export const mergeFacetConfigs = (clientFacets, serverFacets) => { if (serverFacet.facetValueFilter && serverFacet.facetValueFilter !== '') { mergedFacet.facetValueFilter = serverFacet.facetValueFilter } + if (serverFacet.facetLabelFilter && serverFacet.facetLabelFilter !== '') { + mergedFacet.facetLabelFilter = serverFacet.facetLabelFilter + } if (has(serverFacet, 'literal')) { mergedFacet.literal = serverFacet.literal } @@ -210,6 +221,9 @@ export const mergeFacetConfigs = (clientFacets, serverFacets) => { if (serverFacet.facetValueFilter && serverFacet.facetValueFilter !== '') { mergedFacet.facetValueFilter = serverFacet.facetValueFilter } + if (serverFacet.facetLabelFilter && serverFacet.facetLabelFilter !== '') { + mergedFacet.facetLabelFilter = serverFacet.facetLabelFilter + } mergedFacet.facetType = 'hierarchical' mergedFacet.predicate = serverFacet.predicate mergedFacet.parentProperty = serverFacet.parentProperty @@ -250,7 +264,7 @@ export const mergeFacetConfigs = (clientFacets, serverFacets) => { mergedFacets[facetID] = orderedFacet } // console.log(mergedFacets) - // console.log(JSON.stringify(mergedFacets)) + console.log(JSON.stringify(mergedFacets)) } export const createExtraResultClassesForJSONConfig = async oldBackendSearchConfig => { @@ -313,8 +327,11 @@ export const createExtraResultClassesForJSONConfig = async oldBackendSearchConfi } // createExtraResultClassesForJSONConfig(oldBackendSearchConfig) -// mergeFacetConfigs(INITIAL_STATE.facets, findsPerspectiveConfig.facets) + +// mergeFacetConfigs(INITIAL_STATE.facets, entitiesConfig.facets) + // console.log(JSON.stringify(INITIAL_STATE.properties)) + // "tabID": 0, // "tabPath": "", // "tabIcon": "",