Simplify comment system by removing type and message functionality

Remove comment type, associated icons, labels, and related UI elements. Update API calls and database schema to reflect these changes, simplifying the comment submission and management process.

Replit-Commit-Author: Agent
Replit-Commit-Session-Id: 95e8aab4-f39f-4b48-aa57-a759ec15ba3d
Replit-Commit-Checkpoint-Type: full_checkpoint
Replit-Commit-Screenshot-Url: https://storage.googleapis.com/screenshot-production-us-central1/1957c339-2757-4d1f-8e92-e9f71a1ce58e/95e8aab4-f39f-4b48-aa57-a759ec15ba3d/2QB13hj
This commit is contained in:
michaelschal committed 2025-09-10 09:31:05 +00:00
1 parent edbf082c8d
commit deb01d1a81
4 files changed
+54 -140

No files matched your search

+53 -136
View File
@@ -1,16 +1,14 @@
import React, { useState } from "react"; import React, { useState } from "react";
import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query";
import { Button } from "@/components/ui/button"; import { Button } from "@/components/ui/button";
import { Input } from "@/components/ui/input";
import { Label } from "@/components/ui/label"; import { Label } from "@/components/ui/label";
import { Badge } from "@/components/ui/badge"; import { Badge } from "@/components/ui/badge";
import { Card, CardContent, CardHeader, CardTitle } from "@/components/ui/card"; import { Card, CardContent } from "@/components/ui/card";
import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select";
import { Textarea } from "@/components/ui/textarea"; import { Textarea } from "@/components/ui/textarea";
import { useToast } from "@/hooks/use-toast"; import { useToast } from "@/hooks/use-toast";
import { apiRequest } from "@/lib/queryClient"; import { apiRequest } from "@/lib/queryClient";
import { safeFormat } from "@/lib/dateUtils"; import { safeFormat } from "@/lib/dateUtils";
import { MessageSquare, Plus, Edit2, Trash2, Save, X, AlertCircle, CheckCircle, AlertTriangle } from "lucide-react"; import { MessageSquare, Plus, Edit2, Trash2, Save, X } from "lucide-react";
interface ReconciliationCommentsProps { interface ReconciliationCommentsProps {
deliveryId: number; deliveryId: number;
@@ -20,7 +18,6 @@ interface ReconciliationCommentsProps {
interface ReconciliationComment { interface ReconciliationComment {
id: number; id: number;
content: string; content: string;
type: 'warning' | 'error' | 'success';
deliveryId: number; deliveryId: number;
authorId: string; authorId: string;
groupId: number; groupId: number;
@@ -33,44 +30,14 @@ interface ReconciliationComment {
}; };
} }
const typeColors = {
warning: "bg-yellow-50 border-yellow-200 text-yellow-800",
error: "bg-red-50 border-red-200 text-red-800",
success: "bg-green-50 border-green-200 text-green-800",
};
const typeIcons = {
warning: AlertTriangle,
error: AlertCircle,
success: CheckCircle,
};
const typeLabels = {
warning: "Attention",
error: "Erreur",
success: "Succès",
};
export default function ReconciliationComments({ deliveryId, className = "" }: ReconciliationCommentsProps) { export default function ReconciliationComments({ deliveryId, className = "" }: ReconciliationCommentsProps) {
const { toast } = useToast(); const { toast } = useToast();
const queryClient = useQueryClient(); const queryClient = useQueryClient();
const [isAdding, setIsAdding] = useState(false); const [isAdding, setIsAdding] = useState(false);
const [editingId, setEditingId] = useState<number | null>(null); const [editingId, setEditingId] = useState<number | null>(null);
const [newComment, setNewComment] = useState<{ const [newComment, setNewComment] = useState("");
content: string; const [editComment, setEditComment] = useState("");
type: 'warning' | 'error' | 'success';
}>({
content: "",
type: "warning",
});
const [editComment, setEditComment] = useState<{
content: string;
type: 'warning' | 'error' | 'success';
}>({
content: "",
type: "warning",
});
// Récupérer les commentaires // Récupérer les commentaires
const { data: comments = [], isLoading } = useQuery({ const { data: comments = [], isLoading } = useQuery({
@@ -81,8 +48,8 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
// Créer un commentaire // Créer un commentaire
const createCommentMutation = useMutation({ const createCommentMutation = useMutation({
mutationFn: async (data: { content: string; type: string }) => { mutationFn: async (content: string) => {
return apiRequest(`/api/deliveries/${deliveryId}/reconciliation-comments`, "POST", data); return apiRequest(`/api/deliveries/${deliveryId}/reconciliation-comments`, "POST", { content });
}, },
onSuccess: () => { onSuccess: () => {
toast({ toast({
@@ -91,7 +58,7 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
}); });
queryClient.invalidateQueries({ queryKey: [`/api/deliveries/${deliveryId}/reconciliation-comments`] }); queryClient.invalidateQueries({ queryKey: [`/api/deliveries/${deliveryId}/reconciliation-comments`] });
setIsAdding(false); setIsAdding(false);
setNewComment({ content: "", type: "warning" }); setNewComment("");
}, },
onError: (error: any) => { onError: (error: any) => {
toast({ toast({
@@ -104,8 +71,8 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
// Modifier un commentaire // Modifier un commentaire
const updateCommentMutation = useMutation({ const updateCommentMutation = useMutation({
mutationFn: async ({ id, data }: { id: number; data: { content: string; type: string } }) => { mutationFn: async ({ id, content }: { id: number; content: string }) => {
return apiRequest(`/api/reconciliation-comments/${id}`, "PUT", data); return apiRequest(`/api/reconciliation-comments/${id}`, "PUT", { content });
}, },
onSuccess: () => { onSuccess: () => {
toast({ toast({
@@ -147,31 +114,28 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
const handleAddComment = (e: React.FormEvent) => { const handleAddComment = (e: React.FormEvent) => {
e.preventDefault(); e.preventDefault();
if (!newComment.content.trim()) return; if (!newComment.trim()) return;
createCommentMutation.mutate(newComment); createCommentMutation.mutate(newComment);
}; };
const handleEditComment = (comment: ReconciliationComment) => { const handleEditComment = (comment: ReconciliationComment) => {
setEditingId(comment.id); setEditingId(comment.id);
setEditComment({ setEditComment(comment.content);
content: comment.content,
type: comment.type,
});
}; };
const handleSaveEdit = () => { const handleSaveEdit = () => {
if (!editComment.content.trim() || !editingId) return; if (!editComment.trim() || !editingId) return;
updateCommentMutation.mutate({ updateCommentMutation.mutate({
id: editingId, id: editingId,
data: editComment, content: editComment,
}); });
}; };
const handleCancelEdit = () => { const handleCancelEdit = () => {
setEditingId(null); setEditingId(null);
setEditComment({ content: "", type: "warning" }); setEditComment("");
}; };
const handleDeleteComment = (id: number) => { const handleDeleteComment = (id: number) => {
@@ -194,7 +158,7 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
<div className="flex items-center justify-between mb-4"> <div className="flex items-center justify-between mb-4">
<h4 className="font-medium text-gray-900 flex items-center"> <h4 className="font-medium text-gray-900 flex items-center">
<MessageSquare className="w-4 h-4 mr-2" /> <MessageSquare className="w-4 h-4 mr-2" />
Commentaires de rapprochement Commentaires
{comments.length > 0 && ( {comments.length > 0 && (
<Badge variant="secondary" className="ml-2"> <Badge variant="secondary" className="ml-2">
{comments.length} {comments.length}
@@ -202,52 +166,31 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
)} )}
</h4> </h4>
{!isAdding && ( <Button
<Button onClick={() => setIsAdding(true)}
size="sm" size="sm"
variant="outline" variant="outline"
onClick={() => setIsAdding(true)} className="text-xs"
> >
<Plus className="w-4 h-4 mr-1" /> <Plus className="w-3 h-3 mr-1" />
Ajouter Ajouter
</Button> </Button>
)}
</div> </div>
{/* Formulaire d'ajout */} {/* Formulaire d'ajout */}
{isAdding && ( {isAdding && (
<Card className="mb-4 border-dashed"> <Card className="mb-4 border-blue-200 bg-blue-50">
<CardHeader className="pb-2"> <CardContent className="p-4">
<CardTitle className="text-sm text-gray-600">Nouveau commentaire</CardTitle>
</CardHeader>
<CardContent>
<form onSubmit={handleAddComment} className="space-y-3"> <form onSubmit={handleAddComment} className="space-y-3">
<div> <div>
<Label htmlFor="comment-type">Type</Label> <Label htmlFor="new-comment">Nouveau commentaire</Label>
<Select
value={newComment.type}
onValueChange={(value: any) => setNewComment(prev => ({ ...prev, type: value }))}
>
<SelectTrigger id="comment-type">
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="warning">Attention</SelectItem>
<SelectItem value="error">Erreur</SelectItem>
<SelectItem value="success">Succès</SelectItem>
</SelectContent>
</Select>
</div>
<div>
<Label htmlFor="comment-content">Commentaire</Label>
<Textarea <Textarea
id="comment-content" id="new-comment"
value={newComment.content} value={newComment}
onChange={(e) => setNewComment(prev => ({ ...prev, content: e.target.value }))} onChange={(e) => setNewComment(e.target.value)}
placeholder="Saisissez votre commentaire..." placeholder="Tapez votre commentaire..."
rows={3} rows={3}
required
/> />
</div> </div>
@@ -258,18 +201,18 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
size="sm" size="sm"
onClick={() => { onClick={() => {
setIsAdding(false); setIsAdding(false);
setNewComment({ content: "", type: "warning" }); setNewComment("");
}} }}
> >
<X className="w-4 h-4 mr-1" /> <X className="w-3 h-3 mr-1" />
Annuler Annuler
</Button> </Button>
<Button <Button
type="submit" type="submit"
size="sm" size="sm"
disabled={!newComment.content.trim() || createCommentMutation.isPending} disabled={createCommentMutation.isPending || !newComment.trim()}
> >
<Save className="w-4 h-4 mr-1" /> <Save className="w-3 h-3 mr-1" />
{createCommentMutation.isPending ? "Ajout..." : "Ajouter"} {createCommentMutation.isPending ? "Ajout..." : "Ajouter"}
</Button> </Button>
</div> </div>
@@ -287,38 +230,19 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
</div> </div>
) : ( ) : (
comments.map((comment: ReconciliationComment) => { comments.map((comment: ReconciliationComment) => {
const TypeIcon = typeIcons[comment.type];
const isEditing = editingId === comment.id; const isEditing = editingId === comment.id;
return ( return (
<Card key={comment.id} className={`border ${typeColors[comment.type]}`}> <Card key={comment.id} className="border">
<CardContent className="p-4"> <CardContent className="p-4">
{isEditing ? ( {isEditing ? (
<div className="space-y-3"> <div className="space-y-3">
<div>
<Label htmlFor={`edit-type-${comment.id}`}>Type</Label>
<Select
value={editComment.type}
onValueChange={(value: any) => setEditComment(prev => ({ ...prev, type: value }))}
>
<SelectTrigger id={`edit-type-${comment.id}`}>
<SelectValue />
</SelectTrigger>
<SelectContent>
<SelectItem value="warning">Attention</SelectItem>
<SelectItem value="error">Erreur</SelectItem>
<SelectItem value="success">Succès</SelectItem>
</SelectContent>
</Select>
</div>
<div> <div>
<Label htmlFor={`edit-content-${comment.id}`}>Commentaire</Label> <Label htmlFor={`edit-content-${comment.id}`}>Commentaire</Label>
<Textarea <Textarea
id={`edit-content-${comment.id}`} id={`edit-content-${comment.id}`}
value={editComment.content} value={editComment}
onChange={(e) => setEditComment(prev => ({ ...prev, content: e.target.value }))} onChange={(e) => setEditComment(e.target.value)}
rows={3} rows={3}
/> />
</div> </div>
@@ -330,56 +254,49 @@ export default function ReconciliationComments({ deliveryId, className = "" }: R
size="sm" size="sm"
onClick={handleCancelEdit} onClick={handleCancelEdit}
> >
<X className="w-4 h-4 mr-1" /> <X className="w-3 h-3 mr-1" />
Annuler Annuler
</Button> </Button>
<Button <Button
type="button" type="button"
size="sm" size="sm"
onClick={handleSaveEdit} onClick={handleSaveEdit}
disabled={!editComment.content.trim() || updateCommentMutation.isPending} disabled={updateCommentMutation.isPending || !editComment.trim()}
> >
<Save className="w-4 h-4 mr-1" /> <Save className="w-3 h-3 mr-1" />
{updateCommentMutation.isPending ? "Sauvegarde..." : "Sauvegarder"} {updateCommentMutation.isPending ? "Enregistrement..." : "Enregistrer"}
</Button> </Button>
</div> </div>
</div> </div>
) : ( ) : (
<div> <div>
<div className="flex items-start justify-between mb-2"> <div className="flex items-start justify-between mb-2">
<div className="flex items-center space-x-2"> <div className="flex-1">
<TypeIcon className="w-4 h-4" /> <p className="text-gray-900 mb-2">{comment.content}</p>
<Badge variant="outline"> <div className="text-xs text-gray-500">
{typeLabels[comment.type]} Par {comment.author.email} • {safeFormat(comment.createdAt, "dd/MM/yyyy 'à' HH:mm")}
</Badge> </div>
</div> </div>
<div className="flex items-center space-x-1"> <div className="flex space-x-1 ml-2">
<Button <Button
size="sm"
variant="ghost" variant="ghost"
size="sm"
onClick={() => handleEditComment(comment)} onClick={() => handleEditComment(comment)}
className="h-6 w-6 p-0" className="h-8 w-8 p-0"
> >
<Edit2 className="w-3 h-3" /> <Edit2 className="w-3 h-3" />
</Button> </Button>
<Button <Button
size="sm"
variant="ghost" variant="ghost"
size="sm"
onClick={() => handleDeleteComment(comment.id)} onClick={() => handleDeleteComment(comment.id)}
className="h-6 w-6 p-0 text-red-500 hover:text-red-700" className="h-8 w-8 p-0 text-red-600 hover:text-red-700"
> >
<Trash2 className="w-3 h-3" /> <Trash2 className="w-3 h-3" />
</Button> </Button>
</div> </div>
</div> </div>
<p className="text-sm mb-2">{comment.content}</p>
<div className="text-xs text-gray-500 flex items-center justify-between">
<span>Par {comment.author.email}</span>
<span>{safeFormat(new Date(comment.createdAt), 'dd/MM/yyyy HH:mm')}</span>
</div>
</div> </div>
)} )}
</CardContent> </CardContent>
+1 -2
View File
@@ -147,8 +147,7 @@ export default function BLReconciliation() {
const createCommentMutation = useMutation({ const createCommentMutation = useMutation({
mutationFn: async ({ deliveryId, content }: { deliveryId: number; content: string }) => { mutationFn: async ({ deliveryId, content }: { deliveryId: number; content: string }) => {
const response = await apiRequest(`/api/deliveries/${deliveryId}/reconciliation-comments`, 'POST', { const response = await apiRequest(`/api/deliveries/${deliveryId}/reconciliation-comments`, 'POST', {
content, content
type: 'warning'
}); });
return response; return response;
}, },
-1
View File
@@ -2585,7 +2585,6 @@ export class DatabaseStorage implements IStorage {
deliveryId: reconciliationComments.deliveryId, deliveryId: reconciliationComments.deliveryId,
groupId: reconciliationComments.groupId, groupId: reconciliationComments.groupId,
content: reconciliationComments.content, content: reconciliationComments.content,
type: reconciliationComments.type,
authorId: reconciliationComments.authorId, authorId: reconciliationComments.authorId,
createdAt: reconciliationComments.createdAt, createdAt: reconciliationComments.createdAt,
updatedAt: reconciliationComments.updatedAt, updatedAt: reconciliationComments.updatedAt,
-1
View File
@@ -183,7 +183,6 @@ export const reconciliationComments = pgTable("reconciliation_comments", {
deliveryId: integer("delivery_id").notNull(), // Référence vers la livraison concernée deliveryId: integer("delivery_id").notNull(), // Référence vers la livraison concernée
groupId: integer("group_id").notNull(), // Magasin/groupe pour filtrage groupId: integer("group_id").notNull(), // Magasin/groupe pour filtrage
content: text("content").notNull(), // Contenu du commentaire content: text("content").notNull(), // Contenu du commentaire
type: varchar("type", { length: 50 }).notNull().default("warning"), // warning, error, success
authorId: varchar("author_id").notNull(), // Utilisateur auteur du commentaire authorId: varchar("author_id").notNull(), // Utilisateur auteur du commentaire
createdAt: timestamp("created_at").defaultNow(), createdAt: timestamp("created_at").defaultNow(),
updatedAt: timestamp("updated_at").defaultNow(), updatedAt: timestamp("updated_at").defaultNow(),