diff --git a/web/themes/custom/caravane/assets/js/stores/content.js b/web/themes/custom/caravane/assets/js/stores/content.js index ac6838d..54e6d24 100644 --- a/web/themes/custom/caravane/assets/js/stores/content.js +++ b/web/themes/custom/caravane/assets/js/stores/content.js @@ -3,9 +3,11 @@ import { defineStore } from 'pinia'; import REST from '../api/rest-axios'; +import { useLayoutStore } from './layout'; + import { findContentByPath } from '../utils/content/findContentByPath'; import { getCleanDate, fetchFromRelationships, getRelatedEtape } from '../utils/content/contentFetchUtils'; -import { getCarteSensible, getTitreTexte, getChiffresCles, getDiaporama, getEntretien, getVideos } from '../utils/content/cleanParties'; +import { getCarteSensible, getTitreTexte, getChiffresCles, getDiaporama, getEntretien, getVideos, getDocument, getGallerie } from '../utils/content/cleanParties'; import { getPartenaires, getGouvernance, getRessources } from '../utils/content/multiItemPages'; export const useContentStore = defineStore('content', { @@ -24,39 +26,44 @@ export const useContentStore = defineStore('content', { const { contentType, rawContent } = await findContentByPath(contentTypes, path); this.contentType = contentType; - if (this.contentType === 'etape' || this.contentType === 'static') { + if ( + this.contentType === 'etape' + || this.contentType === 'static' + || this.contentType === 'ressourceItem' + ) { const vignettePromise = fetchFromRelationships('field_vignette', rawContent.relationships); - const partiesPromise = fetchFromRelationships('field_parties', rawContent.relationships); + const partiesPromise = fetchFromRelationships(this.contentType === 'ressourceItem' ? 'field_parties_ressource' : 'field_parties', rawContent.relationships); let previousEtapePromise, nextEtapePromise; - if (contentType === 'etape') { - // related étapes + + if (this.contentType === 'etape') { previousEtapePromise = getRelatedEtape('previous', path); nextEtapePromise = getRelatedEtape('next', path); - // 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: getCleanDate(rawContent.attributes.field_dates.value), end: getCleanDate(rawContent.attributes.field_dates.end_value), } } + + if (this.contentType === 'ressourceItem') { + this.content.ressourceType = rawContent.attributes.field_type_de_ressource; + this.content.auteurice = rawContent.attributes.field_autheurice; + this.content.date = getCleanDate(rawContent.attributes.field_date_ressource); + this.content.introduction = rawContent.attributes.field_introduction?.processed; + + useLayoutStore().hideEtapeList(true); + } - // pageTitle this.pageTitle = rawContent.attributes.metatag.find(tag => tag.tag === "meta")?.attributes.content; - - // contentTitle this.content.contentTitle = rawContent.attributes.title; const [vignetteData, partiesData] = await Promise.all([vignettePromise, partiesPromise]); @@ -76,7 +83,7 @@ export const useContentStore = defineStore('content', { if (partiesData) { const partiesPromises = partiesData.map(async (partie) => { const partieType = partie.type.replace(/^paragraph--/, ""); - let partieContent = { type: partieType }; + let partieContent = { type: partieType }; switch (partieType) { case 'carte_sensible': @@ -102,10 +109,41 @@ export const useContentStore = defineStore('content', { case 'video': partieContent.videos = getVideos(partie); break; + case 'document': + partieContent.document = await getDocument(partie); + break; + case 'galleries': + partieContent.gallerie = await getGallerie(partie); + break; } return partieContent; }); + // 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, + }); + } + } + } + this.content.parties = await Promise.all(partiesPromises); } @@ -132,6 +170,8 @@ export const useContentStore = defineStore('content', { switch (this.contentType) { case 'ressource': multiItemPageArray = await getRessources(rawContent); + this.content.ressourceTypes = new Set(multiItemPageArray.map(item => item.ressourceType)); + useLayoutStore().hideEtapeList(true); break; case 'partenaire': multiItemPageArray = await getPartenaires(rawContent); @@ -142,7 +182,6 @@ export const useContentStore = defineStore('content', { } this.content[`${this.contentType}s`] = multiItemPageArray; - console.log(this.content); } } catch (error) { this.error = 'Failed to fetch data'; @@ -157,6 +196,7 @@ export const useContentStore = defineStore('content', { this.content = {}; this.loading = !forFrontDisplay; this.error = null; + useLayoutStore().hideEtapeList(false); } }, }); diff --git a/web/themes/custom/caravane/assets/js/stores/layout.js b/web/themes/custom/caravane/assets/js/stores/layout.js index 008a901..23d9999 100644 --- a/web/themes/custom/caravane/assets/js/stores/layout.js +++ b/web/themes/custom/caravane/assets/js/stores/layout.js @@ -46,6 +46,23 @@ export const useLayoutStore = defineStore('layout', { this.toggleEtapeListScroll(isIntersecting, listeEtape, column, headerRect.height, animationToggleRect.top); }, + hideEtapeList(souldListHide) { + const etapeList = document.querySelector('#etapes-liste'); + const listContainer = etapeList.parentNode; + if (souldListHide) { + listContainer.style.minWidth = '30vw'; + etapeList.style.opacity = '0'; + setTimeout(() => { + etapeList.style.display = 'none'; + }, 300); + } else { + listContainer.style.minWidth = 'unset'; + etapeList.style.display = 'block'; + setTimeout(() => { + etapeList.style.opacity = '1'; + }, 10); + } + }, toggleEtapeListScroll(isIntersecting, listeEtape, column, headerHeight, animationToggleTop) { if (isIntersecting && !this.isEtapeListeScrollable || !isIntersecting && this.isEtapeListeScrollable) { diff --git a/web/themes/custom/caravane/assets/js/utils/content/cleanParties.js b/web/themes/custom/caravane/assets/js/utils/content/cleanParties.js index bb055ab..4247e91 100644 --- a/web/themes/custom/caravane/assets/js/utils/content/cleanParties.js +++ b/web/themes/custom/caravane/assets/js/utils/content/cleanParties.js @@ -1,5 +1,5 @@ import REST from '../../api/rest-axios'; -import { fetchFromRelationships } from './contentFetchUtils'; +import { fetchFromRelationships, getCleanDate } from './contentFetchUtils'; export async function getCarteSensible(partie) { @@ -131,4 +131,46 @@ export function getVideos(partie) { videos.push(videoUrl); } return videos; +} + +export async function getDocument(partie) { + // const documentFetch = await fetchFromRelationships('field_document', partie.relationships); + const uuid = partie.relationships.field_document.data.id; + const documentFetch = await REST.get(`/jsonapi/file/file/${uuid}`); + const url = documentFetch.data.data.attributes.uri.url; + + const titre = partie.attributes.field_titre; + const sousTitre = partie.attributes.field_sous_titre; + const date = partie.attributes.field_date ? getCleanDate(partie.attributes.field_date) : null; + const auteurice = partie.relationships.field_autheurice_s; + const description = partie.relationships.field_document.data.meta.description; + + const vignetteFetch = await REST.get(`/jsonapi/file/file/${partie.relationships.field_vignette.data.id}`); + const vignette = { url: vignetteFetch.data.data.attributes.image_style_uri.content_small, alt: partie.relationships.field_vignette.data.meta.alt }; + + return { url, titre, sousTitre, date, auteurice, description, vignette }; +} + +export async function getGallerie(partie) { + const gallerieFetch = await fetchFromRelationships('field_gallerie', partie.relationships); + + if (gallerieFetch) { + const titre = gallerieFetch.attributes.title; + const introduction = gallerieFetch.attributes.body?.processed; + + const imagesFetch = await fetchFromRelationships('field_images', gallerieFetch.relationships); + let images = []; + imagesFetch.forEach((image, index) => { + images.push({ + url: { + original: image.attributes.uri.url, + small: image.attributes.image_style_uri.content_small, + medium: image.attributes.image_style_uri.content_medium, + large: image.attributes.image_style_uri.content_large, + }, + alt: gallerieFetch.relationships.field_images.data[index].meta.alt, + }); + }); + return { titre, introduction, images }; + } } \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/utils/content/contentFetchUtils.js b/web/themes/custom/caravane/assets/js/utils/content/contentFetchUtils.js index 8ac8452..fcf5629 100644 --- a/web/themes/custom/caravane/assets/js/utils/content/contentFetchUtils.js +++ b/web/themes/custom/caravane/assets/js/utils/content/contentFetchUtils.js @@ -3,7 +3,7 @@ import REST from '../../api/rest-axios'; export async function fetchFromRelationships(field, relationships) { field = relationships[field] ? field : `${field}_static`; - if (relationships[field].links) { + if (relationships[field]?.links) { const contentLink = relationships[field].links.related.href; return REST.get(contentLink) .then(contentFetch => contentFetch.data.data) diff --git a/web/themes/custom/caravane/assets/js/utils/content/findContentByPath.js b/web/themes/custom/caravane/assets/js/utils/content/findContentByPath.js index 240d24f..0deff53 100644 --- a/web/themes/custom/caravane/assets/js/utils/content/findContentByPath.js +++ b/web/themes/custom/caravane/assets/js/utils/content/findContentByPath.js @@ -10,9 +10,9 @@ export async function findContentByPath(contentTypes, path) { ) ); - if (content) { + if (content) { return { - contentType: type, + contentType: content.type === 'node--ressource' ? 'ressourceItem' : type, rawContent: content, }; } diff --git a/web/themes/custom/caravane/assets/js/vuejs/Modale.vue b/web/themes/custom/caravane/assets/js/vuejs/Modale.vue index 56244ec..d6ff4b1 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/Modale.vue +++ b/web/themes/custom/caravane/assets/js/vuejs/Modale.vue @@ -10,6 +10,10 @@ :content="content" :couleur="content.couleur || brandColor" />
+
+ +
+ v-if="contentType === 'ressource'" + :content="content" + :couleur="brandColor" />
-

+
+
+ + +
+
\ No newline at end of file + +const searchQuery = ref(''); +const selectedType = ref(''); +const ressourcesToDisplay = ref({}); +const visibleItemsPerSection = 4; + +const filteredTypes = computed(() => { + return selectedType.value ? [selectedType.value] : props.content.ressourceTypes; +}); + +const ressourcesByType = (type) => { + return props.content.ressources + .filter(ressource => ressource.ressourceType === type) + .filter(ressource => + searchQuery.value === '' || + ressource.title.toLowerCase().includes(searchQuery.value.toLowerCase()) + ); +} + +const initializeRessources = (type) => { + ressourcesToDisplay.value[type] = ressourcesByType(type).slice(0, visibleItemsPerSection); +}; + +props.content.ressourceTypes.forEach(type => initializeRessources(type)); + +const loadMore = (type) => { + const currentLength = ressourcesToDisplay.value[type].length; + + if (currentLength < ressourcesByType(type).length) { + ressourcesToDisplay.value[type] = ressourcesByType(type).slice(0, currentLength + visibleItemsPerSection); + } +}; + +const showLess = (type) => { + ressourcesToDisplay.value[type] = ressourcesByType(type).slice(0, visibleItemsPerSection); +}; + +watch(searchQuery, () => { + props.content.ressourceTypes.forEach(type => { + initializeRessources(type); + }); +}); + + +let relatedItemCards, baseUrl; + +watch(ressourcesToDisplay.value, () => { + setClickableElements(); +}, { deep: true }); + +watch(selectedType, () => { + setClickableElements(); +}); + +onMounted(() => { + baseUrl = window.location.protocol + "//" + window.location.host; + setClickableElements(); +}); + +function setClickableElements() { + setTimeout(() => { + relatedItemCards = document.querySelectorAll('.ressource-item'); + handleClickableElements(relatedItemCards, store, router, baseUrl, siteName, mapStore); + }, 50); +} + + + \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue b/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue index ce9c24e..52eebe7 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue +++ b/web/themes/custom/caravane/assets/js/vuejs/components/ModaleHeader.vue @@ -14,7 +14,7 @@
-

{{content.contentTitle}} ({{ content.adresse.postal_code.slice(0, 2) }})

+

{{ contentType === 'ressourceItem' ? 'Centre de ressources' : content.contentTitle }} ({{ content.adresse.postal_code.slice(0, 2) }})

diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/RessourceItemHeader.vue b/web/themes/custom/caravane/assets/js/vuejs/components/RessourceItemHeader.vue new file mode 100644 index 0000000..abec93b --- /dev/null +++ b/web/themes/custom/caravane/assets/js/vuejs/components/RessourceItemHeader.vue @@ -0,0 +1,60 @@ + + + \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDiaporama.vue b/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDiaporama.vue index d24a54f..4450b92 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDiaporama.vue +++ b/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDiaporama.vue @@ -59,7 +59,7 @@ const handleImageClick = (event) => { const swiperEl = img.closest('swiper-container'); swiperEl.querySelectorAll('swiper-slide').forEach((slide) => { const img = slide.querySelector('img'); - const altText = slide.querySelector('figcaption')?.textContent || ''; + const altText = slide.querySelector('figcaption')?.textContent || ''; Object.values(props.partie.diaporama).forEach((image) => { if (image.url.medium === img.src) { swiperMedia.push({ src: image.url.large, alt: altText }); diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDocument.vue b/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDocument.vue new file mode 100644 index 0000000..b43f73e --- /dev/null +++ b/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleDocument.vue @@ -0,0 +1,88 @@ + + + + + \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleGallerie.vue b/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleGallerie.vue new file mode 100644 index 0000000..417823a --- /dev/null +++ b/web/themes/custom/caravane/assets/js/vuejs/components/parties/ModaleGallerie.vue @@ -0,0 +1,73 @@ + + + \ No newline at end of file diff --git a/web/themes/custom/caravane/assets/js/vuejs/composables/useImageModale.js b/web/themes/custom/caravane/assets/js/vuejs/composables/useImageModale.js index 36041b1..653f885 100644 --- a/web/themes/custom/caravane/assets/js/vuejs/composables/useImageModale.js +++ b/web/themes/custom/caravane/assets/js/vuejs/composables/useImageModale.js @@ -9,7 +9,7 @@ export function useImageModal() { const hamburger = document.querySelector('#hamburger'); const menu = document.querySelector('#menu'); - const openImageModale = (src, alt, swiperMedia) => { + const openImageModale = (src, alt, swiperMedia) => { currentImage.value = { src, alt }; swiperPopupContent.value = swiperMedia || []; isModaleOpen.value = true; diff --git a/web/themes/custom/caravane/assets/scss/main.scss b/web/themes/custom/caravane/assets/scss/main.scss index 5ddb371..fb0b1ed 100644 --- a/web/themes/custom/caravane/assets/scss/main.scss +++ b/web/themes/custom/caravane/assets/scss/main.scss @@ -773,7 +773,8 @@ body{ font-size: $labeur-font-size-desktop; width: $modale-width-desktop; } - &:has(#centre-de-ressource) { + &:has(#centre-de-ressource), + &:has(#ressource-item-header) { @media screen and (min-width: $tablet-min-width) { left: 8vw; width: 84vw; @@ -927,6 +928,9 @@ body{ padding: 0 $modale-x-padding; padding-bottom: 5vh; box-sizing: border-box; + &:has(#ressource-item-header) { + padding-right: 50%; + } > .partie, > #equipe { width: 100%; @@ -954,7 +958,8 @@ body{ } .partie-title, > .chiffres-cles, - > .entretien { + > .entretien, + > .gallerie { > h3 { position: relative; display: inline-block; @@ -1091,6 +1096,54 @@ body{ > .videos iframe { margin: 2rem 0; } + > .gallerie { + > .intro { + margin-bottom: 2rem; + } + > .images-grid { + display: grid; + grid-template-columns: 1fr 1fr; + column-gap: 5rem; + row-gap: 2rem; + } + figure { + margin: 0; + > img { + cursor: pointer; + transform: scale(1); + transition: transform 0.2s ease-in-out; + &:hover { + transform: scale(1.02); + } + &.vertical { + box-sizing: border-box; + padding: 0 25%; + } + } + } + } + > .document { + .download { + > a { + color: $main-color; + text-decoration: none; + font-size: $sm-font-size-mobile; + background-color: $brand-color; + font-weight: bold; + padding: 0.5rem 1rem; + border-radius: 1rem; + display: inline-block; + transform: scale(1); + transition: transform 0.2s ease-in-out; + &:hover { + transform: scale(1.02); + } + @media screen and (min-width: $desktop-min-width) { + font-size: $sm-font-size-desktop; + } + } + } + } } .caption { font-size: $sm-font-size-mobile; @@ -1174,7 +1227,120 @@ body{ } } #centre-de-ressource { - background-color: red; + > .intro { + + } + > .type-section { + > h3 { + display: inline-block; + font-size: $l-font-size-mobile; + font-family: 'Joost', sans-serif; + margin: 0; + margin-bottom: 2rem; + z-index: 1; + position: relative; + padding: 0 0.5rem; + background: linear-gradient(transparent 70%, $brand-color 70%); + @media screen and (min-width: $desktop-min-width) { + font-size: $l-font-size-desktop; + } + } + > .ressource-list > div { + display: grid; + grid-template-columns: repeat(4, 1fr); + align-items: start; + gap: 2rem; + margin-bottom: 2.5rem; + > .ressource-item { + display: flex; + gap: 1.5rem; + cursor: pointer; + transform: scale(1); + transition: transform 0.2s ease-in-out; + &:hover { + transform: scale(1.05); + } + > figure { + width: 40%; + margin: 0; + } + > div { + width: 50%; + > h4 { + font-size: $m-font-size-mobile; + font-family: 'Joost', sans-serif; + margin: 0; + margin-bottom: 0.5rem; + @media screen and (min-width: $desktop-min-width) { + font-size: $m-font-size-desktop; + } + } + > p { + margin: 0; + font-size: $sm-font-size-mobile; + @media screen and (min-width: $desktop-min-width) { + font-size: $sm-font-size-desktop; + } + } + } + } + } + > .button-container { + display: flex; + justify-content: center; + > .ressource-button { + display: inline; + font-size: $sm-font-size-mobile; + background-color: $brand-color; + padding: 0.5rem 1.5rem; + border-radius: 1rem; + cursor: pointer; + transition: transform 0.2s ease-in-out; + transform: scale(1); + &:hover { + transform: scale(0.95); + } + @media screen and (min-width: $desktop-min-width) { + font-size: $sm-font-size-desktop; + } + } + } + } + } + > #ressource-item-header { + > .retour { + margin-bottom: 1.5rem; + > p { + display: inline-block; + cursor: pointer; + font-size: $sm-font-size-mobile; + @media screen and (min-width: $desktop-min-width) { + font-size: $sm-font-size-desktop; + } + } + } + > .type, + > .meta { + text-align: center; + font-size: $m-font-size-mobile; + font-family: 'Joost', sans-serif; + @media screen and (min-width: $desktop-min-width) { + font-size: $m-font-size-desktop; + } + } + > .title { + width: 100%; + text-align: center; + > h2 { + display: inline-block; + margin-top: 1rem; + font-size: $xl-font-size-mobile; + font-family: 'Joost', sans-serif; + @media screen and (min-width: $desktop-min-width) { + font-size: $xl-font-size-desktop; + } + } + } } } > .pieces-jointes {