import { BrowserFrame } from "@/components/Browser/BrowserFrame"; import { useState } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Button } from "@/components/ui/button"; import { Card, CardContent, CardHeader, CardTitle, CardDescription } from "@/components/ui/card"; import { Input } from "@/components/ui/input"; import { Label } from "@/components/ui/label"; import { Tabs, TabsContent, TabsList, TabsTrigger } from "@/components/ui/tabs"; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from "@/components/ui/table"; import { Badge } from "@/components/ui/badge"; import { Dialog, DialogContent, DialogHeader, DialogTitle, DialogFooter } from "@/components/ui/dialog"; import { Select, SelectContent, SelectItem, SelectTrigger, SelectValue } from "@/components/ui/select"; import { Textarea } from "@/components/ui/textarea"; import { ScrollArea } from "@/components/ui/scroll-area"; import { useToast } from "@/hooks/use-toast"; import { useAuth } from "@/hooks/use-auth"; import { Wallet, Building2, ArrowUpCircle, ArrowDownCircle, TrendingUp, Plus, Edit, Trash2, DollarSign, CreditCard, Banknote, CalendarDays, Receipt, Clock, CheckCircle, XCircle, AlertTriangle, ArrowRightLeft, Settings, PiggyBank, ChevronRight } from "lucide-react"; import { format } from "date-fns"; import { ptBR } from "date-fns/locale"; type TabType = "dashboard" | "payables" | "receivables" | "accounts" | "transactions" | "settings"; interface BankAccount { id: number; code: string; name: string; bankCode: string; bankName: string; agency: string; accountNumber: string; accountType: string; initialBalance: string; currentBalance: string; isActive: boolean; } interface PaymentMethod { id: number; code: string; name: string; type: string; fee: string; daysToReceive: number; isActive: boolean; } interface PaymentPlan { id: number; code: string; name: string; installments: number; intervalDays: number; firstDueDays: number; discountPercent: string; interestPercent: string; isActive: boolean; } interface Category { id: number; code: string; name: string; type: string; isActive: boolean; } interface Payable { id: number; documentNumber: string; supplierName: string; description: string; issueDate: string; dueDate: string; originalAmount: string; paidAmount: string; remainingAmount: string; status: string; } interface Receivable { id: number; documentNumber: string; customerName: string; description: string; issueDate: string; dueDate: string; originalAmount: string; receivedAmount: string; remainingAmount: string; status: string; } interface Transaction { id: number; bankAccountId: number; type: string; amount: string; balanceAfter: string; transactionDate: string; description: string; } interface Dashboard { totalBalance: number; bankAccounts: number; totalPayables: number; payablesCount: number; overduePayables: number; totalReceivables: number; receivablesCount: number; overdueReceivables: number; projectedBalance: number; } const api = { get: async (url: string) => { const res = await fetch(url, { credentials: "include" }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, post: async (url: string, data: any) => { const res = await fetch(url, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), credentials: "include", }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, put: async (url: string, data: any) => { const res = await fetch(url, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify(data), credentials: "include", }); if (!res.ok) throw new Error("Request failed"); return res.json(); }, delete: async (url: string) => { const res = await fetch(url, { method: "DELETE", credentials: "include" }); if (!res.ok) throw new Error("Request failed"); }, }; const formatCurrency = (value: string | number) => { const num = typeof value === "string" ? parseFloat(value) : value; return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL" }).format(num || 0); }; const formatDate = (date: string) => { if (!date) return "-"; return format(new Date(date), "dd/MM/yyyy", { locale: ptBR }); }; const getStatusBadge = (status: string) => { const styles: Record = { pending: { variant: "outline", label: "Pendente" }, partial: { variant: "secondary", label: "Parcial" }, paid: { variant: "default", label: "Pago" }, received: { variant: "default", label: "Recebido" }, overdue: { variant: "destructive", label: "Vencido" }, cancelled: { variant: "secondary", label: "Cancelado" }, }; const style = styles[status] || styles.pending; return {style.label}; }; export default function Financeiro() { const { user } = useAuth(); const { toast } = useToast(); const queryClient = useQueryClient(); const [activeTab, setActiveTab] = useState("dashboard"); const [showAccountDialog, setShowAccountDialog] = useState(false); const [showPayableDialog, setShowPayableDialog] = useState(false); const [showReceivableDialog, setShowReceivableDialog] = useState(false); const [showPaymentMethodDialog, setShowPaymentMethodDialog] = useState(false); const [showPaymentPlanDialog, setShowPaymentPlanDialog] = useState(false); const [showCategoryDialog, setShowCategoryDialog] = useState(false); const [showTransferDialog, setShowTransferDialog] = useState(false); const [showPayDialog, setShowPayDialog] = useState(false); const [showReceiveDialog, setShowReceiveDialog] = useState(false); const [editingItem, setEditingItem] = useState(null); const [selectedItem, setSelectedItem] = useState(null); const { data: dashboard } = useQuery({ queryKey: ["/api/financeiro/dashboard"], queryFn: () => api.get("/api/financeiro/dashboard"), }); const { data: bankAccounts = [] } = useQuery({ queryKey: ["/api/financeiro/bank-accounts"], queryFn: () => api.get("/api/financeiro/bank-accounts"), }); const { data: paymentMethods = [] } = useQuery({ queryKey: ["/api/financeiro/payment-methods"], queryFn: () => api.get("/api/financeiro/payment-methods"), }); const { data: paymentPlans = [] } = useQuery({ queryKey: ["/api/financeiro/payment-plans"], queryFn: () => api.get("/api/financeiro/payment-plans"), }); const { data: categories = [] } = useQuery({ queryKey: ["/api/financeiro/categories"], queryFn: () => api.get("/api/financeiro/categories"), }); const { data: payables = [] } = useQuery({ queryKey: ["/api/financeiro/payables"], queryFn: () => api.get("/api/financeiro/payables"), }); const { data: receivables = [] } = useQuery({ queryKey: ["/api/financeiro/receivables"], queryFn: () => api.get("/api/financeiro/receivables"), }); const { data: transactions = [] } = useQuery({ queryKey: ["/api/financeiro/transactions"], queryFn: () => api.get("/api/financeiro/transactions"), }); const createAccountMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/bank-accounts", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/bank-accounts"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); setShowAccountDialog(false); setEditingItem(null); toast({ title: "Conta bancária criada!" }); }, onError: () => toast({ title: "Erro ao criar conta", variant: "destructive" }), }); const updateAccountMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: any }) => api.put(`/api/financeiro/bank-accounts/${id}`, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/bank-accounts"] }); setShowAccountDialog(false); setEditingItem(null); toast({ title: "Conta atualizada!" }); }, }); const deleteAccountMutation = useMutation({ mutationFn: (id: number) => api.delete(`/api/financeiro/bank-accounts/${id}`), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/bank-accounts"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); toast({ title: "Conta excluída!" }); }, }); const createPayableMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/payables", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/payables"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); setShowPayableDialog(false); setEditingItem(null); toast({ title: "Conta a pagar criada!" }); }, onError: () => toast({ title: "Erro ao criar conta a pagar", variant: "destructive" }), }); const createReceivableMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/receivables", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/receivables"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); setShowReceivableDialog(false); setEditingItem(null); toast({ title: "Conta a receber criada!" }); }, onError: () => toast({ title: "Erro ao criar conta a receber", variant: "destructive" }), }); const payMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: any }) => api.post(`/api/financeiro/payables/${id}/pay`, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/payables"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/bank-accounts"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/transactions"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); setShowPayDialog(false); setSelectedItem(null); toast({ title: "Pagamento registrado!" }); }, onError: () => toast({ title: "Erro ao registrar pagamento", variant: "destructive" }), }); const receiveMutation = useMutation({ mutationFn: ({ id, data }: { id: number; data: any }) => api.post(`/api/financeiro/receivables/${id}/receive`, data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/receivables"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/bank-accounts"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/transactions"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); setShowReceiveDialog(false); setSelectedItem(null); toast({ title: "Recebimento registrado!" }); }, onError: () => toast({ title: "Erro ao registrar recebimento", variant: "destructive" }), }); const transferMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/transfers", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/bank-accounts"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/transactions"] }); queryClient.invalidateQueries({ queryKey: ["/api/financeiro/dashboard"] }); setShowTransferDialog(false); toast({ title: "Transferência realizada!" }); }, onError: () => toast({ title: "Erro na transferência", variant: "destructive" }), }); const createPaymentMethodMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/payment-methods", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/payment-methods"] }); setShowPaymentMethodDialog(false); toast({ title: "Meio de pagamento criado!" }); }, }); const createPaymentPlanMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/payment-plans", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/payment-plans"] }); setShowPaymentPlanDialog(false); toast({ title: "Plano de pagamento criado!" }); }, }); const createCategoryMutation = useMutation({ mutationFn: (data: any) => api.post("/api/financeiro/categories", data), onSuccess: () => { queryClient.invalidateQueries({ queryKey: ["/api/financeiro/categories"] }); setShowCategoryDialog(false); toast({ title: "Categoria criada!" }); }, }); const handleSaveAccount = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); const data = { code: formData.get("code"), name: formData.get("name"), bankCode: formData.get("bankCode"), bankName: formData.get("bankName"), agency: formData.get("agency"), accountNumber: formData.get("accountNumber"), accountType: formData.get("accountType"), initialBalance: formData.get("initialBalance") || "0", }; if (editingItem) { updateAccountMutation.mutate({ id: editingItem.id, data }); } else { createAccountMutation.mutate(data); } }; const handleSavePayable = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); const data = { documentNumber: formData.get("documentNumber"), supplierName: formData.get("supplierName"), description: formData.get("description"), issueDate: formData.get("issueDate"), dueDate: formData.get("dueDate"), originalAmount: formData.get("originalAmount"), categoryId: formData.get("categoryId") ? parseInt(formData.get("categoryId") as string) : null, }; createPayableMutation.mutate(data); }; const handleSaveReceivable = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); const data = { documentNumber: formData.get("documentNumber"), customerName: formData.get("customerName"), description: formData.get("description"), issueDate: formData.get("issueDate"), dueDate: formData.get("dueDate"), originalAmount: formData.get("originalAmount"), categoryId: formData.get("categoryId") ? parseInt(formData.get("categoryId") as string) : null, }; createReceivableMutation.mutate(data); }; const handlePay = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); const data = { amount: formData.get("amount"), bankAccountId: parseInt(formData.get("bankAccountId") as string), paymentDate: formData.get("paymentDate"), }; payMutation.mutate({ id: selectedItem.id, data }); }; const handleReceive = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); const data = { amount: formData.get("amount"), bankAccountId: parseInt(formData.get("bankAccountId") as string), receiveDate: formData.get("receiveDate"), }; receiveMutation.mutate({ id: selectedItem.id, data }); }; const handleTransfer = (e: React.FormEvent) => { e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); const data = { fromAccountId: parseInt(formData.get("fromAccountId") as string), toAccountId: parseInt(formData.get("toAccountId") as string), amount: formData.get("amount"), description: formData.get("description"), transferDate: formData.get("transferDate"), }; transferMutation.mutate(data); }; const renderDashboard = () => (
Saldo Total
{formatCurrency(dashboard?.totalBalance || 0)}

{dashboard?.bankAccounts || 0} contas ativas

A Pagar
{formatCurrency(dashboard?.totalPayables || 0)}

{dashboard?.payablesCount || 0} títulos pendentes {(dashboard?.overduePayables || 0) > 0 && ( ({dashboard?.overduePayables} vencidos) )}

A Receber
{formatCurrency(dashboard?.totalReceivables || 0)}

{dashboard?.receivablesCount || 0} títulos pendentes {(dashboard?.overdueReceivables || 0) > 0 && ( ({dashboard?.overdueReceivables} vencidos) )}

Saldo Projetado
= 0 ? "text-green-600" : "text-red-600"}`}> {formatCurrency(dashboard?.projectedBalance || 0)}

Saldo + Receber - Pagar

Contas Bancárias
{bankAccounts.slice(0, 5).map((account) => (

{account.name}

{account.bankName}

= 0 ? "text-green-600" : "text-red-600"}`}> {formatCurrency(account.currentBalance)}

))} {bankAccounts.length === 0 && (

Nenhuma conta cadastrada

)}
Próximos Vencimentos
{[...payables.filter(p => p.status === "pending").slice(0, 3).map(p => ({ ...p, tipo: "pagar" })), ...receivables.filter(r => r.status === "pending").slice(0, 3).map(r => ({ ...r, tipo: "receber" }))] .sort((a, b) => new Date(a.dueDate).getTime() - new Date(b.dueDate).getTime()) .slice(0, 5) .map((item: any, idx) => (
{item.tipo === "pagar" ? ( ) : ( )}

{item.supplierName || item.customerName}

{formatDate(item.dueDate)}

{formatCurrency(item.remainingAmount)}

))} {payables.length === 0 && receivables.length === 0 && (

Sem vencimentos pendentes

)}
); const renderPayables = () => (

Contas a Pagar

Documento Fornecedor Vencimento Valor Pago Saldo Status Ações {payables.map((item) => ( {item.documentNumber || "-"} {item.supplierName} {formatDate(item.dueDate)} {formatCurrency(item.originalAmount)} {formatCurrency(item.paidAmount)} {formatCurrency(item.remainingAmount)} {getStatusBadge(item.status)} {item.status !== "paid" && item.status !== "cancelled" && ( )} ))} {payables.length === 0 && ( Nenhuma conta a pagar cadastrada )}
); const renderReceivables = () => (

Contas a Receber

Documento Cliente Vencimento Valor Recebido Saldo Status Ações {receivables.map((item) => ( {item.documentNumber || "-"} {item.customerName} {formatDate(item.dueDate)} {formatCurrency(item.originalAmount)} {formatCurrency(item.receivedAmount)} {formatCurrency(item.remainingAmount)} {getStatusBadge(item.status)} {item.status !== "received" && item.status !== "cancelled" && ( )} ))} {receivables.length === 0 && ( Nenhuma conta a receber cadastrada )}
); const renderAccounts = () => (

Contas Bancárias

{bankAccounts.map((account) => (
{account.name} {account.bankName}
Agência: {account.agency}
Conta: {account.accountNumber}
Tipo: {account.accountType === "checking" ? "Corrente" : account.accountType === "savings" ? "Poupança" : "Investimento"}
Saldo Atual: = 0 ? "text-green-600" : "text-red-600"}`}> {formatCurrency(account.currentBalance)}
))} {bankAccounts.length === 0 && ( Nenhuma conta bancária cadastrada )}
); const renderTransactions = () => (

Extrato de Movimentações

Data Descrição Tipo Valor Saldo {transactions.map((tx) => ( {formatDate(tx.transactionDate)} {tx.description || "-"} {tx.type === "credit" ? ( Entrada ) : ( Saída )} {tx.type === "credit" ? "+" : "-"}{formatCurrency(tx.amount)} {formatCurrency(tx.balanceAfter)} ))} {transactions.length === 0 && ( Nenhuma movimentação registrada )}
); const renderSettings = () => (

Meios de Pagamento

Código Nome Tipo Taxa Dias p/ Receber Status {paymentMethods.map((method) => ( {method.code} {method.name} {method.type} {method.fee}% {method.daysToReceive} {method.isActive ? "Ativo" : "Inativo"} ))}

Planos de Pagamento

Código Nome Parcelas Intervalo 1ª Parcela Status {paymentPlans.map((plan) => ( {plan.code} {plan.name} {plan.installments}x {plan.intervalDays} dias {plan.firstDueDays} dias {plan.isActive ? "Ativo" : "Inativo"} ))}

Categorias de Fluxo de Caixa

Código Nome Tipo Status {categories.map((cat) => ( {cat.code} {cat.name} {cat.type === "income" ? "Receita" : "Despesa"} {cat.isActive ? "Ativo" : "Inativo"} ))}
); return (

Arcádia Financeiro

Gestão financeira completa: contas a pagar/receber, fluxo de caixa e controle bancário

setActiveTab(v as TabType)} className="space-y-4"> Dashboard A Pagar A Receber Contas Extrato Configurações {renderDashboard()} {renderPayables()} {renderReceivables()} {renderAccounts()} {renderTransactions()} {renderSettings()} {editingItem ? "Editar Conta Bancária" : "Nova Conta Bancária"}
Nova Conta a Pagar
Nova Conta a Receber
Registrar Pagamento {selectedItem && (

{selectedItem.supplierName}

{selectedItem.description}

Saldo: {formatCurrency(selectedItem.remainingAmount)}

)}
Registrar Recebimento {selectedItem && (

{selectedItem.customerName}

{selectedItem.description}

Saldo: {formatCurrency(selectedItem.remainingAmount)}

)}
Transferência entre Contas
Novo Meio de Pagamento
{ e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); createPaymentMethodMutation.mutate({ code: formData.get("code"), name: formData.get("name"), type: formData.get("type"), fee: formData.get("fee") || "0", daysToReceive: parseInt(formData.get("daysToReceive") as string) || 0, }); }} className="space-y-4">
Novo Plano de Pagamento
{ e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); createPaymentPlanMutation.mutate({ code: formData.get("code"), name: formData.get("name"), installments: parseInt(formData.get("installments") as string) || 1, intervalDays: parseInt(formData.get("intervalDays") as string) || 30, firstDueDays: parseInt(formData.get("firstDueDays") as string) || 30, }); }} className="space-y-4">
Nova Categoria
{ e.preventDefault(); const form = e.currentTarget; const formData = new FormData(form); createCategoryMutation.mutate({ code: formData.get("code"), name: formData.get("name"), type: formData.get("type"), }); }} className="space-y-4">
); }