import React, { useState, useEffect } from "react"; import { Download, Plus, ArrowUpDown, ArrowUp, ArrowDown, X, AlertCircle, } from "lucide-react"; interface FinancialTransaction { id: string; fot: number; data: string; curso: string; instituicao: string; anoFormatura: number; empresa: string; tipoEvento: string; tipoServico: string; nome: string; endereco: string; whatsapp: string; cpf: string; tabelaFree: string; valorFree: number; valorExtra: number; descricaoExtra: string; totalPagar: number; dataPgto: string; pgtoOk: boolean; } const Finance: React.FC = () => { const [transactions, setTransactions] = useState([ { id: "1", fot: 12345, data: "2025-11-15", curso: "Medicina", instituicao: "UFPR", anoFormatura: 2025, empresa: "PhotoPro Studio", tipoEvento: "Formatura", tipoServico: "Fotografia Completa", nome: "Ana Paula Silva", endereco: "Rua das Flores, 123 - Curitiba/PR", whatsapp: "(41) 99999-1234", cpf: "123.456.789-00", tabelaFree: "Pacote Premium", valorFree: 5000.0, valorExtra: 1500.0, descricaoExtra: "Álbum adicional + drone", totalPagar: 6500.0, dataPgto: "2025-12-01", pgtoOk: true, }, { id: "2", fot: 12346, data: "2025-11-20", curso: "Direito", instituicao: "PUC-PR", anoFormatura: 2025, empresa: "Lens & Art", tipoEvento: "Formatura", tipoServico: "Fotografia + Vídeo", nome: "Carlos Eduardo", endereco: "Av. Brasil, 456 - Curitiba/PR", whatsapp: "(41) 98888-5678", cpf: "987.654.321-00", tabelaFree: "Pacote Standard", valorFree: 4000.0, valorExtra: 800.0, descricaoExtra: "Ensaio pré-formatura", totalPagar: 4800.0, dataPgto: "2025-12-10", pgtoOk: false, }, ]); const [showAddModal, setShowAddModal] = useState(false); const [showEditModal, setShowEditModal] = useState(false); const [selectedTransaction, setSelectedTransaction] = useState(null); const [sortConfig, setSortConfig] = useState<{ key: keyof FinancialTransaction; direction: "asc" | "desc"; } | null>(null); // Estados para dados da API const [cursos, setCursos] = useState([]); const [instituicoes, setInstituicoes] = useState([]); const [empresas, setEmpresas] = useState([]); const [tiposEventos, setTiposEventos] = useState([]); const [tiposServicos, setTiposServicos] = useState([]); const [apiError, setApiError] = useState(""); const [loadingApi, setLoadingApi] = useState(false); // Form state const [formData, setFormData] = useState>({ fot: 0, data: "", curso: "", instituicao: "", anoFormatura: new Date().getFullYear(), empresa: "", tipoEvento: "", tipoServico: "", nome: "", endereco: "", whatsapp: "", cpf: "", tabelaFree: "", valorFree: 0, valorExtra: 0, descricaoExtra: "", totalPagar: 0, dataPgto: "", pgtoOk: false, }); // Carregar dados da API const loadApiData = async () => { setLoadingApi(true); setApiError(""); try { const API_BASE_URL = import.meta.env.VITE_API_URL || "http://localhost:3000"; // Carregar cursos try { const cursosRes = await fetch(`${API_BASE_URL}/api/cursos`); if (cursosRes.ok) { const cursosData = await cursosRes.json(); setCursos(cursosData); } } catch (error) { console.error("Erro ao carregar cursos:", error); } // Carregar instituições (empresas cadastradas) try { const instRes = await fetch(`${API_BASE_URL}/api/empresas`); if (instRes.ok) { const instData = await instRes.json(); setInstituicoes(instData); } } catch (error) { console.error("Erro ao carregar instituições:", error); } // Carregar empresas try { const empRes = await fetch(`${API_BASE_URL}/api/empresas`); if (empRes.ok) { const empData = await empRes.json(); setEmpresas(empData); } } catch (error) { console.error("Erro ao carregar empresas:", error); } // Carregar tipos de eventos try { const evRes = await fetch(`${API_BASE_URL}/api/tipos-eventos`); if (evRes.ok) { const evData = await evRes.json(); setTiposEventos(evData); } } catch (error) { console.error("Erro ao carregar tipos de eventos:", error); } // Carregar tipos de serviços try { const servRes = await fetch(`${API_BASE_URL}/api/tipos-servicos`); if (servRes.ok) { const servData = await servRes.json(); setTiposServicos(servData); } } catch (error) { console.error("Erro ao carregar tipos de serviços:", error); } // Se todos falharam, mostrar erro if ( cursos.length === 0 && instituicoes.length === 0 && empresas.length === 0 && tiposEventos.length === 0 && tiposServicos.length === 0 ) { setApiError( "Backend não está rodando. Alguns campos podem não estar disponíveis." ); } } catch (error) { setApiError( "Backend não está rodando. Alguns campos podem não estar disponíveis." ); } finally { setLoadingApi(false); } }; useEffect(() => { if (showAddModal || showEditModal) { loadApiData(); } }, [showAddModal, showEditModal]); // Ordenação const handleSort = (key: keyof FinancialTransaction) => { if (sortConfig && sortConfig.key === key) { // Se já está ordenando por este campo, alterna a ordem if (sortConfig.direction === "asc") { setSortConfig({ key, direction: "desc" }); } else { // Se já está descendente, remove a ordenação setSortConfig(null); } } else { // Novo campo, começa com ordem ascendente setSortConfig({ key, direction: "asc" }); } }; const sortedTransactions = React.useMemo(() => { let sortableTransactions = [...transactions]; if (sortConfig !== null) { sortableTransactions.sort((a, b) => { const aValue = a[sortConfig.key]; const bValue = b[sortConfig.key]; if (aValue < bValue) { return sortConfig.direction === "asc" ? -1 : 1; } if (aValue > bValue) { return sortConfig.direction === "asc" ? 1 : -1; } return 0; }); } return sortableTransactions; }, [transactions, sortConfig]); const getSortIcon = (key: keyof FinancialTransaction) => { if (sortConfig?.key !== key) { return ( ); } if (sortConfig.direction === "asc") { return ; } return ; }; // Handlers const handleAddTransaction = () => { setFormData({ fot: 0, data: "", curso: "", instituicao: "", anoFormatura: new Date().getFullYear(), empresa: "", tipoEvento: "", tipoServico: "", nome: "", endereco: "", whatsapp: "", cpf: "", tabelaFree: "", valorFree: 0, valorExtra: 0, descricaoExtra: "", totalPagar: 0, dataPgto: "", pgtoOk: false, }); setShowAddModal(true); }; const handleEditTransaction = (transaction: FinancialTransaction) => { setSelectedTransaction(transaction); setFormData(transaction); setShowEditModal(true); }; const handleSaveTransaction = () => { if (showEditModal && selectedTransaction) { // Atualizar transação existente setTransactions( transactions.map((t) => t.id === selectedTransaction.id ? ({ ...formData, id: selectedTransaction.id, } as FinancialTransaction) : t ) ); setShowEditModal(false); } else { // Adicionar nova transação const newTransaction: FinancialTransaction = { ...formData, id: Date.now().toString(), } as FinancialTransaction; setTransactions([...transactions, newTransaction]); setShowAddModal(false); } setSelectedTransaction(null); }; const handleExport = () => { // Criar CSV const headers = [ "FOT", "Data", "Curso", "Instituição", "Ano Formatura", "Empresa", "Tipo Evento", "Tipo de Serviço", "Nome", "Endereço", "WhatsApp", "CPF", "Tabela Free", "Valor Free", "Valor Extra", "Descrição do Extra", "Total a Pagar", "Data Pgto", "Pgto OK", ]; const csvContent = [ headers.join(","), ...transactions.map((t) => [ t.fot, t.data, t.curso, t.instituicao, t.anoFormatura, t.empresa, t.tipoEvento, t.tipoServico, t.nome, `"${t.endereco}"`, t.whatsapp, t.cpf, t.tabelaFree, t.valorFree, t.valorExtra, `"${t.descricaoExtra}"`, t.totalPagar, t.dataPgto, t.pgtoOk ? "Sim" : "Não", ].join(",") ), ].join("\n"); const blob = new Blob([csvContent], { type: "text/csv;charset=utf-8;" }); const link = document.createElement("a"); link.href = URL.createObjectURL(blob); link.download = `financeiro_${new Date().toISOString().split("T")[0]}.csv`; link.click(); }; const formatCurrency = (value: number) => { return new Intl.NumberFormat("pt-BR", { style: "currency", currency: "BRL", }).format(value); }; const formatDate = (dateString: string) => { if (!dateString) return ""; return new Date(dateString + "T00:00:00").toLocaleDateString("pt-BR"); }; // Atualizar total ao mudar valores useEffect(() => { const total = (formData.valorFree || 0) + (formData.valorExtra || 0); setFormData((prev) => ({ ...prev, totalPagar: total })); }, [formData.valorFree, formData.valorExtra]); return (
{/* Header */}

Financeiro

Gestão de transações financeiras

{/* Tabela */}
{[ { key: "fot", label: "FOT" }, { key: "data", label: "Data" }, { key: "curso", label: "Curso" }, { key: "instituicao", label: "Instituição" }, { key: "anoFormatura", label: "Ano Formatura" }, { key: "empresa", label: "Empresa" }, { key: "tipoEvento", label: "Tipo Evento" }, { key: "tipoServico", label: "Tipo de Serviço" }, { key: "nome", label: "Nome" }, { key: "endereco", label: "Endereço" }, { key: "whatsapp", label: "WhatsApp" }, { key: "cpf", label: "CPF" }, { key: "tabelaFree", label: "Tabela Free" }, { key: "valorFree", label: "Valor Free" }, { key: "valorExtra", label: "Valor Extra" }, { key: "descricaoExtra", label: "Descrição do Extra" }, { key: "totalPagar", label: "Total a Pagar" }, { key: "dataPgto", label: "Data Pgto" }, { key: "pgtoOk", label: "Pgto OK" }, ].map((column) => ( ))} {sortedTransactions.map((transaction) => ( handleEditTransaction(transaction)} className="hover:bg-gray-50 cursor-pointer transition-colors" > ))}
handleSort(column.key as keyof FinancialTransaction) } className="px-4 py-3 text-left font-semibold text-gray-700 cursor-pointer hover:bg-gray-100 transition-colors whitespace-nowrap group" >
{column.label} {getSortIcon(column.key as keyof FinancialTransaction)}
{transaction.fot} {formatDate(transaction.data)} {transaction.curso} {transaction.instituicao} {transaction.anoFormatura} {transaction.empresa} {transaction.tipoEvento} {transaction.tipoServico} {transaction.nome} {transaction.endereco} {transaction.whatsapp} {transaction.cpf} {transaction.tabelaFree} {formatCurrency(transaction.valorFree)} {formatCurrency(transaction.valorExtra)} {transaction.descricaoExtra} {formatCurrency(transaction.totalPagar)} {formatDate(transaction.dataPgto)} {transaction.pgtoOk ? "Sim" : "Não"}
{sortedTransactions.length === 0 && (
Nenhuma transação cadastrada
)}
{/* Modal Adicionar/Editar */} {(showAddModal || showEditModal) && (

{showEditModal ? "Editar Transação" : "Cadastrar Transação"}

{apiError && (

Aviso

{apiError}

)}
{/* FOT */}
{ const value = e.target.value; if (value.length <= 5) { setFormData({ ...formData, fot: parseInt(value) || 0 }); } }} max={99999} className="w-full px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" placeholder="Máx. 5 dígitos" />
{/* Data */}
setFormData({ ...formData, data: e.target.value }) } className="w-full px-3 sm:px-4 py-1.5 sm:py-2 text-xs sm:text-sm border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* Curso */}
{/* Instituição */}
{/* Ano Formatura */}
setFormData({ ...formData, anoFormatura: parseInt(e.target.value), }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* Empresa */}
{/* Tipo Evento */}
{/* Tipo de Serviço */}
{/* Nome */}
setFormData({ ...formData, nome: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* Endereço */}
setFormData({ ...formData, endereco: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* WhatsApp */}
setFormData({ ...formData, whatsapp: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" placeholder="(00) 00000-0000" />
{/* CPF */}
setFormData({ ...formData, cpf: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" placeholder="000.000.000-00" />
{/* Tabela Free */}
setFormData({ ...formData, tabelaFree: e.target.value }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* Valor Free */}
setFormData({ ...formData, valorFree: parseFloat(e.target.value) || 0, }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* Valor Extra */}
setFormData({ ...formData, valorExtra: parseFloat(e.target.value) || 0, }) } className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent" />
{/* Descrição do Extra */}