diff --git a/src/App.tsx b/src/App.tsx index 3b19439..b41ba9c 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -21,7 +21,7 @@ import BackToTop from "./components/BackToTop"; import styles from "./App.module.css"; -import { getDocuments, type ApiDocument } from "./services/theapi"; +import { getDocuments, API_URL, type ApiDocument } from "./services/theapi"; type NavItem = | "home" @@ -104,6 +104,22 @@ export default function App() { : location.pathname === "/admin"? "admin" : "home"; + // Le user de localStorage est un instantané pris au login : un changement de + // rôle en base n'y apparaît jamais. On le resynchronise depuis /me au montage, + // sinon un compte promu admin reste bloqué hors de /admin jusqu'à reconnexion. + useEffect(() => { + const token = localStorage.getItem("authToken"); + if (!token) return; + + fetch(`${API_URL}/me?token=${encodeURIComponent(token)}`) + .then((res) => (res.ok ? res.json() : Promise.reject(res.status))) + .then((freshUser: User) => { + localStorage.setItem("user", JSON.stringify(freshUser)); + setAuthState((prev) => ({ ...prev, user: freshUser })); + }) + .catch((err) => console.error("Rafraîchissement du profil impossible :", err)); + }, []); + useEffect(() => { if (authState.isAuthenticated) { getDocuments() diff --git a/src/pages/Admin.tsx b/src/pages/Admin.tsx index 3cdd67f..eb9db1c 100644 --- a/src/pages/Admin.tsx +++ b/src/pages/Admin.tsx @@ -1,5 +1,6 @@ import React, { useEffect, useState } from 'react' import styles from '../styles/Admin.module.css' +import { API_URL } from '../services/theapi' import { FileText, Database, Users } from 'lucide-react' type User = { id: string; email: string; full_name?: string; role: 'admin' | 'user' } @@ -35,23 +36,23 @@ export default function Admin() { setLoading(true) try{ if(tab==='users'){ - const res = await fetch(`/admin/users?token=${token}`) + const res = await fetch(`${API_URL}/admin/users?token=${token}`) if(!res.ok) throw new Error('Impossible de charger les utilisateurs') const data = await res.json() setUsers(data || []) } else if(tab==='documents'){ - const res = await fetch('/documents') + const res = await fetch(`${API_URL}/documents`) if(!res.ok) throw new Error('Impossible de charger les documents') const data = await res.json() setDocs(data || []) } else if(tab==='stats'){ - const res = await fetch(`/admin/stats?token=${token}`) + const res = await fetch(`${API_URL}/admin/stats?token=${token}`) if(!res.ok) throw new Error('Impossible de charger les statistiques') const data = await res.json() setStats(data) } else if(tab==='scrapers'){ // logs endpoint (fallback) - const res = await fetch(`/admin/scrapers/logs?token=${token}`) + const res = await fetch(`${API_URL}/admin/scrapers/logs?token=${token}`) if(!res.ok) throw new Error('Impossible de charger les logs de scrapers') const data = await res.json() setLogs(data || []) @@ -74,7 +75,7 @@ export default function Admin() { async function deleteUser(id: string){ try{ setLoading(true) - const res = await fetch(`/admin/users/${id}?token=${token}`, { method: 'DELETE' }) + const res = await fetch(`${API_URL}/admin/users/${id}?token=${token}`, { method: 'DELETE' }) if(!res.ok) throw new Error('Suppression impossible') setUsers(u=>u.filter(x=>x.id!==id)) window.dispatchEvent(new CustomEvent('app:toast', { detail: { message: 'Utilisateur supprimé', type: 'success' } })) @@ -89,7 +90,7 @@ export default function Admin() { try{ setLoading(true) const newRole = u.role === 'admin' ? 'user' : 'admin' - const res = await fetch(`/admin/users/${u.id}/role?token=${token}`, { method: 'PATCH', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ role: newRole }) }) + const res = await fetch(`${API_URL}/admin/users/${u.id}/role?token=${token}`, { method: 'PATCH', headers:{'Content-Type':'application/json'}, body: JSON.stringify({ role: newRole }) }) if(!res.ok) throw new Error('Impossible de modifier le rôle') setUsers(list=>list.map(x=> x.id===u.id ? {...x, role:newRole} : x)) window.dispatchEvent(new CustomEvent('app:toast', { detail: { message: `Rôle mis à jour: ${newRole}`, type: 'success' } })) @@ -99,7 +100,7 @@ export default function Admin() { async function deleteDocument(id:string){ try{ setLoading(true) - const res = await fetch(`/admin/documents/${id}?token=${token}`, { method: 'DELETE' }) + const res = await fetch(`${API_URL}/admin/documents/${id}?token=${token}`, { method: 'DELETE' }) if(!res.ok) throw new Error('Suppression impossible') setDocs(d=>d.filter(x=>x.id!==id)) window.dispatchEvent(new CustomEvent('app:toast', { detail: { message: 'Document supprimé', type: 'success' } })) @@ -109,7 +110,7 @@ export default function Admin() { async function runScraper(source: 'CIR'|'SCCS'){ try{ setLoading(true) - const res = await fetch(`/admin/scrapers/run?source=${source}&token=${token}`, { method: 'POST' }) + const res = await fetch(`${API_URL}/admin/scrapers/run?source=${source}&token=${token}`, { method: 'POST' }) if(!res.ok) throw new Error('Erreur lors du lancement') const entry = await res.json() setLogs(l=>[entry, ...l].slice(0,5))