first commit
This commit is contained in:
commit
87eef0b2a8
|
|
@ -0,0 +1,31 @@
|
|||
npm-debug.log
|
||||
.env
|
||||
|
||||
# Laravel 4 specific
|
||||
bootstrap/compiled.php
|
||||
app/storage/
|
||||
|
||||
# Laravel 5 & Lumen specific
|
||||
public/storage
|
||||
public/hot
|
||||
public/.efi-cert
|
||||
storage/*.key
|
||||
.env.*.php
|
||||
.env.php
|
||||
Homestead.yaml
|
||||
Homestead.json
|
||||
|
||||
# Rocketeer PHP task runner and deployment package. https://github.com/rocketeers/rocketeer
|
||||
.rocketeer/
|
||||
.idea/**
|
||||
storage/framework/views/**
|
||||
storage/framework/sessions/**
|
||||
storage/framework/cache/**
|
||||
storage/logs/**
|
||||
public/public/uploads/**
|
||||
public/mobile/platforms/**
|
||||
public/mobile/node_modules/**
|
||||
.DS_Store
|
||||
|
||||
node_modules/**
|
||||
dist/**
|
||||
|
|
@ -0,0 +1,74 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src https://autoatend.linco.work">
|
||||
<title>Floating Button</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="js/jquery/jquery.js"></script>
|
||||
</head>
|
||||
<body>
|
||||
<button id="float-button">
|
||||
<span id="icon">🧑🦰</span> <!-- Ícone de exemplo -->
|
||||
<span id="count">0</span>
|
||||
</button>
|
||||
<script src="floating.js"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
let apiUrl = 'https://autoatend.linco.work/api/v1/';
|
||||
let token = localStorage.getItem('authToken');
|
||||
let colabId = localStorage.getItem('idOperator');
|
||||
|
||||
function obtemProximos() {
|
||||
if (!token && !colabId) {
|
||||
console.warn("Token or colabId not found in localStorage. API requests will not be made.");
|
||||
return; // Stop the function if token or colabId is missing
|
||||
}
|
||||
|
||||
$.ajax(apiUrl + 'get-proximos/'+colabId, {
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': 'Bearer '+token },
|
||||
// data: formData,
|
||||
processData: false,
|
||||
contentType: false,
|
||||
dataType: 'JSON',
|
||||
success: function(response) {
|
||||
console.log('Resposta:', response);
|
||||
// Ensure the response is valid JSON before parsing
|
||||
try {
|
||||
let proximos = response;
|
||||
localStorage.setItem('proximos', JSON.stringify(proximos));
|
||||
let count = proximos.length;
|
||||
$('#float-button span#count').text(count); // Atualiza o texto do botão. Use .text() instead of .html()
|
||||
$('#float-button').removeClass(['without-items', 'has-items']).addClass( count > 0 ? 'has-items' : 'without-items');
|
||||
|
||||
} catch (e) {
|
||||
console.error("Error parsing JSON response:", e);
|
||||
console.error("Response text:", response); // Log the raw response for debugging
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Erro na requisição:', status, error);
|
||||
console.error('Response Text:', xhr.responseText); // Log the response text for debugging
|
||||
// Optionally, handle different error codes:
|
||||
if (xhr.status === 401) {
|
||||
console.warn("Unauthorized. Token might be invalid.");
|
||||
// You could redirect the user to a login page here.
|
||||
} else if (xhr.status === 404) {
|
||||
console.warn("Resource not found. Check the API endpoint.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
|
||||
// Call obtemProximos initially
|
||||
obtemProximos();
|
||||
|
||||
// Set interval to call obtemProximos every 10 seconds
|
||||
setInterval(obtemProximos, 30000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,21 @@
|
|||
const floatButton = document.getElementById('float-button');
|
||||
const countSpan = document.getElementById('count');
|
||||
|
||||
// Atualiza a contagem e a cor do botão quando recebe do main process
|
||||
window.electronAPI.onUpdateCount((value) => {
|
||||
countSpan.textContent = value;
|
||||
// Verifica a contagem para mudar a cor
|
||||
if (value > 0) {
|
||||
floatButton.classList.add('has-items'); // Adiciona a classe para cor avermelhada
|
||||
} else {
|
||||
floatButton.classList.remove('has-items'); // Remove a classe, volta para o azul padrão
|
||||
}
|
||||
});
|
||||
|
||||
// Mostra a janela principal ao clicar
|
||||
floatButton.addEventListener('click', () => {
|
||||
window.electronAPI.showMainWindow();
|
||||
});
|
||||
|
||||
// Ajuste inicial do cursor
|
||||
floatButton.style.cursor = 'pointer';
|
||||
|
|
@ -0,0 +1,8 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onUpdateCount: (callback) => ipcRenderer.on('update-count', (_event, value) => callback(value)),
|
||||
showMainWindow: () => ipcRenderer.send('show-main-window')
|
||||
// Remova a linha abaixo:
|
||||
// startDrag: (offset) => ipcRenderer.send('drag-float-window', offset)
|
||||
});
|
||||
|
|
@ -0,0 +1,140 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'; connect-src https://autoatend.linco.work">
|
||||
<title>Aguardando atendimento</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="js/jquery/jquery.js"></script>
|
||||
<style>
|
||||
body {
|
||||
margin: 10px;
|
||||
padding: 0;
|
||||
font-family: Arial, sans-serif;
|
||||
background-color: #f4f4f4;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div id="list-view">
|
||||
<h1>Fila</h1>
|
||||
<ul id="item-list">
|
||||
<!-- Itens serão carregados aqui -->
|
||||
</ul>
|
||||
<button id="next-button" disabled>Iniciar atendimento</button>
|
||||
<button id="sendto-button" disabled>Encaminhar</button>
|
||||
<button id="sair-button">Sair do app</button>
|
||||
</div>
|
||||
|
||||
<div id="obs-view" style="display: none;">
|
||||
<h1>Observações</h1>
|
||||
<p>Item selecionado: <span id="selected-item-name"></span></p>
|
||||
<textarea id="observation-text" rows="10" cols="50" placeholder="Digite suas observações..."></textarea>
|
||||
<button id="save-button">Salvar</button>
|
||||
</div>
|
||||
|
||||
<div id="encaminhar-view" style="display: none;">
|
||||
<h1>Observações</h1>
|
||||
<p>Item selecionado: <span id="selected-item-name"></span></p>
|
||||
<textarea id="observation-text" rows="10" cols="50" placeholder="Digite suas observações..."></textarea>
|
||||
<button id="save-button">Salvar</button>
|
||||
</div>
|
||||
|
||||
<script src="renderer.js"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
|
||||
let apiUrl = 'https://autoatend.linco.work/api/v1/';
|
||||
let token = localStorage.getItem('authToken');
|
||||
let colabId = localStorage.getItem('idOperator');
|
||||
|
||||
function obtemProximosLocalStorage(){
|
||||
let datastorage = localStorage.getItem('proximos');
|
||||
|
||||
// Adiciona os outros itens apenas para visualização (opcional)
|
||||
const proximos = JSON.parse(datastorage);
|
||||
|
||||
itemList.innerHTML = ''; // Limpa a lista anterior
|
||||
if (!proximos || proximos.length === 0) {
|
||||
itemList.innerHTML = '<li>Nenhum item encontrado.</li>';
|
||||
nextButton.disabled = true;
|
||||
return;
|
||||
}
|
||||
|
||||
// Seleciona o primeiro item por padrão (ou o próximo disponível)
|
||||
// Aqui, vamos apenas pegar o primeiro da lista atual
|
||||
const itemToProcess = proximos[0]; // Pega o primeiro item
|
||||
if (itemToProcess) {
|
||||
selectedItemId = itemToProcess.id;
|
||||
selectedItemName = itemToProcess.clientName;
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `${itemToProcess.senhaGen}: ${itemToProcess.clientName.toUpperCase()} - ${itemToProcess.attendanceType.toUpperCase()} - ${itemToProcess.descricaoServico.toUpperCase()}`;
|
||||
li.dataset.id = itemToProcess.id; // Armazena o ID no elemento
|
||||
li.classList.add('selected'); // Marca como selecionado visualmente (precisa de CSS)
|
||||
itemList.appendChild(li);
|
||||
nextButton.disabled = false;
|
||||
} else {
|
||||
itemList.innerHTML = '<li>Nenhum item para processar.</li>';
|
||||
nextButton.disabled = true;
|
||||
selectedItemId = null;
|
||||
selectedItemName = '';
|
||||
}
|
||||
|
||||
//[{"id":57,"userToken":"1feb970af7708cb","colabId":null,"colabObs":null,"serviceId":2,"attendanceType":"Normal","clientName":"Augusto teles","whatsappNumber":null,"notificationType":"audio","Favorito":false,"Status":"Fila","inicioAtendimento":null,"fimAtendimento":null,"duracaoAtendimento":null,"obsAtendimento":null,"subServiceId":0,"senhaGen":"AWTT","descricaoServico":"ENTREGA DE DOCUMENTO"}]
|
||||
|
||||
// Adiciona os outros itens apenas para visualização (opcional)
|
||||
proximos.slice(1).forEach(item => {
|
||||
const li = document.createElement('li');
|
||||
li.textContent = `${item.senhaGen}: ${item.clientName.toUpperCase()} - ${item.attendanceType.toUpperCase()} - ${item.descricaoServico.toUpperCase()}`;
|
||||
itemList.appendChild(li);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
//não vai ser usado timer pois é usado no floatbuttom, ele atualiza com timer
|
||||
//essa função vai ser chamada apenas quando algum registro já estiver sido atendido
|
||||
function updRemoteList() {
|
||||
if (!token && !colabId) {
|
||||
console.warn("Token or colabId not found in localStorage. API requests will not be made.");
|
||||
return; // Stop the function if token or colabId is missing
|
||||
}
|
||||
|
||||
$.ajax(apiUrl + 'get-proximos/'+colabId, {
|
||||
method: 'GET',
|
||||
headers: { 'Authorization': 'Bearer '+token },
|
||||
processData: false,
|
||||
contentType: false,
|
||||
dataType: 'JSON',
|
||||
success: function(response) {
|
||||
console.log('Resposta:', response);
|
||||
// Ensure the response is valid JSON before parsing
|
||||
try {
|
||||
localStorage.setItem('proximos', JSON.stringify(response));
|
||||
} catch (e) {
|
||||
console.error("Error parsing JSON response:", e);
|
||||
console.error("Response text:", response); // Log the raw response for debugging
|
||||
}
|
||||
},
|
||||
error: function(xhr, status, error) {
|
||||
console.error('Erro na requisição:', status, error);
|
||||
console.error('Response Text:', xhr.responseText); // Log the response text for debugging
|
||||
// Optionally, handle different error codes:
|
||||
if (xhr.status === 401) {
|
||||
console.warn("Unauthorized. Token might be invalid.");
|
||||
// You could redirect the user to a login page here.
|
||||
} else if (xhr.status === 404) {
|
||||
console.warn("Resource not found. Check the API endpoint.");
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
setTimeout(()=>{
|
||||
obtemProximosLocalStorage();
|
||||
},5000);
|
||||
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -0,0 +1,52 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">
|
||||
<title>Login</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="js/jquery/jquery.js"></script>
|
||||
</head>
|
||||
<body class="login-page">
|
||||
<div class="login-container">
|
||||
<h1>Login</h1>
|
||||
<div id="error-message" class="error-message"></div>
|
||||
|
||||
<form id="login-form">
|
||||
<div class="form-group">
|
||||
<label for="username">Usuário - CPF/CNPJ:</label>
|
||||
<input type="text" id="username" name="username" required>
|
||||
</div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="password">Senha:</label>
|
||||
<input type="password" id="password" name="password" required>
|
||||
</div>
|
||||
|
||||
<button type="submit" id="login-button">Entrar</button>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
<script src="login.js"></script>
|
||||
|
||||
<script>
|
||||
$(function(){
|
||||
//o campo username deve ser formatado como cpf ou cnpj
|
||||
function formatarCampoCPFCNPJ() {
|
||||
var campo = document.getElementById('username');
|
||||
var valor = campo.value.replace(/\D/g, ''); // Remove todos os caracteres não numéricos
|
||||
|
||||
if (valor.length <= 11) {
|
||||
// Formata como CPF
|
||||
valor = valor.replace(/(\d{3})(\d{3})(\d{3})(\d{2})/, '$1.$2.$3-$4');
|
||||
} else {
|
||||
// Formata como CNPJ
|
||||
valor = valor.replace(/(\d{2})(\d{3})(\d{3})(\d{4})(\d{2})/, '$1.$2.$3/$4-$5');
|
||||
}
|
||||
campo.value = valor;
|
||||
}
|
||||
$('#username').on('input', formatarCampoCPFCNPJ);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,47 @@
|
|||
const loginForm = document.getElementById('login-form');
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
|
||||
// Verifica se já existe um token de autenticação
|
||||
document.addEventListener('DOMContentLoaded', () => {
|
||||
// Verifica se já existe um token no localStorage
|
||||
const authToken = localStorage.getItem('authToken');
|
||||
|
||||
if (authToken) {
|
||||
console.log('Token encontrado, redirecionando para seleção de operador');
|
||||
// Informa ao processo principal que já existe um token
|
||||
window.electronAPI.tokenExists();
|
||||
}
|
||||
});
|
||||
|
||||
loginForm.addEventListener('submit', (e) => {
|
||||
e.preventDefault();
|
||||
|
||||
const login = document.getElementById('username').value;
|
||||
const password = document.getElementById('password').value;
|
||||
|
||||
// Limpa mensagens de erro anteriores
|
||||
errorMessage.textContent = '';
|
||||
errorMessage.style.display = 'none';
|
||||
|
||||
// Desabilita o botão durante o login
|
||||
const loginButton = document.getElementById('login-button');
|
||||
loginButton.disabled = true;
|
||||
loginButton.textContent = 'Autenticando...';
|
||||
|
||||
// Envia credenciais para o processo principal
|
||||
window.electronAPI.login({ login, password });
|
||||
});
|
||||
|
||||
// Recebe resposta do processo de login
|
||||
window.electronAPI.onLoginResponse((response) => {
|
||||
const loginButton = document.getElementById('login-button');
|
||||
loginButton.disabled = false;
|
||||
loginButton.textContent = 'Entrar';
|
||||
|
||||
if (!response.success) {
|
||||
// Exibe mensagem de erro
|
||||
errorMessage.textContent = response.message;
|
||||
errorMessage.style.display = 'block';
|
||||
}
|
||||
// Se for bem-sucedido, o processo principal fechará esta janela
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
login: (credentials) => ipcRenderer.send('login-attempt', credentials),
|
||||
onLoginResponse: (callback) => ipcRenderer.on('login-response', (_event, response) => callback(response)),
|
||||
tokenExists: () => ipcRenderer.send('token-exists')
|
||||
});
|
||||
|
|
@ -0,0 +1,619 @@
|
|||
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();
|
||||
}
|
||||
});
|
||||
|
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"ignore": [
|
||||
"assets/"
|
||||
],
|
||||
"ext": "js, json, html, htm"
|
||||
}
|
||||
|
|
@ -0,0 +1,36 @@
|
|||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="UTF-8">
|
||||
<meta http-equiv="Content-Security-Policy" content="default-src 'self'; script-src 'self' 'unsafe-inline'; style-src 'self' 'unsafe-inline'">
|
||||
<title>Selecionar Operador</title>
|
||||
<link rel="stylesheet" href="style.css">
|
||||
<script src="js/jquery/jquery.js"></script>
|
||||
</head>
|
||||
<body class="operator-page">
|
||||
<div class="operator-container">
|
||||
<h1>Selecione o Operador</h1>
|
||||
<div id="error-message" class="error-message"></div>
|
||||
|
||||
<div class="form-group">
|
||||
<label for="operator-select">Operador atual: <span class="op"></span></label>
|
||||
<select id="operator-select">
|
||||
<option value="">Selecione um operador</option>
|
||||
<!-- Operadores serão carregados aqui -->
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<button id="select-button" disabled>Continuar</button>
|
||||
</div>
|
||||
|
||||
<script src="operator.js"></script>
|
||||
<script>
|
||||
$(function(){
|
||||
let opse = localStorage.getItem('selectedOperator');
|
||||
setTimeout(()=>{
|
||||
$('span.op').text(opse);
|
||||
},2000);
|
||||
});
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,81 @@
|
|||
const operatorSelect = document.getElementById('operator-select');
|
||||
const selectButton = document.getElementById('select-button');
|
||||
const errorMessage = document.getElementById('error-message');
|
||||
|
||||
// Carrega a lista de operadores ao iniciar
|
||||
window.addEventListener('DOMContentLoaded', async () => {
|
||||
try {
|
||||
const response = await window.electronAPI.getOperators();
|
||||
|
||||
//colabs/list
|
||||
|
||||
if (response.success) {
|
||||
// Preenche o select com os operadores
|
||||
response.operators.forEach(operator => {
|
||||
const option = document.createElement('option');
|
||||
option.value = operator.name;
|
||||
option.textContent = operator.name;
|
||||
option.setAttribute('data-sala', operator.sala);
|
||||
option.setAttribute('data-servicos', operator.servicos);
|
||||
option.setAttribute('data-id', operator.id);
|
||||
operatorSelect.appendChild(option);
|
||||
});
|
||||
} else {
|
||||
// Exibe mensagem de erro
|
||||
errorMessage.textContent = response.message;
|
||||
errorMessage.style.display = 'block';
|
||||
|
||||
// Se houver operadores de fallback, preenche com eles
|
||||
if (response.operators && response.operators.length > 0) {
|
||||
response.operators.forEach(operator => {
|
||||
const option = document.createElement('option');
|
||||
option.value = operator.name;
|
||||
option.textContent = operator.name;
|
||||
operatorSelect.appendChild(option);
|
||||
});
|
||||
}
|
||||
}
|
||||
} catch (error) {
|
||||
errorMessage.textContent = `Erro ao carregar operadores: ${error.message}`;
|
||||
errorMessage.style.display = 'block';
|
||||
}
|
||||
});
|
||||
|
||||
// Habilita/desabilita o botão com base na seleção
|
||||
operatorSelect.addEventListener('change', () => {
|
||||
selectButton.disabled = !operatorSelect.value;
|
||||
});
|
||||
|
||||
// Envia o operador selecionado
|
||||
selectButton.addEventListener('click', () => {
|
||||
const selectedOperatorValue = operatorSelect.value;
|
||||
|
||||
if (selectedOperatorValue) {
|
||||
const selectedOption = operatorSelect.options[operatorSelect.selectedIndex];
|
||||
const selectedOperator = {
|
||||
value: selectedOperatorValue,
|
||||
name: selectedOperatorValue, // Assuming name is the same as value
|
||||
sala: selectedOption.getAttribute('data-sala'),
|
||||
servicos: selectedOption.getAttribute('data-servicos'),
|
||||
id: selectedOption.getAttribute('data-id'),
|
||||
};
|
||||
|
||||
selectButton.disabled = true;
|
||||
selectButton.textContent = 'Processando...';
|
||||
|
||||
window.electronAPI.selectOperator(selectedOperator);
|
||||
}
|
||||
});
|
||||
|
||||
// Recebe resposta do processo de seleção
|
||||
window.electronAPI.onOperatorResponse((response) => {
|
||||
if (!response.success) {
|
||||
// Exibe mensagem de erro
|
||||
errorMessage.textContent = response.message;
|
||||
errorMessage.style.display = 'block';
|
||||
|
||||
selectButton.disabled = false;
|
||||
selectButton.textContent = 'Continuar';
|
||||
}
|
||||
// Se for bem-sucedido, o processo principal fechará esta janela
|
||||
});
|
||||
|
|
@ -0,0 +1,7 @@
|
|||
const { contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
getOperators: () => ipcRenderer.invoke('get-operators'),
|
||||
selectOperator: (operatorName) => ipcRenderer.send('select-operator', operatorName),
|
||||
onOperatorResponse: (callback) => ipcRenderer.on('operator-response', (_event, response) => callback(response))
|
||||
});
|
||||
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,49 @@
|
|||
{
|
||||
"name": "autoatendcolab",
|
||||
"version": "1.0.0",
|
||||
"main": "main.js",
|
||||
"isBuildNow": true,
|
||||
"scripts": {
|
||||
"start": "electron .",
|
||||
"nodemon": "nodemon --exec electron .",
|
||||
"test": "echo \"Error: no test specified\" && exit 1",
|
||||
"build": "electron-builder build --win --publish never"
|
||||
},
|
||||
"repository": "https://github.com/edermcastro/autoatendcolab",
|
||||
"publish": {
|
||||
"provider": "github",
|
||||
"releaseType": "release"
|
||||
},
|
||||
"build": {
|
||||
"appId": "com.electronjs.autoatend",
|
||||
"productName": "AutoAtendColab",
|
||||
"directories": {
|
||||
"output": "./dist/win-unpacked"
|
||||
},
|
||||
"files": [
|
||||
"**/*"
|
||||
],
|
||||
"win": {
|
||||
"asar": true,
|
||||
"target": "nsis"
|
||||
},
|
||||
"nsis": {
|
||||
"deleteAppDataOnUninstall": true
|
||||
}
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "Eder Moraes",
|
||||
"license": "ISC",
|
||||
"description": "Sistema auxiliar para colaboradores de autoatendimento",
|
||||
"dependencies": {
|
||||
"@electron/remote": "^2.1.2",
|
||||
"electron-updater": "^6.6.2",
|
||||
"jquery": "^3.7.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"electron": "^35.2.1",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-reload": "^2.0.0-alpha.1",
|
||||
"nodemon": "^3.1.10"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,18 @@
|
|||
const { app, contextBridge, ipcRenderer } = require('electron');
|
||||
|
||||
contextBridge.exposeInMainWorld('electronAPI', {
|
||||
onLoadData: (callback) => ipcRenderer.on('load-data', (_event, value) => callback(value)),
|
||||
sendNextStep: (itemId) => ipcRenderer.send('next-step', itemId),
|
||||
quitApp : () => ipcRenderer.send('sair'),
|
||||
saveObservation: (data) => ipcRenderer.send('save-observation', data), // data = { itemId, observation }
|
||||
|
||||
updAvailable: () => ipcRenderer.on('update-available',(event,arg)=>{
|
||||
console.log(arg);
|
||||
}),
|
||||
currVersion: () => ipcRenderer.on('current_version',(event,arg)=>{
|
||||
console.log('Versão atual = ' + arg);
|
||||
}),
|
||||
updError: () => ipcRenderer.on('error',(event,arg)=>{
|
||||
console.log('error = ' + arg);
|
||||
}),
|
||||
});
|
||||
|
|
@ -0,0 +1,119 @@
|
|||
const listView = document.getElementById('list-view');
|
||||
const observationView = document.getElementById('obs-view');
|
||||
const encaminharView = document.getElementById('encaminhar-view');
|
||||
const itemList = document.getElementById('item-list');
|
||||
const nextButton = document.getElementById('next-button');
|
||||
const quitButton = document.getElementById('sair-button');
|
||||
const observationText = document.getElementById('observation-text');
|
||||
const saveButton = document.getElementById('save-button');
|
||||
const selectedItemNameSpan = document.getElementById('selected-item-name');
|
||||
|
||||
let currentData = [];
|
||||
let selectedItemId = null;
|
||||
let selectedItemName = '';
|
||||
|
||||
window.electronAPI.onLoadData(() => {
|
||||
populateList();
|
||||
// Reseta a view para a lista sempre que os dados são carregados
|
||||
showListView();
|
||||
});
|
||||
|
||||
// Função para popular a lista de itens
|
||||
function populateList() {
|
||||
// let datastorage = localStorage.getItem('proximos');
|
||||
//
|
||||
// // Adiciona os outros itens apenas para visualização (opcional)
|
||||
// const proximos = JSON.parse(datastorage);
|
||||
//
|
||||
// itemList.innerHTML = ''; // Limpa a lista anterior
|
||||
// if (!proximos || proximos.length === 0) {
|
||||
// itemList.innerHTML = '<li>Nenhum item encontrado.</li>';
|
||||
// nextButton.disabled = true;
|
||||
// return;
|
||||
// }
|
||||
//
|
||||
// // Seleciona o primeiro item por padrão (ou o próximo disponível)
|
||||
// // Aqui, vamos apenas pegar o primeiro da lista atual
|
||||
// const itemToProcess = proximos[0]; // Pega o primeiro item
|
||||
// if (itemToProcess) {
|
||||
// selectedItemId = itemToProcess.id;
|
||||
// selectedItemName = itemToProcess.clientName;
|
||||
// const li = document.createElement('li');
|
||||
// li.textContent = `${itemToProcess.senhaGen}: ${itemToProcess.clientName.toUpperCase()} - ${itemToProcess.attendanceType.toUpperCase()} - ${itemToProcess.descricaoServico.toUpperCase()}`;
|
||||
// li.dataset.id = itemToProcess.id; // Armazena o ID no elemento
|
||||
// li.classList.add('selected'); // Marca como selecionado visualmente (precisa de CSS)
|
||||
// itemList.appendChild(li);
|
||||
// nextButton.disabled = false;
|
||||
// } else {
|
||||
// itemList.innerHTML = '<li>Nenhum item para processar.</li>';
|
||||
// nextButton.disabled = true;
|
||||
// selectedItemId = null;
|
||||
// selectedItemName = '';
|
||||
// }
|
||||
//
|
||||
// // Adiciona os outros itens apenas para visualização (opcional)
|
||||
// proximos.slice(1).forEach(item => {
|
||||
// const li = document.createElement('li');
|
||||
// li.textContent = `${item.senhaGen}: ${item.clientName.toUpperCase()} - ${item.attendanceType.toUpperCase()} - ${item.descricaoServico.toUpperCase()}`;
|
||||
// itemList.appendChild(li);
|
||||
// });
|
||||
}
|
||||
|
||||
|
||||
//mostra a tela de listagem e permite iniciar o atendimento
|
||||
function showListView() {
|
||||
populateList();
|
||||
|
||||
listView.style.display = 'block';
|
||||
encaminharView.style.display = 'none';
|
||||
observationView.style.display = 'none';
|
||||
observationText.value = ''; // Limpa a textarea
|
||||
|
||||
window.electronAPI.currVersion(()=>{});
|
||||
|
||||
// nextButton.disabled = !selectedItemId; // Habilita/desabilita baseado na seleção
|
||||
}
|
||||
|
||||
showListView();
|
||||
|
||||
|
||||
//inicia o atendimento
|
||||
function showObservationView() {
|
||||
if (!selectedItemId) return; // Não muda se nada estiver selecionado
|
||||
|
||||
//obter o id do atendimento de modo asyncrono
|
||||
selectedItemNameSpan.textContent = selectedItemName || `ID ${selectedItemId}`; // Mostra nome ou ID
|
||||
listView.style.display = 'none';
|
||||
observationView.style.display = 'block';
|
||||
}
|
||||
|
||||
// // Evento do botão "Iniciar atendimento"
|
||||
nextButton.addEventListener('click', () => {
|
||||
if (selectedItemId !== null) {
|
||||
console.log("Botão Próximo clicado, enviando ID:", selectedItemId);
|
||||
window.electronAPI.sendNextStep(selectedItemId); // Envia o ID para o main process
|
||||
showObservationView(); // Muda para a tela de observação
|
||||
} else {
|
||||
console.warn("Nenhum item selecionado para 'Próximo'");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
|
||||
quitButton.addEventListener('click',()=>{
|
||||
window.electronAPI.quitApp();
|
||||
});
|
||||
|
||||
//
|
||||
// // Evento do botão "Salvar"
|
||||
// saveButton.addEventListener('click', () => {
|
||||
// const observation = observationText.value.trim();
|
||||
// if (selectedItemId !== null) {
|
||||
// window.electronAPI.saveObservation({ itemId: selectedItemId, observation: observation });
|
||||
// window.location.reload();
|
||||
// // A janela será escondida pelo main process após salvar (conforme main.js)
|
||||
// // Se quiser resetar a view sem esconder, chame showListView() aqui.
|
||||
// }
|
||||
// });
|
||||
|
||||
// Inicialmente, mostra a view da lista (estará vazia até receber dados)
|
||||
|
|
@ -0,0 +1,179 @@
|
|||
/* Estilos Gerais */
|
||||
body {
|
||||
font-family: sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: transparent; /* Para janela flutuante */
|
||||
overflow: hidden; /* Evita barras de rolagem indesejadas na flutuante */
|
||||
-webkit-app-region: drag;
|
||||
}
|
||||
|
||||
/* Janela Flutuante */
|
||||
#float-button {
|
||||
width: 64px;
|
||||
height: 64px;
|
||||
border-radius: 50%;
|
||||
/* Cor padrão (azul) quando não há itens */
|
||||
background-color: rgba(0, 122, 255, 0.6);
|
||||
border: transparent;
|
||||
color: white;
|
||||
font-size: 16px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
box-shadow: inset 0 4px 8px rgba(2, 2, 2, 0.8);
|
||||
transition: background-color 0.3s ease; /* Suaviza a transição de cor */
|
||||
position: absolute;
|
||||
top: -6px;
|
||||
left: 4px;
|
||||
-webkit-app-region: no-drag;
|
||||
cursor: pointer;
|
||||
}
|
||||
/* Nova classe para quando houver itens */
|
||||
#float-button.has-items {
|
||||
background-color: rgba(255, 69, 0, 0.7); /* Tom avermelhado/alaranjado com transparência */
|
||||
box-shadow: inset 0 4px 8px rgba(139, 0, 0, 0.9); /* Sombra mais escura avermelhada */
|
||||
}
|
||||
|
||||
#float-button:hover {
|
||||
/* Ajusta o hover para ambas as cores base */
|
||||
filter: brightness(1.2); /* Clareia um pouco no hover */
|
||||
}
|
||||
|
||||
/* Remove a cor de hover específica anterior se existir */
|
||||
/* #float-button:hover {
|
||||
background-color: rgba(0, 100, 210, 0.9);
|
||||
} */
|
||||
|
||||
|
||||
#float-button #icon {
|
||||
font-size: 24px;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
/* Para permitir arrastar a janela pelo body (se o botão não ocupar tudo) */
|
||||
/* body { -webkit-app-region: drag; } */
|
||||
|
||||
|
||||
/* Janela Principal */
|
||||
#list-view, #observation-view {
|
||||
padding: 20px;
|
||||
background-color: white; /* Fundo branco para a janela principal */
|
||||
height: 100vh; /* Ocupa a altura da viewport */
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
#item-list {
|
||||
list-style: none;
|
||||
padding: 0;
|
||||
margin-bottom: 15px;
|
||||
max-height: 400px; /* Altura máxima para a lista */
|
||||
overflow-y: auto; /* Barra de rolagem se necessário */
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
#item-list li {
|
||||
padding: 8px;
|
||||
border-bottom: 1px solid #eee;
|
||||
}
|
||||
|
||||
#item-list li:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
#item-list li.selected {
|
||||
background-color: #e0e0e0;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
button {
|
||||
padding: 10px 15px;
|
||||
font-size: 14px;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
-webkit-app-region: no-drag;
|
||||
background: transparent;
|
||||
}
|
||||
|
||||
button:disabled {
|
||||
cursor: not-allowed;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
textarea {
|
||||
width: 95%; /* Ajuste conforme necessário */
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
border: 1px solid #ccc;
|
||||
padding: 5px;
|
||||
}
|
||||
|
||||
|
||||
/* Estilos para a página de login */
|
||||
.login-page, .operator-page {
|
||||
background-color: #f5f5f5;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.login-container, .operator-container {
|
||||
background-color: white;
|
||||
padding: 30px;
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||||
width: 350px;
|
||||
}
|
||||
|
||||
.form-group {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.form-group label {
|
||||
display: block;
|
||||
margin-bottom: 5px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.form-group input, .form-group select {
|
||||
width: 100%;
|
||||
padding: 10px;
|
||||
border: 1px solid #ddd;
|
||||
border-radius: 4px;
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
.error-message {
|
||||
background-color: #ffebee;
|
||||
color: #c62828;
|
||||
padding: 10px;
|
||||
border-radius: 4px;
|
||||
margin-bottom: 20px;
|
||||
display: none;
|
||||
}
|
||||
|
||||
#login-button, #select-button {
|
||||
background-color: #0077cc;
|
||||
color: white;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 12px 20px;
|
||||
font-size: 16px;
|
||||
cursor: pointer;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
#login-button:hover, #select-button:hover {
|
||||
background-color: #005fa3;
|
||||
}
|
||||
|
||||
#login-button:disabled, #select-button:disabled {
|
||||
background-color: #cccccc;
|
||||
cursor: not-allowed;
|
||||
}
|
||||
Loading…
Reference in New Issue