import React, { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Button } from "@/components/ui/button"; import { Label } from "@/components/ui/label"; import { Badge } from "@/components/ui/badge"; import { Card, CardContent } from "@/components/ui/card"; import { Textarea } from "@/components/ui/textarea"; import { useToast } from "@/hooks/use-toast"; import { apiRequest } from "@/lib/queryClient"; import { safeFormat } from "@/lib/dateUtils"; import { MessageSquare, Plus, Edit2, Trash2, Save, X } from "lucide-react"; interface ReconciliationCommentsProps { deliveryId: number; className?: string; } interface ReconciliationComment { id: number; content: string; type: "info" | "warning" | "error" | "success"; deliveryId: number; authorId: string; groupId: number; createdAt: Date; updatedAt: Date; author: { id: string; email: string; role: string; }; } export default function ReconciliationComments({ deliveryId, className = "" }: ReconciliationCommentsProps) { const { toast } = useToast(); const queryClient = useQueryClient(); const [isAdding, setIsAdding] = useState(false); const [editingId, setEditingId] = useState(null); const [newComment, setNewComment] = useState(""); const [editComment, setEditComment] = useState(""); const commentsQueryKey = [`/api/deliveries/${deliveryId}/reconciliation-comments`]; // Récupérer les commentaires const { data: comments = [], isLoading } = useQuery({ queryKey: commentsQueryKey, queryFn: () => apiRequest(`/api/deliveries/${deliveryId}/reconciliation-comments`), enabled: !!deliveryId, }); // Compteur de commentaires affiché dans le rapprochement : mis à jour // localement dans la liste en cache (même clé que BLReconciliation) plutôt // que de retélécharger toute la liste des livraisons pour un compteur. // Les listes qui ne contiennent pas la livraison ne sont pas touchées // (undefined : aucune écriture, leur état « périmé » éventuel est conservé). const adjustCommentsCount = (delta: number) => { queryClient.setQueriesData({ queryKey: ['/api/deliveries', 'reconciliation'] }, (old: unknown) => { if (!Array.isArray(old) || !old.some((delivery: any) => delivery?.id === deliveryId)) { return undefined; } return old.map((delivery: any) => delivery?.id === deliveryId ? { ...delivery, reconciliationCommentsCount: Math.max(0, (Number(delivery.reconciliationCommentsCount) || 0) + delta), } : delivery ); }); }; // Créer un commentaire const createCommentMutation = useMutation({ mutationFn: async (content: string) => { return apiRequest(`/api/deliveries/${deliveryId}/reconciliation-comments`, "POST", { content }); }, onSuccess: () => { toast({ title: "Succès", description: "Commentaire ajouté avec succès", }); queryClient.invalidateQueries({ queryKey: commentsQueryKey }); adjustCommentsCount(1); setIsAdding(false); setNewComment(""); }, onError: (error: any) => { toast({ title: "Erreur", description: error?.message || "Impossible d'ajouter le commentaire", variant: "destructive", }); }, }); // Modifier un commentaire const updateCommentMutation = useMutation({ mutationFn: async ({ id, content }: { id: number; content: string }) => { return apiRequest(`/api/reconciliation-comments/${id}`, "PUT", { content }); }, onSuccess: () => { toast({ title: "Succès", description: "Commentaire modifié avec succès", }); // Le nombre de commentaires ne change pas : la liste des livraisons reste valable queryClient.invalidateQueries({ queryKey: commentsQueryKey }); setEditingId(null); }, onError: (error: any) => { toast({ title: "Erreur", description: error?.message || "Impossible de modifier le commentaire", variant: "destructive", }); }, }); // Supprimer un commentaire const deleteCommentMutation = useMutation({ mutationFn: async (id: number) => { return apiRequest(`/api/reconciliation-comments/${id}`, "DELETE"); }, onSuccess: () => { toast({ title: "Succès", description: "Commentaire supprimé avec succès", }); queryClient.invalidateQueries({ queryKey: commentsQueryKey }); adjustCommentsCount(-1); }, onError: (error: any) => { toast({ title: "Erreur", description: error?.message || "Impossible de supprimer le commentaire", variant: "destructive", }); }, }); const handleAddComment = (e: React.FormEvent) => { e.preventDefault(); if (!newComment.trim()) return; createCommentMutation.mutate(newComment); }; const handleEditComment = (comment: ReconciliationComment) => { setEditingId(comment.id); setEditComment(comment.content); }; const handleSaveEdit = () => { if (!editComment.trim() || !editingId) return; updateCommentMutation.mutate({ id: editingId, content: editComment, }); }; const handleCancelEdit = () => { setEditingId(null); setEditComment(""); }; const handleDeleteComment = (id: number) => { if (confirm("Êtes-vous sûr de vouloir supprimer ce commentaire ?")) { deleteCommentMutation.mutate(id); } }; if (isLoading) { return (
); } return (

Commentaires {comments.length > 0 && ( {comments.length} )}

{/* Formulaire d'ajout */} {isAdding && (