import React, { useState, useEffect } from 'react'; import { Puzzle, Radio, CheckCircle2, XCircle, Settings2, Zap, ChevronDown, ChevronUp, Eye, EyeOff, Save, X } from 'lucide-react'; import { PageHeader } from '../components/PageHeader.tsx'; import { useToast } from '../contexts/ToastContext.tsx'; import { getActivePlugins, togglePlugin, getPluginConfig, savePluginConfig } from './plugins/pluginRegistry.ts'; // ─── Plugin definitions ─────────────────────────────────────────────────────── interface ConfigField { key: string; label: string; type: 'text' | 'password'; placeholder: string; defaultValue: string; } interface PluginDef { id: string; name: string; tagline: string; description: string; version: string; author: string; category: string; color: string; sidebarItems: Array<{ id: string; label: string }>; features: string[]; configFields: ConfigField[]; } const PLUGINS: PluginDef[] = [ { id: 'rx-scoreodonto', name: 'RX ScoreOdonto', tagline: 'Radiografias & GTOs', description: 'Integração completa com o sistema de radiografias. Gerencie imagens, GTOs e sincronize com o client Windows instalado nas clínicas.', version: '', author: 'ScoreOdonto', category: 'DIAGNÓSTICO', color: '#7c3aed', sidebarItems: [{ id: 'rx-radiografias', label: 'RX / RADIOGRAFIAS' }], features: [ 'Visualizar radiografias por paciente', 'Upload de imagens (arquivo ou Base64)', 'Gerenciar GTOs e marcar como enviadas', 'Sincronização com client Windows', 'Criar fichas de pacientes remotamente', ], configFields: [ { key: 'apiToken', label: 'TOKEN DE API', type: 'password', placeholder: 'api_...', defaultValue: 'api_d5c8b4f4297ca579c7beceac6b4b1a274eb0df599331d06b', }, { key: 'apiUrl', label: 'URL DO SERVIDOR RX', type: 'text', placeholder: 'https://dev.rx.scoreodonto.com', defaultValue: 'https://dev.rx.scoreodonto.com', }, ], }, ]; // ─── Config Modal ───────────────────────────────────────────────────────────── const ConfigModal: React.FC<{ plugin: PluginDef; onClose: () => void }> = ({ plugin, onClose }) => { const toast = useToast(); const savedConfig = getPluginConfig(plugin.id); const [values, setValues] = useState>( Object.fromEntries(plugin.configFields.map(f => [f.key, savedConfig[f.key] ?? f.defaultValue])) ); const [showPassword, setShowPassword] = useState>({}); const handleSave = () => { savePluginConfig(plugin.id, values); toast.success('CONFIGURAÇÕES SALVAS!'); onClose(); }; const inputCls = "w-full border border-gray-200 rounded-xl px-4 py-2.5 text-sm font-mono focus:outline-none focus:border-purple-400 focus:ring-2 focus:ring-purple-100 bg-white pr-10"; return (

CONFIGURAR {plugin.name}

PARÂMETROS DO PLUGIN

{plugin.configFields.map(field => (
setValues(v => ({ ...v, [field.key]: e.target.value }))} className={inputCls} placeholder={field.placeholder} /> {field.type === 'password' && ( )}
))}
); }; // ─── Plugin Card ────────────────────────────────────────────────────────────── const PluginCard: React.FC<{ plugin: PluginDef; isActive: boolean; onToggle: () => void; onConfigure: () => void }> = ({ plugin, isActive, onToggle, onConfigure }) => { const [expanded, setExpanded] = useState(false); return (
{/* Header strip */}

{plugin.name}

{plugin.category}

{plugin.version ? `v${plugin.version} · ` : ''}por {plugin.author}

{/* Active badge */} {isActive && ( ATIVO )}

{plugin.description}

{/* Features (collapsible) */} {expanded && (
    {plugin.features.map((f, i) => (
  • {f}
  • ))}
)} {/* Sidebar items info */} {isActive && plugin.sidebarItems.length > 0 && (

ADICIONADO AO MENU

{plugin.sidebarItems.map(item => (
{item.label}
))}
)} {/* Actions */}
{plugin.configFields.length > 0 && ( )}
); }; // ─── Main View ──────────────────────────────────────────────────────────────── export const PluginsView: React.FC = () => { const toast = useToast(); const [activePlugins, setActivePlugins] = useState(getActivePlugins); const [configuringPlugin, setConfiguringPlugin] = useState(null); const [pluginVersions, setPluginVersions] = useState>({}); useEffect(() => { // Busca versão real do servidor RX const rxUrl = getPluginConfig('rx-scoreodonto').apiUrl || 'https://rx.scoreodonto.com'; fetch(`${rxUrl.replace(/\/$/, '')}/api/version`) .then(r => r.ok ? r.json() : null) .then(d => { if (d?.client) setPluginVersions(v => ({ ...v, 'rx-scoreodonto': d.client })); }) .catch(() => {}); }, []); const handleToggle = (plugin: PluginDef) => { const nowActive = togglePlugin(plugin.id); setActivePlugins(getActivePlugins()); if (nowActive) { toast.success(`${plugin.name.toUpperCase()} ATIVADO! RECARREGANDO...`); } else { toast.info(`${plugin.name.toUpperCase()} DESATIVADO. RECARREGANDO...`); } setTimeout(() => window.location.reload(), 1200); }; return (
{/* Summary bar */}

{PLUGINS.length} PLUGIN{PLUGINS.length !== 1 ? 'S' : ''} DISPONÍVEL{PLUGINS.length !== 1 ? 'S' : ''}

{activePlugins.length} ativo{activePlugins.length !== 1 ? 's' : ''}

{PLUGINS.map(p => (
))}
{/* Plugin grid */}
{PLUGINS.map(plugin => ( handleToggle(plugin)} onConfigure={() => setConfiguringPlugin(plugin)} /> ))}
{/* Config modal */} {configuringPlugin && ( setConfiguringPlugin(null)} /> )}
); };