generated from gitea_admin/default
Formulaire academie
This commit is contained in:
881
README.md
881
README.md
@@ -1,7 +1,9 @@
|
||||
# Stack
|
||||
- NUXT 4
|
||||
- VueJS 3.5
|
||||
- Tailwindcss pour les pages professionnels
|
||||
- Tailwindcss pour les pages professionnels et certains styles
|
||||
|
||||
|
||||
|
||||
# Dev du site en local
|
||||
## NUXT
|
||||
@@ -57,7 +59,7 @@ git push origin main
|
||||
2. cd /var/www/wondif_vue
|
||||
3.
|
||||
git pull origin main # récupère le dernier code
|
||||
si besoin (ajout de dépendances)
|
||||
si besoin d'ajout de dépendances (ajout bibliothèques)
|
||||
npm ci # installe / met à jour les dépendances (à exécuter uniquement si nouvelle dépendances installées dans package json)
|
||||
Si nouvelle variable d'env
|
||||
ajouter la variable dans le fichier `more ecosystem.config.cjs` manuellement
|
||||
@@ -67,6 +69,33 @@ pm2 reload ecosystem.config.cjs --only wondif_vue # redémarre le process av
|
||||
# URL
|
||||
/inscriptions/projet_lycee
|
||||
|
||||
|
||||
# ICONES
|
||||
Il y a un système mis en place pour mettre des icones.
|
||||
Le système est celui de Google font qui a une font pour les icones.
|
||||
C'est en mode CDN, je n'ai pas chargé la font.
|
||||
|
||||
**Pour choisir une icone** il faut aller sur la page Google font https://fonts.google.com/icons?selected=Material+Symbols+Outlined
|
||||
- Choisir un icone. Quand on clique dessus on voit des infos sur l'icone. Il faut trouver "Icon name" (tout en bas), récupérer le nom de l'icone.
|
||||
|
||||
Ensuite pour **l'afficher sur la page web**.
|
||||
- On ajoute une balise avec la classe qui appelle la Google font et à l'intérieur le nom de l'icone que l'on a récupéré.
|
||||
``` javascript
|
||||
<span class="material-symbols-outlined text-3xl text-outline group-hover:text-primary">download</span>
|
||||
```
|
||||
Dans le head de la page il faut ajouter ceci pour donner le chemin CDN
|
||||
``` javascript
|
||||
useHead({
|
||||
link: [
|
||||
{
|
||||
rel: 'stylesheet',
|
||||
href: 'https://fonts.googleapis.com/css2?family=Material+Symbols+Outlined:wght,FILL@100..700,0..1&display=swap',
|
||||
},
|
||||
],
|
||||
})
|
||||
```
|
||||
|
||||
|
||||
# STRAPI
|
||||
## URL de PROD
|
||||
bo.orchestre-ile.com
|
||||
@@ -212,851 +241,3 @@ Array.from(document.querySelectorAll('body > *')).forEach(el => {
|
||||
el.style.outline = '1px solid red';
|
||||
el.style.backgroundColor = 'rgba(255, 0, 0, 0.3)'; // Utilisation de rgba pour une semi-transparence
|
||||
});
|
||||
|
||||
|
||||
|
||||
@media (max-width: 980px) {
|
||||
.decalage_gauche {
|
||||
margin-left: -23px;
|
||||
}
|
||||
}
|
||||
|
||||
01_siick_smol_girlfriend_5m50.mp3
|
||||
02_siick_lunar_blast 213_5m13.wav
|
||||
03_siick_crystal_cavern_5m24.wav
|
||||
|
||||
|
||||
01_leon_romanens_8m49_stereo.wav
|
||||
02_marie_pierre_8m57_stereo.wav
|
||||
03_siick_7m23_stereo.wav
|
||||
04_celine_espuna_4m34_stereo.wav
|
||||
06_walid_nouh_6m44_stereo.wav
|
||||
08_codimp_7m43_stereo.wav
|
||||
05_adrien_bourmault_7m53_stereo.wav
|
||||
07_rafael_muniz_3m15_stereo.wav
|
||||
|
||||
|
||||
|
||||
debian@vps-48ebe2d9:~$ history
|
||||
1 sudo nano ~/.bashrc
|
||||
2 exit
|
||||
3 ll
|
||||
4 date
|
||||
5 sudo ln -sf /usr/share/zoneinfo/Europe/Paris /etc/localtime
|
||||
6 date
|
||||
7 cd /
|
||||
8 sudo apt update
|
||||
9 apt list --upgradable
|
||||
10 sudo apt upgrade
|
||||
11 apt list --upgradable
|
||||
12 sudo nano /etc/ssh/sshd_config
|
||||
13 sudo systemctl restart sshd
|
||||
14 exit
|
||||
15 sudo apt install fail2ban
|
||||
16 cd /etc/fail2ban/
|
||||
17 ll
|
||||
18 sudo nano /etc/fail2ban/jail.conf
|
||||
19 sudo service fail2ban reload
|
||||
20 sudo service fail2ban status
|
||||
21 sudo service fail2ban start
|
||||
22 sudo service fail2ban status
|
||||
23 fail2ban-client status
|
||||
24 ll
|
||||
25 www
|
||||
26 cd /
|
||||
27 ll
|
||||
28 www
|
||||
29 cd var
|
||||
30 ll
|
||||
31 date
|
||||
32 cd /
|
||||
33 sudo apt update
|
||||
34 apt list --upgradable
|
||||
35 sudo apt upgrade
|
||||
36 cd /etc/fail2ban/
|
||||
37 sudo service fail2ban status
|
||||
38 systemctl status nginx
|
||||
39 sudo apt install nginx
|
||||
40 systemctl status nginx
|
||||
41 sudo systemctl enable nginx
|
||||
42 sudo systemctl is-enabled nginx
|
||||
43 sudo nano /etc/nginx/nginx.conf
|
||||
44 sudo systemctl reload nginx
|
||||
45 systemctl status nginx
|
||||
46 www
|
||||
47 ll
|
||||
48 cd ..
|
||||
49 ll
|
||||
50 cd www
|
||||
51 ll
|
||||
52 cd html/
|
||||
53 ll
|
||||
54 cd www
|
||||
55 ll
|
||||
56 www
|
||||
57 ll
|
||||
58 sudo mkdir wondif_2025
|
||||
59 ll
|
||||
60 cd wondif_2025/
|
||||
61 sudo nano index.html
|
||||
62 ll
|
||||
63 more index.html
|
||||
64 cd /etc/nginx/sites-available
|
||||
65 ll
|
||||
66 de default
|
||||
67 more default
|
||||
68 sudo nano wondif_2025
|
||||
69 ll
|
||||
70 more wondif_2025
|
||||
71 cd /etc/nginx/sites-enabled
|
||||
72 ll
|
||||
73 sudo ln -s /etc/nginx/sites-available/wondif_2025 /etc/nginx/sites-enabled/
|
||||
74 ll
|
||||
75 sudo nginx -t
|
||||
76 systemctl restart nginx
|
||||
77 systemctl status nginx
|
||||
78 www
|
||||
79 ll
|
||||
80 cd wondif_2025/
|
||||
81 ll
|
||||
82 pwd
|
||||
83 cd /etc/nginx/sites-available
|
||||
84 ll
|
||||
85 more wondif_2025
|
||||
86 www
|
||||
87 ll
|
||||
88 cd wondif_2025/
|
||||
89 ll
|
||||
90 sudo nano index.html
|
||||
91 www
|
||||
92 curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.3/install.sh | bash
|
||||
93 exit
|
||||
94 python3 --version
|
||||
95 sudo apt install certbot python3-certbot-nginx
|
||||
96 cd /etc/nginx/sites-available
|
||||
97 ll
|
||||
98 sudo nano strapi_wondif
|
||||
99 cd /etc/nginx/sites-enabled
|
||||
100 ll
|
||||
101 sudo ln -s /etc/nginx/sites-available/strapi_wondif /etc/nginx/sites-enabled/
|
||||
102 ll
|
||||
103 sudo nginx -t
|
||||
104 systemctl restart nginx
|
||||
105 systemctl status nginx
|
||||
106 cd /etc/nginx/sites-available
|
||||
107 sudo certbot --nginx -d 2025.orchestre-ile.com
|
||||
108 ll
|
||||
109 more wondif_2025
|
||||
110 sudo certbot --nginx -d bo.orchestre-ile.com
|
||||
111 ll
|
||||
112 more strapi_wondif
|
||||
113 sudo certbot certificates
|
||||
114 sudo nginx -t
|
||||
115 systemctl restart nginx
|
||||
116 systemctl status nginx
|
||||
117 ll
|
||||
118 sudo nano wondif_2025
|
||||
119 systemctl restart nginx
|
||||
120 systemctl status nginx
|
||||
121 www
|
||||
122 nvm --version
|
||||
123 nvm ls-remote
|
||||
124 nvm install 22.18.0
|
||||
125 node -v
|
||||
126 corepack enable yarn
|
||||
127 yarn -v
|
||||
128 www
|
||||
129 yarn global add pm2
|
||||
130 pm2 start
|
||||
131 pm2 list
|
||||
132 cd /
|
||||
133 pm2 list
|
||||
134 yarn global bin
|
||||
135 more ~/.bashrc
|
||||
136 sudo nano ~/.bashrc
|
||||
137 nano ~/.bashrc
|
||||
138 source ~/.bashrc
|
||||
139 pm2 start
|
||||
140 pm2 list
|
||||
141 mysql -u root -p
|
||||
142 cd /
|
||||
143 ll
|
||||
144 sudo apt install gnupg
|
||||
145 cd /home/debian
|
||||
146 wget https://dev.mysql.com/get/mysql-apt-config_0.8.34-1_all.deb // version du 04/08/2025
|
||||
147 ll
|
||||
148 sudo dpkg -i mysql-apt-config*
|
||||
149 apt update
|
||||
150 sudo apt update --allow-insecure-repositories
|
||||
151 sudo apt-get upgrade
|
||||
152 sudo apt update
|
||||
153 sudo apt install mysql-server
|
||||
154 sudo systemctl status mysql
|
||||
155 sudo nano /etc/mysql/mysql.conf.d/mysqld.cnf
|
||||
156 sudo systemctl restart mysql
|
||||
157 sudo systemctl status mysql
|
||||
158 mysql -u root -p
|
||||
159 sav
|
||||
160 ll
|
||||
161 sudo nano strapi_wondif
|
||||
162 sudo nginx -t
|
||||
163 sudo nano strapi_wondif
|
||||
164 www
|
||||
165 ll
|
||||
166 cd strapi_wondif/
|
||||
167 ll
|
||||
168 rm strapi_wondif.js
|
||||
169 ll
|
||||
170 sav
|
||||
171 ll
|
||||
172 sudo nginx -t
|
||||
173 more strapi_wondif
|
||||
174 sudo nano strapi_wondif
|
||||
175 sudo nginx -t
|
||||
176 sudo nano strapi_wondif
|
||||
177 systemctl restart nginx
|
||||
178 systemctl status nginx
|
||||
179 www
|
||||
180 ll
|
||||
181 cd strapi_wondif/
|
||||
182 ll
|
||||
183 more .env
|
||||
184 www
|
||||
185 ll
|
||||
186 cd /var
|
||||
187 ll
|
||||
188 chown debian www
|
||||
189 ll
|
||||
190 sudo chown debian www
|
||||
191 wwww
|
||||
192 www
|
||||
193 ll
|
||||
194 yarn create strapi
|
||||
195 ll
|
||||
196 cd strapi_wondif/
|
||||
197 ll
|
||||
198 sudo nano strapi_wondif.js
|
||||
199 yarn strapi_wondif start
|
||||
200 ll
|
||||
201 more package.json
|
||||
202 NODE_OPTIONS="--max-old-space-size=2048" NODE_ENV=production yarn build
|
||||
203 yarn run start
|
||||
204 ll
|
||||
205 pm2 start yarn --name strapi_wondif -- start
|
||||
206 pm2 logs strapi_wondif
|
||||
207 pm2 list
|
||||
208 pm2 logs strapi_wondif
|
||||
209 pm2 list
|
||||
210 pm2 save
|
||||
211 pm2 startup systemd
|
||||
212 sudo env PATH=$PATH:/home/debian/.nvm/versions/node/v22.18.0/bin /home/debian/.config/yarn/global/node_modules/pm2/bin/pm2 startup systemd -u debian --hp /home/debian
|
||||
213 pm2 list
|
||||
214 ll
|
||||
215 date
|
||||
216 cd /
|
||||
217 sudo apt update
|
||||
218 sudo apt list --upgradable
|
||||
219 sudo apt upgrade
|
||||
220 sudo apt update
|
||||
221 sudo iptables -L
|
||||
222 sudo apt install fail2ban
|
||||
223 cd /etc/fail2ban/
|
||||
224 ls
|
||||
225 ll
|
||||
226 cd /var/log/fail2ban.log
|
||||
227 cd /var/log/
|
||||
228 ll
|
||||
229 more fail2ban.log
|
||||
230 fail2ban-client status
|
||||
231 sudo fail2ban-client status
|
||||
232 sudo apt-get install net-tools
|
||||
233 netstat -a
|
||||
234 ls
|
||||
235 cd /
|
||||
236 ls
|
||||
237 cd var
|
||||
238 ls
|
||||
239 ll
|
||||
240 cd www
|
||||
241 ll
|
||||
242 sav
|
||||
243 ll
|
||||
244 more wondif_2025
|
||||
245 ll
|
||||
246 sudo nano wondif_media
|
||||
247 more wondif_media
|
||||
248 sudo nano wondif_media
|
||||
249 more wondif_media
|
||||
250 cd /etc/nginx/sites-enabled
|
||||
251 sudo ln -s /etc/nginx/sites-available/wondif_media /etc/nginx/sites-enabled/
|
||||
252 ll
|
||||
253 sudo certbot --nginx -d media.orchestre-ile.com
|
||||
254 sav
|
||||
255 ll
|
||||
256 more wondif_media
|
||||
257 sudo nginx -t
|
||||
258 systemctl restart nginx
|
||||
259 systemctl status nginx
|
||||
260 www
|
||||
261 ll
|
||||
262 pm2
|
||||
263 pm2 list
|
||||
264 ls
|
||||
265 cd strapi_wondif/
|
||||
266 ll
|
||||
267 pwd
|
||||
268 npm install @strapi/provider-upload-aws-s3 --save
|
||||
269 ls
|
||||
270 ll
|
||||
271 cd config/
|
||||
272 ll
|
||||
273 more plugins.js
|
||||
274 sudo nano plugins.js
|
||||
275 ll
|
||||
276 cd ;;
|
||||
277 cd..
|
||||
278 ll
|
||||
279 cd ..
|
||||
280 ll
|
||||
281 sudo nano .env
|
||||
282 npm run build
|
||||
283 npm run start
|
||||
284 ll
|
||||
285 pm2 list
|
||||
286 pm2 restart strapi_wondif
|
||||
287 ll
|
||||
288 cd config/
|
||||
289 ll
|
||||
290 more plugins.js
|
||||
291 sudo nano plugins.js
|
||||
292 pm2 restart strapi_wondif
|
||||
293 sudo nano plugins.js
|
||||
294 sav
|
||||
295 ll
|
||||
296 ls
|
||||
297 sudo nano strapi_wondif
|
||||
298 sudo nano wondif_media
|
||||
299 sudo nginx -t
|
||||
300 sudo systemctl reload nginx
|
||||
301 pm2 restart strapi_wondif
|
||||
302 www
|
||||
303 ll
|
||||
304 cd strapi_wondif/
|
||||
305 ll
|
||||
306 cd config/
|
||||
307 ll
|
||||
308 sudo nano middlewares.js
|
||||
309 pm2 restart strapi_wondif
|
||||
310 pm2 stop strapi_wondif
|
||||
311 npm run build
|
||||
312 pm2 start strapi_wondif
|
||||
313 www
|
||||
314 ll
|
||||
315 cd wondif_2025/
|
||||
316 pwd
|
||||
317 ll
|
||||
318 sav
|
||||
319 ll
|
||||
320 more wondif_2025
|
||||
321 www
|
||||
322 ll
|
||||
323 cd wondif_2025/
|
||||
324 ll
|
||||
325 cd ..
|
||||
326 ll
|
||||
327 mv wondif_2025 wondif_2025_old
|
||||
328 ll
|
||||
329 git clone git@git.parisweb.art:gitea_admin/wondif_vue.git
|
||||
330 sudo apt update
|
||||
331 sudo apt install git -y
|
||||
332 git --version
|
||||
333 git clone git@git.parisweb.art:gitea_admin/wondif_vue.git
|
||||
334 git clone https://git.parisweb.art/gitea_admin/wondif_vue
|
||||
335 ll
|
||||
336 cd wondif_vue/
|
||||
337 ll
|
||||
338 sav
|
||||
339 ll
|
||||
340 sudo nano wondif_2025
|
||||
341 sudo nginx -t
|
||||
342 sudo systemctl reload nginx
|
||||
343 www
|
||||
344 ll
|
||||
345 cd wondif_vue/
|
||||
346 ll
|
||||
347 sudo nano .env
|
||||
348 npm ci
|
||||
349 npm run build
|
||||
350 ll
|
||||
351 pm2 list
|
||||
352 pm2 start npm --name "wondif_vue" -- run start
|
||||
353 pm2 save
|
||||
354 pm2 logs wondif_vue
|
||||
355 curl http://127.0.0.1:3000
|
||||
356 sav
|
||||
357 ll
|
||||
358 sudo nano wondif_2025
|
||||
359 www
|
||||
360 ll
|
||||
361 cd wondif_vue/
|
||||
362 ll
|
||||
363 wondif_vue/
|
||||
364 cat package.json
|
||||
365 sudo nano package.json
|
||||
366 cat package.json
|
||||
367 npm run build
|
||||
368 pm2 list
|
||||
369 pm2 stop wondif_vue
|
||||
370 pm2 start wondif_vue
|
||||
371 pm2 logs wondif_vue
|
||||
372 curl http://127.0.0.1:3000
|
||||
373 sav
|
||||
374 ll
|
||||
375 more wondif_2025
|
||||
376 sudo nano wondif_2025
|
||||
377 sudo nginx -t
|
||||
378 sudo systemctl reload nginx
|
||||
379 more wondif_2025
|
||||
380 www
|
||||
381 ll
|
||||
382 cd wondif_vue/
|
||||
383 ll
|
||||
384 cd public/
|
||||
385 ll
|
||||
386 cd img/
|
||||
387 ll
|
||||
388 cd photos/
|
||||
389 ll
|
||||
390 mv zaho_2.jpg zaho.jpg
|
||||
391 ll
|
||||
392 cd /var/www/wondif_vue
|
||||
393 ll
|
||||
394 npm ci
|
||||
395 npm run build
|
||||
396 pm2 restart wondif_vue
|
||||
397 git pull origin main
|
||||
398 rm public/img/photos/zaho.jpg
|
||||
399 git pull origin main
|
||||
400 npm ci
|
||||
401 npm run build
|
||||
402 ll
|
||||
403 more .env
|
||||
404 pm2 list
|
||||
405 pm2 logs wondif_vue
|
||||
406 cd /home/debian/.pm2/logs/
|
||||
407 ll
|
||||
408 more strapi-wondif-error.log
|
||||
409 ll
|
||||
410 truncate -s 0 strapi-wondif-error.log
|
||||
411 ll
|
||||
412 more strapi-wondif-out.log
|
||||
413 ll
|
||||
414 truncate -s 0 strapi-wondif-out.log
|
||||
415 ll
|
||||
416 more wondif-vue-error.log
|
||||
417 ll
|
||||
418 truncate -s 0 wondif-vue-error.log
|
||||
419 more wondif-vue-out.log
|
||||
420 ll
|
||||
421 truncate -s 0 wondif-vue-out.log
|
||||
422 www
|
||||
423 ll
|
||||
424 cd wondif_vue/
|
||||
425 ll
|
||||
426 cd app
|
||||
427 ll
|
||||
428 cd components/
|
||||
429 ll
|
||||
430 more header_content.vue
|
||||
431 ll
|
||||
432 more header_full.vue
|
||||
433 ll
|
||||
434 cd ..
|
||||
435 ll
|
||||
436 more app.vue
|
||||
437 ll
|
||||
438 cd pages/
|
||||
439 ll
|
||||
440 more index.vue
|
||||
441 ll
|
||||
442 more agenda.vue
|
||||
443 www
|
||||
444 ll
|
||||
445 cd wondif_vue/
|
||||
446 ll
|
||||
447 cd app/
|
||||
448 ll
|
||||
449 cd pages/
|
||||
450 ll
|
||||
451 more index.vue
|
||||
452 cd ..
|
||||
453 ll
|
||||
454 cd components/
|
||||
455 ll
|
||||
456 more header_full.vue
|
||||
457 ll
|
||||
458 more header_content.vue
|
||||
459 www
|
||||
460 ll
|
||||
461 cd wondif_vue/
|
||||
462 ll
|
||||
463 sav
|
||||
464 ll
|
||||
465 more wondif_2025
|
||||
466 sudo adduser --disabled-password --gecos "" deploy
|
||||
467 sudo usermod -aG www-data deploy
|
||||
468 cd /
|
||||
469 ll
|
||||
470 cd srv
|
||||
471 ll
|
||||
472 sudo ll
|
||||
473 sudo ls
|
||||
474 ls
|
||||
475 ll
|
||||
476 cd 2025
|
||||
477 sudo mkdir -p /srv/apps/wondif_vue
|
||||
478 ll
|
||||
479 sudo chown -R deploy:deploy /srv/apps/wondif_vue
|
||||
480 ll
|
||||
481 sudo -u deploy ssh-keygen -t ed25519 -C "deploy@server" -f /home/deploy/.ssh/id_ed25519
|
||||
482 sudo -u deploy cat /home/deploy/.ssh/id_ed25519.pub
|
||||
483 sudo -u deploy ssh -T git@git.parisweb.art
|
||||
484 sudo -u deploy ssh -T gitea_admin@git.parisweb.art
|
||||
485 sudo -u deploy ssh -T git@git.parisweb.art
|
||||
486 sudo -u deploy ssh -T git@git.parisweb.art; echo "EXIT=$?"
|
||||
487 cd /home
|
||||
488 ll
|
||||
489 cd deploy/
|
||||
490 ll
|
||||
491 sudo cd deploy/
|
||||
492 sudo -u deploy rm -f /home/deploy/.ssh/id_ed25519
|
||||
493 sudo -u deploy rm -f /home/deploy/.ssh/id_ed25519.pub
|
||||
494 sudo -u deploy rm -f /home/deploy/.ssh/known_hosts
|
||||
495 sudo -u deploy ls -la /home/deploy/.ssh
|
||||
496 sudo -u deploy ls -la /home/deploy/
|
||||
497 sudo -u deploy ssh-keygen -t ed25519 -C "deploy@bookstore" -f /home/deploy/.ssh/id_ed25519
|
||||
498 sudo -u deploy cat /home/deploy/.ssh/id_ed25519.pub
|
||||
499 sudo -u deploy rm -f /home/deploy/.ssh/id_ed25519
|
||||
500 sudo -u deploy rm -f /home/deploy/.ssh/id_ed25519.pub
|
||||
501 sudo -u deploy rm -f /home/deploy/.ssh/known_hosts
|
||||
502 sudo -u deploy ls -la /home/deploy/.ssh
|
||||
503 sudo -u deploy ssh-keygen -t ed25519 -C "deploy@wondif_vue" -f /home/deploy/.ssh/id_ed25519
|
||||
504 sudo -u deploy cat /home/deploy/.ssh/id_ed25519.pub
|
||||
505 sudo -u deploy ssh -T git@git.parisweb.art
|
||||
506 sudo ss -lntp | grep -E ':22|:2222|:3000|:3022|:2200'
|
||||
507 sudo -u deploy rm -f /home/deploy/.ssh/id_ed25519
|
||||
508 sudo -u deploy rm -f /home/deploy/.ssh/id_ed25519.pub
|
||||
509 sudo -u deploy rm -f /home/deploy/.ssh/known_hosts
|
||||
510 sudo -u deploy ls -la /home/deploy/.ssh
|
||||
511 sudo -u deploy nano /home/deploy/.netrc
|
||||
512 sudo -u deploy more /home/deploy/.netr
|
||||
513 sudo chmod 600 /home/deploy/.netrc
|
||||
514 sudo chown deploy:deploy /home/deploy/.netrc
|
||||
515 sudo -u deploy git ls-remote https://git.parisweb.art/gitea_admin/wondif_vue
|
||||
516 cd /srv
|
||||
517 ll
|
||||
518 cd apps/
|
||||
519 ll
|
||||
520 cd wondif_vue/
|
||||
521 ll
|
||||
522 sudo -u deploy git clone https://git.parisweb.art/gitea_admin/wondif_vue /srv/apps/wondif_vue
|
||||
523 ll
|
||||
524 more .git
|
||||
525 more README.md
|
||||
526 cd /opt
|
||||
527 ll
|
||||
528 cd /var
|
||||
529 ll
|
||||
530 cd log/
|
||||
531 ll
|
||||
532 sudo mkdir -p /opt/deploy/wondif_vue /var/log/deploy
|
||||
533 ll
|
||||
534 sudo chown -R deploy:deploy /opt/deploy/wondif_vue /var/log/deploy
|
||||
535 sudo -u deploy nano /opt/deploy/wondif_vue/deploy.sh
|
||||
536 sudo chmod +x /opt/deploy/wondif_vue/deploy.sh
|
||||
537 cd /opt/
|
||||
538 ll
|
||||
539 cd deploy/
|
||||
540 ll
|
||||
541 cd wondif_vue/
|
||||
542 ll
|
||||
543 sudo -u nano deploy.sh
|
||||
544 sudo -u deploy nano deploy.sh
|
||||
545 pm2 list
|
||||
546 cd www
|
||||
547 ll
|
||||
548 www
|
||||
549 ll
|
||||
550 cd wondif_vue/
|
||||
551 ll
|
||||
552 more package.json
|
||||
553 cd ..
|
||||
554 ll
|
||||
555 cd wondif_vue/
|
||||
556 ll
|
||||
557 sudo chmod +x /opt/deploy/wondif_vue/deploy.sh
|
||||
558 www
|
||||
559 ll
|
||||
560 cd wondif_vue/
|
||||
561 ll
|
||||
562 git pull origin main
|
||||
563 more package.json
|
||||
564 more .gitignore
|
||||
565 sudo nano .gitignore
|
||||
566 more package.json
|
||||
567 git pull origin main
|
||||
568 git fetch origin
|
||||
569 git reset --hard origin/main
|
||||
570 git status
|
||||
571 git pull origin main
|
||||
572 npm ci
|
||||
573 npm run build
|
||||
574 ll
|
||||
575 pm2 list
|
||||
576 pm2 restart wondif_vue
|
||||
577 pm2 describe wondif_vue
|
||||
578 pm2 list
|
||||
579 cd /home/debian/.pm2/logs/
|
||||
580 ll
|
||||
581 more wondif-vue-error.log
|
||||
582 pm2 logs wondif_vue --lines 200
|
||||
583 sav
|
||||
584 ll
|
||||
585 sudo nano wondif_2025
|
||||
586 sudo nginx -t
|
||||
587 sudo systemctl reload nginx
|
||||
588 pm2 flush
|
||||
589 pm2 list
|
||||
590 pm2 logs wondif_vue --err --lines 80
|
||||
591 pm2 reset wondif_vue
|
||||
592 pm2 list
|
||||
593 pm2 describe wondif_vue
|
||||
594 pm2 logs wondif_vue
|
||||
595 www
|
||||
596 ll
|
||||
597 cd wondif_vue
|
||||
598 ll
|
||||
599 git status
|
||||
600 git pull origni main
|
||||
601 git pull origin main
|
||||
602 npm ci
|
||||
603 npm run build
|
||||
604 pm2 list
|
||||
605 pm2 restart wondif_vue
|
||||
606 git pull origin main
|
||||
607 npm ci
|
||||
608 git pull origin main
|
||||
609 npm run build
|
||||
610 pm2 restart wondif_vue
|
||||
611 npx strapi version
|
||||
612 ll
|
||||
613 cd ..
|
||||
614 ll
|
||||
615 cd strapi_wondif/
|
||||
616 ll
|
||||
617 more README.md
|
||||
618 npx strapi version
|
||||
619 pm2 list
|
||||
620 pm2 stop strapi_wondif
|
||||
621 npx @strapi/upgrade latest
|
||||
622 npx strapi version
|
||||
623 NODE_ENV=production npm run build
|
||||
624 pm2 list
|
||||
625 pm2 start strapi_wondif
|
||||
626 pm2 list
|
||||
627 mysql --version
|
||||
628 mysql -u root -p
|
||||
629 pm2 list
|
||||
630 www
|
||||
631 ll
|
||||
632 cd strapi_wondif/
|
||||
633 ll
|
||||
634 NODE_ENV=production npm run build
|
||||
635 pm2 list
|
||||
636 pm2 restart strapi_wondif
|
||||
637 pm2 list
|
||||
638 cd strapi_wondif/
|
||||
639 www
|
||||
640 cd strapi_wondif/
|
||||
641 NODE_ENV=production npm run build
|
||||
642 pm2 list
|
||||
643 pm2 restart strapi_wondif
|
||||
644 pm2 list
|
||||
645 date
|
||||
646 mysql -u root -p
|
||||
647 SHOW VARIABLES LIKE '%time_zone%';
|
||||
648 SHOW VARIABLES LIKE '%time_zone%'; SELECT @@global.time_zone, @@session.time_zone;
|
||||
649 SELECT NOW() AS now_local, UTC_TIMESTAMP() AS now_utc, TIMEDIFF(NOW(), UTC_TIMESTAMP()) AS diff;
|
||||
650 show databeses
|
||||
651 show databeses;
|
||||
652 history
|
||||
653 mysql -u root -p
|
||||
654 pm2 list
|
||||
655 pm2 describe strapi_wondif
|
||||
656 TZ=Europe/Paris pm2 restart strapi_wondif --update-env
|
||||
657 pm2 describe strapi_wondif
|
||||
658 ll
|
||||
659 cd src
|
||||
660 ll
|
||||
661 cd admin/
|
||||
662 ll
|
||||
663 mv app.example.js app.js
|
||||
664 ll
|
||||
665 sudo nano app.js
|
||||
666 more app.js
|
||||
667 cd ..
|
||||
668 ll
|
||||
669 NODE_ENV=production npm run build
|
||||
670 pm2 list
|
||||
671 pm2 restart strapi_wondif
|
||||
672 pm2 list
|
||||
673 ll
|
||||
674 more jsconfig.json
|
||||
675 ll
|
||||
676 cd config/
|
||||
677 ll
|
||||
678 cd ..
|
||||
679 ll
|
||||
680 cd build/
|
||||
681 ll
|
||||
682 cd ..
|
||||
683 ll
|
||||
684 sudo nano
|
||||
685 ll
|
||||
686 more ecosystem.config.js
|
||||
687 pm2 list
|
||||
688 pm2 strapi_wondif stop
|
||||
689 pm2 stop strapi_wondif
|
||||
690 pm2 start ecosystem.config.js
|
||||
691 pm2 list
|
||||
692 pm2 env 0 | grep -E '^TZ=|^NODE_ENV='
|
||||
693 ll
|
||||
694 rm ecosystem.config.js
|
||||
695 pm2 list
|
||||
696 pm2 stop strapi_wondif
|
||||
697 pm2 restart strapi_wondif
|
||||
698 pm2 stop strapi_wondif
|
||||
699 sudo nano
|
||||
700 ll
|
||||
701 pm2 list
|
||||
702 sudo nano eco
|
||||
703 sudo nano ecosystem.config.js
|
||||
704 pm2 start ecosystem.config.js
|
||||
705 pm2 describe strapi_wondif2
|
||||
706 pm2 stop strapi_wondif2
|
||||
707 pm2 delete strapi_wondif2
|
||||
708 rm ecosystem.config.js
|
||||
709 pm2 reload strapi_wondif
|
||||
710 pm2 list
|
||||
711 cd /etc/nginx
|
||||
712 ll
|
||||
713 cd sites-available
|
||||
714 ll
|
||||
715 more wondif_media
|
||||
716 ll
|
||||
717 sudo nano wondif_media
|
||||
718 nginx -t
|
||||
719 sudo nginx -t
|
||||
720 sudo nginx -s reload
|
||||
721 systemctl restart nginx
|
||||
722 cd www
|
||||
723 www
|
||||
724 ll
|
||||
725 cd wondif_vue/
|
||||
726 ll
|
||||
727 git pull origin main
|
||||
728 npm ci
|
||||
729 ll
|
||||
730 cd app
|
||||
731 ll
|
||||
732 cd pages/
|
||||
733 ll
|
||||
734 cd concerts/
|
||||
735 ll
|
||||
736 www
|
||||
737 cd wondif_vue/
|
||||
738 ll
|
||||
739 npm run build
|
||||
740 pm2 list
|
||||
741 pm2 restart wondif_vue
|
||||
742 pm2 logs wondif_vue --lines 100
|
||||
743 ll
|
||||
744 sudo nano .env
|
||||
745 pm2 logs strapi_wondif --lines 100
|
||||
746 pm2 show wondif_vue
|
||||
747 pm2 env 1
|
||||
748 sudo nano .env
|
||||
749 ll
|
||||
750 sudo nano .env
|
||||
751 pm2 show wondif_vue
|
||||
752 ll
|
||||
753 more package.json
|
||||
754 pm2 restart wondif_vue
|
||||
755 pm2 show wondif_vue
|
||||
756 pm2 env 1
|
||||
757 ll
|
||||
758 sudo nano v
|
||||
759 sudo nano nuxt.config.js
|
||||
760 npm run build
|
||||
761 pm2 restart wondif_vue
|
||||
762 pm2 env 1
|
||||
763 git pull origin main
|
||||
764 git reset --hard HEAD
|
||||
765 git pull origin main
|
||||
766 pm2 list
|
||||
767 pm2 env 1
|
||||
768 pm2 env 2
|
||||
769 pm2 env 1
|
||||
770 www
|
||||
771 ll
|
||||
772 cd wondif_vue/
|
||||
773 ll
|
||||
774 more package.json
|
||||
775 ll
|
||||
776 more nuxt.config.js
|
||||
777 ll
|
||||
778 git pull origin main
|
||||
779 npm run build
|
||||
780 pm2 restart wondif_vue
|
||||
781 more nuxt.config.js
|
||||
782 more package.json
|
||||
783 more .env
|
||||
784 git pull origin main
|
||||
785 ll
|
||||
786 npm run build
|
||||
787 pm2 list
|
||||
788 pm2 delete wondif_vue
|
||||
789 pm2 start ecosystem.config.cjs
|
||||
790 pm2 save
|
||||
791 pm2 env wondif_vue
|
||||
792 pm2 env 1
|
||||
793 pm2 reload wondif_vue --update-env
|
||||
794 pm2 list
|
||||
795 pm2 env 1
|
||||
796 pm2 env 3
|
||||
797 more .env
|
||||
798 git pull origin main
|
||||
799 npm run build
|
||||
800 pm2 reload wondif_vue --update-env
|
||||
801 www
|
||||
802 ll
|
||||
803 cd wondif_vue/
|
||||
804 ll
|
||||
805 more .env
|
||||
806 ll
|
||||
807 rm .env
|
||||
808 ll
|
||||
809 npm run build
|
||||
810 pm2 reload wondif_vue --update-env
|
||||
811 history
|
||||
812 www
|
||||
813 ll
|
||||
814 cd strapi_wondif/
|
||||
815 ll
|
||||
816 NODE_ENV=production npm run build
|
||||
817 pm2 list
|
||||
818 pm2 restart strapi_wondif
|
||||
819 pm2 list
|
||||
820 history
|
||||
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@
|
||||
professionnels: "Professionnels",
|
||||
inscriptions: "Inscriptions",
|
||||
projet_lycee: "Inscription projet lycée",
|
||||
projet_academie: "Inscription Académie d'Orchestre",
|
||||
actus: "Actualités"
|
||||
}
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
<slot />
|
||||
<div class="decalage_button_wp">
|
||||
<div v-if="resolvedLienCta" class="decalage_button">
|
||||
<DsButton :to="resolvedLienCta" variant="white" textColor="default" borderColor="none">Candidater à nos projets</DsButton>
|
||||
<DsButton :to="resolvedLienCta" variant="white" textColor="default" borderColor="none">{{ textCta }}</DsButton>
|
||||
</div>
|
||||
<div v-if="ensavoirplusTarget" class="decalage_button">
|
||||
<DsButton :textColor="buttonTone" :borderColor="buttonTone" @click="toggleTarget(ensavoirplusTarget, ensavoirplusGroup)">En savoir +</DsButton>
|
||||
@@ -40,6 +40,7 @@
|
||||
position: { type: String, default: 'left' },
|
||||
cta: { type: String, default: '' },
|
||||
lienCta: { type: String, default: '' },
|
||||
textCta: { type: String, default: 'Candidater à nos projets' },
|
||||
ensavoirplusTarget: { type: String, default: '' },
|
||||
ensavoirplusGroup: { type: String, default: '' },
|
||||
})
|
||||
|
||||
896
app/pages/inscriptions/projet_academie.vue
Normal file
896
app/pages/inscriptions/projet_academie.vue
Normal file
@@ -0,0 +1,896 @@
|
||||
<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 À L'ACADÉMIE D'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 votre candidature à l'Académie d'orchestre de l'Orchestre national d'Île-de-France.
|
||||
</DsText>
|
||||
</SectionContent>
|
||||
</PageSection>
|
||||
|
||||
<!-- ================== -->
|
||||
<!-- FORMULAIRE -->
|
||||
<!-- ================== -->
|
||||
<div class="programmer-orchestre-page text-on-surface">
|
||||
<form class="space-y-6" @submit.prevent="submitQuoteRequest">
|
||||
<div class="px-4 md:px-8 lg:px-16 py-8 max-w-7xl mx-auto">
|
||||
<FormSection title="Identité">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FieldInput
|
||||
id="academie-last-name"
|
||||
v-model.trim="quoteForm.lastName"
|
||||
label="Nom"
|
||||
required
|
||||
:error="quoteFormErrors.lastName"
|
||||
@input="quoteForm.lastName = quoteForm.lastName.toUpperCase()"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-first-name"
|
||||
v-model.trim="quoteForm.firstName"
|
||||
label="Prénom"
|
||||
required
|
||||
:error="quoteFormErrors.firstName"
|
||||
/>
|
||||
<FieldSelect
|
||||
id="academie-gender"
|
||||
v-model="quoteForm.gender"
|
||||
label="Genre"
|
||||
placeholder="Choisir un genre"
|
||||
required
|
||||
:options="genderOptions"
|
||||
:error="quoteFormErrors.gender"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-birth-date"
|
||||
v-model="quoteForm.birthDate"
|
||||
label="Date de naissance"
|
||||
type="date"
|
||||
required
|
||||
:error="quoteFormErrors.birthDate"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-birth-place"
|
||||
v-model.trim="quoteForm.birthPlace"
|
||||
label="Lieu de naissance"
|
||||
required
|
||||
:error="quoteFormErrors.birthPlace"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-nationality"
|
||||
v-model.trim="quoteForm.nationality"
|
||||
label="Nationalité"
|
||||
required
|
||||
:error="quoteFormErrors.nationality"
|
||||
/>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="Coordonnées">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FieldInput
|
||||
id="academie-email"
|
||||
v-model.trim="quoteForm.email"
|
||||
label="Adresse mail"
|
||||
type="email"
|
||||
required
|
||||
:error="quoteFormErrors.email"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-phone"
|
||||
v-model.trim="quoteForm.phone"
|
||||
label="Téléphone"
|
||||
type="tel"
|
||||
required
|
||||
:error="quoteFormErrors.phone"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-address"
|
||||
v-model.trim="quoteForm.address"
|
||||
label="Adresse postale"
|
||||
required
|
||||
:error="quoteFormErrors.address"
|
||||
class="md:col-span-2"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-city"
|
||||
v-model.trim="quoteForm.city"
|
||||
label="Ville"
|
||||
required
|
||||
:error="quoteFormErrors.city"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-postal-code"
|
||||
v-model.trim="quoteForm.postalCode"
|
||||
label="Code postal"
|
||||
required
|
||||
:error="quoteFormErrors.postalCode"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-country"
|
||||
v-model.trim="quoteForm.country"
|
||||
label="Pays de résidence"
|
||||
required
|
||||
:error="quoteFormErrors.country"
|
||||
/>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="Parcours musical">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FieldSelect
|
||||
id="academie-instrument"
|
||||
v-model="quoteForm.instrument"
|
||||
label="Instrument"
|
||||
placeholder="Choisir un instrument"
|
||||
required
|
||||
:options="instrumentOptions"
|
||||
:error="quoteFormErrors.instrument"
|
||||
class="md:col-span-2"
|
||||
>
|
||||
<template #help>
|
||||
Pour les pupitres à instruments doubles (ex : flûte/piccolo, basson/contrebasson, etc.), merci de sélectionner l'option correspondant aux deux instruments si vous souhaitez les présenter tous les deux. Si vous ne cochez qu’un seul instrument, vous ne pourrez en présenter qu’un seul le jour de l’audition.
|
||||
</template>
|
||||
</FieldSelect>
|
||||
<FieldSelect
|
||||
id="academie-previous-participation"
|
||||
v-model="quoteForm.previousAcademyParticipation"
|
||||
label="Avez-vous déjà participé à l’académie ?"
|
||||
placeholder="Choisir une réponse"
|
||||
required
|
||||
:options="yesNoOptions"
|
||||
:error="quoteFormErrors.previousAcademyParticipation"
|
||||
/>
|
||||
<FieldInput
|
||||
v-if="quoteForm.previousAcademyParticipation === 'oui'"
|
||||
id="academie-previous-participation-count"
|
||||
v-model.trim="quoteForm.previousAcademyParticipationCount"
|
||||
label="Combien de fois ?"
|
||||
type="number"
|
||||
min="1"
|
||||
required
|
||||
:error="quoteFormErrors.previousAcademyParticipationCount"
|
||||
/>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="Formation">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FieldSelect
|
||||
id="academie-training-level"
|
||||
v-model="quoteForm.trainingLevel"
|
||||
label="Niveau - cycle actuel année scolaire 2026-2027"
|
||||
placeholder="Choisir un niveau"
|
||||
required
|
||||
:options="trainingLevelOptions"
|
||||
:error="quoteFormErrors.trainingLevel"
|
||||
class="md:col-span-2"
|
||||
>
|
||||
<template #help>
|
||||
Pour rappel, vous devez être inscrit.e en 3e cycle, cycle spécialisé ou équivalent à la rentrée 2025-2026 dans un conservatoire d’Île-de-France (hors CNSMD et Pôles d’enseignement supérieur).
|
||||
</template>
|
||||
</FieldSelect>
|
||||
<FieldInput
|
||||
v-if="quoteForm.trainingLevel === 'Autre'"
|
||||
id="academie-other-training-level"
|
||||
v-model.trim="quoteForm.otherTrainingLevel"
|
||||
label="Autre formation"
|
||||
required
|
||||
:error="quoteFormErrors.otherTrainingLevel"
|
||||
class="md:col-span-2"
|
||||
/>
|
||||
<FieldSelect
|
||||
id="academie-conservatory"
|
||||
v-model="quoteForm.conservatory"
|
||||
label="Conservatoire année scolaire 2026-2027"
|
||||
placeholder="Choisir un conservatoire"
|
||||
required
|
||||
:options="conservatoryOptions"
|
||||
:error="quoteFormErrors.conservatory"
|
||||
class="md:col-span-2"
|
||||
>
|
||||
<template #help>
|
||||
Si votre conservatoire ne figure pas dans la liste mais qu’il est bien situé en Île-de-France, merci de l’ajouter manuellement en respectant le même format que les propositions déjà indiquées.
|
||||
</template>
|
||||
</FieldSelect>
|
||||
<FieldInput
|
||||
v-if="quoteForm.conservatory === 'Autre'"
|
||||
id="academie-other-conservatory"
|
||||
v-model.trim="quoteForm.otherConservatory"
|
||||
label="Autre conservatoire (Ville + Nom)"
|
||||
required
|
||||
:error="quoteFormErrors.otherConservatory"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-teacher-name"
|
||||
v-model.trim="quoteForm.teacherName"
|
||||
label="Nom du professeur d’instrument (NOM prénom)"
|
||||
required
|
||||
:error="quoteFormErrors.teacherName"
|
||||
/>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="3 derniers diplômes musicaux obtenus">
|
||||
<div class="space-y-8">
|
||||
<div
|
||||
v-for="(diploma, index) in quoteForm.diplomas"
|
||||
:key="`diploma-${index}`"
|
||||
class="rounded-xl border border-outline-variant/30 p-5"
|
||||
>
|
||||
<h4 class="mb-4 text-lg font-bold uppercase">Diplôme {{ index + 1 }}</h4>
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FieldSelect
|
||||
:id="`academie-diploma-${index}-type`"
|
||||
v-model="diploma.type"
|
||||
label="Type du diplôme"
|
||||
placeholder="Choisir un type"
|
||||
:options="diplomaTypeOptions"
|
||||
:error="quoteFormErrors.diplomas[index].type"
|
||||
/>
|
||||
<FieldInput
|
||||
v-if="diploma.type === 'Autre'"
|
||||
:id="`academie-diploma-${index}-other-type`"
|
||||
v-model.trim="diploma.otherType"
|
||||
label="Autre type de diplôme"
|
||||
:error="quoteFormErrors.diplomas[index].otherType"
|
||||
/>
|
||||
<FieldInput
|
||||
:id="`academie-diploma-${index}-discipline`"
|
||||
v-model.trim="diploma.discipline"
|
||||
label="Discipline"
|
||||
:error="quoteFormErrors.diplomas[index].discipline"
|
||||
/>
|
||||
<FieldInput
|
||||
:id="`academie-diploma-${index}-year`"
|
||||
v-model.trim="diploma.year"
|
||||
label="Année"
|
||||
type="number"
|
||||
min="1900"
|
||||
max="2027"
|
||||
:error="quoteFormErrors.diplomas[index].year"
|
||||
/>
|
||||
<FieldInput
|
||||
:id="`academie-diploma-${index}-establishment`"
|
||||
v-model.trim="diploma.establishment"
|
||||
label="Établissement"
|
||||
:error="quoteFormErrors.diplomas[index].establishment"
|
||||
class="md:col-span-2"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="Contact d’urgence">
|
||||
<div class="grid grid-cols-1 md:grid-cols-2 gap-6">
|
||||
<FieldInput
|
||||
id="academie-emergency-last-name"
|
||||
v-model.trim="quoteForm.emergencyLastName"
|
||||
label="Nom du contact"
|
||||
required
|
||||
:error="quoteFormErrors.emergencyLastName"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-emergency-first-name"
|
||||
v-model.trim="quoteForm.emergencyFirstName"
|
||||
label="Prénom du contact"
|
||||
required
|
||||
:error="quoteFormErrors.emergencyFirstName"
|
||||
/>
|
||||
<FieldSelect
|
||||
id="academie-emergency-relation"
|
||||
v-model="quoteForm.emergencyRelation"
|
||||
label="Lien avec le participant"
|
||||
placeholder="Choisir un lien"
|
||||
required
|
||||
:options="emergencyRelationOptions"
|
||||
:error="quoteFormErrors.emergencyRelation"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-emergency-phone"
|
||||
v-model.trim="quoteForm.emergencyPhone"
|
||||
label="Téléphone"
|
||||
type="tel"
|
||||
required
|
||||
:error="quoteFormErrors.emergencyPhone"
|
||||
/>
|
||||
<FieldInput
|
||||
id="academie-emergency-email"
|
||||
v-model.trim="quoteForm.emergencyEmail"
|
||||
label="Mail"
|
||||
type="email"
|
||||
required
|
||||
:error="quoteFormErrors.emergencyEmail"
|
||||
/>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<FormSection title="Règlement">
|
||||
<div class="space-y-4">
|
||||
<a
|
||||
:href="regulationFileUrl"
|
||||
target="_blank"
|
||||
download
|
||||
class="inline-flex items-center justify-center rounded-full border border-primary px-6 py-3 text-sm font-bold text-primary transition-colors hover:bg-primary hover:text-on-primary"
|
||||
>
|
||||
Télécharger le règlement
|
||||
</a>
|
||||
<label class="flex items-start gap-3 text-sm font-medium text-on-surface">
|
||||
<input
|
||||
v-model="quoteForm.acceptRules"
|
||||
type="checkbox"
|
||||
class="mt-1 rounded border-outline-variant text-primary focus:ring-primary"
|
||||
>
|
||||
<span>Accepter le règlement *</span>
|
||||
</label>
|
||||
<p v-if="quoteFormErrors.acceptRules" class="text-xs text-error">{{ quoteFormErrors.acceptRules }}</p>
|
||||
</div>
|
||||
</FormSection>
|
||||
|
||||
<!-- 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 candidature a bien été envoyée. Un email de confirmation vous a été adressé.</span>
|
||||
<span v-else>Votre candidature a bien été enregistrée et transmise à l’équipe de l’Académie d’orchestre.</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 candidature 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 candidature' }}
|
||||
</button>
|
||||
<p class="text-xs text-on-surface-variant">
|
||||
En soumettant ce formulaire, j’accepte que les informations saisies soient utilisées dans le cadre de cette candidature.
|
||||
</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'
|
||||
|
||||
const inputClass = "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"
|
||||
|
||||
const FormSection = defineComponent({
|
||||
props: {
|
||||
title: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
},
|
||||
setup(props, { slots }) {
|
||||
return () => h("section", { class: "mb-10" }, [
|
||||
h("div", { class: "flex items-center gap-4 mb-8" }, [
|
||||
h("h3", { class: "text-2xl font-bold tracking-tight uppercase" }, props.title),
|
||||
h("div", { class: "h-[2px] flex-1 bg-surface-container" }),
|
||||
]),
|
||||
slots.default?.(),
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
const FieldInput = defineComponent({
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: [String, Number],
|
||||
default: "",
|
||||
},
|
||||
type: {
|
||||
type: String,
|
||||
default: "text",
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
min: {
|
||||
type: [String, Number],
|
||||
default: undefined,
|
||||
},
|
||||
max: {
|
||||
type: [String, Number],
|
||||
default: undefined,
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue", "input"],
|
||||
setup(props, { emit, slots }) {
|
||||
return () => h("div", { class: "space-y-2" }, [
|
||||
h("label", { for: props.id, class: "block text-sm font-medium text-on-surface pl-[2px]" }, `${props.label}${props.required ? " *" : ""}`),
|
||||
h("input", {
|
||||
id: props.id,
|
||||
value: props.modelValue,
|
||||
type: props.type,
|
||||
min: props.min,
|
||||
max: props.max,
|
||||
class: inputClass,
|
||||
onInput: (event) => {
|
||||
emit("update:modelValue", event.target.value)
|
||||
emit("input", event)
|
||||
},
|
||||
}),
|
||||
slots.help?.(),
|
||||
props.error ? h("p", { class: "text-xs text-error" }, props.error) : null,
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
const FieldSelect = defineComponent({
|
||||
props: {
|
||||
id: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
label: {
|
||||
type: String,
|
||||
required: true,
|
||||
},
|
||||
modelValue: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
placeholder: {
|
||||
type: String,
|
||||
default: "Choisir une option",
|
||||
},
|
||||
required: {
|
||||
type: Boolean,
|
||||
default: false,
|
||||
},
|
||||
options: {
|
||||
type: Array,
|
||||
required: true,
|
||||
},
|
||||
error: {
|
||||
type: String,
|
||||
default: "",
|
||||
},
|
||||
},
|
||||
emits: ["update:modelValue"],
|
||||
setup(props, { emit, slots }) {
|
||||
return () => h("div", { class: "space-y-2" }, [
|
||||
h("label", { for: props.id, class: "block text-sm font-medium text-on-surface pl-[2px]" }, `${props.label}${props.required ? " *" : ""}`),
|
||||
h("select", {
|
||||
id: props.id,
|
||||
value: props.modelValue,
|
||||
class: inputClass,
|
||||
onChange: (event) => emit("update:modelValue", event.target.value),
|
||||
}, [
|
||||
h("option", { value: "" }, props.placeholder),
|
||||
...props.options.map((option) => h("option", { value: option }, option)),
|
||||
]),
|
||||
slots.help ? h("p", { class: "text-xs leading-relaxed text-on-surface-variant" }, slots.help()) : null,
|
||||
props.error ? h("p", { class: "text-xs text-error" }, props.error) : null,
|
||||
])
|
||||
},
|
||||
})
|
||||
|
||||
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',
|
||||
},
|
||||
],
|
||||
})
|
||||
|
||||
const genderOptions = ["Masculin", "Féminin", "Autre"]
|
||||
const yesNoOptions = ["oui", "non"]
|
||||
const instrumentOptions = [
|
||||
"Violon",
|
||||
"Alto",
|
||||
"Violoncelle",
|
||||
"Contrebasse",
|
||||
"Flûte",
|
||||
"Piccolo",
|
||||
"Flûte + piccolo",
|
||||
"Hautbois",
|
||||
"Cor anglais jouant le 2e hautbois",
|
||||
"Clarinette",
|
||||
"Basson",
|
||||
"Contrebasson",
|
||||
"Basson + Contrebasson",
|
||||
"Cor",
|
||||
"Trompette",
|
||||
"Trombone 2",
|
||||
"Trombone basse ou trombone 3",
|
||||
"Tuba",
|
||||
"Harpe",
|
||||
"Percussions",
|
||||
"Timbales",
|
||||
"Percussions + timbales",
|
||||
]
|
||||
const trainingLevelOptions = [
|
||||
"Cycle 3 - 1ère année",
|
||||
"Cycle 3 - 2ème année",
|
||||
"Cycle 3 - 3ème année",
|
||||
"Cycle spécialisé (CPES)",
|
||||
"DEM",
|
||||
"Autre",
|
||||
]
|
||||
const conservatoryOptions = [
|
||||
"75 - Paris - Conservatoire à rayonnement régional de Paris - Ida Rubinstein",
|
||||
"75 - Paris 5 - Conservatoire municipal Gabriel Fauré",
|
||||
"75 - Paris 11 - Conservatoire municipal Charles Münch",
|
||||
"75 - Paris 12 - Conservatoire municipal Paul Dukas",
|
||||
"75 - Paris 13 - Conservatoire municipal Maurice Ravel",
|
||||
"75 - Paris 17 - Conservatoire municipal Claude Debussy",
|
||||
"78 - Versailles - Conservatoire à rayonnement régional de Versailles Grand Parc",
|
||||
"92 - Rueil-Malmaison - Conservatoire à rayonnement régional",
|
||||
"92 - Boulogne-Billancourt - Conservatoire à rayonnement régional",
|
||||
"92 - Clamart - Conservatoire à rayonnement départemental Henri Dutilleux",
|
||||
"92 - Gennevilliers - Conservatoire Edgar Varèse",
|
||||
"92 - Meudon - Conservatoire Marcel Dupré",
|
||||
"93 - Aubervilliers - Conservatoire à rayonnement régional",
|
||||
"93 - Aulnay-sous-Bois - Conservatoire à rayonnement départemental",
|
||||
"93 - Drancy - Conservatoire",
|
||||
"93 - Bobigny - Conservatoire Jean Wiener",
|
||||
"94 - Créteil - Conservatoire à rayonnement régional Marcel Dadi",
|
||||
"94 - Saint-Maur-des-Fossés - Conservatoire à rayonnement régional",
|
||||
"94 - Cachan - Conservatoire à rayonnement départemental du Val-de-Bièvre",
|
||||
"94 - Villeneuve-Saint-Georges - Conservatoire",
|
||||
"95 - Cergy-Pontoise - Conservatoire à rayonnement régional",
|
||||
"Autre",
|
||||
]
|
||||
const diplomaTypeOptions = ["fin de cycle 2", "DEM", "CEM", "Autre"]
|
||||
const emergencyRelationOptions = ["Parent", "Frère - sœur", "Conjoint.e", "Ami", "Autre"]
|
||||
//const regulationFileUrl = "/contenus/reglement-academie-orchestre.pdf"
|
||||
const regulationFileUrl = "https://media.orchestre-ile.com/uploads/25_26_ONDIF_reglement_academie_57934f522a.pdf"
|
||||
|
||||
function createDiploma() {
|
||||
return {
|
||||
type: "",
|
||||
otherType: "",
|
||||
discipline: "",
|
||||
year: "",
|
||||
establishment: "",
|
||||
}
|
||||
}
|
||||
|
||||
function createDiplomaErrors() {
|
||||
return {
|
||||
type: "",
|
||||
otherType: "",
|
||||
discipline: "",
|
||||
year: "",
|
||||
establishment: "",
|
||||
}
|
||||
}
|
||||
|
||||
const quoteForm = reactive({
|
||||
lastName: "",
|
||||
firstName: "",
|
||||
gender: "",
|
||||
birthDate: "",
|
||||
birthPlace: "",
|
||||
nationality: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
city: "",
|
||||
postalCode: "",
|
||||
country: "",
|
||||
instrument: "",
|
||||
previousAcademyParticipation: "",
|
||||
previousAcademyParticipationCount: "",
|
||||
trainingLevel: "",
|
||||
otherTrainingLevel: "",
|
||||
conservatory: "",
|
||||
otherConservatory: "",
|
||||
teacherName: "",
|
||||
diplomas: [createDiploma(), createDiploma(), createDiploma()],
|
||||
emergencyLastName: "",
|
||||
emergencyFirstName: "",
|
||||
emergencyRelation: "",
|
||||
emergencyPhone: "",
|
||||
emergencyEmail: "",
|
||||
acceptRules: false,
|
||||
})
|
||||
|
||||
const quoteFormErrors = reactive({
|
||||
lastName: "",
|
||||
firstName: "",
|
||||
gender: "",
|
||||
birthDate: "",
|
||||
birthPlace: "",
|
||||
nationality: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
city: "",
|
||||
postalCode: "",
|
||||
country: "",
|
||||
instrument: "",
|
||||
previousAcademyParticipation: "",
|
||||
previousAcademyParticipationCount: "",
|
||||
trainingLevel: "",
|
||||
otherTrainingLevel: "",
|
||||
conservatory: "",
|
||||
otherConservatory: "",
|
||||
teacherName: "",
|
||||
diplomas: [createDiplomaErrors(), createDiplomaErrors(), createDiplomaErrors()],
|
||||
emergencyLastName: "",
|
||||
emergencyFirstName: "",
|
||||
emergencyRelation: "",
|
||||
emergencyPhone: "",
|
||||
emergencyEmail: "",
|
||||
acceptRules: "",
|
||||
})
|
||||
|
||||
const quoteSubmitting = ref(false)
|
||||
const quoteSubmitSuccess = ref(false)
|
||||
const quoteSubmitError = ref("")
|
||||
const quoteEmailsSent = ref(false)
|
||||
|
||||
const requiredFieldLabels = {
|
||||
lastName: "Le nom est obligatoire.",
|
||||
firstName: "Le prénom est obligatoire.",
|
||||
gender: "Le genre est obligatoire.",
|
||||
birthDate: "La date de naissance est obligatoire.",
|
||||
birthPlace: "Le lieu de naissance est obligatoire.",
|
||||
nationality: "La nationalité est obligatoire.",
|
||||
email: "L’adresse mail est obligatoire.",
|
||||
phone: "Le téléphone est obligatoire.",
|
||||
address: "L’adresse est obligatoire.",
|
||||
city: "La ville est obligatoire.",
|
||||
postalCode: "Le code postal est obligatoire.",
|
||||
country: "Le pays de résidence est obligatoire.",
|
||||
instrument: "L’instrument est obligatoire.",
|
||||
previousAcademyParticipation: "La participation précédente est obligatoire.",
|
||||
trainingLevel: "Le niveau de formation est obligatoire.",
|
||||
conservatory: "Le conservatoire est obligatoire.",
|
||||
teacherName: "Le nom du professeur d’instrument est obligatoire.",
|
||||
emergencyLastName: "Le nom du contact d’urgence est obligatoire.",
|
||||
emergencyFirstName: "Le prénom du contact d’urgence est obligatoire.",
|
||||
emergencyRelation: "Le lien avec le participant est obligatoire.",
|
||||
emergencyPhone: "Le téléphone du contact d’urgence est obligatoire.",
|
||||
emergencyEmail: "Le mail du contact d’urgence est obligatoire.",
|
||||
}
|
||||
|
||||
function resetQuoteFormErrors() {
|
||||
Object.keys(quoteFormErrors).forEach((key) => {
|
||||
if (key === "diplomas") {
|
||||
quoteFormErrors.diplomas = [createDiplomaErrors(), createDiplomaErrors(), createDiplomaErrors()]
|
||||
} else {
|
||||
quoteFormErrors[key] = ""
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
function isValidEmail(email) {
|
||||
return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email)
|
||||
}
|
||||
|
||||
function validateQuoteForm() {
|
||||
resetQuoteFormErrors()
|
||||
|
||||
let isValid = true
|
||||
|
||||
Object.entries(requiredFieldLabels).forEach(([field, message]) => {
|
||||
if (!quoteForm[field]) {
|
||||
quoteFormErrors[field] = message
|
||||
isValid = false
|
||||
}
|
||||
})
|
||||
|
||||
if (quoteForm.email && !isValidEmail(quoteForm.email)) {
|
||||
quoteFormErrors.email = "L’adresse mail n’est pas valide."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (quoteForm.emergencyEmail && !isValidEmail(quoteForm.emergencyEmail)) {
|
||||
quoteFormErrors.emergencyEmail = "Le mail du contact d’urgence n’est pas valide."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (quoteForm.previousAcademyParticipation === "oui") {
|
||||
const count = Number(quoteForm.previousAcademyParticipationCount)
|
||||
if (!quoteForm.previousAcademyParticipationCount) {
|
||||
quoteFormErrors.previousAcademyParticipationCount = "Le nombre de participations est obligatoire."
|
||||
isValid = false
|
||||
} else if (!Number.isInteger(count) || count <= 0) {
|
||||
quoteFormErrors.previousAcademyParticipationCount = "Le nombre de participations doit être supérieur à 0."
|
||||
isValid = false
|
||||
}
|
||||
}
|
||||
|
||||
if (quoteForm.trainingLevel === "Autre" && !quoteForm.otherTrainingLevel) {
|
||||
quoteFormErrors.otherTrainingLevel = "L’autre formation est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (quoteForm.conservatory === "Autre" && !quoteForm.otherConservatory) {
|
||||
quoteFormErrors.otherConservatory = "L’autre conservatoire est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
quoteForm.diplomas.forEach((diploma, index) => {
|
||||
const hasDiplomaData = Boolean(diploma.type || diploma.otherType || diploma.discipline || diploma.year || diploma.establishment)
|
||||
|
||||
if (!hasDiplomaData) {
|
||||
return
|
||||
}
|
||||
|
||||
if (!diploma.type) {
|
||||
quoteFormErrors.diplomas[index].type = "Le type du diplôme est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (diploma.type === "Autre" && !diploma.otherType) {
|
||||
quoteFormErrors.diplomas[index].otherType = "L’autre type de diplôme est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (!diploma.discipline) {
|
||||
quoteFormErrors.diplomas[index].discipline = "La discipline est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (!diploma.year) {
|
||||
quoteFormErrors.diplomas[index].year = "L’année est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
if (!diploma.establishment) {
|
||||
quoteFormErrors.diplomas[index].establishment = "L’établissement est obligatoire."
|
||||
isValid = false
|
||||
}
|
||||
})
|
||||
|
||||
if (!quoteForm.acceptRules) {
|
||||
quoteFormErrors.acceptRules = "Vous devez accepter le règlement."
|
||||
isValid = false
|
||||
}
|
||||
|
||||
return isValid
|
||||
}
|
||||
|
||||
function resetQuoteForm() {
|
||||
Object.assign(quoteForm, {
|
||||
lastName: "",
|
||||
firstName: "",
|
||||
gender: "",
|
||||
birthDate: "",
|
||||
birthPlace: "",
|
||||
nationality: "",
|
||||
email: "",
|
||||
phone: "",
|
||||
address: "",
|
||||
city: "",
|
||||
postalCode: "",
|
||||
country: "",
|
||||
instrument: "",
|
||||
previousAcademyParticipation: "",
|
||||
previousAcademyParticipationCount: "",
|
||||
trainingLevel: "",
|
||||
otherTrainingLevel: "",
|
||||
conservatory: "",
|
||||
otherConservatory: "",
|
||||
teacherName: "",
|
||||
diplomas: [createDiploma(), createDiploma(), createDiploma()],
|
||||
emergencyLastName: "",
|
||||
emergencyFirstName: "",
|
||||
emergencyRelation: "",
|
||||
emergencyPhone: "",
|
||||
emergencyEmail: "",
|
||||
acceptRules: false,
|
||||
})
|
||||
}
|
||||
|
||||
async function submitQuoteRequest() {
|
||||
quoteSubmitSuccess.value = false
|
||||
quoteSubmitError.value = ""
|
||||
quoteEmailsSent.value = false
|
||||
|
||||
if (!validateQuoteForm()) {
|
||||
return
|
||||
}
|
||||
|
||||
quoteSubmitting.value = true
|
||||
|
||||
try {
|
||||
const response = await $fetch("/api/projet-academie", {
|
||||
method: "POST",
|
||||
body: {
|
||||
...quoteForm,
|
||||
diplomas: quoteForm.diplomas.map((diploma) => ({ ...diploma })),
|
||||
},
|
||||
})
|
||||
|
||||
quoteSubmitSuccess.value = true
|
||||
quoteEmailsSent.value = Boolean(response?.emailsSent)
|
||||
resetQuoteForm()
|
||||
resetQuoteFormErrors()
|
||||
} catch (error) {
|
||||
quoteSubmitError.value = error?.data?.statusMessage || "L’envoi de la candidature 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>
|
||||
@@ -33,7 +33,7 @@
|
||||
<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">
|
||||
<div class="px-4 md:px-8 lg:px-16 py-8 max-w-7xl mx-auto">
|
||||
<!-- SOUS-TITRE -->
|
||||
<section class="mb-8">
|
||||
<div class="flex items-center gap-4 mb-8">
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'entreprise-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -197,6 +199,8 @@
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'particulier-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -223,6 +225,8 @@
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -70,6 +70,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="`texte_cache_${index + 3}`"
|
||||
ensavoirplus-group="projets-details"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -198,6 +200,8 @@ et de la programmation jeune public"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -64,6 +64,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="`texte_cache_${index + 3}`"
|
||||
ensavoirplus-group="valeurs-details"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -170,6 +172,8 @@
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'academie-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -169,6 +171,8 @@
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
244
app/pages/mediation/academie_20260606.vue
Normal file
244
app/pages/mediation/academie_20260606.vue
Normal file
@@ -0,0 +1,244 @@
|
||||
<template>
|
||||
<div class="academie--page">
|
||||
<!-- ================== -->
|
||||
<!-- FILS D'ARIANE -->
|
||||
<!-- ================== -->
|
||||
<PageSection tone="" content-size="default" class="breadcrum_wp">
|
||||
<Breadcrumb/>
|
||||
</PageSection>
|
||||
|
||||
<!-- ================== -->
|
||||
<!-- EN-TêTE -->
|
||||
<!-- ================== -->
|
||||
<section class="fiche_header_simple_wp">
|
||||
<div class="fiche_header_wp_gauche"></div>
|
||||
|
||||
<div class="fiche_header_inner">
|
||||
<div class="fiche_header_titres">
|
||||
<div>
|
||||
<DsHeading as="h1" tone="default" textcase="uppercase" class="concert-card__title">
|
||||
{{academie_donnees_servies.header_titre}}
|
||||
</DsHeading>
|
||||
</div>
|
||||
|
||||
<DsText as="p" align="justify">
|
||||
{{academie_donnees_servies.header_text}}
|
||||
</DsText>
|
||||
</div>
|
||||
<div class="fiche_header_img">
|
||||
<DsMedia
|
||||
v-if="academie_donnees_servies?.header_illustration?.url"
|
||||
:src="academie_donnees_servies.header_illustration.url"
|
||||
:alt="academie_donnees_servies.header_illustration.alternativeText || ''"
|
||||
fit="cover"
|
||||
ratio="square"
|
||||
/>
|
||||
|
||||
<div v-else class="img_placeholder" aria-hidden="true" />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="fiche_header_wp_droite"></div>
|
||||
|
||||
</section>
|
||||
|
||||
<!-- ================== -->
|
||||
<!-- AFFICHAGE DES PARTIE EN DÉCALLAGE AVEC LE CONTENU PROVENANT DE STRAPI -->
|
||||
<!-- ================== -->
|
||||
<template v-for="(t, index) in tiroirs" :key="index">
|
||||
<PageSection :content="false" :class="{ 'decalage-section--spaced': index > 0 }">
|
||||
<Decalage
|
||||
:tone="t.decalage_couleur"
|
||||
title-tone="invert"
|
||||
:position="t.decalage_sens"
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'academie-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
</template>
|
||||
<DsText as="p" tone="invert" :align="t.decalage_sens">
|
||||
{{ t.decalage_texte }}
|
||||
</DsText>
|
||||
</Decalage>
|
||||
</PageSection>
|
||||
<PageSection
|
||||
:id="`texte_cache_${index + 3}`"
|
||||
:data-ensavoirplus-group="t.tiroir_ouvert ? undefined : 'academie-details'"
|
||||
tone=""
|
||||
content-size="default"
|
||||
padded_size=""
|
||||
:class="{ 'decalage_ensavoirplus--hidden': !t.tiroir_ouvert }"
|
||||
>
|
||||
<section v-if="t.tiroir_galerie.length" class="img-gallery_wp">
|
||||
<div class="img-gallery">
|
||||
<DsMedia
|
||||
v-for="img in t.tiroir_galerie"
|
||||
:key="img.id || img.url"
|
||||
:src="img.url"
|
||||
:alt="img.alternativeText"
|
||||
/>
|
||||
</div>
|
||||
</section>
|
||||
<SectionContent class="fiche_description">
|
||||
<StrapiBlocksConvert :blocks="t.tiroir_description" />
|
||||
</SectionContent>
|
||||
<section v-if="t.tiroir_videos.length" class="youtube_wp">
|
||||
<div class="youtube-list">
|
||||
<div v-for="v in t.tiroir_videos" :key="v.id" class="youtube-item">
|
||||
<iframe
|
||||
:src="v.lien_youtube"
|
||||
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>
|
||||
|
||||
|
||||
<!-- ================== -->
|
||||
<!-- CONTACT -->
|
||||
<!-- ================== -->
|
||||
|
||||
<PageSection tone="" content-size="default" padded_size="md" class="contact_spe_wp">
|
||||
<ContactSpecifique
|
||||
titre="Renseignements Académie d'Orchestre 2026"
|
||||
nom="Zoë Crampon"
|
||||
poste="Service action culturelle"
|
||||
numero="06 82 73 65 01"
|
||||
mail="zoe.crampon@orchestre-ile.com"
|
||||
adresse="Orchestre national d’Île-de-France, Service action culturelle, 19, rue des Écoles, 94140 Alfortville"
|
||||
/>
|
||||
</PageSection>
|
||||
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import DsHeading from '@root/design-system/primitives/DsHeading.vue'
|
||||
import DsText from '@root/design-system/primitives/DsText.vue'
|
||||
import DsMedia from '@root/design-system/primitives/DsMedia.vue'
|
||||
import { computed } from 'vue'
|
||||
|
||||
// ======================================
|
||||
// RÉCUPÉRATION DES DONNÉES DANS STRAPI
|
||||
// ======================================
|
||||
|
||||
const endpoint = "/api/__strapi__/academie"
|
||||
const populate = {
|
||||
header_illustration: true,
|
||||
tiroirs_academie: {
|
||||
decalage_parametres: true,
|
||||
tiroir_galerie: true,
|
||||
tiroir_videos: true,
|
||||
},
|
||||
}
|
||||
|
||||
const { items: academie, pending, error } = useStrapi(
|
||||
endpoint,
|
||||
{
|
||||
locale: "fr-FR",
|
||||
populate,
|
||||
limit: 1,
|
||||
}
|
||||
)
|
||||
|
||||
// ======================================
|
||||
// PRÉPARATION DES DONNÉES POUR AFFICHAGE DANS LA PAGE
|
||||
// ======================================
|
||||
const academie_donnees_servies = computed(() => academie.value?.[0] || {})
|
||||
|
||||
const tiroirs = computed(() =>
|
||||
(academie.value?.[0]?.tiroirs_academie || []).map((tiroir_item) => ({
|
||||
decalage_titre: tiroir_item.decalage_parametres?.decalage_titre,
|
||||
decalage_texte: tiroir_item.decalage_parametres?.decalage_texte,
|
||||
tiroir_ouvert: Boolean(tiroir_item.decalage_parametres?.tiroir_ouvert),
|
||||
decalage_sens:
|
||||
tiroir_item.decalage_parametres?.decalage_sens === "droite" ? "right" : "left",
|
||||
decalage_couleur:
|
||||
tiroir_item.decalage_parametres?.decalage_couleur === "rouge"
|
||||
? "brand"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "vert clair"
|
||||
? "brandreverse"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
url: tiroir_item_img.url,
|
||||
alt: tiroir_item_img.alternativeText,
|
||||
})),
|
||||
tiroir_videos: (tiroir_item.tiroir_videos || [])
|
||||
.map((tiroir_item_video) => {
|
||||
const id = getYoutubeId(tiroir_item_video?.lien_youtube)
|
||||
if (!id) return null
|
||||
|
||||
return {
|
||||
id: tiroir_item_video.id || id,
|
||||
lien_youtube: `https://www.youtube-nocookie.com/embed/${id}?rel=0&modestbranding=1&iv_load_policy=3&playsinline=1`,
|
||||
}
|
||||
})
|
||||
.filter(Boolean),
|
||||
}))
|
||||
)
|
||||
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
// =======================
|
||||
// SPÉCIFIQUE À CETTE PAGE
|
||||
// =======================
|
||||
.academie--page {
|
||||
.chiffres_wp {
|
||||
background-color: var(--c-background-vert);
|
||||
margin-bottom: 50px;
|
||||
}
|
||||
.fiche_description {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
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: 40px;
|
||||
}
|
||||
}
|
||||
|
||||
/* ============================ */
|
||||
/* GALERIES */
|
||||
/* ============================ */
|
||||
|
||||
|
||||
|
||||
</style>
|
||||
@@ -54,6 +54,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'chantons-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -170,6 +172,8 @@ et de la programmation jeune public"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'insertion-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -170,6 +172,8 @@ et de la programmation jeune public"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'enfance-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -170,6 +172,8 @@ et de la programmation jeune public"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -65,6 +65,7 @@
|
||||
:ensavoirplus-target="`texte_cache_${index + 3}`"
|
||||
ensavoirplus-group="scolaires-details"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -189,6 +190,7 @@ et de la programmation jeune public"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -54,6 +54,8 @@
|
||||
button-tone="invert"
|
||||
:ensavoirplus-target="t.tiroir_ouvert ? undefined : `texte_cache_${index + 3}`"
|
||||
:ensavoirplus-group="t.tiroir_ouvert ? undefined : 'hopital-details'"
|
||||
:lien-cta="t.lien_cta"
|
||||
:text-cta="t.text_cta"
|
||||
>
|
||||
<template #title>
|
||||
{{ t.decalage_titre }}
|
||||
@@ -170,6 +172,8 @@ et de la programmation jeune public"
|
||||
: tiroir_item.decalage_parametres?.decalage_couleur === "jaune"
|
||||
? "jaune"
|
||||
: "dark",
|
||||
lien_cta: tiroir_item.decalage_parametres?.lien_cta || "",
|
||||
text_cta: tiroir_item.decalage_parametres?.text_cta || undefined,
|
||||
tiroir_description: tiroir_item.tiroir_description,
|
||||
tiroir_galerie: (tiroir_item.tiroir_galerie || []).map((tiroir_item_img) => ({
|
||||
id: tiroir_item_img.id,
|
||||
|
||||
@@ -435,7 +435,7 @@
|
||||
rel="noopener noreferrer"
|
||||
>
|
||||
<div class="w-full aspect-[4/3] bg-surface-container-low rounded-lg flex items-center justify-center group-hover:bg-primary-container transition-colors">
|
||||
<span class="material-symbols-outlined text-3xl text-outline group-hover:text-primary">insert_drive_file</span>
|
||||
<span class="material-symbols-outlined text-3xl text-outline group-hover:text-primary">download</span>
|
||||
</div>
|
||||
<div class="text-center">
|
||||
<p v-if="document.caption" class="text-xs font-bold uppercase tracking-tighter">{{ document.caption }}</p>
|
||||
@@ -718,7 +718,6 @@
|
||||
}
|
||||
if (!programmerDebugData.value) return
|
||||
|
||||
console.log("pro-programmer Strapi content", programmerDebugData.value)
|
||||
})
|
||||
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ export default defineNuxtConfig({
|
||||
mysqlPassword: '',
|
||||
parcRequestRecipientEmail: '',
|
||||
scolaireRequestRecipientEmail: '',
|
||||
academieRequestRecipientEmail: '',
|
||||
smtpHost: '',
|
||||
smtpPort: '',
|
||||
smtpSecure: '',
|
||||
|
||||
287
server/api/projet-academie.post.js
Normal file
287
server/api/projet-academie.post.js
Normal file
@@ -0,0 +1,287 @@
|
||||
import { createError, readBody } from "h3"
|
||||
import { getMysqlPool } from "~~/server/utils/mysql"
|
||||
import { sendProjetAcademieEmails } from "~~/server/utils/mailer"
|
||||
|
||||
function normalizeValue(value) {
|
||||
return typeof value === "string" ? value.trim() : ""
|
||||
}
|
||||
|
||||
function normalizeBoolean(value) {
|
||||
return value === true
|
||||
}
|
||||
|
||||
function normalizePositiveInteger(value) {
|
||||
const number = Number(value)
|
||||
return Number.isInteger(number) && number > 0 ? number : null
|
||||
}
|
||||
|
||||
function normalizeDiploma(diploma) {
|
||||
return {
|
||||
type: normalizeValue(diploma?.type),
|
||||
otherType: normalizeValue(diploma?.otherType),
|
||||
discipline: normalizeValue(diploma?.discipline),
|
||||
year: normalizeValue(diploma?.year),
|
||||
establishment: normalizeValue(diploma?.establishment),
|
||||
}
|
||||
}
|
||||
|
||||
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)
|
||||
}),
|
||||
])
|
||||
}
|
||||
|
||||
function hasDiplomaData(diploma) {
|
||||
return Boolean(diploma.type || diploma.otherType || diploma.discipline || diploma.year || diploma.establishment)
|
||||
}
|
||||
|
||||
function validateDiploma(diploma) {
|
||||
if (!hasDiplomaData(diploma)) {
|
||||
return true
|
||||
}
|
||||
|
||||
if (!diploma.type || !diploma.discipline || !diploma.year || !diploma.establishment) {
|
||||
return false
|
||||
}
|
||||
|
||||
if (diploma.type === "Autre" && !diploma.otherType) {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
export default defineEventHandler(async (event) => {
|
||||
const body = await readBody(event)
|
||||
|
||||
const payload = {
|
||||
lastName: normalizeValue(body?.lastName).toUpperCase(),
|
||||
firstName: normalizeValue(body?.firstName),
|
||||
gender: normalizeValue(body?.gender),
|
||||
birthDate: normalizeValue(body?.birthDate),
|
||||
birthPlace: normalizeValue(body?.birthPlace),
|
||||
nationality: normalizeValue(body?.nationality),
|
||||
email: normalizeValue(body?.email),
|
||||
phone: normalizeValue(body?.phone),
|
||||
address: normalizeValue(body?.address),
|
||||
city: normalizeValue(body?.city),
|
||||
postalCode: normalizeValue(body?.postalCode),
|
||||
country: normalizeValue(body?.country),
|
||||
instrument: normalizeValue(body?.instrument),
|
||||
previousAcademyParticipation: normalizeValue(body?.previousAcademyParticipation),
|
||||
previousAcademyParticipationCount: normalizePositiveInteger(body?.previousAcademyParticipationCount),
|
||||
trainingLevel: normalizeValue(body?.trainingLevel),
|
||||
otherTrainingLevel: normalizeValue(body?.otherTrainingLevel),
|
||||
conservatory: normalizeValue(body?.conservatory),
|
||||
otherConservatory: normalizeValue(body?.otherConservatory),
|
||||
teacherName: normalizeValue(body?.teacherName),
|
||||
diplomas: Array.isArray(body?.diplomas)
|
||||
? body.diplomas.slice(0, 3).map(normalizeDiploma)
|
||||
: [],
|
||||
emergencyLastName: normalizeValue(body?.emergencyLastName),
|
||||
emergencyFirstName: normalizeValue(body?.emergencyFirstName),
|
||||
emergencyRelation: normalizeValue(body?.emergencyRelation),
|
||||
emergencyPhone: normalizeValue(body?.emergencyPhone),
|
||||
emergencyEmail: normalizeValue(body?.emergencyEmail),
|
||||
acceptRules: normalizeBoolean(body?.acceptRules),
|
||||
}
|
||||
|
||||
while (payload.diplomas.length < 3) {
|
||||
payload.diplomas.push(normalizeDiploma({}))
|
||||
}
|
||||
|
||||
if (
|
||||
!payload.lastName ||
|
||||
!payload.firstName ||
|
||||
!payload.gender ||
|
||||
!payload.birthDate ||
|
||||
!payload.birthPlace ||
|
||||
!payload.nationality ||
|
||||
!payload.email ||
|
||||
!payload.phone ||
|
||||
!payload.address ||
|
||||
!payload.city ||
|
||||
!payload.postalCode ||
|
||||
!payload.country ||
|
||||
!payload.instrument ||
|
||||
!payload.previousAcademyParticipation ||
|
||||
!payload.trainingLevel ||
|
||||
!payload.conservatory ||
|
||||
!payload.teacherName ||
|
||||
!payload.emergencyLastName ||
|
||||
!payload.emergencyFirstName ||
|
||||
!payload.emergencyRelation ||
|
||||
!payload.emergencyPhone ||
|
||||
!payload.emergencyEmail ||
|
||||
!payload.acceptRules
|
||||
) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Tous les champs obligatoires doivent être remplis.",
|
||||
})
|
||||
}
|
||||
|
||||
if (!isValidEmail(payload.email) || !isValidEmail(payload.emergencyEmail)) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Une adresse email n’est pas valide.",
|
||||
})
|
||||
}
|
||||
|
||||
if (payload.previousAcademyParticipation === "oui" && !payload.previousAcademyParticipationCount) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Le nombre de participations est obligatoire.",
|
||||
})
|
||||
}
|
||||
|
||||
if (payload.trainingLevel === "Autre" && !payload.otherTrainingLevel) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "L’autre formation est obligatoire.",
|
||||
})
|
||||
}
|
||||
|
||||
if (payload.conservatory === "Autre" && !payload.otherConservatory) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "L’autre conservatoire est obligatoire.",
|
||||
})
|
||||
}
|
||||
|
||||
if (!payload.diplomas.every(validateDiploma)) {
|
||||
throw createError({
|
||||
statusCode: 400,
|
||||
statusMessage: "Chaque diplôme renseigné doit être complet.",
|
||||
})
|
||||
}
|
||||
|
||||
const db = getMysqlPool()
|
||||
const [diploma1, diploma2, diploma3] = payload.diplomas
|
||||
|
||||
try {
|
||||
const [result] = await db.execute(
|
||||
`
|
||||
INSERT INTO academie_orchestre_candidatures (
|
||||
nom,
|
||||
prenom,
|
||||
genre,
|
||||
date_naissance,
|
||||
lieu_naissance,
|
||||
nationalite,
|
||||
email,
|
||||
telephone,
|
||||
adresse,
|
||||
ville,
|
||||
code_postal,
|
||||
pays_residence,
|
||||
instrument,
|
||||
deja_participe_academie,
|
||||
nombre_participations_academie,
|
||||
niveau_cycle_2026_2027,
|
||||
autre_formation,
|
||||
conservatoire_2026_2027,
|
||||
autre_conservatoire,
|
||||
professeur_instrument,
|
||||
diplome_1_type,
|
||||
diplome_1_autre_type,
|
||||
diplome_1_discipline,
|
||||
diplome_1_annee,
|
||||
diplome_1_etablissement,
|
||||
diplome_2_type,
|
||||
diplome_2_autre_type,
|
||||
diplome_2_discipline,
|
||||
diplome_2_annee,
|
||||
diplome_2_etablissement,
|
||||
diplome_3_type,
|
||||
diplome_3_autre_type,
|
||||
diplome_3_discipline,
|
||||
diplome_3_annee,
|
||||
diplome_3_etablissement,
|
||||
contact_urgence_nom,
|
||||
contact_urgence_prenom,
|
||||
contact_urgence_lien,
|
||||
contact_urgence_telephone,
|
||||
contact_urgence_email,
|
||||
reglement_accepte
|
||||
)
|
||||
VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?, ?)
|
||||
`,
|
||||
[
|
||||
payload.lastName,
|
||||
payload.firstName,
|
||||
payload.gender,
|
||||
payload.birthDate,
|
||||
payload.birthPlace,
|
||||
payload.nationality,
|
||||
payload.email,
|
||||
payload.phone,
|
||||
payload.address,
|
||||
payload.city,
|
||||
payload.postalCode,
|
||||
payload.country,
|
||||
payload.instrument,
|
||||
payload.previousAcademyParticipation,
|
||||
payload.previousAcademyParticipationCount,
|
||||
payload.trainingLevel,
|
||||
payload.otherTrainingLevel,
|
||||
payload.conservatory,
|
||||
payload.otherConservatory,
|
||||
payload.teacherName,
|
||||
diploma1.type,
|
||||
diploma1.otherType,
|
||||
diploma1.discipline,
|
||||
diploma1.year,
|
||||
diploma1.establishment,
|
||||
diploma2.type,
|
||||
diploma2.otherType,
|
||||
diploma2.discipline,
|
||||
diploma2.year,
|
||||
diploma2.establishment,
|
||||
diploma3.type,
|
||||
diploma3.otherType,
|
||||
diploma3.discipline,
|
||||
diploma3.year,
|
||||
diploma3.establishment,
|
||||
payload.emergencyLastName,
|
||||
payload.emergencyFirstName,
|
||||
payload.emergencyRelation,
|
||||
payload.emergencyPhone,
|
||||
payload.emergencyEmail,
|
||||
payload.acceptRules,
|
||||
]
|
||||
)
|
||||
|
||||
let emailsSent = false
|
||||
|
||||
try {
|
||||
emailsSent = await withTimeout(
|
||||
sendProjetAcademieEmails(payload),
|
||||
15000,
|
||||
"Délai dépassé pour l'envoi email projet-academie."
|
||||
)
|
||||
} catch (mailError) {
|
||||
console.error("Erreur envoi email projet-academie:", mailError)
|
||||
}
|
||||
|
||||
return {
|
||||
ok: true,
|
||||
id: result.insertId,
|
||||
emailsSent,
|
||||
}
|
||||
} catch (error) {
|
||||
console.error("Erreur API projet-academie:", error)
|
||||
|
||||
throw createError({
|
||||
statusCode: 500,
|
||||
statusMessage: "Impossible d’enregistrer la candidature pour le moment.",
|
||||
})
|
||||
}
|
||||
})
|
||||
46
server/database/academie_orchestre_candidatures.sql
Normal file
46
server/database/academie_orchestre_candidatures.sql
Normal file
@@ -0,0 +1,46 @@
|
||||
CREATE TABLE academie_orchestre_candidatures (
|
||||
id INT UNSIGNED NOT NULL AUTO_INCREMENT,
|
||||
nom VARCHAR(255) NOT NULL,
|
||||
prenom VARCHAR(255) NOT NULL,
|
||||
genre VARCHAR(50) NOT NULL,
|
||||
date_naissance DATE NOT NULL,
|
||||
lieu_naissance VARCHAR(255) NOT NULL,
|
||||
nationalite VARCHAR(255) NOT NULL,
|
||||
email VARCHAR(255) NOT NULL,
|
||||
telephone VARCHAR(100) NOT NULL,
|
||||
adresse TEXT NOT NULL,
|
||||
ville VARCHAR(255) NOT NULL,
|
||||
code_postal VARCHAR(20) NOT NULL,
|
||||
pays_residence VARCHAR(255) NOT NULL,
|
||||
instrument VARCHAR(255) NOT NULL,
|
||||
deja_participe_academie VARCHAR(10) NOT NULL,
|
||||
nombre_participations_academie INT UNSIGNED NULL,
|
||||
niveau_cycle_2026_2027 VARCHAR(255) NOT NULL,
|
||||
autre_formation VARCHAR(255) NULL,
|
||||
conservatoire_2026_2027 VARCHAR(500) NOT NULL,
|
||||
autre_conservatoire VARCHAR(500) NULL,
|
||||
professeur_instrument VARCHAR(255) NOT NULL,
|
||||
diplome_1_type VARCHAR(255) NULL,
|
||||
diplome_1_autre_type VARCHAR(255) NULL,
|
||||
diplome_1_discipline VARCHAR(255) NULL,
|
||||
diplome_1_annee VARCHAR(20) NULL,
|
||||
diplome_1_etablissement VARCHAR(255) NULL,
|
||||
diplome_2_type VARCHAR(255) NULL,
|
||||
diplome_2_autre_type VARCHAR(255) NULL,
|
||||
diplome_2_discipline VARCHAR(255) NULL,
|
||||
diplome_2_annee VARCHAR(20) NULL,
|
||||
diplome_2_etablissement VARCHAR(255) NULL,
|
||||
diplome_3_type VARCHAR(255) NULL,
|
||||
diplome_3_autre_type VARCHAR(255) NULL,
|
||||
diplome_3_discipline VARCHAR(255) NULL,
|
||||
diplome_3_annee VARCHAR(20) NULL,
|
||||
diplome_3_etablissement VARCHAR(255) NULL,
|
||||
contact_urgence_nom VARCHAR(255) NOT NULL,
|
||||
contact_urgence_prenom VARCHAR(255) NOT NULL,
|
||||
contact_urgence_lien VARCHAR(100) NOT NULL,
|
||||
contact_urgence_telephone VARCHAR(100) NOT NULL,
|
||||
contact_urgence_email VARCHAR(255) NOT NULL,
|
||||
reglement_accepte TINYINT(1) NOT NULL DEFAULT 0,
|
||||
created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
|
||||
PRIMARY KEY (id)
|
||||
) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci;
|
||||
@@ -189,3 +189,121 @@ export async function sendProjetLyceeEmails(payload) {
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
function formatAcademieDiploma(diploma, index) {
|
||||
if (!diploma?.type && !diploma?.discipline && !diploma?.year && !diploma?.establishment) {
|
||||
return `Diplome ${index + 1} : Non renseigne`
|
||||
}
|
||||
|
||||
return [
|
||||
`Diplome ${index + 1} :`,
|
||||
`Type : ${diploma.type || "Non renseigne"}`,
|
||||
diploma.type === "Autre" ? `Autre type : ${diploma.otherType || "Non renseigne"}` : null,
|
||||
`Discipline : ${diploma.discipline || "Non renseignee"}`,
|
||||
`Annee : ${diploma.year || "Non renseignee"}`,
|
||||
`Etablissement : ${diploma.establishment || "Non renseigne"}`,
|
||||
].filter(Boolean).join("\n")
|
||||
}
|
||||
|
||||
function getProjetAcademieSummary(payload) {
|
||||
return [
|
||||
"Identite :",
|
||||
`Nom : ${payload.lastName}`,
|
||||
`Prenom : ${payload.firstName}`,
|
||||
`Genre : ${payload.gender}`,
|
||||
`Date de naissance : ${payload.birthDate}`,
|
||||
`Lieu de naissance : ${payload.birthPlace}`,
|
||||
`Nationalite : ${payload.nationality}`,
|
||||
"",
|
||||
"Coordonnees :",
|
||||
`Email : ${payload.email}`,
|
||||
`Telephone : ${payload.phone}`,
|
||||
`Adresse : ${payload.address}`,
|
||||
`Ville : ${payload.city}`,
|
||||
`Code postal : ${payload.postalCode}`,
|
||||
`Pays de residence : ${payload.country}`,
|
||||
"",
|
||||
"Parcours musical :",
|
||||
`Instrument : ${payload.instrument}`,
|
||||
`A deja participe a l'academie : ${payload.previousAcademyParticipation}`,
|
||||
payload.previousAcademyParticipation === "oui"
|
||||
? `Nombre de participations : ${payload.previousAcademyParticipationCount}`
|
||||
: null,
|
||||
"",
|
||||
"Formation :",
|
||||
`Niveau - cycle actuel 2026-2027 : ${payload.trainingLevel}`,
|
||||
payload.trainingLevel === "Autre" ? `Autre formation : ${payload.otherTrainingLevel}` : null,
|
||||
`Conservatoire 2026-2027 : ${payload.conservatory}`,
|
||||
payload.conservatory === "Autre" ? `Autre conservatoire : ${payload.otherConservatory}` : null,
|
||||
`Professeur d'instrument : ${payload.teacherName}`,
|
||||
"",
|
||||
"Diplomes musicaux :",
|
||||
...payload.diplomas.map(formatAcademieDiploma),
|
||||
"",
|
||||
"Contact d'urgence :",
|
||||
`Nom : ${payload.emergencyLastName}`,
|
||||
`Prenom : ${payload.emergencyFirstName}`,
|
||||
`Lien : ${payload.emergencyRelation}`,
|
||||
`Telephone : ${payload.emergencyPhone}`,
|
||||
`Mail : ${payload.emergencyEmail}`,
|
||||
"",
|
||||
`Reglement accepte : ${payload.acceptRules ? "Oui" : "Non"}`,
|
||||
].filter((line) => line !== null).join("\n")
|
||||
}
|
||||
|
||||
export async function sendProjetAcademieEmails(payload) {
|
||||
const config = useRuntimeConfig()
|
||||
const mailer = getTransporter()
|
||||
|
||||
if (!mailer) {
|
||||
console.warn("Email projet académie non envoyé: configuration SMTP incomplète.")
|
||||
return false
|
||||
}
|
||||
|
||||
if (!config.academieRequestRecipientEmail) {
|
||||
console.warn("Email projet académie non envoyé: destinataire académie manquant.")
|
||||
return false
|
||||
}
|
||||
|
||||
const from = config.smtpFromName
|
||||
? `"${config.smtpFromName}" <${config.smtpFromEmail}>`
|
||||
: config.smtpFromEmail
|
||||
|
||||
const summary = getProjetAcademieSummary(payload)
|
||||
const fullName = `${payload.lastName} ${payload.firstName}`
|
||||
|
||||
const adminSubject = `WONDIF - Candidature Academie d'orchestre - ${fullName}`
|
||||
const adminText = [
|
||||
"Une nouvelle candidature a été envoyée depuis le formulaire Académie d'orchestre.",
|
||||
"",
|
||||
summary,
|
||||
].join("\n")
|
||||
|
||||
const userSubject = "ONDIF - Confirmation de votre candidature - Académie d'orchestre"
|
||||
const userText = [
|
||||
`Bonjour ${payload.firstName} ${payload.lastName},`,
|
||||
"",
|
||||
"Votre candidature à l'Académie d'orchestre de l'Orchestre national d'Île-de-France a bien été transmise.",
|
||||
"",
|
||||
"Recapitulatif :",
|
||||
summary,
|
||||
].join("\n")
|
||||
|
||||
await Promise.all([
|
||||
mailer.sendMail({
|
||||
from,
|
||||
to: config.academieRequestRecipientEmail,
|
||||
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