diff --git a/src/App.jsx b/src/App.jsx index 699f01c..3c9f057 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -111,7 +111,7 @@ const App = () => { } /> ...}> diff --git a/src/components/GalleryOfStories.css b/src/components/GalleryOfStories.css index c829b34..c6a86cd 100644 --- a/src/components/GalleryOfStories.css +++ b/src/components/GalleryOfStories.css @@ -9,3 +9,17 @@ background: #f7dfd1; font-size: smaller; } +.GalleryOfStorie figure { + position: relative; +} +.GalleryOfStories img { + position: absolute; + left: 50%; + top: 50%; + max-width: 90%; + -webkit-transform: translate(-50%, -50%); + -moz-transform: translate(-50%, -50%); + -ms-transform: translate(-50%, -50%); + -o-transform: translate(-50%, -50%); + transform: translate(-50%, -50%); +} diff --git a/src/components/GalleryOfStories.jsx b/src/components/GalleryOfStories.jsx index 4b83ecf..3659dc5 100644 --- a/src/components/GalleryOfStories.jsx +++ b/src/components/GalleryOfStories.jsx @@ -73,7 +73,7 @@ const GalleryOfStories = ({ > @@ -88,7 +88,7 @@ const GalleryOfStories = ({ opacity, }} > -
+
diff --git a/src/components/ListOfDocuments.jsx b/src/components/ListOfDocuments.jsx new file mode 100644 index 0000000..dcfcaff --- /dev/null +++ b/src/components/ListOfDocuments.jsx @@ -0,0 +1,35 @@ +import DocumentItem from './DocumentItem' + +import { StatusError, StatusSuccess, useGetJSON } from '../hooks/data' + +const ListOfDocuments = ({ params, className = '', hideIfEmpty, onClick, itemProps, children }) => { + const { data, status, error } = useGetJSON({ + url: '/api/document', + params, + }) + + if (status === StatusError) { + console.error('[ListOfDocuments] error in reading api:', status, params, data, error) + return null + } + if (status === StatusSuccess && !data.results.length && hideIfEmpty) { + return null + } + + return ( +
+ {children} +
    + {status === StatusSuccess + ? data.results.map((doc) => ( +
  1. + +
  2. + )) + : null} +
+
+ ) +} + +export default ListOfDocuments diff --git a/src/components/MetadataField.css b/src/components/MetadataField.css new file mode 100644 index 0000000..98f1700 --- /dev/null +++ b/src/components/MetadataField.css @@ -0,0 +1,15 @@ +.MetadataField { + display: flex; + align-items: center; +} +.MetadataField label { + font-family: var(--bs-font-sans-serif-italic); + opacity: 0.6; + width: 40%; + flex-shrink: 1; + word-break: normal; + /* text-transform: uppercase; + font-size: var(--small-font-size); */ + /* display: block; */ + margin-right: var(--size-3); +} diff --git a/src/components/MetadataField.jsx b/src/components/MetadataField.jsx new file mode 100644 index 0000000..a449401 --- /dev/null +++ b/src/components/MetadataField.jsx @@ -0,0 +1,33 @@ +import './MetadataField.css' +import { useTranslation } from 'react-i18next' + +const MetadataField = ({ label, value, className = '', hideIfEmpty = false }) => { + var { t } = useTranslation() + + if ( + hideIfEmpty && + (typeof value === 'undefined' || + value === null || + (typeof value === 'string' && value.length === 0)) + ) { + return null + } + const valueAsDate = new Date(value) + + return ( +
+ + {typeof value === 'undefined' || value === null ? : null} + {typeof value === 'string' && value.length === 0 ? : null} + {typeof value === 'string' && isNaN(valueAsDate) && value.length > 0 ? ( + {value} + ) : null} + {typeof value === 'string' && !isNaN(valueAsDate) ? ( + {t('dateShort', { date: valueAsDate })} + ) : null} + {Array.isArray(value) ? value.map((v, i) => {v}) : null} +
+ ) +} + +export default MetadataField diff --git a/src/components/PersonSummary.jsx b/src/components/PersonSummary.jsx index 7b17d53..4986cac 100644 --- a/src/components/PersonSummary.jsx +++ b/src/components/PersonSummary.jsx @@ -1,11 +1,7 @@ import { useTranslation } from 'react-i18next' -const PersonSummary = ({ person, className = '' }) => { +const PersonSummary = ({ person, indexOfPlaces = {}, className = '' }) => { const { t } = useTranslation() - const indexOfPlaces = person.data?.places?.reduce((acc, place) => { - acc[place.label] = place - return acc - }, {}) const firstName = person.data.first_name const lastName = person.data.last_name @@ -14,26 +10,26 @@ const PersonSummary = ({ person, className = '' }) => { const deathDate = new Date(person.data.death_date) let birth = null let death = null - + let gender = person.data.gender || '' if (!isNaN(birthDate)) { if (indexOfPlaces.birth_place) { - birth = t('summaryBirth', { + birth = t('summaryBirth' + gender, { birthDate: birthDate, birthPlace: indexOfPlaces.birth_place.toponymName, }) } else { - birth = t('summaryBirthNoPlace', { birthDate: birthDate }) + birth = t('summaryBirthNoPlace' + gender, { birthDate: birthDate }) } } if (!isNaN(deathDate)) { - if (indexOfPlaces.birth_place) { - death = t('summaryDeath', { + if (indexOfPlaces.death_place) { + death = t('summaryDeath' + gender, { deathDate: deathDate, deathPlace: indexOfPlaces.death_place.toponymName, }) } else { - death = t('summaryDeathNoPlace', { deathDate: deathDate }) + death = t('summaryDeathNoPlace' + gender, { deathDate: deathDate }) } } diff --git a/src/components/StoryItem.jsx b/src/components/StoryItem.jsx index bb05257..d18cc31 100644 --- a/src/components/StoryItem.jsx +++ b/src/components/StoryItem.jsx @@ -21,7 +21,7 @@ const StoryItem = ({ story, reduced = false, className = '' }) => { console.info('[StoryItem]', '\n - title:', title, '\n - availableLanguages:', availableLanguages) return (
- {story.covers.length ? : null} + {story.covers.length && !reduced ? : null}

{ +const TopStories = ({ className = '', params = {}, reduced = false, children }) => { const { data, status, error } = useGetJSON({ url: '/api/story', params, diff --git a/src/pages/Convoy.css b/src/pages/Convoy.css new file mode 100644 index 0000000..2f4e2e2 --- /dev/null +++ b/src/pages/Convoy.css @@ -0,0 +1,22 @@ +.Convoy label { + display: block; + margin: 0.5rem 0; + font-weight: 700; + text-transform: uppercase; +} + +.Convoy__StoryModule.no-footnotes .FootnoteDefinition { + display: none !important; +} + +.Convoy__StoryModule.first p:first-of-type { + font-size: 1.2em; +} + +.Convoy__StoryModule.first p:first-of-type > em { + font-style: normal; +} + +.Convoy__StoryEndnotes a { + word-break: break-all; +} diff --git a/src/pages/Convoy.jsx b/src/pages/Convoy.jsx index 621b26d..7e9942b 100644 --- a/src/pages/Convoy.jsx +++ b/src/pages/Convoy.jsx @@ -1,15 +1,20 @@ import { Col, Container, Row } from 'react-bootstrap' import { useTranslation } from 'react-i18next' import { useParams } from 'react-router-dom' -import { BootstrapStartColumnLayout } from '../constants' +import { BootstrapEndColumnLayout, BootstrapStartColumnLayout } from '../constants' import { StatusSuccess, useGetJSON } from '../hooks/data' import { useAvailableLanguage } from '../hooks/language' import NotFound from './NotFound' +import './Convoy.css' +import StoryModule from '../components/StoryModule' +import StoryEndnotes from '../components/StoryEndnotes' +import TopDocuments from '../components/TopDocuments' +import ListOfDocuments from '../components/ListOfDocuments' const Convoy = () => { const { t } = useTranslation() - const { storyId } = useParams() - const safeStoryId = storyId.replace(/[^\dA-Za-z-_]/g, '').toLowerCase() + const { convoyId } = useParams() + const safeStoryId = convoyId.replace(/[^\dA-Za-z-_]/g, '') const { data: story, status, @@ -18,7 +23,10 @@ const Convoy = () => { url: `/api/story/${safeStoryId}`, params: { parser: 'yaml' }, }) - const isValidStory = status === StatusSuccess && Array.isArray(story?.contents?.modules) + const isValidStory = + status === StatusSuccess && + Array.isArray(story?.contents?.modules) && + story?.contents?.modules.length > 0 const { availableLanguage } = useAvailableLanguage({ translatable: status === StatusSuccess ? story.data.title : {}, }) @@ -35,13 +43,54 @@ const Convoy = () => { if (error && error.response && error.response.status === 404) { return } + const firstModule = isValidStory ? story.contents.modules[0] : null + const otherModules = isValidStory ? story.contents.modules.slice(1) : [] return (
- {isValidStory ? 'loaded' : 'loading...'} - {t('loading')} + + {isValidStory && ( +
+ +
+ )} + +
+ + + {otherModules.map((d, i) => { + return ( +
+ +
+ ) + })} + {isValidStory && ( + + )} + + + +

{t('people')} 

+
diff --git a/src/pages/Person.jsx b/src/pages/Person.jsx index b6e968d..eb80ad0 100644 --- a/src/pages/Person.jsx +++ b/src/pages/Person.jsx @@ -10,6 +10,9 @@ import TopStories from '../components/TopStories' import TopDocuments from '../components/TopDocuments' import { useTranslation } from 'react-i18next' import DocumentImage from '../components/DocumentImage' +import MetadataField from '../components/MetadataField' + +const LocationMetadataFields = ['address_before_19411016'] const Person = () => { const { t } = useTranslation() @@ -29,10 +32,17 @@ const Person = () => { person && Array.isArray(person.documents) && person.documents.length > 0 ? person.documents[0] : null + const indexOfPlaces = person + ? person.data?.places?.reduce((acc, place) => { + acc[place.label] = place + return acc + }, {}) || {} + : {} if (error) { console.error('[Person]', '\n - docId:', safePersonId, '\n - api error:', error, person) } console.debug('[Person]', '\n - docId:', safePersonId, '\n - data:', person) + return (
@@ -45,9 +55,90 @@ const Person = () => { > {status === StatusSuccess && ( <> - - {/*
{JSON.stringify(person, null, 2)}
*/} - {/* */} + +
+ {['first_name', 'last_name'].map((label) => ( + + ))} + +
+
+ + {[ + 'birth_date_org', + 'birth_place', + 'birth_place_alt', + 'birth_country', + 'birth_country_alt', + ].map((field) => ( + + ))} +
+
+ + {[ + 'death_date_org', + 'death_place', + 'death_place_alt', + 'death_country', + 'death_country_alt', + ].map((label) => ( + + ))} +
+
+ + + + + +
+
+ {[ + 'exit_19400510_to_19411015_date', + 'exit_19400510_to_19411015_place', + 'exit_before_19411015_date', + 'exit_before_19411015_place', + 'exit_19411016_TO_19440910_date', + 'exit_19411016_TO_19440910_place', + ].map((label) => ( + + ))} +
+
+ {t('Technical details')} + +
{JSON.stringify(person?.data, null, 2)}
+
)}
diff --git a/src/pages/Search.jsx b/src/pages/Search.jsx index aad58c4..e656b7a 100644 --- a/src/pages/Search.jsx +++ b/src/pages/Search.jsx @@ -179,7 +179,6 @@ const Search = ({ limit = 5 }) => { /> { @@ -200,7 +199,7 @@ const Search = ({ limit = 5 }) => { {isSearchEnabled ? (
    {pagefindResult.matches.map((result, i) => ( -
  1. +
  2. {(result) => ( <> @@ -232,7 +231,7 @@ const Search = ({ limit = 5 }) => {
      {data?.pages.map((page, i) => page.results.map((story) => ( -
    1. +
    2. {/* */} diff --git a/src/pages/Slides.css b/src/pages/Slides.css index 8f0cb12..415fa84 100644 --- a/src/pages/Slides.css +++ b/src/pages/Slides.css @@ -69,3 +69,7 @@ font-size: 1rem; max-width: 600px; } + +.Slides main { + margin-left: 50px; +} diff --git a/src/pages/Slides.jsx b/src/pages/Slides.jsx index cc80f7b..f309b15 100644 --- a/src/pages/Slides.jsx +++ b/src/pages/Slides.jsx @@ -18,18 +18,21 @@ const Slides = () => { translatable: page?.data?.title, }) + const params = + page && page.data.households + ? { filters: JSON.stringify({ slug__in: page.data.households }), limit: 100 } + : {} + + console.debug('[Slides]', '\n - pageStatus:', pageStatus, '\n - params:', params) const { data: stories, status: storiesStatus } = useGetJSON({ url: `/api/story/`, - params: { - limit: 5, - exclude: { tags__name: 'static' }, - }, + params, enabled: pageStatus === StatusSuccess, }) if (!page) return null - const title = page.data.title[availableLanguage] + // const title = page.data.title[availableLanguage] const subtitle = page.data.subtitle[availableLanguage] const abstract = page.data.abstract[availableLanguage] @@ -55,7 +58,7 @@ const Slides = () => {

- Salle Edmond Dune + Salle José Ensch
9.00-17.00
@@ -83,10 +86,7 @@ const Slides = () => {
- - Les biographies des familles déportées le 16 octobre 1941 de Luxembourg au ghetto de - Litzmannstadt (Lodz) sur memorialshoah.lu - +

diff --git a/src/translations.json b/src/translations.json index b846abc..5bdd137 100644 --- a/src/translations.json +++ b/src/translations.json @@ -32,6 +32,29 @@ "languageFR": "Fr", "languageDE": "De", "latestBiographies": "Biographies", + "metadataField_first_name": "Prénom", + "metadataField_last_name": "Nom", + "metadataField_last_name_changed": "Nom de famille", + "metadataField_birth_country": "Pays de naissance (nom actuel)", + "metadataField_birth_country_alt": "Pays de naissance (autre dénomination)", + "metadataField_birth_date": "Date de naissance", + "metadataField_birth_date_org": "Notes sur la date de naissance", + "metadataField_birth_place": "Lieu de naissance (nom actuel)", + "metadataField_birth_place_alt": "Lieu de naissance (autre dénomination)", + "metadataField_death_country": "Pays de décès", + "metadataField_death_country_alt": "Pays de décès (autre dénomination)", + "metadataField_death_date": "Date de décès", + "metadataField_death_date_org": "Notes sur la date de décès", + "metadataField_death_place": "Lieu de décès", + "metadataField_death_place_alt": "Lieu de décès (autre dénomination)", + "metadataField_enter_date": "Date d'entrée au Luxembourg", + "metadataField_enter_place": "Lieu de provenance", + "metadataField_nationality": "Nationalité", + "metadataField_nationality_notes": "Notes sur la nationalité", + "metadataField_exit_19400510_to_19411015_date": "Date de sortie du Luxembourg entre le 10 mai 1940 et le 15 octobre 1941", + "metadataField_exit_19400510_to_19411015_place": "Le lieu vers lequel il a quitté le Luxembourg entre le 10 mai 1940 et le 15 octobre 1941.", + "metadataField_exit_19411016_TO_19440910_date": "Date de sortie du Luxembourg entre le 16 octobre 1941 et le 10 septembre 1944", + "metadataField_exit_19411016_TO_19440910_place": "Le lieu vers lequel il a quitté le Luxembourg entre le 16 octobre 1941 et le 10 septembre 1944.", "modalIconCreate": "Déposer", "modalIconPebble": "une pierre", "modalTitleCreateAPebble": "Déposer une pierre", @@ -75,11 +98,19 @@ "peopleCount": "{{n,2}} personnes", "peopleCountWithQuery": "{{n,2}} personnes matching {{q}}", "publicationDate": "Date de publication", + "summaryBirthm": "naît le {{ birthDate,long }} à {{ birthPlace }}", + "summaryBirthNoPlacem": "naît le {{ birthDate,long }}", + "summaryDeathm": "Il est décédé le {{ deathDate,long }} à {{ deathPlace }}.", + "summaryDeathNoPlacem": "Il est décédé le {{ deathDate,long }}.", + "summaryBirthf": "naît le {{ birthDate,long }} à {{ birthPlace }}", + "summaryBirthNoPlacef": "naît le {{ birthDate,long }}", + "summaryDeathf": "Elle est décédée le {{ deathDate,long }} à {{ deathPlace }}.", + "summaryDeathNoPlacef": "Elle est décédée le {{ deathDate,long }}.", "summaryBirth": "naît le {{ birthDate,long }} à {{ birthPlace }}", "summaryBirthNoPlace": "naît le {{ birthDate,long }}", "summaryDeath": "Décédé le {{ deathDate,long }} à {{ deathPlace }}.", "summaryDeathNoPlace": "Décédé le {{ deathDate,long }}.", - "summaryPerson": "{{ firstName }} {{ lastName }} {{ birth }}. {{ death }}", + "summaryPerson": "{{ firstName }} {{ lastName }} {{ birth }}. {{ death }}", "topStoriesIntro": "Nous allons retracer tous ensemble, dans la mesure du possible le vécu de ces personnes, le milieu dans lequel ils ont évolué au Luxembourg avant la Shoah et de décrire leur parcours pendant la Seconde Guerre mondiale. ", "translatedIn": "traduit en", "unkownDate": "Date inconnue.", @@ -167,7 +198,16 @@ "summaryBirthNoPlace": "was born on {{ birthDate,long }}", "summaryDeath": "He/She died on {{ deathDate,long }} in {{ deathPlace }}.", "summaryDeathNoPlace": "He/She died on {{ deathDate,long }}.", - "summaryPerson": "{{ firstName }} {{ lastName }} {{ birth }}. {{ death }}", + "summaryBirthm": "was born on {{ birthDate,long }} in {{ birthPlace }}", + "summaryBirthNoPlacem": "was born on {{ birthDate,long }}", + "summaryDeathm": "He died on {{ deathDate,long }} in {{ deathPlace }}.", + "summaryDeathNoPlacem": "He died on {{ deathDate,long }}.", + "summaryBirthf": "was born on {{ birthDate,long }} in {{ birthPlace }}", + "summaryBirthNoPlacef": "was born on {{ birthDate,long }}", + "summaryDeathf": "She died on {{ deathDate,long }} in {{ deathPlace }}.", + "summaryDeathNoPlacef": "She died on {{ deathDate,long }}.", + "summaryPerson": "{{ firstName }} {{ lastName }} {{ birth }}. {{ death }}", + "topStoriesIntro": "Nous allons retracer tous ensemble, dans la mesure du possible le vécu de ces personnes, le milieu dans lequel ils ont évolué au Luxembourg avant la Shoah et de décrire leur parcours pendant la Seconde Guerre mondiale. ", "translatedIn": "translated in", "unkownDate": "Unknown date.", @@ -253,7 +293,11 @@ "summaryBirthNoPlace": "wird am {{ birthDate,long }}", "summaryDeath": "Sie stirbt am {{ deathDate,long }} in {{ deathPlace }}", "summaryDeathNoPlace": "Sie stirbt am {{ deathDate,long }}.", - "summaryPerson": "{{ firstName }} {{ lastName }} {{ birth }}. {{ death }}", + "summaryBirthm": "wird am {{ birthDate,long }} in {{ birthPlace }} geboren", + "summaryBirthNoPlacem": "wird am {{ birthDate,long }}", + "summaryDeathm": "Sie stirbt am {{ deathDate,long }} in {{ deathPlace }}", + "summaryDeathNoPlacem": "Sie stirbt am {{ deathDate,long }}.", + "summaryPerson": "{{ firstName }} {{ lastName }} {{ birth }}. {{ death }}", "topStoriesIntro": "Nous allons retracer tous ensemble, dans la mesure du possible le vécu de ces personnes, le milieu dans lequel ils ont évolué au Luxembourg avant la Shoah et de décrire leur parcours pendant la Seconde Guerre mondiale. ", "translatedIn": "übersetzt auf", "unkownDate": "Date inconnue.",