From 990c7c75ab15c46b8848e89d84012f9b22880e62 Mon Sep 17 00:00:00 2001 From: Thibault Ehrhart Date: Fri, 14 Oct 2022 17:50:23 +0200 Subject: [PATCH] Add filters in vocabulary pages --- config/routes/fragrant-spaces.js | 65 +++++++-- config/routes/odour-carriers.js | 62 ++++++-- config/routes/smell-sources.js | 59 ++++++-- config/routes/texts.js | 21 +++ src/pages/odeuropa-vocabulary.js | 236 +++++++++++++++++++++++++++---- 5 files changed, 391 insertions(+), 52 deletions(-) diff --git a/config/routes/fragrant-spaces.js b/config/routes/fragrant-spaces.js index b860f43..61bf439 100644 --- a/config/routes/fragrant-spaces.js +++ b/config/routes/fragrant-spaces.js @@ -50,30 +50,73 @@ module.exports = { $langTag: 'hide', }), }, - query: ({ language }) => ({ + query: ({ language, params }) => ({ $from: 'http://www.ontotext.com/disable-sameAs', // Prevent returning Wikidata entities '@graph': [ { - '@id': '?id', - label: '?sourceLabel', + items: { + '@id': '?id', + label: '?bestLabel', + count: '?count', + }, + dates: '?date', }, ], $where: [ ` { - SELECT DISTINCT ?id WHERE { - ?place crm:P2_has_type ?id . - ?emission crm:P7_took_place_at ?place . - ?emission od:F1_generated ?smell . + { + SELECT DISTINCT ?emission ?id WHERE { + ?place crm:P2_has_type ?id . + ?emission crm:P7_took_place_at ?place . + + ${ + params.date + ? ` + ?emission_source crm:P67_refers_to ?emission . + ?emission_source schema:dateCreated/time:hasBeginning ${JSON.stringify( + params.date + )}^^xsd:gYear . + ` + : '' + } + } + } + { + OPTIONAL { + SELECT DISTINCT ?id (COUNT(?object) AS ?count) WHERE { + ?object crm:P137_exemplifies ?id . + } + GROUP BY ?id + } + } + { + OPTIONAL { ?id skos:prefLabel ?label_fr . FILTER(LANGMATCHES(LANG(?label_fr), "${language}")) } + OPTIONAL { ?id skos:prefLabel ?label_unk . FILTER(LANGMATCHES(LANG(?label_unk), "")) } + OPTIONAL { ?id skos:prefLabel ?label_en . FILTER(LANGMATCHES(LANG(?label_en), "en")) } + BIND(COALESCE(?label_fr, ?label_unk, ?label_en) AS ?bestKnownLabel) + OPTIONAL { + SELECT ?label_default WHERE { + ?id skos:prefLabel ?label_default + } + ORDER BY ASC(?label_default) + LIMIT 1 + } + BIND(COALESCE(?bestKnownLabel, ?label_default) AS ?bestLabel) } } + UNION { - ?id skos:prefLabel ?sourceLabel . - FILTER(LANG(?sourceLabel) = "${language}" || LANG(?sourceLabel) = "") + SELECT DISTINCT ?emission ?date WHERE { + ?place crm:P2_has_type ?id . + ?emission crm:P7_took_place_at ?place . + ?source crm:P67_refers_to ?emission . + ?source schema:dateCreated/time:hasBeginning ?date . + } } `, ], - $orderby: 'ASC(?sourceLabel)', + $orderby: params.order === 'count' ? 'DESC(?count)' : 'ASC(?bestLabel)', $langTag: 'hide', }), plugins: { @@ -137,7 +180,7 @@ module.exports = { ], $where: [ ` - ?place crm:P2_has_type ?id . + ?place crm:P137_exemplifies ?id . ?emission crm:P7_took_place_at ?place . ?emission od:F1_generated ?smell . diff --git a/config/routes/odour-carriers.js b/config/routes/odour-carriers.js index 605b651..38e001b 100644 --- a/config/routes/odour-carriers.js +++ b/config/routes/odour-carriers.js @@ -52,29 +52,73 @@ module.exports = { $langTag: 'hide', }), }, - query: ({ language }) => ({ + query: ({ language, params }) => ({ $from: 'http://www.ontotext.com/disable-sameAs', // Prevent returning Wikidata entities '@graph': [ { - '@id': '?id', - label: '?sourceLabel', + items: { + '@id': '?id', + label: '?bestLabel', + count: '?count', + }, + dates: '?date', }, ], $where: [ ` { - SELECT DISTINCT ?id WHERE { - olfactory-objects:carrier skos:member ?id . - ?source crm:P2_has_type ?id . + { + SELECT DISTINCT ?emission ?id WHERE { + olfactory-objects:carrier skos:member ?id . + ?emission od:F3_had_source / crm:P137_exemplifies ?id . + + ${ + params.date + ? ` + ?emission_source crm:P67_refers_to ?emission . + ?emission_source schema:dateCreated/time:hasBeginning ${JSON.stringify( + params.date + )}^^xsd:gYear . + ` + : '' + } + } + } + { + OPTIONAL { + SELECT DISTINCT ?id (COUNT(?object) AS ?count) WHERE { + ?object crm:P137_exemplifies ?id . + } + GROUP BY ?id + } + } + { + OPTIONAL { ?id skos:prefLabel ?label_fr . FILTER(LANGMATCHES(LANG(?label_fr), "${language}")) } + OPTIONAL { ?id skos:prefLabel ?label_unk . FILTER(LANGMATCHES(LANG(?label_unk), "")) } + OPTIONAL { ?id skos:prefLabel ?label_en . FILTER(LANGMATCHES(LANG(?label_en), "en")) } + BIND(COALESCE(?label_fr, ?label_unk, ?label_en) AS ?bestKnownLabel) + OPTIONAL { + SELECT ?label_default WHERE { + ?id skos:prefLabel ?label_default + } + ORDER BY ASC(?label_default) + LIMIT 1 + } + BIND(COALESCE(?bestKnownLabel, ?label_default) AS ?bestLabel) } } + UNION { - ?id skos:prefLabel ?sourceLabel . - FILTER(LANG(?sourceLabel) = "${language}" || LANG(?sourceLabel) = "") + SELECT DISTINCT ?emission ?date WHERE { + olfactory-objects:carrier skos:member ?id . + ?emission od:F3_had_source / crm:P137_exemplifies ?id . + ?source crm:P67_refers_to ?emission . + ?source schema:dateCreated/time:hasBeginning ?date . + } } `, ], - $orderby: 'ASC(?sourceLabel)', + $orderby: params.order === 'count' ? 'DESC(?count)' : 'ASC(?bestLabel)', $langTag: 'hide', }), plugins: { diff --git a/config/routes/smell-sources.js b/config/routes/smell-sources.js index 6b35a0d..054c84e 100644 --- a/config/routes/smell-sources.js +++ b/config/routes/smell-sources.js @@ -49,28 +49,71 @@ module.exports = { $langTag: 'hide', }), }, - query: ({ language }) => ({ + query: ({ language, params }) => ({ $from: 'http://www.ontotext.com/disable-sameAs', // Prevent returning Wikidata entities '@graph': [ { - '@id': '?id', - label: '?sourceLabel', + items: { + '@id': '?id', + label: '?bestLabel', + count: '?count', + }, + dates: '?date', }, ], $where: [ ` { - SELECT DISTINCT ?id WHERE { - ?emission od:F3_had_source / crm:P137_exemplifies ?id . + { + SELECT DISTINCT ?emission ?id WHERE { + ?emission od:F3_had_source / crm:P137_exemplifies ?id . + + ${ + params.date + ? ` + ?emission_source crm:P67_refers_to ?emission . + ?emission_source schema:dateCreated/time:hasBeginning ${JSON.stringify( + params.date + )}^^xsd:gYear . + ` + : '' + } + } + } + { + OPTIONAL { + SELECT DISTINCT ?id (COUNT(?object) AS ?count) WHERE { + ?object crm:P137_exemplifies ?id . + } + GROUP BY ?id + } + } + { + OPTIONAL { ?id skos:prefLabel ?label_fr . FILTER(LANGMATCHES(LANG(?label_fr), "${language}")) } + OPTIONAL { ?id skos:prefLabel ?label_unk . FILTER(LANGMATCHES(LANG(?label_unk), "")) } + OPTIONAL { ?id skos:prefLabel ?label_en . FILTER(LANGMATCHES(LANG(?label_en), "en")) } + BIND(COALESCE(?label_fr, ?label_unk, ?label_en) AS ?bestKnownLabel) + OPTIONAL { + SELECT ?label_default WHERE { + ?id skos:prefLabel ?label_default + } + ORDER BY ASC(?label_default) + LIMIT 1 + } + BIND(COALESCE(?bestKnownLabel, ?label_default) AS ?bestLabel) } } + UNION { - ?id skos:prefLabel ?sourceLabel . - FILTER(LANG(?sourceLabel) = "${language}" || LANG(?sourceLabel) = "") + SELECT DISTINCT ?emission ?date WHERE { + ?emission od:F3_had_source / crm:P137_exemplifies ?id . + ?source crm:P67_refers_to ?emission . + ?source schema:dateCreated/time:hasBeginning ?date . + } } `, ], - $orderby: 'ASC(?sourceLabel)', + $orderby: params.order === 'count' ? 'DESC(?count)' : 'ASC(?bestLabel)', $langTag: 'hide', }), plugins: { diff --git a/config/routes/texts.js b/config/routes/texts.js index 2510190..e180fbb 100644 --- a/config/routes/texts.js +++ b/config/routes/texts.js @@ -569,5 +569,26 @@ module.exports = { ], filterFunc: (val) => `STR(?language) = ${JSON.stringify(val)}`, }, + { + id: 'museum', + isMulti: true, + isSortable: false, + query: () => ({ + '@graph': [ + { + '@id': '?location', + label: '?locationName', + }, + ], + $where: [ + '?id crm:P53_has_former_or_current_location ?location', + '?location a gn:Feature', + '?location gn:name ?locationName', + ], + $langTag: 'hide', + }), + whereFunc: () => ['?id crm:P53_has_former_or_current_location ?location'], + filterFunc: (val) => `?location = <${val}>`, + }, ], }; diff --git a/src/pages/odeuropa-vocabulary.js b/src/pages/odeuropa-vocabulary.js index afa7fac..6f2dd0f 100644 --- a/src/pages/odeuropa-vocabulary.js +++ b/src/pages/odeuropa-vocabulary.js @@ -1,3 +1,4 @@ +import { useState, useEffect } from 'react'; import styled from 'styled-components'; import { useRouter } from 'next/router'; import Link from 'next/link'; @@ -14,11 +15,47 @@ import Metadata from '@components/Metadata'; import Debug from '@components/Debug'; import PageTitle from '@components/PageTitle'; import SPARQLQueryLink from '@components/SPARQLQueryLink'; +import Input from '@components/Input'; +import Select from '@components/Select'; import breakpoints from '@styles/breakpoints'; import SparqlClient from '@helpers/sparql'; -import { getQueryObject, uriToId } from '@helpers/utils'; +import { getQueryObject, removeEmptyObjects, uriToId } from '@helpers/utils'; import { getEntityMainLabel } from '@helpers/explorer'; import config from '~/config'; +import theme from '~/theme'; + +const selectTheme = (base) => ({ + ...base, + ...theme.select, + colors: { + ...base.colors, + primary: '#000', + neutral0: '#eee', + primary25: '#ddd', + ...theme.select?.colors, + }, +}); + +const selectStyles = { + control: (provided) => ({ + ...provided, + border: 'none', + border: '1px solid #b9d59b', + backgroundColor: '#f0f0f0', + }), + dropdownIndicator: (base) => ({ + ...base, + color: 'hsl(0,0%,20%)', + '&:hover': { + color: 'hsl(0,0%,20%)', + }, + }), + option: (base) => ({ + ...base, + overflow: 'hidden', + textOverflow: 'ellipsis', + }), +}; const Hero = styled.div` width: 100%; @@ -74,6 +111,10 @@ const VocabularyTitle = styled.div` } `; +const StyledSelect = styled(Select)` + min-width: 200px; +`; + const Results = styled.div` flex: 1; display: grid; @@ -118,18 +159,97 @@ const ItemTitle = styled.div` } `; +const StyledInput = styled(Input)` + border: 1px solid #b9d59b; + border-radius: 20px; + background-color: #fff; + padding: 0 16px; + + ::placeholder { + color: #b9d59b; + } +`; + +const FilterBar = styled.div` + display: flex; + margin-bottom: 24px; + + ${breakpoints.weirdMedium` + margin-left: 120px; + margin-right: 120px; + `} +`; + +const orderOptions = [ + { + label: 'Alphabetical', + value: 'name', + }, + { + label: 'Occurrences', + value: 'count', + }, +]; + const OdeuropaVocabularyPage = ({ results, debugSparqlQuery }) => { const { t, i18n } = useTranslation(['common', 'project']); const router = useRouter(); - const query = { ...router.query }; + const [searchText, setSearchText] = useState(''); + const [searchDate, setSearchDate] = useState(query.date); + const [searchOrder, setSearchOrder] = useState(query.order); + const [resultsWithLabel, setResultsWithLabel] = useState([]); + const [dateOptions, setDateOptions] = useState([]); + const route = config.routes[query.type]; + useEffect(() => { + const resultsWithLabel = []; + if (Array.isArray(results[0]?.items)) { + results[0].items.forEach((item) => { + resultsWithLabel.push({ + ...item, + mainLabel: getEntityMainLabel(item, { route, language: i18n.language }), + }); + }); + } + setResultsWithLabel(resultsWithLabel); + + const dates = [].concat(results[0]?.dates).filter((x) => x); + dates.sort((a, b) => a.toLocaleLowerCase().localeCompare(b.toLocaleLowerCase())); + setDateOptions( + dates.map((date) => ({ + label: date, + value: date, + })) + ); + }, [results]); + const useWith = []; if (route && Array.isArray(route.useWith)) { useWith.push(...route.useWith); } + const handleSearchTextChange = (e) => { + setSearchText(e.target.value); + }; + + const onSelectDate = (selectedOption) => { + setSearchDate(selectedOption?.value); + + router.push({ + query: { ...query, date: selectedOption?.value }, + }); + }; + + const onSelectOrder = (selectedOption) => { + setSearchOrder(selectedOption?.value); + + router.push({ + query: { ...query, order: selectedOption?.value }, + }); + }; + return ( @@ -147,29 +267,94 @@ const OdeuropaVocabularyPage = ({ results, debugSparqlQuery }) => { + +
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} + +
+ +
+ {dateOptions.length > 0 && ( +
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} + +
+ )} +
+ {/* eslint-disable-next-line jsx-a11y/label-has-associated-control */} + +
+
+
+ - {results.map((result) => ( - - - - - -

{getEntityMainLabel(result, { route, language: i18n.language })}

-
- {result.description &&

{result.description}

} -
-
- -
- ))} + {resultsWithLabel + .filter((result) => + result.mainLabel + ?.trim() + .toLocaleLowerCase() + .includes(searchText.trim().toLocaleLowerCase()) + ) + .map((result) => ( + + + + + +

{result.mainLabel}

+
+ {result.description &&

{result.description}

} +
+
+ +
+ ))}
@@ -200,7 +385,10 @@ export async function getServerSideProps({ query, locale }) { const results = []; if (route) { - const mainQuery = getQueryObject(route.query, { language: locale }); + const mainQuery = getQueryObject(route.query, { + language: locale, + params: { date: query.date, order: query.order }, + }); if (config.debug) { debugSparqlQuery.results = await SparqlClient.getSparqlQuery(mainQuery);