import { useEffect, useState } from 'react'; import { Link, useSearchParams } from 'react-router-dom'; import { usePublicData } from '../lib/usePublic.js'; 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'; import { associationSettings } from '../lib/siteSettings.js'; export function InscriptionModal({ rencontre, onClose, onDone }) { const { user, login } = useAuth(); const [context, setContext] = useState(null); // Each participant: { nom, imageConsent } — image consent is recorded per // person (an adult can't consent for another, art. 9 Code civil). const [participants, setParticipants] = useState([{ nom: '', imageConsent: true }]); const [attestation, setAttestation] = useState(false); const [credentials, setCredentials] = useState({ email: '', password: '' }); const [done, setDone] = useState(false); const [wasRegistered, setWasRegistered] = useState(false); const [error, setError] = useState(''); const [busy, setBusy] = useState(false); const { jour, mois } = dateParts(rencontre.date_renc); useEffect(() => { if (user?.role !== 'member' || user.mustChangePassword) return; setError(''); api.get(`/api/public/rencontres/${rencontre.id}/inscription`) .then((d) => { setContext(d); setWasRegistered(d.participants.length > 0); setParticipants( d.participants.length ? d.participants.map((p) => ({ nom: p.nom, imageConsent: p.image_consent !== false })) : [{ nom: d.member.dirigeant || '', imageConsent: true }] ); // A returning registration already carried the attestation. setAttestation(d.participants.length > 0); }) .catch((err) => setError(err.message)); }, [user, rencontre.id]); const submitLogin = async (e) => { e.preventDefault(); setError(''); setBusy(true); try { await login(credentials.email, credentials.password); } catch (err) { setError(err.message); } finally { setBusy(false); } }; const changeParticipant = (index, value) => { setParticipants((current) => current.map((p, i) => (i === index ? { ...p, nom: value } : p))); }; const toggleParticipantImage = (index) => { setParticipants((current) => current.map((p, i) => (i === index ? { ...p, imageConsent: !p.imageConsent } : p))); }; const maxPerAccount = context?.rencontre.participants_par_compte || rencontre.participants_par_compte || 1; const submit = async (e) => { e.preventDefault(); setError(''); if (!attestation) { setError("Merci de confirmer l'attestation sur le droit à l'image."); return; } setBusy(true); try { await api.post(`/api/public/rencontres/${rencontre.id}/inscriptions`, { participants, attestation }); setDone(true); onDone?.(); } catch (err) { setError(err.message); } finally { setBusy(false); } }; if (done) { return (
✓

{wasRegistered ? 'Inscription modifiée' : 'Inscription enregistrée'}

Votre demande pour {rencontre.titre} est bien enregistrée.

{participants.length} participant{participants.length > 1 ? 's' : ''} — confirmation par l'équipe du Club.

); } return ( {!user && (

Connectez-vous avec votre compte membre avant de choisir les participants.

{error &&

{error}

}
)} {user && (user.role !== 'member' || user.mustChangePassword) && (

{user.role !== 'member' ? 'Les inscriptions doivent être effectuées avec un compte membre.' : 'Vous devez d’abord remplacer votre mot de passe temporaire.'}

Ouvrir mon espace membre
)} {user?.role === 'member' && !user.mustChangePassword && !context && (

{error || 'Chargement de votre compte…'}

)} {user?.role === 'member' && !user.mustChangePassword && context && (
Compte : {context.member.nom} · maximum {maxPerAccount} participant{maxPerAccount > 1 ? 's' : ''}
{participants.map((p, index) => (
{participants.length > 1 && ( )}
))}
{participants.length < maxPerAccount && ( )} {error &&

{error}

}

Vous pourrez rouvrir ce formulaire pour modifier les noms et les choix.{' '} Politique de confidentialité.

)}
); } export default function Home() { const { data, reload } = usePublicData(); const { user } = useAuth(); const [searchParams, setSearchParams] = useSearchParams(); const [inscrRenc, setInscrRenc] = useState(null); const [adhesionOpen, setAdhesionOpen] = useState(false); const [registrations, setRegistrations] = useState({}); const refreshRegistrations = () => { if (user?.role !== 'member' || user.mustChangePassword) { setRegistrations({}); return Promise.resolve(); } return api.get('/api/member/inscriptions').then((result) => { setRegistrations(Object.fromEntries(result.inscriptions.map((item) => [item.rencontre_id, item]))); }).catch(() => setRegistrations({})); }; useEffect(() => { refreshRegistrations(); }, [user]); // Registration links in invitation emails point to /?inscription= : // 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]); useEffect(() => { if (searchParams.has('adhesion')) setAdhesionOpen(true); }, [searchParams]); const closeInscription = () => { setInscrRenc(null); if (searchParams.has('inscription')) setSearchParams({}, { replace: true }); }; const closeAdhesion = () => { setAdhesionOpen(false); if (searchParams.has('adhesion')) { const next = new URLSearchParams(searchParams); next.delete('adhesion'); setSearchParams(next, { replace: true }); } }; const members = data?.members ?? []; const rencontres = data?.rencontres ?? []; const passees = data?.rencontresPassees ?? []; const content = data?.content ?? {}; const settings = associationSettings(content); const marquee = members.concat(members); return (
{/* HERO */}
Réseau d'affaires · SLUC Nancy Basket

Le business se joue en équipe.

Depuis 30 ans, {settings.association_name} rassemble les dirigeants du Grand Est autour des valeurs du sport de haut niveau : performance, engagement et esprit collectif.

{[[String(members.length), 'entreprises membres'], [String(passees.length), 'rencontres organisées'], ['30 ans', 'de partenariat']].map(([n, l]) => (
{n}
{l}
))}
{content.hero_photo ? ( L'esprit du Club ) : ( photo — networking en tribune VIP )}
« {content.hero_quote_text || 'On ne réussit jamais seul.'} »
{content.hero_quote_author || "L'esprit du Club"}
{/* OFFICIAL SLUC PARTNER */}
Logo officiel du SLUC Nancy Basket
Partenaire officiel du
SLUC Nancy Basket
Retrouvez l'actualité, le calendrier et la billetterie du club sur son site officiel.
Voir le site ↗
{/* MEMBERS MARQUEE */}
Ils nous font confiance

Nos entreprises membres

Voir l'annuaire complet →
{marquee.map((m, i) => (
{m.logo_path ? : 'logo'}
{m.nom}
{m.secteur}
))}
{/* UPCOMING */}
Agenda

Prochaines rencontres

{rencontres.map((e) => { const { jour, mois } = dateParts(e.date_renc); const restantes = Math.max(0, e.places - e.inscrits); const registration = registrations[e.id]; const isRegistered = Boolean(registration); return (
{e.image_path && ( )}
{jour}
{mois}
{e.heure}·{e.lieu}

{e.titre}

{e.description}

{isRegistered ? '✓ Inscrit' : (restantes > 0 ? `${restantes} places restantes` : 'Complet')} {isRegistered ? `${registration.participants} participant${registration.participants > 1 ? 's' : ''} · ${registration.confirmee ? 'Confirmée' : 'En attente'}` : `Max. ${e.participants_par_compte} par compte`}
); })}
{/* PAST */}
Ils y étaient

Rencontres passées

Voir toutes les rencontres →
{passees.slice(0, 3).map((p) => ( ))}
{/* ADHESION */}
Nous rejoindre

Faites entrer votre entreprise dans le Club.

Rejoignez un réseau de {members.length} dirigeant{members.length > 1 ? 's' : ''}, accédez à toutes nos rencontres et bénéficiez d'une visibilité au cœur du SLUC Nancy.

    {['Accès à toutes les rencontres business', "Présence dans l'annuaire des membres"].map((t) => (
  • —{t}
  • ))}
{inscrRenc && ( { reload(); refreshRegistrations(); }} /> )} {adhesionOpen && }
); }