Merge branch 'pr-70'

This commit is contained in:
Felipe Coutinho
2026-05-23 12:57:43 -03:00

View File

@@ -8,6 +8,7 @@ import {
PAYMENT_METHODS, PAYMENT_METHODS,
type TRANSACTION_TYPES, type TRANSACTION_TYPES,
} from "@/features/transactions/lib/constants"; } from "@/features/transactions/lib/constants";
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import { Button } from "@/shared/components/ui/button"; import { Button } from "@/shared/components/ui/button";
import { CurrencyInput } from "@/shared/components/ui/currency-input"; import { CurrencyInput } from "@/shared/components/ui/currency-input";
import { DatePicker } from "@/shared/components/ui/date-picker"; import { DatePicker } from "@/shared/components/ui/date-picker";
@@ -148,6 +149,9 @@ export function MassAddDialog({
defaultCardId, defaultCardId,
}: MassAddDialogProps) { }: MassAddDialogProps) {
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const [isDirty, setIsDirty] = useState(false);
const [confirmCloseOpen, setConfirmCloseOpen] = useState(false);
const [cancelConfirmOpen, setCancelConfirmOpen] = useState(false);
// Fixed fields state (sempre ativos, sem checkboxes) // Fixed fields state (sempre ativos, sem checkboxes)
const [transactionType, setTransactionType] = const [transactionType, setTransactionType] =
@@ -179,11 +183,22 @@ export function MassAddDialog({
return groupAndSortCategories(filtered); return groupAndSortCategories(filtered);
}, [categoryOptions, transactionType]); }, [categoryOptions, transactionType]);
const resetForm = () => {
setTransactionType("Despesa");
setPaymentMethod(PAYMENT_METHODS[0]);
setPeriod(selectedPeriod);
setContaId(undefined);
setCartaoId(defaultCardId ?? undefined);
setTransactions([createEmptyTransactionRow(defaultPayerId)]);
setIsDirty(false);
};
const addTransaction = () => { const addTransaction = () => {
setTransactions([ setTransactions([
...transactions, ...transactions,
createEmptyTransactionRow(defaultPayerId), createEmptyTransactionRow(defaultPayerId),
]); ]);
setIsDirty(true);
}; };
const removeTransaction = (id: string) => { const removeTransaction = (id: string) => {
@@ -192,6 +207,7 @@ export function MassAddDialog({
return; return;
} }
setTransactions(transactions.filter((t) => t.id !== id)); setTransactions(transactions.filter((t) => t.id !== id));
setIsDirty(true);
}; };
const updateTransaction = ( const updateTransaction = (
@@ -202,6 +218,7 @@ export function MassAddDialog({
setTransactions( setTransactions(
transactions.map((t) => (t.id === id ? { ...t, [field]: value } : t)), transactions.map((t) => (t.id === id ? { ...t, [field]: value } : t)),
); );
setIsDirty(true);
}; };
const handleSubmit = async () => { const handleSubmit = async () => {
@@ -250,13 +267,7 @@ export function MassAddDialog({
try { try {
await onSubmit(formData); await onSubmit(formData);
onOpenChange(false); onOpenChange(false);
// Reset form resetForm();
setTransactionType("Despesa");
setPaymentMethod(PAYMENT_METHODS[0]);
setPeriod(selectedPeriod);
setContaId(undefined);
setCartaoId(defaultCardId ?? undefined);
setTransactions([createEmptyTransactionRow(defaultPayerId)]);
} catch (_error) { } catch (_error) {
// Error is handled by the onSubmit function // Error is handled by the onSubmit function
} finally { } finally {
@@ -265,7 +276,19 @@ export function MassAddDialog({
}; };
return ( return (
<Dialog open={open} onOpenChange={onOpenChange}> <Dialog
open={open}
onOpenChange={(newOpen) => {
if (!newOpen && isDirty) {
setConfirmCloseOpen(true);
} else {
onOpenChange(newOpen);
if (newOpen === false) {
resetForm();
}
}
}}
>
<DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto p-6 sm:px-8"> <DialogContent className="sm:max-w-3xl max-h-[90vh] overflow-y-auto p-6 sm:px-8">
<DialogHeader> <DialogHeader>
<DialogTitle>Adicionar múltiplos lançamentos</DialogTitle> <DialogTitle>Adicionar múltiplos lançamentos</DialogTitle>
@@ -286,9 +309,10 @@ export function MassAddDialog({
<Label htmlFor="transaction-type">Tipo de Transação</Label> <Label htmlFor="transaction-type">Tipo de Transação</Label>
<Select <Select
value={transactionType} value={transactionType}
onValueChange={(value) => onValueChange={(value) => {
setTransactionType(value as MassAddTransactionType) setTransactionType(value as MassAddTransactionType);
} setIsDirty(true);
}}
> >
<SelectTrigger id="transaction-type" className="w-full"> <SelectTrigger id="transaction-type" className="w-full">
<SelectValue> <SelectValue>
@@ -315,6 +339,7 @@ export function MassAddDialog({
value={paymentMethod} value={paymentMethod}
onValueChange={(value) => { onValueChange={(value) => {
setPaymentMethod(value as MassAddPaymentMethod); setPaymentMethod(value as MassAddPaymentMethod);
setIsDirty(true);
// Reset conta/cartao when changing payment method // Reset conta/cartao when changing payment method
if (value === "Cartão de crédito") { if (value === "Cartão de crédito") {
setContaId(undefined); setContaId(undefined);
@@ -346,7 +371,10 @@ export function MassAddDialog({
<Label htmlFor="cartao">Cartão</Label> <Label htmlFor="cartao">Cartão</Label>
<Select <Select
value={cardId} value={cardId}
onValueChange={setCartaoId} onValueChange={(value) => {
setCartaoId(value);
setIsDirty(true);
}}
disabled={isLockedToCartao} disabled={isLockedToCartao}
> >
<SelectTrigger id="cartao" className="w-full"> <SelectTrigger id="cartao" className="w-full">
@@ -395,7 +423,10 @@ export function MassAddDialog({
{cardId ? ( {cardId ? (
<InlinePeriodPicker <InlinePeriodPicker
period={period} period={period}
onPeriodChange={setPeriod} onPeriodChange={(value) => {
setPeriod(value);
setIsDirty(true);
}}
/> />
) : null} ) : null}
</div> </div>
@@ -405,7 +436,13 @@ export function MassAddDialog({
{!isCartaoSelected ? ( {!isCartaoSelected ? (
<div className="space-y-2"> <div className="space-y-2">
<Label htmlFor="conta">Conta</Label> <Label htmlFor="conta">Conta</Label>
<Select value={accountId} onValueChange={setContaId}> <Select
value={accountId}
onValueChange={(value) => {
setContaId(value);
setIsDirty(true);
}}
>
<SelectTrigger id="conta" className="w-full"> <SelectTrigger id="conta" className="w-full">
<SelectValue placeholder="Selecione"> <SelectValue placeholder="Selecione">
{accountId && {accountId &&
@@ -635,7 +672,13 @@ export function MassAddDialog({
<DialogFooter> <DialogFooter>
<Button <Button
variant="outline" variant="outline"
onClick={() => onOpenChange(false)} onClick={() => {
if (isDirty) {
setCancelConfirmOpen(true);
} else {
onOpenChange(false);
}
}}
disabled={loading} disabled={loading}
> >
Cancelar Cancelar
@@ -646,6 +689,36 @@ export function MassAddDialog({
{transactions.length === 1 ? "lançamento" : "lançamentos"} {transactions.length === 1 ? "lançamento" : "lançamentos"}
</Button> </Button>
</DialogFooter> </DialogFooter>
<ConfirmActionDialog
open={confirmCloseOpen}
onOpenChange={setConfirmCloseOpen}
title="Descartar alterações?"
description="Há lançamentos não salvos. Se fechar agora, todos os dados serão perdidos."
confirmLabel="Descartar"
cancelLabel="Continuar editando"
confirmVariant="destructive"
onConfirm={() => {
setConfirmCloseOpen(false);
onOpenChange(false);
resetForm();
}}
/>
<ConfirmActionDialog
open={cancelConfirmOpen}
onOpenChange={setCancelConfirmOpen}
title="Cancelar adição de lançamentos?"
description="Há lançamentos não salvos. Se cancelar, todos os dados serão perdidos."
confirmLabel="Cancelar"
cancelLabel="Continuar editando"
confirmVariant="destructive"
onConfirm={() => {
setCancelConfirmOpen(false);
onOpenChange(false);
resetForm();
}}
/>
</DialogContent> </DialogContent>
</Dialog> </Dialog>
); );