654 lines
22 KiB
JavaScript
654 lines
22 KiB
JavaScript
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 obter contagem (ex: se o JSON for atualizado)
|
|
ipcMain.handle('get-count', async () => {
|
|
const data = readData();
|
|
return data.length;
|
|
});
|
|
|
|
// Ouvir pedido para mostrar a janela principal
|
|
ipcMain.on('chamar-fila', 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();
|
|
});
|
|
}
|
|
|
|
try{
|
|
//chama-fila-app-colab/{colabId}
|
|
const colabId = await getSelectedOperatorId();
|
|
const token = await getAuthToken('token');
|
|
const url = apiUrl + 'chama-fila-app-colab/'+colabId; // URL de exemplo para enviar a solicitação
|
|
const request = net.request({
|
|
method: 'GET',
|
|
url: url,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer ' + token
|
|
}
|
|
});
|
|
|
|
request.on('response', (response) => {
|
|
if (response.statusCode === 200) {
|
|
mainWin.webContents.send('select-atend-id', response.data.id);
|
|
// window.electronAPI.selectAtendID(response.data.id);
|
|
console.log(response.data);
|
|
}
|
|
});
|
|
|
|
} catch(error){
|
|
console.log(error);
|
|
}
|
|
|
|
});
|
|
|
|
ipcMain.on('select-atend-id',(itemId)=>{
|
|
selectedItemId = itemId;
|
|
console.log(selectedItemId);
|
|
});
|
|
|
|
// Ouvir clique no botão "Iniciar atendimento"
|
|
ipcMain.on('iniciar-atendimento', (event, itemId) => {
|
|
|
|
//TODO inicia o atendimento o id do atendimento deve ser requisitado do backend
|
|
|
|
const url = apiUrl + 'iniciar-atendimento/'+itemId; // 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: 'GET',
|
|
url: url,
|
|
headers: {
|
|
'Content-Type': 'application/json',
|
|
'Authorization': 'Bearer ' + localStorage.getItem('token')
|
|
}
|
|
});
|
|
|
|
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 }) => {
|
|
|
|
//TODO salva a observação e finaliza o atendimento
|
|
|
|
console.log(`Salvando observação para item ${itemId}: ${observation}`);
|
|
|
|
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();
|
|
}
|
|
}); |