This commit is contained in:
2026-02-16 07:59:52 +01:00
parent 1fc267faa8
commit b8b8e53f07
70 changed files with 3088 additions and 272 deletions

View File

@@ -1,15 +0,0 @@
<template>
<ConcertCard
v-for="c in concerts"
:key="c.id"
:title="c.title"
:date-label="c.dateLabel"
:venue="c.venue"
:city="c.city"
:image="{ src: c.imageUrl, alt: c.imageAlt }"
:tags="c.tags"
:price-from="c.priceFrom"
:is-sold-out="c.soldOut"
:href="`/concerts/${c.slug}`"
/>
</template>

883
app/pages/concerts/[id].vue Normal file
View File

@@ -0,0 +1,883 @@
<template>
<div>
<section v-if="pending" aria-busy="true" aria-live="polite">
<p>en cours de chargement...</p>
</section>
<template v-else>
<PageSection tone="" content-size="default" class="breadcrum_wp">
<Breadcrumb :current-label="concert?.titre_concert || ''" />
</PageSection>
<section class="fiche_header_wp">
<div class="fiche_header_wp_gauche"></div>
<div class="fiche_header_wp_gauche_carre"></div>
<div class="fiche_header_inner">
<div class="fiche_header_titres">
<div>
<DsHeading as="h1" tone="default" textcase="uppercase" class="concert-card__title">
{{ concert.titre_concert }}
</DsHeading>
</div>
<div>
<DsText as="p" size="md" tone="default" class="" v-if="concert.sous_titre_concert">
{{ concert.sous_titre_concert }}
</DsText>
</div>
</div>
<div class="fiche_header_img">
<DsMedia
v-if="illustration?.url"
:src="illustration.url"
:alt="illustration.alternativeText || concert?.titre_concert || ''"
ratio="3-4"
/>
</div>
<div class="fiche_header_bandeau"></div>
<div class="fiche_header_infos">
<div>
<DsHeading as="h2" tone="invert" textcase="uppercase" class="fiche_header_infos_genre" v-if="genreLabel">
{{ genreLabel }}
</DsHeading>
</div>
<div v-if="concert.duree_concert">
<DsText as="p" tone="invert" weight="bold" spacing="space-0" class="">
DURÉE {{ concert.duree_concert }}
</DsText>
<DsText as="p" tone="invert" class="" v-if="concert.duree_entracte">
Entracte {{ concert.duree_entracte }}
</DsText>
</div>
<div v-if="concert.production_concert">
<DsText as="p" tone="invert" class="">
{{ concert.production_concert }}
</DsText>
</div>
</div>
</div>
<div class="fiche_header_wp_droite"></div>
</section>
<PageSection tone="" content-size="default">
<section class="fiche_details_wp">
<section class="distribution_wp">
<div v-if="directionsOndif.length">
<div v-for="d in directionsOndif" :key="d.id" class="distribution_item">
<DsText as="p" tone="default" size="lg" v-if="d.postes_artiste_ondif?.length" class="distribution_item_poste direction">
{{ d.postes_artiste_ondif.map((p) => p.nom_poste).join(', ') }}
</DsText>
<DsText as="p" tone="default" size="lg" weight="bold" class="">
{{ d.nom_artiste_ondif }}
</DsText>
</div>
</div>
<div v-if="directionsInvite.length">
<div v-for="d in directionsInvite" :key="d.id" class="distribution_item">
<DsText as="p" tone="default" size="lg" v-if="d.postes_artiste_invite?.length" class="distribution_item_poste direction">
{{ d.postes_artiste_invite.map((p) => p.nom_poste).join(', ') }}
</DsText>
<DsText as="p" tone="default" size="lg" weight="bold" class="">
{{ d.nom_artiste_invite }}
</DsText>
</div>
</div>
<div v-if="artistesOndif.length">
<div v-for="d in artistesOndif" :key="d.id" class="distribution_item">
<DsText as="p" tone="default" size="lg" v-if="d.postes_artiste_ondif?.length" class="distribution_item_poste">
{{ d.postes_artiste_ondif.map((p) => p.nom_poste).join(', ') }}
</DsText>
<DsText as="p" tone="default" size="lg" class="">
{{ d.nom_artiste_ondif }}
</DsText>
</div>
</div>
<div v-if="artistesInvite.length">
<div v-for="d in artistesInvite" :key="d.id" class="distribution_item">
<DsText as="p" tone="default" size="lg" v-if="d.postes_artiste_invite?.length" class="distribution_item_poste">
{{ d.postes_artiste_invite.map((p) => p.nom_poste).join(', ') }}
</DsText>
<DsText as="p" tone="default" size="lg" class="">
{{ d.nom_artiste_invite }}
</DsText>
</div>
</div>
</section>
<section class="programme_wp">
<div>
<DsHeading as="h2" tone="invert" textcase="uppercase" class="programme_titre">
PROGRAMME
</DsHeading>
</div>
<div class="programme_list">
<div v-for="(p, i) in programmes" :key="p.id || i" class="programme_item" >
<DsText as="p" tone="default" size="lg" spacing="space-0" weight="semibold" class="programme_compositeur">
{{ p.compositeur_programme }}
</DsText>
<DsText as="p" tone="default" size="lg" spacing="space-0" class="programme_oeuvre">
{{ p.oeuvre_programme }}
</DsText>
<DsText as="p" tone="default" size="lg" class="" v-if="p.piece_programme">
{{ p.piece_programme }}
</DsText>
</div>
</div>
</section>
<div v-if="representations.length" class="representation_wp">
<div v-for="(r, i) in representations" :key="r.id || i" class="representation_item">
<div>
<DsHeading as="h6" tone="default" v-if="r.date_debut_representation">
{{ formatDateLong(r.date_debut_representation) }} <span v-if="r.date_fin_representation">- {{ formatDateLong(r.date_fin_representation) }}</span>
</DsHeading>
</div>
<div>
<DsHeading as="h5" tone="default" v-if="r.lieu_representation?.nom_lieu" class="representation_item_lieu">
{{ r.lieu_representation.nom_lieu }}
</DsHeading>
</div>
<div>
<DsText as="p" tone="default" spacing="space-0" v-if="r.lieu_representation?.adresse_lieu">
{{ r.lieu_representation.adresse_lieu }}
</DsText>
</div>
<div class="representation_item_comment_wp">
<DsText as="p" tone="default" spacing="space-0" v-if="r.commentaire_representation" class="representation_item_comment">
{{ r.commentaire_representation }}
</DsText>
</div>
<div class="representation_cta">
<a
v-if="r.lien_billetterie_representation"
:href="r.lien_billetterie_representation"
target="_blank"
rel="noopener noreferrer"
>
Réserver
</a>
</div>
</div>
</div>
</section>
<section class="description_wp">
<StrapiBlocksConvert :blocks="concert?.description_concert" />
</section>
<section class="img-gallery_wp">
<div v-if="imagesConcert.length" class="img-gallery">
<DsMedia
v-for="img in imagesConcert"
:key="img.id || img.url"
:src="img.url"
:alt="img.alternativeText || concert?.titre_concert || ''"
/>
</div>
</section>
<section class="youtube_wp">
<div v-if="youtubeEmbeds.length" class="youtube-list">
<div v-for="v in youtubeEmbeds" :key="v.id" class="youtube-item">
<iframe
:src="v.src"
title="Vidéo YouTube"
loading="lazy"
referrerpolicy="strict-origin-when-cross-origin"
allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"
allowfullscreen
/>
</div>
</div>
</section>
</PageSection>
</template>
</div>
</template>
<script setup>
import { formatDateLong } from "@/utils/dateFormat.js"
import DsMedia from '@root/design-system/primitives/DsMedia.vue'
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
import DsText from '@root/design-system/primitives/DsText.vue'
import DsButton from '@root/design-system/primitives/DsButton.vue'
const route = useRoute()
//////////////////////////////////////////////////////////////
// RÉCUPÉRATION DU CONTENU
//////////////////////////////////////////////////////////////
const concertSlug = computed(() => String(route.params.id || ''))
const populate = {
saison_concert: true,
genre_concert: true,
type_audience_concert: true,
direction_ondif_concert: { postes_artiste_ondif: true },
direction_invite_concert: { postes_artiste_invite: true },
artistes_ondif_concert: { postes_artiste_ondif: true },
artistes_invite_concert: { postes_artiste_invite: true },
image_illustration_concert: true,
images_concert: true,
videos_concert: true,
audios_concert: true,
programme_concert: true,
representation_concert: { lieu_representation: true },
liens_youtube_concert: true,
}
const filters = computed(() => ({
slug_concert: {
$eq: concertSlug.value,
},
}))
const { concerts, pending, error } = useConcerts({
locale: 'fr-FR',
populate,
filters,
limit: 1,
upcomingOnly: false,
})
const concert = computed(() => concerts.value?.[0] || {})
useSeoMeta({
title: () => concert.value?.titre_concert || 'Concert',
description: () => concert.value?.resume_concert || undefined,
})
const genreLabel = computed(() => {
const g = concert.value?.genre_concert
// Strapi relation classique
if (Array.isArray(g?.data)) return g.data.map(x => x?.attributes?.nom_genre).filter(Boolean).join(', ')
if (g?.data) return g.data?.attributes?.nom_genre || ''
// Si déjà normalisé/flat
if (Array.isArray(g)) return g.map(x => x?.nom_genre).filter(Boolean).join(', ')
return g?.nom_genre || ''
})
const directionsOndif = computed(() => {
const value = concert.value?.direction_ondif_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
const directionsInvite = computed(() => {
const value = concert.value?.direction_invite_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
const artistesOndif = computed(() => {
const value = concert.value?.artistes_ondif_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
const artistesInvite = computed(() => {
const value = concert.value?.artistes_invite_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
const programmes = computed(() => {
const value = concert.value?.programme_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
const representations = computed(() => {
const value = concert.value?.representation_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
const illustration = computed(() => {
const m = concert.value?.image_illustration_concert
if (!m) return null
if (m.url) return m
return null
})
const imagesConcert = computed(() => {
const value = concert.value?.images_concert
if (!value) return []
if (Array.isArray(value) && value[0]?.url) return value
return Array.isArray(value) ? value : []
})
const youtube = computed(() => {
const value = concert.value?.liens_youtube_concert
if (!value) return []
return Array.isArray(value) ? value : [value]
})
function getYoutubeId(url = '') {
try {
const u = new URL(url)
if (u.hostname.includes('youtu.be')) return u.pathname.slice(1)
if (u.pathname.startsWith('/shorts/')) return u.pathname.split('/')[2]
if (u.pathname.startsWith('/embed/')) return u.pathname.split('/')[2]
return u.searchParams.get('v')
} catch {
return null
}
}
const youtubeEmbeds = computed(() =>
youtube.value
.map((item) => {
const id = getYoutubeId(item?.lien_youtube)
if (!id) return null
return {
id: item.id || id,
src: `https://www.youtube-nocookie.com/embed/${id}?rel=0&modestbranding=1&iv_load_policy=3&playsinline=1`,
}
})
.filter(Boolean)
)
</script>
<style lang="scss">
.breadcrum_wp {
padding-top: 30px;
}
.fiche_header_wp {
display: grid;
@media (min-width: 0px) and (max-width: 600px) {
grid-template-columns: 1fr;
grid-template-rows: auto 510px 20px 200px;
padding-top: 40px;
}
@media (min-width: 600px) {
grid-template-columns: minmax(10px, 10px) 580px 0px;
grid-template-rows: 40px 280px 20px 230px;
}
@media (min-width: 700px) {
grid-template-columns: minmax(10px, 10px) 660px 0px;
grid-template-rows: 70px 250px 90px 300px;
}
@media (min-width: 800px) {
grid-template-columns: minmax(10px, 10px) 780px minmax(10px, 10px);
grid-template-rows: 60px 280px 70px 300px;
}
@media (min-width: 900px) {
grid-template-columns: minmax(10px, 10px) 860px minmax(10px, 10px);
grid-template-rows: 90px 340px 100px 250px;
}
@media (min-width: 1000px) {
grid-template-columns: minmax(20px, auto) 950px minmax(10px, auto);
grid-template-rows: 90px 340px 120px 270px;
}
@media (min-width: 1100px) {
grid-template-columns: minmax(20px, auto) 1020px minmax(20px, auto);
grid-template-rows: 90px 340px 140px 290px;
}
@media (min-width: 1200px) {
grid-template-columns: minmax(20px, auto) 1100px minmax(20px, auto);
grid-template-rows: 90px 340px 160px 330px;
}
@media (min-width: 1300px) {
grid-template-columns: minmax(20px, auto) 1200px minmax(20px, auto);
grid-template-rows: 90px 340px 160px 330px;
}
@media (min-width: 1400px) {
grid-template-columns: minmax(20px, auto) 1300px minmax(20px, auto);
grid-template-rows: 90px 340px 160px 380px;
}
@media (min-width: 1500px) {
grid-template-columns: minmax(20px, auto) 1400px minmax(20px, auto);
grid-template-rows: 90px 340px 160px 380px;
}
}
.fiche_header_wp_gauche {
@media (min-width: 0px) and (max-width: 600px) {
display: none;
}
@media (min-width: 600px) {
grid-column: 1;
}
}
.fiche_header_wp_gauche_carre {
@media (min-width: 0px) and (max-width: 600px) {
display: none;
}
@media (min-width: 600px) {
grid-column: 1;
grid-row: 4;
}
background-color: var(--c-backgroud-black);
}
.fiche_header_wp_droite {
@media (min-width: 0px) and (max-width: 600px) {
display: none;
}
@media (min-width: 600px) {
grid-column: 3;
}
}
.fiche_header_inner {
@media (min-width: 0px) and (max-width: 600px) {
grid-column: 1;
grid-row: 1/5;
}
@media (min-width: 600px) {
grid-column: 2;
grid-row: 1/5;
}
display: grid;
@media (min-width: 0px) and (max-width: 600px) {
grid-template-columns: 4fr 1fr 0.5fr;
grid-template-rows: auto 510px 20px 200px;
}
@media (min-width: 600px) {
width: 575px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 40px 280px 20px 230px;
}
@media (min-width: 700px) {
width: 675px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 70px 250px 90px 300px;
}
@media (min-width: 800px) {
width: 780px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 60px 280px 70px 300px;
}
@media (min-width: 900px) {
width: 860px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 100px 250px;
}
@media (min-width: 1000px) {
width: 950px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 120px 270px;
}
@media (min-width: 1100px) {
width: 1020px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 140px 290px;
}
@media (min-width: 1200px) {
width: 1100px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 330px;
}
@media (min-width: 1300px) {
width: 1200px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 330px;
}
@media (min-width: 1400px) {
width: 1300px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 380px;
}
@media (min-width: 1500px) {
width: 1400px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 380px;
}
@media (min-width: 1600px) {
width: 1400px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 380px;
}
@media (min-width: 1700px) {
width: 1400px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 380px;
}
@media (min-width: 1800px) {
width: 1400px;
grid-template-columns: 4fr 0.5fr 0.5fr 6.5fr;
grid-template-rows: 90px 340px 160px 380px;
}
}
.fiche_header_titres {
@media (min-width: 0px) and (max-width: 600px) {
grid-column: 1;
grid-row: 1;
padding-bottom: 20px;
padding-left: 10px;
}
@media (min-width: 600px) {
grid-column: 1;
grid-row: 2;
}
display: grid;
align-content: start;
gap: 0.75rem;
h1 {
font-size: 55px;
@media (min-width: 0px) and (max-width: 600px) {
font-size: 25px;
}
@media (min-width: 600px) {
font-size: 25px;
}
@media (min-width: 700px) {
font-size: 30px;
}
@media (min-width: 800px) {
font-size: 30px;
}
@media (min-width: 900px) {
font-size: 40px;
}
@media (min-width: 1000px) {
font-size: 40px;
}
@media (min-width: 1100px) {
font-size: 40px;
}
@media (min-width: 1200px) {
font-size: 50px;
}
@media (min-width: 1300px) {
font-size: 55px;
}
@media (min-width: 1400px) {
font-size: 55px;
}
@media (min-width: 1500px) {
font-size: 55px;
}
@media (min-width: 1600px) {
font-size: 55px;
}
@media (min-width: 1700px) {
font-size: 55px;
}
@media (min-width: 1800px) {
font-size: 55px;
}
}
}
.fiche_header_img {
@media (min-width: 0px) and (max-width: 600px) {
grid-column: 1 / 4;
grid-row: 2 / 4;
}
@media (min-width: 600px) {
grid-column: 3 / 5;
grid-row: 1 / 5;
}
overflow: hidden;
.ds-media {
height: 100%;
}
}
.fiche_header_infos {
@media (min-width: 0px) and (max-width: 600px) {
grid-column: 1 / 2;
grid-row: 4;
margin-top: -30px;
}
@media (min-width: 600px) {
grid-column: 1;
grid-row: 4;
}
display: grid;
align-content: center;
justify-content: end;
@media (min-width: 0px) and (max-width: 700px) {
gap: 7px;
}
@media (min-width: 700px) {
gap: 20px;
}
text-align: right;
.fiche_header_infos_genre {
font-weight: 900;
}
}
.fiche_header_bandeau {
@media (min-width: 0px) and (max-width: 600px) {
grid-column: 1 / 3;
grid-row: 4;
margin-top: -30px;
}
@media (min-width: 600px) {
grid-column: 1 / 4;
grid-row: 4;
}
background-color: var(--c-backgroud-black);
}
/* ============================ */
/* DISTRIBUTION / PROGRAMME */
/* ============================ */
.fiche_details_wp {
.distribution_wp {
padding-top: 50px;
padding-bottom: 50px;
padding-left: 20px;
.distribution_item {
display: flex;
.distribution_item_poste {
padding-right: 10px;
font-weight: 200;
}
.direction {
font-weight: 400;
}
}
}
.programme_wp {
background-color: var(--c-backgroud-brandreverse);
margin-bottom: 70px;
padding-top: 50px;
padding-right: 30px;
padding-left: 40px;
padding-bottom: 50px;
/* DÉCALAGE DU BLOC VERS LA DROITE */
position: relative;
@media (min-width: 0px) and (max-width: 700px) {
width: 89vw;
left: 5%;
}
@media (min-width: 700px) {
width: 67vw;
left: 30%;
}
@media (min-width: 800px) {
width: 50vw;
left: 49%;
}
transform: translateX(0px);
margin-left: 0px;
margin-right: 0px;
box-sizing: border-box;
display: block;
justify-content: initial;
align-items: initial;
.programme_titre {
padding-bottom: 20px;
}
.programme_list {
display: flex;
flex-wrap: wrap;
column-gap: 20px;
row-gap: 20px;
}
.programme_item {
flex: 1 0 200px;
background-color: var(--c-surface);
padding-top: 14px;
padding-right: 20px;
padding-left: 18px;
padding-bottom: 10px;
}
}
}
/* ============================ */
/* REPRÉSENTATIONS */
/* ============================ */
.representation_wp {
display: flex;
flex-wrap: wrap;
/* justify-content: center; */
column-gap: 30px;
row-gap: 30px;
padding-bottom: 70px;
@media (min-width: 0px) and (max-width: 500px) {
padding-left: 20px;
padding-right: 5px;
}
.representation_item {
@media (min-width: 0px) and (max-width: 500px) {
width: 100%;
}
@media (min-width: 500px) {
max-width: 215px;
}
@media (min-width: 600px) {
max-width: 262px;
}
@media (min-width: 700px) {
max-width: 300px;
}
flex: 1 1 300px;
display: grid;
border: 2px var(--c-brand_rouge) solid;
padding-top: 20px;
> * {
padding-left: 20px;
padding-right: 20px;
}
.representation_item_comment_wp {
padding-top: 5px;
}
.representation_item_comment {
background-color: lightgray;
padding: 5px;
}
}
.representation_cta {
color: var(--c-surface);
background-color: var(--c-brand_rouge);
margin-top: 15px;
padding-top: 10px;
padding-bottom: 10px;
text-align: center;
a {
font-family: var(--font-roboto);
font-weight: 600;
font-size: 18px;
}
}
}
/* ============================ */
/* DESCRIPTION */
/* ============================ */
.description_wp {
display: flex;
justify-content: center;
padding-bottom: 70px;
padding-left: 10px;
padding-right: 10px;
> * {
max-width: 570px;
display: flex;
flex-direction: column;
}
}
/* ============================ */
/* GALERIES */
/* ============================ */
.img-gallery_wp {
padding-bottom: 50px;
}
.img-gallery {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(220px, 520px));
gap: 1rem;
justify-content: center;
margin-top: 1.5rem;
width: 100%;
}
.img-gallery > * {
display: block;
width: 100%;
overflow: hidden;
border-radius: 5px;
}
.img-gallery :deep(.ds-media) {
display: block;
width: 100%;
height: auto;
background: transparent;
}
.img-gallery :deep(.ds-media__img) {
display: block;
width: 100%;
max-width: 100%;
height: auto;
transition: transform 0.25s ease, box-shadow 0.25s ease;
}
.img-gallery :deep(.ds-media__img:hover) {
transform: scale(1.02);
}
@media (max-width: 1100px) {
.img-gallery {
grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
}
}
@media (max-width: 820px) {
.img-gallery {
grid-template-columns: repeat(auto-fit, minmax(180px, 1fr));
gap: 0.75rem;
}
}
@media (max-width: 520px) {
.img-gallery {
grid-template-columns: 1fr;
}
}
.youtube_wp {
margin-bottom: 70px;
}
.youtube-list {
display: grid;
justify-content: center;
/* flex-wrap: wrap; */
gap: 20px;
}
.youtube-item {
@media (min-width: 0px) and (max-width: 300px) {
min-width: 290px;
}
@media (min-width: 300px) {
min-width: 298px;
}
@media (min-width: 400px) {
min-width: 398px;
}
@media (min-width: 500px) {
min-width: 480px;
}
@media (min-width: 600px) {
min-width: 580px;
}
@media (min-width: 700px) {
min-width: 670px;
}
}
.youtube-item iframe {
aspect-ratio: 16 / 9;
border: 0;
}
</style>

View File

@@ -0,0 +1,71 @@
<template>
<div>
<PageSection tone="dark" content-size="default">
<SectionTitle tone="invert" pad="md">
LES CONCERTS À VENIR
</SectionTitle>
</PageSection>
<PageSection padded_size="md" content-size="default" class="">
<ConcertCardList :highlight-first="false" :limit-cards-on-breakpoint="false">
<ConcertCard
v-for="c in concerts"
:key="c.id"
:id="c.slug_concert"
:title="c.titre_concert"
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
:adresse_lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
:description="c.resume_concert"
:imageUrl="c.image_illustration_concert?.url"
:imageAlt="c.image_illustration_concert?.alternativeText"
:href="`${c.slug_concert}`"
/>
</ConcertCardList>
</PageSection>
</div>
</template>
<script setup>
import { onMounted } from "vue"
import { formatDateLong } from "@/utils/dateFormat.js"
const { concerts, refresh } = useConcerts({
locale: "fr-FR",
populate: {
saison_concert: true,
genre_concert: true,
type_audience_concert: true,
direction_ondif_concert: { postes_artiste_ondif: true },
direction_invite_concert: { postes_artiste_invite: true },
artistes_ondif_concert: { postes_artiste_ondif: true },
artistes_invite_concert: { postes_artiste_invite: true },
image_illustration_concert: true,
images_concert: true,
videos_concert: true,
audios_concert: true,
programme_concert: true,
representation_concert: { lieu_representation: true },
liens_youtube_concert: true,
},
filters: {
saison_concert: {
nom_saison: {
$eq: "2025/2026",
},
},
},
upcomingOnly: true,
})
onMounted(() => {
if (!concerts.value?.length) {
refresh()
}
})
</script>

View File

@@ -1,24 +0,0 @@
<template>
<div>
<div v-if="toto">
<h1>#{{route.params.id }} / {{ toto.title }}</h1>
<p>{{ toto.body }}</p>
</div>
<div v-else>
<p>Chargement...</p>
</div>
</div>
</template>
<script setup>
const route = useRoute()
const {data: toto} = await useFetch(() => 'https://jsonplaceholder.typicode.com/posts/' + route.params.id, { lazy: true })
useSeoMeta({
title: () => toto.value?.title
})
</script>
<style lang="scss">
</style>

View File

@@ -0,0 +1,4 @@
<script setup>
await navigateTo('/concerts/agenda', { redirectCode: 301 })
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,71 @@
<template>
<div>
<PageSection tone="dark" content-size="default">
<SectionTitle tone="invert" pad="md">
SAISON 2025/2026
</SectionTitle>
</PageSection>
<PageSection padded_size="md" content-size="default" class="">
<ConcertCardList :highlight-first="false" :limit-cards-on-breakpoint="false">
<ConcertCard
v-for="c in concerts"
:key="c.id"
:id="c.slug_concert"
:title="c.titre_concert"
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
:adresse_lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
:description="c.resume_concert"
:imageUrl="c.image_illustration_concert?.url"
:imageAlt="c.image_illustration_concert?.alternativeText"
:href="`${c.slug_concert}`"
/>
</ConcertCardList>
</PageSection>
</div>
</template>
<script setup>
import { onMounted } from "vue"
import { formatDateLong } from "@/utils/dateFormat.js"
const { concerts, refresh } = useConcerts({
locale: "fr-FR",
populate: {
saison_concert: true,
genre_concert: true,
type_audience_concert: true,
direction_ondif_concert: { postes_artiste_ondif: true },
direction_invite_concert: { postes_artiste_invite: true },
artistes_ondif_concert: { postes_artiste_ondif: true },
artistes_invite_concert: { postes_artiste_invite: true },
image_illustration_concert: true,
images_concert: true,
videos_concert: true,
audios_concert: true,
programme_concert: true,
representation_concert: { lieu_representation: true },
liens_youtube_concert: true,
},
filters: {
saison_concert: {
nom_saison: {
$eq: "2025/2026",
},
},
},
upcomingOnly: false,
})
onMounted(() => {
if (!concerts.value?.length) {
refresh()
}
})
</script>

View File

@@ -82,7 +82,7 @@
<ConcertCard
id="1"
title="TITRE DU CONCERT EN MAJUSCULE"
venue="Nom du lieu, éventuellement de la salle"
lieu="Nom du lieu, éventuellement de la salle"
dateISO="2026-01-15T20:30:00+01:00"
dateLabel="Jeudi 15 janvier 2026 — 20h30"
description="Description du concert assez courte qui reprend l'essentiel, les artistes... On pourra écrire un nombre de lettres limitées."

View File

@@ -6,7 +6,7 @@
<ConcertCard
id="1"
title="TITRE DU CONCERT EN MAJUSCULE"
venue="Nom du lieu, éventuellement de la salle"
lieu="Nom du lieu, éventuellement de la salle"
dateISO="2026-01-15T20:30:00+01:00"
dateLabel="Jeudi 15 janvier 2026 — 20h30"
description="Description du concert assez courte qui reprend l'essentiel, les artistes... On pourra écrire un nombre de lettres limitées."
@@ -39,10 +39,8 @@
const runtimeConfig = useRuntimeConfig()
const STRAPI_URL = runtimeConfig.public.strapiUrl
console.log("STRAPI_URL : ",STRAPI_URL)
// Config app (pour ton SEO)
// Config app
const config = useAppConfig()
useSeoMeta({
title: config.title
@@ -78,10 +76,6 @@
const appConfig = useAppConfig()
console.log("test 3 : ",appConfig.title) // "Mon site Nuxt"
onMounted(() => {
clientLog('info', 'test de log depuis vuejs', { })
clientLog('info', `STRAPI_URL : ${STRAPI_URL}`, { strapiUrl: STRAPI_URL })
})
</script>
<style>

View File

@@ -1,4 +1,5 @@
<template>
<div>
<!-- ================== -->
<!-- Fond noir -->
@@ -15,40 +16,17 @@
<PageSection padded_size="md" content-size="default" class="remonter_concert_list">
<ConcertCardList>
<ConcertCard
id="1"
title="TITRE DU CONCERT EN MAJUSCULE"
venue="Nom du lieu, éventuellement de la salle"
dateISO="2026-01-15T20:30:00+01:00"
dateLabel="Jeudi 15 janvier 2026 — 20h30"
description="Description du concert assez courte qui reprend l'essentiel, les artistes... On pourra écrire un nombre de lettres limitées."
imageUrl="https://picsum.photos/id/56/500/700"
imageAlt="Orchestre sur scène"
ctaHref="/concert[id]"
detailsHref="/concerts/concert_template"
/>
<ConcertCard
id="1"
title="TITRE DU CONCERT EN MAJUSCULE"
venue="Nom du lieu, éventuellement de la salle"
dateISO="2026-01-15T20:30:00+01:00"
dateLabel="Jeudi 15 janvier 2026 — 20h30"
description="Description du concert assez courte qui reprend l'essentiel, les artistes... On pourra écrire un nombre de lettres limitées."
imageUrl="https://picsum.photos/id/56/500/700"
imageAlt="Orchestre sur scène"
ctaHref="/concert[id]"
detailsHref="/concerts/concert_template"
/>
<ConcertCard
id="1"
title="TITRE DU CONCERT EN MAJUSCULE"
venue="Nom du lieu, éventuellement de la salle"
dateISO="2026-01-15T20:30:00+01:00"
dateLabel="Jeudi 15 janvier 2026 — 20h30"
description="Description du concert assez courte qui reprend l'essentiel, les artistes... On pourra écrire un nombre de lettres limitées."
imageUrl="https://picsum.photos/id/56/500/700"
imageAlt="Orchestre sur scène"
ctaHref="/concert[id]"
detailsHref="/concerts/concert_template"
v-for="c in concerts"
:key="c.id"
:id="c.slug_concert"
:title="c.titre_concert"
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
:description="c.resume_concert"
:imageUrl="c.image_illustration_concert?.url"
:imageAlt="c.image_illustration_concert?.alternativeText"
:href="`/concerts/${c.slug_concert}`"
/>
</ConcertCardList>
</PageSection>
@@ -65,7 +43,7 @@
</SectionTitle>
<SectionContent pad="xs" class="theme_ppt--description">
<DsText tone="invert" size="lg" class="theme_ppt--txt" >
Ici le texte qui décrit le concept de Tous à lOrchestre - Dans les régions - Ici le texte qui décrit le concept de Tous à lOrchestre - Dans les régions - Ici le texte qui décrit le concept de Tous à lOrchestre - Dans les régions -
Les 95 musiciennes et musiciens proposent chaque saison plus de 120 concerts dans des salles et théâtres, des lieux culturels et des espaces atypiques de la région francilienne. Porté par une forte mission territoriale, lorchestre sengage à rendre la musique symphonique accessible à toutes et tous, en la faisant vivre au plus près des habitants grâce notamment à des actions culturelles, pédagogiques et participatives au cœur du territoire.
</DsText>
<DsButtonArrow to="/" variant="invert">
Carte des événements
@@ -161,12 +139,14 @@
<BannierePros />
</SectionContent>
</PageSection>
</div>
</template>
<script setup>
import { onMounted, computed } from 'vue'
import { clientLog } from '~/utils/clientLog'
import { formatDateLong } from "@/utils/dateFormat.js"
import SectionContent from '../components/section/SectionContent.vue'
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
import DsText from '@root/design-system/primitives/DsText.vue'
@@ -181,7 +161,6 @@
const runtimeConfig = useRuntimeConfig()
const STRAPI_URL = runtimeConfig.public.strapiUrl
console.log("STRAPI_URL : ",STRAPI_URL)
// Config app (pour SEO)
const config = useAppConfig()
@@ -189,38 +168,34 @@
title: config.title
})
// On récupère le fichier le plus récent de la Media Library Strapi
const { data, error } = await useFetch(
() => `${STRAPI_URL}/api/upload/files?pagination[pageSize]=1&sort=createdAt:desc`
)
const appConfig = useAppConfig()
console.log("Bienvenue : ",appConfig.title)
const imageUrl = computed(() => {
const file = data.value?.[0]
console.log("file : ",file)
if (!file) return null
// Si Strapi renvoie une URL absolue (S3/OVH)
if (file.url?.startsWith('http')) {
return file.url
}
// Si jamais c'était une URL relative
return `${STRAPI_URL}${file.url}`
//--------------------
// DONNÉES POUR LES CONCERTS À VENIR …
//--------------------
const { concerts, refresh } = useConcerts({
locale: "fr-FR",
populate: {
saison_concert: true,
image_illustration_concert: true,
representation_concert: { lieu_representation: true },
},
filters: {
saison_concert: {
nom_saison: {
$eq: "2025/2026",
},
},
},
upcomingOnly: true,
limit: 3,
})
if (error.value) {
console.error('Erreur en récupérant les fichiers Strapi :', error.value)
clientLog('error', 'Erreur en récupérant les fichiers Strapi', {
endpoint: `${STRAPI_URL}/api/upload/files?pagination[pageSize]=1&sort=createdAt:desc`,
error: error.value?.message || error.value
})
}
const appConfig = useAppConfig()
console.log("test 3 : ",appConfig.title) // "Mon site Nuxt"
onMounted(() => {
clientLog('info', 'test de log depuis vuejs', { })
clientLog('info', `STRAPI_URL : ${STRAPI_URL}`, { strapiUrl: STRAPI_URL })
if (!concerts.value?.length) {
refresh()
}
})
//--------------------

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,4 @@
<script setup>
await navigateTo('/mecenat/soutenir', { redirectCode: 301 })
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,4 @@
<script setup>
await navigateTo('/mediation/petite-enfance', { redirectCode: 301 })
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
Page en construction direction
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,4 @@
<script setup>
await navigateTo('/orchestre/missions', { redirectCode: 301 })
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div>
Page en construction Missions
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -14,7 +14,6 @@
const runtimeConfig = useRuntimeConfig()
//const STRAPI_URL = "http://localhost:1337"
const STRAPI_URL = runtimeConfig.public.strapiUrl
console.log("STRAPI_URL : ",STRAPI_URL)
// Config app (pour SEO)
const config = useAppConfig()
@@ -51,11 +50,7 @@
}
const appConfig = useAppConfig()
console.log("test 3 : ",appConfig.title) // "Mon site Nuxt"
onMounted(() => {
clientLog('info', 'test de log depuis vuejs', { })
clientLog('info', `STRAPI_URL : ${STRAPI_URL}`, { strapiUrl: STRAPI_URL })
})
console.log("Bienvenue : ",appConfig.title)
</script>
<style lang="scss">

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,4 @@
<script setup>
await navigateTo('/professionnels/programmer-orchestre', { redirectCode: 301 })
</script>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -0,0 +1,13 @@
<template>
<div>
</div>
</template>
<script lang="ts" setup>
</script>
<style>
</style>

View File

@@ -31,7 +31,7 @@
title: config.title
})
const appConfig = useAppConfig()
console.log("test 3 : ",appConfig.title) // "Mon site Nuxt"
console.log("test 3 : ",appConfig.title)
</script>
<style lang="scss">