feat: enhance transaction deletion process with optimistic updates, improved error handling, and restore previous data on failure

This commit is contained in:
Julien Froidefond
2025-12-08 08:52:47 +01:00
parent 4224c8aa83
commit 28baf9aa9e
4 changed files with 48 additions and 6 deletions

View File

@@ -123,8 +123,10 @@ export async function DELETE(request: Request) {
return NextResponse.json({ success: true }); return NextResponse.json({ success: true });
} catch (error) { } catch (error) {
console.error("Error deleting transaction:", error); console.error("Error deleting transaction:", error);
const errorMessage =
error instanceof Error ? error.message : "Failed to delete transaction";
return NextResponse.json( return NextResponse.json(
{ error: "Failed to delete transaction" }, { error: errorMessage },
{ status: 500 }, { status: 500 },
); );
} }

View File

@@ -1,5 +1,6 @@
@import "tailwindcss"; @import "tailwindcss";
@import "tw-animate-css"; @import "tw-animate-css";
@import "react-day-picker/style.css";
@custom-variant dark (&:is(.dark *)); @custom-variant dark (&:is(.dark *));

View File

@@ -480,6 +480,23 @@ export default function TransactionsPage() {
newSelected.delete(transactionId); newSelected.delete(transactionId);
setSelectedTransactions(newSelected); setSelectedTransactions(newSelected);
// Sauvegarder les données actuelles pour pouvoir les restaurer en cas d'erreur
const queryKey = getTransactionsQueryKey(transactionParams);
const previousData =
queryClient.getQueryData<typeof transactionsData>(queryKey);
// Mise à jour optimiste du cache
queryClient.setQueryData<typeof transactionsData>(queryKey, (oldData) => {
if (!oldData) return oldData;
return {
...oldData,
transactions: oldData.transactions.filter(
(t) => t.id !== transactionId
),
total: oldData.total - 1,
};
});
try { try {
const response = await fetch( const response = await fetch(
`/api/banking/transactions?id=${transactionId}`, `/api/banking/transactions?id=${transactionId}`,
@@ -487,10 +504,25 @@ export default function TransactionsPage() {
method: "DELETE", method: "DELETE",
} }
); );
if (!response.ok) throw new Error("Failed to delete transaction");
invalidateTransactions(); if (!response.ok) {
const errorData = await response.json().catch(() => ({}));
throw new Error(
errorData.error || `Failed to delete transaction: ${response.status}`
);
}
// Ne pas invalider immédiatement - la mise à jour optimiste est déjà correcte
// On invalide seulement les autres queries qui pourraient être affectées (métadonnées, stats)
queryClient.invalidateQueries({ queryKey: ["banking-metadata"] });
} catch (error) { } catch (error) {
console.error("Failed to delete transaction:", error); console.error("Failed to delete transaction:", error);
// Restaurer les données précédentes en cas d'erreur
if (previousData) {
queryClient.setQueryData(queryKey, previousData);
}
// Invalider pour récupérer les données correctes du serveur
invalidateTransactions();
} }
}; };

View File

@@ -119,9 +119,16 @@ export const transactionService = {
}, },
async delete(id: string): Promise<void> { async delete(id: string): Promise<void> {
try {
await prisma.transaction.delete({ await prisma.transaction.delete({
where: { id }, where: { id },
}); });
} catch (error: any) {
if (error.code === "P2025") {
throw new Error(`Transaction with id ${id} not found`);
}
throw error;
}
}, },
async deduplicate(): Promise<{ async deduplicate(): Promise<{