Adicionado endpoint e botao para Testar Envio de Template do WhatsApp
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 58s
Details
Build and Deploy (Gitea) / build-and-deploy (push) Successful in 58s
Details
This commit is contained in:
parent
4cc23a9e5b
commit
3b0313d5c0
54
server.ts
54
server.ts
|
|
@ -2562,6 +2562,60 @@ app.post('/api/admin/reply-message', authenticateToken, requireAdmin, async (req
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// Admin: Test WhatsApp template message
|
||||||
|
app.post('/api/admin/whatsapp/test-template', authenticateToken, requireAdmin, async (req: AuthenticatedRequest, res: Response) => {
|
||||||
|
try {
|
||||||
|
const { templateId } = req.body;
|
||||||
|
if (!templateId) return res.status(400).json({ error: 'templateId é obrigatório' });
|
||||||
|
|
||||||
|
const adminUser = await prisma.user.findUnique({ where: { id: req.user!.id } });
|
||||||
|
if (!adminUser?.whatsapp) {
|
||||||
|
return res.status(400).json({ error: 'Você precisa ter um número de WhatsApp configurado no seu perfil para receber o teste.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const settings = await prisma.systemSettings.findFirst();
|
||||||
|
let templateStr = '';
|
||||||
|
|
||||||
|
switch(templateId) {
|
||||||
|
case 'whatsappTemplatePayment':
|
||||||
|
templateStr = settings?.whatsappTemplatePayment || '';
|
||||||
|
break;
|
||||||
|
case 'whatsappTemplateWelcome':
|
||||||
|
templateStr = settings?.whatsappTemplateWelcome || '';
|
||||||
|
break;
|
||||||
|
case 'whatsappTemplateNewContent':
|
||||||
|
templateStr = settings?.whatsappTemplateNewContent || '';
|
||||||
|
break;
|
||||||
|
case 'whatsappTemplatePromo':
|
||||||
|
templateStr = settings?.whatsappTemplatePromo || '';
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!templateStr) {
|
||||||
|
return res.status(400).json({ error: 'Este modelo de mensagem ainda não foi configurado ou está vazio.' });
|
||||||
|
}
|
||||||
|
|
||||||
|
const firstName = adminUser.name.split(' ')[0];
|
||||||
|
const msg = processTemplate(templateStr, {
|
||||||
|
nome: firstName,
|
||||||
|
curso: 'Curso de Informática (TESTE)',
|
||||||
|
plano: 'Plano Premium (TESTE)',
|
||||||
|
valor: 'R$ 49,90'
|
||||||
|
});
|
||||||
|
|
||||||
|
const success = await sendWhatsappNotification(adminUser.whatsapp, msg);
|
||||||
|
|
||||||
|
if (success) {
|
||||||
|
res.json({ success: true, message: 'Mensagem de teste enviada com sucesso para o seu número!' });
|
||||||
|
} else {
|
||||||
|
res.status(500).json({ error: 'Falha ao enviar mensagem pela Evolution API. Verifique a conexão e o status da instância.' });
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error testing template:', error);
|
||||||
|
res.status(500).json({ error: 'Internal server error' });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
// Admin: Broadcast WhatsApp message to all students with anti-spam delay
|
// Admin: Broadcast WhatsApp message to all students with anti-spam delay
|
||||||
app.post('/api/admin/whatsapp/broadcast', authenticateToken, requireAdmin, async (req: AuthenticatedRequest, res: Response) => {
|
app.post('/api/admin/whatsapp/broadcast', authenticateToken, requireAdmin, async (req: AuthenticatedRequest, res: Response) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -107,6 +107,7 @@ export default function AdminMessagesView() {
|
||||||
const [activeTemplateModal, setActiveTemplateModal] = useState<string | null>(null);
|
const [activeTemplateModal, setActiveTemplateModal] = useState<string | null>(null);
|
||||||
const [currentTemplateText, setCurrentTemplateText] = useState('');
|
const [currentTemplateText, setCurrentTemplateText] = useState('');
|
||||||
const [isSavingTemplate, setIsSavingTemplate] = useState(false);
|
const [isSavingTemplate, setIsSavingTemplate] = useState(false);
|
||||||
|
const [isTestingTemplate, setIsTestingTemplate] = useState<string | null>(null);
|
||||||
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
const [showEmojiPicker, setShowEmojiPicker] = useState(false);
|
||||||
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
const textareaRef = useRef<HTMLTextAreaElement>(null);
|
||||||
|
|
||||||
|
|
@ -202,6 +203,30 @@ export default function AdminMessagesView() {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleTestTemplate = async (templateId: string, e: React.MouseEvent) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
setIsTestingTemplate(templateId);
|
||||||
|
try {
|
||||||
|
const token = localStorage.getItem('admin_token');
|
||||||
|
const res = await fetch('/api/admin/whatsapp/test-template', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' },
|
||||||
|
body: JSON.stringify({ templateId })
|
||||||
|
});
|
||||||
|
const data = await res.json();
|
||||||
|
if (res.ok) {
|
||||||
|
setAlertModal({isOpen: true, title: 'Enviado! 📱', message: data.message});
|
||||||
|
} else {
|
||||||
|
setAlertModal({isOpen: true, title: 'Erro no Teste', message: data.error, isError: true});
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('Error testing template:', error);
|
||||||
|
setAlertModal({isOpen: true, title: 'Erro', message: 'Erro interno ao testar template.', isError: true});
|
||||||
|
} finally {
|
||||||
|
setIsTestingTemplate(null);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
const insertEmoji = (emoji: string, isForBroadcast = false) => {
|
const insertEmoji = (emoji: string, isForBroadcast = false) => {
|
||||||
const ref = isForBroadcast ? broadcastRef : textareaRef;
|
const ref = isForBroadcast ? broadcastRef : textareaRef;
|
||||||
const setter = isForBroadcast ? setBroadcastText : setCurrentTemplateText;
|
const setter = isForBroadcast ? setBroadcastText : setCurrentTemplateText;
|
||||||
|
|
@ -452,12 +477,31 @@ export default function AdminMessagesView() {
|
||||||
<span key={tag} className="text-[9px] bg-blue-500/10 text-blue-300 px-1.5 py-0.5 rounded font-mono">{tag}</span>
|
<span key={tag} className="text-[9px] bg-blue-500/10 text-blue-300 px-1.5 py-0.5 rounded font-mono">{tag}</span>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
<div className="mt-2 flex items-center gap-1.5">
|
<div className="mt-3 flex items-center justify-between">
|
||||||
|
<div className="flex items-center gap-1.5">
|
||||||
<div className={`w-2 h-2 rounded-full ${settings?.[t.id] ? 'bg-green-500' : 'bg-zinc-600'}`}></div>
|
<div className={`w-2 h-2 rounded-full ${settings?.[t.id] ? 'bg-green-500' : 'bg-zinc-600'}`}></div>
|
||||||
<span className={`text-[10px] font-bold ${settings?.[t.id] ? 'text-green-400' : 'text-zinc-500'}`}>
|
<span className={`text-[10px] font-bold ${settings?.[t.id] ? 'text-green-400' : 'text-zinc-500'}`}>
|
||||||
{settings?.[t.id] ? 'Ativo' : 'Não configurado'}
|
{settings?.[t.id] ? 'Ativo' : 'Não configurado'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
{settings?.[t.id] && (
|
||||||
|
<button
|
||||||
|
onClick={(e) => handleTestTemplate(t.id, e)}
|
||||||
|
disabled={isTestingTemplate === t.id}
|
||||||
|
className="bg-zinc-800 hover:bg-zinc-700 text-zinc-300 px-3 py-1 rounded text-[10px] font-bold flex items-center gap-1 transition-colors"
|
||||||
|
>
|
||||||
|
{isTestingTemplate === t.id ? (
|
||||||
|
<div className="w-3 h-3 border-2 border-white/30 border-t-white rounded-full animate-spin" />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
<Send className="w-3 h-3" />
|
||||||
|
<span>Testar Envio</span>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</button>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue