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,
type TRANSACTION_TYPES,
} from "@/features/transactions/lib/constants";
import { ConfirmActionDialog } from "@/shared/components/confirm-action-dialog";
import { Button } from "@/shared/components/ui/button";
import { CurrencyInput } from "@/shared/components/ui/currency-input";
import { DatePicker } from "@/shared/components/ui/date-picker";
@@ -148,6 +149,9 @@ export function MassAddDialog({
defaultCardId,
}: MassAddDialogProps) {
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)
const [transactionType, setTransactionType] =
@@ -179,11 +183,22 @@ export function MassAddDialog({
return groupAndSortCategories(filtered);
}, [categoryOptions, transactionType]);
const resetForm = () => {
setTransactionType("Despesa");
setPaymentMethod(PAYMENT_METHODS[0]);
setPeriod(selectedPeriod);
setContaId(undefined);
setCartaoId(defaultCardId ?? undefined);
setTransactions([createEmptyTransactionRow(defaultPayerId)]);
setIsDirty(false);
};
const addTransaction = () => {
setTransactions([
...transactions,
createEmptyTransactionRow(defaultPayerId),
]);
setIsDirty(true);
};
const removeTransaction = (id: string) => {
@@ -192,6 +207,7 @@ export function MassAddDialog({
return;
}
setTransactions(transactions.filter((t) => t.id !== id));
setIsDirty(true);
};
const updateTransaction = (
@@ -202,6 +218,7 @@ export function MassAddDialog({
setTransactions(
transactions.map((t) => (t.id === id ? { ...t, [field]: value } : t)),
);
setIsDirty(true);
};
const handleSubmit = async () => {
@@ -250,13 +267,7 @@ export function MassAddDialog({
try {
await onSubmit(formData);
onOpenChange(false);
// Reset form
setTransactionType("Despesa");
setPaymentMethod(PAYMENT_METHODS[0]);
setPeriod(selectedPeriod);
setContaId(undefined);
setCartaoId(defaultCardId ?? undefined);
setTransactions([createEmptyTransactionRow(defaultPayerId)]);
resetForm();
} catch (_error) {
// Error is handled by the onSubmit function
} finally {
@@ -265,7 +276,19 @@ export function MassAddDialog({
};
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">
<DialogHeader>
<DialogTitle>Adicionar múltiplos lançamentos</DialogTitle>
@@ -286,9 +309,10 @@ export function MassAddDialog({
<Label htmlFor="transaction-type">Tipo de Transação</Label>
<Select
value={transactionType}
onValueChange={(value) =>
setTransactionType(value as MassAddTransactionType)
}
onValueChange={(value) => {
setTransactionType(value as MassAddTransactionType);
setIsDirty(true);
}}
>
<SelectTrigger id="transaction-type" className="w-full">
<SelectValue>
@@ -315,6 +339,7 @@ export function MassAddDialog({
value={paymentMethod}
onValueChange={(value) => {
setPaymentMethod(value as MassAddPaymentMethod);
setIsDirty(true);
// Reset conta/cartao when changing payment method
if (value === "Cartão de crédito") {
setContaId(undefined);
@@ -346,7 +371,10 @@ export function MassAddDialog({
<Label htmlFor="cartao">Cartão</Label>
<Select
value={cardId}
onValueChange={setCartaoId}
onValueChange={(value) => {
setCartaoId(value);
setIsDirty(true);
}}
disabled={isLockedToCartao}
>
<SelectTrigger id="cartao" className="w-full">
@@ -395,7 +423,10 @@ export function MassAddDialog({
{cardId ? (
<InlinePeriodPicker
period={period}
onPeriodChange={setPeriod}
onPeriodChange={(value) => {
setPeriod(value);
setIsDirty(true);
}}
/>
) : null}
</div>
@@ -405,7 +436,13 @@ export function MassAddDialog({
{!isCartaoSelected ? (
<div className="space-y-2">
<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">
<SelectValue placeholder="Selecione">
{accountId &&
@@ -635,7 +672,13 @@ export function MassAddDialog({
<DialogFooter>
<Button
variant="outline"
onClick={() => onOpenChange(false)}
onClick={() => {
if (isDirty) {
setCancelConfirmOpen(true);
} else {
onOpenChange(false);
}
}}
disabled={loading}
>
Cancelar
@@ -646,6 +689,36 @@ export function MassAddDialog({
{transactions.length === 1 ? "lançamento" : "lançamentos"}
</Button>
</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>
</Dialog>
);