"use client"; import { FormEvent, useEffect, useMemo, useState } from "react"; import Link from "next/link"; type AdminMessage = { id: number; name: string; email: string; project: string; requestType: string; message: string; createdAt: string; repliedAt: string | null; adminReply: string | null; status: "pending" | "replied"; }; type FilterState = "all" | "pending" | "replied"; export default function AdminPanelClient() { const [isAuthenticated, setIsAuthenticated] = useState(false); const [isChecking, setIsChecking] = useState(true); const [authError, setAuthError] = useState(null); const [login, setLogin] = useState({ username: "", password: "" }); const [messages, setMessages] = useState([]); const [loadingMessages, setLoadingMessages] = useState(false); const [replyDrafts, setReplyDrafts] = useState>({}); const [replyingId, setReplyingId] = useState(null); const [deletingId, setDeletingId] = useState(null); const [statusUpdatingId, setStatusUpdatingId] = useState(null); const [activeFilter, setActiveFilter] = useState("all"); async function checkSession() { setIsChecking(true); try { const response = await fetch("/api/admin/me"); setIsAuthenticated(response.ok); } finally { setIsChecking(false); } } useEffect(() => { checkSession(); }, []); async function loadMessages() { setLoadingMessages(true); try { const response = await fetch("/api/admin/messages"); if (!response.ok) { if (response.status === 401) { setIsAuthenticated(false); return; } throw new Error("Impossible de charger les messages."); } const data = (await response.json()) as { messages: AdminMessage[] }; setMessages(data.messages); const initialDrafts = Object.fromEntries( data.messages.map((item) => [item.id, item.adminReply ?? ""]), ) as Record; setReplyDrafts(initialDrafts); } catch (error) { setAuthError(error instanceof Error ? error.message : "Erreur de chargement."); } finally { setLoadingMessages(false); } } useEffect(() => { if (isAuthenticated) { loadMessages(); } }, [isAuthenticated]); async function submitLogin(event: FormEvent) { event.preventDefault(); setAuthError(null); const response = await fetch("/api/admin/login", { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(login), }); if (!response.ok) { const payload = (await response.json().catch(() => null)) as { message?: string } | null; setAuthError(payload?.message ?? "Connexion impossible."); return; } setIsAuthenticated(true); setLogin({ username: "", password: "" }); await loadMessages(); } async function logout() { await fetch("/api/admin/logout", { method: "POST" }); setIsAuthenticated(false); setMessages([]); } async function sendReply(id: number) { const reply = (replyDrafts[id] || "").trim(); if (reply.length < 5) { setAuthError("La reponse doit contenir au moins 5 caracteres."); return; } setReplyingId(id); setAuthError(null); try { const response = await fetch(`/api/admin/messages/${id}/reply`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ reply }), }); if (!response.ok) { const payload = (await response.json().catch(() => null)) as { message?: string } | null; throw new Error(payload?.message ?? "Impossible d'envoyer la reponse."); } await loadMessages(); } catch (error) { setAuthError(error instanceof Error ? error.message : "Erreur d'envoi."); } finally { setReplyingId(null); } } async function toggleStatus(item: AdminMessage) { const nextStatus = item.status === "pending" ? "replied" : "pending"; setStatusUpdatingId(item.id); setAuthError(null); try { const response = await fetch(`/api/admin/messages/${item.id}/status`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ status: nextStatus }), }); if (!response.ok) { const payload = (await response.json().catch(() => null)) as { message?: string } | null; throw new Error(payload?.message ?? "Impossible de changer le statut."); } await loadMessages(); } catch (error) { setAuthError(error instanceof Error ? error.message : "Erreur de statut."); } finally { setStatusUpdatingId(null); } } async function removeMessage(id: number) { const confirmed = window.confirm("Supprimer ce message ? Cette action est irreversible."); if (!confirmed) { return; } setDeletingId(id); setAuthError(null); try { const response = await fetch(`/api/admin/messages/${id}`, { method: "DELETE", }); if (!response.ok) { const payload = (await response.json().catch(() => null)) as { message?: string } | null; throw new Error(payload?.message ?? "Suppression impossible."); } await loadMessages(); } catch (error) { setAuthError(error instanceof Error ? error.message : "Erreur de suppression."); } finally { setDeletingId(null); } } const stats = useMemo(() => { const total = messages.length; const replied = messages.filter((item) => item.status === "replied").length; return { total, replied, pending: total - replied }; }, [messages]); const filteredMessages = useMemo(() => { if (activeFilter === "all") { return messages; } return messages.filter((item) => item.status === activeFilter); }, [messages, activeFilter]); if (isChecking) { return
Verification de session...
; } if (!isAuthenticated) { return (
Retour a l'accueil

Connexion admin

Connecte-toi pour voir les messages et repondre depuis le tableau de bord.

{authError ?

{authError}

: null}
); } return (

Espace admin

Gestion des messages de contact

Retour a l'accueil
{loadingMessages ? (
Chargement...
) : null} {filteredMessages.map((item) => (

#{item.id} - {new Date(item.createdAt).toLocaleString("fr-FR")}

{item.name} ({item.email})

{item.project} - {item.requestType}

{item.status === "replied" ? ( Repondu ) : ( En attente )}

{item.message}