diff --git a/client/src/pages/Avoirs.tsx b/client/src/pages/Avoirs.tsx index 9516c49..8871368 100644 --- a/client/src/pages/Avoirs.tsx +++ b/client/src/pages/Avoirs.tsx @@ -1,11 +1,12 @@ import { useState, useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; -import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload, XCircle, MessageSquare } from "lucide-react"; +import { Plus, Search, FileText, CheckCircle, AlertCircle, Clock, Edit, Trash2, UserCheck, Send, Upload, XCircle, MessageSquare, Settings } from "lucide-react"; import { useStore } from "@/components/Layout"; import { Button } from "@/components/ui/button"; import { Input } from "@/components/ui/input"; import { Card, CardContent, CardDescription, CardHeader, CardTitle } from "@/components/ui/card"; import { Badge } from "@/components/ui/badge"; +import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Dialog, DialogContent, DialogDescription, DialogHeader, DialogTitle, DialogTrigger, DialogFooter } from "@/components/ui/dialog"; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from "@/components/ui/alert-dialog"; import { Label } from "@/components/ui/label"; @@ -108,6 +109,7 @@ type AvoirUpdateData = AvoirFormData & { }; export default function Avoirs() { + const [activeTab, setActiveTab] = useState("pending"); const [searchTerm, setSearchTerm] = useState(""); const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false); const [isEditDialogOpen, setIsEditDialogOpen] = useState(false); @@ -872,45 +874,364 @@ export default function Avoirs() { } }; + // Séparer les avoirs par statut + const pendingAvoirs = avoirs.filter(avoir => avoir.status !== 'Reçu'); + const completedAvoirs = avoirs.filter(avoir => avoir.status === 'Reçu'); + + // Filtrage par recherche + const filterAvoirs = (avoirsList: Avoir[]) => { + return avoirsList.filter((avoir) => { + const searchLower = searchTerm.toLowerCase(); + return ( + avoir.supplier?.name?.toLowerCase().includes(searchLower) || + avoir.invoiceReference?.toLowerCase().includes(searchLower) || + avoir.comment?.toLowerCase().includes(searchLower) + ); + }); + }; + + const filteredPendingAvoirs = filterAvoirs(pendingAvoirs); + const filteredCompletedAvoirs = filterAvoirs(completedAvoirs); + + const canEditDelete = ['admin', 'directeur'].includes((user as any)?.role); + + // Fonction pour rendre le tableau d'avoirs + const renderAvoirTable = (avoirsList: Avoir[]) => { + if (isLoading) { + return ( +
+
+ {Array.from({ length: 3 }).map((_, i) => ( +
+ ))} +
+
+ ); + } + + if (avoirsList.length === 0) { + return ( +
+ +

Aucun avoir

+

+ {searchTerm ? "Aucun avoir ne correspond à votre recherche." : "Aucun avoir dans cette catégorie."} +

+
+ ); + } + + return ( +
+ + + + + + + + + + + + + + + {avoirsList?.filter(avoir => avoir && avoir.id).map((avoir) => ( + + + + + + + + + + + ))} + +
+ Fournisseur + + Statut + + Référence + + Magasin + + Montant + + Créé le + + Vérification + + Actions +
+ {avoir.supplier?.name || 'Fournisseur non défini'} + + + + #{avoir.invoiceReference || 'Sans référence'} + +
+
+ {avoir.group?.name || 'Magasin non défini'} +
+
+
+ {avoir.amount !== null && avoir.amount !== undefined ? `${Number(avoir.amount).toFixed(2)} €` : 'Non spécifié'} + {avoir.commercialProcessed && ( + + )} +
+
+ {format(new Date(avoir.createdAt), 'dd/MM/yyyy HH:mm', { locale: fr })} + + {(avoirVerificationResults[avoir.id]?.exists === true || avoir.nocodbVerified) ? ( +
+ + Validé +
+ ) : avoir.invoiceReference && avoir.invoiceReference.trim() !== '' ? ( +
+ {verifyingAvoirs.has(avoir.id) ? ( +
+ ) : ( + + )} +
+ ) : ( + Sans réf. + )} +
+
+ {/* Upload pour avoirs "Reçu" */} + {avoir?.status === 'Reçu' && ( + + )} + + {/* Bouton Valider */} + {avoirVerificationResults[avoir.id]?.exists === true && !avoir.nocodbVerified && ( + + )} + + {/* Bouton Dévalider */} + {avoir.nocodbVerified && canEditDelete && ( + + )} + + {/* Bouton Commentaire */} + {avoir.comment && avoir.comment.trim() !== '' && ( + + )} + + {canEditDelete && ( + <> + + + + )} + {!canEditDelete && avoir?.status !== 'Reçu' && ( + Lecture seule + )} +
+
+
+ ); + }; + if (!user) { return
Chargement...
; } return ( -
-
-
-

Avoirs

-

- Gestion des demandes d'avoirs et de remboursements -

-
- -
- {/* Bouton Vérifier tous si des avoirs avec référence existent */} - {avoirs.some(avoir => avoir.invoiceReference?.trim()) && ( - - )} - - - - - - + )} + +
- - - + + {/* Onglets */} + + + + + En cours + + {pendingAvoirs.length} + + + + + Finalisés + + {completedAvoirs.length} + + + + +
+ + {/* Filters */} +
+
+
+
+ + setSearchTerm(e.target.value)} + className="pl-10 border border-gray-300 shadow-sm w-full" + /> +
+
+
+
+ + {/* Contenu des onglets */} + + + + + + Avoirs en cours + {filteredPendingAvoirs.length} éléments + + + Avoirs en attente de demande ou en cours de traitement + + + + {renderAvoirTable(filteredPendingAvoirs)} + + + + + + + + + Avoirs finalisés + {filteredCompletedAvoirs.length} éléments + + + Avoirs reçus et traités + + + + {renderAvoirTable(filteredCompletedAvoirs)} + + + + + + {/* Modal de création */} + + Créer un nouvel avoir @@ -1080,10 +1401,297 @@ export default function Avoirs() { - - {/* Filters and Search */} -
+ {/* Modal d'édition */} + + + + Modifier l'avoir + + Modifiez les informations de cet avoir + + + +
+ + ( + + Fournisseur + + + + )} + /> + + ( + + Magasin + + + + )} + /> + + ( + + Référence facture + + + + + + )} + /> + +
+ + setEditAmountValue(e.target.value)} + /> +
+ + ( + + Commentaire + +