"use client"; import { createMassLancamentosAction, deleteLancamentoAction, deleteLancamentoBulkAction, deleteMultipleLancamentosAction, toggleLancamentoSettlementAction, updateLancamentoBulkAction, } from "@/app/(dashboard)/lancamentos/actions"; import { ConfirmActionDialog } from "@/components/confirm-action-dialog"; import { useCallback, useState } from "react"; import { toast } from "sonner"; import { AnticipateInstallmentsDialog } from "../dialogs/anticipate-installments-dialog/anticipate-installments-dialog"; import { AnticipationHistoryDialog } from "../dialogs/anticipate-installments-dialog/anticipation-history-dialog"; import { BulkActionDialog, type BulkActionScope } from "../dialogs/bulk-action-dialog"; import { LancamentoDetailsDialog } from "../dialogs/lancamento-details-dialog"; import { LancamentoDialog } from "../dialogs/lancamento-dialog/lancamento-dialog"; import { LancamentosTable } from "../table/lancamentos-table"; import { MassAddDialog, type MassAddFormData } from "../dialogs/mass-add-dialog"; import type { ContaCartaoFilterOption, LancamentoFilterOption, LancamentoItem, SelectOption, } from "../types"; interface LancamentosPageProps { lancamentos: LancamentoItem[]; pagadorOptions: SelectOption[]; splitPagadorOptions: SelectOption[]; defaultPagadorId: string | null; contaOptions: SelectOption[]; cartaoOptions: SelectOption[]; categoriaOptions: SelectOption[]; pagadorFilterOptions: LancamentoFilterOption[]; categoriaFilterOptions: LancamentoFilterOption[]; contaCartaoFilterOptions: ContaCartaoFilterOption[]; selectedPeriod: string; estabelecimentos: string[]; allowCreate?: boolean; defaultCartaoId?: string | null; defaultPaymentMethod?: string | null; lockCartaoSelection?: boolean; lockPaymentMethod?: boolean; } export function LancamentosPage({ lancamentos, pagadorOptions, splitPagadorOptions, defaultPagadorId, contaOptions, cartaoOptions, categoriaOptions, pagadorFilterOptions, categoriaFilterOptions, contaCartaoFilterOptions, selectedPeriod, estabelecimentos, allowCreate = true, defaultCartaoId, defaultPaymentMethod, lockCartaoSelection, lockPaymentMethod, }: LancamentosPageProps) { const [selectedLancamento, setSelectedLancamento] = useState(null); const [editOpen, setEditOpen] = useState(false); const [createOpen, setCreateOpen] = useState(false); const [copyOpen, setCopyOpen] = useState(false); const [lancamentoToCopy, setLancamentoToCopy] = useState(null); const [massAddOpen, setMassAddOpen] = useState(false); const [deleteOpen, setDeleteOpen] = useState(false); const [lancamentoToDelete, setLancamentoToDelete] = useState(null); const [detailsOpen, setDetailsOpen] = useState(false); const [settlementLoadingId, setSettlementLoadingId] = useState( null ); const [bulkEditOpen, setBulkEditOpen] = useState(false); const [bulkDeleteOpen, setBulkDeleteOpen] = useState(false); const [pendingEditData, setPendingEditData] = useState<{ id: string; name: string; categoriaId: string | undefined; note: string; pagadorId: string | undefined; contaId: string | undefined; cartaoId: string | undefined; amount: number; dueDate: string | null; boletoPaymentDate: string | null; lancamento: LancamentoItem; } | null>(null); const [pendingDeleteData, setPendingDeleteData] = useState(null); const [multipleBulkDeleteOpen, setMultipleBulkDeleteOpen] = useState(false); const [pendingMultipleDeleteData, setPendingMultipleDeleteData] = useState< LancamentoItem[] >([]); const [anticipateOpen, setAnticipateOpen] = useState(false); const [anticipationHistoryOpen, setAnticipationHistoryOpen] = useState(false); const [selectedForAnticipation, setSelectedForAnticipation] = useState(null); const handleToggleSettlement = useCallback(async (item: LancamentoItem) => { if (item.paymentMethod === "Cartão de crédito") { toast.info( "Pagamentos com cartão são conciliados automaticamente. Ajuste pelo cartão." ); return; } const supportedMethods = ["Pix", "Boleto", "Dinheiro", "Cartão de débito"]; if (!supportedMethods.includes(item.paymentMethod)) { return; } const nextValue = !Boolean(item.isSettled); try { setSettlementLoadingId(item.id); const result = await toggleLancamentoSettlementAction({ id: item.id, value: nextValue, }); if (!result.success) { throw new Error(result.error); } toast.success(result.message); } catch (error) { const message = error instanceof Error ? error.message : "Não foi possível atualizar o pagamento."; toast.error(message); } finally { setSettlementLoadingId(null); } }, []); const handleDelete = useCallback(async () => { if (!lancamentoToDelete) { return; } const result = await deleteLancamentoAction({ id: lancamentoToDelete.id, }); if (!result.success) { toast.error(result.error); throw new Error(result.error); } toast.success(result.message); setDeleteOpen(false); }, [lancamentoToDelete]); const handleBulkDelete = useCallback( async (scope: BulkActionScope) => { if (!pendingDeleteData) { return; } const result = await deleteLancamentoBulkAction({ id: pendingDeleteData.id, scope, }); if (!result.success) { toast.error(result.error); throw new Error(result.error); } toast.success(result.message); setBulkDeleteOpen(false); setPendingDeleteData(null); }, [pendingDeleteData] ); const handleBulkEditRequest = useCallback( (data: { id: string; name: string; categoriaId: string | undefined; note: string; pagadorId: string | undefined; contaId: string | undefined; cartaoId: string | undefined; amount: number; dueDate: string | null; boletoPaymentDate: string | null; }) => { if (!selectedLancamento) { return; } setPendingEditData({ ...data, lancamento: selectedLancamento, }); setEditOpen(false); setBulkEditOpen(true); }, [selectedLancamento] ); const handleBulkEdit = useCallback( async (scope: BulkActionScope) => { if (!pendingEditData) { return; } const result = await updateLancamentoBulkAction({ id: pendingEditData.id, scope, name: pendingEditData.name, categoriaId: pendingEditData.categoriaId, note: pendingEditData.note, pagadorId: pendingEditData.pagadorId, contaId: pendingEditData.contaId, cartaoId: pendingEditData.cartaoId, amount: pendingEditData.amount, dueDate: pendingEditData.dueDate, boletoPaymentDate: pendingEditData.boletoPaymentDate, }); if (!result.success) { toast.error(result.error); throw new Error(result.error); } toast.success(result.message); setBulkEditOpen(false); setPendingEditData(null); }, [pendingEditData] ); const handleMassAddSubmit = useCallback(async (data: MassAddFormData) => { const result = await createMassLancamentosAction(data); if (!result.success) { toast.error(result.error); throw new Error(result.error); } toast.success(result.message); }, []); const handleMultipleBulkDelete = useCallback((items: LancamentoItem[]) => { setPendingMultipleDeleteData(items); setMultipleBulkDeleteOpen(true); }, []); const confirmMultipleBulkDelete = useCallback(async () => { if (pendingMultipleDeleteData.length === 0) { return; } const ids = pendingMultipleDeleteData.map((item) => item.id); const result = await deleteMultipleLancamentosAction({ ids }); if (!result.success) { toast.error(result.error); throw new Error(result.error); } toast.success(result.message); setMultipleBulkDeleteOpen(false); setPendingMultipleDeleteData([]); }, [pendingMultipleDeleteData]); const handleCreate = useCallback(() => { setCreateOpen(true); }, []); const handleMassAdd = useCallback(() => { setMassAddOpen(true); }, []); const handleEdit = useCallback((item: LancamentoItem) => { setSelectedLancamento(item); setEditOpen(true); }, []); const handleCopy = useCallback((item: LancamentoItem) => { setLancamentoToCopy(item); setCopyOpen(true); }, []); const handleConfirmDelete = useCallback((item: LancamentoItem) => { if (item.seriesId) { setPendingDeleteData(item); setBulkDeleteOpen(true); } else { setLancamentoToDelete(item); setDeleteOpen(true); } }, []); const handleViewDetails = useCallback((item: LancamentoItem) => { setSelectedLancamento(item); setDetailsOpen(true); }, []); const handleAnticipate = useCallback((item: LancamentoItem) => { setSelectedForAnticipation(item); setAnticipateOpen(true); }, []); const handleViewAnticipationHistory = useCallback((item: LancamentoItem) => { setSelectedForAnticipation(item); setAnticipationHistoryOpen(true); }, []); return ( <> settlementLoadingId === id} /> {allowCreate ? ( ) : null} { setCopyOpen(open); if (!open) { setLancamentoToCopy(null); } }} pagadorOptions={pagadorOptions} splitPagadorOptions={splitPagadorOptions} defaultPagadorId={defaultPagadorId} contaOptions={contaOptions} cartaoOptions={cartaoOptions} categoriaOptions={categoriaOptions} estabelecimentos={estabelecimentos} lancamento={lancamentoToCopy ?? undefined} defaultPeriod={selectedPeriod} /> { setDetailsOpen(open); if (!open) { setSelectedLancamento(null); } }} lancamento={detailsOpen ? selectedLancamento : null} /> {allowCreate ? ( ) : null} 0} onOpenChange={setMultipleBulkDeleteOpen} title={`Remover ${pendingMultipleDeleteData.length} ${ pendingMultipleDeleteData.length === 1 ? "lançamento" : "lançamentos" }?`} description="Essa ação é irreversível e removerá os lançamentos selecionados de forma permanente." confirmLabel="Remover" pendingLabel="Removendo..." confirmVariant="destructive" onConfirm={confirmMultipleBulkDelete} disabled={pendingMultipleDeleteData.length === 0} /> {/* Dialogs de Antecipação */} {selectedForAnticipation && ( ({ id: c.value, name: c.label, icon: c.icon ?? null, }))} pagadores={pagadorOptions.map((p) => ({ id: p.value, name: p.label, }))} defaultPeriod={selectedPeriod} /> )} {selectedForAnticipation && ( { const lancamento = lancamentos.find((l) => l.id === lancamentoId); if (lancamento) { setSelectedLancamento(lancamento); setDetailsOpen(true); setAnticipationHistoryOpen(false); } }} /> )} ); }