import { useEffect, useState } from 'react';
import { useAuth } from '../lib/AuthContext.jsx';
import { api, seasonLabel } from '../lib/api.js';
import ImageSlot from '../components/ImageSlot.jsx';
import { AdminShell } from './Admin.jsx';
function LoginSection({ title = 'Gérez votre présence au Club', kicker = 'Espace membre' }) {
const { login } = useAuth();
const [email, setEmail] = useState('');
const [password, setPassword] = useState('');
const [error, setError] = useState('');
const [busy, setBusy] = useState(false);
const submit = async (e) => {
e.preventDefault();
setError('');
setBusy(true);
try {
await login(email, password);
} catch (err) {
setError(err.message);
} finally {
setBusy(false);
}
};
return (
);
}
function PasswordCard() {
const [form, setForm] = useState({ currentPassword: '', newPassword: '' });
const [msg, setMsg] = useState(null);
const submit = async (e) => {
e.preventDefault();
setMsg(null);
try {
await api.post('/api/auth/change-password', form);
setMsg({ ok: true, text: '✓ Mot de passe modifié' });
setForm({ currentPassword: '', newPassword: '' });
} catch (err) {
setMsg({ ok: false, text: err.message });
}
};
return (
Mot de passe
10 caractères minimum, avec au moins une lettre et un chiffre.
);
}
// Blocking gate shown right after login when the account still carries a
// temporary password (new member, or an admin reset). The temporary
// password itself doubles as "mot de passe actuel" here.
function ForcedPasswordChange() {
const { logout, refreshUser } = useAuth();
const [form, setForm] = useState({ currentPassword: '', newPassword: '' });
const [confirm, setConfirm] = useState('');
const [error, setError] = useState('');
const [busy, setBusy] = useState(false);
const submit = async (e) => {
e.preventDefault();
setError('');
if (form.newPassword !== confirm) {
setError('Les deux mots de passe ne correspondent pas.');
return;
}
setBusy(true);
try {
await api.post('/api/auth/change-password', form);
await refreshUser();
} catch (err) {
setError(err.message);
} finally {
setBusy(false);
}
};
return (
Première connexion
Choisissez votre mot de passe
Votre mot de passe temporaire doit être remplacé avant de continuer.
);
}
function Portal() {
const { logout } = useAuth();
const [member, setMember] = useState(null);
const [categories, setCategories] = useState([]);
const [form, setForm] = useState(null);
const [saved, setSaved] = useState(false);
const [error, setError] = useState('');
const season = seasonLabel();
useEffect(() => {
Promise.all([api.get('/api/member/profile'), api.get('/api/public/bootstrap')])
.then(([p, b]) => {
setMember(p.member);
setForm(p.member);
setCategories(b.categories);
})
.catch((e) => setError(e.message));
}, []);
if (!form) {
return (
);
}
const onChange = (e) => {
const { name, value } = e.target;
setForm({ ...form, [name]: name === 'categorie_id' ? (value ? Number(value) : null) : value });
setSaved(false);
};
const save = async (e) => {
e.preventDefault();
setError('');
try {
const d = await api.put('/api/member/profile', {
nom: form.nom, secteur: form.secteur, categorie_id: form.categorie_id,
dirigeant: form.dirigeant, email: form.email || '', tel: form.tel || '',
site: form.site || '', presentation: form.presentation || '',
});
setMember(d.member);
setForm(d.member);
setSaved(true);
} catch (err) {
setError(err.message);
}
};
const valide = member?.valide;
const catName = categories.find((c) => c.id === form.categorie_id)?.name || 'Non classée';
return (
Espace membre
Bonjour {member.dirigeant}
{member.nom}
Se déconnecter
{valide ? '✓' : '⏳'}
{valide ? `Adhésion validée pour la saison ${season}` : 'Adhésion à renouveler'}
{valide
? "Votre fiche est visible dans l'annuaire et vous pouvez vous inscrire aux rencontres."
: "Tant que votre adhésion n'est pas validée par le Club, votre fiche n'apparaît pas dans l'annuaire et vous ne pouvez pas vous inscrire aux rencontres."}
Mon profil
Ces informations alimentent votre fiche dans l'annuaire des membres.
Logo
{ setForm((f) => ({ ...f, logo_path: path })); setMember((m) => ({ ...m, logo_path: path })); }} />
Photo dirigeant·e
{ setForm((f) => ({ ...f, photo_path: path })); setMember((m) => ({ ...m, photo_path: path })); }} />
Aperçu de votre fiche annuaire
{form.logo_path ?
: 'logo'}
{form.nom}
{form.secteur}
{catName}
{form.presentation}
{form.dirigeant}
Voir la fiche →
{valide
? "Votre fiche est publiée dans l'annuaire des membres."
: "Cette fiche ne sera publiée qu'une fois votre adhésion validée pour la saison en cours."}
);
}
// Single login entry point for members, admins and moderators. Once
// authenticated, the content shown depends on the account's role — the
// URL and the login form never differ.
export default function Espace() {
const { user, loading } = useAuth();
if (loading) return ;
if (!user) return ;
if (user.mustChangePassword) return ;
if (user.role === 'admin' || user.role === 'moderator') return ;
return ;
}