diff --git a/server/src/routes/public.js b/server/src/routes/public.js index 5bf6c67..978bd61 100644 --- a/server/src/routes/public.js +++ b/server/src/routes/public.js @@ -83,10 +83,10 @@ publicRouter.post( validate(demandeSchema), async (req, res, next) => { try { - const { nom, fonction, entreprise, email } = req.data; + const { nom, fonction, entreprise, email, tel } = req.data; await query( - 'INSERT INTO demandes_adhesion (nom, fonction, entreprise, email) VALUES ($1, $2, $3, $4)', - [nom, fonction, entreprise, email] + 'INSERT INTO demandes_adhesion (nom, fonction, entreprise, email, tel) VALUES ($1, $2, $3, $4, $5)', + [nom, fonction, entreprise, email, tel] ); res.status(201).json({ ok: true }); } catch (err) { diff --git a/server/src/schemas.js b/server/src/schemas.js index ab19f97..3dcdd5c 100644 --- a/server/src/schemas.js +++ b/server/src/schemas.js @@ -24,6 +24,7 @@ export const demandeSchema = z.object({ fonction: trimmed(120).optional().default(''), entreprise: trimmed(120, 1), email: trimmed(254, 3).email(), + tel: trimmed(30, 6), }); export const inscriptionPublicSchema = z.object({ diff --git a/server/src/sql/schema.sql b/server/src/sql/schema.sql index 29d4c54..c18624f 100644 --- a/server/src/sql/schema.sql +++ b/server/src/sql/schema.sql @@ -135,9 +135,11 @@ CREATE TABLE IF NOT EXISTS demandes_adhesion ( fonction TEXT NOT NULL DEFAULT '' CHECK (char_length(fonction) <= 120), entreprise TEXT NOT NULL CHECK (char_length(entreprise) BETWEEN 1 AND 120), email CITEXT NOT NULL CHECK (char_length(email) <= 254), + tel TEXT CHECK (char_length(tel) <= 30), statut TEXT NOT NULL DEFAULT 'nouvelle' CHECK (statut IN ('nouvelle', 'traitee')), created_at TIMESTAMPTZ NOT NULL DEFAULT now() ); +ALTER TABLE demandes_adhesion ADD COLUMN IF NOT EXISTS tel TEXT CHECK (char_length(tel) <= 30); CREATE TABLE IF NOT EXISTS site_content ( key TEXT PRIMARY KEY, diff --git a/web/src/components/AdhesionForm.jsx b/web/src/components/AdhesionForm.jsx new file mode 100644 index 0000000..ead02d0 --- /dev/null +++ b/web/src/components/AdhesionForm.jsx @@ -0,0 +1,88 @@ +import { useState } from 'react'; +import { api } from '../lib/api.js'; +import Modal from './Modal.jsx'; + +const EMPTY_FORM = { nom: '', fonction: '', entreprise: '', email: '', tel: '' }; + +export function AdhesionForm({ showTitle = true }) { + const [form, setForm] = useState(EMPTY_FORM); + const [done, setDone] = useState(false); + const [error, setError] = useState(''); + const [busy, setBusy] = useState(false); + + const onChange = (event) => setForm({ ...form, [event.target.name]: event.target.value }); + const submit = async (event) => { + event.preventDefault(); + setError(''); + setBusy(true); + try { + await api.post('/api/public/demandes-adhesion', form); + setDone(true); + } catch (err) { + setError(err.message); + } finally { + setBusy(false); + } + }; + + if (done) { + return ( +
+
✓
+

Demande envoyée !

+

+ Merci pour votre intérêt. Notre équipe vous recontacte sous 48h. +

+ +
+ ); + } + + return ( +
+ {showTitle &&

Demande d'adhésion

} +
+
+ + +
+ + + +
+ {error &&

{error}

} + +
+ ); +} + +export function AdhesionModal({ onClose }) { + return ( + +
+ +

Demande d'adhésion

+ +
+
+ ); +} diff --git a/web/src/components/Header.jsx b/web/src/components/Header.jsx index 97351c7..70dfef9 100644 --- a/web/src/components/Header.jsx +++ b/web/src/components/Header.jsx @@ -30,7 +30,7 @@ export default function Header() { Rencontres passées L'association Espace membre - + diff --git a/web/src/pages/Home.jsx b/web/src/pages/Home.jsx index 3b1d169..4424d48 100644 --- a/web/src/pages/Home.jsx +++ b/web/src/pages/Home.jsx @@ -5,6 +5,7 @@ import { useAuth } from '../lib/AuthContext.jsx'; import { api, dateParts } from '../lib/api.js'; import Modal from '../components/Modal.jsx'; import PastEventCard from '../components/PastEventCard.jsx'; +import { AdhesionForm, AdhesionModal } from '../components/AdhesionForm.jsx'; export function InscriptionModal({ rencontre, onClose, onDone }) { const { user, login } = useAuth(); @@ -161,9 +162,7 @@ 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: '' }); + const [adhesionOpen, setAdhesionOpen] = useState(false); // Registration links in invitation emails point to /?inscription= : // once the public data is loaded, open the matching inscription modal. @@ -174,21 +173,21 @@ export default function Home() { if (renc) setInscrRenc(renc); }, [data, searchParams]); + useEffect(() => { + if (searchParams.has('adhesion')) setAdhesionOpen(true); + }, [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) => { - e.preventDefault(); - setHomeError(''); - try { - await api.post('/api/public/demandes-adhesion', form); - setHomeDone(true); - } catch (err) { - setHomeError(err.message); + const closeAdhesion = () => { + setAdhesionOpen(false); + if (searchParams.has('adhesion')) { + const next = new URLSearchParams(searchParams); + next.delete('adhesion'); + setSearchParams(next, { replace: true }); } }; @@ -212,9 +211,7 @@ export default function Home() { de haut niveau : performance, engagement et esprit collectif.

- - - + @@ -390,42 +387,7 @@ export default function Home() {
- {homeDone ? ( -
-
✓
-

Demande envoyée !

-

- Merci pour votre intérêt. Notre équipe vous recontacte sous 48h. -

- -
- ) : ( -
-

Demande d'adhésion

-
-
- - -
- - -
- {homeError &&

{homeError}

} - -
- )} +
@@ -433,6 +395,7 @@ export default function Home() { {inscrRenc && ( )} + {adhesionOpen && } ); }