Spaces:
Running
Running
| <!DOCTYPE html> | |
| <html lang="pt-BR"> | |
| <head> | |
| <meta charset="UTF-8"> | |
| <meta name="viewport" content="width=device-width, initial-scale=1.0"> | |
| <title>Painel de Controle - DeekSeek</title> | |
| <script src="https://cdn.tailwindcss.com"></script> | |
| <link href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css" rel="stylesheet"> | |
| <script src="https://cdn.jsdelivr.net/npm/axios/dist/axios.min.js"></script> | |
| <style> | |
| :root { | |
| --bg-primary: #1a1a2e; | |
| --bg-secondary: #16213e; | |
| --text-primary: #e6e6e6; | |
| --text-secondary: #b8b8b8; | |
| --accent: #4f46e5; | |
| --transition-speed: 0.3s; | |
| } | |
| .light { | |
| --bg-primary: #f8fafc; | |
| --bg-secondary: #ffffff; | |
| --text-primary: #1e293b; | |
| --text-secondary: #64748b; | |
| } | |
| body { | |
| background-color: var(--bg-primary); | |
| color: var(--text-primary); | |
| transition: all var(--transition-speed) ease; | |
| font-family: 'Inter', -apple-system, BlinkMacSystemFont, sans-serif; | |
| } | |
| .sidebar { | |
| background-color: var(--bg-secondary); | |
| border-right: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .card { | |
| background-color: var(--bg-secondary); | |
| border: 1px solid rgba(255, 255, 255, 0.1); | |
| transition: transform 0.2s ease, box-shadow 0.2s ease; | |
| border-radius: 0.5rem; | |
| } | |
| .card:hover { | |
| transform: translateY(-2px); | |
| box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.2); | |
| } | |
| .loading-spinner { | |
| animation: spin 1s linear infinite; | |
| } | |
| @keyframes spin { | |
| 0% { transform: rotate(0deg); } | |
| 100% { transform: rotate(360deg); } | |
| } | |
| @media (max-width: 768px) { | |
| .sidebar { | |
| width: 100%; | |
| position: fixed; | |
| z-index: 40; | |
| height: auto; | |
| bottom: 0; | |
| left: 0; | |
| right: 0; | |
| border-right: none; | |
| border-top: 1px solid rgba(255, 255, 255, 0.1); | |
| } | |
| .sidebar-nav { | |
| display: flex; | |
| overflow-x: auto; | |
| } | |
| .sidebar-nav ul { | |
| display: flex; | |
| flex-direction: row; | |
| } | |
| .sidebar-nav li { | |
| margin-right: 0.5rem; | |
| } | |
| .sidebar-nav button { | |
| padding: 0.5rem; | |
| white-space: nowrap; | |
| } | |
| .sidebar-nav i { | |
| margin-right: 0; | |
| } | |
| .sidebar-nav span { | |
| display: none; | |
| } | |
| .user-profile { | |
| display: none; | |
| } | |
| main { | |
| padding-bottom: 80px; | |
| } | |
| } | |
| </style> | |
| </head> | |
| <body class="dark"> | |
| <div id="root"></div> | |
| <script> | |
| // Configuração da API | |
| const API_BASE_URL = process.env.API_BASE_URL || 'http://localhost:3000/api'; | |
| // Serviços de API | |
| const ApiService = { | |
| async getNews() { | |
| try { | |
| const response = await axios.get(`${API_BASE_URL}/news`); | |
| return response.data; | |
| } catch (error) { | |
| console.error('Erro ao buscar notícias:', error); | |
| return []; | |
| } | |
| }, | |
| async getAlerts() { | |
| try { | |
| const response = await axios.get(`${API_BASE_URL}/alerts`); | |
| return response.data; | |
| } catch (error) { | |
| console.error('Erro ao buscar alertas:', error); | |
| return []; | |
| } | |
| }, | |
| async postReport(reportData) { | |
| try { | |
| const response = await axios.post(`${API_BASE_URL}/denuncias`, reportData); | |
| return response.data; | |
| } catch (error) { | |
| console.error('Erro ao enviar denúncia:', error); | |
| throw error; | |
| } | |
| }, | |
| async getDashboardData() { | |
| try { | |
| const response = await axios.get(`${API_BASE_URL}/dashboard`); | |
| return response.data; | |
| } catch (error) { | |
| console.error('Erro ao buscar dados do dashboard:', error); | |
| return null; | |
| } | |
| } | |
| }; | |
| // Componentes reutilizáveis | |
| const Button = ({ children, icon, onClick, variant = 'primary', size = 'md', className = '', disabled = false }) => { | |
| const baseClasses = 'flex items-center justify-center rounded-lg font-medium transition-all'; | |
| const sizeClasses = { | |
| sm: 'px-3 py-1 text-sm', | |
| md: 'px-4 py-2', | |
| lg: 'px-6 py-3' | |
| }; | |
| const variantClasses = { | |
| primary: 'bg-indigo-600 hover:bg-indigo-700 text-white', | |
| secondary: 'bg-gray-700 hover:bg-gray-600 text-white', | |
| success: 'bg-green-600 hover:bg-green-700 text-white', | |
| danger: 'bg-red-600 hover:bg-red-700 text-white', | |
| warning: 'bg-yellow-600 hover:bg-yellow-700 text-white', | |
| ghost: 'bg-transparent hover:bg-gray-700/50 text-white' | |
| }; | |
| return ` | |
| <button | |
| onclick="${onClick}" | |
| ${disabled ? 'disabled' : ''} | |
| class="${baseClasses} ${sizeClasses[size]} ${variantClasses[variant]} ${className}" | |
| > | |
| ${icon ? `<i class="${icon} ${children ? 'mr-2' : ''}"></i>` : ''} | |
| ${children || ''} | |
| </button> | |
| `; | |
| }; | |
| const Card = ({ children, title, className = '', headerClassName = '', bodyClassName = '' }) => { | |
| return ` | |
| <div class="card ${className}"> | |
| ${title ? ` | |
| <div class="border-b border-gray-700 p-4 ${headerClassName}"> | |
| <h3 class="font-semibold">${title}</h3> | |
| </div> | |
| ` : ''} | |
| <div class="p-4 ${bodyClassName}"> | |
| ${children} | |
| </div> | |
| </div> | |
| `; | |
| }; | |
| const LoadingSpinner = () => { | |
| return ` | |
| <div class="flex items-center justify-center h-64"> | |
| <i class="fas fa-circle-notch fa-spin text-4xl text-indigo-500 loading-spinner"></i> | |
| </div> | |
| `; | |
| }; | |
| // Componentes principais | |
| const App = () => { | |
| const [darkMode, setDarkMode] = useState(true); | |
| const [activeSection, setActiveSection] = useState('dashboard'); | |
| const [isLoading, setIsLoading] = useState(true); | |
| const [dashboardData, setDashboardData] = useState(null); | |
| const [news, setNews] = useState([]); | |
| const [alerts, setAlerts] = useState([]); | |
| const toggleDarkMode = () => { | |
| setDarkMode(prev => { | |
| const newMode = !prev; | |
| document.body.classList.toggle('dark'); | |
| document.body.classList.toggle('light'); | |
| return newMode; | |
| }); | |
| }; | |
| const fetchData = async () => { | |
| setIsLoading(true); | |
| try { | |
| const [dashboard, newsData, alertsData] = await Promise.all([ | |
| ApiService.getDashboardData(), | |
| ApiService.getNews(), | |
| ApiService.getAlerts() | |
| ]); | |
| setDashboardData(dashboard); | |
| setNews(newsData); | |
| setAlerts(alertsData); | |
| } catch (error) { | |
| console.error('Erro ao carregar dados:', error); | |
| } finally { | |
| setIsLoading(false); | |
| } | |
| }; | |
| useEffect(() => { | |
| fetchData(); | |
| }, []); | |
| const renderSection = () => { | |
| if (isLoading) return LoadingSpinner(); | |
| switch (activeSection) { | |
| case 'dashboard': | |
| return DashboardSection({ dashboardData, news, alerts }); | |
| case 'news': | |
| return NewsSection({ news }); | |
| case 'alerts': | |
| return AlertsSection({ alerts }); | |
| case 'reports': | |
| return ReportsSection(); | |
| default: | |
| return DashboardSection({ dashboardData, news, alerts }); | |
| } | |
| }; | |
| return ` | |
| <div class="flex h-screen overflow-hidden"> | |
| <!-- Sidebar --> | |
| ${Sidebar({ activeSection, setActiveSection })} | |
| <!-- Main Content --> | |
| <div class="flex-1 overflow-auto"> | |
| <!-- Header --> | |
| ${Header({ | |
| activeSection, | |
| darkMode, | |
| toggleDarkMode, | |
| isLoading | |
| })} | |
| <!-- Main Content --> | |
| <main class="p-4 md:p-6"> | |
| ${renderSection()} | |
| </main> | |
| </div> | |
| </div> | |
| `; | |
| }; | |
| const Sidebar = ({ activeSection, setActiveSection }) => { | |
| const sections = [ | |
| { id: 'dashboard', icon: 'fa-tachometer-alt', label: 'Dashboard' }, | |
| { id: 'news', icon: 'fa-newspaper', label: 'Notícias' }, | |
| { id: 'alerts', icon: 'fa-bell', label: 'Alertas' }, | |
| { id: 'reports', icon: 'fa-flag', label: 'Denúncias' } | |
| ]; | |
| return ` | |
| <div class="sidebar w-16 md:w-64 flex-shrink-0 flex flex-col"> | |
| <div class="p-4 border-b border-gray-700 flex items-center justify-center md:justify-between"> | |
| <h1 class="text-xl font-bold text-white flex items-center"> | |
| <i class="fas fa-search text-indigo-500 mr-0 md:mr-2"></i> | |
| <span class="hidden md:inline">DeekSeek</span> | |
| </h1> | |
| </div> | |
| <div class="flex-1 overflow-y-auto"> | |
| <nav class="p-2 md:p-4 sidebar-nav"> | |
| <ul class="space-y-1 md:space-y-2"> | |
| ${sections.map(section => ` | |
| <li key="${section.id}" class="mb-0 md:mb-2"> | |
| ${Button({ | |
| onClick: `(${setActiveSection})('${section.id}')`, | |
| variant: activeSection === section.id ? 'primary' : 'ghost', | |
| className: `w-full text-left ${activeSection === section.id ? 'bg-indigo-500/20 text-indigo-400' : 'hover:bg-gray-700'}`, | |
| icon: section.icon, | |
| children: `<span class="hidden md:inline">${section.label}</span>` | |
| })} | |
| </li> | |
| `).join('')} | |
| </ul> | |
| </nav> | |
| </div> | |
| <div class="p-4 border-t border-gray-700 user-profile"> | |
| <div class="flex items-center justify-center md:justify-between"> | |
| <div class="flex items-center"> | |
| <div class="w-10 h-10 rounded-full bg-indigo-500 flex items-center justify-center"> | |
| <i class="fas fa-user text-white"></i> | |
| </div> | |
| <div class="ml-3 hidden md:block"> | |
| <p class="text-sm font-medium">Usuário</p> | |
| <p class="text-xs text-gray-400">Admin</p> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }; | |
| const Header = ({ activeSection, darkMode, toggleDarkMode, isLoading }) => { | |
| const sectionTitles = { | |
| dashboard: 'Dashboard', | |
| news: 'Gerenciamento de Notícias', | |
| alerts: 'Monitoramento de Alertas', | |
| reports: 'Registro de Denúncias' | |
| }; | |
| return ` | |
| <header class="bg-gray-800 border-b border-gray-700 p-4 flex flex-col md:flex-row justify-between items-start md:items-center space-y-3 md:space-y-0"> | |
| <h2 class="text-xl font-semibold">${sectionTitles[activeSection]}</h2> | |
| <div class="flex items-center space-x-2 md:space-x-4 w-full md:w-auto"> | |
| <div class="relative flex-1 md:flex-none"> | |
| <i class="fas fa-search absolute left-3 top-1/2 transform -translate-y-1/2 text-gray-400"></i> | |
| <input | |
| type="text" | |
| placeholder="Pesquisar..." | |
| class="bg-gray-700 border border-gray-600 rounded-lg pl-10 pr-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 w-full md:w-64" | |
| ${isLoading ? 'disabled' : ''} | |
| /> | |
| </div> | |
| ${Button({ | |
| icon: darkMode ? 'fa-sun' : 'fa-moon', | |
| variant: 'ghost', | |
| onClick: `(${toggleDarkMode})()`, | |
| size: 'sm' | |
| })} | |
| </div> | |
| </header> | |
| `; | |
| }; | |
| // Seções | |
| const DashboardSection = ({ dashboardData, news, alerts }) => { | |
| if (!dashboardData) return LoadingSpinner(); | |
| return ` | |
| <div class="space-y-6"> | |
| <!-- Quick Stats --> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 lg:grid-cols-4 gap-4"> | |
| ${Card({ | |
| className: 'relative', | |
| children: ` | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <p class="text-gray-400">Notícias</p> | |
| <h3 class="text-2xl font-bold mt-1">${news.length}</h3> | |
| </div> | |
| <div class="bg-blue-500/10 p-3 rounded-full"> | |
| <i class="fas fa-newspaper text-blue-500"></i> | |
| </div> | |
| </div> | |
| ` | |
| })} | |
| ${Card({ | |
| className: 'relative', | |
| children: ` | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <p class="text-gray-400">Alertas</p> | |
| <h3 class="text-2xl font-bold mt-1">${alerts.length}</h3> | |
| </div> | |
| <div class="bg-red-500/10 p-3 rounded-full"> | |
| <i class="fas fa-bell text-red-500"></i> | |
| </div> | |
| </div> | |
| ` | |
| })} | |
| ${Card({ | |
| className: 'relative', | |
| children: ` | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <p class="text-gray-400">Última Atualização</p> | |
| <h3 class="text-2xl font-bold mt-1">${new Date(dashboardData.lastUpdated).toLocaleTimeString()}</h3> | |
| </div> | |
| <div class="bg-green-500/10 p-3 rounded-full"> | |
| <i class="fas fa-clock text-green-500"></i> | |
| </div> | |
| </div> | |
| ` | |
| })} | |
| ${Card({ | |
| className: 'relative', | |
| children: ` | |
| <div class="flex justify-between items-start"> | |
| <div> | |
| <p class="text-gray-400">Status do Sistema</p> | |
| <h3 class="text-2xl font-bold mt-1">${dashboardData.systemStatus}</h3> | |
| </div> | |
| <div class="bg-yellow-500/10 p-3 rounded-full"> | |
| <i class="fas fa-server text-yellow-500"></i> | |
| </div> | |
| </div> | |
| ` | |
| })} | |
| </div> | |
| <!-- Recent Activity --> | |
| ${Card({ | |
| title: 'Atividade Recente', | |
| children: ` | |
| <div class="space-y-4"> | |
| ${alerts.slice(0, 5).map(alert => ` | |
| <div class="flex items-start"> | |
| <div class="p-2 rounded-full mr-3 bg-red-500/10 text-red-500"> | |
| <i class="fas fa-exclamation-triangle"></i> | |
| </div> | |
| <div> | |
| <p class="text-sm">${alert.message}</p> | |
| <p class="text-xs text-gray-400">${new Date(alert.date).toLocaleString()}</p> | |
| </div> | |
| </div> | |
| `).join('')} | |
| </div> | |
| ` | |
| })} | |
| </div> | |
| `; | |
| }; | |
| const NewsSection = ({ news }) => { | |
| const [selectedNews, setSelectedNews] = useState(null); | |
| return ` | |
| <div class="space-y-6"> | |
| <div class="flex justify-between items-center"> | |
| <h3 class="text-lg font-semibold">Gerenciamento de Notícias</h3> | |
| ${Button({ | |
| onClick: "alert('Funcionalidade de adicionar notícia será implementada')", | |
| variant: 'primary', | |
| icon: 'fa-plus', | |
| children: 'Adicionar Notícia' | |
| })} | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- News List --> | |
| <div class="lg:col-span-1"> | |
| ${Card({ | |
| title: 'Lista de Notícias', | |
| children: ` | |
| <div class="space-y-3"> | |
| ${news.map(item => ` | |
| <div | |
| onclick="(${setSelectedNews})(${JSON.stringify(item)})" | |
| class="p-3 rounded-lg cursor-pointer transition-all ${ | |
| selectedNews?.id === item.id ? 'bg-indigo-500/10 border border-indigo-500/30' : 'hover:bg-gray-700' | |
| }" | |
| > | |
| <div class="flex justify-between items-center"> | |
| <h4 class="font-medium truncate">${item.title}</h4> | |
| <span class="px-2 py-1 text-xs rounded-full bg-green-500/20 text-green-400"> | |
| ${item.status} | |
| </span> | |
| </div> | |
| <p class="text-sm text-gray-400 truncate">${item.source}</p> | |
| <div class="flex justify-between mt-2 text-xs"> | |
| <span class="text-gray-400">Publicado em: ${new Date(item.publishedDate).toLocaleDateString()}</span> | |
| </div> | |
| </div> | |
| `).join('')} | |
| </div> | |
| ` | |
| })} | |
| </div> | |
| <!-- News Details --> | |
| <div class="lg:col-span-2"> | |
| ${selectedNews ? ` | |
| ${Card({ | |
| children: ` | |
| <div class="flex flex-col md:flex-row md:justify-between md:items-center mb-4 space-y-3 md:space-y-0"> | |
| <h3 class="text-xl font-semibold">${selectedNews.title}</h3> | |
| <div class="flex space-x-2"> | |
| ${Button({ | |
| variant: 'primary', | |
| size: 'sm', | |
| icon: 'fa-edit', | |
| children: 'Editar' | |
| })} | |
| ${Button({ | |
| variant: 'danger', | |
| size: 'sm', | |
| icon: 'fa-trash', | |
| children: 'Excluir' | |
| })} | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6"> | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Fonte</p> | |
| <p class="font-medium">${selectedNews.source}</p> | |
| ` | |
| })} | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Status</p> | |
| <span class="px-2 py-1 text-xs rounded-full bg-green-500/20 text-green-400"> | |
| ${selectedNews.status} | |
| </span> | |
| ` | |
| })} | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Data de Publicação</p> | |
| <p class="font-medium">${new Date(selectedNews.publishedDate).toLocaleString()}</p> | |
| ` | |
| })} | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Autor</p> | |
| <p class="font-medium">${selectedNews.author}</p> | |
| ` | |
| })} | |
| </div> | |
| <div> | |
| <h4 class="font-medium mb-3">Conteúdo</h4> | |
| <div class="bg-gray-800 border border-gray-700 rounded-lg p-4"> | |
| <p class="text-gray-300">${selectedNews.content}</p> | |
| </div> | |
| </div> | |
| ` | |
| })} | |
| ` : ` | |
| ${Card({ | |
| className: 'flex flex-col items-center justify-center h-64', | |
| children: ` | |
| <i class="fas fa-newspaper text-4xl text-gray-500 mb-4"></i> | |
| <p class="text-gray-400">Selecione uma notícia para visualizar os detalhes</p> | |
| ` | |
| })} | |
| `} | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }; | |
| const AlertsSection = ({ alerts }) => { | |
| const [selectedAlert, setSelectedAlert] = useState(null); | |
| return ` | |
| <div class="space-y-6"> | |
| <div class="flex justify-between items-center"> | |
| <h3 class="text-lg font-semibold">Monitoramento de Alertas</h3> | |
| ${Button({ | |
| onClick: "alert('Funcionalidade de configuração de alertas será implementada')", | |
| variant: 'primary', | |
| icon: 'fa-cog', | |
| children: 'Configurar' | |
| })} | |
| </div> | |
| <div class="grid grid-cols-1 lg:grid-cols-3 gap-6"> | |
| <!-- Alerts List --> | |
| <div class="lg:col-span-1"> | |
| ${Card({ | |
| title: 'Lista de Alertas', | |
| children: ` | |
| <div class="space-y-3"> | |
| ${alerts.map(alert => ` | |
| <div | |
| onclick="(${setSelectedAlert})(${JSON.stringify(alert)})" | |
| class="p-3 rounded-lg cursor-pointer transition-all ${ | |
| selectedAlert?.id === alert.id ? 'bg-indigo-500/10 border border-indigo-500/30' : 'hover:bg-gray-700' | |
| }" | |
| > | |
| <div class="flex justify-between items-center"> | |
| <h4 class="font-medium truncate">${alert.title}</h4> | |
| <span class="px-2 py-1 text-xs rounded-full bg-red-500/20 text-red-400"> | |
| ${alert.severity} | |
| </span> | |
| </div> | |
| <p class="text-sm text-gray-400 truncate">${alert.source}</p> | |
| <div class="flex justify-between mt-2 text-xs"> | |
| <span class="text-gray-400">Detectado em: ${new Date(alert.detectedAt).toLocaleTimeString()}</span> | |
| </div> | |
| </div> | |
| `).join('')} | |
| </div> | |
| ` | |
| })} | |
| </div> | |
| <!-- Alert Details --> | |
| <div class="lg:col-span-2"> | |
| ${selectedAlert ? ` | |
| ${Card({ | |
| children: ` | |
| <div class="flex flex-col md:flex-row md:justify-between md:items-center mb-4 space-y-3 md:space-y-0"> | |
| <h3 class="text-xl font-semibold">${selectedAlert.title}</h3> | |
| <div class="flex space-x-2"> | |
| ${Button({ | |
| variant: 'primary', | |
| size: 'sm', | |
| icon: 'fa-check', | |
| children: 'Resolver' | |
| })} | |
| ${Button({ | |
| variant: 'danger', | |
| size: 'sm', | |
| icon: 'fa-trash', | |
| children: 'Ignorar' | |
| })} | |
| </div> | |
| </div> | |
| <div class="grid grid-cols-1 sm:grid-cols-2 gap-4 mb-6"> | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Fonte</p> | |
| <p class="font-medium">${selectedAlert.source}</p> | |
| ` | |
| })} | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Gravidade</p> | |
| <span class="px-2 py-1 text-xs rounded-full bg-red-500/20 text-red-400"> | |
| ${selectedAlert.severity} | |
| </span> | |
| ` | |
| })} | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Detectado em</p> | |
| <p class="font-medium">${new Date(selectedAlert.detectedAt).toLocaleString()}</p> | |
| ` | |
| })} | |
| ${Card({ | |
| children: ` | |
| <p class="text-gray-400 text-sm">Status</p> | |
| <span class="px-2 py-1 text-xs rounded-full bg-yellow-500/20 text-yellow-400"> | |
| ${selectedAlert.status} | |
| </span> | |
| ` | |
| })} | |
| </div> | |
| <div> | |
| <h4 class="font-medium mb-3">Detalhes</h4> | |
| <div class="bg-gray-800 border border-gray-700 rounded-lg p-4"> | |
| <p class="text-gray-300">${selectedAlert.details}</p> | |
| </div> | |
| </div> | |
| ` | |
| })} | |
| ` : ` | |
| ${Card({ | |
| className: 'flex flex-col items-center justify-center h-64', | |
| children: ` | |
| <i class="fas fa-bell text-4xl text-gray-500 mb-4"></i> | |
| <p class="text-gray-400">Selecione um alerta para visualizar os detalhes</p> | |
| ` | |
| })} | |
| `} | |
| </div> | |
| </div> | |
| </div> | |
| `; | |
| }; | |
| const ReportsSection = () => { | |
| const [reportData, setReportData] = useState({ | |
| title: '', | |
| description: '', | |
| category: 'spam', | |
| urgency: 'medium' | |
| }); | |
| const [isSubmitting, setIsSubmitting] = useState(false); | |
| const handleSubmit = async (e) => { | |
| e.preventDefault(); | |
| setIsSubmitting(true); | |
| try { | |
| await ApiService.postReport(reportData); | |
| alert('Denúncia enviada com sucesso!'); | |
| setReportData({ | |
| title: '', | |
| description: '', | |
| category: 'spam', | |
| urgency: 'medium' | |
| }); | |
| } catch (error) { | |
| alert('Erro ao enviar denúncia. Por favor, tente novamente.'); | |
| } finally { | |
| setIsSubmitting(false); | |
| } | |
| }; | |
| return ` | |
| <div class="space-y-6"> | |
| <div class="flex justify-between items-center"> | |
| <h3 class="text-lg font-semibold">Registro de Denúncias</h3> | |
| </div> | |
| ${Card({ | |
| children: ` | |
| <form onsubmit="event.preventDefault(); (${handleSubmit})(event);"> | |
| <div class="space-y-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-1">Título</label> | |
| <input | |
| type="text" | |
| value="${reportData.title}" | |
| onchange="(${setReportData})({...${JSON.stringify(reportData)}, title: event.target.value})" | |
| placeholder="Resumo da denúncia" | |
| class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| required | |
| /> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-1">Descrição</label> | |
| <textarea | |
| value="${reportData.description}" | |
| onchange="(${setReportData})({...${JSON.stringify(reportData)}, description: event.target.value})" | |
| placeholder="Descreva detalhadamente o problema encontrado" | |
| class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500 h-32" | |
| required | |
| ></textarea> | |
| </div> | |
| <div class="grid grid-cols-1 md:grid-cols-2 gap-4"> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-1">Categoria</label> | |
| <select | |
| value="${reportData.category}" | |
| onchange="(${setReportData})({...${JSON.stringify(reportData)}, category: event.target.value})" | |
| class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| > | |
| <option value="spam">Spam</option> | |
| <option value="fake_news">Fake News</option> | |
| <option value="inappropriate">Conteúdo Inapropriado</option> | |
| <option value="other">Outro</option> | |
| </select> | |
| </div> | |
| <div> | |
| <label class="block text-sm font-medium text-gray-400 mb-1">Urgência</label> | |
| <select | |
| value="${reportData.urgency}" | |
| onchange="(${setReportData})({...${JSON.stringify(reportData)}, urgency: event.target.value})" | |
| class="w-full bg-gray-700 border border-gray-600 rounded-lg px-4 py-2 focus:outline-none focus:ring-2 focus:ring-indigo-500" | |
| > | |
| <option value="low">Baixa</option> | |
| <option value="medium">Média</option> | |
| <option value="high">Alta</option> | |
| </select> | |
| </div> | |
| </div> | |
| <div class="flex justify-end pt-4"> | |
| ${Button({ | |
| type: 'submit', | |
| variant: 'primary', | |
| disabled: isSubmitting, | |
| children: isSubmitting ? ` | |
| <i class="fas fa-circle-notch fa-spin mr-2 loading-spinner"></i> | |
| Enviando... | |
| ` : 'Enviar Denúncia' | |
| })} | |
| </div> | |
| </div> | |
| </form> | |
| ` | |
| })} | |
| </div> | |
| `; | |
| }; | |
| // Funções auxiliares | |
| function useState(initialValue) { | |
| let state = initialValue; | |
| const listeners = []; | |
| function setState(newValue) { | |
| state = typeof newValue === 'function' ? newValue(state) : newValue; | |
| listeners.forEach(listener => listener(state)); | |
| } | |
| function useEffect(callback, deps) { | |
| callback(); | |
| } | |
| function render() { | |
| document.getElementById('root').innerHTML = App(); | |
| } | |
| listeners.push(render); | |
| return [state, setState, useEffect]; | |
| } | |
| // Inicialização | |
| const [darkMode, setDarkMode, useEffect] = useState(true); | |
| document.getElementById('root').innerHTML = App(); | |
| </script> | |
| <p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=dandydow/dashmaster" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body> | |
| </html> |