import React, { useState, useEffect } from 'react'; import { SchoolData, Contract, Student, Payment } from '../types'; import { useDialog } from '../DialogContext'; import { Plus, Search, Trash2, X, User, Calendar, FileSignature, ListChecks, Printer, AlertTriangle, RefreshCw, Edit2, Info } from 'lucide-react'; import { pdfService } from '../services/pdfService'; interface ContractsProps { data: SchoolData; updateData: (newData: Partial) => void; } const Contracts: React.FC = ({ data, updateData }) => { const { showAlert, showConfirm } = useDialog(); const [activeTab, setActiveTab] = useState<'contracts' | 'templates'>('contracts'); const [isModalOpen, setIsModalOpen] = useState(false); const [isTemplateModalOpen, setIsTemplateModalOpen] = useState(false); const [isClosing, setIsClosing] = useState(false); const [searchTerm, setSearchTerm] = useState(''); const [isGeneratingPDF, setIsGeneratingPDF] = useState(null); const [showGenerateModal, setShowGenerateModal] = useState(false); const [contractToGenerate, setContractToGenerate] = useState(null); const [genConfig, setGenConfig] = useState({ startDate: new Date().toLocaleDateString('pt-BR'), installments: 12, discount: 0 }); const [formData, setFormData] = useState>({ studentId: '', title: '', content: '' }); const [templateFormData, setTemplateFormData] = useState({ id: '', name: '', content: '' }); // Pre-load content when student is selected based on template useEffect(() => { if (formData.studentId && !formData.content) { const student = data.students.find(s => s.id === formData.studentId); const cls = data.classes.find(c => c.id === student?.classId); const course = data.courses.find(c => c.id === cls?.courseId); const templateObj = data.contractTemplates?.find(t => t.id === student?.contractTemplateId); if (student && course) { let template = templateObj?.content || ''; // Aluno template = template.replace(/{{aluno}}/g, student.name || ''); template = template.replace(/{{aluno_cpf}}/g, student.cpf || ''); template = template.replace(/{{aluno_rg}}/g, student.rg || ''); template = template.replace(/{{aluno_nascimento}}/g, student.birthDate ? new Date(student.birthDate).toLocaleDateString('pt-BR') : ''); template = template.replace(/{{aluno_email}}/g, student.email || ''); template = template.replace(/{{aluno_telefone}}/g, student.phone || ''); template = template.replace(/{{aluno_cep}}/g, student.addressZip || ''); template = template.replace(/{{aluno_endereco}}/g, `${student.addressStreet || ''}, ${student.addressNumber || ''}`); template = template.replace(/{{aluno_bairro}}/g, student.addressNeighborhood || ''); template = template.replace(/{{aluno_cidade}}/g, student.addressCity || ''); template = template.replace(/{{aluno_estado}}/g, student.addressState || ''); // Responsável template = template.replace(/{{responsavel_nome}}/g, student.guardianName || ''); template = template.replace(/{{responsavel_cpf}}/g, student.guardianCpf || ''); template = template.replace(/{{responsavel_nascimento}}/g, student.guardianBirthDate ? new Date(student.guardianBirthDate).toLocaleDateString('pt-BR') : ''); // Curso e Turma template = template.replace(/{{curso}}/g, course.name || ''); template = template.replace(/{{mensalidade}}/g, course.monthlyFee ? `R$ ${course.monthlyFee.toFixed(2)}` : 'R$ 0,00'); template = template.replace(/{{duracao}}/g, course.duration || ''); template = template.replace(/{{curso_taxa_matricula}}/g, course.registrationFee ? `R$ ${course.registrationFee.toFixed(2)}` : 'R$ 0,00'); template = template.replace(/{{turma_nome}}/g, cls?.name || ''); template = template.replace(/{{turma_professor}}/g, cls?.teacher || ''); template = template.replace(/{{turma_horario}}/g, cls?.schedule || ''); // Escola template = template.replace(/{{data}}/g, new Date().toLocaleDateString('pt-BR')); template = template.replace(/{{escola}}/g, data.profile.name || ''); template = template.replace(/{{cnpj_escola}}/g, data.profile.cnpj || ''); setFormData(prev => ({ ...prev, content: template, title: prev.title || `Contrato de Matrícula - ${student.name}` })); } } }, [formData.studentId, data]); const filteredContracts = data.contracts.filter(c => { const student = data.students.find(s => s.id === c.studentId); const search = (searchTerm || '').toLowerCase(); return (c.title || '').toLowerCase().includes(search) || (student?.name || '').toLowerCase().includes(search); }); const filteredTemplates = (data.contractTemplates || []).filter(t => (t.name || '').toLowerCase().includes((searchTerm || '').toLowerCase()) ); const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!formData.studentId || !formData.title || !formData.content) { showAlert('Atenção', '⚠️ Por favor, selecione um aluno e preencha o título e conteúdo do contrato.', 'warning'); return; } const newContract: Contract = { ...formData, id: crypto.randomUUID(), createdAt: new Date().toISOString() }; updateData({ contracts: [...data.contracts, newContract] }); closeModal(); }; const handleTemplateSubmit = (e: React.FormEvent) => { e.preventDefault(); if (!templateFormData.name || !templateFormData.content) { showAlert('Atenção', '⚠️ Preencha o nome e o conteúdo do modelo.', 'warning'); return; } const templates = data.contractTemplates || []; let updatedTemplates; if (templateFormData.id) { updatedTemplates = templates.map(t => t.id === templateFormData.id ? templateFormData : t); } else { updatedTemplates = [...templates, { ...templateFormData, id: crypto.randomUUID() }]; } updateData({ contractTemplates: updatedTemplates }); closeTemplateModal(); }; const closeModal = () => { setIsClosing(true); setTimeout(() => { setIsModalOpen(false); setShowGenerateModal(false); setIsClosing(false); setFormData({ studentId: '', title: '', content: '' }); setContractToGenerate(null); }, 400); }; const closeTemplateModal = () => { setIsClosing(true); setTimeout(() => { setIsTemplateModalOpen(false); setIsClosing(false); setTemplateFormData({ id: '', name: '', content: '' }); }, 400); }; const handleDelete = (id: string) => { showConfirm( 'Excluir Contrato', 'Tem certeza que deseja excluir este contrato?', () => { updateData({ contracts: data.contracts.filter(c => c.id !== id) }); } ); }; const handleDeleteTemplate = (id: string) => { showConfirm( 'Excluir Modelo', 'Tem certeza que deseja excluir este modelo de contrato?', () => { updateData({ contractTemplates: (data.contractTemplates || []).filter(t => t.id !== id) }); } ); }; const handleDownloadContract = async (contract: Contract, student: Student) => { setIsGeneratingPDF(contract.id); try { await pdfService.generateContractPDF(contract, student, data); } catch (error) { console.error('Error generating PDF:', error); } finally { setIsGeneratingPDF(null); } }; const openGenerateModal = (contract: Contract) => { const student = data.students.find(s => s.id === contract.studentId); const cls = data.classes.find(c => c.id === student?.classId); const course = data.courses.find(c => c.id === cls?.courseId); if (student && cls && course) { setContractToGenerate(contract); setGenConfig({ startDate: new Date().toLocaleDateString('pt-BR'), installments: course.durationMonths || 12, discount: 0 }); setShowGenerateModal(true); } else { console.warn("Missing data for generation"); } }; const handleGenerate = async () => { if (!contractToGenerate) return; const contract = contractToGenerate; const student = data.students.find(s => s.id === contract.studentId); const cls = data.classes.find(c => c.id === student?.classId); const course = data.courses.find(c => c.id === cls?.courseId); if (!course || !student) return; // Parse date from DD/MM/YYYY const [d, m, y] = genConfig.startDate.split('/'); const startDate = new Date(`${y}-${m}-${d}`); const newPayments: Payment[] = []; for (let i = 0; i < genConfig.installments; i++) { const dueDate = new Date(startDate); dueDate.setMonth(startDate.getMonth() + i); const finalAmount = Math.max(0, course.monthlyFee - genConfig.discount); newPayments.push({ id: crypto.randomUUID(), studentId: student.id, contractId: contract.id, amount: finalAmount, discount: genConfig.discount, dueDate: dueDate.toISOString().split('T')[0], status: 'pending', type: 'monthly', installmentNumber: i + 1, totalInstallments: genConfig.installments, description: `Parcela ${i + 1}/${genConfig.installments} - ${course.name}${genConfig.discount > 0 ? ` (Desc: R$ ${genConfig.discount})` : ''}` }); } // 1. Salvar no JSON (manter compatibilidade) updateData({ payments: [...data.payments, ...newPayments] }); // 2. Fase 2: Salvar no SQL via sincronização no próximo boot // Nota: Parcelas de contrato sem Asaas serão migradas pelo syncJsonToRelationalTables no próximo restart. // A Fase 3 eliminará essa dependência quando o Manager ler direto do SQL. closeModal(); }; const formatDateMask = (val: string) => { return val.replace(/\D/g, '').replace(/(\d{2})(\d)/, '$1/$2').replace(/(\d{2})(\d)/, '$1/$2').slice(0, 10); }; const inputClass = "w-full px-4 py-3 bg-white text-black border border-slate-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-indigo-500 transition-all shadow-sm"; return (

Contratos

Gestão de termos de adesão e modelos de contrato.

setSearchTerm(e.target.value)} />
{activeTab === 'contracts' ? (
{filteredContracts.map(contract => { const student = data.students.find(s => s.id === contract.studentId); const hasPayments = data.payments.some(p => p.contractId === contract.id); return ( ); })}
Documento / Beneficiário Data Emissão Ações
{contract.title}
{student?.name || 'Aluno Removido'} {hasPayments && Financeiro Gerado}
{new Date(contract.createdAt).toLocaleDateString('pt-BR')}
) : (
{filteredTemplates.map(template => ( ))} {filteredTemplates.length === 0 && ( )}
Nome do Modelo Ações
{template.name}
Nenhum modelo de contrato encontrado.
)}
{/* CREATE CONTRACT MODAL */} {isModalOpen && (

Criar Contrato Manual

O conteúdo será preenchido pelo modelo vinculado ao aluno.

setFormData({...formData, title: e.target.value})} />