Email d'invitation HTML généré depuis l'administration des rencontres
Chaque rencontre dispose d'un bouton « ✉ Email » dans l'administration (admins et modérateurs) qui génère un email HTML prêt à envoyer, aux couleurs du site avec le logo du club : titre, description, encadré date/heure/lieu/places restantes et bouton d'inscription. - Nouvelle route GET /api/admin/rencontres/:id/email : construit l'email (mise en page en tables + styles inline, compatible clients mail) avec des URL absolues dérivées de l'hôte de la requête. - Le bouton de l'email pointe vers /?inscription=<id> : la page d'accueil ouvre alors automatiquement la modale d'inscription de la rencontre. - Modale d'aperçu dans l'admin : copie de l'email (HTML enrichi presse- papiers pour coller directement dans un logiciel de messagerie), copie de l'objet et du lien d'inscription, téléchargement du fichier .html. - Les images publiques (logo, uploads) sont maintenant servies avec Cross-Origin-Resource-Policy: cross-origin pour que le logo se charge dans les webmails et l'aperçu. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01Fkg15RCxNgUys4ru73He2o
This commit is contained in:
5 files changed
+235
-4
No files matched your search
+18
-3
@@ -1,5 +1,5 @@
|
||||
import { useState } from 'react';
|
||||
import { Link } from 'react-router-dom';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Link, useSearchParams } from 'react-router-dom';
|
||||
import { usePublicData } from '../lib/usePublic.js';
|
||||
import { api, dateParts } from '../lib/api.js';
|
||||
import Modal from '../components/Modal.jsx';
|
||||
@@ -75,11 +75,26 @@ function InscriptionModal({ rencontre, onClose, onDone }) {
|
||||
|
||||
export default function Home() {
|
||||
const { data, reload } = usePublicData();
|
||||
const [searchParams, setSearchParams] = useSearchParams();
|
||||
const [inscrRenc, setInscrRenc] = useState(null);
|
||||
const [homeDone, setHomeDone] = useState(false);
|
||||
const [homeError, setHomeError] = useState('');
|
||||
const [form, setForm] = useState({ nom: '', fonction: '', entreprise: '', email: '' });
|
||||
|
||||
// Registration links in invitation emails point to /?inscription=<id> :
|
||||
// once the public data is loaded, open the matching inscription modal.
|
||||
useEffect(() => {
|
||||
const id = Number(searchParams.get('inscription'));
|
||||
if (!id || !data) return;
|
||||
const renc = (data.rencontres || []).find((r) => r.id === id);
|
||||
if (renc) setInscrRenc(renc);
|
||||
}, [data, searchParams]);
|
||||
|
||||
const closeInscription = () => {
|
||||
setInscrRenc(null);
|
||||
if (searchParams.has('inscription')) setSearchParams({}, { replace: true });
|
||||
};
|
||||
|
||||
const onChange = (e) => setForm({ ...form, [e.target.name]: e.target.value });
|
||||
|
||||
const submitHome = async (e) => {
|
||||
@@ -329,7 +344,7 @@ export default function Home() {
|
||||
</section>
|
||||
|
||||
{inscrRenc && (
|
||||
<InscriptionModal rencontre={inscrRenc} onClose={() => setInscrRenc(null)} onDone={reload} />
|
||||
<InscriptionModal rencontre={inscrRenc} onClose={closeInscription} onDone={reload} />
|
||||
)}
|
||||
</main>
|
||||
);
|
||||
|
||||
Reference in new issue
Block a user