photum/frontend/pages/Finance.tsx
João Vitor 7fc96d77d2 feat: add photographer finance page and UI improvements
- Add photographer finance page at /meus-pagamentos with payment history table
- Remove university management page and related routes
- Update Finance and UserApproval pages with consistent spacing and typography
- Fix Dashboard background color to match other pages (bg-gray-50)
- Standardize navbar logo sizing across all pages
- Change institution field in course form from dropdown to text input
- Add year and semester fields for university graduation dates
- Improve header spacing on all pages to pt-20 sm:pt-24 md:pt-28 lg:pt-32
- Apply font-serif styling consistently across page headers
2025-12-12 16:26:12 -03:00

995 lines
36 KiB
TypeScript

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<FinancialTransaction[]>([
{
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<FinancialTransaction | null>(null);
const [sortConfig, setSortConfig] = useState<{
key: keyof FinancialTransaction;
direction: "asc" | "desc";
} | null>(null);
// Estados para dados da API
const [cursos, setCursos] = useState<any[]>([]);
const [instituicoes, setInstituicoes] = useState<any[]>([]);
const [empresas, setEmpresas] = useState<any[]>([]);
const [tiposEventos, setTiposEventos] = useState<any[]>([]);
const [tiposServicos, setTiposServicos] = useState<any[]>([]);
const [apiError, setApiError] = useState<string>("");
const [loadingApi, setLoadingApi] = useState(false);
// Form state
const [formData, setFormData] = useState<Partial<FinancialTransaction>>({
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 (
<ArrowUpDown
size={14}
className="opacity-0 group-hover:opacity-50 transition-opacity"
/>
);
}
if (sortConfig.direction === "asc") {
return <ArrowUp size={14} className="text-brand-gold" />;
}
return <ArrowDown size={14} className="text-brand-gold" />;
};
// 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 (
<div className="min-h-screen bg-gray-50 pt-20 sm:pt-24 md:pt-28 lg:pt-32 pb-8 sm:pb-12">
<div className="max-w-[95%] mx-auto px-3 sm:px-4">
{/* Header */}
<div className="mb-6 sm:mb-8">
<div className="flex items-center justify-between mb-2">
<div>
<h1 className="text-2xl sm:text-3xl font-serif font-bold text-brand-black">
Financeiro
</h1>
<p className="text-sm sm:text-base text-gray-600 mt-1">
Gestão de transações financeiras
</p>
</div>
<div className="flex gap-2 sm:gap-3">
<button
onClick={handleExport}
className="flex items-center gap-1.5 sm:gap-2 px-3 sm:px-4 py-1.5 sm:py-2 bg-gray-600 text-white rounded-md hover:bg-gray-700 transition-colors font-medium text-xs sm:text-sm"
>
<Download size={16} className="sm:w-[18px] sm:h-[18px]" />
<span className="hidden sm:inline">Exportar</span>
</button>
<button
onClick={handleAddTransaction}
className="flex items-center gap-1.5 sm:gap-2 px-3 sm:px-4 py-1.5 sm:py-2 bg-brand-gold text-white rounded-md hover:bg-[#a5bd2e] transition-colors font-medium text-xs sm:text-sm"
>
<Plus size={16} className="sm:w-[18px] sm:h-[18px]" />
<span className="sm:hidden">Nova Transação</span>
<span className="hidden sm:inline">Cadastrar Transação</span>
</button>
</div>
</div>
</div>
{/* Tabela */}
<div className="bg-white rounded-lg shadow-sm border border-gray-200 overflow-x-auto">
<table className="w-full text-xs sm:text-sm min-w-[1200px]">
<thead className="bg-gray-50 border-b border-gray-200">
<tr>
{[
{ 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) => (
<th
key={column.key}
onClick={() =>
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"
>
<div className="flex items-center gap-2">
{column.label}
{getSortIcon(column.key as keyof FinancialTransaction)}
</div>
</th>
))}
</tr>
</thead>
<tbody className="divide-y divide-gray-200">
{sortedTransactions.map((transaction) => (
<tr
key={transaction.id}
onClick={() => handleEditTransaction(transaction)}
className="hover:bg-gray-50 cursor-pointer transition-colors"
>
<td className="px-4 py-3 whitespace-nowrap font-medium">
{transaction.fot}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{formatDate(transaction.data)}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.curso}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.instituicao}
</td>
<td className="px-4 py-3 whitespace-nowrap text-center">
{transaction.anoFormatura}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.empresa}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.tipoEvento}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.tipoServico}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.nome}
</td>
<td className="px-4 py-3 max-w-xs truncate">
{transaction.endereco}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.whatsapp}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.cpf}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{transaction.tabelaFree}
</td>
<td className="px-4 py-3 whitespace-nowrap text-right">
{formatCurrency(transaction.valorFree)}
</td>
<td className="px-4 py-3 whitespace-nowrap text-right">
{formatCurrency(transaction.valorExtra)}
</td>
<td className="px-4 py-3 max-w-xs truncate">
{transaction.descricaoExtra}
</td>
<td className="px-4 py-3 whitespace-nowrap text-right font-semibold text-green-600">
{formatCurrency(transaction.totalPagar)}
</td>
<td className="px-4 py-3 whitespace-nowrap">
{formatDate(transaction.dataPgto)}
</td>
<td className="px-4 py-3 whitespace-nowrap text-center">
<span
className={`px-2 py-1 rounded-full text-xs font-medium ${
transaction.pgtoOk
? "bg-green-100 text-green-800"
: "bg-red-100 text-red-800"
}`}
>
{transaction.pgtoOk ? "Sim" : "Não"}
</span>
</td>
</tr>
))}
</tbody>
</table>
{sortedTransactions.length === 0 && (
<div className="text-center py-12 text-gray-500">
Nenhuma transação cadastrada
</div>
)}
</div>
</div>
{/* Modal Adicionar/Editar */}
{(showAddModal || showEditModal) && (
<div className="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center z-50 p-3 sm:p-4">
<div className="bg-white rounded-lg max-w-4xl w-full max-h-[90vh] overflow-y-auto">
<div className="sticky top-0 bg-white border-b border-gray-200 p-4 sm:p-6 flex items-center justify-between">
<h2 className="text-lg sm:text-xl md:text-2xl font-bold">
{showEditModal ? "Editar Transação" : "Cadastrar Transação"}
</h2>
<button
onClick={() => {
setShowAddModal(false);
setShowEditModal(false);
setSelectedTransaction(null);
}}
className="text-gray-500 hover:text-gray-700"
>
<X size={20} className="sm:w-6 sm:h-6" />
</button>
</div>
{apiError && (
<div className="mx-4 sm:mx-6 mt-4 sm:mt-6 p-3 sm:p-4 bg-yellow-50 border border-yellow-200 rounded-lg flex items-start gap-2 sm:gap-3">
<AlertCircle
className="text-yellow-600 flex-shrink-0 mt-0.5"
size={18}
/>
<div className="flex-1">
<p className="text-xs sm:text-sm font-medium text-yellow-800">
Aviso
</p>
<p className="text-xs sm:text-sm text-yellow-700 mt-1">
{apiError}
</p>
</div>
</div>
)}
<div className="p-4 sm:p-6">
<div className="grid grid-cols-1 md:grid-cols-2 gap-3 sm:gap-4">
{/* FOT */}
<div>
<label className="block text-xs sm:text-sm font-medium text-gray-700 mb-1.5 sm:mb-2">
FOT *
</label>
<input
type="number"
value={formData.fot || ""}
onChange={(e) => {
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"
/>
</div>
{/* Data */}
<div>
<label className="block text-xs sm:text-sm font-medium text-gray-700 mb-1.5 sm:mb-2">
Data *
</label>
<input
type="date"
value={formData.data}
onChange={(e) =>
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"
/>
</div>
{/* Curso */}
<div>
<label className="block text-xs sm:text-sm font-medium text-gray-700 mb-1.5 sm:mb-2">
Curso *
</label>
<select
value={formData.curso}
onChange={(e) =>
setFormData({ ...formData, curso: e.target.value })
}
disabled={loadingApi}
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 disabled:bg-gray-50 disabled:text-gray-500"
>
<option value="">
{loadingApi
? "Carregando..."
: cursos.length > 0
? "Selecione um curso"
: "Nenhum curso disponível"}
</option>
{cursos.map((curso) => (
<option key={curso.id} value={curso.nome}>
{curso.nome}
</option>
))}
</select>
</div>
{/* Instituição */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Instituição *
</label>
<select
value={formData.instituicao}
onChange={(e) =>
setFormData({ ...formData, instituicao: e.target.value })
}
disabled={loadingApi}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
>
<option value="">
{loadingApi
? "Carregando..."
: instituicoes.length > 0
? "Selecione uma instituição"
: "Nenhuma instituição disponível"}
</option>
{instituicoes.map((inst) => (
<option key={inst.id} value={inst.nome}>
{inst.nome}
</option>
))}
</select>
</div>
{/* Ano Formatura */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Ano Formatura *
</label>
<input
type="number"
value={formData.anoFormatura}
onChange={(e) =>
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"
/>
</div>
{/* Empresa */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Empresa *
</label>
<select
value={formData.empresa}
onChange={(e) =>
setFormData({ ...formData, empresa: e.target.value })
}
disabled={loadingApi}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
>
<option value="">
{loadingApi
? "Carregando..."
: empresas.length > 0
? "Selecione uma empresa"
: "Nenhuma empresa disponível"}
</option>
{empresas.map((emp) => (
<option key={emp.id} value={emp.nome}>
{emp.nome}
</option>
))}
</select>
</div>
{/* Tipo Evento */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Tipo Evento *
</label>
<select
value={formData.tipoEvento}
onChange={(e) =>
setFormData({ ...formData, tipoEvento: e.target.value })
}
disabled={loadingApi}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
>
<option value="">
{loadingApi
? "Carregando..."
: tiposEventos.length > 0
? "Selecione um tipo"
: "Nenhum tipo de evento disponível"}
</option>
{tiposEventos.map((tipo) => (
<option key={tipo.id} value={tipo.nome}>
{tipo.nome}
</option>
))}
</select>
</div>
{/* Tipo de Serviço */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Tipo de Serviço *
</label>
<select
value={formData.tipoServico}
onChange={(e) =>
setFormData({ ...formData, tipoServico: e.target.value })
}
disabled={loadingApi}
className="w-full px-4 py-2 border border-gray-300 rounded-md focus:ring-2 focus:ring-brand-gold focus:border-transparent disabled:bg-gray-50 disabled:text-gray-500"
>
<option value="">
{loadingApi
? "Carregando..."
: tiposServicos.length > 0
? "Selecione um tipo"
: "Nenhum tipo de serviço disponível"}
</option>
{tiposServicos.map((tipo) => (
<option key={tipo.id} value={tipo.nome}>
{tipo.nome}
</option>
))}
</select>
</div>
{/* Nome */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Nome *
</label>
<input
type="text"
value={formData.nome}
onChange={(e) =>
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"
/>
</div>
{/* Endereço */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Endereço
</label>
<input
type="text"
value={formData.endereco}
onChange={(e) =>
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"
/>
</div>
{/* WhatsApp */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
WhatsApp
</label>
<input
type="text"
value={formData.whatsapp}
onChange={(e) =>
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"
/>
</div>
{/* CPF */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
CPF
</label>
<input
type="text"
value={formData.cpf}
onChange={(e) =>
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"
/>
</div>
{/* Tabela Free */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Tabela Free
</label>
<input
type="text"
value={formData.tabelaFree}
onChange={(e) =>
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"
/>
</div>
{/* Valor Free */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Valor Free
</label>
<input
type="number"
step="0.01"
value={formData.valorFree}
onChange={(e) =>
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"
/>
</div>
{/* Valor Extra */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Valor Extra
</label>
<input
type="number"
step="0.01"
value={formData.valorExtra}
onChange={(e) =>
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"
/>
</div>
{/* Descrição do Extra */}
<div className="md:col-span-2">
<label className="block text-sm font-medium text-gray-700 mb-2">
Descrição do Extra
</label>
<textarea
value={formData.descricaoExtra}
onChange={(e) =>
setFormData({
...formData,
descricaoExtra: 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"
rows={3}
/>
</div>
{/* Total a Pagar */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Total a Pagar
</label>
<input
type="text"
value={formatCurrency(formData.totalPagar || 0)}
disabled
className="w-full px-4 py-2 border border-gray-300 rounded-md bg-gray-50 text-gray-700 font-semibold"
/>
</div>
{/* Data Pgto */}
<div>
<label className="block text-sm font-medium text-gray-700 mb-2">
Data Pgto
</label>
<input
type="date"
value={formData.dataPgto}
onChange={(e) =>
setFormData({ ...formData, dataPgto: 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"
/>
</div>
{/* Pgto OK */}
<div className="md:col-span-2">
<label className="flex items-center gap-2 cursor-pointer">
<input
type="checkbox"
checked={formData.pgtoOk}
onChange={(e) =>
setFormData({ ...formData, pgtoOk: e.target.checked })
}
className="w-4 h-4 sm:w-5 sm:h-5 text-brand-gold focus:ring-brand-gold border-gray-300 rounded"
/>
<span className="text-xs sm:text-sm font-medium text-gray-700">
Pagamento Confirmado
</span>
</label>
</div>
</div>
{/* Botões */}
<div className="flex gap-2 sm:gap-3 mt-6 sm:mt-8">
<button
onClick={() => {
setShowAddModal(false);
setShowEditModal(false);
setSelectedTransaction(null);
}}
className="flex-1 px-3 sm:px-4 py-2 sm:py-3 bg-gray-200 text-gray-800 rounded-md hover:bg-gray-300 transition-colors font-medium text-xs sm:text-sm"
>
Cancelar
</button>
<button
onClick={handleSaveTransaction}
className="flex-1 px-3 sm:px-4 py-2 sm:py-3 bg-brand-gold text-white rounded-md hover:bg-[#a5bd2e] transition-colors font-medium text-xs sm:text-sm"
>
{showEditModal ? "Salvar" : "Cadastrar"}
</button>
</div>
</div>
</div>
</div>
)}
</div>
);
};
export default Finance;