Adicionado modal de perfil do admin, corrigido emoji picker, tags em todas as caixas
Build and Deploy (Gitea) / build-and-deploy (push) Failing after 38s
Details
Build and Deploy (Gitea) / build-and-deploy (push) Failing after 38s
Details
This commit is contained in:
parent
3b0313d5c0
commit
8dde8bf9e3
|
|
@ -3,7 +3,7 @@ import { useModal } from '../contexts/ModalContext';
|
|||
import {
|
||||
TrendingUp, Users, AlertTriangle, XCircle, Search,
|
||||
Settings, CheckCircle2, RefreshCw, Link as LinkIcon, ShieldCheck, HelpCircle, Eye, DollarSign,
|
||||
Shield, Layers, LogOut, Menu, X, GraduationCap, Mail, ThumbsUp, ThumbsDown
|
||||
Shield, Layers, LogOut, Menu, X, GraduationCap, Mail, ThumbsUp, ThumbsDown, UserCog
|
||||
} from 'lucide-react';
|
||||
import { LineChart, Line, BarChart, Bar, XAxis, YAxis, CartesianGrid, Tooltip as RechartsTooltip, ResponsiveContainer, Legend, Cell, PieChart, Pie } from 'recharts';
|
||||
import { SubscriptionStatus } from '../types';
|
||||
|
|
@ -11,6 +11,7 @@ import { AdminPricingManager } from './AdminPricingManager';
|
|||
import { WhatsappConnectCard } from './WhatsappConnectCard';
|
||||
import AdminContentManager from './AdminContentManager';
|
||||
import AdminMessagesView from './AdminMessagesView';
|
||||
import AdminProfileModal from './AdminProfileModal';
|
||||
import ToggleSwitch from './ToggleSwitch';
|
||||
|
||||
interface WebhookLog {
|
||||
|
|
@ -92,6 +93,7 @@ export default function AdminDashboard({ token, adminUser, onLogout }: AdminDash
|
|||
const [showSaveSuccessModal, setShowSaveSuccessModal] = useState(false);
|
||||
const [asaasTestResult, setAsaasTestResult] = useState<{ loading: boolean; success: boolean | null; message: string } | null>(null);
|
||||
const [smtpTestResult, setSmtpTestResult] = useState<{ loading: boolean; success: boolean | null; message: string } | null>(null);
|
||||
const [showProfileModal, setShowProfileModal] = useState(false);
|
||||
|
||||
useEffect(() => {
|
||||
fetchDashboardData();
|
||||
|
|
@ -493,15 +495,20 @@ export default function AdminDashboard({ token, adminUser, onLogout }: AdminDash
|
|||
|
||||
{/* Footer profile card */}
|
||||
<div className="p-4 border-t border-white/5 space-y-4">
|
||||
<div className="flex items-center space-x-3 bg-white/5 p-3 rounded-xl border border-white/5">
|
||||
<div className="w-9 h-9 rounded-full bg-[#E50914] text-white font-extrabold flex items-center justify-center text-xs shadow-md">
|
||||
<button
|
||||
onClick={() => setShowProfileModal(true)}
|
||||
className="w-full flex items-center space-x-3 bg-white/5 hover:bg-white/10 p-3 rounded-xl border border-white/5 hover:border-white/15 transition-all group text-left"
|
||||
title="Editar Meu Perfil"
|
||||
>
|
||||
<div className="w-9 h-9 rounded-full bg-[#E50914] text-white font-extrabold flex items-center justify-center text-xs shadow-md flex-shrink-0">
|
||||
{adminUser?.name ? adminUser.name.split(' ').map((n: string) => n[0]).join('').substring(0, 2).toUpperCase() : 'AD'}
|
||||
</div>
|
||||
<div className="flex-1 min-w-0">
|
||||
<p className="text-xs font-bold text-white truncate">{adminUser?.name || 'Super Admin'}</p>
|
||||
<p className="text-[10px] text-zinc-500 truncate">{adminUser?.email || 'admin@microtecflix.com'}</p>
|
||||
</div>
|
||||
</div>
|
||||
<UserCog size={14} className="text-zinc-600 group-hover:text-zinc-300 transition-colors flex-shrink-0" />
|
||||
</button>
|
||||
{onLogout && (
|
||||
<button
|
||||
onClick={onLogout}
|
||||
|
|
@ -512,6 +519,13 @@ export default function AdminDashboard({ token, adminUser, onLogout }: AdminDash
|
|||
</button>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{/* Admin Profile Modal */}
|
||||
<AdminProfileModal
|
||||
isOpen={showProfileModal}
|
||||
onClose={() => setShowProfileModal(false)}
|
||||
token={token}
|
||||
/>
|
||||
</aside>
|
||||
|
||||
{/* BACKGROUND SHADOW FOR MOBILE SIDEBAR */}
|
||||
|
|
|
|||
|
|
@ -275,8 +275,8 @@ export default function AdminMessagesView() {
|
|||
|
||||
const whatsappTemplates = [
|
||||
{ id: 'whatsappTemplatePayment', title: 'Pagamento Confirmado', desc: 'Enviada automaticamente quando o aluno paga um plano ou curso.', icon: '💰', tags: ['{nome}', '{valor}', '{plano}', '{curso}'] },
|
||||
{ id: 'whatsappTemplateWelcome', title: 'Boas-Vindas (Nova Conta)', desc: 'Enviada automaticamente quando o aluno cria uma conta.', icon: '👋', tags: ['{nome}'] },
|
||||
{ id: 'whatsappTemplateNewContent', title: 'Novo Conteúdo (Carrossel)', desc: 'Enviada manualmente ao postar curso/módulo/aula.', icon: '🎬', tags: ['{nome}'] },
|
||||
{ id: 'whatsappTemplateWelcome', title: 'Boas-Vindas (Nova Conta)', desc: 'Enviada automaticamente quando o aluno cria uma conta.', icon: '👋', tags: ['{nome}', '{curso}', '{plano}', '{valor}'] },
|
||||
{ id: 'whatsappTemplateNewContent', title: 'Novo Conteúdo (Carrossel)', desc: 'Enviada manualmente ao postar curso/módulo/aula.', icon: '🎬', tags: ['{nome}', '{curso}', '{modulo}', '{plano}', '{valor}'] },
|
||||
];
|
||||
|
||||
return (
|
||||
|
|
@ -531,27 +531,53 @@ export default function AdminMessagesView() {
|
|||
ref={broadcastRef}
|
||||
value={broadcastText}
|
||||
onChange={(e) => setBroadcastText(e.target.value)}
|
||||
className="w-full h-40 bg-black/40 border border-white/10 rounded-lg p-3 pr-12 text-white text-sm focus:outline-none focus:border-amber-500/50 resize-none"
|
||||
className="w-full h-40 bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:outline-none focus:border-amber-500/50 resize-none"
|
||||
placeholder="Escreva a mensagem de promoção aqui..."
|
||||
/>
|
||||
<div className="absolute bottom-3 right-3">
|
||||
</div>
|
||||
|
||||
{/* Tags e emoji para broadcast */}
|
||||
<div className="flex flex-wrap gap-2 items-center">
|
||||
<span className="text-[11px] text-zinc-500">Tags:</span>
|
||||
{['{nome}', '{curso}', '{modulo}', '{plano}', '{valor}'].map(tag => (
|
||||
<code
|
||||
key={tag}
|
||||
className="bg-zinc-800 hover:bg-zinc-700 text-amber-300 px-1.5 py-0.5 rounded font-mono cursor-pointer border border-zinc-600 hover:border-amber-400 transition-colors text-[10px]"
|
||||
title="Clique para inserir"
|
||||
onClick={() => {
|
||||
if (broadcastRef.current) {
|
||||
const start = broadcastRef.current.selectionStart;
|
||||
const end = broadcastRef.current.selectionEnd;
|
||||
const newText = broadcastText.substring(0, start) + tag + broadcastText.substring(end);
|
||||
setBroadcastText(newText);
|
||||
setTimeout(() => {
|
||||
broadcastRef.current?.focus();
|
||||
broadcastRef.current?.setSelectionRange(start + tag.length, start + tag.length);
|
||||
}, 0);
|
||||
} else {
|
||||
setBroadcastText(prev => prev + tag);
|
||||
}
|
||||
}}
|
||||
>{tag}</code>
|
||||
))}
|
||||
<div className="relative ml-auto">
|
||||
<button
|
||||
onClick={() => setShowBroadcastEmojiPicker(!showBroadcastEmojiPicker)}
|
||||
className="w-8 h-8 flex items-center justify-center text-zinc-400 hover:text-amber-400 transition-colors"
|
||||
title="Inserir emoji"
|
||||
className="flex items-center gap-1.5 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 hover:border-zinc-500 text-zinc-300 hover:text-white px-2.5 py-1 rounded-lg text-[11px] font-bold transition-all"
|
||||
>
|
||||
<Smile className="w-5 h-5" />
|
||||
<Smile className="w-3.5 h-3.5" />
|
||||
<span>Emoji</span>
|
||||
</button>
|
||||
{showBroadcastEmojiPicker && (
|
||||
<div className="absolute top-8 right-0 bg-zinc-900 border border-zinc-700 rounded-xl p-3 grid grid-cols-8 gap-1.5 z-50 shadow-2xl">
|
||||
{EMOJI_LIST.map(emoji => (
|
||||
<button key={emoji} onClick={() => insertEmoji(emoji, true)} className="text-xl hover:scale-125 transition-transform p-1 rounded hover:bg-white/10">
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
{showBroadcastEmojiPicker && (
|
||||
<div className="absolute bottom-12 right-3 bg-zinc-800 border border-white/10 rounded-xl p-3 grid grid-cols-6 gap-1.5 z-10 shadow-2xl">
|
||||
{EMOJI_LIST.map(emoji => (
|
||||
<button key={emoji} onClick={() => insertEmoji(emoji, true)} className="text-xl hover:scale-125 transition-transform p-1 rounded hover:bg-white/10">
|
||||
{emoji}
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
|
||||
<div className="flex items-center gap-3">
|
||||
|
|
@ -587,16 +613,34 @@ export default function AdminMessagesView() {
|
|||
</div>
|
||||
)}
|
||||
|
||||
{/* Template Edit Modal */}
|
||||
<CustomModal isOpen={!!activeTemplateModal} onClose={() => { setActiveTemplateModal(null); setShowEmojiPicker(false); }} title={whatsappTemplates.find(t => t.id === activeTemplateModal)?.title || 'Editar Template'} isGlass={true}>
|
||||
<div className="space-y-4">
|
||||
<div className="bg-blue-500/10 border border-blue-500/20 p-3 rounded-lg text-xs text-blue-200 space-y-1">
|
||||
<div className="bg-blue-500/10 border border-blue-500/20 p-3 rounded-lg text-xs text-blue-200 space-y-2">
|
||||
<p><strong>Formatação do WhatsApp:</strong> Use <code className="bg-black/40 px-1 rounded">*negrito*</code>, <code className="bg-black/40 px-1 rounded">_itálico_</code>, <code className="bg-black/40 px-1 rounded">~riscado~</code>.</p>
|
||||
<p><strong>Tags disponíveis:</strong>{' '}
|
||||
{whatsappTemplates.find(t => t.id === activeTemplateModal)?.tags.map(tag => (
|
||||
<code key={tag} className="bg-black/40 px-1 rounded font-bold text-blue-100 mr-1">{tag}</code>
|
||||
<div className="flex flex-wrap gap-1 items-center">
|
||||
<strong>Tags:</strong>
|
||||
{['{nome}', '{curso}', '{modulo}', '{plano}', '{valor}'].map(tag => (
|
||||
<code
|
||||
key={tag}
|
||||
className="bg-zinc-800 hover:bg-zinc-700 text-blue-300 px-1.5 py-0.5 rounded font-mono cursor-pointer border border-zinc-600 hover:border-blue-400 transition-colors"
|
||||
title="Clique para inserir"
|
||||
onClick={() => {
|
||||
if (textareaRef.current) {
|
||||
const start = textareaRef.current.selectionStart;
|
||||
const end = textareaRef.current.selectionEnd;
|
||||
const newText = currentTemplateText.substring(0, start) + tag + currentTemplateText.substring(end);
|
||||
setCurrentTemplateText(newText);
|
||||
setTimeout(() => {
|
||||
textareaRef.current?.focus();
|
||||
textareaRef.current?.setSelectionRange(start + tag.length, start + tag.length);
|
||||
}, 0);
|
||||
} else {
|
||||
setCurrentTemplateText(prev => prev + tag);
|
||||
}
|
||||
}}
|
||||
>{tag}</code>
|
||||
))}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="relative">
|
||||
|
|
@ -604,20 +648,23 @@ export default function AdminMessagesView() {
|
|||
ref={textareaRef}
|
||||
value={currentTemplateText}
|
||||
onChange={(e) => setCurrentTemplateText(e.target.value)}
|
||||
className="w-full h-52 bg-black/40 border border-white/10 rounded-lg p-3 pr-12 text-white text-sm focus:outline-none focus:border-[#E50914] resize-none"
|
||||
className="w-full h-52 bg-black/40 border border-white/10 rounded-lg p-3 text-white text-sm focus:outline-none focus:border-[#E50914] resize-none"
|
||||
placeholder="Escreva a mensagem aqui..."
|
||||
/>
|
||||
<div className="absolute bottom-3 right-3">
|
||||
<button
|
||||
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
|
||||
className="w-8 h-8 flex items-center justify-center text-zinc-400 hover:text-[#E50914] transition-colors"
|
||||
title="Inserir emoji"
|
||||
>
|
||||
<Smile className="w-5 h-5" />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{/* Emoji picker row - outside textarea, fully visible */}
|
||||
<div className="relative">
|
||||
<button
|
||||
onClick={() => setShowEmojiPicker(!showEmojiPicker)}
|
||||
className="flex items-center gap-2 bg-zinc-800 hover:bg-zinc-700 border border-zinc-700 hover:border-zinc-500 text-zinc-300 hover:text-white px-3 py-1.5 rounded-lg text-xs font-bold transition-all"
|
||||
title="Inserir emoji"
|
||||
>
|
||||
<Smile className="w-4 h-4" />
|
||||
<span>Inserir Emoji</span>
|
||||
</button>
|
||||
{showEmojiPicker && (
|
||||
<div className="absolute bottom-12 right-3 bg-zinc-800 border border-white/10 rounded-xl p-3 grid grid-cols-6 gap-1.5 z-10 shadow-2xl">
|
||||
<div className="absolute top-10 left-0 bg-zinc-900 border border-zinc-700 rounded-xl p-3 grid grid-cols-8 gap-1.5 z-50 shadow-2xl">
|
||||
{EMOJI_LIST.map(emoji => (
|
||||
<button key={emoji} onClick={() => insertEmoji(emoji, false)} className="text-xl hover:scale-125 transition-transform p-1 rounded hover:bg-white/10">
|
||||
{emoji}
|
||||
|
|
|
|||
Loading…
Reference in New Issue