const { app, BrowserWindow, ipcMain, screen, net } = require('electron'); const path = require('path'); const fs = require('fs'); const { autoUpdater } = require('electron-updater'); const pjson = require(path.join(__dirname,'','package.json')); let floatingWin; let mainWin; let loginWin; let operatorWin; const dataPath = path.join(__dirname, 'data.json'); // Caminho para o JSON (backup local) const apiUrl = 'https://autoatend.linco.work/api/v1/'; const updUrl = 'https://autoatend.linco.work/public/aa_upd/'; autoUpdater.setFeedURL(updUrl); if(!pjson.isBuildNow){ require('electron-reload')(__dirname,{ electron: require(`${__dirname}/node_modules/electron`) }) } // Função modificada para buscar dados da API async function readData() { try { // Verifica se existe token de autenticação const token = await getAuthToken(); const colabId = await floatingWin.webContents.executeJavaScript("localStorage.getItem('idOperator')") if (!token || !colabId) { console.log("Usuário não autenticado, retornando lista vazia"); return []; } // Tenta buscar dados da API return await fetchDataFromAPI(token,colabId); //não usada aqui vai ser disparada via jquery dentro do modulo } catch (error) { console.error("Erro ao buscar dados da API:", error); // Fallback: tenta ler do arquivo local em caso de falha na API try { const rawData = fs.readFileSync(dataPath, 'utf-8'); return JSON.parse(rawData); } catch (localError) { console.error("Erro ao ler data.json local:", localError); return []; // Retorna array vazio em caso de erro } } } // Função para buscar dados da API async function fetchDataFromAPI(token,colabId) { //refactorado } // Função para verificar se o token existe no localStorage async function getAuthToken() { return new Promise((resolve) => { if (loginWin && !loginWin.isDestroyed()) { loginWin.webContents.executeJavaScript('localStorage.getItem("authToken");') .then(token => resolve(token)) .catch(() => resolve(null)); } else if (mainWin && !mainWin.isDestroyed()) { mainWin.webContents.executeJavaScript('localStorage.getItem("authToken");') .then(token => resolve(token)) .catch(() => resolve(null)); } else if (floatingWin && !floatingWin.isDestroyed()) { floatingWin.webContents.executeJavaScript('localStorage.getItem("authToken");') .then(token => resolve(token)) .catch(() => resolve(null)); } else if (operatorWin && !operatorWin.isDestroyed()) { operatorWin.webContents.executeJavaScript('localStorage.getItem("authToken");') .then(token => resolve(token)) .catch(() => resolve(null)); } else { resolve(null); } }); } // Função para criar a janela de login function createLoginWindow() { loginWin = new BrowserWindow({ width: 500, height: 500, frame: true, autoHideMenuBar: true, webPreferences: { preload: path.join(__dirname, 'login_preload.js'), contextIsolation: true, nodeIntegration: true, }, }); loginWin.loadFile('login.html'); // loginWin.webContents.openDevTools(); loginWin.on('closed', () => { loginWin = null; }); } // Função para criar a janela de seleção de operador function createOperatorWindow() { operatorWin = new BrowserWindow({ width: 1200,//500 height: 600, frame: true, autoHideMenuBar: true, webPreferences: { preload: path.join(__dirname, 'operator_preload.js'), contextIsolation: true, nodeIntegration: true, }, }); // operatorWin.webContents.openDevTools(); operatorWin.loadFile('operator.html'); operatorWin.on('closed', () => { operatorWin = null; }); } function createFloatingWindow() { const primaryDisplay = screen.getPrimaryDisplay(); const { width: screenWidth, height: screenHeight } = primaryDisplay.workAreaSize; const winWidth = 50; const winHeight = 70; floatingWin = new BrowserWindow({ width: winWidth, height: winHeight, x: screenWidth - winWidth + 0, y: screenHeight - winHeight - 60, frame: false, transparent: true, alwaysOnTop: true, skipTaskbar: true, resizable: false, hasShadow: false, webPreferences: { preload: path.join(__dirname, 'floating_preload.js'), contextIsolation: true, nodeIntegration: true }, backgroundColor: '#00000000', titleBarStyle: 'hidden', roundedCorners: false }); floatingWin.loadFile('floating.html'); floatingWin.webContents.executeJavaScript('localStorage.setItem("version","'+app.getVersion()+'")'); // Envia a contagem inicial para a janela flutuante const data = readData(); floatingWin.webContents.on('did-finish-load', () => { floatingWin.webContents.send('update-count', data.length); }); floatingWin.on('closed', () => { floatingWin = null; }); // floatingWin.webContents.openDevTools(); } function createMainWindow() { mainWin = new BrowserWindow({ width: 1024, height: 600, show: false, // Inicia oculta frame: true, // Sem bordas, título, etc. autoHideMenuBar: true, // Oculta a barra de menus menuBarVisible: false, // Garante que a barra de menus começa webPreferences: { preload: path.join(__dirname, 'preload.js'), contextIsolation: true, nodeIntegration: true, }, }); mainWin.loadFile('index.html'); if(!pjson.isBuildNow) { mainWin.webContents.openDevTools(); // Descomente para depurar } mainWin.webContents.send('current_version', pjson.version); mainWin.on('close', (event) => { // Em vez de fechar, apenas oculta a janela principal if (!app.isQuitting) { event.preventDefault(); mainWin.hide(); } }); mainWin.on('closed', () => { mainWin = null; }); } function verifyUpdates(){ autoUpdater.on('download-progress',(obj) => { mainWin.webContents.send('update_version',`Estamos baixando uma nova atualização: ${obj.percent.toFixed(2)}%`); }); autoUpdater.on('update-downloaded',(obj) => { mainWin.webContents.send('update_version',`Atualização concluída: ${obj.percent.toFixed(2)}%`); setTimeout(()=>{ autoUpdater.quitAndInstall(); },5000); }); autoUpdater.on('update-available', () => { mainWin.webContents.send('update_version',`Uma nova versão está dispinível.`); }) autoUpdater.on('error',err => { mainWin.webContents.send('error',err); }); } verifyUpdates(); // Inicialização do aplicativo modificada para verificar autenticação app.whenReady().then(async () => { // Verifica se o usuário já está autenticado const token = await getAuthToken(); if (!token) { // Se não estiver autenticado, mostra a tela de login createLoginWindow(); } else { // Se já estiver autenticado, verifica se tem operador selecionado const operator = await getSelectedOperator(); if (!operator || operator === 'null' || operator === null || operator === undefined || operator === '') { // Se não tiver operador selecionado, mostra a tela de seleção createOperatorWindow(); } else { // Se já tiver operador, inicia normalmente createFloatingWindow(); createMainWindow(); } } app.on('activate', () => { // No macOS é comum recriar uma janela no aplicativo quando o // ícone do dock é clicado e não há outras janelas abertas. if (BrowserWindow.getAllWindows().length === 0) { // Poderia recriar a janela principal aqui se necessário, // mas como temos a flutuante, talvez não precise. if (!floatingWin) createFloatingWindow(); if (!mainWin) createMainWindow(); } }); }); // Função para verificar se já existe um operador selecionado async function getSelectedOperator() { return new Promise((resolve) => { if (loginWin && !loginWin.isDestroyed()) { loginWin.webContents.executeJavaScript('localStorage.getItem("selectedOperator");') .then(operator => resolve(operator)) .catch(() => resolve(null)); } else if (operatorWin && !operatorWin.isDestroyed()) { operatorWin.webContents.executeJavaScript('localStorage.getItem("selectedOperator");') .then(operator => resolve(operator)) .catch(() => resolve(null)); } else if (mainWin && !mainWin.isDestroyed()) { mainWin.webContents.executeJavaScript('localStorage.getItem("selectedOperator");') .then(operator => resolve(operator)) .catch(() => resolve(null)); } else if (floatingWin && !floatingWin.isDestroyed()) { floatingWin.webContents.executeJavaScript('localStorage.getItem("selectedOperator");') .then(operator => resolve(operator)) .catch(() => resolve(null)); } else { resolve(null); } }); } async function getSelectedOperatorId() { return new Promise((resolve, reject) => { if (mainWin && !mainWin.isDestroyed()) { mainWin.webContents.executeJavaScript('localStorage.getItem("idOperator");') .then(operatorId => resolve(operatorId)) .catch(() => resolve(null)); } else if (floatingWin && !floatingWin.isDestroyed()) { floatingWin.webContents.executeJavaScript('localStorage.getItem("idOperator");') .then(operatorId => resolve(operatorId)) .catch(() => resolve(null)); } else { reject(new Error('Nenhum componente aberto')); } }); } // Ouvir pedido para mostrar a janela principal ipcMain.on('show-main-window', async () => { if (mainWin) { if (!mainWin.isVisible()) { const data = readData(); mainWin.webContents.send('load-data', data); // Envia dados ao mostrar mainWin.show(); mainWin.focus(); } else { mainWin.focus(); } } else { createMainWindow(); // Cria se não existir mainWin.webContents.on('did-finish-load', () => { const data = readData(); mainWin.webContents.send('load-data', data); mainWin.show(); mainWin.focus(); }); } }); // Ouvir pedido para obter contagem (ex: se o JSON for atualizado) ipcMain.handle('get-count', async () => { const data = readData(); return data.length; }); // Ouvir clique no botão "Próximo" ipcMain.on('next-step', (event, itemId) => { console.log('Botão Próximo clicado para o item ID:', itemId); // Log para depuração const url = 'https://httpbin.org/post'; // URL de exemplo para enviar a solicitação // Simula o envio de uma solicitação POST com o ID do item const request = net.request({ method: 'POST', url: url, headers: { 'Content-Type': 'application/json' } }); request.on('response', (response) => { console.log(`STATUS: ${response.statusCode}`); console.log(`HEADERS: ${JSON.stringify(response.headers)}`); response.on('data', (chunk) => { console.log(`BODY: ${chunk}`); }); response.on('end', () => { console.log('Solicitação concluída.'); // Avisa a janela principal que a solicitação foi feita (opcional) // mainWin.webContents.send('request-done', itemId); }); }); request.on('error', (error) => { console.error(`Erro na solicitação: ${error}`); // Poderia notificar a UI sobre o erro }); // Envia o ID como corpo da requisição (exemplo) request.write(JSON.stringify({ id: itemId })); request.end(); // Não precisamos esperar a resposta para mudar a UI na janela principal // A janela principal já mudou a UI ao enviar o evento 'next-step' }); // Ouvir clique no botão "Salvar" ipcMain.on('save-observation', (event, { itemId, observation }) => { console.log(`Salvando observação para item ${itemId}: ${observation}`); // Aqui você implementaria a lógica para salvar a observação. // Poderia ser: // 1. Atualizar o arquivo data.json (cuidado com concorrência se houver muita escrita) // 2. Salvar em outro arquivo // 3. Enviar para outra API/banco de dados // Exemplo simples (apenas log): console.log("Observação 'salva' (apenas log por enquanto)."); // Opcional: Ler dados novamente e atualizar contagem na janela flutuante const data = readData(); if (floatingWin) { floatingWin.webContents.send('update-count', data.length); } // Opcional: Fechar ou resetar a janela principal após salvar if (mainWin) { mainWin.hide(); // Ou mainWin.webContents.send('reset-view'); } }); // Permite que a janela flutuante seja arrastada // REMOVA ou comente este listener inteiro: /* ipcMain.on('drag-float-window', (event, { offsetX, offsetY }) => { if (floatingWin) { const { x, y } = screen.getCursorScreenPoint(); floatingWin.setPosition(x - offsetX, y - offsetY); } }); */ ipcMain.on('sair', ()=>{ app.exit(); }); // Handler para login ipcMain.on('login-attempt', async (event, credentials) => { try { // Substitua pela URL real da sua API de autenticação const route = apiUrl + 'login'; const request = net.request({ method: 'POST', url: route, headers: { 'Content-Type': 'application/json' } }); let responseData = ''; request.on('response', (response) => { response.on('data', (chunk) => { responseData += chunk.toString(); console.log("Resposta da API:", responseData); // Adiciona este log para ver a resposta crua }); response.on('end', () => { try { const data = JSON.parse(responseData); if (data.status === 'Authorized' && data.api_key) { // Login bem-sucedido loginWin.webContents.executeJavaScript(` localStorage.setItem("authToken", "${data.api_key}"); `).then(() => { // Fecha a janela de login e abre a de seleção de operador event.reply('login-response', { success: true }); loginWin.close(); createOperatorWindow(); }); } else { // Login falhou event.reply('login-response', { success: false, message: data.message || 'Falha na autenticação' }); } } catch (error) { console.log("Resposta da API:", error); event.reply('login-response', { success: false, message: 'Erro ao processar resposta do servidor' }); } }); }); request.on('error', (error) => { event.reply('login-response', { success: false, message: `Erro de conexão: ${error.message}` }); }); // Envia as credenciais request.write(JSON.stringify(credentials)); request.end(); } catch (error) { event.reply('login-response', { success: false, message: `Erro: ${error.message}` }); } }); // Handler para seleção de operador ipcMain.on('select-operator', async (event, operator) => { try { // Salva o operador selecionado operatorWin.webContents.executeJavaScript(` localStorage.setItem("idOperator", "${operator.id}"); localStorage.setItem("selectedOperator", "${operator.name}"); localStorage.setItem("salaOperator", "${operator.sala}"); localStorage.setItem("servicosOperator", "${operator.servicos}"); `).then(() => { // Fecha a janela de operador e inicia o aplicativo operatorWin.close(); createFloatingWindow(); createMainWindow(); }); } catch (error) { event.reply('operator-response', { success: false, message: `Erro: ${error.message}` }); } }); // Handler para buscar lista de operadores ipcMain.handle('get-operators', async () => { try { // Verifica se existe token de autenticação const token = await getAuthToken(); if (!token) { return { success: false, message: 'Não autenticado', operators: [] }; } // Aqui você pode fazer uma chamada à API para obter os operadores // Por enquanto, vamos retornar alguns operadores de exemplo // Substitua pela URL real da sua API de autenticação const route = apiUrl + 'colabs/list'; return new Promise((resolve, reject) => { const request = net.request({ method: 'GET', url: route, headers: { 'Content-Type': 'application/json', 'Authorization': `Bearer ${token}` } }); let responseData = ''; request.on('response', (response) => { response.on('data', (chunk) => { responseData += chunk.toString(); console.log("Resposta da API:", responseData); // Adiciona este log para ver a resposta crua }); response.on('end', () => { try { const data = JSON.parse(responseData); if (data.colabs) { const operators = data.colabs.map(colab => ({ id: colab.id, name: colab.nome.toUpperCase(), sala: colab.sala, servicos: colab.servicos })); resolve({ success: true, operators: operators }); } else { reject({ success: false, message: data.message || 'Erro ao obter a lista de colaboradores', operators: [] }); } } catch (error) { console.log("Erro ao processar resposta da API:", error); reject({ success: false, message: 'Erro ao processar resposta do servidor', operators: [] }); } }); }); request.on('error', (error) => { reject({ success: false, message: `Erro de conexão: ${error.message}`, operators: [] }); }); request.end(); }); } catch (error) { console.error('Erro ao obter operadores:', error); return { success: false, message: `Erro: ${error.message}`, operators: [] }; } }); // Handler para verificar se o token já existe ipcMain.on('token-exists', async () => { // Checa se o operador já foi selecionado const operator = await getSelectedOperator(); // Fecha a janela de login, independentemente do operador já ter sido selecionado if (loginWin && !loginWin.isDestroyed()) { loginWin.close(); loginWin = null; // Garante que a referência seja limpa } if (!operator || operator === 'null' || operator === null || operator === undefined || operator === '') { // Se não tiver operador selecionado, mostra a tela de seleção createOperatorWindow(); } else { // Se já tiver operador, inicia normalmente createFloatingWindow(); createMainWindow(); } });