generated from gitea_admin/default
ajout de bouton réserver et ajout du formulaire lycée
This commit is contained in:
@@ -62,6 +62,8 @@ si besoin (ajout de dépendances)
|
|||||||
npm run build # rebuild Nuxt
|
npm run build # rebuild Nuxt
|
||||||
pm2 reload ecosystem.config.cjs --only wondif_vue # redémarre le process avec les nouvelles variables d'environnement de ecosystem
|
pm2 reload ecosystem.config.cjs --only wondif_vue # redémarre le process avec les nouvelles variables d'environnement de ecosystem
|
||||||
|
|
||||||
|
# URL
|
||||||
|
/inscriptions/projet_lycee
|
||||||
|
|
||||||
# STRAPI
|
# STRAPI
|
||||||
## URL de PROD
|
## URL de PROD
|
||||||
@@ -1055,3 +1057,4 @@ debian@vps-48ebe2d9:~$ history
|
|||||||
819 pm2 list
|
819 pm2 list
|
||||||
820 history
|
820 history
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@
|
|||||||
mediation: "Médiation",
|
mediation: "Médiation",
|
||||||
mecenat: "Mécénat",
|
mecenat: "Mécénat",
|
||||||
professionnels: "Professionnels",
|
professionnels: "Professionnels",
|
||||||
|
inscriptions: "Inscriptions",
|
||||||
|
projet_lycee: "Inscription projet lycée",
|
||||||
actus: "Actualités"
|
actus: "Actualités"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +77,7 @@
|
|||||||
? props.currentLabel
|
? props.currentLabel
|
||||||
: (labelMap[part] || humanize(decodeURIComponent(part)))
|
: (labelMap[part] || humanize(decodeURIComponent(part)))
|
||||||
|
|
||||||
const noLink = part === 'orchestre' || part === 'concerts' || part === 'mediation' || part === 'professionnels' || part === 'mecenat'
|
const noLink = part === 'orchestre' || part === 'concerts' || part === 'mediation' || part === 'professionnels' || part === 'mecenat' || part === 'inscriptions'
|
||||||
crumbs.push({ to: resolveTo(part, index, parts, acc), label, noLink })
|
crumbs.push({ to: resolveTo(part, index, parts, acc), label, noLink })
|
||||||
|
|
||||||
if (part === 'concerts') {
|
if (part === 'concerts') {
|
||||||
|
|||||||
@@ -3,15 +3,19 @@
|
|||||||
<div class="concert-card__grid">
|
<div class="concert-card__grid">
|
||||||
<!-- Image -->
|
<!-- Image -->
|
||||||
<div class="concert-card__media">
|
<div class="concert-card__media">
|
||||||
<DsMedia v-if="imageUrl" :src="imageUrl" :alt="imageAlt" ratio="square" />
|
<NuxtLink v-if="href" :to="href" class="concert-card__link">
|
||||||
|
<DsMedia v-if="imageUrl" :src="imageUrl" :alt="imageAlt" ratio="square" />
|
||||||
|
</NuxtLink>
|
||||||
<div v-else class="musicien_card_media-placeholder" aria-hidden="true" />
|
<div v-else class="musicien_card_media-placeholder" aria-hidden="true" />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<!-- Content -->
|
<!-- Content -->
|
||||||
<div class="concert-card__content">
|
<div class="concert-card__content">
|
||||||
<DsHeading as="h4" tone="default" class="concert-card__title">
|
<NuxtLink v-if="href" :to="href" class="concert-card__link">
|
||||||
{{ title }}
|
<DsHeading as="h4" tone="default" class="concert-card__title">
|
||||||
</DsHeading>
|
{{ title }}
|
||||||
|
</DsHeading>
|
||||||
|
</NuxtLink>
|
||||||
|
|
||||||
<!-- Meta : date + lieu -->
|
<!-- Meta : date + lieu -->
|
||||||
<div class="concert-card__meta">
|
<div class="concert-card__meta">
|
||||||
@@ -33,6 +37,9 @@
|
|||||||
<DsButtonArrow :to="`${href}`" variant="secondary">
|
<DsButtonArrow :to="`${href}`" variant="secondary">
|
||||||
Découvrir
|
Découvrir
|
||||||
</DsButtonArrow>
|
</DsButtonArrow>
|
||||||
|
<DsButton :href="`${lien_billetterie_representation}`" variant="third" size="xs" class="concert-card__actions--reserver" target="_blank" rel="noopener noreferrer">
|
||||||
|
Réserver
|
||||||
|
</DsButton>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -45,6 +52,7 @@
|
|||||||
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
||||||
import DsText from '@root/design-system/primitives/DsText.vue'
|
import DsText from '@root/design-system/primitives/DsText.vue'
|
||||||
import DsButtonArrow from '@root/design-system/primitives/DsButtonArrow.vue'
|
import DsButtonArrow from '@root/design-system/primitives/DsButtonArrow.vue'
|
||||||
|
import DsButton from '@root/design-system/primitives/DsButton.vue'
|
||||||
|
|
||||||
|
|
||||||
defineProps({
|
defineProps({
|
||||||
@@ -53,6 +61,7 @@
|
|||||||
lieu: { type: String, default: '' },
|
lieu: { type: String, default: '' },
|
||||||
dateISO: { type: String, required: true }, // ex: "2026-01-15T20:00:00+01:00"
|
dateISO: { type: String, required: true }, // ex: "2026-01-15T20:00:00+01:00"
|
||||||
dateLabel: { type: String, required: true }, // ex: "Jeu. 15 jan. 2026 — 20h"
|
dateLabel: { type: String, required: true }, // ex: "Jeu. 15 jan. 2026 — 20h"
|
||||||
|
lien_billetterie_representation: { type: String, default: '' },
|
||||||
description: { type: String, default: '' },
|
description: { type: String, default: '' },
|
||||||
imageUrl: { type: String, default: '' },
|
imageUrl: { type: String, default: '' },
|
||||||
imageAlt: { type: String, default: '' },
|
imageAlt: { type: String, default: '' },
|
||||||
@@ -83,21 +92,34 @@
|
|||||||
|
|
||||||
.concert-card__content {
|
.concert-card__content {
|
||||||
display: grid;
|
display: grid;
|
||||||
gap: var(--sp-6);
|
gap: var(--sp-4);
|
||||||
max-width: 518px;
|
max-width: 518px;
|
||||||
padding-left: 5px;
|
padding-left: 5px;
|
||||||
padding-right: 5px;
|
padding-right: 5px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.concert-card__link:hover,
|
||||||
|
.concert-card__link:focus-visible {
|
||||||
|
color: var(--c-brand_rouge) !important;
|
||||||
|
.ds-heading--default {
|
||||||
|
color: var(--c-brand_rouge) !important;
|
||||||
|
}
|
||||||
|
}
|
||||||
.concert-card__meta {
|
.concert-card__meta {
|
||||||
margin-top: calc(var(--sp-4) * -1);
|
margin-top: calc(var(--sp-4) * -1);
|
||||||
}
|
}
|
||||||
.concert-card__description {
|
.concert-card__description {
|
||||||
margin-top: 10px;
|
margin-top: 3px;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 4px;
|
||||||
}
|
}
|
||||||
.concert-card__actions {
|
.concert-card__actions {
|
||||||
margin-top: auto;
|
margin-top: auto;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
&--reserver {
|
||||||
|
margin-right: 5px;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@@ -15,11 +15,13 @@
|
|||||||
<slot name="title" />
|
<slot name="title" />
|
||||||
</DsHeading>
|
</DsHeading>
|
||||||
<slot />
|
<slot />
|
||||||
<div v-if="$slots.button" class="decalage_button">
|
<div class="decalage_button_wp">
|
||||||
<slot name="button" />
|
<div v-if="resolvedLienCta" class="decalage_button">
|
||||||
</div>
|
<DsButton :to="resolvedLienCta" variant="white" textColor="default" borderColor="none">Candidater à nos projets</DsButton>
|
||||||
<div v-if="ensavoirplusTarget" class="decalage_button">
|
</div>
|
||||||
<DsButton :textColor="buttonTone" :borderColor="buttonTone" @click="toggleTarget(ensavoirplusTarget, ensavoirplusGroup)">En savoir +</DsButton>
|
<div v-if="ensavoirplusTarget" class="decalage_button">
|
||||||
|
<DsButton :textColor="buttonTone" :borderColor="buttonTone" @click="toggleTarget(ensavoirplusTarget, ensavoirplusGroup)">En savoir +</DsButton>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
@@ -27,17 +29,23 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
|
import { computed } from 'vue'
|
||||||
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
||||||
import DsText from '@root/design-system/primitives/DsText.vue'
|
import DsText from '@root/design-system/primitives/DsText.vue'
|
||||||
import DsButton from '@root/design-system/primitives/DsButton.vue'
|
import DsButton from '@root/design-system/primitives/DsButton.vue'
|
||||||
defineProps({
|
const props = defineProps({
|
||||||
tone: { type: String, default: 'default' },
|
tone: { type: String, default: 'default' },
|
||||||
titleTone: { type: String, default: 'default' },
|
titleTone: { type: String, default: 'default' },
|
||||||
buttonTone: { type: String, default: 'default' },
|
buttonTone: { type: String, default: 'default' },
|
||||||
position: { type: String, default: 'left' },
|
position: { type: String, default: 'left' },
|
||||||
|
cta: { type: String, default: '' },
|
||||||
|
lienCta: { type: String, default: '' },
|
||||||
ensavoirplusTarget: { type: String, default: '' },
|
ensavoirplusTarget: { type: String, default: '' },
|
||||||
ensavoirplusGroup: { type: String, default: '' }
|
ensavoirplusGroup: { type: String, default: '' },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const resolvedLienCta = computed(() => props.lienCta || props.cta)
|
||||||
|
|
||||||
function toggleTarget(ensavoirplusTarget, ensavoirplusGroup) {
|
function toggleTarget(ensavoirplusTarget, ensavoirplusGroup) {
|
||||||
const target = document.getElementById(ensavoirplusTarget)
|
const target = document.getElementById(ensavoirplusTarget)
|
||||||
if (!target) return
|
if (!target) return
|
||||||
@@ -134,10 +142,17 @@
|
|||||||
.decalage_button {
|
.decalage_button {
|
||||||
text-align: right;
|
text-align: right;
|
||||||
}
|
}
|
||||||
|
.decalage_button_wp {
|
||||||
|
justify-content: flex-end;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
.decalage_button {
|
|
||||||
|
.decalage_button_wp {
|
||||||
margin-top: 32px;
|
margin-top: 32px;
|
||||||
|
display: flex;
|
||||||
|
gap: 20px;
|
||||||
|
flex-wrap: wrap;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -23,6 +23,7 @@
|
|||||||
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
:lieu="c.representation_concert?.[0]?.lieu_representation?.nom_lieu"
|
||||||
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
|
:dateISO="c.representation_concert?.[0]?.date_debut_representation"
|
||||||
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
|
:dateLabel="formatDateLong(c.representation_concert?.[0]?.date_debut_representation)"
|
||||||
|
:lien_billetterie_representation="c.representation_concert?.[0]?.lien_billetterie_representation"
|
||||||
:description="c.resume_concert"
|
:description="c.resume_concert"
|
||||||
:imageUrl="c.image_illustration_concert?.url"
|
:imageUrl="c.image_illustration_concert?.url"
|
||||||
:imageAlt="c.image_illustration_concert?.alternativeText"
|
:imageAlt="c.image_illustration_concert?.alternativeText"
|
||||||
|
|||||||
588
app/pages/inscriptions/projet_lycee.vue
Normal file
588
app/pages/inscriptions/projet_lycee.vue
Normal file
@@ -0,0 +1,588 @@
|
|||||||
|
<template>
|
||||||
|
<div class="inscription-page">
|
||||||
|
<!-- ================== -->
|
||||||
|
<!-- FILS D'ARIANE -->
|
||||||
|
<!-- ================== -->
|
||||||
|
<PageSection tone="" content-size="default" class="breadcrum_wp">
|
||||||
|
<Breadcrumb/>
|
||||||
|
</PageSection>
|
||||||
|
|
||||||
|
<!-- ================== -->
|
||||||
|
<!-- EN-TÊTE -->
|
||||||
|
<!-- ================== -->
|
||||||
|
<PageSection tone="jaune" content-size="default" class="theme_bandeau--grid">
|
||||||
|
<SectionContent pad="xs" class="theme_bandeau--grid--left">
|
||||||
|
<SectionTitle tone="invert" pad="">
|
||||||
|
CANDIDATER À UN PROJET AVEC L'ORCHESTRE
|
||||||
|
</SectionTitle>
|
||||||
|
<DsHeading as="h3" tone="invert">
|
||||||
|
</DsHeading>
|
||||||
|
</SectionContent>
|
||||||
|
|
||||||
|
|
||||||
|
<SectionContent pad="xs" class="theme_bandeau--grid--right">
|
||||||
|
<DsText tone="invert" size="lg" class="theme_bandeau--grid--right--text" >
|
||||||
|
Renseignez ce formulaire pour soumettre la candidature de votre établissement à un programme d'action culturelle de l'Orchestre national d'Île-de-France.
|
||||||
|
</DsText>
|
||||||
|
</SectionContent>
|
||||||
|
</PageSection>
|
||||||
|
|
||||||
|
<!-- ================== -->
|
||||||
|
<!-- FORMULAIRE -->
|
||||||
|
<!-- ================== -->
|
||||||
|
<div class="programmer-orchestre-page text-on-surface">
|
||||||
|
<!-- FORM -->
|
||||||
|
<form class="space-y-6" @submit.prevent="submitQuoteRequest">
|
||||||
|
<div class="px-12 py-8 max-w-7xl mx-auto">
|
||||||
|
<!-- SOUS-TITRE -->
|
||||||
|
<section class="mb-8">
|
||||||
|
<div class="flex items-center gap-4 mb-8">
|
||||||
|
<h3 class="text-2xl font-bold tracking-tight uppercase">Établissement scolaire</h3>
|
||||||
|
<div class="h-[2px] flex-1 bg-surface-container"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- SOUS-TITRE QUESTIONS -->
|
||||||
|
<section>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-etablissement" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Nom de l'établissement scolaire *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-etablissement"
|
||||||
|
v-model.trim="quoteForm.schoolName"
|
||||||
|
type="text"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.schoolName" class="text-xs text-error">{{ quoteFormErrors.schoolName }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-departement" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Département *
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="quote-departement"
|
||||||
|
v-model="quoteForm.department"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
>
|
||||||
|
<option value="">Choisir un département</option>
|
||||||
|
<option value="75">75 – Paris</option>
|
||||||
|
<option value="77">77 – Seine-et-Marne</option>
|
||||||
|
<option value="78">78 – Yvelines</option>
|
||||||
|
<option value="91">91 – Essonne</option>
|
||||||
|
<option value="92">92 – Hauts-de-Seine</option>
|
||||||
|
<option value="93">93 – Seine-Saint-Denis</option>
|
||||||
|
<option value="94">94 – Val-de-Marne</option>
|
||||||
|
</select>
|
||||||
|
<p v-if="quoteFormErrors.department" class="text-xs text-error">{{ quoteFormErrors.department }}</p>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-ville" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Ville *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-ville"
|
||||||
|
v-model.trim="quoteForm.city"
|
||||||
|
type="text"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.city" class="text-xs text-error">{{ quoteFormErrors.city }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SOUS-TITRE -->
|
||||||
|
<section class="mb-8 mt-10">
|
||||||
|
<div class="flex items-center gap-4 mb-8">
|
||||||
|
<h3 class="text-2xl font-bold tracking-tight uppercase">Enseignant coordinateur du projet</h3>
|
||||||
|
<div class="h-[2px] flex-1 bg-surface-container"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- SOUS-TITRE QUESTIONS -->
|
||||||
|
<section>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-name" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Nom et prénom *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-name"
|
||||||
|
v-model.trim="quoteForm.coordinatorName"
|
||||||
|
type="text"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.coordinatorName" class="text-xs text-error">{{ quoteFormErrors.coordinatorName }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-email" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Adresse mail *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-email"
|
||||||
|
v-model.trim="quoteForm.email"
|
||||||
|
type="email"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.email" class="text-xs text-error">{{ quoteFormErrors.email }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-phone" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Téléphone *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-phone"
|
||||||
|
v-model.trim="quoteForm.phone"
|
||||||
|
type="tel"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.phone" class="text-xs text-error">{{ quoteFormErrors.phone }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-discipline" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Discipline enseignée *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-discipline"
|
||||||
|
v-model.trim="quoteForm.discipline"
|
||||||
|
type="text"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.discipline" class="text-xs text-error">{{ quoteFormErrors.discipline }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-niveau" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Niveau de classe *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-niveau"
|
||||||
|
v-model.trim="quoteForm.classLevel"
|
||||||
|
type="text"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.classLevel" class="text-xs text-error">{{ quoteFormErrors.classLevel }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-nb_eleves" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Nombre probable d'élèves concernés *
|
||||||
|
</label>
|
||||||
|
<input
|
||||||
|
id="quote-nb_eleves"
|
||||||
|
v-model.trim="quoteForm.studentCount"
|
||||||
|
type="number"
|
||||||
|
min="1"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
>
|
||||||
|
<p v-if="quoteFormErrors.studentCount" class="text-xs text-error">{{ quoteFormErrors.studentCount }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SOUS-TITRE -->
|
||||||
|
<section class="mb-8 mt-10">
|
||||||
|
<div class="flex items-center gap-4 mb-8">
|
||||||
|
<h3 class="text-2xl font-bold tracking-tight uppercase">Souhaits de projet</h3>
|
||||||
|
<div class="h-[2px] flex-1 bg-surface-container"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- SOUS-TITRE QUESTIONS -->
|
||||||
|
<section>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-type_rencontre" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Concert-rencontre
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="quote-type_rencontre"
|
||||||
|
v-model="quoteForm.concertRencontrePriority"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
>
|
||||||
|
<option value="">Priorité</option>
|
||||||
|
<option value="concert_rencontre_priorite_0">non souhaité</option>
|
||||||
|
<option value="concert_rencontre_priorite_1">1</option>
|
||||||
|
<option value="concert_rencontre_priorite_2">2</option>
|
||||||
|
<option value="concert_rencontre_priorite_3">3</option>
|
||||||
|
<option value="concert_rencontre_priorite_4">4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-type_immersion" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Parcours Entrer dans la peau d'un professionnel du spectacle
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="quote-type_immersion"
|
||||||
|
v-model="quoteForm.immersionPriority"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
>
|
||||||
|
<option value="">Priorité</option>
|
||||||
|
<option value="immersion_0">non souhaité</option>
|
||||||
|
<option value="immersion_1">1</option>
|
||||||
|
<option value="immersion_2">2</option>
|
||||||
|
<option value="immersion_3">3</option>
|
||||||
|
<option value="immersion_4">4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-type_eac" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Parcours EAC
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="quote-type_eac"
|
||||||
|
v-model="quoteForm.eacPriority"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
>
|
||||||
|
<option value="">Priorité</option>
|
||||||
|
<option value="eac_0">non souhaité</option>
|
||||||
|
<option value="eac_1">1</option>
|
||||||
|
<option value="eac_2">2</option>
|
||||||
|
<option value="eac_3">3</option>
|
||||||
|
<option value="eac_4">4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-type_commente" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Concert commenté
|
||||||
|
</label>
|
||||||
|
<select
|
||||||
|
id="quote-type_commente"
|
||||||
|
v-model="quoteForm.concertCommentePriority"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
>
|
||||||
|
<option value="">Priorité</option>
|
||||||
|
<option value="concert_commente_0">non souhaité</option>
|
||||||
|
<option value="concert_commente_1">1</option>
|
||||||
|
<option value="concert_commente_2">2</option>
|
||||||
|
<option value="concert_commente_3">3</option>
|
||||||
|
<option value="concert_commente_4">4</option>
|
||||||
|
</select>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- SOUS-TITRE -->
|
||||||
|
<section class="mb-8 mt-10">
|
||||||
|
<div class="flex items-center gap-4 mb-8">
|
||||||
|
<h3 class="text-2xl font-bold tracking-tight uppercase">Période souhaitée</h3>
|
||||||
|
<div class="h-[2px] flex-1 bg-surface-container"></div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
<!-- SOUS-TITRE QUESTIONS -->
|
||||||
|
<section>
|
||||||
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
|
<div class="space-y-2">
|
||||||
|
<label for="quote-periode" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
|
Période souhaitée de déroulement du projet *
|
||||||
|
</label>
|
||||||
|
<textarea
|
||||||
|
id="quote-periode"
|
||||||
|
v-model.trim="quoteForm.projectPeriod"
|
||||||
|
rows="6"
|
||||||
|
class="w-full rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface outline-none transition-colors focus:border-primary"
|
||||||
|
placeholder=""
|
||||||
|
></textarea>
|
||||||
|
<p v-if="quoteFormErrors.projectPeriod" class="text-xs text-error">{{ quoteFormErrors.projectPeriod }}</p>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</section>
|
||||||
|
|
||||||
|
<!-- ENVOYER LE FORMULAIRE -->
|
||||||
|
<section class="mb-12 mt-12">
|
||||||
|
<div v-if="quoteSubmitError" class="mb-8 rounded-xl border border-error/20 bg-error-container/20 px-4 py-3 text-sm text-error">
|
||||||
|
{{ quoteSubmitError }}
|
||||||
|
</div>
|
||||||
|
<div v-if="quoteSubmitSuccess" class="mb-8 rounded-xl border border-primary/20 bg-primary-container/40 px-4 py-3 text-sm text-on-surface">
|
||||||
|
<span v-if="quoteEmailsSent">Votre demande a bien été envoyée. Un email de confirmation vous a été adressé.</span>
|
||||||
|
<span v-else>Votre demande a bien été enregistrée et transmise à l’équipe de la médiation scolaire.</span>
|
||||||
|
</div>
|
||||||
|
<div v-if="quoteSubmitting" class="mb-8 rounded-xl border border-outline-variant/30 bg-white px-4 py-3 text-sm text-on-surface">
|
||||||
|
Envoi de votre demande en cours...
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center gap-4">
|
||||||
|
<button
|
||||||
|
type="submit"
|
||||||
|
class="inline-flex items-center justify-center rounded-full bg-primary px-6 py-3 text-sm font-bold text-on-primary transition-colors hover:bg-primary-dim disabled:cursor-not-allowed disabled:opacity-60"
|
||||||
|
:disabled="quoteSubmitting"
|
||||||
|
>
|
||||||
|
{{ quoteSubmitting ? 'Envoi en cours...' : 'Envoyer la demande' }}
|
||||||
|
</button>
|
||||||
|
<p class="text-xs text-on-surface-variant">
|
||||||
|
L'équipe d'action culturelle de l'Ondif reviendra vers vous d'ici fin juin 2026 afin de vous informer de la suite donnée à votre candidature.
|
||||||
|
<br>
|
||||||
|
En soumettant ce formulaire, j’accepte que les informations saisies soient utilisées dans le cadre de cette demande.
|
||||||
|
</p>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</form>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup>
|
||||||
|
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
||||||
|
import DsText from '@root/design-system/primitives/DsText.vue'
|
||||||
|
|
||||||
|
|
||||||
|
useHead({
|
||||||
|
link: [
|
||||||
|
{
|
||||||
|
rel: 'preconnect',
|
||||||
|
href: 'https://fonts.googleapis.com',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: 'preconnect',
|
||||||
|
href: 'https://fonts.gstatic.com',
|
||||||
|
crossorigin: '',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: 'stylesheet',
|
||||||
|
href: 'https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700;800&display=swap',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
rel: 'stylesheet',
|
||||||
|
href: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
})
|
||||||
|
|
||||||
|
//--------------------
|
||||||
|
// RÉCUPÉRATION DES DONNÉES STRAPI
|
||||||
|
//--------------------
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const quoteForm = reactive({
|
||||||
|
schoolName: "",
|
||||||
|
department: "",
|
||||||
|
city: "",
|
||||||
|
coordinatorName: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
discipline: "",
|
||||||
|
classLevel: "",
|
||||||
|
studentCount: "",
|
||||||
|
concertRencontrePriority: "",
|
||||||
|
immersionPriority: "",
|
||||||
|
eacPriority: "",
|
||||||
|
concertCommentePriority: "",
|
||||||
|
projectPeriod: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
const quoteFormErrors = reactive({
|
||||||
|
schoolName: "",
|
||||||
|
department: "",
|
||||||
|
city: "",
|
||||||
|
coordinatorName: "",
|
||||||
|
email: "",
|
||||||
|
phone: "",
|
||||||
|
discipline: "",
|
||||||
|
classLevel: "",
|
||||||
|
studentCount: "",
|
||||||
|
projectPeriod: "",
|
||||||
|
})
|
||||||
|
|
||||||
|
const quoteSubmitting = ref(false)
|
||||||
|
const quoteSubmitSuccess = ref(false)
|
||||||
|
const quoteSubmitError = ref("")
|
||||||
|
const quoteEmailsSent = ref(false)
|
||||||
|
|
||||||
|
|
||||||
|
function resetQuoteFormErrors() {
|
||||||
|
quoteFormErrors.schoolName = ""
|
||||||
|
quoteFormErrors.department = ""
|
||||||
|
quoteFormErrors.city = ""
|
||||||
|
quoteFormErrors.coordinatorName = ""
|
||||||
|
quoteFormErrors.email = ""
|
||||||
|
quoteFormErrors.phone = ""
|
||||||
|
quoteFormErrors.discipline = ""
|
||||||
|
quoteFormErrors.classLevel = ""
|
||||||
|
quoteFormErrors.studentCount = ""
|
||||||
|
quoteFormErrors.projectPeriod = ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function validateQuoteForm() {
|
||||||
|
resetQuoteFormErrors()
|
||||||
|
|
||||||
|
let isValid = true
|
||||||
|
|
||||||
|
if (!quoteForm.schoolName) {
|
||||||
|
quoteFormErrors.schoolName = "Le nom de l'établissement scolaire est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.department) {
|
||||||
|
quoteFormErrors.department = "Le département est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.city) {
|
||||||
|
quoteFormErrors.city = "La ville est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.coordinatorName) {
|
||||||
|
quoteFormErrors.coordinatorName = "Le nom et le prénom de l'enseignant coordinateur sont obligatoires."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.email) {
|
||||||
|
quoteFormErrors.email = "L’email est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
} else if (!/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(quoteForm.email)) {
|
||||||
|
quoteFormErrors.email = "L’email n’est pas valide."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.phone) {
|
||||||
|
quoteFormErrors.phone = "Le téléphone est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.discipline) {
|
||||||
|
quoteFormErrors.discipline = "La discipline enseignée est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.classLevel) {
|
||||||
|
quoteFormErrors.classLevel = "Le niveau de classe est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.studentCount) {
|
||||||
|
quoteFormErrors.studentCount = "Le nombre probable d'élèves concernés est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
} else if (Number(quoteForm.studentCount) <= 0) {
|
||||||
|
quoteFormErrors.studentCount = "Le nombre d'élèves doit être supérieur à 0."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!quoteForm.projectPeriod) {
|
||||||
|
quoteFormErrors.projectPeriod = "La période souhaitée est obligatoire."
|
||||||
|
isValid = false
|
||||||
|
}
|
||||||
|
|
||||||
|
return isValid
|
||||||
|
}
|
||||||
|
|
||||||
|
async function submitQuoteRequest() {
|
||||||
|
quoteSubmitSuccess.value = false
|
||||||
|
quoteSubmitError.value = ""
|
||||||
|
quoteEmailsSent.value = false
|
||||||
|
|
||||||
|
if (!validateQuoteForm()) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
quoteSubmitting.value = true
|
||||||
|
|
||||||
|
try {
|
||||||
|
const response = await $fetch("/api/projet-lycee", {
|
||||||
|
method: "POST",
|
||||||
|
body: {
|
||||||
|
schoolName: quoteForm.schoolName,
|
||||||
|
department: quoteForm.department,
|
||||||
|
city: quoteForm.city,
|
||||||
|
coordinatorName: quoteForm.coordinatorName,
|
||||||
|
email: quoteForm.email,
|
||||||
|
phone: quoteForm.phone,
|
||||||
|
discipline: quoteForm.discipline,
|
||||||
|
classLevel: quoteForm.classLevel,
|
||||||
|
studentCount: quoteForm.studentCount,
|
||||||
|
concertRencontrePriority: quoteForm.concertRencontrePriority,
|
||||||
|
immersionPriority: quoteForm.immersionPriority,
|
||||||
|
eacPriority: quoteForm.eacPriority,
|
||||||
|
concertCommentePriority: quoteForm.concertCommentePriority,
|
||||||
|
projectPeriod: quoteForm.projectPeriod,
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
quoteSubmitSuccess.value = true
|
||||||
|
quoteEmailsSent.value = Boolean(response?.emailsSent)
|
||||||
|
quoteForm.schoolName = ""
|
||||||
|
quoteForm.department = ""
|
||||||
|
quoteForm.city = ""
|
||||||
|
quoteForm.coordinatorName = ""
|
||||||
|
quoteForm.email = ""
|
||||||
|
quoteForm.phone = ""
|
||||||
|
quoteForm.discipline = ""
|
||||||
|
quoteForm.classLevel = ""
|
||||||
|
quoteForm.studentCount = ""
|
||||||
|
quoteForm.concertRencontrePriority = ""
|
||||||
|
quoteForm.immersionPriority = ""
|
||||||
|
quoteForm.eacPriority = ""
|
||||||
|
quoteForm.concertCommentePriority = ""
|
||||||
|
quoteForm.projectPeriod = ""
|
||||||
|
resetQuoteFormErrors()
|
||||||
|
} catch (error) {
|
||||||
|
quoteSubmitError.value = error?.data?.statusMessage || "L’envoi de la demande a échoué."
|
||||||
|
} finally {
|
||||||
|
quoteSubmitting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style lang="scss">
|
||||||
|
|
||||||
|
|
||||||
|
.breadcrum_wp {
|
||||||
|
padding-top: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// =======================
|
||||||
|
// SPÉCIFIQUE À CETTE PAGE
|
||||||
|
// =======================
|
||||||
|
.inscription-page {
|
||||||
|
|
||||||
|
|
||||||
|
.fiche_description {
|
||||||
|
display: flex;
|
||||||
|
justify-content: center;
|
||||||
|
padding-top: 10px;
|
||||||
|
padding-bottom: 10px;
|
||||||
|
padding-left: 10px;
|
||||||
|
padding-right: 10px;
|
||||||
|
> * {
|
||||||
|
max-width: 640px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.contact_spe_wp {
|
||||||
|
background-color: var(--c-background-jaune-clair);
|
||||||
|
margin-top: 50px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
||||||
@@ -64,6 +64,7 @@
|
|||||||
button-tone="invert"
|
button-tone="invert"
|
||||||
:ensavoirplus-target="`texte_cache_${index + 3}`"
|
:ensavoirplus-target="`texte_cache_${index + 3}`"
|
||||||
ensavoirplus-group="scolaires-details"
|
ensavoirplus-group="scolaires-details"
|
||||||
|
:lien-cta="t.lien_cta"
|
||||||
>
|
>
|
||||||
<template #title>
|
<template #title>
|
||||||
{{ t.decalage_titre }}
|
{{ t.decalage_titre }}
|
||||||
@@ -187,6 +188,7 @@ et de la programmation jeune public"
|
|||||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||||
? "jaune"
|
? "jaune"
|
||||||
: "dark",
|
: "dark",
|
||||||
|
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||||
tiroir_description: tiroir_item.tiroir_description,
|
tiroir_description: tiroir_item.tiroir_description,
|
||||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||||
id: tiroir_item_img.id,
|
id: tiroir_item_img.id,
|
||||||
|
|||||||
@@ -205,7 +205,7 @@
|
|||||||
<form class="space-y-6" @submit.prevent="submitQuoteRequest">
|
<form class="space-y-6" @submit.prevent="submitQuoteRequest">
|
||||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="quote-request-type" class="block text-sm font-medium text-on-surface">
|
<label for="quote-request-type" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
Type de demande *
|
Type de demande *
|
||||||
</label>
|
</label>
|
||||||
<select
|
<select
|
||||||
@@ -220,7 +220,7 @@
|
|||||||
<p v-if="quoteFormErrors.requestType" class="text-xs text-error">{{ quoteFormErrors.requestType }}</p>
|
<p v-if="quoteFormErrors.requestType" class="text-xs text-error">{{ quoteFormErrors.requestType }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="quote-name" class="block text-sm font-medium text-on-surface">
|
<label for="quote-name" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
Nom complet *
|
Nom complet *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -233,7 +233,7 @@
|
|||||||
<p v-if="quoteFormErrors.name" class="text-xs text-error">{{ quoteFormErrors.name }}</p>
|
<p v-if="quoteFormErrors.name" class="text-xs text-error">{{ quoteFormErrors.name }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="quote-email" class="block text-sm font-medium text-on-surface">
|
<label for="quote-email" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
Email *
|
Email *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -246,7 +246,7 @@
|
|||||||
<p v-if="quoteFormErrors.email" class="text-xs text-error">{{ quoteFormErrors.email }}</p>
|
<p v-if="quoteFormErrors.email" class="text-xs text-error">{{ quoteFormErrors.email }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="quote-phone" class="block text-sm font-medium text-on-surface">
|
<label for="quote-phone" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
Téléphone *
|
Téléphone *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -259,7 +259,7 @@
|
|||||||
<p v-if="quoteFormErrors.phone" class="text-xs text-error">{{ quoteFormErrors.phone }}</p>
|
<p v-if="quoteFormErrors.phone" class="text-xs text-error">{{ quoteFormErrors.phone }}</p>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="quote-organisation" class="block text-sm font-medium text-on-surface">
|
<label for="quote-organisation" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
Structure / organisme *
|
Structure / organisme *
|
||||||
</label>
|
</label>
|
||||||
<input
|
<input
|
||||||
@@ -273,7 +273,7 @@
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div class="space-y-2">
|
<div class="space-y-2">
|
||||||
<label for="quote-message" class="block text-sm font-medium text-on-surface">
|
<label for="quote-message" class="block text-sm font-medium text-on-surface pl-[2px]">
|
||||||
Message *
|
Message *
|
||||||
</label>
|
</label>
|
||||||
<textarea
|
<textarea
|
||||||
@@ -838,7 +838,6 @@
|
|||||||
// =======================
|
// =======================
|
||||||
.parc-page {
|
.parc-page {
|
||||||
|
|
||||||
|
|
||||||
.fiche_description {
|
.fiche_description {
|
||||||
display: flex;
|
display: flex;
|
||||||
justify-content: center;
|
justify-content: center;
|
||||||
@@ -853,7 +852,6 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
.contact_spe_wp {
|
.contact_spe_wp {
|
||||||
background-color: var(--c-background-jaune-clair);
|
background-color: var(--c-background-jaune-clair);
|
||||||
margin-top: 50px;
|
margin-top: 50px;
|
||||||
|
|||||||
@@ -60,7 +60,7 @@
|
|||||||
const isButton = computed(() => !props.to && !props.href) // <button> si pas "href" et pas "to"
|
const isButton = computed(() => !props.to && !props.href) // <button> si pas "href" et pas "to"
|
||||||
// décision de l'élément en fonction de la prop fournie
|
// décision de l'élément en fonction de la prop fournie
|
||||||
const componentTag = computed(() => {
|
const componentTag = computed(() => {
|
||||||
if (isNuxtLink.value) resolveComponent('NuxtLink')
|
if (isNuxtLink.value) return resolveComponent('NuxtLink')
|
||||||
if (isAnchor.value) return 'a'
|
if (isAnchor.value) return 'a'
|
||||||
return 'button'
|
return 'button'
|
||||||
})
|
})
|
||||||
@@ -121,6 +121,12 @@
|
|||||||
padding-inline: var(--sp-14);
|
padding-inline: var(--sp-14);
|
||||||
min-height: 2rem;
|
min-height: 2rem;
|
||||||
}
|
}
|
||||||
|
&--xs {
|
||||||
|
font-size: var(--text-sm);
|
||||||
|
padding: 5px 8px;
|
||||||
|
min-height: 30px;
|
||||||
|
max-height: 30px;
|
||||||
|
}
|
||||||
&--sm {
|
&--sm {
|
||||||
font-size: var(--text-sm);
|
font-size: var(--text-sm);
|
||||||
padding: var(--sp-4) var(--sp-8);
|
padding: var(--sp-4) var(--sp-8);
|
||||||
@@ -141,7 +147,6 @@
|
|||||||
|
|
||||||
/* Variants */
|
/* Variants */
|
||||||
&--primary {
|
&--primary {
|
||||||
background: var(--c-brand);
|
|
||||||
color: var(--ds-button-text, var(--c-brand-contrast));
|
color: var(--ds-button-text, var(--c-brand-contrast));
|
||||||
border-color: var(--ds-button-border, transparent);
|
border-color: var(--ds-button-border, transparent);
|
||||||
}
|
}
|
||||||
@@ -156,6 +161,15 @@
|
|||||||
background: var(--c-hover);
|
background: var(--c-hover);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
&--third {
|
||||||
|
background: var(--c-gris);
|
||||||
|
color: var(--ds-button-text, var(--c-brand-contrast));
|
||||||
|
border-color: var(--ds-button-border, transparent);
|
||||||
|
}
|
||||||
|
&--white {
|
||||||
|
background: var(--c-surface);
|
||||||
|
}
|
||||||
|
|
||||||
&--ghost {
|
&--ghost {
|
||||||
background: transparent;
|
background: transparent;
|
||||||
color: var(--ds-button-text, var(--c-text));
|
color: var(--ds-button-text, var(--c-text));
|
||||||
@@ -189,6 +203,7 @@
|
|||||||
&--text-info { --ds-button-text: var(--c-info); }
|
&--text-info { --ds-button-text: var(--c-info); }
|
||||||
|
|
||||||
&--border-default { --ds-button-border: var(--c-text); }
|
&--border-default { --ds-button-border: var(--c-text); }
|
||||||
|
&--border-none { --ds-button-border: none; }
|
||||||
&--border-muted { --ds-button-border: var(--c-text-muted); }
|
&--border-muted { --ds-button-border: var(--c-text-muted); }
|
||||||
&--border-invert { --ds-button-border: var(--c-text-invert); }
|
&--border-invert { --ds-button-border: var(--c-text-invert); }
|
||||||
&--border-brand_rouge { --ds-button-border: var(--c-brand_rouge); }
|
&--border-brand_rouge { --ds-button-border: var(--c-brand_rouge); }
|
||||||
|
|||||||
@@ -54,6 +54,7 @@ export default defineNuxtConfig({
|
|||||||
mysqlUser: '',
|
mysqlUser: '',
|
||||||
mysqlPassword: '',
|
mysqlPassword: '',
|
||||||
parcRequestRecipientEmail: '',
|
parcRequestRecipientEmail: '',
|
||||||
|
scolaireRequestRecipientEmail: '',
|
||||||
smtpHost: '',
|
smtpHost: '',
|
||||||
smtpPort: '',
|
smtpPort: '',
|
||||||
smtpSecure: '',
|
smtpSecure: '',
|
||||||
|
|||||||
163
server/api/projet-lycee.post.js
Normal file
163
server/api/projet-lycee.post.js
Normal file
@@ -0,0 +1,163 @@
|
|||||||
|
import { createError, readBody } from "h3"
|
||||||
|
import { getMysqlPool } from "~~/server/utils/mysql"
|
||||||
|
import { sendProjetLyceeEmails } from "~~/server/utils/mailer"
|
||||||
|
|
||||||
|
function normalizeValue(value) {
|
||||||
|
return typeof value === "string" ? value.trim() : ""
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePositiveInteger(value) {
|
||||||
|
const number = Number(value)
|
||||||
|
return Number.isInteger(number) && number > 0 ? number : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function normalizePriority(value) {
|
||||||
|
const normalized = normalizeValue(value)
|
||||||
|
if (!normalized) return null
|
||||||
|
|
||||||
|
const match = normalized.match(/_(\d)$/)
|
||||||
|
if (!match) return null
|
||||||
|
|
||||||
|
const priority = Number(match[1])
|
||||||
|
return priority >= 0 && priority <= 4 ? priority : null
|
||||||
|
}
|
||||||
|
|
||||||
|
function isValidEmail(email) {
|
||||||
|
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
||||||
|
}
|
||||||
|
|
||||||
|
function withTimeout(promise, delay, message) {
|
||||||
|
return Promise.race([
|
||||||
|
promise,
|
||||||
|
new Promise((_, reject) => {
|
||||||
|
setTimeout(() => reject(new Error(message)), delay)
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
}
|
||||||
|
|
||||||
|
export default defineEventHandler(async (event) => {
|
||||||
|
const body = await readBody(event)
|
||||||
|
|
||||||
|
const payload = {
|
||||||
|
schoolName: normalizeValue(body?.schoolName),
|
||||||
|
department: normalizeValue(body?.department),
|
||||||
|
city: normalizeValue(body?.city),
|
||||||
|
coordinatorName: normalizeValue(body?.coordinatorName),
|
||||||
|
email: normalizeValue(body?.email),
|
||||||
|
phone: normalizeValue(body?.phone),
|
||||||
|
discipline: normalizeValue(body?.discipline),
|
||||||
|
classLevel: normalizeValue(body?.classLevel),
|
||||||
|
studentCount: normalizePositiveInteger(body?.studentCount),
|
||||||
|
concertRencontrePriority: normalizePriority(body?.concertRencontrePriority),
|
||||||
|
immersionPriority: normalizePriority(body?.immersionPriority),
|
||||||
|
eacPriority: normalizePriority(body?.eacPriority),
|
||||||
|
concertCommentePriority: normalizePriority(body?.concertCommentePriority),
|
||||||
|
projectPeriod: normalizeValue(body?.projectPeriod),
|
||||||
|
}
|
||||||
|
|
||||||
|
if (
|
||||||
|
!payload.schoolName ||
|
||||||
|
!payload.department ||
|
||||||
|
!payload.city ||
|
||||||
|
!payload.coordinatorName ||
|
||||||
|
!payload.email ||
|
||||||
|
!payload.phone ||
|
||||||
|
!payload.discipline ||
|
||||||
|
!payload.classLevel ||
|
||||||
|
!payload.studentCount ||
|
||||||
|
!payload.projectPeriod
|
||||||
|
) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: "Tous les champs obligatoires doivent être remplis.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isValidEmail(payload.email)) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: "L’adresse email n’est pas valide.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!/^\d{2}$/.test(payload.department)) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: "Le département n’est pas valide.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (payload.projectPeriod.length > 5000) {
|
||||||
|
throw createError({
|
||||||
|
statusCode: 400,
|
||||||
|
statusMessage: "La période souhaitée est trop longue.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const db = getMysqlPool()
|
||||||
|
|
||||||
|
try {
|
||||||
|
const [result] = await db.execute(
|
||||||
|
`
|
||||||
|
INSERT INTO projet_lycee_demandes (
|
||||||
|
nom_etablissement,
|
||||||
|
departement,
|
||||||
|
ville,
|
||||||
|
coordinateur_nom,
|
||||||
|
email,
|
||||||
|
telephone,
|
||||||
|
discipline,
|
||||||
|
niveau_classe,
|
||||||
|
nombre_eleves,
|
||||||
|
priorite_concert_rencontre,
|
||||||
|
priorite_immersion,
|
||||||
|
priorite_eac,
|
||||||
|
priorite_concert_commente,
|
||||||
|
periode_souhaitee
|
||||||
|
)
|
||||||
|
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||||
|
`,
|
||||||
|
[
|
||||||
|
payload.schoolName,
|
||||||
|
payload.department,
|
||||||
|
payload.city,
|
||||||
|
payload.coordinatorName,
|
||||||
|
payload.email,
|
||||||
|
payload.phone,
|
||||||
|
payload.discipline,
|
||||||
|
payload.classLevel,
|
||||||
|
payload.studentCount,
|
||||||
|
payload.concertRencontrePriority,
|
||||||
|
payload.immersionPriority,
|
||||||
|
payload.eacPriority,
|
||||||
|
payload.concertCommentePriority,
|
||||||
|
payload.projectPeriod,
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
let emailsSent = false
|
||||||
|
|
||||||
|
try {
|
||||||
|
emailsSent = await withTimeout(
|
||||||
|
sendProjetLyceeEmails(payload),
|
||||||
|
15000,
|
||||||
|
"Délai dépassé pour l'envoi email projet-lycee."
|
||||||
|
)
|
||||||
|
} catch (mailError) {
|
||||||
|
console.error("Erreur envoi email projet-lycee:", mailError)
|
||||||
|
}
|
||||||
|
|
||||||
|
return {
|
||||||
|
ok: true,
|
||||||
|
id: result.insertId,
|
||||||
|
emailsSent,
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("Erreur API projet-lycee:", error)
|
||||||
|
|
||||||
|
throw createError({
|
||||||
|
statusCode: 500,
|
||||||
|
statusMessage: "Impossible d’enregistrer la candidature pour le moment.",
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -27,6 +27,9 @@ function getTransporter() {
|
|||||||
host: config.smtpHost,
|
host: config.smtpHost,
|
||||||
port: Number(config.smtpPort),
|
port: Number(config.smtpPort),
|
||||||
secure: String(config.smtpSecure).toLowerCase() === "true",
|
secure: String(config.smtpSecure).toLowerCase() === "true",
|
||||||
|
connectionTimeout: 10000,
|
||||||
|
greetingTimeout: 10000,
|
||||||
|
socketTimeout: 15000,
|
||||||
auth: {
|
auth: {
|
||||||
user: config.smtpUser,
|
user: config.smtpUser,
|
||||||
pass: config.smtpPassword,
|
pass: config.smtpPassword,
|
||||||
@@ -96,3 +99,93 @@ export async function sendParcDemandEmails(payload) {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function formatProjetLyceePriority(priority) {
|
||||||
|
if (priority === null || priority === undefined) return "Non renseigné"
|
||||||
|
if (priority === 0) return "Non souhaité"
|
||||||
|
return `Priorité ${priority}`
|
||||||
|
}
|
||||||
|
|
||||||
|
function getProjetLyceeSummary(payload) {
|
||||||
|
const projectWishes = [
|
||||||
|
["Concert-rencontre", payload.concertRencontrePriority],
|
||||||
|
["Parcours Entrer dans la peau d'un professionnel du spectacle", payload.immersionPriority],
|
||||||
|
["Parcours EAC", payload.eacPriority],
|
||||||
|
["Concert commente", payload.concertCommentePriority],
|
||||||
|
]
|
||||||
|
.filter(([, priority]) => priority >= 1 && priority <= 4)
|
||||||
|
.map(([label, priority]) => `${label} : ${formatProjetLyceePriority(priority)}`)
|
||||||
|
|
||||||
|
return [
|
||||||
|
`Etablissement scolaire : ${payload.schoolName}`,
|
||||||
|
`Departement : ${payload.department}`,
|
||||||
|
`Ville : ${payload.city}`,
|
||||||
|
`Enseignant coordinateur : ${payload.coordinatorName}`,
|
||||||
|
`Email : ${payload.email}`,
|
||||||
|
`Telephone : ${payload.phone}`,
|
||||||
|
`Discipline enseignee : ${payload.discipline}`,
|
||||||
|
`Niveau de classe : ${payload.classLevel}`,
|
||||||
|
`Nombre probable d'eleves concernes : ${payload.studentCount}`,
|
||||||
|
...(projectWishes.length ? ["", "Souhaits de projet :", ...projectWishes] : []),
|
||||||
|
"",
|
||||||
|
"Periode souhaitee :",
|
||||||
|
payload.projectPeriod,
|
||||||
|
].join("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function sendProjetLyceeEmails(payload) {
|
||||||
|
const config = useRuntimeConfig()
|
||||||
|
const mailer = getTransporter()
|
||||||
|
|
||||||
|
if (!mailer) {
|
||||||
|
console.warn("Email projet lycée non envoyé: configuration SMTP incomplète.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!config.scolaireRequestRecipientEmail) {
|
||||||
|
console.warn("Email projet lycée non envoyé: destinataire scolaire manquant.")
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
const from = config.smtpFromName
|
||||||
|
? `"${config.smtpFromName}" <${config.smtpFromEmail}>`
|
||||||
|
: config.smtpFromEmail
|
||||||
|
|
||||||
|
const summary = getProjetLyceeSummary(payload)
|
||||||
|
|
||||||
|
const adminSubject = `WONDIF - Candidature projet lycee - ${payload.schoolName}`
|
||||||
|
const adminText = [
|
||||||
|
"Une nouvelle candidature a été envoyée depuis le formulaire Projet lycée.",
|
||||||
|
"",
|
||||||
|
summary,
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
const userSubject = "ONDIF - Confirmation de votre candidature - Projet lycée"
|
||||||
|
const userText = [
|
||||||
|
`Bonjour ${payload.coordinatorName},`,
|
||||||
|
"",
|
||||||
|
"Votre candidature à un projet avec l'Orchestre national d'Île-de-France a bien été transmise.",
|
||||||
|
"L'équipe d'action culturelle reviendra vers vous d'ici fin juin 2026 afin de vous informer de la suite donnée à votre candidature.",
|
||||||
|
"",
|
||||||
|
"Recapitulatif :",
|
||||||
|
summary,
|
||||||
|
].join("\n")
|
||||||
|
|
||||||
|
await Promise.all([
|
||||||
|
mailer.sendMail({
|
||||||
|
from,
|
||||||
|
to: config.scolaireRequestRecipientEmail,
|
||||||
|
replyTo: payload.email,
|
||||||
|
subject: adminSubject,
|
||||||
|
text: adminText,
|
||||||
|
}),
|
||||||
|
mailer.sendMail({
|
||||||
|
from,
|
||||||
|
to: payload.email,
|
||||||
|
subject: userSubject,
|
||||||
|
text: userText,
|
||||||
|
}),
|
||||||
|
])
|
||||||
|
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user