refactor: atualiza transacoes dashboard e relatorios

This commit is contained in:
Felipe Coutinho
2026-03-14 12:51:22 +00:00
parent 43b0f0c47e
commit 6854017a8c
89 changed files with 2785 additions and 2705 deletions

View File

@@ -3,16 +3,16 @@ import { RiAddLine } from "@remixicon/react";
import { useEffect, useMemo, useState, useTransition } from "react";
import { toast } from "sonner";
import {
createLancamentoAction,
updateLancamentoAction,
createTransactionAction,
updateTransactionAction,
} from "@/features/transactions/actions";
import {
filterSecondaryPagadorOptions,
groupAndSortCategorias,
} from "@/features/transactions/categoria-helpers";
filterSecondaryPayerOptions,
groupAndSortCategories,
} from "@/features/transactions/category-helpers";
import {
applyFieldDependencies,
buildLancamentoInitialState,
buildTransactionInitialState,
deriveCreditCardPeriod,
} from "@/features/transactions/form-helpers";
import { Button } from "@/shared/components/ui/button";
@@ -36,41 +36,41 @@ import { BoletoFieldsSection } from "./boleto-fields-section";
import { CategorySection } from "./category-section";
import { ConditionSection } from "./condition-section";
import { NoteSection } from "./note-section";
import { PagadorSection } from "./pagador-section";
import { PayerSection } from "./payer-section";
import { PaymentMethodSection } from "./payment-method-section";
import { SplitAndSettlementSection } from "./split-settlement-section";
import type {
FormState,
LancamentoDialogProps,
TransactionDialogProps,
} from "./transaction-dialog-types";
export function LancamentoDialog({
export function TransactionDialog({
mode,
trigger,
open,
onOpenChange,
pagadorOptions,
splitPagadorOptions,
defaultPagadorId,
contaOptions,
cartaoOptions,
categoriaOptions,
payerOptions,
splitPayerOptions,
defaultPayerId,
accountOptions,
cardOptions,
categoryOptions,
estabelecimentos,
lancamento,
transaction,
defaultPeriod,
defaultCartaoId,
defaultCardId,
defaultPaymentMethod,
defaultPurchaseDate,
defaultName,
defaultAmount,
lockCartaoSelection,
lockCardSelection,
lockPaymentMethod,
isImporting = false,
defaultTransactionType,
forceShowTransactionType = false,
onSuccess,
onBulkEditRequest,
}: LancamentoDialogProps) {
}: TransactionDialogProps) {
const [dialogOpen, setDialogOpen] = useControlledState(
open,
false,
@@ -78,8 +78,8 @@ export function LancamentoDialog({
);
const [formState, setFormState] = useState<FormState>(() =>
buildLancamentoInitialState(lancamento, defaultPagadorId, defaultPeriod, {
defaultCartaoId,
buildTransactionInitialState(transaction, defaultPayerId, defaultPeriod, {
defaultCardId,
defaultPaymentMethod,
defaultPurchaseDate,
defaultName,
@@ -93,12 +93,12 @@ export function LancamentoDialog({
useEffect(() => {
if (dialogOpen) {
const initial = buildLancamentoInitialState(
lancamento,
defaultPagadorId,
const initial = buildTransactionInitialState(
transaction,
defaultPayerId,
defaultPeriod,
{
defaultCartaoId,
defaultCardId,
defaultPaymentMethod,
defaultPurchaseDate,
defaultName,
@@ -108,15 +108,13 @@ export function LancamentoDialog({
},
);
// Derive credit card period on open when cartaoId is pre-filled
// Derive credit card period on open when cardId is pre-filled
if (
initial.paymentMethod === "Cartão de crédito" &&
initial.cartaoId &&
initial.cardId &&
initial.purchaseDate
) {
const card = cartaoOptions.find(
(opt) => opt.value === initial.cartaoId,
);
const card = cardOptions.find((opt) => opt.value === initial.cardId);
if (card?.closingDay) {
initial.period = deriveCreditCardPeriod(
initial.purchaseDate,
@@ -131,45 +129,45 @@ export function LancamentoDialog({
}
}, [
dialogOpen,
lancamento,
defaultPagadorId,
transaction,
defaultPayerId,
defaultPeriod,
defaultCartaoId,
defaultCardId,
defaultPaymentMethod,
defaultPurchaseDate,
defaultName,
defaultAmount,
defaultTransactionType,
isImporting,
cartaoOptions,
cardOptions,
]);
const primaryPagador = formState.pagadorId;
const primaryPayerId = formState.payerId;
const secondaryPagadorOptions = useMemo(
() => filterSecondaryPagadorOptions(splitPagadorOptions, primaryPagador),
[splitPagadorOptions, primaryPagador],
const secondaryPayerOptions = useMemo(
() => filterSecondaryPayerOptions(splitPayerOptions, primaryPayerId),
[splitPayerOptions, primaryPayerId],
);
const categoriaGroups = useMemo(() => {
const filtered = categoriaOptions.filter(
const categoryGroups = useMemo(() => {
const filtered = categoryOptions.filter(
(option) =>
option.group?.toLowerCase() === formState.transactionType.toLowerCase(),
);
return groupAndSortCategorias(filtered);
}, [categoriaOptions, formState.transactionType]);
return groupAndSortCategories(filtered);
}, [categoryOptions, formState.transactionType]);
type CreateLancamentoInput = Parameters<typeof createLancamentoAction>[0];
type UpdateLancamentoInput = Parameters<typeof updateLancamentoAction>[0];
type CreateTransactionInput = Parameters<typeof createTransactionAction>[0];
type UpdateTransactionInput = Parameters<typeof updateTransactionAction>[0];
const totalAmount = useMemo(() => {
const parsed = Number.parseFloat(formState.amount);
return Number.isNaN(parsed) ? 0 : Math.abs(parsed);
}, [formState.amount]);
function getCardInfo(cartaoId: string | undefined) {
if (!cartaoId) return null;
const card = cartaoOptions.find((opt) => opt.value === cartaoId);
function getCardInfo(cardId: string | undefined) {
if (!cardId) return null;
const card = cardOptions.find((opt) => opt.value === cardId);
if (!card) return null;
return {
closingDay: card.closingDay ?? null,
@@ -182,9 +180,9 @@ export function LancamentoDialog({
value: FormState[Key],
) {
setFormState((prev) => {
const effectiveCartaoId =
key === "cartaoId" ? (value as string) : prev.cartaoId;
const cardInfo = getCardInfo(effectiveCartaoId);
const effectiveCardId =
key === "cardId" ? (value as string) : prev.cardId;
const cardInfo = getCardInfo(effectiveCardId);
const dependencies = applyFieldDependencies(key, value, prev, cardInfo);
@@ -214,7 +212,7 @@ export function LancamentoDialog({
return;
}
if (formState.isSplit && !formState.pagadorId) {
if (formState.isSplit && !formState.payerId) {
const message =
"Selecione o pagador principal para dividir o lançamento.";
setErrorMessage(message);
@@ -222,7 +220,7 @@ export function LancamentoDialog({
return;
}
if (formState.isSplit && !formState.secondaryPagadorId) {
if (formState.isSplit && !formState.secondaryPayerId) {
const message =
"Selecione o pagador secundário para dividir o lançamento.";
setErrorMessage(message);
@@ -240,7 +238,7 @@ export function LancamentoDialog({
const sanitizedAmount = Math.abs(amountValue);
if (!formState.categoriaId) {
if (!formState.categoryId) {
const message = "Selecione uma categoria.";
setErrorMessage(message);
toast.error(message);
@@ -248,32 +246,32 @@ export function LancamentoDialog({
}
if (formState.paymentMethod === "Cartão de crédito") {
if (!formState.cartaoId) {
if (!formState.cardId) {
const message = "Selecione o cartão.";
setErrorMessage(message);
toast.error(message);
return;
}
} else if (!formState.contaId) {
} else if (!formState.accountId) {
const message = "Selecione a conta.";
setErrorMessage(message);
toast.error(message);
return;
}
const payload: CreateLancamentoInput = {
const payload: CreateTransactionInput = {
purchaseDate: formState.purchaseDate,
period: formState.period,
name: formState.name.trim(),
transactionType:
formState.transactionType as CreateLancamentoInput["transactionType"],
formState.transactionType as CreateTransactionInput["transactionType"],
amount: sanitizedAmount,
condition: formState.condition as CreateLancamentoInput["condition"],
condition: formState.condition as CreateTransactionInput["condition"],
paymentMethod:
formState.paymentMethod as CreateLancamentoInput["paymentMethod"],
pagadorId: formState.pagadorId ?? null,
secondaryPagadorId: formState.isSplit
? formState.secondaryPagadorId
formState.paymentMethod as CreateTransactionInput["paymentMethod"],
payerId: formState.payerId ?? null,
secondaryPayerId: formState.isSplit
? formState.secondaryPayerId
: undefined,
isSplit: formState.isSplit,
primarySplitAmount: formState.isSplit
@@ -282,9 +280,9 @@ export function LancamentoDialog({
secondarySplitAmount: formState.isSplit
? Number.parseFloat(formState.secondarySplitAmount) || undefined
: undefined,
contaId: formState.contaId ?? null,
cartaoId: formState.cartaoId ?? null,
categoriaId: formState.categoriaId ?? null,
accountId: formState.accountId ?? null,
cardId: formState.cardId ?? null,
categoryId: formState.categoryId ?? null,
note: formState.note.trim() || null,
isSettled:
formState.paymentMethod === "Cartão de crédito"
@@ -309,7 +307,7 @@ export function LancamentoDialog({
startTransition(async () => {
if (mode === "create") {
const result = await createLancamentoAction(payload);
const result = await createTransactionAction(payload);
if (result.success) {
toast.success(result.message);
@@ -324,18 +322,18 @@ export function LancamentoDialog({
}
// Update mode
const hasSeriesId = Boolean(lancamento?.seriesId);
const hasSeriesId = Boolean(transaction?.seriesId);
if (hasSeriesId && onBulkEditRequest) {
// Para lançamentos em série, abre o diálogo de bulk action
onBulkEditRequest({
id: lancamento?.id ?? "",
id: transaction?.id ?? "",
name: formState.name.trim(),
categoriaId: formState.categoriaId,
categoryId: formState.categoryId,
note: formState.note.trim() || "",
pagadorId: formState.pagadorId,
contaId: formState.contaId,
cartaoId: formState.cartaoId,
payerId: formState.payerId,
accountId: formState.accountId,
cardId: formState.cardId,
amount: sanitizedAmount,
dueDate:
formState.paymentMethod === "Boleto"
@@ -350,12 +348,12 @@ export function LancamentoDialog({
}
// Atualização normal para lançamentos únicos ou todos os campos
const updatePayload: UpdateLancamentoInput = {
id: lancamento?.id ?? "",
const updatePayload: UpdateTransactionInput = {
id: transaction?.id ?? "",
...payload,
};
const result = await updateLancamentoAction(updatePayload);
const result = await updateTransactionAction(updatePayload);
if (result.success) {
toast.success(result.message);
@@ -369,15 +367,15 @@ export function LancamentoDialog({
});
};
const isCopyMode = mode === "create" && Boolean(lancamento) && !isImporting;
const isImportMode = mode === "create" && Boolean(lancamento) && isImporting;
const isCopyMode = mode === "create" && Boolean(transaction) && !isImporting;
const isImportMode = mode === "create" && Boolean(transaction) && isImporting;
const isNewWithType =
mode === "create" && !lancamento && defaultTransactionType;
mode === "create" && !transaction && defaultTransactionType;
const title =
mode === "create"
? isImportMode
? "Importar para Minha Conta"
? "Importar para Minha FinancialAccount"
: isCopyMode
? "Copiar lançamento"
: isNewWithType
@@ -405,7 +403,7 @@ export function LancamentoDialog({
const showSettledToggle = formState.paymentMethod !== "Cartão de crédito";
const isUpdateMode = mode === "update";
const disablePaymentMethod = Boolean(lockPaymentMethod && mode === "create");
const disableCartaoSelect = Boolean(lockCartaoSelection && mode === "create");
const disableCardSelect = Boolean(lockCardSelection && mode === "create");
return (
<Dialog open={dialogOpen} onOpenChange={setDialogOpen}>
@@ -430,8 +428,8 @@ export function LancamentoDialog({
<CategorySection
formState={formState}
onFieldChange={handleFieldChange}
categoriaOptions={categoriaOptions}
categoriaGroups={categoriaGroups}
categoryOptions={categoryOptions}
categoryGroups={categoryGroups}
isUpdateMode={isUpdateMode}
hideTransactionType={
Boolean(isNewWithType) && !forceShowTransactionType
@@ -446,22 +444,22 @@ export function LancamentoDialog({
/>
) : null}
<PagadorSection
<PayerSection
formState={formState}
onFieldChange={handleFieldChange}
pagadorOptions={pagadorOptions}
secondaryPagadorOptions={secondaryPagadorOptions}
payerOptions={payerOptions}
secondaryPayerOptions={secondaryPayerOptions}
totalAmount={totalAmount}
/>
<PaymentMethodSection
formState={formState}
onFieldChange={handleFieldChange}
contaOptions={contaOptions}
cartaoOptions={cartaoOptions}
accountOptions={accountOptions}
cardOptions={cardOptions}
isUpdateMode={isUpdateMode}
disablePaymentMethod={disablePaymentMethod}
disableCartaoSelect={disableCartaoSelect}
disableCardSelect={disableCardSelect}
/>
{showDueDate ? (