From 2db8d5687d0f9c3b91dcadcafee9b8cabcecd8b6 Mon Sep 17 00:00:00 2001 From: Sidney Date: Sat, 18 Jul 2026 20:37:44 -0300 Subject: [PATCH] feat(pre-matricula): add mass messaging modal and remove from general messages --- manager/components/Messages.tsx | 49 +----- manager/components/PreMatricula.tsx | 250 +++++++++++++++++++++++++++- 2 files changed, 253 insertions(+), 46 deletions(-) diff --git a/manager/components/Messages.tsx b/manager/components/Messages.tsx index 56fe481..e437467 100644 --- a/manager/components/Messages.tsx +++ b/manager/components/Messages.tsx @@ -244,7 +244,6 @@ const Messages: React.FC = ({ data, updateData }) => { } let targetStudents = []; - let isPreMatricula = false; if (targetType === 'todos') { targetStudents = data.students || []; @@ -254,38 +253,14 @@ const Messages: React.FC = ({ data, updateData }) => { } else if (targetType === 'aluno') { if (!targetId) return showAlert('Aviso', 'Selecione um aluno.', 'warning'); targetStudents = (data.students || []).filter(s => s.id === targetId); - } else if (targetType === 'prematricula_todos') { - targetStudents = dbPreMatriculas; - isPreMatricula = true; - } else if (targetType === 'prematricula_turma') { - if (!targetId) return showAlert('Aviso', 'Selecione uma turma.', 'warning'); - targetStudents = dbPreMatriculas.filter(p => p.turmaId === targetId); - isPreMatricula = true; - } else if (targetType === 'prematricula_aluno') { - if (!targetId) return showAlert('Aviso', 'Selecione uma pré-matrícula.', 'warning'); - targetStudents = dbPreMatriculas.filter(p => p.id === targetId); - isPreMatricula = true; } - let payloadAlunos = []; + const validStudents = targetStudents.filter(a => a.phone || a.guardianPhone); + if (validStudents.length === 0) { + return showAlert('Erro', 'Nenhum aluno com telefone cadastrado foi selecionado.', 'error'); + } - if (isPreMatricula) { - const validPre = targetStudents.filter(p => p.telefone); - if (validPre.length === 0) { - return showAlert('Erro', 'Nenhuma pré-matrícula com telefone válido foi selecionada.', 'error'); - } - payloadAlunos = validPre.map(p => ({ - nome: p.nome.split(' ')[0], - telefone: p.telefone, - matricula: 'Pré-Matrícula' - })); - } else { - const validStudents = targetStudents.filter(a => a.phone || a.guardianPhone); - if (validStudents.length === 0) { - return showAlert('Erro', 'Nenhum aluno com telefone cadastrado foi selecionado.', 'error'); - } - - payloadAlunos = validStudents.flatMap(a => { + let payloadAlunos = validStudents.flatMap(a => { const entries = []; // Entrada para o Aluno @@ -307,8 +282,7 @@ const Messages: React.FC = ({ data, updateData }) => { } return entries; - }); - } + }); setIsSendingMass(true); try { @@ -405,23 +379,18 @@ const Messages: React.FC = ({ data, updateData }) => { - - - - {targetType !== 'todos' && targetType !== 'prematricula_todos' && ( + {targetType !== 'todos' && ( )} diff --git a/manager/components/PreMatricula.tsx b/manager/components/PreMatricula.tsx index ff63945..d14f9cf 100644 --- a/manager/components/PreMatricula.tsx +++ b/manager/components/PreMatricula.tsx @@ -3,8 +3,10 @@ import { SchoolData, PreMatriculaCampo, PreMatriculaConfig, PreMatriculaInscrica import { Plus, Trash2, Save, Eye, EyeOff, Download, GripVertical, Link2, ClipboardPen, Copy, Check, ChevronDown, ChevronRight, Users, RefreshCw, - FileText, X, Settings2, ArrowLeft, ExternalLink, AlertCircle, UserCheck, Edit, Upload + FileText, X, Settings2, ArrowLeft, ExternalLink, AlertCircle, UserCheck, Edit, Upload, + MessageSquare, Paperclip, Smile } from 'lucide-react'; +import { useDialog } from '../DialogContext'; interface Props { data: SchoolData; @@ -37,6 +39,7 @@ const getFriendlyLabel = (key: string, value: any): string => { const DAY_NAMES = ['Domingo', 'Segunda-feira', 'Terça-feira', 'Quarta-feira', 'Quinta-feira', 'Sexta-feira', 'Sábado']; const PreMatricula: React.FC = ({ data, onConvert }) => { + const { showAlert } = useDialog(); const [config, setConfig] = useState(null); const [campos, setCampos] = useState([]); const [todosCampos, setTodosCampos] = useState([]); @@ -51,6 +54,78 @@ const PreMatricula: React.FC = ({ data, onConvert }) => { const [showNewField, setShowNewField] = useState(false); const [newField, setNewField] = useState({ label: '', tipo: 'text', placeholder: '', obrigatorio: false, opcoes: '' }); + const [showMassModal, setShowMassModal] = useState(false); + const [targetType, setTargetType] = useState('todos'); + const [targetId, setTargetId] = useState(''); + const [messageText, setMessageText] = useState(''); + const [isSendingMass, setIsSendingMass] = useState(false); + const [massDelay, setMassDelay] = useState('60'); + const [massAttachment, setMassAttachment] = useState(null); + const [showMassEmojis, setShowMassEmojis] = useState(false); + const commonEmojis = ['😀', '😂', '🥰', '😎', '🎉', '👍', '🙏', '❤️', '🔥', '🚀', '✅', '❌', '⚠️', '💡', '🎓', '🏫', '📚', '📖', '✏️', '📝', '🎒', '💻', '🧠', '🤓', '🥇']; + + const normalizeLineBreaks = (text: string) => text.replace(/\r\n/g, '\n'); + + const handleMassSend = async () => { + if (!messageText.trim()) { + return showAlert('Aviso', 'Digite uma mensagem para enviar.', 'warning'); + } + + let targetStudents = []; + + if (targetType === 'todos') { + targetStudents = inscricoes; + } else if (targetType === 'turma') { + if (!targetId) return showAlert('Aviso', 'Selecione uma turma.', 'warning'); + targetStudents = inscricoes.filter(p => p.turmaId === targetId); + } else if (targetType === 'aluno') { + if (!targetId) return showAlert('Aviso', 'Selecione uma pré-matrícula.', 'warning'); + targetStudents = inscricoes.filter(p => p.id === targetId); + } + + const validPre = targetStudents.filter(p => p.telefone); + if (validPre.length === 0) { + return showAlert('Erro', 'Nenhuma pré-matrícula com telefone válido foi selecionada.', 'error'); + } + + const payloadAlunos = validPre.map(p => ({ + nome: p.nome.split(' ')[0], + telefone: p.telefone, + matricula: 'Pré-Matrícula' + })); + + setIsSendingMass(true); + try { + const formData = new FormData(); + formData.append('alunos', JSON.stringify(payloadAlunos)); + formData.append('mensagem', normalizeLineBreaks(messageText)); + formData.append('delay', String(parseInt(massDelay) || 60)); + if (massAttachment) { + formData.append('attachment', massAttachment); + } + + const resp = await fetch('/api/enviar-massa', { + method: 'POST', + body: formData + }); + const resData = await resp.json(); + + if (resp.ok) { + setMessageText(''); + setTargetId(''); + setMassAttachment(null); + setShowMassModal(false); + showAlert('Sucesso', 'Disparo iniciado no servidor!', 'success'); + } else { + showAlert('Erro', resData.error || 'Erro ao iniciar disparo.', 'error'); + } + } catch (e) { + showAlert('Erro', 'Erro de conexão.', 'error'); + } finally { + setIsSendingMass(false); + } + }; + const loadData = useCallback(async () => { setIsLoading(true); try { @@ -772,11 +847,18 @@ const PreMatricula: React.FC = ({ data, onConvert }) => { - +
+ + +
{filteredInscricoes.length === 0 ? ( @@ -889,6 +971,162 @@ const PreMatricula: React.FC = ({ data, onConvert }) => { )} )} + + {showMassModal && ( +
+
!isSendingMass && setShowMassModal(false)} /> +
+ + +

+ Disparo em Massa +

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