import React, { useState } from 'react'; import { SchoolData } from '../types'; import { useDialog } from '../DialogContext'; import { MessageSquare, Save, Info, Settings, Send, Clock, AlertTriangle, FileText, CheckCircle, Cake, X } from 'lucide-react'; interface MessagesProps { data: SchoolData; updateData: (newData: Partial) => void; } const defaultTemplates = { boletoGerado: "Olá {nome}, sua cobrança referente a {descricao} no valor de R$ {valor} foi gerada. Vencimento: {vencimento}.", pagamentoConfirmado: "Olá {nome}, confirmamos o pagamento de R$ {valor} referente a {descricao}. Muito obrigado!", boletoVencido: "Olá {nome}, o boleto referente a {descricao} de R$ {valor} venceu em {vencimento}. Segue o PDF da 2ª via atualizada abaixo:", cobrancaCancelada: "Olá {nome}, a cobrança referente a {descricao} foi cancelada.", cobrancaAtualizada: "Olá {nome}, o boleto de {descricao} foi atualizado. Segue a nova versão:", felizAniversario: "Olá {nome}, a equipe da {escola} passa para te desejar um Feliz Aniversário! Muita saúde, paz e conquistas neste novo ciclo! 🎂🎈", automationRules: { sendOnDueDate: true, sendDaysAfter: '1', repeatEveryDays: '3' } }; const Messages: React.FC = ({ data, updateData }) => { const { showAlert, showConfirm } = useDialog(); const defaultVars = data.messageTemplates || defaultTemplates; const initRules = defaultVars.automationRules || defaultTemplates.automationRules; const [templates, setTemplates] = useState({ ...defaultTemplates, ...defaultVars, automationRules: { ...defaultTemplates.automationRules, ...initRules } }); const [isSending, setIsSending] = useState(false); // Estados WhatsApp em Massa const [targetType, setTargetType] = useState('todos'); const [targetId, setTargetId] = useState(''); const [messageText, setMessageText] = useState(''); const [isSendingMass, setIsSendingMass] = useState(false); const [isSendingBdays, setIsSendingBdays] = useState(false); // Modal de Edição de Modelo const [editingTemplate, setEditingTemplate] = useState<{ key: keyof typeof defaultTemplates | 'felizAniversario', label: string, desc: string, color: string, icon: any, vars: string[] } | null>(null); const normalizeLineBreaks = (text: string) => text.replace(/\r\n/g, '\n'); const birthdayStudents = (data.students || []).filter(s => { if (!s.birthDate || s.status !== 'active') return false; const bdayParts = s.birthDate.split('-'); const bdayDay = parseInt(bdayParts[2]); const bdayMonth = parseInt(bdayParts[1]); const today = new Date(); return bdayDay === today.getDate() && bdayMonth === (today.getMonth() + 1); }); const handleSendBirthdays = async () => { if (birthdayStudents.length === 0) return; showConfirm( 'Enviar Felicitações', `Deseja enviar a mensagem de aniversário para os ${birthdayStudents.length} alunos que fazem aniversário hoje?`, async () => { setIsSendingBdays(true); try { const payloadAlunos = birthdayStudents.map(s => { const nome = s.name.split(' ')[0]; const telefone = s.phone || s.guardianPhone; return { nome, telefone }; }).filter(a => a.telefone); if (payloadAlunos.length === 0) { showAlert('Aviso', 'Nenhum dos aniversariantes possui telefone cadastrado.', 'warning'); return; } const msgTemplate = normalizeLineBreaks(templates.felizAniversario).replace(/{escola}/g, data.profile.name); const resp = await fetch('/api/enviar-massa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ alunos: payloadAlunos, mensagem: msgTemplate }) }); if (resp.ok) { showAlert('Sucesso', 'O disparo das mensagens de aniversário foi iniciado!', 'success'); } else { const resData = await resp.json(); showAlert('Erro', resData.error || 'Erro ao iniciar disparo.', 'error'); } } catch (e) { showAlert('Erro', 'Erro de conexão.', 'error'); } finally { setIsSendingBdays(false); } } ); }; const handleSave = () => { const normalizedTemplates = { ...templates, boletoGerado: normalizeLineBreaks(templates.boletoGerado), pagamentoConfirmado: normalizeLineBreaks(templates.pagamentoConfirmado), boletoVencido: normalizeLineBreaks(templates.boletoVencido), cobrancaCancelada: normalizeLineBreaks(templates.cobrancaCancelada), cobrancaAtualizada: normalizeLineBreaks(templates.cobrancaAtualizada), felizAniversario: normalizeLineBreaks(templates.felizAniversario) }; updateData({ messageTemplates: normalizedTemplates }); showAlert('Sucesso', 'Configurações de mensagens salvas com sucesso!', 'success'); }; const handleDispararCobrancas = async () => { showConfirm( 'Disparar Cobranças', 'Tem certeza que deseja processar e enviar as mensagens para TODOS os alunos com pagamentos atrasados agora?', async () => { setIsSending(true); try { const resp = await fetch('/api/disparar_cobrancas', { method: 'POST' }); const resData = await resp.json(); if (resp.ok) { showAlert('Sucesso', resData.message || 'Cobranças processadas e disparadas com sucesso!', 'success'); } else { showAlert('Erro', resData.error || 'Erro ao disparar cobranças', 'error'); } } catch (e: any) { showAlert('Erro', 'Erro de conexão ao disparar cobranças.', 'error'); } finally { setIsSending(false); } } ); }; const handleMassSend = async () => { if (!messageText.trim()) { return showAlert('Aviso', 'Digite uma mensagem para enviar.', 'warning'); } let targetStudents = []; if (targetType === 'todos') { targetStudents = data.students || []; } else if (targetType === 'turma') { if (!targetId) return showAlert('Aviso', 'Selecione uma turma.', 'warning'); targetStudents = (data.students || []).filter(s => s.classId === targetId); } else if (targetType === 'aluno') { if (!targetId) return showAlert('Aviso', 'Selecione um aluno.', 'warning'); targetStudents = (data.students || []).filter(s => s.id === targetId); } const validStudents = targetStudents.filter(a => a.phone || a.guardianPhone); if (validStudents.length === 0) { return showAlert('Erro', 'Nenhum aluno com telefone cadastrado foi selecionado.', 'error'); } const payloadAlunos = validStudents.map(a => { let nome = a.name; let telefone = a.phone; if (a.birthDate) { const birthDate = new Date(a.birthDate); const age = Math.abs(new Date(Date.now() - birthDate.getTime()).getUTCFullYear() - 1970); if (age < 18) { nome = a.guardianName || a.name; telefone = a.guardianPhone || a.phone; } } return { nome, telefone, matricula: a.enrollmentNumber || '—' }; }); setIsSendingMass(true); try { const resp = await fetch('/api/enviar-massa', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ alunos: payloadAlunos, mensagem: normalizeLineBreaks(messageText) }) }); const resData = await resp.json(); if (resp.ok) { setMessageText(''); setTargetId(''); showAlert('Sucesso', 'Disparo iniciado no servidor! Você já pode fechar esta tela ou continuar usando o sistema.', 'success'); } else { showAlert('Erro', resData.error || 'Erro ao iniciar disparo.', 'error'); } } catch (e) { showAlert('Erro', 'Erro de conexão.', 'error'); } finally { setIsSendingMass(false); } }; const templateCards = [ { key: 'boletoGerado', label: 'Boleto Gerado / Novo Carnê', desc: 'Enviado assim que a cobrança é criada no sistema.', color: 'blue', icon: FileText, vars: ['{nome}', '{matricula}', '{descricao}', '{valor}', '{vencimento}', '{link_boleto}', '{escola}'] }, { key: 'pagamentoConfirmado', label: 'Pagamento Confirmado', desc: 'Enviado quando o sistema (Asaas) compensa o pagamento.', color: 'emerald', icon: CheckCircle, vars: ['{nome}', '{matricula}', '{descricao}', '{valor}', '{escola}'] }, { key: 'boletoVencido', label: 'Boleto Vencido', desc: 'Enviado conforme automação ou disparo manual de atrasados.', color: 'red', icon: AlertTriangle, vars: ['{nome}', '{matricula}', '{descricao}', '{valor}', '{vencimento}', '{link_boleto}', '{escola}'] }, { key: 'cobrancaCancelada', label: 'Cobrança Cancelada', desc: 'Enviado quando o boleto for cancelado no sistema.', color: 'slate', icon: AlertTriangle, vars: ['{nome}', '{matricula}', '{descricao}', '{escola}'] }, { key: 'cobrancaAtualizada', label: 'Cobrança Atualizada', desc: 'Enviado quando houver edição/atualização da cobrança.', color: 'amber', icon: Settings, vars: ['{nome}', '{matricula}', '{descricao}', '{valor}', '{vencimento}', '{link_boleto}', '{escola}'] }, { key: 'felizAniversario', label: 'Feliz Aniversário', desc: 'Mensagem carinhosa para os aniversariantes do dia.', color: 'pink', icon: Cake, vars: ['{nome}', '{escola}'] } ]; const insertVariable = (variable: string) => { if (!editingTemplate) return; const textarea = document.getElementById('template-editor') as HTMLTextAreaElement; if (!textarea) return; const start = textarea.selectionStart; const end = textarea.selectionEnd; const text = (templates[editingTemplate.key as keyof typeof templates] as string) || ''; const newText = text.substring(0, start) + variable + text.substring(end); setTemplates(p => ({ ...p, [editingTemplate.key]: newText })); setTimeout(() => { textarea.focus(); textarea.setSelectionRange(start + variable.length, start + variable.length); }, 10); }; return (

Mensagens

Configure modelos e rotinas de notificação via WhatsApp.

{/* Lado Esquerdo - Configurações e Disparos */}

Automação

setTemplates(p => ({ ...p, automationRules: { ...p.automationRules, sendDaysAfter: e.target.value } }))} className="w-16 px-3 py-2 border border-slate-200 rounded-lg text-center bg-white shadow-sm" /> dias
setTemplates(p => ({ ...p, automationRules: { ...p.automationRules, repeatEveryDays: e.target.value } }))} className="w-16 px-3 py-2 border border-slate-200 rounded-lg text-center bg-white shadow-sm" /> dias

Disparo em Massa

{targetType !== 'todos' && ( )}
{['{nome}', '{matricula}'].map(v => ( ))}