146 lines
6.4 KiB
React
146 lines
6.4 KiB
React
|
|
import React, { useState, useEffect } from 'react';
|
|||
|
|
import { api } from '../stores/api';
|
|||
|
|
import PageHeader from '../components/PageHeader';
|
|||
|
|
|
|||
|
|
export default function GererMembres() {
|
|||
|
|
const [membres, setMembres] = useState([]);
|
|||
|
|
const [loading, setLoading] = useState(true);
|
|||
|
|
const [detail, setDetail] = useState(null);
|
|||
|
|
const [confirmAction, setConfirmAction] = useState(null);
|
|||
|
|
const [msg, setMsg] = useState('');
|
|||
|
|
|
|||
|
|
const charger = async () => {
|
|||
|
|
setLoading(true);
|
|||
|
|
try {
|
|||
|
|
const data = await api.get('/membres/');
|
|||
|
|
setMembres(data);
|
|||
|
|
} catch {}
|
|||
|
|
setLoading(false);
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
useEffect(() => { charger(); }, []);
|
|||
|
|
|
|||
|
|
const desactiver = async (id) => {
|
|||
|
|
try {
|
|||
|
|
const res = await api.patch(`/membres/${id}/desactiver`);
|
|||
|
|
setMsg(res.message);
|
|||
|
|
setConfirmAction(null);
|
|||
|
|
setDetail(null);
|
|||
|
|
charger();
|
|||
|
|
setTimeout(() => setMsg(''), 3000);
|
|||
|
|
} catch (e) { alert(e.message); }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
const droitOubli = async (id) => {
|
|||
|
|
try {
|
|||
|
|
const res = await api.request('DELETE', `/membres/${id}/oubli`);
|
|||
|
|
setMsg(res.message);
|
|||
|
|
setConfirmAction(null);
|
|||
|
|
setDetail(null);
|
|||
|
|
charger();
|
|||
|
|
setTimeout(() => setMsg(''), 3000);
|
|||
|
|
} catch (e) { alert(e.message); }
|
|||
|
|
};
|
|||
|
|
|
|||
|
|
// Vue détail d'un membre
|
|||
|
|
if (detail) {
|
|||
|
|
return (
|
|||
|
|
<div className="min-h-screen bg-gray-100">
|
|||
|
|
<div className="bg-aa-blue text-white p-4 flex items-center gap-3">
|
|||
|
|
<button onClick={() => { setDetail(null); setConfirmAction(null); }} className="text-2xl">←</button>
|
|||
|
|
<h1 className="text-lg font-bold">{detail.prenom} {detail.nom}</h1>
|
|||
|
|
</div>
|
|||
|
|
<div className="p-4 space-y-4">
|
|||
|
|
{msg && <div className="bg-green-50 text-green-700 p-3 rounded-xl text-center text-sm font-medium">{msg}</div>}
|
|||
|
|
|
|||
|
|
<div className="bg-white rounded-2xl p-4 shadow-sm space-y-3">
|
|||
|
|
<div><p className="text-xs text-gray-400">Prénom</p><p className="font-medium">{detail.prenom}</p></div>
|
|||
|
|
<div><p className="text-xs text-gray-400">Nom</p><p className="font-medium">{detail.nom}</p></div>
|
|||
|
|
<div><p className="text-xs text-gray-400">Téléphone</p><p className="font-medium">{detail.telephone || '—'}</p></div>
|
|||
|
|
<div><p className="text-xs text-gray-400">Courriel</p><p className="font-medium">{detail.courriel || '—'}</p></div>
|
|||
|
|
<div><p className="text-xs text-gray-400">Date d'abstinence</p><p className="font-medium">{detail.date_abstinence || '—'}</p></div>
|
|||
|
|
<div><p className="text-xs text-gray-400">Statut</p>
|
|||
|
|
<span className={`inline-block px-2 py-1 rounded-full text-xs font-bold ${detail.actif ? 'bg-green-100 text-green-700' : 'bg-red-100 text-red-700'}`}>
|
|||
|
|
{detail.actif ? 'Actif' : 'Inactif'}
|
|||
|
|
</span>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Contact rapide */}
|
|||
|
|
<div className="grid grid-cols-2 gap-2">
|
|||
|
|
{detail.telephone && (
|
|||
|
|
<a href={`tel:${detail.telephone}`}
|
|||
|
|
className="p-3 bg-green-50 border border-green-200 rounded-xl text-center text-green-700 font-bold text-sm">
|
|||
|
|
📞 Appeler
|
|||
|
|
</a>
|
|||
|
|
)}
|
|||
|
|
{detail.courriel && (
|
|||
|
|
<a href={`mailto:${detail.courriel}`}
|
|||
|
|
className="p-3 bg-blue-50 border border-blue-200 rounded-xl text-center text-blue-700 font-bold text-sm">
|
|||
|
|
✉️ Courriel
|
|||
|
|
</a>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
|
|||
|
|
{/* Actions */}
|
|||
|
|
{detail.actif && !confirmAction && (
|
|||
|
|
<button onClick={() => setConfirmAction('desactiver')}
|
|||
|
|
className="w-full p-3 bg-orange-50 border border-orange-200 text-orange-700 rounded-xl font-bold text-sm">
|
|||
|
|
Désactiver ce membre
|
|||
|
|
</button>
|
|||
|
|
)}
|
|||
|
|
{!confirmAction && (
|
|||
|
|
<button onClick={() => setConfirmAction('oubli')}
|
|||
|
|
className="w-full p-3 bg-red-50 border border-red-200 text-red-700 rounded-xl font-bold text-sm">
|
|||
|
|
Droit à l'oubli (irréversible)
|
|||
|
|
</button>
|
|||
|
|
)}
|
|||
|
|
|
|||
|
|
{/* Confirmations */}
|
|||
|
|
{confirmAction === 'desactiver' && (
|
|||
|
|
<div className="bg-orange-50 rounded-2xl p-4 border border-orange-200">
|
|||
|
|
<p className="text-sm text-orange-800 mb-3">Désactiver {detail.prenom} ? Le membre ne pourra plus se connecter.</p>
|
|||
|
|
<div className="flex gap-2">
|
|||
|
|
<button onClick={() => desactiver(detail.id)} className="flex-1 p-3 bg-orange-500 text-white rounded-xl font-bold">Confirmer</button>
|
|||
|
|
<button onClick={() => setConfirmAction(null)} className="flex-1 p-3 bg-gray-200 text-gray-700 rounded-xl font-bold">Annuler</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
{confirmAction === 'oubli' && (
|
|||
|
|
<div className="bg-red-50 rounded-2xl p-4 border border-red-200">
|
|||
|
|
<p className="text-sm text-red-800 mb-3">⚠️ Cette action est irréversible. Toutes les données personnelles de {detail.prenom} seront anonymisées.</p>
|
|||
|
|
<div className="flex gap-2">
|
|||
|
|
<button onClick={() => droitOubli(detail.id)} className="flex-1 p-3 bg-red-600 text-white rounded-xl font-bold">Supprimer</button>
|
|||
|
|
<button onClick={() => setConfirmAction(null)} className="flex-1 p-3 bg-gray-200 text-gray-700 rounded-xl font-bold">Annuler</button>
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
return (
|
|||
|
|
<div className="min-h-screen bg-gray-100">
|
|||
|
|
<PageHeader titre="Gérer les membres" sousTitre="G2 — Fiche complète, désactiver" />
|
|||
|
|
<div className="p-4 space-y-2">
|
|||
|
|
{msg && <div className="bg-green-50 text-green-700 p-3 rounded-xl text-center text-sm font-medium mb-2">{msg}</div>}
|
|||
|
|
{loading ? (
|
|||
|
|
<p className="text-center text-gray-400 py-8">Chargement...</p>
|
|||
|
|
) : (
|
|||
|
|
membres.map((m) => (
|
|||
|
|
<button key={m.id} onClick={() => setDetail(m)}
|
|||
|
|
className="w-full bg-white rounded-2xl p-4 shadow-sm flex items-center justify-between active:scale-[0.98] transition">
|
|||
|
|
<div>
|
|||
|
|
<p className="font-bold text-gray-800">{m.prenom} {m.nom}</p>
|
|||
|
|
<p className="text-xs text-gray-400">{m.telephone || m.courriel || '—'}</p>
|
|||
|
|
</div>
|
|||
|
|
<span className="text-gray-300">→</span>
|
|||
|
|
</button>
|
|||
|
|
))
|
|||
|
|
)}
|
|||
|
|
</div>
|
|||
|
|
</div>
|
|||
|
|
);
|
|||
|
|
}
|