Checkpoint: Ajout de boutons Modifier et Supprimer sur chaque ligne de la liste des factures. Le bouton Modifier redirige vers la page de détail, le bouton Supprimer affiche une boîte de dialogue de confirmation avant suppression définitive.
This commit is contained in:
@@ -5,6 +5,16 @@ import { Button } from "@/components/ui/button";
|
|||||||
import { Input } from "@/components/ui/input";
|
import { Input } from "@/components/ui/input";
|
||||||
import { Badge } from "@/components/ui/badge";
|
import { Badge } from "@/components/ui/badge";
|
||||||
import { Checkbox } from "@/components/ui/checkbox";
|
import { Checkbox } from "@/components/ui/checkbox";
|
||||||
|
import {
|
||||||
|
AlertDialog,
|
||||||
|
AlertDialogAction,
|
||||||
|
AlertDialogCancel,
|
||||||
|
AlertDialogContent,
|
||||||
|
AlertDialogDescription,
|
||||||
|
AlertDialogFooter,
|
||||||
|
AlertDialogHeader,
|
||||||
|
AlertDialogTitle,
|
||||||
|
} from "@/components/ui/alert-dialog";
|
||||||
import {
|
import {
|
||||||
Table,
|
Table,
|
||||||
TableBody,
|
TableBody,
|
||||||
@@ -14,7 +24,7 @@ import {
|
|||||||
TableRow,
|
TableRow,
|
||||||
} from "@/components/ui/table";
|
} from "@/components/ui/table";
|
||||||
import { trpc } from "@/lib/trpc";
|
import { trpc } from "@/lib/trpc";
|
||||||
import { Search, FileText, Download, FileSpreadsheet, Trash2 } from "lucide-react";
|
import { Search, FileText, Download, FileSpreadsheet, Trash2, Edit, Trash } from "lucide-react";
|
||||||
import * as XLSX from 'xlsx';
|
import * as XLSX from 'xlsx';
|
||||||
import { toast } from "sonner";
|
import { toast } from "sonner";
|
||||||
import { useLocation } from "wouter";
|
import { useLocation } from "wouter";
|
||||||
@@ -24,6 +34,8 @@ export default function Invoices() {
|
|||||||
const [searchQuery, setSearchQuery] = useState("");
|
const [searchQuery, setSearchQuery] = useState("");
|
||||||
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
const [selectedIds, setSelectedIds] = useState<number[]>([]);
|
||||||
const [statusFilter, setStatusFilter] = useState<string>("all");
|
const [statusFilter, setStatusFilter] = useState<string>("all");
|
||||||
|
const [deleteDialogOpen, setDeleteDialogOpen] = useState(false);
|
||||||
|
const [invoiceToDelete, setInvoiceToDelete] = useState<number | null>(null);
|
||||||
const { data: invoices, isLoading } = trpc.invoices.list.useQuery();
|
const { data: invoices, isLoading } = trpc.invoices.list.useQuery();
|
||||||
const utils = trpc.useUtils();
|
const utils = trpc.useUtils();
|
||||||
|
|
||||||
@@ -77,6 +89,18 @@ export default function Invoices() {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const deleteMutation = trpc.invoices.delete.useMutation({
|
||||||
|
onSuccess: () => {
|
||||||
|
toast.success("Facture supprimée avec succès");
|
||||||
|
utils.invoices.list.invalidate();
|
||||||
|
setDeleteDialogOpen(false);
|
||||||
|
setInvoiceToDelete(null);
|
||||||
|
},
|
||||||
|
onError: (error) => {
|
||||||
|
toast.error(error.message || "Erreur lors de la suppression");
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
const filteredInvoices = invoices?.filter((inv) => {
|
const filteredInvoices = invoices?.filter((inv) => {
|
||||||
// Filter by search query
|
// Filter by search query
|
||||||
if (searchQuery) {
|
if (searchQuery) {
|
||||||
@@ -309,6 +333,7 @@ export default function Invoices() {
|
|||||||
<TableHead>Ventilation</TableHead>
|
<TableHead>Ventilation</TableHead>
|
||||||
<TableHead>Score</TableHead>
|
<TableHead>Score</TableHead>
|
||||||
<TableHead>Statut</TableHead>
|
<TableHead>Statut</TableHead>
|
||||||
|
<TableHead className="w-32">Actions</TableHead>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
</TableHeader>
|
</TableHeader>
|
||||||
<TableBody>
|
<TableBody>
|
||||||
@@ -345,6 +370,29 @@ export default function Invoices() {
|
|||||||
<TableCell className="text-sm">{invoice.ventilationComptable || "-"}</TableCell>
|
<TableCell className="text-sm">{invoice.ventilationComptable || "-"}</TableCell>
|
||||||
<TableCell>{getQualityBadge(invoice.qualityScore)}</TableCell>
|
<TableCell>{getQualityBadge(invoice.qualityScore)}</TableCell>
|
||||||
<TableCell>{getExportStatusBadge(invoice.exportStatus || "not_exported")}</TableCell>
|
<TableCell>{getExportStatusBadge(invoice.exportStatus || "not_exported")}</TableCell>
|
||||||
|
<TableCell>
|
||||||
|
<div className="flex gap-2">
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => setLocation(`/invoices/${invoice.id}`)}
|
||||||
|
className="h-8 px-2"
|
||||||
|
>
|
||||||
|
<Edit className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
size="sm"
|
||||||
|
variant="outline"
|
||||||
|
onClick={() => {
|
||||||
|
setInvoiceToDelete(invoice.id);
|
||||||
|
setDeleteDialogOpen(true);
|
||||||
|
}}
|
||||||
|
className="h-8 px-2 text-red-600 hover:text-red-700 hover:bg-red-50"
|
||||||
|
>
|
||||||
|
<Trash className="h-4 w-4" />
|
||||||
|
</Button>
|
||||||
|
</div>
|
||||||
|
</TableCell>
|
||||||
</TableRow>
|
</TableRow>
|
||||||
);
|
);
|
||||||
})}
|
})}
|
||||||
@@ -373,6 +421,31 @@ export default function Invoices() {
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{/* Delete Confirmation Dialog */}
|
||||||
|
<AlertDialog open={deleteDialogOpen} onOpenChange={setDeleteDialogOpen}>
|
||||||
|
<AlertDialogContent>
|
||||||
|
<AlertDialogHeader>
|
||||||
|
<AlertDialogTitle>Êtes-vous sûr de vouloir supprimer cette facture ?</AlertDialogTitle>
|
||||||
|
<AlertDialogDescription>
|
||||||
|
Cette action est irréversible. La facture sera définitivement supprimée de la base de données.
|
||||||
|
</AlertDialogDescription>
|
||||||
|
</AlertDialogHeader>
|
||||||
|
<AlertDialogFooter>
|
||||||
|
<AlertDialogCancel onClick={() => setInvoiceToDelete(null)}>Annuler</AlertDialogCancel>
|
||||||
|
<AlertDialogAction
|
||||||
|
onClick={() => {
|
||||||
|
if (invoiceToDelete) {
|
||||||
|
deleteMutation.mutate({ id: invoiceToDelete });
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
className="bg-red-600 hover:bg-red-700"
|
||||||
|
>
|
||||||
|
Supprimer
|
||||||
|
</AlertDialogAction>
|
||||||
|
</AlertDialogFooter>
|
||||||
|
</AlertDialogContent>
|
||||||
|
</AlertDialog>
|
||||||
</DashboardLayout>
|
</DashboardLayout>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
7
todo.md
7
todo.md
@@ -215,3 +215,10 @@
|
|||||||
- [x] Modifier InvoiceDetail.tsx pour charger les listes depuis la base de données
|
- [x] Modifier InvoiceDetail.tsx pour charger les listes depuis la base de données
|
||||||
- [x] Remplacer les valeurs codées en dur par des appels tRPC
|
- [x] Remplacer les valeurs codées en dur par des appels tRPC
|
||||||
- [x] Tester que les valeurs personnalisées apparaissent dans les menus déroulants
|
- [x] Tester que les valeurs personnalisées apparaissent dans les menus déroulants
|
||||||
|
|
||||||
|
## Boutons Modifier et Supprimer dans la liste des factures
|
||||||
|
- [x] Ajouter une colonne "Actions" dans le tableau des factures
|
||||||
|
- [x] Ajouter un bouton "Modifier" qui redirige vers InvoiceDetail
|
||||||
|
- [x] Ajouter un bouton "Supprimer" avec confirmation (AlertDialog)
|
||||||
|
- [x] Utiliser la route tRPC existante pour supprimer une facture
|
||||||
|
- [x] Tester la suppression avec confirmation
|
||||||
|
|||||||
Reference in New Issue
Block a user