document.addEventListener('DOMContentLoaded', async () => { // ── Elementos ────────────────────────────────────────────────── const loginContainer = document.getElementById('login-container'); const settingsContainer = document.getElementById('settings-container'); const loginForm = document.getElementById('login-form'); const loginPasswordInput = document.getElementById('login-password'); const btnLoginCancel = document.getElementById('btn-login-cancel'); const configForm = document.getElementById('config-form'); const tokenInput = document.getElementById('token'); const serverUrlInput = document.getElementById('serverUrl'); const monitorPathInput = document.getElementById('monitorPath'); const adminPasswordInput = document.getElementById('adminPassword'); const deviceCard = document.getElementById('device-card'); const deviceClinic = document.getElementById('device-clinic'); const devicePc = document.getElementById('device-pc'); const btnVerifyToken = document.getElementById('btn-verify-token'); const verifySpinner = document.getElementById('verify-spinner'); const verifyText = document.getElementById('verify-text'); const btnTest = document.getElementById('btn-test'); const testSpinner = document.getElementById('test-spinner'); const testText = document.getElementById('test-text'); const btnCancel = document.getElementById('btn-cancel'); const btnLoginCancelBtn = document.getElementById('btn-login-cancel'); let loadedConfig = null; let verifiedDevice = null; // { clinicName, pcName } após verificação // ── Toast ─────────────────────────────────────────────────────── function showToast(message, type = 'success') { const toast = document.getElementById('toast'); toast.textContent = message; toast.className = `toast show ${type}`; setTimeout(() => { toast.className = 'toast'; }, 3500); } // ── Inicialização ─────────────────────────────────────────────── try { if (window.api?.getVersion) { const version = await window.api.getVersion(); const badge = document.getElementById('app-version-badge'); if (badge) badge.innerText = `Client v${version}`; } loadedConfig = await window.api.getConfig(); serverUrlInput.value = loadedConfig.serverUrl || 'https://rx.scoreodonto.com'; monitorPathInput.value = loadedConfig.monitorPath || 'C:\\ProgramData\\RF\\Dental Sensor\\Images'; tokenInput.value = loadedConfig.token || ''; adminPasswordInput.value = loadedConfig.adminPassword || ''; // Se já tem token salvo, verificar automaticamente e mostrar card if (loadedConfig.token && loadedConfig.token.includes('-')) { await verifyToken(loadedConfig.token, loadedConfig.serverUrl, false); } // Carregar logs no terminal if (window.api?.getLogs) { const logs = await window.api.getLogs(); if (logs?.length) renderLogs(logs); } // Primeira execução: exibe config direto sem pedir senha const isFirstRun = !loadedConfig.adminPassword; if (isFirstRun) { loginContainer.classList.add('hidden'); settingsContainer.classList.remove('hidden'); } else { loginContainer.classList.remove('hidden'); settingsContainer.classList.add('hidden'); } } catch (e) { showToast('Erro ao carregar configurações: ' + e.message, 'error'); } // ── Verificar token com o servidor ───────────────────────────── async function verifyToken(token, serverUrl, showFeedback = true) { if (!token || !token.includes('-')) { if (showFeedback) showToast('Token inválido — deve ser o UUID gerado no painel.', 'error'); return false; } const url = (serverUrl || serverUrlInput.value).trim().replace(/\/$/, ''); if (!url) { if (showFeedback) showToast('Informe a URL do servidor antes de verificar.', 'error'); return false; } try { const res = await fetch(`${url}/api/auth/device-info?token=${encodeURIComponent(token)}`); const data = await res.json(); if (data.success) { verifiedDevice = data; deviceClinic.textContent = `🏥 ${data.clinicName}`; devicePc.textContent = `🖥️ ${data.pcName}`; deviceCard.classList.remove('hidden'); if (showFeedback) showToast(`Dispositivo identificado: ${data.clinicName} — ${data.pcName}`, 'success'); return true; } else { verifiedDevice = null; deviceCard.classList.add('hidden'); if (showFeedback) showToast('Token inválido: ' + data.error, 'error'); return false; } } catch (e) { verifiedDevice = null; deviceCard.classList.add('hidden'); if (showFeedback) showToast('Erro ao verificar token: ' + e.message, 'error'); return false; } } // Botão verificar token btnVerifyToken.addEventListener('click', async () => { btnVerifyToken.disabled = true; verifySpinner.style.display = 'inline-block'; verifyText.textContent = ' Verificando...'; await verifyToken(tokenInput.value.trim(), serverUrlInput.value.trim(), true); btnVerifyToken.disabled = false; verifySpinner.style.display = 'none'; verifyText.textContent = 'Verificar'; }); // Re-verificar quando URL muda (e já tem token) serverUrlInput.addEventListener('change', () => { if (tokenInput.value.trim().includes('-')) { verifyToken(tokenInput.value.trim(), serverUrlInput.value.trim(), false); } }); // ── Login ─────────────────────────────────────────────────────── loginForm.addEventListener('submit', (e) => { e.preventDefault(); const correctPassword = loadedConfig?.adminPassword || 'admin'; if (loginPasswordInput.value === correctPassword) { showToast('Autenticado!', 'success'); loginContainer.classList.add('hidden'); settingsContainer.classList.remove('hidden'); } else { showToast('Senha incorreta.', 'error'); loginPasswordInput.value = ''; loginPasswordInput.focus(); } }); // ── Salvar configurações ──────────────────────────────────────── configForm.addEventListener('submit', async (e) => { e.preventDefault(); const token = tokenInput.value.trim(); const serverUrl = serverUrlInput.value.trim(); const monitorPath = monitorPathInput.value.trim(); const adminPassword = adminPasswordInput.value.trim(); if (!token) { showToast('Cole o Token do Dispositivo antes de salvar.', 'error'); return; } if (!serverUrl) { showToast('Informe a URL do servidor.', 'error'); return; } // Verificar token antes de salvar const saveBtn = document.getElementById('btn-save'); const saveSpinner = document.getElementById('save-spinner'); saveBtn.disabled = true; saveSpinner.style.display = 'inline-block'; const valid = await verifyToken(token, serverUrl, true); saveBtn.disabled = false; saveSpinner.style.display = 'none'; if (!valid) return; // clientName vem do pc_name do servidor (verifiedDevice) const clientName = verifiedDevice?.pcName || loadedConfig?.clientName || 'PC-CLINICA'; const newConfig = { ...loadedConfig, serverUrl, monitorPath: monitorPath || 'C:\\ProgramData\\RF\\Dental Sensor\\Images', clientName, token, apiKey: token, // compatibilidade retroativa serverEmail: '', // não mais necessário serverPassword: '', // não mais necessário adminUsername: 'admin', adminPassword: adminPassword || 'admin', clientType: 'windows', }; try { const success = await window.api.saveConfig(newConfig); if (success) { showToast('Configurações salvas! Reconectando...'); setTimeout(() => window.api.closeWindow(), 1200); } else { showToast('Falha ao gravar as configurações.', 'error'); } } catch (err) { showToast('Erro: ' + err.message, 'error'); } }); // ── Testar conexão ────────────────────────────────────────────── btnTest.addEventListener('click', async () => { const url = serverUrlInput.value.trim(); if (!url) { showToast('Informe a URL do servidor.', 'error'); return; } btnTest.disabled = true; testSpinner.style.display = 'inline-block'; testText.textContent = ' Testando...'; try { const result = await window.api.testServer(url); if (result.success) showToast('Conexão estabelecida com sucesso!', 'success'); else showToast('Falha: ' + result.error, 'error'); } catch (err) { showToast('Erro: ' + err.message, 'error'); } finally { btnTest.disabled = false; testSpinner.style.display = 'none'; testText.textContent = '⚡ Testar Conexão'; } }); // ── Toggle senha do painel ────────────────────────────────────── document.getElementById('toggle-admin-password')?.addEventListener('click', () => { const type = adminPasswordInput.type === 'password' ? 'text' : 'password'; adminPasswordInput.type = type; document.getElementById('toggle-admin-password').textContent = type === 'password' ? '👁️' : '🙈'; }); // ── Minimizar ─────────────────────────────────────────────────── btnCancel?.addEventListener('click', () => window.api.closeWindow()); btnLoginCancelBtn?.addEventListener('click', () => window.api.closeWindow()); // ── Terminal de logs ──────────────────────────────────────────── function renderLogs(logs) { const terminal = document.getElementById('terminal-logs'); if (!terminal) return; terminal.innerHTML = ''; logs.forEach(log => { const div = document.createElement('div'); div.className = 'log-line'; div.textContent = log; terminal.appendChild(div); }); terminal.scrollTop = terminal.scrollHeight; } if (window.api?.onLog) { window.api.onLog((log) => { const terminal = document.getElementById('terminal-logs'); if (!terminal) return; const wait = terminal.querySelector('.log-line'); if (wait?.textContent.includes('Aguardando logs')) terminal.innerHTML = ''; const div = document.createElement('div'); div.className = 'log-line'; div.textContent = log; terminal.appendChild(div); terminal.scrollTop = terminal.scrollHeight; while (terminal.children.length > 100) terminal.removeChild(terminal.firstChild); }); } document.getElementById('btn-copy-logs')?.addEventListener('click', () => { const terminal = document.getElementById('terminal-logs'); const text = Array.from(terminal.children).map(el => el.textContent).join('\n'); navigator.clipboard.writeText(text).then(() => { const btn = document.getElementById('btn-copy-logs'); const orig = btn.innerHTML; btn.innerHTML = '✅ Copiado'; setTimeout(() => { btn.innerHTML = orig; }, 2000); }); }); });