Initial commit: Club DynaSQY
This commit is contained in:
@@ -0,0 +1,91 @@
|
||||
import { useState, useEffect } from 'react'
|
||||
import { useAuth } from '../context/AuthContext'
|
||||
|
||||
const ACTION_LABELS = {
|
||||
connexion: 'Connexion',
|
||||
changement_mot_de_passe: 'Changement mot de passe',
|
||||
recommandation_ajout: 'Recommandation ajoutée',
|
||||
recommandation_modification: 'Recommandation modifiée',
|
||||
recommandation_suppression: 'Recommandation supprimée',
|
||||
invite_ajout: 'Invité ajouté',
|
||||
invite_modification: 'Invité modifié',
|
||||
invite_suppression: 'Invité supprimé',
|
||||
invite_email_envoye: 'Email invitation envoyé',
|
||||
candidature_demarrage: 'Candidature démarrée',
|
||||
candidature_refus: 'Candidature refusée',
|
||||
absence_ajout: 'Absence ajoutée',
|
||||
absence_suppression: 'Absence supprimée',
|
||||
}
|
||||
|
||||
export default function AdminLogs() {
|
||||
const { authFetch } = useAuth()
|
||||
const [logs, setLogs] = useState([])
|
||||
const [page, setPage] = useState(1)
|
||||
const [pages, setPages] = useState(1)
|
||||
const [loading, setLoading] = useState(true)
|
||||
|
||||
const load = async (p) => {
|
||||
setLoading(true)
|
||||
setPage(p)
|
||||
try {
|
||||
const r = await authFetch(`/api/activity-logs?page=${p}&limit=50`)
|
||||
if (r.ok) { const d = await r.json(); setLogs(d.logs); setPages(d.pages) }
|
||||
} catch {} finally { setLoading(false) }
|
||||
}
|
||||
|
||||
useEffect(() => { load(1) }, [])
|
||||
|
||||
return (
|
||||
<div className="space-y-4">
|
||||
<h2 className="text-xl font-bold text-gray-800">Journal d'activité</h2>
|
||||
|
||||
{loading ? (
|
||||
<p className="text-center text-gray-400 py-8">Chargement...</p>
|
||||
) : logs.length === 0 ? (
|
||||
<p className="text-center text-gray-400 py-8">Aucune activité enregistrée</p>
|
||||
) : (
|
||||
<div className="bg-white rounded-xl shadow-sm overflow-x-auto">
|
||||
<table className="w-full text-sm">
|
||||
<thead>
|
||||
<tr className="bg-gray-50 text-left text-xs text-gray-500 uppercase">
|
||||
<th className="px-4 py-3">Date</th>
|
||||
<th className="px-4 py-3">Utilisateur</th>
|
||||
<th className="px-4 py-3">Action</th>
|
||||
<th className="px-4 py-3">Détails</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody className="divide-y">
|
||||
{logs.map((log) => (
|
||||
<tr key={log.id} className="hover:bg-gray-50">
|
||||
<td className="px-4 py-2.5 text-gray-500 whitespace-nowrap">
|
||||
{new Date(log.created_at).toLocaleDateString('fr-FR', { day: '2-digit', month: '2-digit', year: 'numeric', hour: '2-digit', minute: '2-digit' })}
|
||||
</td>
|
||||
<td className="px-4 py-2.5 whitespace-nowrap font-medium">{log.user_prenom} {log.user_nom}</td>
|
||||
<td className="px-4 py-2.5 whitespace-nowrap">
|
||||
<span className="bg-blue-100 text-blue-800 text-xs font-medium px-2 py-0.5 rounded">
|
||||
{ACTION_LABELS[log.action] || log.action}
|
||||
</span>
|
||||
</td>
|
||||
<td className="px-4 py-2.5 text-gray-600 max-w-xs truncate">
|
||||
{log.details ? (() => { try { return JSON.stringify(JSON.parse(log.details), null, 1).replace(/[{}"]/g, '').replace(/,/g, ', ').trim() } catch { return log.details } })() : '-'}
|
||||
</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
)}
|
||||
|
||||
{pages > 1 && (
|
||||
<div className="flex justify-center gap-2">
|
||||
{Array.from({ length: pages }, (_, i) => i + 1).map((p) => (
|
||||
<button key={p} onClick={() => load(p)}
|
||||
className={`w-8 h-8 rounded-lg text-sm font-medium ${p === page ? 'bg-blue-900 text-white' : 'bg-white text-gray-600 border hover:bg-gray-50'}`}>
|
||||
{p}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
)
|
||||
}
|
||||
Reference in New Issue
Block a user