diff --git a/prisma/schema.prisma b/prisma/schema.prisma index 49d0926..477ef63 100644 --- a/prisma/schema.prisma +++ b/prisma/schema.prisma @@ -293,6 +293,11 @@ model SystemSettings { whatsappConnected Boolean @default(false) @map("whatsapp_connected") whatsappPhoneNumber String? @map("whatsapp_phone_number") + whatsappTemplatePayment String? @map("whatsapp_template_payment") @db.Text + whatsappTemplateWelcome String? @map("whatsapp_template_welcome") @db.Text + whatsappTemplateNewContent String? @map("whatsapp_template_new_content") @db.Text + whatsappTemplatePromo String? @map("whatsapp_template_promo") @db.Text + brandName String @default("MICROTEC") @map("brand_name") brandSlogan String @default("Acelere seus estudos em informática e pacote Office no estilo Netflix.") @map("brand_slogan") cnpj String @default("") @map("cnpj") diff --git a/server.ts b/server.ts index c77d80f..d0c1496 100644 --- a/server.ts +++ b/server.ts @@ -1407,7 +1407,8 @@ app.post('/api/admin/settings', authenticateToken, requireAdmin, async (req: Aut 'maxBoletoInstallments', 'evolutionApiUrl', 'evolutionApiKey', 'evolutionInstance', 'whatsappConnected', 'whatsappPhoneNumber', 'brandName', 'brandSlogan', 'cnpj', 'address', 'smtpHost', 'smtpPort', 'smtpUser', 'smtpPass', 'smtpFrom', 'smtpSecure', - 'termsOfUse', 'privacyPolicy', 'featuredCourseId', 'heroImageUrl', 'heroTitle', 'heroDescription' + 'termsOfUse', 'privacyPolicy', 'featuredCourseId', 'heroImageUrl', 'heroTitle', 'heroDescription', + 'whatsappTemplatePayment', 'whatsappTemplateWelcome', 'whatsappTemplateNewContent', 'whatsappTemplatePromo' ]; const cleanedSettings: any = {}; for (const key of allowedKeys) { diff --git a/src/components/AdminMessagesView.tsx b/src/components/AdminMessagesView.tsx index c5ecd2c..35b048f 100644 --- a/src/components/AdminMessagesView.tsx +++ b/src/components/AdminMessagesView.tsx @@ -1,6 +1,7 @@ import React, { useState, useEffect } from 'react'; import { Mail, MessageSquare, BookOpen, Send, User, Reply, CheckCircle2, AlertCircle } from 'lucide-react'; import CustomModal from './CustomModal'; +import { Smartphone, Save } from 'lucide-react'; interface SupportTicket { id: string; @@ -38,7 +39,7 @@ interface Note { } export default function AdminMessagesView() { - const [activeTab, setActiveTab] = useState<'tickets' | 'notes'>('tickets'); + const [activeTab, setActiveTab] = useState<'tickets' | 'notes' | 'whatsapp_templates'>('tickets'); const [tickets, setTickets] = useState([]); const [notes, setNotes] = useState([]); const [isLoading, setIsLoading] = useState(true); @@ -46,6 +47,12 @@ export default function AdminMessagesView() { const [replyingTo, setReplyingTo] = useState(null); const [isSending, setIsSending] = useState(false); const [alertModal, setAlertModal] = useState<{isOpen: boolean, title: string, message: string, isError?: boolean}>({isOpen: false, title: '', message: ''}); + + // WhatsApp Templates State + const [settings, setSettings] = useState(null); + const [activeTemplateModal, setActiveTemplateModal] = useState(null); + const [currentTemplateText, setCurrentTemplateText] = useState(''); + const [isSavingTemplate, setIsSavingTemplate] = useState(false); useEffect(() => { fetchMessages(); @@ -63,8 +70,15 @@ export default function AdminMessagesView() { setTickets(data.tickets); setNotes(data.notes); } + const resSettings = await fetch('/api/admin/settings', { + headers: { 'Authorization': `Bearer ${token}` } + }); + if (resSettings.ok) { + const data = await resSettings.json(); + setSettings(data); + } } catch (error) { - console.error('Error fetching messages:', error); + console.error('Error fetching data:', error); } finally { setIsLoading(false); } @@ -99,6 +113,37 @@ export default function AdminMessagesView() { } }; + const handleSaveTemplate = async (field: string) => { + setIsSavingTemplate(true); + try { + const token = localStorage.getItem('admin_token'); + const res = await fetch('/api/admin/settings', { + method: 'POST', + headers: { 'Authorization': `Bearer ${token}`, 'Content-Type': 'application/json' }, + body: JSON.stringify({ [field]: currentTemplateText }) + }); + if (res.ok) { + setSettings({...settings, [field]: currentTemplateText}); + setActiveTemplateModal(null); + setAlertModal({isOpen: true, title: 'Sucesso', message: 'Template salvo com sucesso!'}); + } else { + setAlertModal({isOpen: true, title: 'Erro', message: 'Erro ao salvar template.', isError: true}); + } + } catch (error) { + console.error('Error saving template:', error); + setAlertModal({isOpen: true, title: 'Erro', message: 'Erro interno ao salvar template.', isError: true}); + } finally { + setIsSavingTemplate(false); + } + }; + + const whatsappTemplates = [ + { id: 'whatsappTemplatePayment', title: 'Pagamento Confirmado', desc: 'Enviada quando o aluno paga um plano ou curso.', icon: '💰' }, + { id: 'whatsappTemplateWelcome', title: 'Boas-Vindas (Nova Conta)', desc: 'Enviada quando o aluno cria a conta.', icon: '👋' }, + { id: 'whatsappTemplateNewContent', title: 'Novo Conteúdo (Carrossel)', desc: 'Enviada ao postar curso/módulo/aula.', icon: '🎬' }, + { id: 'whatsappTemplatePromo', title: 'Promoção em Massa', desc: 'Mensagem para todos os clientes.', icon: '🚀' }, + ]; + return (
@@ -111,21 +156,28 @@ export default function AdminMessagesView() {
-
+
+
{isLoading ? ( @@ -286,9 +338,64 @@ export default function AdminMessagesView() { )) ) )} + + {activeTab === 'whatsapp_templates' && ( +
+ {whatsappTemplates.map(t => ( +
{ setCurrentTemplateText(settings?.[t.id] || ''); setActiveTemplateModal(t.id); }} + > +
+
+ {t.icon} +
+
+

{t.title}

+

{t.desc}

+
+
+
+ {settings?.[t.id] || 'Clique para configurar esta mensagem...'} +
+
+ ))} +
+ )}
)} + setActiveTemplateModal(null)} title={whatsappTemplates.find(t => t.id === activeTemplateModal)?.title || 'Editar Template'} isGlass={true}> +
+
+

Formatação do WhatsApp: Use *negrito*, _itálico_, ~riscado~.

+

Tags dinâmicas: Use {'{nome}'} para inserir o primeiro nome do aluno.

+

Emojis são suportados nativamente (pressione Win + . no Windows ou Cmd + Ctrl + Espaço no Mac).

+
+ +