mirror of
https://github.com/R0m1k3/LogiFlow.git
synced 2026-10-11 17:27:31 +02:00
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:
1 parent
edbf082c8d
commit
deb01d1a81
4 files changed
+54
-140
No files matched your search
@@ -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>
|
||||||
|
|||||||
@@ -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;
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(),
|
||||||
|
|||||||
Reference in new issue
Block a user