generated from gitea_admin/default
Concerts
This commit is contained in:
@@ -22,12 +22,17 @@
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-bottom: none !important;
|
||||
.header_nav_topbar_submenu {
|
||||
visibility: visible;
|
||||
/* Pour l'effet de transition */
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&.is-active {
|
||||
border-bottom: 2px solid var(--c-brand_rouge);
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
}
|
||||
.header_nav_topbar_submenu {
|
||||
position: absolute;
|
||||
@@ -184,12 +189,19 @@
|
||||
cursor: pointer;
|
||||
|
||||
&:hover {
|
||||
border-bottom: none !important;
|
||||
padding-bottom: 2px;
|
||||
.header_nav_sub_menu {
|
||||
visibility: visible;
|
||||
/* Pour l'effet de transition */
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&.is-active {
|
||||
border-bottom: 2px solid var(--c-brand_rouge);
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
list-style: none;
|
||||
|
||||
|
||||
@@ -219,25 +231,36 @@
|
||||
|
||||
.header_nav_sub_menu {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
|
||||
@media (min-width: 0px) {
|
||||
left: -10px;
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
left: 0px;
|
||||
}
|
||||
z-index: 2;
|
||||
visibility:hidden;
|
||||
/* Pour l'effet de transition */
|
||||
opacity: 0;
|
||||
transition: visibility 0.2s,opacity 0.2s cubic-bezier(0.4, 0, 1, 1);
|
||||
padding-top: 10px;
|
||||
padding-left: 25px;
|
||||
padding-right: 22px;
|
||||
padding-left: 20px;
|
||||
padding-right: 7px;
|
||||
padding-bottom: 10px;
|
||||
text-align: left;
|
||||
background-color: rgba(255, 255, 255, 0.93);
|
||||
background-color: rgba(255, 255, 255, 0.97);
|
||||
border-radius: 3px;
|
||||
|
||||
.header_nav_sub_menu_item {
|
||||
list-style: circle;
|
||||
//font-family: 'brandontext_regular';
|
||||
font-family: var(--font-roboto);
|
||||
font-size: 18px;
|
||||
@media (min-width: 0px) {
|
||||
font-size: 16px;
|
||||
}
|
||||
@media (min-width: 900px) {
|
||||
font-size: 18px;
|
||||
}
|
||||
padding-bottom: 4px;
|
||||
&:hover {
|
||||
a {
|
||||
@@ -411,6 +434,7 @@
|
||||
|
||||
.header_drawer_link {
|
||||
display: block;
|
||||
width: fit-content;
|
||||
text-decoration: none;
|
||||
color: $blanc;
|
||||
padding: 6px 0;
|
||||
@@ -426,6 +450,11 @@
|
||||
opacity: 1;
|
||||
}
|
||||
}
|
||||
&.is-active {
|
||||
border-bottom: 2px solid var(--c-brand_rouge);
|
||||
padding-bottom: 1px;
|
||||
}
|
||||
|
||||
list-style: none;
|
||||
}
|
||||
.header_drawer_sub_menu {
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
}
|
||||
.page-enter-active,
|
||||
.page-leave-active {
|
||||
transition: all 0.4s ease;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
.page-enter-from,
|
||||
.page-leave-to {
|
||||
|
||||
117
app/components/Breadcrumb.vue
Normal file
117
app/components/Breadcrumb.vue
Normal file
@@ -0,0 +1,117 @@
|
||||
<template>
|
||||
<div v-if="items.length > 1" aria-label="Fil d’Ariane" class="breadcrumb">
|
||||
<ul class="breadcrumb__list">
|
||||
<li v-for="(item, i) in items" :key="item.to" class="breadcrumb__item">
|
||||
<NuxtLink v-if="i < items.length - 1" :to="item.to">
|
||||
<img
|
||||
v-if="i === 0"
|
||||
src="/img/icones/house-grey.svg"
|
||||
alt="Accueil"
|
||||
class="breadcrumb__home-icon"
|
||||
/>
|
||||
<span v-else>{{ item.label }}</span>
|
||||
</NuxtLink>
|
||||
<span v-else aria-current="page">{{ item.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const props = defineProps({
|
||||
currentLabel: { type: String, default: '' } // utile pour les pages dynamiques
|
||||
})
|
||||
|
||||
const route = useRoute()
|
||||
|
||||
const labelMap = {
|
||||
concerts: 'Concerts',
|
||||
agenda: 'Agenda',
|
||||
saison: 'Saison',
|
||||
orchestre: "L'Orchestre",
|
||||
professionnels: "Professionnels"
|
||||
}
|
||||
|
||||
function humanize(segment) {
|
||||
return segment
|
||||
.replace(/-/g, ' ')
|
||||
.replace(/\b\w/g, (m) => m.toUpperCase())
|
||||
}
|
||||
|
||||
const items = computed(() => {
|
||||
const parts = route.path.split('/').filter(Boolean)
|
||||
const crumbs = [{ to: '/', label: 'Accueil' }]
|
||||
|
||||
let acc = ''
|
||||
parts.forEach((part, index) => {
|
||||
acc += `/${part}`
|
||||
const isLast = index === parts.length - 1
|
||||
const label = isLast && props.currentLabel
|
||||
? props.currentLabel
|
||||
: (labelMap[part] || humanize(decodeURIComponent(part)))
|
||||
|
||||
crumbs.push({ to: acc, label })
|
||||
})
|
||||
|
||||
return crumbs
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.breadcrumb {
|
||||
padding-top: 10px;
|
||||
padding-bottom: 10px;
|
||||
font-size: 15px;
|
||||
font-family: var(--font-roboto);
|
||||
font-weight: var(--fw-extralight);
|
||||
color: #6D798A;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
@media (min-width: 0px) {
|
||||
padding-left: 20px;
|
||||
}
|
||||
@media (min-width: 600px) {
|
||||
padding-left: 20px;
|
||||
}
|
||||
@media (min-width: 700px) {
|
||||
padding-left: 20px;
|
||||
}
|
||||
}
|
||||
.breadcrumb__list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
}
|
||||
.breadcrumb__item { display: inline-flex; align-items: center; }
|
||||
.breadcrumb a {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
pointer-events: auto;
|
||||
}
|
||||
.breadcrumb__item:not(:last-child)::after {
|
||||
content: "";
|
||||
display: inline-block;
|
||||
width: 11px;
|
||||
height: 11px;
|
||||
margin-left: 8px;
|
||||
background: url('/img/icones/angle-right-grey.svg') no-repeat center / contain;
|
||||
vertical-align: middle;
|
||||
position: relative;
|
||||
top: -1px; /* ajuste entre 0 et 2px selon ton rendu */
|
||||
pointer-events: none;
|
||||
}
|
||||
.breadcrumb__home-icon {
|
||||
width: 14px;
|
||||
height: 14px;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -167,7 +167,7 @@
|
||||
max-width: 210px;
|
||||
}
|
||||
@media (min-width: 300px) {
|
||||
max-width: 290px;
|
||||
max-width: 250px;
|
||||
}
|
||||
@media (min-width: 400px) {
|
||||
max-width: 390px;
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
<!-- Meta : date + lieu -->
|
||||
<div class="concert-card__meta">
|
||||
<DsHeading as="h5" tone="default">
|
||||
{{ venue }}
|
||||
{{ lieu }}
|
||||
</DsHeading>
|
||||
<DsHeading as="h6" tone="default">
|
||||
<time :datetime="dateISO">{{ dateLabel }}</time>
|
||||
@@ -29,8 +29,8 @@
|
||||
|
||||
<!-- Actions -->
|
||||
<div class="concert-card__actions">
|
||||
<DsButtonArrow :to="`/concerts/${id}`" variant="secondary">
|
||||
Réserver
|
||||
<DsButtonArrow :to="`${href}`" variant="secondary">
|
||||
Découvrir
|
||||
</DsButtonArrow>
|
||||
</div>
|
||||
</div>
|
||||
@@ -49,12 +49,13 @@
|
||||
defineProps({
|
||||
id: { type: [String, Number], required: true },
|
||||
title: { type: String, required: true },
|
||||
venue: { type: String, required: true },
|
||||
lieu: { type: String, required: true },
|
||||
dateISO: { type: String, required: true }, // ex: "2026-01-15T20:00:00+01:00"
|
||||
dateLabel: { type: String, required: true }, // ex: "Jeu. 15 jan. 2026 — 20h"
|
||||
description: { type: String, default: '' },
|
||||
imageUrl: { type: String, default: '' },
|
||||
imageAlt: { type: String, default: '' },
|
||||
href: { type: String, default: '' },
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,30 +1,47 @@
|
||||
<!-- app/components/concert/ConcertCardList.vue -->
|
||||
<template>
|
||||
<div class="concert-card-list">
|
||||
<div
|
||||
class="concert-card-list"
|
||||
:class="{
|
||||
'concert-card-list--highlight-first': highlightFirst,
|
||||
'concert-card-list--limit-cards': limitCardsOnBreakpoint,
|
||||
}"
|
||||
>
|
||||
<slot />
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
defineProps({
|
||||
highlightFirst: { type: Boolean, default: true },
|
||||
limitCardsOnBreakpoint: { type: Boolean, default: true },
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.concert-card-list {
|
||||
display: flex;
|
||||
flex-wrap: wrap;
|
||||
gap: var(--gap-cards);
|
||||
justify-content: center;
|
||||
.concert-card {
|
||||
max-width: 452px;
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher seulement 1 cards < 600px
|
||||
@media (max-width: 599px) {
|
||||
.concert-card-list > .concert-card:nth-child(2) {
|
||||
.concert-card-list.concert-card-list--limit-cards > .concert-card:nth-child(2) {
|
||||
display: none;
|
||||
}
|
||||
.concert-card-list > .concert-card:nth-child(3) {
|
||||
.concert-card-list.concert-card-list--limit-cards > .concert-card:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
// Afficher seulement 2 cards < 900px
|
||||
@media (max-width: 899px) {
|
||||
.concert-card-list > .concert-card:nth-child(3) {
|
||||
.concert-card-list.concert-card-list--limit-cards > .concert-card:nth-child(3) {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
@@ -40,7 +57,7 @@
|
||||
.concert-card-list > .concert-card {
|
||||
flex: 1 1 260px;
|
||||
}
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 280px;
|
||||
}
|
||||
}
|
||||
@@ -48,7 +65,7 @@
|
||||
.concert-card-list > .concert-card {
|
||||
flex: 1 1 280px;
|
||||
}
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 300px;
|
||||
}
|
||||
}
|
||||
@@ -56,7 +73,7 @@
|
||||
.concert-card-list > .concert-card {
|
||||
flex: 1 1 280px;
|
||||
}
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 300px;
|
||||
}
|
||||
}
|
||||
@@ -65,7 +82,7 @@
|
||||
flex: 1 1 260px;
|
||||
}
|
||||
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 300px;
|
||||
}
|
||||
}
|
||||
@@ -75,7 +92,7 @@
|
||||
flex: 1 1 280px;
|
||||
}
|
||||
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 340px;
|
||||
}
|
||||
}
|
||||
@@ -85,7 +102,7 @@
|
||||
flex: 1 1 300px;
|
||||
}
|
||||
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 380px;
|
||||
}
|
||||
}
|
||||
@@ -95,7 +112,7 @@
|
||||
flex: 1 1 320px;
|
||||
}
|
||||
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 400px;
|
||||
}
|
||||
}
|
||||
@@ -105,7 +122,7 @@
|
||||
flex: 1 1 340px;
|
||||
}
|
||||
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 440px;
|
||||
}
|
||||
}
|
||||
@@ -116,7 +133,7 @@
|
||||
flex: 1 1 360px;
|
||||
}
|
||||
//règle spécifique après la règle générale
|
||||
.concert-card-list > .concert-card:first-child {
|
||||
.concert-card-list.concert-card-list--highlight-first > .concert-card:first-child {
|
||||
flex: 2 1 480px;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,7 +10,9 @@
|
||||
<template>
|
||||
<HeaderNav burger-color="hamburger_black">
|
||||
<template #logo>
|
||||
<NuxtImg :src="logoDefault" :alt="brand.logoAlt" class="logo-img" />
|
||||
<NuxtLink to="/" aria-label="Accueil">
|
||||
<NuxtImg :src="logoDefault" :alt="brand.logoAlt" class="logo-img" />
|
||||
</NuxtLink>
|
||||
</template>
|
||||
|
||||
<template #agenda-icon>
|
||||
@@ -44,4 +46,4 @@
|
||||
}
|
||||
|
||||
|
||||
</style>
|
||||
</style>
|
||||
|
||||
@@ -3,15 +3,15 @@
|
||||
<div class="height_10"></div>
|
||||
|
||||
<ul class="header_navigation_topbar" aria-label="Language selector">
|
||||
<li class="header_nav_topbar_item">
|
||||
<li class="header_nav_topbar_item" :class="{ 'is-active': isPro }">
|
||||
Professionnels
|
||||
<ul class="header_nav_topbar_submenu">
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/">Programmer l'Orchestre</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/">Le studio et les espaces</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/">Louer des instruments</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/">Recrutement / Concours</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/">Espace candidats</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/">Presse</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/professionnels/programmer-orchestre">Programmer l'Orchestre</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/professionnels/studio">Le studio et les espaces</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/professionnels/louer">Louer des instruments</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/professionnels/recrutement">Recrutement / Concours</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/professionnels/candidats">Espace candidats</NuxtLink></li>
|
||||
<li class="header_nav_topbar_submenu_item"><NuxtLink to="/professionnels/presse">Presse</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
<li class="header_nav_topbar_item header_nav_lang">
|
||||
@@ -32,55 +32,56 @@
|
||||
<nav class="header_nav_cont" aria-label="Primary navigation">
|
||||
<!-- Desktop nav -->
|
||||
<ul class="header_nav header_nav--desktop">
|
||||
<li class="header_nav_item">
|
||||
<li class="header_nav_item" :class="{ 'is-active': isOrchestre }">
|
||||
L'Orchestre
|
||||
<ul class="header_nav_sub_menu">
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Nos missions</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Direction musicale</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Les musiciens</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Les artistes invités</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Discographie</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Nos partenaires</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Nous soutenir</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/orchestre/missions">Nos missions</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/orchestre/direction">Direction musicale</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/orchestre/musiciens">Les musiciens</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/orchestre/artistes-invitees">Les artistes invités</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/orchestre/discographie">Discographie</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/orchestre/partenaires">Nos partenaires</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mecenat/soutenir">Nous soutenir</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="header_nav_item">
|
||||
<li class="header_nav_item" :class="{ 'is-active': isConcerts }">
|
||||
Concerts
|
||||
<ul class="header_nav_sub_menu">
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Saison</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Jeune public</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Concert mode d'emploi</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">ONDIF MAG</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">ONDIF LIVE !</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/concerts/saison">Saison</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/concerts/jeune-public">Jeune public</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/concerts/mode-emploi">Concert mode d'emploi</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/concerts/mag">ONDIF MAG</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/concerts/live">ONDIF LIVE !</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="header_nav_item">
|
||||
<li class="header_nav_item" :class="{ 'is-active': isMediation }">
|
||||
Éducation et médiation
|
||||
<ul class="header_nav_sub_menu">
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Petite enfance</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Scolaires</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Champ social</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Insertion professionnelle</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Pratiques amateurs</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Ressources pédagogiques</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mediation/petite-enfance">Petite enfance</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mediation/scolaires">Scolaires</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mediation/social">Champ social</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mediation/insertion-pro">Insertion professionnelle</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mediation/amateurs">Pratiques amateurs</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mediation/ressources-pedagogiques">Ressources pédagogiques</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="header_nav_item">
|
||||
<li class="header_nav_item" :class="{ 'is-active': isMecenat }">
|
||||
Mécénat
|
||||
<ul class="header_nav_sub_menu">
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Entreprises</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Les projets</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Particuliers</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/">Ils nous font confiance</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mecenat/soutenir">Nous soutenir</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mecenat/entreprises">Entreprises</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mecenat/projets">Les projets</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mecenat/particuliers">Particuliers</NuxtLink></li>
|
||||
<li class="header_nav_sub_menu_item"><NuxtLink to="/mecenat/mecenes">Ils nous font confiance</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li class="header_nav_item header_nav_icones">
|
||||
<div class="">
|
||||
<NuxtLink to="/agenda">
|
||||
<NuxtLink to="/concerts/agenda">
|
||||
<div class="nav_icone">
|
||||
<div class="nav_icone_img nav_icone_img--agenda">
|
||||
<!-- ICÔNE injectée -->
|
||||
@@ -91,7 +92,7 @@
|
||||
</NuxtLink>
|
||||
</div>
|
||||
<div class=" padding_top_1">
|
||||
<NuxtLink to="/agenda">
|
||||
<NuxtLink to="https://orchestre-ile.com/shop" external target="_blank" rel="noopener noreferrer">
|
||||
<div class="nav_icone">
|
||||
<div class="nav_icone_img nav_icone_img--ticket">
|
||||
<!-- ICÔNE injectée -->
|
||||
@@ -124,7 +125,7 @@
|
||||
<!-- Mobile icons -->
|
||||
<div class="header_nav header_nav--mobile-icons">
|
||||
<div class="header_nav_item">
|
||||
<NuxtLink to="/agenda">
|
||||
<NuxtLink to="/concerts/agenda">
|
||||
<div class="nav_icone">
|
||||
<div class="nav_icone_img nav_icone_img--agenda">
|
||||
<!-- ICÔNE injectée -->
|
||||
@@ -136,7 +137,7 @@
|
||||
</div>
|
||||
|
||||
<div class="header_nav_item padding_top_1">
|
||||
<NuxtLink to="/agenda">
|
||||
<NuxtLink to="https://orchestre-ile.com/shop" external target="_blank" rel="noopener noreferrer">
|
||||
<div class="nav_icone">
|
||||
<div class="nav_icone_img nav_icone_img--ticket">
|
||||
<!-- ICÔNE injectée -->
|
||||
@@ -157,33 +158,33 @@
|
||||
<ul class="header_drawer_inner">
|
||||
<li
|
||||
class="header_drawer_link"
|
||||
:class="{ 'is-open': activeDrawer === 'orchestre' }"
|
||||
:class="{ 'is-open': activeDrawer === 'orchestre','is-active': isOrchestre }"
|
||||
@click="toggleDrawer('orchestre')"
|
||||
>
|
||||
L'Orchestre
|
||||
<ul class="header_drawer_sub_menu">
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Nos missions</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Direction musicale</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Les musiciens</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Les artistes invités</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Discographie</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Nos partenaires</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Nous soutenir</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/orchestre/missions">Nos missions</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/orchestre/direction">Direction musicale</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/orchestre/musiciens">Les musiciens</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/orchestre/artistes-invitees">Les artistes invités</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/orchestre/discographie">Discographie</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/orchestre/partenaires">Nos partenaires</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mecenat/soutenir">Nous soutenir</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
<li
|
||||
class="header_drawer_link"
|
||||
:class="{ 'is-open': activeDrawer === 'concerts' }"
|
||||
:class="{ 'is-open': activeDrawer === 'concerts','is-active': isConcerts }"
|
||||
@click="toggleDrawer('concerts')"
|
||||
>
|
||||
Concerts
|
||||
<ul class="header_drawer_sub_menu">
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Saison</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Jeune public</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Concert mode d'emploi</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">ONDIF MAG</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">ONDIF LIVE !</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/concerts/saison">Saison</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/concerts/jeune-public">Jeune public</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/concerts/mode-emploi">Concert mode d'emploi</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/concerts/mag">ONDIF MAG</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/concerts/live">ONDIF LIVE !</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -194,12 +195,12 @@
|
||||
>
|
||||
Éducation et médiation
|
||||
<ul class="header_drawer_sub_menu">
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Petite enfance</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Scolaires</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Champ social</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Insertion professionnelle</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Pratiques amateurs</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Ressources pédagogiques</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mediation/petite-enfance">Petite enfance</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mediation/scolaires">Scolaires</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mediation/social">Champ social</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mediation/insertion-pro">Insertion professionnelle</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mediation/amateurs">Pratiques amateurs</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mediation/ressources-pedagogiques">Ressources pédagogiques</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
@@ -210,20 +211,21 @@
|
||||
>
|
||||
Mécénat
|
||||
<ul class="header_drawer_sub_menu">
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Entreprises</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Les projets</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Particuliers</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/">Ils nous font confiance</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mecenat/soutenir">Nous soutenir</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mecenat/entreprises">Entreprises</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mecenat/projets">Les projets</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mecenat/particuliers">Particuliers</NuxtLink></li>
|
||||
<li class="header_drawer_sub_menu_item"><NuxtLink to="/mecenat/mecenes">Ils nous font confiance</NuxtLink></li>
|
||||
</ul>
|
||||
</li>
|
||||
|
||||
|
||||
<li class="icon_mobile">
|
||||
<NuxtLink class="header_drawer_link icon_mobile_agenda" to="/agenda" @click="close">
|
||||
<NuxtLink class="header_drawer_link icon_mobile_agenda" to="/concerts/agenda" @click="close">
|
||||
<!-- ICÔNE injectée -->
|
||||
<slot name="mobile_agenda_icon" />
|
||||
</NuxtLink>
|
||||
<NuxtLink class="header_drawer_link icon_mobile_ticket" to="/agenda" @click="close">
|
||||
<NuxtLink class="header_drawer_link icon_mobile_ticket" to="https://orchestre-ile.com/shop" external target="_blank" rel="noopener noreferrer" @click="close">
|
||||
<!-- ICÔNE injectée -->
|
||||
<slot name="mobile_ticket" />
|
||||
</NuxtLink>
|
||||
@@ -241,12 +243,16 @@
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
const route = useRoute()
|
||||
import { watch } from 'vue'
|
||||
|
||||
defineProps({
|
||||
burgerColor: { type: String, default: 'hamburger_black' }
|
||||
})
|
||||
|
||||
/////////////////////////////////
|
||||
// MENU MOBILE
|
||||
/////////////////////////////////
|
||||
const isOpen = ref(false)
|
||||
const activeDrawer = ref(null)
|
||||
const toggle = () => (isOpen.value = !isOpen.value)
|
||||
@@ -256,11 +262,38 @@
|
||||
}
|
||||
|
||||
// ✅ ferme automatiquement le mobile drawer si on navigue
|
||||
const route = useRoute()
|
||||
|
||||
watch(() => route.fullPath, () => {
|
||||
close()
|
||||
activeDrawer.value = null
|
||||
})
|
||||
/////////////////////////////////
|
||||
// MENU ACTIF
|
||||
/////////////////////////////////
|
||||
// L'Orchestre
|
||||
const isOrchestre = computed(() =>
|
||||
route.path.startsWith('/orchestre')
|
||||
)
|
||||
|
||||
// Concerts
|
||||
const isConcerts = computed(() =>
|
||||
route.path.startsWith('/concerts')
|
||||
)
|
||||
|
||||
// Éducation et médiation
|
||||
const isMediation = computed(() =>
|
||||
route.path.startsWith('/mediation')
|
||||
)
|
||||
|
||||
// Mécénat
|
||||
const isMecenat = computed(() =>
|
||||
route.path.startsWith('/mecenat')
|
||||
)
|
||||
// professionnels
|
||||
const isPro = computed(() =>
|
||||
route.path.startsWith('/professionnels')
|
||||
)
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
@@ -12,7 +12,8 @@
|
||||
size: { type: String, default: 'default' }, // default / wide / narrow
|
||||
padb : { type: String, default: '' },
|
||||
padt : { type: String, default: '' },
|
||||
position : { type: String, dafault : ''}
|
||||
position : { type: String, dafault : ''},
|
||||
overflow : { type: String, default: '' }
|
||||
})
|
||||
</script>
|
||||
|
||||
@@ -39,9 +40,6 @@
|
||||
&--default {
|
||||
/* mobile / small screens */
|
||||
|
||||
@media (max-width: 700px) {
|
||||
//padding-inline: var(--page-padding-mobile);
|
||||
}
|
||||
|
||||
@media (min-width: 0px) {
|
||||
max-width: 100%;
|
||||
|
||||
91
app/components/strapiconvert/StrapiBlockChildsConvert.vue
Normal file
91
app/components/strapiconvert/StrapiBlockChildsConvert.vue
Normal file
@@ -0,0 +1,91 @@
|
||||
<template>
|
||||
<component v-if="isText && hasTextValue && wrapTag" :is="wrapTag">
|
||||
{{ textValue }}
|
||||
</component>
|
||||
|
||||
<template v-else-if="isText && hasTextValue">
|
||||
{{ textValue }}
|
||||
</template>
|
||||
|
||||
<component v-else-if="isList" :is="listTag">
|
||||
<StrapiBlockChildsConvert
|
||||
v-for="(child, i) in children"
|
||||
:key="i"
|
||||
:node="child"
|
||||
/>
|
||||
</component>
|
||||
|
||||
<li v-else-if="isListItem">
|
||||
<StrapiBlockChildsConvert
|
||||
v-for="(child, i) in children"
|
||||
:key="i"
|
||||
:node="child"
|
||||
/>
|
||||
</li>
|
||||
|
||||
<a
|
||||
v-else-if="isLink"
|
||||
:href="href"
|
||||
class="strapi-inline__link"
|
||||
rel="noopener noreferrer"
|
||||
target="_blank"
|
||||
>
|
||||
<StrapiBlockChildsConvert
|
||||
v-for="(child, i) in children"
|
||||
:key="i"
|
||||
:node="child"
|
||||
/>
|
||||
</a>
|
||||
|
||||
<template v-else>
|
||||
<StrapiBlockChildsConvert
|
||||
v-for="(child, i) in children"
|
||||
:key="i"
|
||||
:node="child"
|
||||
/>
|
||||
</template>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue'
|
||||
|
||||
const props = defineProps({
|
||||
node: { type: Object, required: true },
|
||||
})
|
||||
|
||||
const isText = computed(() => props.node?.type === 'text')
|
||||
const isLink = computed(() => props.node?.type === 'link')
|
||||
const isList = computed(() => props.node?.type === 'list')
|
||||
const isListItem = computed(() => props.node?.type === 'list-item')
|
||||
const children = computed(() => props.node?.children || [])
|
||||
const listTag = computed(() => (
|
||||
props.node?.format === 'ordered' ? 'ol' : 'ul'
|
||||
))
|
||||
|
||||
const textValue = computed(() => (props.node?.text ?? '').toString())
|
||||
const hasTextValue = computed(() => textValue.value.length > 0)
|
||||
|
||||
// Dans Strapi blocks, les liens peuvent être "url" ou "href" selon les versions/plugins
|
||||
const href = computed(() => props.node?.url || props.node?.href || '#')
|
||||
|
||||
/**
|
||||
* Gestion des "marks" (bold/italic/underline/...)
|
||||
* Ici on choisit une stratégie simple : UN seul wrapper.
|
||||
* -> si tu veux combiner plusieurs marks (bold + italic), on le fait après.
|
||||
*/
|
||||
const wrapTag = computed(() => {
|
||||
const n = props.node || {}
|
||||
if (!isText.value) return null
|
||||
|
||||
if (n.code) return 'code'
|
||||
if (n.bold) return 'strong'
|
||||
if (n.italic) return 'em'
|
||||
if (n.underline) return 'u'
|
||||
if (n.strikethrough) return 's'
|
||||
return null
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
|
||||
</style>
|
||||
222
app/components/strapiconvert/StrapiBlocksConvert.vue
Normal file
222
app/components/strapiconvert/StrapiBlocksConvert.vue
Normal file
@@ -0,0 +1,222 @@
|
||||
<template>
|
||||
<div class="strapi-blocks">
|
||||
<template v-for="(block, i) in blocks" :key="i">
|
||||
|
||||
<!-- Paragraph -->
|
||||
<p v-if="block.type === 'paragraph'" class="strapi-blocks--p">
|
||||
<template v-for="(child, j) in (block.children)" :key="j">
|
||||
<StrapiBlockChildsConvert :node="child" />
|
||||
</template>
|
||||
</p>
|
||||
|
||||
<!-- Heading -->
|
||||
<component
|
||||
v-else-if="block.type === 'heading'"
|
||||
:is="`h${block.level}`"
|
||||
class="strapi-blocks__h"
|
||||
>
|
||||
<template v-for="(child, j) in (block.children)" :key="j">
|
||||
<StrapiBlockChildsConvert :node="child" />
|
||||
</template>
|
||||
</component>
|
||||
|
||||
<!-- Quote -->
|
||||
<blockquote
|
||||
v-else-if="block.type === 'quote'"
|
||||
>
|
||||
<template v-for="(child, j) in (block.children)" :key="j">
|
||||
<StrapiBlockChildsConvert :node="child" />
|
||||
</template>
|
||||
</blockquote>
|
||||
|
||||
<!-- Lists -->
|
||||
<ul
|
||||
v-else-if="block.type === 'list' && block.format === 'unordered'"
|
||||
>
|
||||
<li
|
||||
v-for="(item, j) in normalizeListItems(block.children)"
|
||||
:key="j"
|
||||
>
|
||||
<template v-for="(child, k) in (item.children)" :key="k">
|
||||
<StrapiBlockChildsConvert :node="child" />
|
||||
</template>
|
||||
</li>
|
||||
</ul>
|
||||
|
||||
<ol
|
||||
v-else-if="block.type === 'list' && block.format === 'ordered'"
|
||||
>
|
||||
<li
|
||||
v-for="(item, j) in normalizeListItems(block.children)"
|
||||
:key="j"
|
||||
>
|
||||
<template v-for="(child, k) in (item.children)" :key="k">
|
||||
<StrapiBlockChildsConvert :node="child" />
|
||||
</template>
|
||||
</li>
|
||||
</ol>
|
||||
|
||||
<!-- Fallback -->
|
||||
<div v-else class="strapi-blocks--unknown">
|
||||
<!-- debug éventuel -->
|
||||
</div>
|
||||
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import StrapiBlockChildsConvert from './StrapiBlockChildsConvert.vue'
|
||||
|
||||
const props = defineProps({
|
||||
blocks: { type: Array, default: () => [] },
|
||||
})
|
||||
|
||||
const normalizeListItems = (children = []) => {
|
||||
const normalized = []
|
||||
|
||||
for (const child of children) {
|
||||
if (!child || typeof child !== 'object') continue
|
||||
|
||||
if (child.type === 'list-item') {
|
||||
normalized.push({
|
||||
...child,
|
||||
children: Array.isArray(child.children) ? [...child.children] : [],
|
||||
})
|
||||
continue
|
||||
}
|
||||
|
||||
// Certains contenus Strapi placent une sous-liste comme sibling d'un list-item.
|
||||
// On la rattache au dernier list-item pour produire un HTML imbriqué valide.
|
||||
if (child.type === 'list' && normalized.length > 0) {
|
||||
normalized[normalized.length - 1].children.push(child)
|
||||
}
|
||||
}
|
||||
|
||||
return normalized
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.strapi-blocks {
|
||||
font-family: var(--font-roboto);
|
||||
|
||||
&--p {
|
||||
|
||||
font-weight: var(--fw-light);
|
||||
font-size: 17px;
|
||||
line-height: 22px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
h1 {
|
||||
padding-bottom: 10px;
|
||||
font-weight: var(--fw-bold);
|
||||
font-size: 30px;
|
||||
}
|
||||
h2 {
|
||||
padding-bottom: 7px;
|
||||
font-weight: var(--fw-bold);
|
||||
font-size: 27px;
|
||||
color: var(--c-brand_rouge);
|
||||
}
|
||||
h3 {
|
||||
padding-bottom: 5px;
|
||||
font-weight: var(--fw-semibold);
|
||||
font-size: 26px;
|
||||
}
|
||||
h4 {
|
||||
padding-bottom: 3px;
|
||||
font-weight: var(--fw-medium);
|
||||
font-size: 22px;
|
||||
}
|
||||
h4 {
|
||||
padding-bottom: 3px;
|
||||
font-weight: var(--fw-medium);
|
||||
font-size: 22px;
|
||||
}
|
||||
h4 {
|
||||
padding-bottom: 3px;
|
||||
font-weight: var(--fw-medium);
|
||||
font-size: 22px;
|
||||
}
|
||||
|
||||
a {
|
||||
text-decoration: underline;
|
||||
color: var(--c-brand_rouge-weak);
|
||||
text-decoration-thickness: from-font;
|
||||
}
|
||||
|
||||
ul,
|
||||
ol {
|
||||
list-style: none;
|
||||
margin: 0 0 5px;
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
li {
|
||||
position: relative;
|
||||
margin: 0;
|
||||
padding-left: 23px;
|
||||
margin-bottom: 5px;
|
||||
|
||||
}
|
||||
|
||||
ul > li::before {
|
||||
content: "•";
|
||||
position: absolute;
|
||||
left: 0;
|
||||
top: -7px;
|
||||
width: 0.5rem;
|
||||
height: 0.5rem;
|
||||
/*background-image: var(--strapi-li-icon);
|
||||
background-repeat: no-repeat;
|
||||
background-size: contain;
|
||||
background-position: center;*/
|
||||
font-size: 39px;
|
||||
line-height: 1;
|
||||
/* color: var(--c-brand_rouge); /* couleur */
|
||||
}
|
||||
|
||||
ol {
|
||||
counter-reset: item;
|
||||
}
|
||||
|
||||
ol > li {
|
||||
counter-increment: item;
|
||||
}
|
||||
|
||||
ol > li::before {
|
||||
content: counter(item) ". ";
|
||||
}
|
||||
|
||||
li > ul,
|
||||
li > ol {
|
||||
margin-top: 4px;
|
||||
padding-left: 1rem;
|
||||
}
|
||||
|
||||
li > ul > li::before {
|
||||
/* background-image: var(--strapi-li-icon-nested); */
|
||||
content: "◦";
|
||||
top: -6px;
|
||||
font-size: 30px;
|
||||
line-height: 1;
|
||||
color: var(--c-text); /* couleur */
|
||||
}
|
||||
blockquote {
|
||||
border-left: 11px var(--c-text-muted) solid;
|
||||
padding-left: 20px;
|
||||
margin-left: 2%;
|
||||
margin-bottom: 30px;
|
||||
margin-top: 30px;
|
||||
}
|
||||
|
||||
// Espace uniquement avant un titre s'il suit un bloc de contenu
|
||||
&--p + &__h,
|
||||
&--ul + &__h,
|
||||
&--ol + &__h,
|
||||
&--quote + &__h {
|
||||
margin-top: 13px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
128
app/composables/useConcerts.js
Normal file
128
app/composables/useConcerts.js
Normal file
@@ -0,0 +1,128 @@
|
||||
export function useConcerts(options = {}) {
|
||||
const queryString = computed(() => {
|
||||
const locale = unref(options.locale) ?? "fr-FR"
|
||||
const sort = unref(options.sort) ?? null
|
||||
const populate = unref(options.populate) ?? null
|
||||
const filters = unref(options.filters) ?? null
|
||||
|
||||
const query = new URLSearchParams()
|
||||
query.set("locale", locale)
|
||||
if (sort) {
|
||||
query.set("sort[0]", sort)
|
||||
}
|
||||
if (populate && typeof populate === "object") {
|
||||
appendPopulate(query, populate)
|
||||
}
|
||||
if (filters && typeof filters === "object") {
|
||||
appendFilters(query, filters)
|
||||
}
|
||||
|
||||
return query.toString()
|
||||
})
|
||||
|
||||
const { data, pending, error, refresh } = useFetch(
|
||||
() => `/api/__strapi__/concerts?${queryString.value}`,
|
||||
{
|
||||
server: true,
|
||||
key: () => `concerts:${queryString.value}`,
|
||||
watch: [queryString],
|
||||
}
|
||||
)
|
||||
|
||||
const concerts = computed(() => {
|
||||
const rows = (data.value?.data || []).map(normalizeConcert)
|
||||
let list = rows.sort(compareByRepresentationDate)
|
||||
|
||||
const upcomingOnly = Boolean(unref(options.upcomingOnly) ?? false)
|
||||
if (upcomingOnly) {
|
||||
const todayStart = new Date()
|
||||
todayStart.setHours(0, 0, 0, 0)
|
||||
list = list.filter((c) => {
|
||||
const d = getFirstRepresentationDate(c)
|
||||
return d ? d >= todayStart : false
|
||||
})
|
||||
}
|
||||
|
||||
const limit = unref(options.limit)
|
||||
if (typeof limit === "number") {
|
||||
list = list.slice(0, Math.max(0, limit))
|
||||
}
|
||||
|
||||
return list
|
||||
})
|
||||
|
||||
return {
|
||||
concerts,
|
||||
pending,
|
||||
error,
|
||||
refresh,
|
||||
}
|
||||
}
|
||||
|
||||
function appendPopulate(query, populate, prefix = "populate") {
|
||||
Object.entries(populate).forEach(([key, value]) => {
|
||||
if (value === true) {
|
||||
query.set(`${prefix}[${key}]`, "true")
|
||||
return
|
||||
}
|
||||
if (value && typeof value === "object") {
|
||||
const entries = Object.entries(value)
|
||||
const allTrue = entries.length > 0 && entries.every(([, v]) => v === true)
|
||||
if (allTrue) {
|
||||
const list = entries.map(([k]) => k).join(",")
|
||||
query.set(`${prefix}[${key}][populate]`, list)
|
||||
return
|
||||
}
|
||||
appendPopulate(query, value, `${prefix}[${key}][populate]`)
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function appendFilters(query, filters, prefix = "filters") {
|
||||
Object.entries(filters).forEach(([key, value]) => {
|
||||
if (value === null || value === undefined) return
|
||||
if (typeof value !== "object") {
|
||||
query.set(`${prefix}[${key}]`, String(value))
|
||||
return
|
||||
}
|
||||
Object.entries(value).forEach(([k, v]) => {
|
||||
if (v === null || v === undefined) return
|
||||
if (typeof v !== "object") {
|
||||
query.set(`${prefix}[${key}][${k}]`, String(v))
|
||||
return
|
||||
}
|
||||
appendFilters(query, v, `${prefix}[${key}][${k}]`)
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
function normalizeConcert(item) {
|
||||
const a = item.attributes || item || {}
|
||||
|
||||
return {
|
||||
id: item.id,
|
||||
...a,
|
||||
}
|
||||
}
|
||||
|
||||
// tri par date de représentation
|
||||
function compareByRepresentationDate(a, b) {
|
||||
const da = getFirstRepresentationDate(a)
|
||||
const db = getFirstRepresentationDate(b)
|
||||
if (!da && !db) return 0
|
||||
if (!da) return 1
|
||||
if (!db) return -1
|
||||
return da - db
|
||||
}
|
||||
|
||||
function getFirstRepresentationDate(concert) {
|
||||
const reps = concert?.representation_concert || []
|
||||
let earliest = null
|
||||
reps.forEach((r) => {
|
||||
const iso = r?.date_debut_representation
|
||||
if (!iso) return
|
||||
const d = new Date(iso)
|
||||
if (!earliest || d < earliest) earliest = d
|
||||
})
|
||||
return earliest
|
||||
}
|
||||
@@ -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
883
app/pages/concerts/[id].vue
Normal 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>
|
||||
71
app/pages/concerts/agenda.vue
Normal file
71
app/pages/concerts/agenda.vue
Normal 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>
|
||||
@@ -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>
|
||||
4
app/pages/concerts/index.vue
Normal file
4
app/pages/concerts/index.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<script setup>
|
||||
await navigateTo('/concerts/agenda', { redirectCode: 301 })
|
||||
</script>
|
||||
|
||||
13
app/pages/concerts/jeune-public.vue
Normal file
13
app/pages/concerts/jeune-public.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/concerts/live.vue
Normal file
13
app/pages/concerts/live.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/concerts/mag.vue
Normal file
13
app/pages/concerts/mag.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/concerts/mode-emploi.vue
Normal file
13
app/pages/concerts/mode-emploi.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
71
app/pages/concerts/saison.vue
Normal file
71
app/pages/concerts/saison.vue
Normal 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>
|
||||
@@ -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."
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -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 à l’Orchestre - Dans les régions - Ici le texte qui décrit le concept de Tous à l’Orchestre - Dans les régions - Ici le texte qui décrit le concept de Tous à l’Orchestre - 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, l’orchestre s’engage à 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()
|
||||
}
|
||||
})
|
||||
|
||||
//--------------------
|
||||
|
||||
13
app/pages/mecenat/entreprises.vue
Normal file
13
app/pages/mecenat/entreprises.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
4
app/pages/mecenat/index.vue
Normal file
4
app/pages/mecenat/index.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<script setup>
|
||||
await navigateTo('/mecenat/soutenir', { redirectCode: 301 })
|
||||
</script>
|
||||
|
||||
13
app/pages/mecenat/mecenes.vue
Normal file
13
app/pages/mecenat/mecenes.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mecenat/particuliers.vue
Normal file
13
app/pages/mecenat/particuliers.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mecenat/projets.vue
Normal file
13
app/pages/mecenat/projets.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mecenat/soutenir.vue
Normal file
13
app/pages/mecenat/soutenir.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mediation/amateurs.vue
Normal file
13
app/pages/mediation/amateurs.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
4
app/pages/mediation/index.vue
Normal file
4
app/pages/mediation/index.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<script setup>
|
||||
await navigateTo('/mediation/petite-enfance', { redirectCode: 301 })
|
||||
</script>
|
||||
|
||||
13
app/pages/mediation/insertion-pro.vue
Normal file
13
app/pages/mediation/insertion-pro.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mediation/petite-enfance.vue
Normal file
13
app/pages/mediation/petite-enfance.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mediation/ressources-pedagogiques.vue
Normal file
13
app/pages/mediation/ressources-pedagogiques.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mediation/scolaires.vue
Normal file
13
app/pages/mediation/scolaires.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/mediation/social.vue
Normal file
13
app/pages/mediation/social.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/orchestre/artistes-invitees.vue
Normal file
13
app/pages/orchestre/artistes-invitees.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/orchestre/direction.vue
Normal file
13
app/pages/orchestre/direction.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
Page en construction direction
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/orchestre/discographie.vue
Normal file
13
app/pages/orchestre/discographie.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
4
app/pages/orchestre/index.vue
Normal file
4
app/pages/orchestre/index.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<script setup>
|
||||
await navigateTo('/orchestre/missions', { redirectCode: 301 })
|
||||
</script>
|
||||
|
||||
13
app/pages/orchestre/missions.vue
Normal file
13
app/pages/orchestre/missions.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
Page en construction Missions
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/orchestre/musiciens.vue
Normal file
13
app/pages/orchestre/musiciens.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/orchestre/partenaires.vue
Normal file
13
app/pages/orchestre/partenaires.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/orchestre/soutenir.vue
Normal file
13
app/pages/orchestre/soutenir.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -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">
|
||||
13
app/pages/professionnels/candidats.vue
Normal file
13
app/pages/professionnels/candidats.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
4
app/pages/professionnels/index.vue
Normal file
4
app/pages/professionnels/index.vue
Normal file
@@ -0,0 +1,4 @@
|
||||
<script setup>
|
||||
await navigateTo('/professionnels/programmer-orchestre', { redirectCode: 301 })
|
||||
</script>
|
||||
|
||||
13
app/pages/professionnels/louer.vue
Normal file
13
app/pages/professionnels/louer.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/professionnels/presse.vue
Normal file
13
app/pages/professionnels/presse.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/professionnels/programmer-orchestre.vue
Normal file
13
app/pages/professionnels/programmer-orchestre.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/professionnels/recrutement.vue
Normal file
13
app/pages/professionnels/recrutement.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
13
app/pages/professionnels/studio.vue
Normal file
13
app/pages/professionnels/studio.vue
Normal file
@@ -0,0 +1,13 @@
|
||||
<template>
|
||||
<div>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
|
||||
</script>
|
||||
|
||||
<style>
|
||||
|
||||
</style>
|
||||
@@ -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">
|
||||
|
||||
16
app/utils/dateFormat.js
Normal file
16
app/utils/dateFormat.js
Normal file
@@ -0,0 +1,16 @@
|
||||
export function formatDateLong(iso) {
|
||||
if (!iso) return ""
|
||||
const d = new Date(iso)
|
||||
const date = new Intl.DateTimeFormat("fr-FR", {
|
||||
weekday: "long",
|
||||
day: "numeric",
|
||||
month: "long",
|
||||
year: "numeric",
|
||||
}).format(d)
|
||||
const time = new Intl.DateTimeFormat("fr-FR", {
|
||||
hour: "2-digit",
|
||||
minute: "2-digit",
|
||||
hour12: false,
|
||||
}).format(d).replace(":", "h")
|
||||
return `${date} — ${time}`
|
||||
}
|
||||
33
app/utils/strapi.js
Normal file
33
app/utils/strapi.js
Normal file
@@ -0,0 +1,33 @@
|
||||
// app/utils/strapi.js
|
||||
|
||||
export function getStrapiBaseUrl(event) {
|
||||
// En server/Nitro, on récupère runtimeConfig via l'event si dispo,
|
||||
// sinon on retombe sur la variable d'env publique.
|
||||
const base =
|
||||
event?.context?.runtimeConfig?.public?.strapiUrl ||
|
||||
process.env.NUXT_PUBLIC_STRAPI_URL
|
||||
|
||||
if (!base) {
|
||||
throw new Error("Missing runtimeConfig.public.strapiUrl (NUXT_PUBLIC_STRAPI_URL)")
|
||||
}
|
||||
|
||||
try {
|
||||
new URL(base)
|
||||
} catch {
|
||||
throw new Error(`Invalid Strapi base URL: ${base}`)
|
||||
}
|
||||
|
||||
return base.replace(/\/$/, "")
|
||||
}
|
||||
|
||||
export async function strapiFetch(event, path, options = {}) {
|
||||
const base = getStrapiBaseUrl(event)
|
||||
const url = `${base}${path.startsWith("/") ? path : `/${path}`}`
|
||||
|
||||
return await $fetch(url, {
|
||||
...options,
|
||||
headers: {
|
||||
...(options.headers || {}),
|
||||
},
|
||||
})
|
||||
}
|
||||
Reference in New Issue
Block a user