+
-
Fila de Espera
- 0 + 0
+
-
-
-
- | SENHA | -CLIENTE | -SERVIÇO | -TIPO | +Senha | +Cliente |
|---|
- 📋
-
Nenhum cliente aguardando
+
+ Ninguém na fila
-
+
+
@@ -95,11 +88,18 @@
+
-
+
+
+ 🔔
+ Aguardando Chamada
+Clique no botão abaixo para chamar o próximo cliente
+ +
- ---
- Chamado
+ CHAMADO
+
---
---
@@ -59,17 +56,13 @@
+
- ✅
-
Nenhum atendimento chamado
- Clique no botão flutuante para chamar o próximo -
-
Observações
-Atendendo:
- - + +
+
@@ -107,4 +107,4 @@
-
+
\ No newline at end of file
diff --git a/main.js b/main.js
index b3e59aa..ff6e39f 100644
--- a/main.js
+++ b/main.js
@@ -739,10 +739,11 @@ ipcMain.on('rechamar-atendimento', async (event, itemId) => {
const token = await getAuthToken();
const tenantId = await getTenantId();
const colabId = await getSelectedOperatorId();
- const url = apiUrl + 'attendance/call-next/' + colabId;
+ // Rota correta para atualizar o status do item específico e disparar o chamado na TV
+ const url = apiUrl + 'attendance/' + itemId + '/status';
const request = net.request({
- method: 'POST',
+ method: 'PATCH',
url: url,
headers: {
'Content-Type': 'application/json',
@@ -751,6 +752,12 @@ ipcMain.on('rechamar-atendimento', async (event, itemId) => {
}
});
+ // Enviar o corpo com o status "Chamado"
+ request.write(JSON.stringify({
+ status: 'Chamado',
+ collaboratorId: colabId
+ }));
+
request.on('response', (response) => {
response.on('data', (chunk) => {
console.log(`Rechamar BODY: ${chunk}`);
@@ -766,6 +773,39 @@ ipcMain.on('rechamar-atendimento', async (event, itemId) => {
request.end();
});
+// Encaminhar atendimento para outro colaborador
+ipcMain.on('encaminhar-atendimento', async (event, { itemId, collaboratorId }) => {
+ const token = await getAuthToken();
+ const tenantId = await getTenantId();
+ const url = apiUrl + 'attendance/' + itemId + '/forward';
+
+ const request = net.request({
+ method: 'PATCH',
+ url: url,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ' + token,
+ 'x-tenant-id': tenantId
+ }
+ });
+
+ request.write(JSON.stringify({ collaboratorId }));
+
+ request.on('response', (response) => {
+ response.on('data', (chunk) => {
+ console.log(`Encaminhar BODY: ${chunk}`);
+ });
+ response.on('end', () => {
+ console.log('Encaminhamento concluído.');
+ });
+ });
+ request.on('error', (error) => {
+ console.error(`Erro no encaminhamento: ${error}`);
+ });
+
+ request.end();
+});
+
// Ouve quando um atendimento é iniciado e notifica a janela flutuante
ipcMain.on('atendimento-iniciado', (event, itemId) => {
if (floatingWin) {
diff --git a/package.json b/package.json
index afc7be5..6dba3d3 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "autoatendcolab",
- "version": "1.1.6",
+ "version": "1.1.7",
"main": "main.js",
"isBuildNow": true,
"scripts": {
diff --git a/preload.js b/preload.js
index 696e404..5bc2147 100644
--- a/preload.js
+++ b/preload.js
@@ -17,6 +17,10 @@ contextBridge.exposeInMainWorld('electronAPI', {
//rechama o atendimento atual
rechamarAtendimento: (itemId) => ipcRenderer.send('rechamar-atendimento', itemId),
+ //encaminha o atendimento atual
+ getOperators: () => ipcRenderer.invoke('get-operators'),
+ encaminharAtendimento: (data) => ipcRenderer.send('encaminhar-atendimento', data),
+
//notifica sobre o status do atendimento
atendimentoIniciado: (itemId) => ipcRenderer.send('atendimento-iniciado', itemId),
atendimentoFinalizado: () => ipcRenderer.send('atendimento-finalizado'),
diff --git a/renderer.js b/renderer.js
index b7f2a21..ea2a19a 100644
--- a/renderer.js
+++ b/renderer.js
@@ -1,6 +1,5 @@
const listView = document.getElementById('list-view');
const observationView = document.getElementById('obs-view');
-const encaminharView = document.getElementById('encaminhar-view');
// Novos elementos do layout redesenhado
const queueTableBody = document.getElementById('queue-table-body');
@@ -15,6 +14,7 @@ const cardService = document.getElementById('card-service');
const cardType = document.getElementById('card-type');
const nextButton = document.getElementById('next-button');
const recallButton = document.getElementById('recall-button');
+const forwardButton = document.getElementById('forward-button');
const logoutButton = document.getElementById('logout-button');
const observationText = document.getElementById('observation-text');
const saveButton = document.getElementById('save-button');
@@ -22,6 +22,12 @@ const selectedItemNameSpan = document.getElementById('selected-item-name');
const idAtend = document.getElementById('idAtend');
const counterStart = document.getElementById('counter-start');
+// Elementos do Modal de Encaminhamento
+const forwardView = document.getElementById('forward-view');
+const operatorsList = document.getElementById('operators-list');
+const closeForward = document.getElementById('close-forward');
+const forwardTicketSpan = document.getElementById('forward-ticket');
+
let currentData = [];
let selectedItemId = null;
let selectedItemName = '';
@@ -137,6 +143,7 @@ function populateQueueTable(proximos) {
cardType.textContent = '';
nextButton.disabled = true;
recallButton.disabled = true;
+ forwardButton.disabled = true;
return;
}
@@ -214,6 +221,7 @@ function showCurrentCard(data) {
setTimeout(() => {
nextButton.disabled = false;
recallButton.disabled = false;
+ forwardButton.disabled = false;
}, 500);
}
@@ -240,7 +248,7 @@ document.addEventListener('DOMContentLoaded', () => {
//mostra a tela de listagem e permite iniciar o atendimento
function showListView() {
listView.style.display = 'block';
- encaminharView.style.display = 'none';
+ forwardView.style.display = 'none';
observationView.style.display = 'none';
observationText.value = ''; // Limpa a textarea
}
@@ -301,6 +309,54 @@ recallButton.addEventListener('click', () => {
}
});
+// Evento do botão ENCAMINHAR
+forwardButton.addEventListener('click', async () => {
+ if (calledAtendimentoData) {
+ forwardTicketSpan.textContent = calledAtendimentoData.ticketNumber;
+ forwardView.style.display = 'flex';
+
+ // Carrega operadores
+ operatorsList.innerHTML = '
+
+
+ Encaminhar
+ +Selecione para quem encaminhar :
+
+
+
+ Carregando atendentes...
'; + const response = await window.electronAPI.getOperators(); + + if (response.success && response.operators) { + operatorsList.innerHTML = ''; + + response.operators.forEach(op => { + const item = document.createElement('div'); + item.className = 'operator-item'; + item.textContent = op.name; + item.onclick = () => { + if (confirm(`Encaminhar para ${op.name}?`)) { + window.electronAPI.encaminharAtendimento({ + itemId: calledAtendimentoData._id, + collaboratorId: op._id + }); + forwardView.style.display = 'none'; + + // Limpa o atendimento atual pois foi encaminhado + localStorage.removeItem('atendimentoAtual'); + localStorage.removeItem('atendimentoAtualNome'); + calledAtendimentoData = null; + selectedItemId = null; + showListView(); + } + }; + operatorsList.appendChild(item); + }); + + if (response.operators.length === 0) { + operatorsList.innerHTML = 'Nenhum outro atendente disponível.
'; + } + } else { + operatorsList.innerHTML = `${response.message || 'Erro ao carregar atendentes.'}
`; + } + } +}); + +closeForward.addEventListener('click', () => { + forwardView.style.display = 'none'; +}); logoutButton.addEventListener('click', () => { window.electronAPI.logoutApp(); diff --git a/style.css b/style.css index 5bb7e8e..30e8be4 100644 --- a/style.css +++ b/style.css @@ -499,8 +499,8 @@ body#floating{ } .btn-logout:hover { + color: var(--white); background: var(--danger-color); - color: #fff; } @@ -703,10 +703,12 @@ button{ } #logout-button{ + color: var(--white); background-color: var(--danger-color); } #logout-button:hover{ + color: var(--white); background-color: var(--dark-danger-color); } @@ -726,10 +728,12 @@ button{ #sair-button{ + color: var(--white); background-color: #eb574d; } #sair-button:hover{ + color: var(--white); background-color: #e93f2c; } @@ -822,4 +826,108 @@ input:checked + .slider:before { .queue-table-wrapper::-webkit-scrollbar-thumb:hover { background: rgba(255, 255, 255, 0.25); -} \ No newline at end of file +} +.btn-forward { + flex: 1; + padding: 12px 16px; + border: 2px solid #8e44ad; + background: transparent; + color: #8e44ad; + border-radius: 8px; + font-weight: 700; + font-size: 13px; + cursor: pointer; + transition: all 0.3s ease; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; +} + +.btn-forward:hover:not(:disabled) { + background: #8e44ad; + color: #fff; +} + +.btn-forward:disabled { + opacity: 0.4; + cursor: not-allowed; +} + +/* Modal Forward styles */ +.modal-view { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background: rgba(0, 0, 0, 0.8); + display: flex; + align-items: center; + justify-content: center; + z-index: 1000; + backdrop-filter: blur(5px); +} + +.modal-content { + background: var(--primary-color); + width: 90%; + max-width: 500px; + border-radius: 16px; + border: 1px solid var(--secondary-color); + padding: 24px; + box-shadow: 0 20px 40px rgba(0,0,0,0.5); +} + +.modal-header { + display: flex; + justify-content: space-between; + align-items: center; + margin-bottom: 20px; +} + +.modal-header h2 { + color: var(--white); + margin: 0; + font-size: 1.5rem; +} + +.btn-close { + background: transparent; + border: none; + color: var(--dark-gray); + font-size: 24px; + cursor: pointer; +} + +.operators-grid { + display: grid; + grid-template-columns: 1fr; + gap: 12px; + max-height: 300px; + overflow-y: auto; + margin-top: 15px; + padding-right: 5px; +} + +.operator-item { + background: var(--secondary-color); + border: 1px solid var(--tertiary-color); + padding: 15px; + border-radius: 10px; + color: var(--white); + cursor: pointer; + transition: all 0.2s; + text-align: left; + font-weight: 500; +} + +.operator-item:hover { + background: var(--medium-gold); + border-color: var(--accent-gold); +} + +.highlight { + color: var(--accent-gold); + font-weight: bold; +}