import React, { useState, useEffect } from "react"; import TicketTimeline from "./TicketTimeline"; import MessageCard from "./MessageCard"; type Ticket = { id: string; subject: string; status: "open" | "waiting_client" | "waiting_staff" | "closed" | string; priority: "low" | "normal" | "high" | "urgent" | string; created_at?: string; last_message_at?: string; message_count?: number; unread_by_client?: number; }; type ApiMessage = { id: string; body: string; author_id: string; via: "web" | "staff" | string; internal?: boolean; created_at: string; is_staff?: boolean; }; type DisplayMessage = { id: string; content: string; author_type: "client" | "staff"; author_name?: string; created_at: string; attachments?: Array<{ filename: string; url: string; size?: number; }>; }; interface TicketConversationProps { ticket: Ticket; onClose: () => void; } export default function TicketConversation({ ticket, onClose }: TicketConversationProps) { const [messages, setMessages] = useState([]); const [loading, setLoading] = useState(true); const [error, setError] = useState(null); const [expandedMessages, setExpandedMessages] = useState>(new Set()); const [replyContent, setReplyContent] = useState(""); const [posting, setPosting] = useState(false); // Déterminer qui a envoyé le dernier message const lastMessage = messages[messages.length - 1]; const lastMessageBy = lastMessage?.author_type; useEffect(() => { loadMessages(); }, [ticket.id]); async function loadMessages() { setLoading(true); setError(null); try { const res = await fetch(`/api/tickets/${ticket.id}/messages`, { credentials: "include", cache: "no-store" }); if (!res.ok) throw new Error(await res.text()); const data = await res.json(); // Adapter les données pour MessageCard const adaptedMessages: DisplayMessage[] = (data.items || []).map((msg: ApiMessage) => { const isStaff = msg.is_staff === true || msg.via === "staff"; return { id: msg.id, content: msg.body, author_type: isStaff ? "staff" : "client", author_name: isStaff ? "Support Odentas" : "Vous", created_at: msg.created_at, attachments: [] }; }); setMessages(adaptedMessages); // Marquer le ticket comme lu (pour réinitialiser le compteur de messages non lus) markTicketAsRead(); } catch (e: any) { setError(e?.message || "Erreur de chargement des messages"); } finally { setLoading(false); } } async function markTicketAsRead() { try { await fetch(`/api/tickets/${ticket.id}/read`, { method: "POST", credentials: "include" }); } catch (error) { // Erreur silencieuse, ce n'est pas critique console.warn("Impossible de marquer le ticket comme lu:", error); } } function toggleMessage(messageId: string) { const newExpanded = new Set(expandedMessages); if (newExpanded.has(messageId)) { newExpanded.delete(messageId); } else { newExpanded.add(messageId); } setExpandedMessages(newExpanded); } async function handleReply(e: React.FormEvent) { e.preventDefault(); if (!replyContent.trim()) return; setPosting(true); try { const res = await fetch(`/api/tickets/${ticket.id}/messages`, { method: "POST", credentials: "include", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ body: replyContent.trim() }) }); if (!res.ok) throw new Error(await res.text()); setReplyContent(""); await loadMessages(); // Recharger les messages } catch (e: any) { setError(e?.message || "Erreur lors de l'envoi du message"); } finally { setPosting(false); } } if (loading) { return (
); } return (
{/* En-tête avec bouton fermer */}

{ticket.subject}

Ticket #{ticket.id.slice(0, 8)}

{/* Timeline */} {error && (
{error}
)} {/* Messages */}

Conversation

{messages.length > 0 ? (
{messages.map((message) => ( toggleMessage(message.id)} /> ))}
) : (
Aucun message pour ce ticket
)}
{/* Formulaire de réponse (seulement si le ticket n'est pas fermé) */} {ticket.status !== "closed" && (

Répondre