mirror of
https://github.com/escalante29/WealthySmart.git
synced 2026-07-17 10:28:47 +02:00
Mutation feedback: toasts on transaction save/delete, confirm override clear
Transaction create/update/delete now confirm success or failure with a toast (UX-01); save failures always show a user-visible message instead of console-only (FE-06, with a typed error guard replacing the any cast). Clearing a balance override — which recalculates every following month — now requires confirmation like the other destructive actions (UX-15). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
@@ -11,6 +11,8 @@ import {
|
||||
Banknote,
|
||||
} from 'lucide-react';
|
||||
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import api, { type Transaction } from '@/lib/api';
|
||||
import TransactionModal from './TransactionModal';
|
||||
import ConfirmDialog from './ConfirmDialog';
|
||||
@@ -66,8 +68,11 @@ export default function TransactionList({
|
||||
setDeleting(true);
|
||||
try {
|
||||
await api.delete(`/transactions/${deleteId}`);
|
||||
toast.success('Transacción eliminada');
|
||||
setDeleteId(null);
|
||||
onRefresh();
|
||||
} catch {
|
||||
toast.error('No se pudo eliminar la transacción');
|
||||
} finally {
|
||||
setDeleting(false);
|
||||
}
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { toast } from 'sonner';
|
||||
|
||||
import api, { type Category, type Transaction } from '@/lib/api';
|
||||
import { formatLocalDatetime } from '@/lib/format';
|
||||
import { Button } from '@/components/ui/button';
|
||||
@@ -92,12 +94,18 @@ export default function TransactionModal({ transaction, source, onClose, onSaved
|
||||
} else {
|
||||
await api.post('/transactions/', payload);
|
||||
}
|
||||
toast.success(transaction ? 'Transacción actualizada' : 'Transacción creada');
|
||||
onSaved();
|
||||
onClose();
|
||||
} catch (err: any) {
|
||||
if (err.response?.status === 409) {
|
||||
} catch (err) {
|
||||
const status =
|
||||
err && typeof err === 'object' && 'response' in err
|
||||
? (err as { response: { status: number } }).response.status
|
||||
: null;
|
||||
if (status === 409) {
|
||||
setError('Duplicate transaction: a transaction with this reference already exists.');
|
||||
} else {
|
||||
setError('No se pudo guardar la transacción.');
|
||||
console.error(err);
|
||||
}
|
||||
} finally {
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
import ConfirmDialog from '@/components/ConfirmDialog';
|
||||
import { useState, useRef, useEffect } from 'react';
|
||||
import { Pencil } from 'lucide-react';
|
||||
|
||||
@@ -42,6 +43,7 @@ export default function YearlyOverview({
|
||||
const currentMonth = new Date().getMonth() + 1;
|
||||
const currentYear = new Date().getFullYear();
|
||||
const [editingMonth, setEditingMonth] = useState<number | null>(null);
|
||||
const [confirmClearMonth, setConfirmClearMonth] = useState<number | null>(null);
|
||||
const [editValue, setEditValue] = useState('');
|
||||
const inputRef = useRef<HTMLInputElement>(null);
|
||||
|
||||
@@ -61,7 +63,16 @@ export default function YearlyOverview({
|
||||
if (editingMonth === null) return;
|
||||
const trimmed = editValue.trim();
|
||||
if (trimmed === '') {
|
||||
await onClearOverride(editingMonth);
|
||||
// Clearing an override is destructive — confirm first (it changes the
|
||||
// cumulative chain for every following month).
|
||||
const hadOverride = months.find(
|
||||
(m) => m.month === editingMonth,
|
||||
)?.balance_overridden;
|
||||
if (hadOverride) {
|
||||
setConfirmClearMonth(editingMonth);
|
||||
setEditingMonth(null);
|
||||
return;
|
||||
}
|
||||
} else {
|
||||
const num = parseFloat(trimmed);
|
||||
if (!isNaN(num)) {
|
||||
@@ -71,6 +82,12 @@ export default function YearlyOverview({
|
||||
setEditingMonth(null);
|
||||
};
|
||||
|
||||
const handleConfirmClear = async () => {
|
||||
if (confirmClearMonth === null) return;
|
||||
await onClearOverride(confirmClearMonth);
|
||||
setConfirmClearMonth(null);
|
||||
};
|
||||
|
||||
const handleKeyDown = (e: React.KeyboardEvent) => {
|
||||
if (e.key === 'Enter') {
|
||||
handleSave();
|
||||
@@ -202,6 +219,15 @@ export default function YearlyOverview({
|
||||
})}
|
||||
</TableBody>
|
||||
</Table>
|
||||
{confirmClearMonth !== null && (
|
||||
<ConfirmDialog
|
||||
title="Eliminar ajuste de balance"
|
||||
message={`El balance de ${MONTH_NAMES[confirmClearMonth] ?? confirmClearMonth} volverá al valor calculado y los meses siguientes se recalcularán.`}
|
||||
confirmLabel="Eliminar ajuste"
|
||||
onConfirm={handleConfirmClear}
|
||||
onCancel={() => setConfirmClearMonth(null)}
|
||||
/>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user