"use client"; import { RiAddLine, RiDeleteBinLine } from "@remixicon/react"; import { useMemo, useState } from "react"; import { toast } from "sonner"; import { Button } from "@/components/ui/button"; import { CurrencyInput } from "@/components/ui/currency-input"; import { DatePicker } from "@/components/ui/date-picker"; import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from "@/components/ui/dialog"; import { Label } from "@/components/ui/label"; import { MonthPicker } from "@/components/ui/month-picker"; import { Popover, PopoverContent, PopoverTrigger, } from "@/components/ui/popover"; import { Select, SelectContent, SelectGroup, SelectItem, SelectLabel, SelectTrigger, SelectValue, } from "@/components/ui/select"; import { Separator } from "@/components/ui/separator"; import { Spinner } from "@/components/ui/spinner"; import { groupAndSortCategorias } from "@/lib/lancamentos/categoria-helpers"; import { LANCAMENTO_PAYMENT_METHODS } from "@/lib/lancamentos/constants"; import { getTodayDateString } from "@/lib/utils/date"; import { displayPeriod } from "@/lib/utils/period"; import { CategoriaSelectContent, ContaCartaoSelectContent, PagadorSelectContent, PaymentMethodSelectContent, TransactionTypeSelectContent, } from "../select-items"; import { EstabelecimentoInput } from "../shared/estabelecimento-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", ); function periodToDate(period: string): Date { const [year, month] = period.split("-").map(Number); return new Date(year, month - 1, 1); } function dateToPeriod(date: Date): string { const year = date.getFullYear(); const month = String(date.getMonth() + 1).padStart(2, "0"); return `${year}-${month}`; } function InlinePeriodPicker({ period, onPeriodChange, }: { period: string; onPeriodChange: (value: string) => void; }) { const [open, setOpen] = useState(false); return (
Fatura de { onPeriodChange(dateToPeriod(date)); setOpen(false); }} />
); } interface MassAddDialogProps { open: boolean; onOpenChange: (open: boolean) => void; onSubmit: (data: MassAddFormData) => Promise; pagadorOptions: SelectOption[]; contaOptions: SelectOption[]; cartaoOptions: SelectOption[]; categoriaOptions: SelectOption[]; estabelecimentos: string[]; selectedPeriod: string; defaultPagadorId?: string | null; defaultCartaoId?: string | null; } export interface MassAddFormData { fixedFields: { transactionType?: string; paymentMethod?: string; condition?: string; period?: string; contaId?: string; cartaoId?: string; }; transactions: Array<{ purchaseDate: string; name: string; amount: string; categoriaId?: string; pagadorId?: string; }>; } interface TransactionRow { id: string; purchaseDate: string; name: string; amount: string; categoriaId: string | undefined; pagadorId: string | undefined; } export function MassAddDialog({ open, onOpenChange, onSubmit, pagadorOptions, contaOptions, cartaoOptions, categoriaOptions, estabelecimentos, selectedPeriod, defaultPagadorId, defaultCartaoId, }: MassAddDialogProps) { const [loading, setLoading] = useState(false); // Fixed fields state (sempre ativos, sem checkboxes) const [transactionType, setTransactionType] = useState("Despesa"); const [paymentMethod, setPaymentMethod] = useState( LANCAMENTO_PAYMENT_METHODS[0], ); const [period, setPeriod] = useState(selectedPeriod); const [contaId, setContaId] = useState(undefined); const [cartaoId, setCartaoId] = useState( defaultCartaoId ?? undefined, ); // Quando defaultCartaoId está definido, exibe apenas o cartão específico const isLockedToCartao = !!defaultCartaoId; const isCartaoSelected = paymentMethod === "Cartão de crédito"; // Transaction rows const [transactions, setTransactions] = useState([ { id: crypto.randomUUID(), purchaseDate: getTodayDateString(), name: "", amount: "", categoriaId: undefined, pagadorId: defaultPagadorId ?? undefined, }, ]); // Categorias agrupadas e filtradas por tipo de transação const groupedCategorias = useMemo(() => { const filtered = categoriaOptions.filter( (option) => option.group?.toLowerCase() === transactionType.toLowerCase(), ); return groupAndSortCategorias(filtered); }, [categoriaOptions, transactionType]); const addTransaction = () => { setTransactions([ ...transactions, { id: crypto.randomUUID(), purchaseDate: getTodayDateString(), name: "", amount: "", categoriaId: undefined, pagadorId: defaultPagadorId ?? undefined, }, ]); }; const removeTransaction = (id: string) => { if (transactions.length === 1) { toast.error("É necessário ter pelo menos uma transação"); return; } setTransactions(transactions.filter((t) => t.id !== id)); }; const updateTransaction = ( id: string, field: keyof TransactionRow, value: string | undefined, ) => { setTransactions( transactions.map((t) => (t.id === id ? { ...t, [field]: value } : t)), ); }; const handleSubmit = async () => { // Validate conta/cartao selection if (isCartaoSelected && !cartaoId) { toast.error("Selecione um cartão para continuar"); return; } if (!isCartaoSelected && !contaId) { toast.error("Selecione uma conta para continuar"); return; } // Validate transactions const invalidTransactions = transactions.filter( (t) => !t.name.trim() || !t.amount.trim() || !t.purchaseDate, ); if (invalidTransactions.length > 0) { toast.error( "Preencha todos os campos obrigatórios das transações (data, estabelecimento e valor)", ); return; } // Build form data const formData: MassAddFormData = { fixedFields: { transactionType, paymentMethod, condition: "À vista", period, contaId, cartaoId, }, transactions: transactions.map((t) => ({ purchaseDate: t.purchaseDate, name: t.name.trim(), amount: t.amount.trim(), categoriaId: t.categoriaId, pagadorId: t.pagadorId, })), }; setLoading(true); try { await onSubmit(formData); onOpenChange(false); // Reset form setTransactionType("Despesa"); setPaymentMethod(LANCAMENTO_PAYMENT_METHODS[0]); setPeriod(selectedPeriod); setContaId(undefined); setCartaoId(defaultCartaoId ?? undefined); setTransactions([ { id: crypto.randomUUID(), purchaseDate: getTodayDateString(), name: "", amount: "", categoriaId: undefined, pagadorId: defaultPagadorId ?? undefined, }, ]); } catch (_error) { // Error is handled by the onSubmit function } finally { setLoading(false); } }; return ( Adicionar múltiplos lançamentos Configure os valores padrão e adicione várias transações de uma vez. Todos os lançamentos adicionados aqui são{" "} sempre à vista.
{/* Fixed Fields Section */}

Valores Padrão

{/* Transaction Type */}
{/* Payment Method */}
{/* Cartão (only for credit card) */} {isCartaoSelected ? (
{cartaoId ? ( ) : null}
) : null} {/* Conta (for non-credit-card methods) */} {!isCartaoSelected ? (
) : null}
{/* Transactions Section */}

Lançamentos

{transactions.map((transaction, index) => (
updateTransaction( transaction.id, "purchaseDate", value, ) } placeholder="Data" compact required />
updateTransaction(transaction.id, "name", value) } estabelecimentos={estabelecimentos} required />
updateTransaction(transaction.id, "amount", value) } required />
))}
); }