Initial commit

This commit is contained in:
Olivier CROGUENNEC committed 2026-06-13 14:57:15 +02:00
commit 48ed7fe65e
209 files changed
+49979

No files matched your search

+114
View File
@@ -0,0 +1,114 @@
import { useState, useEffect, useCallback } from 'react';
import { api } from '../../api.js';
import ConfirmModal from '../../components/ConfirmModal.jsx';
import { fmt, Badge } from './adminHelpers.jsx';
export default function UsersSection({ currentUserId }) {
const [users, setUsers] = useState([]);
const [loading, setLoading] = useState(true);
const [err, setErr] = useState(null);
const [confirmAction, setConfirmAction] = useState(null);
const load = useCallback(async () => {
try {
setLoading(true);
const data = await api.get('/admin/users');
setUsers(data);
} catch (e) { setErr(e.message); }
finally { setLoading(false); }
}, []);
useEffect(() => { load(); }, [load]);
const toggleRole = (u) => {
const newRole = u.role === 'admin' ? 'user' : 'admin';
setConfirmAction({
title: 'Changer le rôle',
message: `Changer le rôle de ${u.display_name || u.email} → ${newRole === 'admin' ? 'Administrateur' : 'Utilisateur'} ?`,
confirmLabel: 'Confirmer',
onConfirm: async () => {
try {
await api.patch(`/admin/users/${u.id}/role`, { role: newRole });
load();
} catch (e) { setErr('Erreur : ' + e.message); }
finally { setConfirmAction(null); }
},
});
};
const deleteUser = (u) => {
setConfirmAction({
title: 'Supprimer l\'utilisateur',
message: `Supprimer définitivement ${u.display_name || u.email} ? Toutes ses données seront effacées.`,
onConfirm: async () => {
try {
await api.del(`/admin/users/${u.id}`);
load();
} catch (e) { setErr('Erreur : ' + e.message); }
finally { setConfirmAction(null); }
},
});
};
if (loading) return <p style={{ color: 'var(--text-muted)' }}>Chargement…</p>;
if (err) return <p style={{ color: '#ef4444' }}>{err}</p>;
return (
<>
<div className="card">
<h3 style={{ margin: '0 0 4px' }}>Comptes utilisateurs</h3>
<p className="text-muted" style={{ margin: '0 0 20px', fontSize: 'var(--fs-sm)' }}>
{users.length} utilisateur{users.length !== 1 ? 's' : ''} enregistré{users.length !== 1 ? 's' : ''}
</p>
<table>
<thead>
<tr>
<th style={{ width: 36 }}>ID</th>
<th>Nom</th>
<th>Email</th>
<th>Rôle</th>
<th>Créé le</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{users.map(u => (
<tr key={u.id}>
<td style={{ color: 'var(--text-muted)' }}>{u.id}</td>
<td style={{ fontWeight: 500 }}>{u.display_name || <em style={{ color: 'var(--text-muted)' }}>—</em>}</td>
<td>{u.email}</td>
<td><Badge role={u.role} /></td>
<td style={{ color: 'var(--text-muted)', fontSize: 12 }}>{fmt(u.created_at)}</td>
<td>
<div style={{ display: 'flex', gap: 8 }}>
<button
className="btn btn-sm btn-outline"
onClick={() => toggleRole(u)}
disabled={u.id === currentUserId && u.role === 'admin'}
title={u.id === currentUserId ? 'Vous ne pouvez pas vous rétrograder' : ''}
>
{u.role === 'admin' ? '→ Utilisateur' : '→ Admin'}
</button>
{u.id !== currentUserId && (
<button className="btn btn-sm btn-danger" onClick={() => deleteUser(u)}>
Supprimer
</button>
)}
</div>
</td>
</tr>
))}
</tbody>
</table>
</div>
<ConfirmModal
open={!!confirmAction}
title={confirmAction?.title}
message={confirmAction?.message}
confirmLabel={confirmAction?.confirmLabel}
onConfirm={confirmAction?.onConfirm}
onCancel={() => setConfirmAction(null)}
/>
</>
);
}