import React, { useState, useCallback, useEffect } from 'react'; import { analyzeCertificate } from './services/aiService'; import { exportAsPdf } from './services/pdfService'; import type { ReportData } from './types'; import type { AIProvider } from './types/providers'; import { Header } from './components/Header'; import { FileUpload } from './components/FileUpload'; import { Loader } from './components/Loader'; import { ReportDisplay } from './components/ReportDisplay'; import { PrintableReport } from './components/PrintableReport'; import { DownloadIcon, EyeIcon, CogIcon } from './components/Icons'; import { ApiKeySetup } from './components/ApiKeySetup'; import { AdminPanel } from './components/AdminPanel'; const App: React.FC = () => { const [viewMode, setViewMode] = useState<'analysis' | 'admin'>('analysis'); const [file, setFile] = useState(null); const [apiKey, setApiKey] = useState(''); const [endpoint, setEndpoint] = useState(''); const [provider, setProvider] = useState('gemini'); const [model, setModel] = useState('gemini-2.5-flash'); const [adminApiKey, setAdminApiKey] = useState(''); const [adminModel, setAdminModel] = useState('claude-3.5-sonnet-20241022'); const [hasKey, setHasKey] = useState(false); const [reportsData, setReportsData] = useState(null); const [isLoading, setIsLoading] = useState(false); const [error, setError] = useState(null); const [showSetup, setShowSetup] = useState(false); const [isConfigLoaded, setIsConfigLoaded] = useState(false); useEffect(() => { // 1. Tentar ler do localStorage primeiro const savedApiKey = localStorage.getItem('api-key'); const savedProvider = localStorage.getItem('ai-provider') as AIProvider; const savedModel = localStorage.getItem('model-' + savedProvider); const savedEndpoint = localStorage.getItem('ollama-endpoint'); let hasLocal = false; if (savedApiKey || savedEndpoint) { setApiKey(savedApiKey || ''); if (savedProvider) setProvider(savedProvider); if (savedModel) setModel(savedModel); if (savedEndpoint) setEndpoint(savedEndpoint); setHasKey(true); setShowSetup(false); hasLocal = true; } else { setShowSetup(true); } // 2. Sincronizar com as configurações globais persistidas no servidor (mesmo em outros navegadores) fetch('/api/config') .then(res => res.json()) .then(config => { if (config.adminApiKey) setAdminApiKey(config.adminApiKey); if (config.adminModel) setAdminModel(config.adminModel); if (config.provider) { setProvider(config.provider); if (config.apiKey) setApiKey(config.apiKey); if (config.model) setModel(config.model); if (config.endpoint) setEndpoint(config.endpoint); setHasKey(true); setShowSetup(false); // Sincroniza o localStorage localStorage.setItem('ai-provider', config.provider); if (config.apiKey) { localStorage.setItem('api-key', config.apiKey); localStorage.setItem('last-api-key', config.apiKey); } if (config.model) { localStorage.setItem('model-' + config.provider, config.model); } if (config.endpoint) { localStorage.setItem('ollama-endpoint', config.endpoint); } } else if (!hasLocal) { setShowSetup(true); } setIsConfigLoaded(true); }) .catch(err => { console.warn('Erro ao carregar configurações do servidor:', err); if (!hasLocal) { setShowSetup(true); } setIsConfigLoaded(true); }); }, []); const handleFileChange = useCallback((selectedFile: File | null) => { setFile(selectedFile); if (!selectedFile) { setReportsData(null); setError(null); } }, []); const handleKeySave = useCallback((key: string, newProvider: AIProvider, newModel: string, newEndpoint?: string) => { setApiKey(key); setProvider(newProvider); setModel(newModel); if (newEndpoint) { setEndpoint(newEndpoint); localStorage.setItem('ollama-endpoint', newEndpoint); } if (key) { localStorage.setItem('api-key', key); localStorage.setItem('last-api-key', key); } localStorage.setItem('ai-provider', newProvider); localStorage.setItem('model-' + newProvider, newModel); setHasKey(true); setShowSetup(false); // Salvar no servidor para persistência multi-navegador fetch('/api/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ provider: newProvider, apiKey: key, model: newModel, endpoint: newEndpoint || '' }) }).catch(err => console.error('Erro ao persistir configurações no servidor:', err)); }, []); const handleAnalyzeClick = async () => { if (provider !== 'ollama' && !apiKey) { setError("A chave de API não foi encontrada. Por favor, configure-a novamente."); setHasKey(false); return; } if (provider === 'ollama' && !endpoint) { setError("O endereço do Ollama não foi configurado. Por favor, configure-o."); setHasKey(false); return; } if (!file) { setError("Por favor, selecione um arquivo primeiro."); return; } setIsLoading(true); setError(null); setReportsData(null); try { const data = await analyzeCertificate({ provider, apiKey, model, file, endpoint: provider === 'ollama' ? endpoint : undefined }); setReportsData(data); } catch (err) { if (err instanceof Error) { if (err.message.includes('API key') || err.message.includes('not valid') || err.message.includes('invalid')) { setError('Erro: A chave de API fornecida não é válida. Por favor, insira uma nova chave.'); handleClearKey(); } else { setError(`Erro na análise: ${err.message}`); } } else { setError("Ocorreu um erro desconhecido durante a análise."); } } finally { setIsLoading(false); } }; const handleReset = useCallback(() => { setFile(null); setReportsData(null); setIsLoading(false); setError(null); }, []); const handleClearKey = useCallback(() => { setApiKey(''); localStorage.removeItem('api-key'); setHasKey(false); setShowSetup(true); handleReset(); // Limpar no servidor fetch('/api/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({}) }).catch(err => console.error('Erro ao limpar configurações no servidor:', err)); }, [handleReset]); const handleExport = async (action: 'preview' | 'download') => { if (action === 'download') { setIsLoading(true); try { await exportAsPdf('printable-report-container', 'SteelCheck_Report', 'download'); } finally { setIsLoading(false); } } else { window.print(); } }; return (
{isLoading && }
setViewMode(viewMode === 'admin' ? 'analysis' : 'admin')} isAdminView={viewMode === 'admin'} />
{!isConfigLoaded ? (
) : viewMode === 'admin' ? ( { setAdminApiKey(key); setAdminModel(mdl); fetch('/api/config', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ provider, apiKey, model, endpoint, adminApiKey: key, adminModel: mdl }) }); }} /> ) : (!hasKey || showSetup) ? ( setShowSetup(false) : undefined} /> ) : !reportsData ? (

Análise Técnica Inteligente

Carregue seu certificado de qualidade e deixe nossa IA verificar a conformidade.

{provider.toUpperCase()} • {model}
{file && ( )} {error && (

{error}

)}

IA Assistiva • Verificação profissional recomendada

) : (
{reportsData.map((report, idx) => ( ))}
)}
{reportsData && (
)}
); }; export default App;