mirror of
https://github.com/felipegcoutinho/openmonetis.git
synced 2026-06-09 23:06:01 +00:00
refactor: atualiza transacoes dashboard e relatorios
This commit is contained in:
@@ -55,8 +55,8 @@ interface AnticipateInstallmentsDialogProps {
|
||||
type AnticipationFormValues = {
|
||||
anticipationPeriod: string;
|
||||
discount: string;
|
||||
pagadorId: string;
|
||||
categoriaId: string;
|
||||
payerId: string;
|
||||
categoryId: string;
|
||||
note: string;
|
||||
};
|
||||
|
||||
@@ -90,8 +90,8 @@ export function AnticipateInstallmentsDialog({
|
||||
useFormState<AnticipationFormValues>({
|
||||
anticipationPeriod: defaultPeriod,
|
||||
discount: "0",
|
||||
pagadorId: "",
|
||||
categoriaId: "",
|
||||
payerId: "",
|
||||
categoryId: "",
|
||||
note: "",
|
||||
});
|
||||
|
||||
@@ -119,8 +119,8 @@ export function AnticipateInstallmentsDialog({
|
||||
replaceForm({
|
||||
anticipationPeriod: defaultPeriod,
|
||||
discount: "0",
|
||||
pagadorId: first.pagadorId ?? "",
|
||||
categoriaId: first.categoriaId ?? "",
|
||||
payerId: first.payerId ?? "",
|
||||
categoryId: first.categoryId ?? "",
|
||||
note: "",
|
||||
});
|
||||
}
|
||||
@@ -182,8 +182,8 @@ export function AnticipateInstallmentsDialog({
|
||||
installmentIds: selectedIds,
|
||||
anticipationPeriod: formState.anticipationPeriod,
|
||||
discount: Number(formState.discount) || 0,
|
||||
pagadorId: formState.pagadorId || undefined,
|
||||
categoriaId: formState.categoriaId || undefined,
|
||||
payerId: formState.payerId || undefined,
|
||||
categoryId: formState.categoryId || undefined,
|
||||
note: formState.note || undefined,
|
||||
});
|
||||
|
||||
@@ -269,11 +269,11 @@ export function AnticipateInstallmentsDialog({
|
||||
</Field>
|
||||
|
||||
<Field className="gap-1">
|
||||
<FieldLabel htmlFor="anticipation-pagador">Pagador</FieldLabel>
|
||||
<FieldLabel htmlFor="anticipation-pagador">Payer</FieldLabel>
|
||||
<FieldContent>
|
||||
<Select
|
||||
value={formState.pagadorId}
|
||||
onValueChange={(value) => updateField("pagadorId", value)}
|
||||
value={formState.payerId}
|
||||
onValueChange={(value) => updateField("payerId", value)}
|
||||
disabled={isPending}
|
||||
>
|
||||
<SelectTrigger id="anticipation-pagador" className="w-full">
|
||||
@@ -292,12 +292,12 @@ export function AnticipateInstallmentsDialog({
|
||||
|
||||
<Field className="gap-1">
|
||||
<FieldLabel htmlFor="anticipation-categoria">
|
||||
Categoria
|
||||
Category
|
||||
</FieldLabel>
|
||||
<FieldContent>
|
||||
<Select
|
||||
value={formState.categoriaId}
|
||||
onValueChange={(value) => updateField("categoriaId", value)}
|
||||
value={formState.categoryId}
|
||||
onValueChange={(value) => updateField("categoryId", value)}
|
||||
disabled={isPending}
|
||||
>
|
||||
<SelectTrigger
|
||||
|
||||
@@ -29,7 +29,7 @@ interface AnticipationHistoryDialogProps {
|
||||
lancamentoName: string;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
onViewLancamento?: (lancamentoId: string) => void;
|
||||
onViewLancamento?: (transactionId: string) => void;
|
||||
}
|
||||
|
||||
export function AnticipationHistoryDialog({
|
||||
|
||||
@@ -2,8 +2,8 @@
|
||||
|
||||
import { useMemo, useState, useTransition } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { createLancamentoAction } from "@/features/transactions/actions";
|
||||
import { groupAndSortCategorias } from "@/features/transactions/categoria-helpers";
|
||||
import { createTransactionAction } from "@/features/transactions/actions";
|
||||
import { groupAndSortCategories } from "@/features/transactions/category-helpers";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import {
|
||||
Dialog,
|
||||
@@ -24,46 +24,46 @@ import {
|
||||
SelectValue,
|
||||
} from "@/shared/components/ui/select";
|
||||
import {
|
||||
CategoriaSelectContent,
|
||||
ContaCartaoSelectContent,
|
||||
PagadorSelectContent,
|
||||
CategorySelectContent,
|
||||
AccountCardSelectContent,
|
||||
PayerSelectContent,
|
||||
} from "../select-items";
|
||||
import type { LancamentoItem, SelectOption } from "../types";
|
||||
import type { SelectOption, TransactionItem } from "../types";
|
||||
|
||||
interface BulkImportDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
items: LancamentoItem[];
|
||||
pagadorOptions: SelectOption[];
|
||||
contaOptions: SelectOption[];
|
||||
cartaoOptions: SelectOption[];
|
||||
categoriaOptions: SelectOption[];
|
||||
defaultPagadorId?: string | null;
|
||||
items: TransactionItem[];
|
||||
payerOptions: SelectOption[];
|
||||
accountOptions: SelectOption[];
|
||||
cardOptions: SelectOption[];
|
||||
categoryOptions: SelectOption[];
|
||||
defaultPayerId?: string | null;
|
||||
}
|
||||
|
||||
export function BulkImportDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
items,
|
||||
pagadorOptions,
|
||||
contaOptions,
|
||||
cartaoOptions,
|
||||
categoriaOptions,
|
||||
defaultPagadorId,
|
||||
payerOptions,
|
||||
accountOptions,
|
||||
cardOptions,
|
||||
categoryOptions,
|
||||
defaultPayerId,
|
||||
}: BulkImportDialogProps) {
|
||||
const [pagadorId, setPagadorId] = useState<string | undefined>(
|
||||
defaultPagadorId ?? undefined,
|
||||
const [payerId, setPagadorId] = useState<string | undefined>(
|
||||
defaultPayerId ?? undefined,
|
||||
);
|
||||
const [categoriaId, setCategoriaId] = useState<string | undefined>(undefined);
|
||||
const [contaId, setContaId] = useState<string | undefined>(undefined);
|
||||
const [cartaoId, setCartaoId] = useState<string | undefined>(undefined);
|
||||
const [categoryId, setCategoriaId] = useState<string | undefined>(undefined);
|
||||
const [accountId, setContaId] = useState<string | undefined>(undefined);
|
||||
const [cardId, setCartaoId] = useState<string | undefined>(undefined);
|
||||
const [isPending, startTransition] = useTransition();
|
||||
type CreateLancamentoInput = Parameters<typeof createLancamentoAction>[0];
|
||||
type CreateTransactionInput = Parameters<typeof createTransactionAction>[0];
|
||||
|
||||
// Reset form when dialog opens/closes
|
||||
const handleOpenChange = (newOpen: boolean) => {
|
||||
if (!newOpen) {
|
||||
setPagadorId(defaultPagadorId ?? undefined);
|
||||
setPagadorId(defaultPayerId ?? undefined);
|
||||
setCategoriaId(undefined);
|
||||
setContaId(undefined);
|
||||
setCartaoId(undefined);
|
||||
@@ -71,30 +71,30 @@ export function BulkImportDialog({
|
||||
onOpenChange(newOpen);
|
||||
};
|
||||
|
||||
const categoriaGroups = useMemo(() => {
|
||||
const categoryGroups = useMemo(() => {
|
||||
// Get unique transaction types from items
|
||||
const transactionTypes = new Set(items.map((item) => item.transactionType));
|
||||
|
||||
// Filter categories based on transaction types
|
||||
const filtered = categoriaOptions.filter((option) => {
|
||||
const filtered = categoryOptions.filter((option) => {
|
||||
if (!option.group) return false;
|
||||
return Array.from(transactionTypes).some(
|
||||
(type) => option.group?.toLowerCase() === type.toLowerCase(),
|
||||
);
|
||||
});
|
||||
|
||||
return groupAndSortCategorias(filtered);
|
||||
}, [categoriaOptions, items]);
|
||||
return groupAndSortCategories(filtered);
|
||||
}, [categoryOptions, items]);
|
||||
|
||||
const handleSubmit = async (event: React.FormEvent<HTMLFormElement>) => {
|
||||
event.preventDefault();
|
||||
|
||||
if (!pagadorId) {
|
||||
if (!payerId) {
|
||||
toast.error("Selecione o pagador.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!categoriaId) {
|
||||
if (!categoryId) {
|
||||
toast.error("Selecione a categoria.");
|
||||
return;
|
||||
}
|
||||
@@ -110,32 +110,32 @@ export function BulkImportDialog({
|
||||
const isCredit = item.paymentMethod === "Cartão de crédito";
|
||||
|
||||
// Validate payment method fields
|
||||
if (isCredit && !cartaoId) {
|
||||
if (isCredit && !cardId) {
|
||||
toast.error("Selecione um cartão de crédito.");
|
||||
return;
|
||||
}
|
||||
|
||||
if (!isCredit && !contaId) {
|
||||
if (!isCredit && !accountId) {
|
||||
toast.error("Selecione uma conta.");
|
||||
return;
|
||||
}
|
||||
|
||||
const payload: CreateLancamentoInput = {
|
||||
const payload: CreateTransactionInput = {
|
||||
purchaseDate: item.purchaseDate,
|
||||
period: item.period,
|
||||
name: item.name,
|
||||
transactionType:
|
||||
item.transactionType as CreateLancamentoInput["transactionType"],
|
||||
item.transactionType as CreateTransactionInput["transactionType"],
|
||||
amount: sanitizedAmount,
|
||||
condition: item.condition as CreateLancamentoInput["condition"],
|
||||
condition: item.condition as CreateTransactionInput["condition"],
|
||||
paymentMethod:
|
||||
item.paymentMethod as CreateLancamentoInput["paymentMethod"],
|
||||
pagadorId: pagadorId ?? null,
|
||||
secondaryPagadorId: undefined,
|
||||
item.paymentMethod as CreateTransactionInput["paymentMethod"],
|
||||
payerId: payerId ?? null,
|
||||
secondaryPayerId: undefined,
|
||||
isSplit: false,
|
||||
contaId: isCredit ? null : (contaId ?? null),
|
||||
cartaoId: isCredit ? (cartaoId ?? null) : null,
|
||||
categoriaId: categoriaId ?? null,
|
||||
accountId: isCredit ? null : (accountId ?? null),
|
||||
cardId: isCredit ? (cardId ?? null) : null,
|
||||
categoryId: categoryId ?? null,
|
||||
note: item.note ?? null,
|
||||
isSettled: isCredit ? null : Boolean(item.isSettled),
|
||||
installmentCount:
|
||||
@@ -152,7 +152,7 @@ export function BulkImportDialog({
|
||||
: undefined,
|
||||
};
|
||||
|
||||
const result = await createLancamentoAction(payload);
|
||||
const result = await createTransactionAction(payload);
|
||||
|
||||
if (result.success) {
|
||||
successCount++;
|
||||
@@ -203,17 +203,17 @@ export function BulkImportDialog({
|
||||
|
||||
<form className="space-y-4" onSubmit={handleSubmit}>
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="pagador">Pagador *</Label>
|
||||
<Select value={pagadorId} onValueChange={setPagadorId}>
|
||||
<Label htmlFor="pagador">Payer *</Label>
|
||||
<Select value={payerId} onValueChange={setPagadorId}>
|
||||
<SelectTrigger id="pagador" className="w-full">
|
||||
<SelectValue placeholder="Selecione o pagador">
|
||||
{pagadorId &&
|
||||
{payerId &&
|
||||
(() => {
|
||||
const selectedOption = pagadorOptions.find(
|
||||
(opt) => opt.value === pagadorId,
|
||||
const selectedOption = payerOptions.find(
|
||||
(opt) => opt.value === payerId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={selectedOption.label}
|
||||
avatarUrl={selectedOption.avatarUrl}
|
||||
/>
|
||||
@@ -222,9 +222,9 @@ export function BulkImportDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{pagadorOptions.map((option) => (
|
||||
{payerOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={option.label}
|
||||
avatarUrl={option.avatarUrl}
|
||||
/>
|
||||
@@ -235,17 +235,17 @@ export function BulkImportDialog({
|
||||
</div>
|
||||
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="categoria">Categoria *</Label>
|
||||
<Select value={categoriaId} onValueChange={setCategoriaId}>
|
||||
<Label htmlFor="categoria">Category *</Label>
|
||||
<Select value={categoryId} onValueChange={setCategoriaId}>
|
||||
<SelectTrigger id="categoria" className="w-full">
|
||||
<SelectValue placeholder="Selecione a categoria">
|
||||
{categoriaId &&
|
||||
{categoryId &&
|
||||
(() => {
|
||||
const selectedOption = categoriaOptions.find(
|
||||
(opt) => opt.value === categoriaId,
|
||||
const selectedOption = categoryOptions.find(
|
||||
(opt) => opt.value === categoryId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<CategoriaSelectContent
|
||||
<CategorySelectContent
|
||||
label={selectedOption.label}
|
||||
icon={selectedOption.icon}
|
||||
/>
|
||||
@@ -254,12 +254,12 @@ export function BulkImportDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categoriaGroups.map((group) => (
|
||||
{categoryGroups.map((group) => (
|
||||
<SelectGroup key={group.label}>
|
||||
<SelectLabel>{group.label}</SelectLabel>
|
||||
{group.options.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<CategoriaSelectContent
|
||||
<CategorySelectContent
|
||||
label={option.label}
|
||||
icon={option.icon}
|
||||
/>
|
||||
@@ -274,18 +274,18 @@ export function BulkImportDialog({
|
||||
{hasNonCredit && (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="conta">
|
||||
Conta {hasCredit ? "(para não cartão)" : "*"}
|
||||
FinancialAccount {hasCredit ? "(para não cartão)" : "*"}
|
||||
</Label>
|
||||
<Select value={contaId} onValueChange={setContaId}>
|
||||
<Select value={accountId} onValueChange={setContaId}>
|
||||
<SelectTrigger id="conta" className="w-full">
|
||||
<SelectValue placeholder="Selecione a conta">
|
||||
{contaId &&
|
||||
{accountId &&
|
||||
(() => {
|
||||
const selectedOption = contaOptions.find(
|
||||
(opt) => opt.value === contaId,
|
||||
const selectedOption = accountOptions.find(
|
||||
(opt) => opt.value === accountId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={false}
|
||||
@@ -295,9 +295,9 @@ export function BulkImportDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{contaOptions.map((option) => (
|
||||
{accountOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={false}
|
||||
@@ -314,16 +314,16 @@ export function BulkImportDialog({
|
||||
<Label htmlFor="cartao">
|
||||
Cartão {hasNonCredit ? "(para cartão de crédito)" : "*"}
|
||||
</Label>
|
||||
<Select value={cartaoId} onValueChange={setCartaoId}>
|
||||
<Select value={cardId} onValueChange={setCartaoId}>
|
||||
<SelectTrigger id="cartao" className="w-full">
|
||||
<SelectValue placeholder="Selecione o cartão">
|
||||
{cartaoId &&
|
||||
{cardId &&
|
||||
(() => {
|
||||
const selectedOption = cartaoOptions.find(
|
||||
(opt) => opt.value === cartaoId,
|
||||
const selectedOption = cardOptions.find(
|
||||
(opt) => opt.value === cardId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={true}
|
||||
@@ -333,9 +333,9 @@ export function BulkImportDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{cartaoOptions.map((option) => (
|
||||
{cardOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={true}
|
||||
|
||||
@@ -3,10 +3,10 @@
|
||||
import { RiAddLine, RiDeleteBinLine } from "@remixicon/react";
|
||||
import { useMemo, useState } from "react";
|
||||
import { toast } from "sonner";
|
||||
import { groupAndSortCategorias } from "@/features/transactions/categoria-helpers";
|
||||
import { groupAndSortCategories } from "@/features/transactions/category-helpers";
|
||||
import {
|
||||
LANCAMENTO_PAYMENT_METHODS,
|
||||
type LANCAMENTO_TRANSACTION_TYPES,
|
||||
PAYMENT_METHODS,
|
||||
type TRANSACTION_TYPES,
|
||||
} from "@/features/transactions/constants";
|
||||
import { Button } from "@/shared/components/ui/button";
|
||||
import { CurrencyInput } from "@/shared/components/ui/currency-input";
|
||||
@@ -44,9 +44,9 @@ import {
|
||||
periodToDate,
|
||||
} from "@/shared/utils/period";
|
||||
import {
|
||||
CategoriaSelectContent,
|
||||
ContaCartaoSelectContent,
|
||||
PagadorSelectContent,
|
||||
CategorySelectContent,
|
||||
AccountCardSelectContent,
|
||||
PayerSelectContent,
|
||||
PaymentMethodSelectContent,
|
||||
TransactionTypeSelectContent,
|
||||
} from "../select-items";
|
||||
@@ -54,11 +54,9 @@ import { EstabelecimentoInput } from "../shared/establishment-input";
|
||||
import type { SelectOption } from "../types";
|
||||
|
||||
/** Payment methods sem Boleto para este modal */
|
||||
const MASS_ADD_PAYMENT_METHODS = LANCAMENTO_PAYMENT_METHODS.filter(
|
||||
(m) => m !== "Boleto",
|
||||
);
|
||||
type MassAddTransactionType = (typeof LANCAMENTO_TRANSACTION_TYPES)[number];
|
||||
type MassAddPaymentMethod = (typeof LANCAMENTO_PAYMENT_METHODS)[number];
|
||||
const MASS_ADD_PAYMENT_METHODS = PAYMENT_METHODS.filter((m) => m !== "Boleto");
|
||||
type MassAddTransactionType = (typeof TRANSACTION_TYPES)[number];
|
||||
type MassAddPaymentMethod = (typeof PAYMENT_METHODS)[number];
|
||||
|
||||
function InlinePeriodPicker({
|
||||
period,
|
||||
@@ -71,7 +69,7 @@ function InlinePeriodPicker({
|
||||
|
||||
return (
|
||||
<div className="-mt-1">
|
||||
<span className="text-xs text-muted-foreground">Fatura de </span>
|
||||
<span className="text-xs text-muted-foreground">Invoice de </span>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
@@ -99,18 +97,18 @@ interface MassAddDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
onSubmit: (data: MassAddFormData) => Promise<void>;
|
||||
pagadorOptions: SelectOption[];
|
||||
contaOptions: SelectOption[];
|
||||
cartaoOptions: SelectOption[];
|
||||
categoriaOptions: SelectOption[];
|
||||
payerOptions: SelectOption[];
|
||||
accountOptions: SelectOption[];
|
||||
cardOptions: SelectOption[];
|
||||
categoryOptions: SelectOption[];
|
||||
estabelecimentos: string[];
|
||||
selectedPeriod: string;
|
||||
defaultPagadorId?: string | null;
|
||||
defaultCartaoId?: string | null;
|
||||
defaultPayerId?: string | null;
|
||||
defaultCardId?: string | null;
|
||||
}
|
||||
|
||||
export type MassAddFormData = Parameters<
|
||||
typeof import("@/features/transactions/actions").createMassLancamentosAction
|
||||
typeof import("@/features/transactions/actions").createMassTransactionsAction
|
||||
>[0];
|
||||
|
||||
interface TransactionRow {
|
||||
@@ -118,22 +116,22 @@ interface TransactionRow {
|
||||
purchaseDate: string;
|
||||
name: string;
|
||||
amount: string;
|
||||
categoriaId: string | undefined;
|
||||
pagadorId: string | undefined;
|
||||
categoryId: string | undefined;
|
||||
payerId: string | undefined;
|
||||
}
|
||||
|
||||
export function MassAddDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
onSubmit,
|
||||
pagadorOptions,
|
||||
contaOptions,
|
||||
cartaoOptions,
|
||||
categoriaOptions,
|
||||
payerOptions,
|
||||
accountOptions,
|
||||
cardOptions,
|
||||
categoryOptions,
|
||||
estabelecimentos,
|
||||
selectedPeriod,
|
||||
defaultPagadorId,
|
||||
defaultCartaoId,
|
||||
defaultPayerId,
|
||||
defaultCardId,
|
||||
}: MassAddDialogProps) {
|
||||
const [loading, setLoading] = useState(false);
|
||||
|
||||
@@ -141,16 +139,16 @@ export function MassAddDialog({
|
||||
const [transactionType, setTransactionType] =
|
||||
useState<MassAddTransactionType>("Despesa");
|
||||
const [paymentMethod, setPaymentMethod] = useState<MassAddPaymentMethod>(
|
||||
LANCAMENTO_PAYMENT_METHODS[0],
|
||||
PAYMENT_METHODS[0],
|
||||
);
|
||||
const [period, setPeriod] = useState<string>(selectedPeriod);
|
||||
const [contaId, setContaId] = useState<string | undefined>(undefined);
|
||||
const [cartaoId, setCartaoId] = useState<string | undefined>(
|
||||
defaultCartaoId ?? undefined,
|
||||
const [accountId, setContaId] = useState<string | undefined>(undefined);
|
||||
const [cardId, setCartaoId] = useState<string | undefined>(
|
||||
defaultCardId ?? undefined,
|
||||
);
|
||||
|
||||
// Quando defaultCartaoId está definido, exibe apenas o cartão específico
|
||||
const isLockedToCartao = !!defaultCartaoId;
|
||||
// Quando defaultCardId está definido, exibe apenas o cartão específico
|
||||
const isLockedToCartao = !!defaultCardId;
|
||||
|
||||
const isCartaoSelected = paymentMethod === "Cartão de crédito";
|
||||
|
||||
@@ -161,18 +159,18 @@ export function MassAddDialog({
|
||||
purchaseDate: getTodayDateString(),
|
||||
name: "",
|
||||
amount: "",
|
||||
categoriaId: undefined,
|
||||
pagadorId: defaultPagadorId ?? undefined,
|
||||
categoryId: undefined,
|
||||
payerId: defaultPayerId ?? undefined,
|
||||
},
|
||||
]);
|
||||
|
||||
// Categorias agrupadas e filtradas por tipo de transação
|
||||
const groupedCategorias = useMemo(() => {
|
||||
const filtered = categoriaOptions.filter(
|
||||
const filtered = categoryOptions.filter(
|
||||
(option) => option.group?.toLowerCase() === transactionType.toLowerCase(),
|
||||
);
|
||||
return groupAndSortCategorias(filtered);
|
||||
}, [categoriaOptions, transactionType]);
|
||||
return groupAndSortCategories(filtered);
|
||||
}, [categoryOptions, transactionType]);
|
||||
|
||||
const addTransaction = () => {
|
||||
setTransactions([
|
||||
@@ -182,8 +180,8 @@ export function MassAddDialog({
|
||||
purchaseDate: getTodayDateString(),
|
||||
name: "",
|
||||
amount: "",
|
||||
categoriaId: undefined,
|
||||
pagadorId: defaultPagadorId ?? undefined,
|
||||
categoryId: undefined,
|
||||
payerId: defaultPayerId ?? undefined,
|
||||
},
|
||||
]);
|
||||
};
|
||||
@@ -208,11 +206,11 @@ export function MassAddDialog({
|
||||
|
||||
const handleSubmit = async () => {
|
||||
// Validate conta/cartao selection
|
||||
if (isCartaoSelected && !cartaoId) {
|
||||
if (isCartaoSelected && !cardId) {
|
||||
toast.error("Selecione um cartão para continuar");
|
||||
return;
|
||||
}
|
||||
if (!isCartaoSelected && !contaId) {
|
||||
if (!isCartaoSelected && !accountId) {
|
||||
toast.error("Selecione uma conta para continuar");
|
||||
return;
|
||||
}
|
||||
@@ -236,15 +234,15 @@ export function MassAddDialog({
|
||||
paymentMethod,
|
||||
condition: "À vista",
|
||||
period,
|
||||
contaId,
|
||||
cartaoId,
|
||||
accountId,
|
||||
cardId,
|
||||
},
|
||||
transactions: transactions.map((t) => ({
|
||||
purchaseDate: t.purchaseDate,
|
||||
name: t.name.trim(),
|
||||
amount: Number(t.amount.trim()),
|
||||
categoriaId: t.categoriaId,
|
||||
pagadorId: t.pagadorId,
|
||||
categoryId: t.categoryId,
|
||||
payerId: t.payerId,
|
||||
})),
|
||||
};
|
||||
|
||||
@@ -254,18 +252,18 @@ export function MassAddDialog({
|
||||
onOpenChange(false);
|
||||
// Reset form
|
||||
setTransactionType("Despesa");
|
||||
setPaymentMethod(LANCAMENTO_PAYMENT_METHODS[0]);
|
||||
setPaymentMethod(PAYMENT_METHODS[0]);
|
||||
setPeriod(selectedPeriod);
|
||||
setContaId(undefined);
|
||||
setCartaoId(defaultCartaoId ?? undefined);
|
||||
setCartaoId(defaultCardId ?? undefined);
|
||||
setTransactions([
|
||||
{
|
||||
id: crypto.randomUUID(),
|
||||
purchaseDate: getTodayDateString(),
|
||||
name: "",
|
||||
amount: "",
|
||||
categoriaId: undefined,
|
||||
pagadorId: defaultPagadorId ?? undefined,
|
||||
categoryId: undefined,
|
||||
payerId: defaultPayerId ?? undefined,
|
||||
},
|
||||
]);
|
||||
} catch (_error) {
|
||||
@@ -356,19 +354,19 @@ export function MassAddDialog({
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="cartao">Cartão</Label>
|
||||
<Select
|
||||
value={cartaoId}
|
||||
value={cardId}
|
||||
onValueChange={setCartaoId}
|
||||
disabled={isLockedToCartao}
|
||||
>
|
||||
<SelectTrigger id="cartao" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{cartaoId &&
|
||||
{cardId &&
|
||||
(() => {
|
||||
const selectedOption = cartaoOptions.find(
|
||||
(opt) => opt.value === cartaoId,
|
||||
const selectedOption = cardOptions.find(
|
||||
(opt) => opt.value === cardId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={true}
|
||||
@@ -378,22 +376,22 @@ export function MassAddDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{cartaoOptions.length === 0 ? (
|
||||
{cardOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhum cartão cadastrado
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
cartaoOptions
|
||||
cardOptions
|
||||
.filter(
|
||||
(option) =>
|
||||
!isLockedToCartao ||
|
||||
option.value === defaultCartaoId,
|
||||
option.value === defaultCardId,
|
||||
)
|
||||
.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={true}
|
||||
@@ -403,7 +401,7 @@ export function MassAddDialog({
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{cartaoId ? (
|
||||
{cardId ? (
|
||||
<InlinePeriodPicker
|
||||
period={period}
|
||||
onPeriodChange={setPeriod}
|
||||
@@ -412,20 +410,20 @@ export function MassAddDialog({
|
||||
</div>
|
||||
) : null}
|
||||
|
||||
{/* Conta (for non-credit-card methods) */}
|
||||
{/* FinancialAccount (for non-credit-card methods) */}
|
||||
{!isCartaoSelected ? (
|
||||
<div className="space-y-2">
|
||||
<Label htmlFor="conta">Conta</Label>
|
||||
<Select value={contaId} onValueChange={setContaId}>
|
||||
<Label htmlFor="conta">FinancialAccount</Label>
|
||||
<Select value={accountId} onValueChange={setContaId}>
|
||||
<SelectTrigger id="conta" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{contaId &&
|
||||
{accountId &&
|
||||
(() => {
|
||||
const selectedOption = contaOptions.find(
|
||||
(opt) => opt.value === contaId,
|
||||
const selectedOption = accountOptions.find(
|
||||
(opt) => opt.value === accountId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={false}
|
||||
@@ -435,16 +433,16 @@ export function MassAddDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{contaOptions.length === 0 ? (
|
||||
{accountOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhuma conta cadastrada
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
contaOptions.map((option) => (
|
||||
accountOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={false}
|
||||
@@ -536,26 +534,26 @@ export function MassAddDialog({
|
||||
htmlFor={`pagador-${transaction.id}`}
|
||||
className="sr-only"
|
||||
>
|
||||
Pagador {index + 1}
|
||||
Payer {index + 1}
|
||||
</Label>
|
||||
<Select
|
||||
value={transaction.pagadorId}
|
||||
value={transaction.payerId}
|
||||
onValueChange={(value) =>
|
||||
updateTransaction(transaction.id, "pagadorId", value)
|
||||
updateTransaction(transaction.id, "payerId", value)
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id={`pagador-${transaction.id}`}
|
||||
className="w-32 truncate"
|
||||
>
|
||||
<SelectValue placeholder="Pagador">
|
||||
{transaction.pagadorId &&
|
||||
<SelectValue placeholder="Payer">
|
||||
{transaction.payerId &&
|
||||
(() => {
|
||||
const selectedOption = pagadorOptions.find(
|
||||
(opt) => opt.value === transaction.pagadorId,
|
||||
const selectedOption = payerOptions.find(
|
||||
(opt) => opt.value === transaction.payerId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={selectedOption.label}
|
||||
avatarUrl={selectedOption.avatarUrl}
|
||||
/>
|
||||
@@ -564,9 +562,9 @@ export function MassAddDialog({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{pagadorOptions.map((option) => (
|
||||
{payerOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={option.label}
|
||||
avatarUrl={option.avatarUrl}
|
||||
/>
|
||||
@@ -581,23 +579,19 @@ export function MassAddDialog({
|
||||
htmlFor={`categoria-${transaction.id}`}
|
||||
className="sr-only"
|
||||
>
|
||||
Categoria {index + 1}
|
||||
Category {index + 1}
|
||||
</Label>
|
||||
<Select
|
||||
value={transaction.categoriaId}
|
||||
value={transaction.categoryId}
|
||||
onValueChange={(value) =>
|
||||
updateTransaction(
|
||||
transaction.id,
|
||||
"categoriaId",
|
||||
value,
|
||||
)
|
||||
updateTransaction(transaction.id, "categoryId", value)
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id={`categoria-${transaction.id}`}
|
||||
className="w-32 truncate"
|
||||
>
|
||||
<SelectValue placeholder="Categoria" />
|
||||
<SelectValue placeholder="Category" />
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{groupedCategorias.map((group) => (
|
||||
@@ -608,7 +602,7 @@ export function MassAddDialog({
|
||||
key={option.value}
|
||||
value={option.value}
|
||||
>
|
||||
<CategoriaSelectContent
|
||||
<CategorySelectContent
|
||||
label={option.label}
|
||||
icon={option.icon}
|
||||
/>
|
||||
|
||||
@@ -25,29 +25,29 @@ import { Separator } from "@/shared/components/ui/separator";
|
||||
import { parseLocalDateString } from "@/shared/utils/date";
|
||||
import { getPaymentMethodIcon } from "@/shared/utils/icons";
|
||||
import { InstallmentTimeline } from "../shared/installment-timeline";
|
||||
import type { LancamentoItem } from "../types";
|
||||
import type { TransactionItem } from "../types";
|
||||
|
||||
interface LancamentoDetailsDialogProps {
|
||||
interface TransactionDetailsDialogProps {
|
||||
open: boolean;
|
||||
onOpenChange: (open: boolean) => void;
|
||||
lancamento: LancamentoItem | null;
|
||||
transaction: TransactionItem | null;
|
||||
}
|
||||
|
||||
export function LancamentoDetailsDialog({
|
||||
export function TransactionDetailsDialog({
|
||||
open,
|
||||
onOpenChange,
|
||||
lancamento,
|
||||
}: LancamentoDetailsDialogProps) {
|
||||
if (!lancamento) return null;
|
||||
transaction,
|
||||
}: TransactionDetailsDialogProps) {
|
||||
if (!transaction) return null;
|
||||
|
||||
const isInstallment =
|
||||
lancamento.condition?.toLowerCase() === "parcelado" &&
|
||||
lancamento.currentInstallment &&
|
||||
lancamento.installmentCount;
|
||||
transaction.condition?.toLowerCase() === "parcelado" &&
|
||||
transaction.currentInstallment &&
|
||||
transaction.installmentCount;
|
||||
|
||||
const valorParcela = Math.abs(lancamento.amount);
|
||||
const totalParcelas = lancamento.installmentCount ?? 1;
|
||||
const parcelaAtual = lancamento.currentInstallment ?? 1;
|
||||
const valorParcela = Math.abs(transaction.amount);
|
||||
const totalParcelas = transaction.installmentCount ?? 1;
|
||||
const parcelaAtual = transaction.currentInstallment ?? 1;
|
||||
const valorTotal = isInstallment
|
||||
? valorParcela * totalParcelas
|
||||
: valorParcela;
|
||||
@@ -62,10 +62,10 @@ export function LancamentoDetailsDialog({
|
||||
<CardHeader className="flex flex-row items-start border-b sm:border-b-0">
|
||||
<div>
|
||||
<DialogTitle className="group flex items-center gap-2 text-lg">
|
||||
#{lancamento.id}
|
||||
#{transaction.id}
|
||||
</DialogTitle>
|
||||
<CardDescription>
|
||||
{formatDate(lancamento.purchaseDate)}
|
||||
{formatDate(transaction.purchaseDate)}
|
||||
</CardDescription>
|
||||
</div>
|
||||
</CardHeader>
|
||||
@@ -73,11 +73,11 @@ export function LancamentoDetailsDialog({
|
||||
<CardContent className="text-sm">
|
||||
<div className="grid gap-3">
|
||||
<ul className="grid gap-3">
|
||||
<DetailRow label="Descrição" value={lancamento.name} />
|
||||
<DetailRow label="Descrição" value={transaction.name} />
|
||||
|
||||
<DetailRow
|
||||
label="Período"
|
||||
value={formatPeriod(lancamento.period)}
|
||||
value={formatPeriod(transaction.period)}
|
||||
/>
|
||||
|
||||
<li className="flex items-center justify-between">
|
||||
@@ -85,21 +85,21 @@ export function LancamentoDetailsDialog({
|
||||
Forma de Pagamento
|
||||
</span>
|
||||
<span className="flex items-center gap-1.5">
|
||||
{getPaymentMethodIcon(lancamento.paymentMethod)}
|
||||
{getPaymentMethodIcon(transaction.paymentMethod)}
|
||||
<span className="capitalize">
|
||||
{lancamento.paymentMethod}
|
||||
{transaction.paymentMethod}
|
||||
</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<DetailRow
|
||||
label={lancamento.cartaoName ? "Cartão" : "Conta"}
|
||||
value={lancamento.cartaoName ?? lancamento.contaName ?? "—"}
|
||||
label={transaction.cartaoName ? "Cartão" : "FinancialAccount"}
|
||||
value={transaction.cartaoName ?? transaction.contaName ?? "—"}
|
||||
/>
|
||||
|
||||
<DetailRow
|
||||
label="Categoria"
|
||||
value={lancamento.categoriaName ?? "—"}
|
||||
label="Category"
|
||||
value={transaction.categoriaName ?? "—"}
|
||||
/>
|
||||
|
||||
<li className="flex items-center justify-between">
|
||||
@@ -109,37 +109,37 @@ export function LancamentoDetailsDialog({
|
||||
<span className="capitalize">
|
||||
<Badge
|
||||
variant={getTransactionBadgeVariant(
|
||||
lancamento.categoriaName === "Saldo inicial"
|
||||
transaction.categoriaName === "Saldo inicial"
|
||||
? "Saldo inicial"
|
||||
: lancamento.transactionType,
|
||||
: transaction.transactionType,
|
||||
)}
|
||||
>
|
||||
{lancamento.categoriaName === "Saldo inicial"
|
||||
{transaction.categoriaName === "Saldo inicial"
|
||||
? "Saldo Inicial"
|
||||
: lancamento.transactionType}
|
||||
: transaction.transactionType}
|
||||
</Badge>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<DetailRow
|
||||
label="Condição"
|
||||
value={formatCondition(lancamento.condition)}
|
||||
value={formatCondition(transaction.condition)}
|
||||
/>
|
||||
|
||||
<li className="flex items-center justify-between">
|
||||
<span className="text-muted-foreground">Responsável</span>
|
||||
<span className="flex items-center gap-2 capitalize">
|
||||
<span>{lancamento.pagadorName}</span>
|
||||
<span>{transaction.pagadorName}</span>
|
||||
</span>
|
||||
</li>
|
||||
|
||||
<DetailRow
|
||||
label="Status"
|
||||
value={lancamento.isSettled ? "Pago" : "Pendente"}
|
||||
value={transaction.isSettled ? "Pago" : "Pendente"}
|
||||
/>
|
||||
|
||||
{lancamento.note && (
|
||||
<DetailRow label="Notas" value={lancamento.note} />
|
||||
{transaction.note && (
|
||||
<DetailRow label="Notas" value={transaction.note} />
|
||||
)}
|
||||
</ul>
|
||||
|
||||
@@ -148,11 +148,11 @@ export function LancamentoDetailsDialog({
|
||||
<li className="mt-4">
|
||||
<InstallmentTimeline
|
||||
purchaseDate={parseLocalDateString(
|
||||
lancamento.purchaseDate,
|
||||
transaction.purchaseDate,
|
||||
)}
|
||||
currentInstallment={parcelaAtual}
|
||||
totalInstallments={totalParcelas}
|
||||
period={lancamento.period}
|
||||
period={transaction.period}
|
||||
/>
|
||||
</li>
|
||||
)}
|
||||
@@ -169,10 +169,10 @@ export function LancamentoDetailsDialog({
|
||||
/>
|
||||
)}
|
||||
|
||||
{lancamento.recurrenceCount && (
|
||||
{transaction.recurrenceCount && (
|
||||
<DetailRow
|
||||
label="Quantidade de Recorrências"
|
||||
value={`${lancamento.recurrenceCount} meses`}
|
||||
value={`${transaction.recurrenceCount} meses`}
|
||||
/>
|
||||
)}
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { LANCAMENTO_TRANSACTION_TYPES } from "@/features/transactions/constants";
|
||||
import { TRANSACTION_TYPES } from "@/features/transactions/constants";
|
||||
import { Label } from "@/shared/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
@@ -13,7 +13,7 @@ import {
|
||||
} from "@/shared/components/ui/select";
|
||||
import { cn } from "@/shared/utils/ui";
|
||||
import {
|
||||
CategoriaSelectContent,
|
||||
CategorySelectContent,
|
||||
TransactionTypeSelectContent,
|
||||
} from "../../select-items";
|
||||
import type { CategorySectionProps } from "./transaction-dialog-types";
|
||||
@@ -21,8 +21,8 @@ import type { CategorySectionProps } from "./transaction-dialog-types";
|
||||
export function CategorySection({
|
||||
formState,
|
||||
onFieldChange,
|
||||
categoriaOptions,
|
||||
categoriaGroups,
|
||||
categoryOptions,
|
||||
categoryGroups,
|
||||
isUpdateMode,
|
||||
hideTransactionType = false,
|
||||
}: CategorySectionProps) {
|
||||
@@ -47,13 +47,13 @@ export function CategorySection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LANCAMENTO_TRANSACTION_TYPES.filter(
|
||||
(type) => type !== "Transferência",
|
||||
).map((type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
<TransactionTypeSelectContent label={type} />
|
||||
</SelectItem>
|
||||
))}
|
||||
{TRANSACTION_TYPES.filter((type) => type !== "Transferência").map(
|
||||
(type) => (
|
||||
<SelectItem key={type} value={type}>
|
||||
<TransactionTypeSelectContent label={type} />
|
||||
</SelectItem>
|
||||
),
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
</div>
|
||||
@@ -65,20 +65,20 @@ export function CategorySection({
|
||||
showTransactionTypeField ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<Label htmlFor="categoria">Categoria</Label>
|
||||
<Label htmlFor="categoria">Category</Label>
|
||||
<Select
|
||||
value={formState.categoriaId}
|
||||
onValueChange={(value) => onFieldChange("categoriaId", value)}
|
||||
value={formState.categoryId}
|
||||
onValueChange={(value) => onFieldChange("categoryId", value)}
|
||||
>
|
||||
<SelectTrigger id="categoria" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.categoriaId &&
|
||||
{formState.categoryId &&
|
||||
(() => {
|
||||
const selectedOption = categoriaOptions.find(
|
||||
(opt) => opt.value === formState.categoriaId,
|
||||
const selectedOption = categoryOptions.find(
|
||||
(opt) => opt.value === formState.categoryId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<CategoriaSelectContent
|
||||
<CategorySelectContent
|
||||
label={selectedOption.label}
|
||||
icon={selectedOption.icon}
|
||||
/>
|
||||
@@ -87,12 +87,12 @@ export function CategorySection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{categoriaGroups.map((group) => (
|
||||
{categoryGroups.map((group) => (
|
||||
<SelectGroup key={group.label}>
|
||||
<SelectLabel>{group.label}</SelectLabel>
|
||||
{group.options.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<CategoriaSelectContent
|
||||
<CategorySelectContent
|
||||
label={option.label}
|
||||
icon={option.icon}
|
||||
/>
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
"use client";
|
||||
|
||||
import { LANCAMENTO_CONDITIONS } from "@/features/transactions/constants";
|
||||
import { TRANSACTION_CONDITIONS } from "@/features/transactions/constants";
|
||||
import { Label } from "@/shared/components/ui/label";
|
||||
import {
|
||||
Select,
|
||||
@@ -64,7 +64,7 @@ export function ConditionSection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LANCAMENTO_CONDITIONS.map((condition) => (
|
||||
{TRANSACTION_CONDITIONS.map((condition) => (
|
||||
<SelectItem key={condition} value={condition}>
|
||||
<ConditionSelectContent label={condition} />
|
||||
</SelectItem>
|
||||
|
||||
@@ -9,16 +9,16 @@ import {
|
||||
SelectTrigger,
|
||||
SelectValue,
|
||||
} from "@/shared/components/ui/select";
|
||||
import { PagadorSelectContent } from "../../select-items";
|
||||
import type { PagadorSectionProps } from "./transaction-dialog-types";
|
||||
import { PayerSelectContent } from "../../select-items";
|
||||
import type { PayerSectionProps } from "./transaction-dialog-types";
|
||||
|
||||
export function PagadorSection({
|
||||
export function PayerSection({
|
||||
formState,
|
||||
onFieldChange,
|
||||
pagadorOptions,
|
||||
secondaryPagadorOptions,
|
||||
payerOptions,
|
||||
secondaryPayerOptions,
|
||||
totalAmount,
|
||||
}: PagadorSectionProps) {
|
||||
}: PayerSectionProps) {
|
||||
const handlePrimaryAmountChange = (value: string) => {
|
||||
onFieldChange("primarySplitAmount", value);
|
||||
const numericValue = Number.parseFloat(value) || 0;
|
||||
@@ -36,24 +36,24 @@ export function PagadorSection({
|
||||
return (
|
||||
<div className="flex w-full flex-col gap-2 md:flex-row">
|
||||
<div className="w-full space-y-1">
|
||||
<Label htmlFor="pagador">Pagador</Label>
|
||||
<Label htmlFor="payer">Payer</Label>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
value={formState.pagadorId}
|
||||
onValueChange={(value) => onFieldChange("pagadorId", value)}
|
||||
value={formState.payerId}
|
||||
onValueChange={(value) => onFieldChange("payerId", value)}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="pagador"
|
||||
id="payer"
|
||||
className={formState.isSplit ? "w-[55%]" : "w-full"}
|
||||
>
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.pagadorId &&
|
||||
{formState.payerId &&
|
||||
(() => {
|
||||
const selectedOption = pagadorOptions.find(
|
||||
(opt) => opt.value === formState.pagadorId,
|
||||
const selectedOption = payerOptions.find(
|
||||
(opt) => opt.value === formState.payerId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={selectedOption.label}
|
||||
avatarUrl={selectedOption.avatarUrl}
|
||||
/>
|
||||
@@ -62,9 +62,9 @@ export function PagadorSection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{pagadorOptions.map((option) => (
|
||||
{payerOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={option.label}
|
||||
avatarUrl={option.avatarUrl}
|
||||
/>
|
||||
@@ -85,27 +85,27 @@ export function PagadorSection({
|
||||
|
||||
{formState.isSplit ? (
|
||||
<div className="w-full space-y-1 mb-1">
|
||||
<Label htmlFor="secondaryPagador">Dividir com</Label>
|
||||
<Label htmlFor="secondaryPayer">Dividir com</Label>
|
||||
<div className="flex gap-2">
|
||||
<Select
|
||||
value={formState.secondaryPagadorId}
|
||||
value={formState.secondaryPayerId}
|
||||
onValueChange={(value) =>
|
||||
onFieldChange("secondaryPagadorId", value)
|
||||
onFieldChange("secondaryPayerId", value)
|
||||
}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="secondaryPagador"
|
||||
disabled={secondaryPagadorOptions.length === 0}
|
||||
id="secondaryPayer"
|
||||
disabled={secondaryPayerOptions.length === 0}
|
||||
className="w-[55%]"
|
||||
>
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.secondaryPagadorId &&
|
||||
{formState.secondaryPayerId &&
|
||||
(() => {
|
||||
const selectedOption = secondaryPagadorOptions.find(
|
||||
(opt) => opt.value === formState.secondaryPagadorId,
|
||||
const selectedOption = secondaryPayerOptions.find(
|
||||
(opt) => opt.value === formState.secondaryPayerId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={selectedOption.label}
|
||||
avatarUrl={selectedOption.avatarUrl}
|
||||
/>
|
||||
@@ -114,9 +114,9 @@ export function PagadorSection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{secondaryPagadorOptions.map((option) => (
|
||||
{secondaryPayerOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<PagadorSelectContent
|
||||
<PayerSelectContent
|
||||
label={option.label}
|
||||
avatarUrl={option.avatarUrl}
|
||||
/>
|
||||
@@ -1,7 +1,7 @@
|
||||
"use client";
|
||||
|
||||
import { useState } from "react";
|
||||
import { LANCAMENTO_PAYMENT_METHODS } from "@/features/transactions/constants";
|
||||
import { PAYMENT_METHODS } from "@/features/transactions/constants";
|
||||
import { Label } from "@/shared/components/ui/label";
|
||||
import { MonthPicker } from "@/shared/components/ui/month-picker";
|
||||
import {
|
||||
@@ -23,7 +23,7 @@ import {
|
||||
} from "@/shared/utils/period";
|
||||
import { cn } from "@/shared/utils/ui";
|
||||
import {
|
||||
ContaCartaoSelectContent,
|
||||
AccountCardSelectContent,
|
||||
PaymentMethodSelectContent,
|
||||
} from "../../select-items";
|
||||
import type { PaymentMethodSectionProps } from "./transaction-dialog-types";
|
||||
@@ -39,7 +39,7 @@ function InlinePeriodPicker({
|
||||
|
||||
return (
|
||||
<div className="ml-1">
|
||||
<span className="text-xs text-muted-foreground">Fatura de </span>
|
||||
<span className="text-xs text-muted-foreground">Invoice de </span>
|
||||
<Popover open={open} onOpenChange={setOpen}>
|
||||
<PopoverTrigger asChild>
|
||||
<button
|
||||
@@ -66,11 +66,11 @@ function InlinePeriodPicker({
|
||||
export function PaymentMethodSection({
|
||||
formState,
|
||||
onFieldChange,
|
||||
contaOptions,
|
||||
cartaoOptions,
|
||||
accountOptions,
|
||||
cardOptions,
|
||||
isUpdateMode,
|
||||
disablePaymentMethod,
|
||||
disableCartaoSelect,
|
||||
disableCardSelect,
|
||||
}: PaymentMethodSectionProps) {
|
||||
const isCartaoSelected = formState.paymentMethod === "Cartão de crédito";
|
||||
const showContaSelect = [
|
||||
@@ -85,10 +85,10 @@ export function PaymentMethodSection({
|
||||
// Filtrar contas apenas do tipo "Pré-Pago | VR/VA" quando forma de pagamento for "Pré-Pago | VR/VA"
|
||||
const filteredContaOptions =
|
||||
formState.paymentMethod === "Pré-Pago | VR/VA"
|
||||
? contaOptions.filter(
|
||||
? accountOptions.filter(
|
||||
(option) => option.accountType === "Pré-Pago | VR/VA",
|
||||
)
|
||||
: contaOptions;
|
||||
: accountOptions;
|
||||
|
||||
return (
|
||||
<>
|
||||
@@ -120,7 +120,7 @@ export function PaymentMethodSection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{LANCAMENTO_PAYMENT_METHODS.map((method) => (
|
||||
{PAYMENT_METHODS.map((method) => (
|
||||
<SelectItem key={method} value={method}>
|
||||
<PaymentMethodSelectContent label={method} />
|
||||
</SelectItem>
|
||||
@@ -133,23 +133,23 @@ export function PaymentMethodSection({
|
||||
<div className="space-y-1 w-full md:w-1/2">
|
||||
<Label htmlFor="cartao">Cartão</Label>
|
||||
<Select
|
||||
value={formState.cartaoId}
|
||||
onValueChange={(value) => onFieldChange("cartaoId", value)}
|
||||
disabled={disableCartaoSelect}
|
||||
value={formState.cardId}
|
||||
onValueChange={(value) => onFieldChange("cardId", value)}
|
||||
disabled={disableCardSelect}
|
||||
>
|
||||
<SelectTrigger
|
||||
id="cartao"
|
||||
className="w-full"
|
||||
disabled={disableCartaoSelect}
|
||||
disabled={disableCardSelect}
|
||||
>
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.cartaoId &&
|
||||
{formState.cardId &&
|
||||
(() => {
|
||||
const selectedOption = cartaoOptions.find(
|
||||
(opt) => opt.value === formState.cartaoId,
|
||||
const selectedOption = cardOptions.find(
|
||||
(opt) => opt.value === formState.cardId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={true}
|
||||
@@ -159,16 +159,16 @@ export function PaymentMethodSection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{cartaoOptions.length === 0 ? (
|
||||
{cardOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhum cartão cadastrado
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
cartaoOptions.map((option) => (
|
||||
cardOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={true}
|
||||
@@ -178,7 +178,7 @@ export function PaymentMethodSection({
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{formState.cartaoId ? (
|
||||
{formState.cardId ? (
|
||||
<InlinePeriodPicker
|
||||
period={formState.period}
|
||||
onPeriodChange={(value) => onFieldChange("period", value)}
|
||||
@@ -194,20 +194,20 @@ export function PaymentMethodSection({
|
||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<Label htmlFor="conta">Conta</Label>
|
||||
<Label htmlFor="conta">FinancialAccount</Label>
|
||||
<Select
|
||||
value={formState.contaId}
|
||||
onValueChange={(value) => onFieldChange("contaId", value)}
|
||||
value={formState.accountId}
|
||||
onValueChange={(value) => onFieldChange("accountId", value)}
|
||||
>
|
||||
<SelectTrigger id="conta" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.contaId &&
|
||||
{formState.accountId &&
|
||||
(() => {
|
||||
const selectedOption = filteredContaOptions.find(
|
||||
(opt) => opt.value === formState.contaId,
|
||||
(opt) => opt.value === formState.accountId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={false}
|
||||
@@ -226,7 +226,7 @@ export function PaymentMethodSection({
|
||||
) : (
|
||||
filteredContaOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={false}
|
||||
@@ -252,18 +252,18 @@ export function PaymentMethodSection({
|
||||
>
|
||||
<Label htmlFor="cartaoUpdate">Cartão</Label>
|
||||
<Select
|
||||
value={formState.cartaoId}
|
||||
onValueChange={(value) => onFieldChange("cartaoId", value)}
|
||||
value={formState.cardId}
|
||||
onValueChange={(value) => onFieldChange("cardId", value)}
|
||||
>
|
||||
<SelectTrigger id="cartaoUpdate" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.cartaoId &&
|
||||
{formState.cardId &&
|
||||
(() => {
|
||||
const selectedOption = cartaoOptions.find(
|
||||
(opt) => opt.value === formState.cartaoId,
|
||||
const selectedOption = cardOptions.find(
|
||||
(opt) => opt.value === formState.cardId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={true}
|
||||
@@ -273,16 +273,16 @@ export function PaymentMethodSection({
|
||||
</SelectValue>
|
||||
</SelectTrigger>
|
||||
<SelectContent>
|
||||
{cartaoOptions.length === 0 ? (
|
||||
{cardOptions.length === 0 ? (
|
||||
<div className="px-2 py-6 text-center">
|
||||
<p className="text-sm text-muted-foreground">
|
||||
Nenhum cartão cadastrado
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
cartaoOptions.map((option) => (
|
||||
cardOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={true}
|
||||
@@ -292,7 +292,7 @@ export function PaymentMethodSection({
|
||||
)}
|
||||
</SelectContent>
|
||||
</Select>
|
||||
{formState.cartaoId ? (
|
||||
{formState.cardId ? (
|
||||
<InlinePeriodPicker
|
||||
period={formState.period}
|
||||
onPeriodChange={(value) => onFieldChange("period", value)}
|
||||
@@ -308,20 +308,20 @@ export function PaymentMethodSection({
|
||||
!isUpdateMode ? "md:w-1/2" : "md:w-full",
|
||||
)}
|
||||
>
|
||||
<Label htmlFor="contaUpdate">Conta</Label>
|
||||
<Label htmlFor="contaUpdate">FinancialAccount</Label>
|
||||
<Select
|
||||
value={formState.contaId}
|
||||
onValueChange={(value) => onFieldChange("contaId", value)}
|
||||
value={formState.accountId}
|
||||
onValueChange={(value) => onFieldChange("accountId", value)}
|
||||
>
|
||||
<SelectTrigger id="contaUpdate" className="w-full">
|
||||
<SelectValue placeholder="Selecione">
|
||||
{formState.contaId &&
|
||||
{formState.accountId &&
|
||||
(() => {
|
||||
const selectedOption = filteredContaOptions.find(
|
||||
(opt) => opt.value === formState.contaId,
|
||||
(opt) => opt.value === formState.accountId,
|
||||
);
|
||||
return selectedOption ? (
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={selectedOption.label}
|
||||
logo={selectedOption.logo}
|
||||
isCartao={false}
|
||||
@@ -340,7 +340,7 @@ export function PaymentMethodSection({
|
||||
) : (
|
||||
filteredContaOptions.map((option) => (
|
||||
<SelectItem key={option.value} value={option.value}>
|
||||
<ContaCartaoSelectContent
|
||||
<AccountCardSelectContent
|
||||
label={option.label}
|
||||
logo={option.logo}
|
||||
isCartao={false}
|
||||
|
||||
@@ -1,28 +1,28 @@
|
||||
import type { LancamentoFormState } from "@/features/transactions/form-helpers";
|
||||
import type { LancamentoItem, SelectOption } from "../../types";
|
||||
import type { TransactionFormState } from "@/features/transactions/form-helpers";
|
||||
import type { SelectOption, TransactionItem } from "../../types";
|
||||
|
||||
export type FormState = LancamentoFormState;
|
||||
export type FormState = TransactionFormState;
|
||||
|
||||
export interface LancamentoDialogProps {
|
||||
export interface TransactionDialogProps {
|
||||
mode: "create" | "update";
|
||||
trigger?: React.ReactNode;
|
||||
open?: boolean;
|
||||
onOpenChange?: (open: boolean) => void;
|
||||
pagadorOptions: SelectOption[];
|
||||
splitPagadorOptions: SelectOption[];
|
||||
defaultPagadorId?: string | null;
|
||||
contaOptions: SelectOption[];
|
||||
cartaoOptions: SelectOption[];
|
||||
categoriaOptions: SelectOption[];
|
||||
payerOptions: SelectOption[];
|
||||
splitPayerOptions: SelectOption[];
|
||||
defaultPayerId?: string | null;
|
||||
accountOptions: SelectOption[];
|
||||
cardOptions: SelectOption[];
|
||||
categoryOptions: SelectOption[];
|
||||
estabelecimentos: string[];
|
||||
lancamento?: LancamentoItem;
|
||||
transaction?: TransactionItem;
|
||||
defaultPeriod?: string;
|
||||
defaultCartaoId?: string | null;
|
||||
defaultCardId?: string | null;
|
||||
defaultPaymentMethod?: string | null;
|
||||
defaultPurchaseDate?: string | null;
|
||||
defaultName?: string | null;
|
||||
defaultAmount?: string | null;
|
||||
lockCartaoSelection?: boolean;
|
||||
lockCardSelection?: boolean;
|
||||
lockPaymentMethod?: boolean;
|
||||
isImporting?: boolean;
|
||||
defaultTransactionType?: "Despesa" | "Receita";
|
||||
@@ -33,11 +33,11 @@ export interface LancamentoDialogProps {
|
||||
onBulkEditRequest?: (data: {
|
||||
id: string;
|
||||
name: string;
|
||||
categoriaId: string | undefined;
|
||||
categoryId: string | undefined;
|
||||
note: string;
|
||||
pagadorId: string | undefined;
|
||||
contaId: string | undefined;
|
||||
cartaoId: string | undefined;
|
||||
payerId: string | undefined;
|
||||
accountId: string | undefined;
|
||||
cardId: string | undefined;
|
||||
amount: number;
|
||||
dueDate: string | null;
|
||||
boletoPaymentDate: string | null;
|
||||
@@ -57,8 +57,8 @@ export interface BasicFieldsSectionProps extends BaseFieldSectionProps {
|
||||
}
|
||||
|
||||
export interface CategorySectionProps extends BaseFieldSectionProps {
|
||||
categoriaOptions: SelectOption[];
|
||||
categoriaGroups: Array<{
|
||||
categoryOptions: SelectOption[];
|
||||
categoryGroups: Array<{
|
||||
label: string;
|
||||
options: SelectOption[];
|
||||
}>;
|
||||
@@ -70,18 +70,18 @@ export interface SplitAndSettlementSectionProps extends BaseFieldSectionProps {
|
||||
showSettledToggle: boolean;
|
||||
}
|
||||
|
||||
export interface PagadorSectionProps extends BaseFieldSectionProps {
|
||||
pagadorOptions: SelectOption[];
|
||||
secondaryPagadorOptions: SelectOption[];
|
||||
export interface PayerSectionProps extends BaseFieldSectionProps {
|
||||
payerOptions: SelectOption[];
|
||||
secondaryPayerOptions: SelectOption[];
|
||||
totalAmount: number;
|
||||
}
|
||||
|
||||
export interface PaymentMethodSectionProps extends BaseFieldSectionProps {
|
||||
contaOptions: SelectOption[];
|
||||
cartaoOptions: SelectOption[];
|
||||
accountOptions: SelectOption[];
|
||||
cardOptions: SelectOption[];
|
||||
isUpdateMode: boolean;
|
||||
disablePaymentMethod: boolean;
|
||||
disableCartaoSelect: boolean;
|
||||
disableCardSelect: boolean;
|
||||
}
|
||||
|
||||
export interface BoletoFieldsSectionProps extends BaseFieldSectionProps {
|
||||
|
||||
@@ -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 ? (
|
||||
|
||||
Reference in New Issue
Block a user