From 1b88937a6f7518aec14f565945608b46ce2d6d77 Mon Sep 17 00:00:00 2001 From: Valentin Le Moign Date: Wed, 12 Feb 2025 14:42:24 +0100 Subject: [PATCH] =?UTF-8?q?affichage=20des=20pi=C3=A8ces=20jointes=20et=20?= =?UTF-8?q?liens?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../caravane/assets/js/stores/content.js | 257 +++++++++++++++++- .../assets/js/utils/handle-navigation.js | 3 + .../caravane/assets/js/vuejs/Modale.vue | 107 ++++---- .../js/vuejs/components/PiecesJointes.vue | 17 ++ .../assets/pictograms/arrow_forward.svg | 1 + .../caravane/assets/pictograms/download.svg | 1 + .../custom/caravane/assets/scss/main.scss | 59 ++++ 7 files changed, 380 insertions(+), 65 deletions(-) create mode 100644 web/themes/custom/caravane/assets/js/vuejs/components/PiecesJointes.vue create mode 100644 web/themes/custom/caravane/assets/pictograms/arrow_forward.svg create mode 100644 web/themes/custom/caravane/assets/pictograms/download.svg diff --git a/web/themes/custom/caravane/assets/js/stores/content.js b/web/themes/custom/caravane/assets/js/stores/content.js index 63ad6c4..e1947a6 100644 --- a/web/themes/custom/caravane/assets/js/stores/content.js +++ b/web/themes/custom/caravane/assets/js/stores/content.js @@ -18,6 +18,8 @@ export const useContentStore = defineStore('content', { next: {}, vignette: {}, parties: [], + liens: [], + pieces_jointes: [], intro: '', partenaires: [], @@ -28,6 +30,7 @@ export const useContentStore = defineStore('content', { }), actions: { async fetchContentData(path) { + console.log('start fetch content'); this.resetStore(false); const contentTypes = [ 'etape', 'static', 'gouvernance', 'partenaire' ]; try { @@ -42,6 +45,8 @@ export const useContentStore = defineStore('content', { ); if (content) { + console.log('content found'); + return { contentType: type, rawContent: content, @@ -60,17 +65,20 @@ export const useContentStore = defineStore('content', { } } return null; - }; - - const { contentType, rawContent } = await findContentByPath(contentTypes, path); + }; + const { contentType, rawContent } = await findContentByPath(contentTypes, path); this.contentType = contentType; // console.log(`current type: ${contentType}`); + // TO DEBUG + const cleanContentMethod = 'original'; - - if (this.contentType !== 'gouvernance' && this.contentType !== 'partenaire') { + if (this.contentType !== 'gouvernance' && this.contentType !== 'partenaire' + && cleanContentMethod === 'original' + ) { + console.time('etape content processing'); // pageTitle for (let tag of rawContent.attributes.metatag) { if (tag.tag === "meta") { @@ -95,6 +103,31 @@ export const useContentStore = defineStore('content', { }; } + // liens + if (rawContent.attributes.field_liens.length) { + this.content.liens = []; + for (let lien of rawContent.attributes.field_liens) { + this.content.liens.push({ + title: lien.title, + url: lien.uri, + }); + } + } + // pièces jointes + if (rawContent.relationships.field_pieces_jointes.data.length) { + this.content.pieces_jointes = []; + for (let pieceJointe of rawContent.relationships.field_pieces_jointes.data) { + if (pieceJointe.meta.display) { + const uuid = pieceJointe.id; + const response = await REST.get(`/jsonapi/file/file/${uuid}`); + this.content.pieces_jointes.push({ + title: pieceJointe.meta.description, + url: response.data.data.attributes.uri.url, + }); + } + } + } + if (contentType === 'etape') { // coordinates this.content.coordinates = { @@ -120,7 +153,8 @@ export const useContentStore = defineStore('content', { // parties const fieldParties = contentType === 'etape' ? 'field_parties' : 'field_parties_static'; const partiesFetch = await this.fetchFromRelationships(fieldParties, rawContent.relationships); - + console.log(this.content); + console.timeEnd('etape content processing'); if (partiesFetch) { this.content.parties = []; for (let partie of partiesFetch) { @@ -146,6 +180,7 @@ export const useContentStore = defineStore('content', { } break; case 'titre_texte': + console.time('get images from text original') partieContent.titre = partie.attributes.field_titre; partieContent.texte = partie.attributes.field_texte.value; @@ -170,8 +205,10 @@ export const useContentStore = defineStore('content', { partieContent.texte = partieContent.texte.replace(imgTag, newImgTag); } } - } + } + + console.timeEnd('get images from text original') break; case 'chiffres_cles': const chiffresClesFetch = await this.fetchFromRelationships('field_chiffres_clefs', partie.relationships); @@ -250,6 +287,153 @@ export const useContentStore = defineStore('content', { this.content.parties.push(partieContent); } } + console.log('content cleaned'); + } else if (this.contentType !== 'gouvernance' && this.contentType !== 'partenaire' + && cleanContentMethod === 'promise' + ) { + + + + + console.time('etape content promise processing'); + + + const vignettePromise = this.fetchFromRelationships('field_vignette', rawContent.relationships); + const partiesPromise = this.fetchFromRelationships('field_parties', rawContent.relationships); + + const previousEtapePromise = contentType === 'etape'? this.getRelatedEtape('previous', path) : null; + const nextEtapePromise = contentType === 'etape' ? this.getRelatedEtape('next', path) : null; + + if (contentType === 'etape') { + // coordinates + this.content.coordinates = { + lat: rawContent.attributes.field_geofield.lat, + lon: rawContent.attributes.field_geofield.lon, + }; + // adresse + this.content.adresse = rawContent.attributes.field_adresse; + // étape number + this.content.etape_number = rawContent.attributes.field_arret_numero; + // couleur + this.content.couleur = rawContent.attributes.field_couleur; + // dates + this.content.dates = { + start: this.getCleanDate(rawContent.attributes.field_dates.value), + end: this.getCleanDate(rawContent.attributes.field_dates.end_value), + } + } + + // pageTitle + for (let tag of rawContent.attributes.metatag) { + if (tag.tag === "meta") { + this.pageTitle = tag.attributes.content; + break; + } + } + + // contentTitle + this.content.contentTitle = rawContent.attributes.title; + + const [vignetteData, partiesData] = await Promise.all([vignettePromise, partiesPromise]); + + if (vignetteData) { + this.content.vignette = { + url: { + original: vignetteData.attributes.uri.url, + small: vignetteData.attributes.image_style_uri.content_small, + medium: vignetteData.attributes.image_style_uri.content_medium, + large: vignetteData.attributes.image_style_uri.content_large, + }, + alt: rawContent.relationships.field_vignette.data.meta.alt + }; + } + + if (partiesData) { + const partiesPromises = partiesData.map(async (partie) => { + const partieType = partie.type.replace(/^paragraph--/, ""); + let partieContent = { type: partieType }; + + switch(partieType) { + case 'carte_sensible': + const carteSensiblePromise = this.fetchFromRelationships('field_image_carte', partie.relationships); + + const carteSensibleData = await carteSensiblePromise; + if (carteSensibleData) { + partieContent.carteSensible = { + url: { + original: carteSensibleData.attributes.uri.url, + small: carteSensibleData.attributes.image_style_uri.content_small, + medium: carteSensibleData.attributes.image_style_uri.content_medium, + large: carteSensibleData.attributes.image_style_uri.content_large, + xlarge: carteSensibleData.attributes.image_style_uri.content_x_large, + }, + alt: partie.relationships.field_image_carte.data.meta.alt, + }; + } + + break; + case 'titre_texte': + partieContent.titre = partie.attributes.field_titre; + partieContent.texte = partie.attributes.field_texte.value; + + // get the resized images from the text + const imgRegex = /]+>/g; + const uuidRegex = /data-entity-uuid="([^"]+)"/; + const imgTags = partieContent.texte.match(imgRegex); + + if (imgTags) { + const imagePromises = imgTags.map(imgTag => { + const uuidMatch = imgTag.match(uuidRegex); + if (uuidMatch && uuidMatch[1]) { + return REST.get(`/jsonapi/file/file/${uuidMatch[1]}`) + .then(response => ({ + originalTag: imgTag, + imageData: response.data.data + })); + } + }); + + const images = await Promise.all(imagePromises); + images.forEach(({originalTag, imageData}) => { + const newImgTag = originalTag + .replace(/src="[^"]+"/,`src="${imageData.attributes.image_style_uri.content_medium}"`) + .replace('>',' data-large-src="' + imageData.attributes.image_style_uri.content_large + '">'); + partieContent.texte = partieContent.texte.replace(originalTag, newImgTag); + }); + } + break; + case 'chiffres_cles': + const chiffresClesFetch = await this.fetchFromRelationships('field_chiffres_clefs', partie.relationships); + if (chiffresClesFetch) { + partieContent.chiffresCles = []; + for (let chiffre of chiffresClesFetch) { + partieContent.chiffresCles.push({ + chiffre: chiffre.attributes.field_chiffre, + description: chiffre.attributes.field_description, + }); + } + } + break; + case 'diaporama': + break; + case 'entretien': + break; + case 'exergue': + break; + case 'video': + break; + } + return partieContent; + }); + + this.content.parties = await Promise.all(partiesPromises); + } + + // related étapes + if (contentType === 'etape') await Promise.all([previousEtapePromise, nextEtapePromise]); + + console.log(this.content); + console.timeEnd('etape content promise processing'); } else { // pages gouvernance (contact) et partenaire // ont plusieurs items par pages @@ -366,18 +550,61 @@ export const useContentStore = defineStore('content', { } } }, + async getRelatedEtape(direction, path) { + const getRelatedEtapeContent = (relatedEtapeData) => { + if (relatedEtapeData) { + return this.fetchFromRelationships('field_vignette', relatedEtapeData.relationships) + .then(vignetteFetch => { + if (vignetteFetch) { + this.content[direction] = { + url: relatedEtapeData.attributes.metatag.find(tag => tag.tag === "link")?.attributes.href, + couleur: relatedEtapeData.attributes.field_couleur, + title: relatedEtapeData.attributes.title, + postalCode: relatedEtapeData.attributes.field_adresse.postal_code, + dates: { + start: this.getCleanDate(relatedEtapeData.attributes.field_dates.value), + end: this.getCleanDate(relatedEtapeData.attributes.field_dates.end_value), + }, + vignette: { + url: { + original: vignetteFetch.attributes.uri.url, + small: vignetteFetch.attributes.image_style_uri.content_small, + medium: vignetteFetch.attributes.image_style_uri.content_medium, + large: vignetteFetch.attributes.image_style_uri.content_large, + }, + alt: relatedEtapeData.relationships.field_vignette.data.meta.alt, + }, + } + } + }); + } + } + + const allEtapesPromise = REST.get('/jsonapi/views/etapes/block_1/'); + + return allEtapesPromise.then(allEtapesData => { + for (let [index, etape] of allEtapesData.data.data.entries()) { + if (etape.attributes.metatag.some(tag => + tag.tag === "link" && tag.attributes.href === path + )) { + const relatedEtapeIndex = direction === 'next' ? index + 1 : index - 1; + return getRelatedEtapeContent(allEtapesData.data.data[relatedEtapeIndex]); + } + } + }); + }, async fetchFromRelationships(field, relationships) { if (relationships[field].links) { - try { const contentLink = relationships[field].links.related.href; - const contentFetch = await REST.get(contentLink); - return contentFetch.data.data; - } catch (error) { - this.error = 'Failed to fetch data'; - console.error('Issue with getNodeData', error); - } + return REST.get(contentLink) + .then(contentFetch => contentFetch.data.data) + .catch(error => { + this.error = 'Failed to fetch data'; + console.error('Issue with getNodeData', error); + }); } - }, + return null; + }, resetStore(forFrontDisplay) { this.contentType = ''; this.pageTitle = ''; diff --git a/web/themes/custom/caravane/assets/js/utils/handle-navigation.js b/web/themes/custom/caravane/assets/js/utils/handle-navigation.js index efaee8b..c012da0 100644 --- a/web/themes/custom/caravane/assets/js/utils/handle-navigation.js +++ b/web/themes/custom/caravane/assets/js/utils/handle-navigation.js @@ -23,6 +23,8 @@ export function handleClickableElements(clickableElements, store, router, baseUr if (href.startsWith(baseUrl)) href = href.replace(baseUrl, ''); link.onclick = async function (e) { + console.log('click on link, route push'); + router.push(href); if (href !== window.location.pathname) { pageChange(href, store, siteName, mapStore, baseUrl); @@ -38,6 +40,7 @@ export async function handleBrowserNavigation(store, baseUrl, siteName, mapStore } export async function pageChange(href, store, siteName, mapStore, baseUrl) { + console.log('trigger page change'); if (href === '/') { store.resetStore(true); document.title = siteName; diff --git a/web/themes/custom/caravane/assets/js/vuejs/Modale.vue b/web/themes/custom/caravane/assets/js/vuejs/Modale.vue index 8c089de..f415d96 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/Modale.vue +++ b/web/themes/custom/caravane/assets/js/vuejs/Modale.vue @@ -51,11 +51,15 @@ v-if="contentType === 'partenaire'" :content="content" /> + + :contentType="contentType" + :content="content" + :couleur="content.couleur || brandColor" + /> @@ -72,6 +76,7 @@ import ModaleHeader from './components/ModaleHeader.vue'; import ModaleFooter from './components/ModaleFooter.vue'; import EquipeContent from './components/EquipeContent.vue'; import PartenairesContent from './components/PartenairesContent.vue'; +import PiecesJointes from './components/PiecesJointes.vue'; import ModaleCarteSensible from './components/parties/ModaleCarteSensible.vue'; import ModaleTitreTexte from './components/parties/ModaleTitreTexte.vue'; @@ -113,68 +118,70 @@ const handleMapMovement = () => { () => loading.value, () => { if (!loading.value) { - isModaleEtape = contentType.value === 'etape'; + console.log('loading done'); + isModaleEtape = contentType.value === 'etape'; - // Define helper functions in variables - const disableModaleTransition = () => { - document.documentElement.style.setProperty('margin-top', '0'); - document.documentElement.style.setProperty('transition', 'none'); - } - const setModaleTransition = (enterDelay) => { - document.documentElement.style.setProperty('--modale-enter-delay', `${enterDelay}s`); - }; + // Define helper functions in variables + const disableModaleTransition = () => { + document.documentElement.style.setProperty('margin-top', '0'); + document.documentElement.style.setProperty('transition', 'none'); + } + const setModaleTransition = (enterDelay) => { + document.documentElement.style.setProperty('--modale-enter-delay', `${enterDelay}s`); + }; - const zoomToContentPlace = () => { - mapState.zoomToPlace( - content.value.coordinates.lat ? content.value.coordinates.lat : defaultMapCenter.value.lat, - content.value.coordinates.lon ? content.value.coordinates.lon : defaultMapCenter.value.lng - ); - }; + const zoomToContentPlace = () => { + mapState.zoomToPlace( + content.value.coordinates.lat ? content.value.coordinates.lat : defaultMapCenter.value.lat, + content.value.coordinates.lon ? content.value.coordinates.lon : defaultMapCenter.value.lng + ); + }; - if (animationsAreEnabled.value) { + if (animationsAreEnabled.value) { - if (isModaleEtape) { - if (!wasModaleEtape) { - // national -> détail - setModaleTransition(animationDuration.value); - zoomToContentPlace(); - } else { - // détail -> détail - setModaleTransition(animationDuration.value); - zoomToContentPlace(); - } - } else { - if (wasModaleEtape) { - // détail -> national - setModaleTransition(animationDuration.value); - mapState.resetMap(); - } else { - // national -> national - setModaleTransition(0); - } - } - } else { - if (isModaleEtape) { + if (isModaleEtape) { + if (!wasModaleEtape) { + // national -> détail + setModaleTransition(animationDuration.value); zoomToContentPlace(); } else { - mapState.resetMap(); + // détail -> détail + setModaleTransition(animationDuration.value); + zoomToContentPlace(); + } + } else { + if (wasModaleEtape) { + // détail -> national + setModaleTransition(animationDuration.value); + mapState.resetMap(); + } else { + // national -> national + setModaleTransition(0); } - disableModaleTransition(); } + } else { + if (isModaleEtape) { + zoomToContentPlace(); + } else { + mapState.resetMap(); + } + disableModaleTransition(); + } - scrollTo(0, 0); + scrollTo(0, 0); - wasModaleEtape = isModaleEtape; + wasModaleEtape = isModaleEtape; } }, ); }; onMounted(() => { - isModaleEtape = contentType.value === 'etape'; - wasModaleEtape = isModaleEtape; - handleColorChange(); - handleMapMovement(); + console.log('modale mounted'); + isModaleEtape = contentType.value === 'etape'; + wasModaleEtape = isModaleEtape; + handleColorChange(); + handleMapMovement(); }); diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/PiecesJointes.vue b/web/themes/custom/caravane/assets/js/vuejs/components/PiecesJointes.vue new file mode 100644 index 0000000..4f9374c --- /dev/null +++ b/web/themes/custom/caravane/assets/js/vuejs/components/PiecesJointes.vue @@ -0,0 +1,17 @@ + + + diff --git a/web/themes/custom/caravane/assets/pictograms/arrow_forward.svg b/web/themes/custom/caravane/assets/pictograms/arrow_forward.svg new file mode 100644 index 0000000..4f079d2 --- /dev/null +++ b/web/themes/custom/caravane/assets/pictograms/arrow_forward.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/pictograms/download.svg b/web/themes/custom/caravane/assets/pictograms/download.svg new file mode 100644 index 0000000..ab6fe91 --- /dev/null +++ b/web/themes/custom/caravane/assets/pictograms/download.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/scss/main.scss b/web/themes/custom/caravane/assets/scss/main.scss index 487544f..24805c1 100644 --- a/web/themes/custom/caravane/assets/scss/main.scss +++ b/web/themes/custom/caravane/assets/scss/main.scss @@ -1177,12 +1177,71 @@ body{ } } } + > .pieces-jointes { + z-index: 1; + position: relative; + padding: 0 $modale-x-padding; + box-sizing: border-box; + display: flex; + flex-wrap: wrap; + gap: 1.8rem 1rem; + justify-content: space-between; + padding-bottom: 3rem; + > div { + padding-top: 5px; + > a { + background-color: white; + padding: 0.8rem 1rem; + padding-left: 1.2rem; + border-radius: 10rem; + text-decoration: none; + color: $main-color; + display: flex; + transition: transform 0.2s ease-in-out; + transform: translateY(0px); + &:hover { + transform: translateY(-4px); + } + } + > a.pj-link { + &::after { + mask: url('/themes/custom/caravane/assets/pictograms/download.svg') no-repeat center; + content: ''; + mask-size: contain; + background-color: $main-color; + display: inline-block; + width: 1rem; + height: 1rem; + margin-top: 0.3rem; + margin-left: 0.5rem; + } + } + > a.lien-link { + &::after { + mask: url('/themes/custom/caravane/assets/pictograms/arrow_forward.svg') no-repeat center; + content: ''; + mask-size: contain; + background-color: $main-color; + display: inline-block; + width: 1rem; + height: 1rem; + margin-top: 0.3rem; + margin-left: 0.5rem; + } + } + } + } > footer { + z-index: 0; .pattern-bottom { mask-image: linear-gradient(to top, rgba(0,0,0,1), rgba(0,0,0,0)); height: $modale-bottom-padding; position: absolute; bottom: 0; + width: 100%; + background-image: url(/themes/custom/caravane/assets/imgs/motif-caravane-invert-tile.png); + background-size: 300px; + background-size: repeat; } .related-etape-links { position: absolute;