From c9c28e68c758dd27c600df6995d2d96b2ec8bdd9 Mon Sep 17 00:00:00 2001
From: Eder Moraes <54563944+edermcastro@users.noreply.github.com>
Date: Mon, 27 Apr 2026 02:31:05 -0300
Subject: [PATCH] nova interface da tela de atendimento
---
index.html | 93 ++++++++--
main.js | 33 ++++
preload.js | 3 +
renderer.js | 208 ++++++++++++++++------
style.css | 487 ++++++++++++++++++++++++++++++++++++++++++++++++++--
5 files changed, 742 insertions(+), 82 deletions(-)
diff --git a/index.html b/index.html
index 19f4d2d..3d39164 100644
--- a/index.html
+++ b/index.html
@@ -15,34 +15,91 @@
+
-
Fila
-
-
-
-
-
+
+
+
+
+
+
+
+
+ | SENHA |
+ CLIENTE |
+ SERVIÇO |
+ TIPO |
+
+
+
+
+
+
+
+
📋
+
Nenhum cliente aguardando
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
✅
+
Nenhum atendimento chamado
+
Clique no botão flutuante para chamar o próximo
+
+
+
+
+
+
+
+
-
Observações
-
Atendendo:
-
-
-
+
+
+
+
+
+
+
+
Observações
-
Atendendo:
+
Atendendo:
-
+
diff --git a/main.js b/main.js
index a63023a..12130eb 100644
--- a/main.js
+++ b/main.js
@@ -733,6 +733,39 @@ ipcMain.on('iniciar-atendimento', async (event, itemId) => {
request.end();
});
+// Ouvir clique no botão "Rechamar" - chama novamente o mesmo atendimento
+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;
+
+ const request = net.request({
+ method: 'POST',
+ url: url,
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Authorization': 'Bearer ' + token,
+ 'x-tenant-id': tenantId
+ }
+ });
+
+ request.on('response', (response) => {
+ response.on('data', (chunk) => {
+ console.log(`Rechamar BODY: ${chunk}`);
+ });
+ response.on('end', () => {
+ console.log('Rechamada concluída.');
+ });
+ });
+ request.on('error', (error) => {
+ console.error(`Erro na rechamada: ${error}`);
+ });
+
+ request.end();
+});
+
// Ouve quando um atendimento é iniciado e notifica a janela flutuante
ipcMain.on('atendimento-iniciado', (event, itemId) => {
if (floatingWin) {
diff --git a/preload.js b/preload.js
index 5037998..696e404 100644
--- a/preload.js
+++ b/preload.js
@@ -14,6 +14,9 @@ contextBridge.exposeInMainWorld('electronAPI', {
//inicia o atendimento atual
iniciaAtendimento: (itemId) => ipcRenderer.send('iniciar-atendimento', itemId),
+ //rechama o atendimento atual
+ rechamarAtendimento: (itemId) => ipcRenderer.send('rechamar-atendimento', itemId),
+
//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 5f061b2..b7f2a21 100644
--- a/renderer.js
+++ b/renderer.js
@@ -1,14 +1,24 @@
const listView = document.getElementById('list-view');
const observationView = document.getElementById('obs-view');
const encaminharView = document.getElementById('encaminhar-view');
-const itemList = document.getElementById('item-list');
+
+// Novos elementos do layout redesenhado
+const queueTableBody = document.getElementById('queue-table-body');
+const queueEmpty = document.getElementById('queue-empty');
+const queueCount = document.getElementById('queue-count');
+const currentCard = document.getElementById('current-card');
+const noCurrentCard = document.getElementById('no-current');
+const cardTicket = document.getElementById('card-ticket');
+const cardStatusBadge = document.getElementById('card-status-badge');
+const cardClientName = document.getElementById('card-client-name');
+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 logoutButton = document.getElementById('logout-button');
const observationText = document.getElementById('observation-text');
-const encObservationText = document.getElementById('enc-observation-text');
const saveButton = document.getElementById('save-button');
const selectedItemNameSpan = document.getElementById('selected-item-name');
-const queueNumber = document.getElementById('queue-number');
const idAtend = document.getElementById('idAtend');
const counterStart = document.getElementById('counter-start');
@@ -16,16 +26,22 @@ let currentData = [];
let selectedItemId = null;
let selectedItemName = '';
+// ===========================
+// FLAG que trava o ID do atendimento chamado.
+// Quando o call-next retorna e define o atendimento atual,
+// este flag impede que atualizações da fila sobrescrevam o selectedItemId.
+// ===========================
+let calledAtendimentoData = null; // Guarda os dados completos do atendimento chamado
+
window.electronAPI.onLoadData((data) => {
// Se já estiver em atendimento, ignora atualizações da lista para evitar flickering no botão
if (localStorage.getItem('atendimentoAtual')) {
return;
}
- nextButton.disabled = true;
if (!data) {
return;
}
- populateList(data);
+ populateQueueTable(data);
});
async function initializeSocket() {
@@ -75,20 +91,22 @@ initializeSocket();
window.electronAPI.selectAtendID((data) => {
nextButton.disabled = true;
if (!data) {
- queueNumber.innerHTML = 'Ninguem aguardando atendimento, fechando a janela em alguns segundos...';
+ showNoCurrentCard('Ninguém aguardando atendimento');
return;
}
- // Reseta a view para a lista sempre que os dados são carregados ao clicar no botão para abrir a janela
- populateList(data);
- showListView();
- // Garante que o item selecionado (ID e Nome) seja o que veio da chamada, sobrescrevendo o da lista.
+ // Trava os dados do atendimento chamado para que atualizações da fila
+ // NÃO sobrescrevam quem foi chamado.
+ calledAtendimentoData = data;
selectedItemId = data._id ?? null;
selectedItemName = data.clientName ?? '';
- //data.senhaGen
- queueNumber.innerHTML = data ? `NA VEZ: ${data.clientName.toUpperCase()}` : 'Ninguem aguardando atendimento';
- selectedItemNameSpan.innerHTML = data ? ` ${data.clientName.toUpperCase()} [ ${data.ticketNumber} ]` : 'Ninguem aguardando atendimento';
+ // Atualiza a tabela da fila (sem afetar o card atual)
+ // populateQueueTable já NÃO mexe no selectedItemId quando calledAtendimentoData está travado
+ showListView();
+
+ // Mostra o card do atendimento chamado
+ showCurrentCard(data);
});
window.electronAPI.showObservation(() => {
@@ -96,15 +114,29 @@ window.electronAPI.showObservation(() => {
showObservationView(); // Muda para a tela de observação
});
-// Função para popular a lista de itens
-function populateList(proximos) {
+// Função para popular a tabela da fila
+function populateQueueTable(proximos) {
const atendimentoEmAndamentoId = localStorage.getItem('atendimentoAtual');
const atendimentoEmAndamentoNome = localStorage.getItem('atendimentoAtualNome');
if (atendimentoEmAndamentoId) {
- itemList.innerHTML = `Atendimento com ${(atendimentoEmAndamentoNome || '').toUpperCase()} em andamento.`;
- queueNumber.innerHTML = `EM ATENDIMENTO: ${(atendimentoEmAndamentoNome || '').toUpperCase()}`;
+ // Em atendimento: mostra card de atendimento no painel direito
+ queueTableBody.innerHTML = '';
+ queueCount.textContent = '0';
+ queueEmpty.style.display = 'flex';
+ document.querySelector('.queue-table').style.display = 'none';
+
+ // Mostra card como "Atendendo"
+ currentCard.style.display = 'flex';
+ noCurrentCard.style.display = 'none';
+ cardTicket.textContent = '';
+ cardStatusBadge.textContent = 'Atendendo';
+ cardStatusBadge.className = 'card-badge badge-atendendo';
+ cardClientName.textContent = (atendimentoEmAndamentoNome || '').toUpperCase();
+ cardService.textContent = '';
+ cardType.textContent = '';
nextButton.disabled = true;
+ recallButton.disabled = true;
return;
}
@@ -114,36 +146,81 @@ function populateList(proximos) {
proximos = JSON.parse(datastorage || '[]');
}
- itemList.innerHTML = '';
+ // Limpa e popula a tabela
+ queueTableBody.innerHTML = '';
- setTimeout(() => {
- nextButton.disabled = !proximos || proximos.length === 0;
- }, 1000);
-
- // 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.innerHTML = /*${itemToProcess.ticketNumber}: */ `${itemToProcess.clientName.toUpperCase()} - ${itemToProcess.ticketNumber}`;
- li.dataset.id = itemToProcess._id; // Armazena o ID no elemento
- li.classList.add('selected'); // Marca como selecionado visualmente (precisa de CSS)
- itemList.appendChild(li);
+ if (!proximos || proximos.length === 0) {
+ queueEmpty.style.display = 'flex';
+ document.querySelector('.queue-table').style.display = 'none';
+ queueCount.textContent = '0';
} else {
- itemList.innerHTML = 'Fila vazia!';
- nextButton.disabled = true;
- selectedItemId = null;
- selectedItemName = '';
+ queueEmpty.style.display = 'none';
+ document.querySelector('.queue-table').style.display = 'table';
+ queueCount.textContent = proximos.length;
+
+ proximos.forEach((item, index) => {
+ const tr = document.createElement('tr');
+ const isPreferencial = item.ticketNumber && item.ticketNumber.startsWith('P');
+
+ tr.innerHTML = `
+ ${item.ticketNumber || '---'} |
+ ${(item.clientName || '---').toUpperCase()} |
+ ${item.serviceName || 'Atendimento'} |
+ ${isPreferencial ? 'PREFERENCIAL' : 'NORMAL'} |
+ `;
+
+ queueTableBody.appendChild(tr);
+ });
}
- // Adiciona os outros itens apenas para visualização (opcional)
- proximos.slice(1).forEach(item => {
- const li = document.createElement('li');
- li.textContent = `${item.clientName.toUpperCase()} - ${item.ticketNumber}`;
- itemList.appendChild(li);
- });
+ // CORREÇÃO DO BUG: Só atualiza selectedItemId se NÃO houver um atendimento já chamado (travado)
+ if (!calledAtendimentoData) {
+ // Nenhum atendimento foi chamado ainda, pode selecionar o primeiro
+ const firstItem = proximos && proximos[0];
+ if (firstItem) {
+ selectedItemId = firstItem._id;
+ selectedItemName = firstItem.clientName;
+ } else {
+ selectedItemId = null;
+ selectedItemName = '';
+ }
+ // Sem atendimento chamado: não mostra card
+ if (!currentCard.style.display || currentCard.style.display === 'none') {
+ noCurrentCard.style.display = 'flex';
+ }
+ }
+ // Se calledAtendimentoData está definido, NÃO toca no selectedItemId — mantém o que foi chamado.
+}
+
+// Mostra o card do atendimento atual (chamado)
+function showCurrentCard(data) {
+ currentCard.style.display = 'flex';
+ noCurrentCard.style.display = 'none';
+
+ cardTicket.textContent = data.ticketNumber || '---';
+
+ const statusText = data.status === 'Atendendo' ? 'Atendendo' : 'Chamado';
+ cardStatusBadge.textContent = statusText;
+ cardStatusBadge.className = `card-badge ${data.status === 'Atendendo' ? 'badge-atendendo' : 'badge-chamado'}`;
+
+ cardClientName.textContent = (data.clientName || '---').toUpperCase();
+ cardService.textContent = (data.serviceName || 'ATENDIMENTO').toUpperCase();
+
+ const isPreferencial = data.ticketNumber && data.ticketNumber.startsWith('P');
+ cardType.textContent = isPreferencial ? 'PREFERENCIAL' : 'NORMAL';
+ cardType.className = `card-type ${isPreferencial ? 'type-preferencial' : ''}`;
+
+ // Habilita os botões
+ setTimeout(() => {
+ nextButton.disabled = false;
+ recallButton.disabled = false;
+ }, 500);
+}
+
+function showNoCurrentCard(message) {
+ currentCard.style.display = 'none';
+ noCurrentCard.style.display = 'flex';
+ noCurrentCard.querySelector('p').textContent = message || 'Nenhum atendimento chamado';
}
@@ -178,23 +255,51 @@ function showObservationView() {
observationView.style.display = 'block';
}
-// // Evento do botão "Iniciar atendimento"
+// // Evento do botão "Iniciar atendimento" (INICIAR)
nextButton.addEventListener('click', () => {
- if (selectedItemId !== null) {
+ // Usa calledAtendimentoData para garantir que estamos iniciando O ATENDIMENTO CORRETO
+ const idToStart = calledAtendimentoData ? calledAtendimentoData._id : selectedItemId;
+ const nameToStart = calledAtendimentoData ? calledAtendimentoData.clientName : selectedItemName;
+
+ if (idToStart !== null) {
// Salva o estado de atendimento no localStorage
- localStorage.setItem('atendimentoAtual', selectedItemId);
- localStorage.setItem('atendimentoAtualNome', selectedItemName);
+ localStorage.setItem('atendimentoAtual', idToStart);
+ localStorage.setItem('atendimentoAtualNome', nameToStart);
+
+ // Atualiza o span da tela de observação
+ selectedItemId = idToStart;
+ selectedItemName = nameToStart;
+ selectedItemNameSpan.innerHTML = ` ${(nameToStart || '').toUpperCase()} `;
// Notifica o main process e muda a view
// IMPORTANTE: iniciaAtendimento deve apenas mudar o status na API, não chamar o próximo.
- window.electronAPI.atendimentoIniciado(selectedItemId);
- window.electronAPI.iniciaAtendimento(selectedItemId);
+ window.electronAPI.atendimentoIniciado(idToStart);
+ window.electronAPI.iniciaAtendimento(idToStart);
+
+ // Limpa o travamento — atendimento foi iniciado
+ calledAtendimentoData = null;
+
showObservationView(); // Muda para a tela de observação
} else {
- console.warn("Nenhum item selecionado para 'Próximo'");
+ console.warn("Nenhum item selecionado para 'Iniciar'");
}
});
+// Evento do botão RECHAMAR
+recallButton.addEventListener('click', () => {
+ if (calledAtendimentoData) {
+ // Re-chama o mesmo atendimento (pode tocar som, exibir no painel, etc.)
+ window.electronAPI.rechamarAtendimento(calledAtendimentoData._id);
+
+ // Feedback visual
+ recallButton.textContent = 'RECHAMANDO...';
+ recallButton.disabled = true;
+ setTimeout(() => {
+ recallButton.textContent = 'RECHAMAR';
+ recallButton.disabled = false;
+ }, 2000);
+ }
+});
logoutButton.addEventListener('click', () => {
@@ -211,6 +316,9 @@ saveButton.addEventListener('click', () => {
localStorage.removeItem('atendimentoAtualNome');
window.electronAPI.atendimentoFinalizado();
+ // Limpa o travamento
+ calledAtendimentoData = null;
+
window.electronAPI.saveObservation({ itemId: selectedItemId, observation: observation });
window.location.reload();
}
diff --git a/style.css b/style.css
index fd62a23..5bb7e8e 100644
--- a/style.css
+++ b/style.css
@@ -34,7 +34,7 @@
/* Estilos Gerais */
body {
- font-family: sans-serif;
+ font-family: 'Segoe UI', sans-serif;
color: #FFF;
margin: 0;
padding: 10px;
@@ -125,16 +125,465 @@ body#floating{
}
-/* Janela Principal */
-#list-view, #obs-view {
+/* ================================================
+ NOVO LAYOUT: Tela de Atendimento Redesenhada
+ ================================================ */
+
+#list-view {
+ padding: 16px;
+ background-color: var(--primary-color);
+ height: 96vh;
+ box-sizing: border-box;
+ display: flex;
+ flex-direction: column;
+}
+
+#obs-view {
padding: 20px;
- background-color: var(--secondary-color); /* Fundo branco para a janela principal */
+ background-color: var(--secondary-color);
border-radius: var(--border-radius);
box-shadow: var(--box-shadow);
- height: 96vh; /* Ocupa a altura da viewport */
+ height: 96vh;
box-sizing: border-box;
}
+.attendance-layout {
+ display: flex;
+ gap: 16px;
+ flex: 1;
+ min-height: 0;
+}
+
+/* PAINEL ESQUERDO: Tabela da fila */
+.queue-panel {
+ flex: 1.6;
+ background-color: rgba(11, 44, 80, 0.4);
+ border-radius: 10px;
+ border: 1px solid rgba(255, 255, 255, 0.06);
+ display: flex;
+ flex-direction: column;
+ overflow: hidden;
+}
+
+.panel-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ padding: 16px 20px;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.08);
+}
+
+.panel-header h2 {
+ margin: 0;
+ font-size: 16px;
+ font-weight: 600;
+ color: rgba(255, 255, 255, 0.85);
+ letter-spacing: 0.3px;
+}
+
+.queue-badge {
+ background: var(--info-color);
+ color: white;
+ padding: 2px 10px;
+ border-radius: 12px;
+ font-size: 13px;
+ font-weight: 600;
+ min-width: 20px;
+ text-align: center;
+}
+
+.queue-table-wrapper {
+ flex: 1;
+ overflow-y: auto;
+ padding: 0;
+}
+
+/* Tabela da fila */
+.queue-table {
+ width: 100%;
+ border-collapse: collapse;
+ table-layout: fixed;
+}
+
+.queue-table thead th {
+ padding: 10px 20px;
+ text-align: left;
+ font-size: 11px;
+ font-weight: 600;
+ text-transform: uppercase;
+ letter-spacing: 1px;
+ color: rgba(255, 255, 255, 0.4);
+ border-bottom: 1px solid rgba(255, 255, 255, 0.06);
+ position: sticky;
+ top: 0;
+ background: rgba(11, 44, 80, 0.95);
+ z-index: 1;
+}
+
+.queue-table tbody tr {
+ transition: background-color 0.2s ease;
+ border-bottom: 1px solid rgba(255, 255, 255, 0.04);
+}
+
+.queue-table tbody tr:hover {
+ background-color: rgba(255, 255, 255, 0.03);
+}
+
+.queue-table tbody td {
+ padding: 12px 20px;
+ font-size: 13px;
+ color: rgba(255, 255, 255, 0.7);
+}
+
+.ticket-cell {
+ font-weight: 700;
+ font-size: 14px !important;
+}
+
+.ticket-normal {
+ color: var(--light-info-color) !important;
+}
+
+.ticket-preferencial {
+ color: var(--warning-color) !important;
+}
+
+.type-normal {
+ color: rgba(255, 255, 255, 0.5) !important;
+ font-size: 11px !important;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+}
+
+.type-preferencial {
+ color: var(--warning-color) !important;
+ font-size: 11px !important;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+}
+
+.queue-empty {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ padding: 40px;
+ color: rgba(255, 255, 255, 0.3);
+ gap: 8px;
+}
+
+.queue-empty .empty-icon {
+ font-size: 36px;
+ opacity: 0.5;
+}
+
+.queue-empty p {
+ margin: 0;
+ font-size: 14px;
+}
+
+
+/* PAINEL DIREITO: Card do atendimento atual */
+.current-card-panel {
+ flex: 0.8;
+ display: flex;
+ align-items: stretch;
+}
+
+.current-card {
+ background: linear-gradient(145deg, rgba(11, 44, 80, 0.6), rgba(8, 30, 60, 0.8));
+ border-radius: 12px;
+ border: 1px solid rgba(255, 255, 255, 0.08);
+ display: flex;
+ flex-direction: column;
+ padding: 24px;
+ width: 100%;
+ box-sizing: border-box;
+ animation: cardFadeIn 0.3s ease-out;
+}
+
+@keyframes cardFadeIn {
+ from {
+ opacity: 0;
+ transform: translateY(8px);
+ }
+ to {
+ opacity: 1;
+ transform: translateY(0);
+ }
+}
+
+.card-header {
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ margin-bottom: 16px;
+}
+
+.card-ticket {
+ font-size: 32px;
+ font-weight: 800;
+ color: #fff;
+ letter-spacing: 1px;
+}
+
+.card-badge {
+ padding: 4px 14px;
+ border-radius: 16px;
+ font-size: 12px;
+ font-weight: 700;
+ text-transform: uppercase;
+ letter-spacing: 0.5px;
+}
+
+.badge-chamado {
+ background: var(--warning-color);
+ color: #fff;
+}
+
+.badge-atendendo {
+ background: var(--success-color);
+ color: #fff;
+ animation: pulse-badge 1.5s infinite;
+}
+
+@keyframes pulse-badge {
+ 0%, 100% { opacity: 1; }
+ 50% { opacity: 0.7; }
+}
+
+.card-body {
+ flex: 1;
+ display: flex;
+ flex-direction: column;
+ gap: 4px;
+ margin-bottom: 20px;
+}
+
+.card-client-name {
+ font-size: 18px;
+ font-weight: 700;
+ color: #fff;
+ margin: 0;
+ line-height: 1.3;
+}
+
+.card-service {
+ font-size: 13px;
+ color: rgba(255, 255, 255, 0.5);
+ margin: 0;
+ font-weight: 500;
+ letter-spacing: 0.3px;
+}
+
+.card-type {
+ font-size: 12px;
+ color: rgba(255, 255, 255, 0.35);
+ margin: 4px 0 0 0;
+ font-weight: 600;
+ letter-spacing: 0.5px;
+}
+
+.card-type.type-preferencial {
+ color: var(--warning-color);
+}
+
+.card-actions {
+ display: flex;
+ gap: 10px;
+ margin-top: auto;
+}
+
+.btn-recall {
+ flex: 1;
+ padding: 12px 16px;
+ border: 2px solid var(--warning-color);
+ background: transparent;
+ color: var(--warning-color);
+ border-radius: 8px;
+ font-size: 13px;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ letter-spacing: 0.5px;
+ -webkit-app-region: no-drag;
+}
+
+.btn-recall:hover:not(:disabled) {
+ background: var(--warning-color);
+ color: #fff;
+}
+
+.btn-recall:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.btn-start {
+ flex: 1;
+ padding: 12px 16px;
+ border: none;
+ background: var(--info-color);
+ color: #fff;
+ border-radius: 8px;
+ font-size: 13px;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ letter-spacing: 0.5px;
+ -webkit-app-region: no-drag;
+}
+
+.btn-start:hover:not(:disabled) {
+ background: var(--light-info-color);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(41, 128, 185, 0.4);
+}
+
+.btn-start:disabled {
+ opacity: 0.4;
+ cursor: not-allowed;
+}
+
+.no-current-card {
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: center;
+ width: 100%;
+ background: rgba(11, 44, 80, 0.2);
+ border-radius: 12px;
+ border: 1px dashed rgba(255, 255, 255, 0.1);
+ color: rgba(255, 255, 255, 0.3);
+ gap: 8px;
+ padding: 20px;
+ box-sizing: border-box;
+}
+
+.no-current-card .empty-icon {
+ font-size: 36px;
+ opacity: 0.4;
+}
+
+.no-current-card p {
+ margin: 0;
+ font-size: 14px;
+ font-weight: 500;
+}
+
+.no-current-card small {
+ font-size: 12px;
+ opacity: 0.6;
+}
+
+
+/* Barra inferior */
+.bottom-bar {
+ padding: 10px 0 0 0;
+ display: flex;
+ justify-content: flex-start;
+ gap: 10px;
+}
+
+.btn-logout {
+ padding: 8px 20px;
+ background: transparent;
+ border: 1px solid var(--danger-color);
+ color: var(--danger-color);
+ border-radius: 6px;
+ font-size: 12px;
+ font-weight: 600;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ -webkit-app-region: no-drag;
+}
+
+.btn-logout:hover {
+ background: var(--danger-color);
+ color: #fff;
+}
+
+
+/* ================================================
+ TELA DE OBSERVAÇÕES (Atendimento em andamento)
+ ================================================ */
+.obs-layout {
+ display: flex;
+ flex-direction: column;
+ height: 100%;
+}
+
+.obs-header {
+ margin-bottom: 20px;
+}
+
+.obs-header h2 {
+ margin: 0 0 8px 0;
+ font-size: 18px;
+ font-weight: 600;
+ color: rgba(255, 255, 255, 0.9);
+}
+
+.obs-client-info {
+ display: flex;
+ align-items: center;
+ gap: 12px;
+}
+
+.obs-client-name {
+ font-size: 16px;
+ color: var(--light-info-color);
+ font-weight: 600;
+}
+
+#observation-text {
+ width: calc(100% - 22px) !important;
+ padding: 10px;
+ background-color: var(--secondary-color);
+ border: 1px solid var(--tertiary-color);
+ border-radius: 6px;
+ color: var(--medium-gray);
+ font-size: 14px;
+ flex: 1;
+ resize: vertical;
+ min-height: 120px;
+}
+
+#observation-text:focus-visible {
+ box-shadow: var(--box-shadow-inputs);
+ outline: none;
+}
+
+.obs-actions {
+ display: flex;
+ justify-content: flex-end;
+ padding-top: 12px;
+}
+
+.btn-finish {
+ padding: 12px 28px;
+ background: var(--warning-color);
+ border: none;
+ color: #fff;
+ border-radius: 8px;
+ font-size: 14px;
+ font-weight: 700;
+ cursor: pointer;
+ transition: all 0.2s ease;
+ -webkit-app-region: no-drag;
+}
+
+.btn-finish:hover {
+ background: var(--light-warning-color);
+ transform: translateY(-1px);
+ box-shadow: 0 4px 12px rgba(243, 156, 18, 0.3);
+}
+
+
+/* ================================================
+ ESTILOS LEGADOS (mantidos para outras telas)
+ ================================================ */
+
#item-list {
list-style: none;
padding: 0;
@@ -227,15 +676,6 @@ textarea {
font-size: 16px;
}
-textarea#observation-text{
- width: calc(100% -20px) !important;
- padding: 10px;
- background-color: var(--secondary-color);
- border: 1px solid var(--tertiary-color);
- border-radius: 4px;
- color: var(--medium-gray);
-}
-
.error-message {
background-color: #ffebee;
color: #c62828;
@@ -363,4 +803,23 @@ input:checked + .slider:before {
.slider.round:before {
border-radius: 50%;
+}
+
+
+/* Scrollbar personalizada */
+.queue-table-wrapper::-webkit-scrollbar {
+ width: 6px;
+}
+
+.queue-table-wrapper::-webkit-scrollbar-track {
+ background: transparent;
+}
+
+.queue-table-wrapper::-webkit-scrollbar-thumb {
+ background: rgba(255, 255, 255, 0.15);
+ border-radius: 3px;
+}
+
+.queue-table-wrapper::-webkit-scrollbar-thumb:hover {
+ background: rgba(255, 255, 255, 0.25);
}
\ No newline at end of file