feat: comprehensive design system overhaul
Applied impeccable design critique, audit, harden, layout, distill, and polish across all pages. DESIGN SYSTEM: - Created PRODUCT.md (5 design principles, WCAG 2.1 AA target) - Created DESIGN.md (34 color tokens, 6 radius scale, full component spec) - Created .impeccable/design.json (sidecar with tonal ramps, shadows, motion) ACCESSIBILITY: - 33 aria-labels across 7 pages (was ~13) - 63 <label> elements (was ~30) - 6 <main> landmarks + role=navigation on all sidebars - Esc to close modals on 4 pages - Keyboard shortcuts (Ctrl+J/K, Ctrl+F) on chat THEMING: - 216 hardcoded colors replaced with var() references - 449 !important removed from dark-mode.css - Dark mode script unified (was duplicated 9x inline) - All modals converted to .modal-overlay + .modal design system classes CHAT: - Error states for messages (.msg.erro, .msg.enviando) - Connection status indicator - Confirmation modal before finalizar - Input bar reorganized (3 visible actions, + menu) - Right panel accordions - Send button disabled when empty - Touch targets 44px on mobile CLIENT DETAIL: - Tabs/badges/sub-tabs use CSS classes instead of inline styles - 'Iniciar Conversa' button now primary action - alert() replaced with inline feedback - Modal converted to design system classes CLIENT LIST: - Modal moved inside <body> (was HTML-invalid) - alert() replaced with inline feedback - Modal uses .show pattern SETTINGS: - 4 modals converted to design system classes - 14 alert() calls replaced with inline feedback - fecharModal() uses classList instead of style.display ROUTES: - border-radius normalized, hardcoded colors fixed Score progression: Chat: 23 → 32/40 Client List: 32 → 35/40 Client Detail: 26 → 30/40 Settings: 29 → 35/40 Routes: 37 → 38/40 Audit (project): 14/20 → 17/20
This commit is contained in:
+477
-118
@@ -7,7 +7,7 @@
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<style>
|
||||
/* ===== CHAT PAGE - Estilos específicos ===== */
|
||||
body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
body { background:var(--surface-3); display:flex; height:100vh; overflow:hidden; }
|
||||
|
||||
/* ===== SIDEBAR ESQUERDA ===== */
|
||||
.sidebar-left {
|
||||
@@ -37,7 +37,7 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
}
|
||||
.sidebar-left .header-nav a {
|
||||
padding: 6px 9px;
|
||||
border-radius: 7px;
|
||||
border-radius: 6px;
|
||||
color: rgba(255,255,255,0.55);
|
||||
text-decoration: none;
|
||||
font-size: 17px;
|
||||
@@ -237,35 +237,35 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
|
||||
.chat-header {
|
||||
padding: 11px 18px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
border-bottom: 1px solid var(--border);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
box-shadow: 0 1px 4px rgba(0,0,0,0.05);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.chat-header .info h3 { font-size: 15px; font-weight: 600; color: #111827; }
|
||||
.chat-header .info span { font-size: 12px; color: #6b7280; }
|
||||
.chat-header .info h3 { font-size: 15px; font-weight: 600; color: var(--text-primary); }
|
||||
.chat-header .info span { font-size: 12px; color: var(--text-muted); }
|
||||
.chat-header .actions { display: flex; gap: 7px; }
|
||||
.chat-header .actions button {
|
||||
padding: 6px 14px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
color: #374151;
|
||||
color: var(--text-secondary);
|
||||
transition: all .15s;
|
||||
}
|
||||
.chat-header .actions button:hover { background: #f3f4f6; border-color: #d1d5db; }
|
||||
.chat-header .actions button:hover { background: var(--surface-3); border-color: #d1d5db; }
|
||||
.chat-header .actions .btn-finalizar {
|
||||
background: #ef4444;
|
||||
background: var(--danger);
|
||||
color: #fff;
|
||||
border-color: #ef4444;
|
||||
border-color: var(--danger);
|
||||
font-weight: 600;
|
||||
}
|
||||
.chat-header .actions .btn-finalizar:hover { background: #dc2626; border-color: #dc2626; }
|
||||
.chat-header .actions .btn-finalizar:hover { background: var(--danger-text); border-color: var(--danger-text); }
|
||||
|
||||
/* Área de mensagens - estilo chat */
|
||||
.messages-area {
|
||||
@@ -280,7 +280,7 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
}
|
||||
.messages-area::-webkit-scrollbar { width: 5px; }
|
||||
.messages-area::-webkit-scrollbar-track { background: transparent; }
|
||||
.messages-area::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 3px; }
|
||||
.messages-area::-webkit-scrollbar-thumb { background: rgba(0,0,0,0.15); border-radius: 4px; }
|
||||
|
||||
/* Bolhas de mensagem */
|
||||
.msg {
|
||||
@@ -297,19 +297,19 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
background: #fff;
|
||||
border-bottom-left-radius: 4px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.08);
|
||||
color: #1f2937;
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.msg.usuario {
|
||||
align-self: flex-end;
|
||||
background: #dcf8c6;
|
||||
color: #1f2937;
|
||||
color: var(--text-primary);
|
||||
border-bottom-right-radius: 4px;
|
||||
box-shadow: 0 1px 2px rgba(0,0,0,0.08);
|
||||
}
|
||||
.msg.sistema {
|
||||
align-self: center;
|
||||
background: rgba(255,255,255,0.75);
|
||||
color: #6b7280;
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
padding: 4px 14px;
|
||||
border-radius: 8px;
|
||||
@@ -338,10 +338,76 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
display: block;
|
||||
}
|
||||
|
||||
/* Estado: enviando */
|
||||
.msg.enviando {
|
||||
opacity: 0.55;
|
||||
}
|
||||
.msg.enviando .hora::after {
|
||||
content: ' ⌛';
|
||||
font-size: 10px;
|
||||
}
|
||||
|
||||
/* Estado: erro ao enviar */
|
||||
.msg.erro {
|
||||
border: 1px solid var(--danger);
|
||||
background: var(--danger-bg);
|
||||
}
|
||||
.msg.erro .msg-erro-acoes {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
margin-top: 6px;
|
||||
align-items: center;
|
||||
font-size: 12px;
|
||||
color: var(--danger-text);
|
||||
}
|
||||
.msg.erro .btn-reenviar {
|
||||
padding: 3px 10px;
|
||||
border: 1px solid var(--danger-text);
|
||||
border-radius: 6px;
|
||||
background: #fff;
|
||||
color: var(--danger-text);
|
||||
font-size: 11px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all .15s;
|
||||
}
|
||||
.msg.erro .btn-reenviar:hover {
|
||||
background: var(--danger-text);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* Indicador de conexão */
|
||||
.connection-status {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
font-size: 11px;
|
||||
font-weight: 500;
|
||||
padding: 2px 10px;
|
||||
border-radius: 9999px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.connection-status.online {
|
||||
background: var(--success-bg);
|
||||
color: var(--success-text);
|
||||
}
|
||||
.connection-status.offline {
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger-text);
|
||||
}
|
||||
.connection-status .conn-dot {
|
||||
width: 7px;
|
||||
height: 7px;
|
||||
border-radius: 50%;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.connection-status.online .conn-dot { background: var(--success); }
|
||||
.connection-status.offline .conn-dot { background: var(--danger); animation: pulse 1.5s infinite; }
|
||||
|
||||
/* Input de chat */
|
||||
.chat-input {
|
||||
padding: 10px 14px;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
border-top: 1px solid var(--border);
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
align-items: flex-end;
|
||||
@@ -351,20 +417,20 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
.chat-input textarea {
|
||||
flex: 1;
|
||||
padding: 10px 16px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 22px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 9999px;
|
||||
font-size: 14px;
|
||||
resize: none;
|
||||
outline: none;
|
||||
max-height: 120px;
|
||||
font-family: inherit;
|
||||
background: #fff;
|
||||
color: #1f2937;
|
||||
color: var(--text-primary);
|
||||
line-height: 1.4;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.chat-input textarea:focus { border-color: #667eea; }
|
||||
.chat-input textarea::placeholder { color: #9ca3af; }
|
||||
.chat-input textarea:focus { border-color: var(--primary); }
|
||||
.chat-input textarea::placeholder { color: var(--text-faint); }
|
||||
|
||||
.chat-input .btn-enviar {
|
||||
width: 40px;
|
||||
@@ -383,10 +449,18 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
padding: 0;
|
||||
}
|
||||
.chat-input .btn-enviar:hover { background: #22c55e; transform: scale(1.06); }
|
||||
.chat-input .btn-enviar:disabled {
|
||||
opacity: 0.35;
|
||||
cursor: not-allowed;
|
||||
transform: none;
|
||||
}
|
||||
.chat-input .btn-enviar:disabled:hover {
|
||||
background: #25d366;
|
||||
transform: none;
|
||||
}
|
||||
|
||||
.chat-input .btn-midia,
|
||||
.chat-input .btn-audio,
|
||||
.chat-input .privada-toggle {
|
||||
/* Botões de ação agrupados no menu "+" */
|
||||
.chat-input .btn-action {
|
||||
width: 36px;
|
||||
height: 36px;
|
||||
display: flex;
|
||||
@@ -396,18 +470,89 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
border-radius: 50%;
|
||||
background: transparent;
|
||||
cursor: pointer;
|
||||
font-size: 20px;
|
||||
color: #6b7280;
|
||||
font-size: 18px;
|
||||
color: var(--text-muted);
|
||||
transition: all .15s;
|
||||
padding: 0;
|
||||
}
|
||||
.chat-input .btn-midia:hover,
|
||||
.chat-input .btn-audio:hover { background: #e5e7eb; color: #374151; }
|
||||
.chat-input .privada-toggle.ativa { background: #fef3c7; color: #d97706; }
|
||||
.chat-input .privada-toggle:hover { background: #fef9c3; color: #d97706; }
|
||||
.chat-input .btn-action:hover { background: var(--border); color: var(--text-secondary); }
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.chat-input .btn-action {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 20px;
|
||||
}
|
||||
.chat-input .btn-enviar {
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 20px;
|
||||
}
|
||||
}
|
||||
|
||||
/* Menu "+" dropdown */
|
||||
.chat-input .action-menu-wrap {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.chat-input .action-menu {
|
||||
display: none;
|
||||
position: absolute;
|
||||
bottom: 100%;
|
||||
left: 0;
|
||||
margin-bottom: 6px;
|
||||
background: #fff;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
box-shadow: 0 4px 16px rgba(0,0,0,0.10);
|
||||
padding: 6px;
|
||||
z-index: 50;
|
||||
min-width: 160px;
|
||||
}
|
||||
.chat-input .action-menu.show { display: block; }
|
||||
.chat-input .action-menu button {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
width: 100%;
|
||||
padding: 8px 12px;
|
||||
border: none;
|
||||
border-radius: 6px;
|
||||
background: transparent;
|
||||
font-size: 13px;
|
||||
cursor: pointer;
|
||||
color: var(--text-secondary);
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
transition: background .15s;
|
||||
}
|
||||
.chat-input .action-menu button:hover { background: var(--surface-3); }
|
||||
|
||||
/* Toggle de mensagem privada com texto */
|
||||
.chat-input .privada-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 4px;
|
||||
padding: 6px 12px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 9999px;
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
font-size: 12px;
|
||||
font-weight: 500;
|
||||
color: var(--text-secondary);
|
||||
transition: all .15s;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.chat-input .privada-toggle:hover { background: var(--surface-3); }
|
||||
.chat-input .privada-toggle.ativa { background: var(--warning-bg); color: #d97706; border-color: #fde047; }
|
||||
.chat-input .privada-toggle .priv-icon { font-size: 14px; }
|
||||
|
||||
/* Gravação */
|
||||
.chat-input .btn-audio.recording {
|
||||
background: #fef2f2;
|
||||
color: #ef4444;
|
||||
background: var(--danger-bg);
|
||||
color: var(--danger);
|
||||
animation: pulse 1s infinite;
|
||||
}
|
||||
@keyframes pulse { 0%{transform:scale(1)} 50%{transform:scale(1.15)} 100%{transform:scale(1)} }
|
||||
@@ -416,7 +561,7 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
.sidebar-right {
|
||||
width: 280px;
|
||||
background: #fff;
|
||||
border-left: 1px solid #e5e7eb;
|
||||
border-left: 1px solid var(--border);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-shrink: 0;
|
||||
@@ -424,23 +569,52 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
}
|
||||
.sidebar-right .header {
|
||||
padding: 14px 16px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
background: #f9fafb;
|
||||
border-bottom: 1px solid var(--border);
|
||||
background: var(--surface-2);
|
||||
flex-shrink: 0;
|
||||
}
|
||||
.sidebar-right .header h3 {
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: #6b7280;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.07em;
|
||||
}
|
||||
|
||||
/* Accordions no painel direito */
|
||||
.sidebar-right .accordion-toggle {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
padding: 10px 16px;
|
||||
border: none;
|
||||
border-bottom: 1px solid var(--surface-3);
|
||||
background: none;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
color: var(--text-muted);
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.07em;
|
||||
cursor: pointer;
|
||||
transition: background .15s;
|
||||
}
|
||||
.sidebar-right .accordion-toggle:hover { background: var(--surface-2); }
|
||||
.sidebar-right .accordion-toggle .arrow {
|
||||
font-size: 10px;
|
||||
transition: transform .15s;
|
||||
}
|
||||
.sidebar-right .accordion-toggle.open .arrow { transform: rotate(90deg); }
|
||||
.sidebar-right .accordion-body {
|
||||
display: none;
|
||||
}
|
||||
.sidebar-right .accordion-body.open { display: block; }
|
||||
.sidebar-right .cliente-foto {
|
||||
width: 70px;
|
||||
height: 70px;
|
||||
border-radius: 50%;
|
||||
margin: 18px auto 10px;
|
||||
background: linear-gradient(135deg, #667eea, #764ba2);
|
||||
background: linear-gradient(135deg, var(--primary), #764ba2);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
@@ -454,46 +628,46 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
.sidebar-right .cliente-foto img { width: 100%; height: 100%; object-fit: cover; }
|
||||
.sidebar-right .info-section {
|
||||
padding: 10px 16px;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
border-bottom: 1px solid var(--surface-3);
|
||||
}
|
||||
.sidebar-right .info-section .label {
|
||||
font-size: 10px;
|
||||
text-transform: uppercase;
|
||||
color: #9ca3af;
|
||||
color: var(--text-faint);
|
||||
letter-spacing: 0.07em;
|
||||
font-weight: 600;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
.sidebar-right .info-section .value { font-size: 13px; color: #374151; }
|
||||
.sidebar-right .info-section .value { font-size: 13px; color: var(--text-secondary); }
|
||||
.sidebar-right .info-section select {
|
||||
width: 100%;
|
||||
padding: 6px 10px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border-radius: 7px;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
font-size: 12px;
|
||||
outline: none;
|
||||
color: #374151;
|
||||
color: var(--text-secondary);
|
||||
background: #fff;
|
||||
cursor: pointer;
|
||||
transition: border-color .15s;
|
||||
}
|
||||
.sidebar-right .info-section select:focus { border-color: #667eea; }
|
||||
.sidebar-right .info-section .inadimplente { color: #ef4444; font-weight: 600; }
|
||||
.sidebar-right .info-section .adimplente { color: #059669; font-weight: 600; }
|
||||
.sidebar-right .info-section select:focus { border-color: var(--primary); }
|
||||
.sidebar-right .info-section .inadimplente { color: var(--danger); font-weight: 600; }
|
||||
.sidebar-right .info-section .adimplente { color: var(--success); font-weight: 600; }
|
||||
.sidebar-right .acoes { padding: 10px 12px; display: flex; flex-direction: column; gap: 6px; }
|
||||
.sidebar-right .acoes button {
|
||||
padding: 9px 12px;
|
||||
border: 1px solid #e5e7eb;
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 8px;
|
||||
background: #fff;
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
text-align: left;
|
||||
color: #374151;
|
||||
color: var(--text-secondary);
|
||||
transition: all .15s;
|
||||
}
|
||||
.sidebar-right .acoes button:hover { background: #f3f4f6; border-color: #d1d5db; }
|
||||
.sidebar-right .acoes button:hover { background: var(--surface-3); border-color: #d1d5db; }
|
||||
.sidebar-right .acoes button .icon { margin-right: 6px; }
|
||||
.sidebar-right .labels-section { padding: 10px 16px; }
|
||||
.sidebar-right .labels-section .label-tag {
|
||||
@@ -535,20 +709,20 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
from { opacity: 0; transform: scale(0.96) translateY(-8px); }
|
||||
to { opacity: 1; transform: scale(1) translateY(0); }
|
||||
}
|
||||
.modal h3 { margin-bottom: 14px; font-size: 16px; font-weight: 700; color: #111827; }
|
||||
.modal h3 { margin-bottom: 14px; font-size: 16px; font-weight: 700; color: var(--text-primary); }
|
||||
.modal input, .modal select {
|
||||
width: 100%;
|
||||
padding: 9px 13px;
|
||||
border: 2px solid #e5e7eb;
|
||||
border: 2px solid var(--border);
|
||||
border-radius: 8px;
|
||||
font-size: 14px;
|
||||
margin-bottom: 12px;
|
||||
outline: none;
|
||||
transition: border-color .15s;
|
||||
background: #f9fafb;
|
||||
color: #1f2937;
|
||||
background: var(--surface-2);
|
||||
color: var(--text-primary);
|
||||
}
|
||||
.modal input:focus { border-color: #667eea; background: #fff; box-shadow: 0 0 0 3px rgba(102,126,234,0.1); }
|
||||
.modal input:focus { border-color: var(--primary); background: #fff; box-shadow: 0 0 0 3px rgba(102,126,234,0.1); }
|
||||
.modal .btn-group { display: flex; gap: 8px; justify-content: flex-end; margin-top: 4px; }
|
||||
.modal .btn-group button {
|
||||
padding: 9px 18px;
|
||||
@@ -556,14 +730,14 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
font-size: 13px;
|
||||
font-weight: 500;
|
||||
cursor: pointer;
|
||||
border: 1px solid #e5e7eb;
|
||||
border: 1px solid var(--border);
|
||||
background: #fff;
|
||||
color: #374151;
|
||||
color: var(--text-secondary);
|
||||
transition: all .15s;
|
||||
}
|
||||
.modal .btn-group button:hover { background: #f3f4f6; }
|
||||
.modal .btn-group .btn-primary { background: #667eea; color: #fff; border-color: #667eea; }
|
||||
.modal .btn-group .btn-primary:hover { background: #5a67d8; border-color: #5a67d8; }
|
||||
.modal .btn-group button:hover { background: var(--surface-3); }
|
||||
.modal .btn-group .btn-primary { background: var(--primary); color: #fff; border-color: var(--primary); }
|
||||
.modal .btn-group .btn-primary:hover { background: var(--primary-dark); border-color: var(--primary-dark); }
|
||||
|
||||
/* ===== DRAG OVERLAY ===== */
|
||||
.drag-overlay {
|
||||
@@ -572,12 +746,12 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
top: 0; left: 0;
|
||||
width: 100%; height: 100%;
|
||||
background: rgba(102,126,234,0.12);
|
||||
border: 3px dashed #667eea;
|
||||
border: 3px dashed var(--primary);
|
||||
z-index: 999;
|
||||
pointer-events: none;
|
||||
font-size: 22px;
|
||||
font-weight: 600;
|
||||
color: #667eea;
|
||||
color: var(--primary);
|
||||
}
|
||||
.drag-overlay.show { display: flex; align-items: center; justify-content: center; }
|
||||
|
||||
@@ -588,12 +762,12 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-direction: column;
|
||||
color: #9ca3af;
|
||||
color: var(--text-faint);
|
||||
gap: 6px;
|
||||
user-select: none;
|
||||
}
|
||||
.empty-state .icon { font-size: 52px; opacity: 0.45; }
|
||||
.empty-state p { font-size: 15px; color: #6b7280; }
|
||||
.empty-state p { font-size: 15px; color: var(--text-muted); }
|
||||
|
||||
/* Botões só do mobile (voltar / info) */
|
||||
.mobile-only { display: none; }
|
||||
@@ -623,17 +797,44 @@ body { background:#f3f4f6; display:flex; height:100vh; overflow:hidden; }
|
||||
.chat-header { gap: 8px; }
|
||||
.chat-header .info h3 { font-size: 14px; }
|
||||
}
|
||||
|
||||
/* ===== DARK MODE: Novos componentes ===== */
|
||||
body.dark-mode .connection-status.online { background: #0a2e0a; color: #4ade80; }
|
||||
body.dark-mode .connection-status.offline { background: #2e0a0a; color: #ff6b6b; }
|
||||
body.dark-mode .action-menu { background: #16213e; border-color: #0f3460; }
|
||||
body.dark-mode .action-menu button { color: #e0e0e0; }
|
||||
body.dark-mode .action-menu button:hover { background: #1a2744; }
|
||||
body.dark-mode .privada-toggle { background: #1a1a2e; border-color: #0f3460; color: #888; }
|
||||
body.dark-mode .privada-toggle:hover { background: #16213e; }
|
||||
body.dark-mode .privada-toggle.ativa { background: #3a2e0a; color: #ffd700; border-color: #5a4a0a; }
|
||||
body.dark-mode .accordion-toggle { color: #888; border-bottom-color: #0f3460; }
|
||||
body.dark-mode .accordion-toggle:hover { background: #1a2744; }
|
||||
body.dark-mode .msg.erro { background: #2e0a0a; border-color: #8b0000; }
|
||||
body.dark-mode .msg.erro .msg-erro-acoes { color: #ff6b6b; }
|
||||
body.dark-mode .msg.erro .btn-reenviar { background: #1a1a2e; border-color: #8b0000; color: #ff6b6b; }
|
||||
body.dark-mode .msg.erro .btn-reenviar:hover { background: #8b0000; color: #fff; }
|
||||
body.dark-mode .msg.enviando { opacity: 0.4; }
|
||||
</style>
|
||||
<link rel="stylesheet" href="/css/dark-mode.css">
|
||||
<script>function darkModeToggle(){var e=document.body;if(!e)return;var a=localStorage.getItem('chatc2_dark_mode')!=='true';e.classList.toggle('dark-mode',a);localStorage.setItem('chatc2_dark_mode',a?'true':'false');document.querySelectorAll('.dark-mode-toggle').forEach(function(b){b.innerHTML=a?'☀️ Claro':'🌙 Escuro'});}
|
||||
window.darkModeApply=function(a){var e=document.body;if(!e)return;e.classList.toggle('dark-mode',a);localStorage.setItem('chatc2_dark_mode',a?'true':'false');document.querySelectorAll('.dark-mode-toggle').forEach(function(b){b.innerHTML=a?'☀️ Claro':'🌙 Escuro'});};
|
||||
window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')==='true';};</script>
|
||||
<script>if(localStorage.getItem("chatc2_dark_mode")==="true")document.documentElement.classList.add("dark-mode-pending")</script>
|
||||
</head>
|
||||
<body>
|
||||
|
||||
<!-- Drag overlay -->
|
||||
<div class="drag-overlay" id="dragOverlay">📎 Solte o arquivo aqui</div>
|
||||
|
||||
<!-- Modal confirmar finalização -->
|
||||
<div class="modal-overlay" id="modalConfirmarFinalizar">
|
||||
<div class="modal">
|
||||
<h3>⚠️ Finalizar conversa</h3>
|
||||
<p style="font-size:13px;color:#6b7280;margin-bottom:16px">Tem certeza que deseja finalizar este atendimento? A conversa será movida para finalizadas.</p>
|
||||
<div class="btn-group">
|
||||
<button onclick="fecharModal('modalConfirmarFinalizar')">Cancelar</button>
|
||||
<button class="btn-primary" onclick="fecharModal('modalConfirmarFinalizar');finalizarConversa()">Finalizar</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Modal associar cliente -->
|
||||
<div class="modal-overlay" id="modalAssociar">
|
||||
<div class="modal">
|
||||
@@ -696,14 +897,20 @@ window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')
|
||||
|
||||
<div class="chat-center">
|
||||
<div class="chat-header" id="chatHeader" style="display:none">
|
||||
<button class="mobile-only" onclick="voltarLista()" title="Voltar" style="background:none;border:none;font-size:20px;cursor:pointer;padding:4px 6px;color:#374151">←</button>
|
||||
<button class="mobile-only" onclick="voltarLista()" title="Voltar" style="background:none;border:none;font-size:20px;cursor:pointer;padding:4px 6px;color:#374151" aria-label="Voltar para lista de conversas">←</button>
|
||||
<div class="info">
|
||||
<h3 id="chatNome"></h3>
|
||||
<span id="chatStatus"></span>
|
||||
<span class="connection-status online" id="connectionStatus" style="display:none">
|
||||
<span class="conn-dot"></span>
|
||||
<span id="connectionLabel">Conectado</span>
|
||||
</span>
|
||||
<h3 id="chatNome"></h3>
|
||||
<span id="chatStatus"></span>
|
||||
</div>
|
||||
<div class="actions">
|
||||
<button class="mobile-only" onclick="toggleInfo()" title="Informações">ℹ️</button>
|
||||
<button class="btn-finalizar" onclick="finalizarConversa()">✅ Finalizar</button>
|
||||
<button class="mobile-only" onclick="toggleInfo()" title="Informações" aria-label="Informações do cliente">ℹ️</button>
|
||||
<button class="btn-finalizar" onclick="confirmarFinalizar()" aria-label="Finalizar conversa">✅ Finalizar</button>
|
||||
</div>
|
||||
</div>
|
||||
<div class="messages-area" id="messagesArea">
|
||||
@@ -713,16 +920,23 @@ window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')
|
||||
</div>
|
||||
</div>
|
||||
<div class="chat-input" id="chatInput" style="display:none">
|
||||
<button class="privada-toggle" id="privadaToggle" onclick="togglePrivada()" title="Mensagem privada">🔓</button>
|
||||
<button class="btn-midia" onclick="document.getElementById('fileInput').click()" title="Anexar arquivo">📎</button>
|
||||
<button class="btn-audio" id="btnAudio" onclick="toggleGravacao()" title="Gravar áudio">🎤</button>
|
||||
<input type="file" id="fileInput" style="display:none" accept="image/*,audio/*" onchange="enviarMidia(this)">
|
||||
<textarea id="msgInput" placeholder="Digite uma mensagem..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();enviarMensagem()}"></textarea>
|
||||
<button class="btn-enviar" onclick="enviarMensagem()" title="Enviar">➤</button>
|
||||
<button class="privada-toggle" id="privadaToggle" onclick="togglePrivada()" title="Alternar mensagem privada" aria-label="Mensagem privada">
|
||||
<span class="priv-icon">🔒</span> <span id="privadaLabel">Normal</span>
|
||||
</button>
|
||||
<div class="action-menu-wrap">
|
||||
<button class="btn-action" id="btnPlus" onclick="toggleActionMenu()" title="Mais ações" aria-label="Mais ações">+</button>
|
||||
<div class="action-menu" id="actionMenu">
|
||||
<button onclick="document.getElementById('fileInput').click();toggleActionMenu()" aria-label="Anexar arquivo">📎 Anexar</button>
|
||||
<button id="btnAudio" class="btn-audio" onclick="toggleGravacao();toggleActionMenu()" aria-label="Gravar áudio">🎤 Gravar</button>
|
||||
</div>
|
||||
</div>
|
||||
<input type="file" id="fileInput" style="display:none" accept="image/*,audio/*" onchange="enviarMidia(this)" aria-label="Selecionar arquivo">
|
||||
<textarea id="msgInput" placeholder="Digite uma mensagem..." rows="1" onkeydown="if(event.key==='Enter'&&!event.shiftKey){event.preventDefault();enviarMensagem()}" oninput="toggleBtnEnviar()" aria-label="Mensagem de texto"></textarea>
|
||||
<button class="btn-enviar" id="btnEnviar" onclick="enviarMensagem()" title="Enviar mensagem" aria-label="Enviar mensagem" disabled>➤</button>
|
||||
<div id="audioPreview" style="display:none;align-items:center;gap:8px;flex:1">
|
||||
<audio controls style="height:38px;flex:1" id="audioPreviewPlayer"></audio>
|
||||
<button class="btn-enviar" onclick="enviarAudioGravado()" title="Enviar áudio">➤</button>
|
||||
<button class="btn-midia" onclick="cancelarAudioGravado()" title="Cancelar" style="background:#fef2f2;color:#ef4444">✕</button>
|
||||
<audio controls style="height:38px;flex:1" id="audioPreviewPlayer" aria-label="Pré-visualização do áudio"></audio>
|
||||
<button class="btn-enviar" onclick="enviarAudioGravado()" title="Enviar áudio" aria-label="Enviar áudio gravado">➤</button>
|
||||
<button class="btn-action" onclick="cancelarAudioGravado()" title="Cancelar" aria-label="Cancelar gravação" style="background:#fef2f2;color:#ef4444;width:36px;height:36px">✕</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -730,43 +944,57 @@ window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')
|
||||
<div class="sidebar-right" id="sidebarRight" style="display:none">
|
||||
<div class="header"><h3>📋 Informações</h3></div>
|
||||
<div class="cliente-foto" id="clienteFoto">?</div>
|
||||
<div class="info-section" id="clienteInfoContainer"></div>
|
||||
|
||||
<button class="accordion-toggle open" onclick="toggleAccordion(this,'accordionCliente')">
|
||||
DADOS DO CLIENTE <span class="arrow">▸</span>
|
||||
</button>
|
||||
<div class="accordion-body open" id="accordionCliente">
|
||||
<div class="info-section" id="clienteInfoContainer"></div>
|
||||
</div>
|
||||
|
||||
<!-- Fluxo de Resolução (aparece conforme configuração da empresa) -->
|
||||
<div class="info-section" id="resolucaoPanel" style="display:none">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:6px">RESOLUÇÃO DO ATENDIMENTO</div>
|
||||
<div id="motivoWrap" style="display:none;margin-bottom:8px">
|
||||
<select id="selectMotivo" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none">
|
||||
<option value="">Selecione o motivo...</option>
|
||||
<!-- Fluxo de Resolução -->
|
||||
<button class="accordion-toggle" id="accResolucaoToggle" style="display:none" onclick="toggleAccordion(this,'accordionResolucao')">
|
||||
RESOLUÇÃO <span class="arrow">▸</span>
|
||||
</button>
|
||||
<div class="accordion-body" id="accordionResolucao">
|
||||
<div class="info-section" id="resolucaoPanel" style="display:none">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:6px">RESOLUÇÃO DO ATENDIMENTO</div>
|
||||
<div id="motivoWrap" style="display:none;margin-bottom:8px">
|
||||
<select id="selectMotivo" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none">
|
||||
<option value="">Selecione o motivo...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="resolucaoWrap" style="display:none">
|
||||
<textarea id="campoResolucao" rows="3" placeholder="Descreva a resolução do atendimento..." style="width:100%;padding:8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none;resize:vertical;font-family:inherit"></textarea>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<button class="accordion-toggle open" onclick="toggleAccordion(this,'accordionClassificacao')">
|
||||
CLASSIFICAÇÃO <span class="arrow">▸</span>
|
||||
</button>
|
||||
<div class="accordion-body open" id="accordionClassificacao">
|
||||
<div class="info-section">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:4px">ATENDENTE</div>
|
||||
<select id="selectAtendente" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none" onchange="mudarAtendente(this)">
|
||||
<option value="">Selecione...</option>
|
||||
</select>
|
||||
</div>
|
||||
<div id="resolucaoWrap" style="display:none">
|
||||
<textarea id="campoResolucao" rows="3" placeholder="Descreva a resolução do atendimento..." style="width:100%;padding:8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none;resize:vertical;font-family:inherit"></textarea>
|
||||
<div class="info-section">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:4px">EQUIPE</div>
|
||||
<select id="selectEquipe" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none" onchange="mudarEquipe(this)">
|
||||
<option value="">Sem equipe</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="labels-section" id="labelsContainer">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:4px">ETIQUETAS</div>
|
||||
<div id="etiquetasAtivas" style="margin-bottom:6px"></div>
|
||||
<select id="selectEtiqueta" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none" onchange="adicionarEtiqueta(this)">
|
||||
<option value="">Adicionar etiqueta...</option>
|
||||
</select>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:4px">ATENDENTE</div>
|
||||
<select id="selectAtendente" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none" onchange="mudarAtendente(this)">
|
||||
<option value="">Selecione...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="info-section">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:4px">EQUIPE</div>
|
||||
<select id="selectEquipe" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none" onchange="mudarEquipe(this)">
|
||||
<option value="">Sem equipe</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="labels-section" id="labelsContainer">
|
||||
<div class="label" style="font-size:10px;text-transform:uppercase;color:#9ca3af;margin-bottom:4px">ETIQUETAS</div>
|
||||
<div id="etiquetasAtivas" style="margin-bottom:6px"></div>
|
||||
<select id="selectEtiqueta" style="width:100%;padding:6px 8px;border:1px solid #e5e7eb;border-radius:6px;font-size:12px;outline:none" onchange="adicionarEtiqueta(this)">
|
||||
<option value="">Adicionar etiqueta...</option>
|
||||
</select>
|
||||
</div>
|
||||
|
||||
<div class="acoes" id="acoesContainer">
|
||||
<button onclick="mostrarModalAssociar()"><span class="icon">🔗</span> Associar</button>
|
||||
<button onclick="irPaginaCliente()"><span class="icon">📋</span> Dados do Titular</button>
|
||||
@@ -818,10 +1046,18 @@ async function carregarConfigResolucao() {
|
||||
}
|
||||
function aplicarPainelResolucao(conv) {
|
||||
var panel = document.getElementById('resolucaoPanel');
|
||||
var accToggle = document.getElementById('accResolucaoToggle');
|
||||
var accBody = document.getElementById('accordionResolucao');
|
||||
if (!panel) return;
|
||||
var verMot = cfgResolucao.motivoVisualizar === 'S';
|
||||
var verRes = cfgResolucao.resolucaoVisualizar === 'S';
|
||||
panel.style.display = (verMot || verRes) ? 'block' : 'none';
|
||||
var mostrar = verMot || verRes;
|
||||
panel.style.display = mostrar ? 'block' : 'none';
|
||||
if (accToggle) accToggle.style.display = mostrar ? 'flex' : 'none';
|
||||
if (mostrar && accBody) {
|
||||
accToggle.classList.add('open');
|
||||
accBody.classList.add('open');
|
||||
}
|
||||
document.getElementById('motivoWrap').style.display = verMot ? 'block' : 'none';
|
||||
document.getElementById('resolucaoWrap').style.display = verRes ? 'block' : 'none';
|
||||
if (verMot) {
|
||||
@@ -1316,6 +1552,12 @@ window.enviarMensagem = async function() {
|
||||
|
||||
var privada = document.getElementById('privadaToggle').classList.contains('ativa') ? 'S' : 'N';
|
||||
|
||||
// Mostrar mensagem temporária "enviando"
|
||||
var tempId = 'temp-' + Date.now();
|
||||
var tempMsg = criarMsgTemp(texto, privada === 'S', tempId);
|
||||
document.getElementById('messagesArea').appendChild(tempMsg);
|
||||
scrollToBottom();
|
||||
|
||||
input.value = '';
|
||||
input.style.height = 'auto';
|
||||
|
||||
@@ -1327,10 +1569,67 @@ window.enviarMensagem = async function() {
|
||||
});
|
||||
var data = await res.json();
|
||||
if (data.success) {
|
||||
// Remove temporária e recarrega mensagens
|
||||
var el = document.getElementById(tempId);
|
||||
if (el) el.remove();
|
||||
carregarMensagens();
|
||||
carregarConversas();
|
||||
} else {
|
||||
// Mostrar estado de erro na mensagem temporária
|
||||
var el = document.getElementById(tempId);
|
||||
if (el) {
|
||||
el.classList.remove('enviando');
|
||||
el.classList.add('erro');
|
||||
el.querySelector('.msg-erro-acoes').style.display = 'flex';
|
||||
}
|
||||
}
|
||||
} catch(e) {}
|
||||
} catch(e) {
|
||||
var el = document.getElementById(tempId);
|
||||
if (el) {
|
||||
el.classList.remove('enviando');
|
||||
el.classList.add('erro');
|
||||
el.querySelector('.msg-erro-acoes').style.display = 'flex';
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// ===== CRIAR MENSAGEM TEMPORÁRIA =====
|
||||
window.criarMsgTemp = function(texto, privada, id) {
|
||||
var div = document.createElement('div');
|
||||
div.id = id;
|
||||
div.className = 'msg usuario enviando';
|
||||
if (privada) div.className += ' privada';
|
||||
div.innerHTML = esc(texto).replace(/\n/g, '<br>') +
|
||||
'<span class="hora">agora ⌛</span>' +
|
||||
'<div class="msg-erro-acoes" style="display:none">⚠️ Falha ao enviar. <button class="btn-reenviar" onclick="reenviarMensagem(\'' + id + '\',\'' + esc(texto).replace(/'/g,"\\'") + '\',' + (privada ? 'true' : 'false') + ')">Reenviar</button></div>';
|
||||
return div;
|
||||
};
|
||||
|
||||
window.reenviarMensagem = function(oldId, texto, privada) {
|
||||
var el = document.getElementById(oldId);
|
||||
if (el) el.remove();
|
||||
var input = document.getElementById('msgInput');
|
||||
input.value = texto;
|
||||
if (privada && !document.getElementById('privadaToggle').classList.contains('ativa')) {
|
||||
togglePrivada();
|
||||
} else if (!privada && document.getElementById('privadaToggle').classList.contains('ativa')) {
|
||||
togglePrivada();
|
||||
}
|
||||
enviarMensagem();
|
||||
};
|
||||
|
||||
window.scrollToBottom = function() {
|
||||
var area = document.getElementById('messagesArea');
|
||||
if (area) area.scrollTop = area.scrollHeight;
|
||||
};
|
||||
|
||||
// ===== HABILITAR/DESABILITAR BOTÃO ENVIAR =====
|
||||
window.toggleBtnEnviar = function() {
|
||||
var input = document.getElementById('msgInput');
|
||||
var btn = document.getElementById('btnEnviar');
|
||||
if (input && btn) {
|
||||
btn.disabled = !input.value.trim();
|
||||
}
|
||||
};
|
||||
|
||||
// ===== ENVIAR MÍDIA =====
|
||||
@@ -1369,7 +1668,7 @@ window.toggleGravacao = async function() {
|
||||
if (gravando) {
|
||||
if (mediaRecorder && mediaRecorder.state !== "inactive") mediaRecorder.stop();
|
||||
btn.classList.remove("recording");
|
||||
btn.textContent = "🎤";
|
||||
btn.innerHTML = "🎤 Gravar";
|
||||
gravando = false;
|
||||
return;
|
||||
}
|
||||
@@ -1407,7 +1706,7 @@ window.toggleGravacao = async function() {
|
||||
document.getElementById("msgInput").style.display = "none";
|
||||
document.querySelector(".btn-enviar").style.display = "none";
|
||||
document.getElementById("btnAudio").style.display = "none";
|
||||
document.querySelector(".btn-midia").style.display = "none";
|
||||
document.getElementById("btnPlus").style.display = "none";
|
||||
document.getElementById("privadaToggle").style.display = "none";
|
||||
|
||||
// Converte para base64
|
||||
@@ -1420,7 +1719,7 @@ window.toggleGravacao = async function() {
|
||||
|
||||
mediaRecorder.start();
|
||||
btn.classList.add("recording");
|
||||
btn.textContent = "⏹️";
|
||||
btn.innerHTML = "⏹️ Parar";
|
||||
gravando = true;
|
||||
} catch(err) {
|
||||
alert("Erro ao acessar microfone: " + err.message);
|
||||
@@ -1457,7 +1756,7 @@ window.cancelarAudioGravado = function() {
|
||||
document.getElementById("msgInput").style.display = "";
|
||||
document.querySelector(".btn-enviar").style.display = "";
|
||||
document.getElementById("btnAudio").style.display = "";
|
||||
document.querySelector(".btn-midia").style.display = "";
|
||||
document.getElementById("btnPlus").style.display = "";
|
||||
document.getElementById("privadaToggle").style.display = "";
|
||||
};
|
||||
|
||||
@@ -1495,7 +1794,7 @@ window.finalizarConversa = async function() {
|
||||
}
|
||||
}
|
||||
|
||||
if (!confirm('Finalizar esta conversa?')) return;
|
||||
// Confirmação já foi feita pelo modal — segue direto
|
||||
try {
|
||||
var res = await fetch('/api/' + alias + '/conversations/' + conversaAtiva + '/finalize', {
|
||||
method: 'POST',
|
||||
@@ -1584,11 +1883,68 @@ window.enviarBoleto = async function(carneId, btn) {
|
||||
};
|
||||
|
||||
window.togglePrivada = function() {
|
||||
document.getElementById('privadaToggle').classList.toggle('ativa');
|
||||
var btn = document.getElementById('privadaToggle');
|
||||
btn.textContent = btn.classList.contains('ativa') ? '🔒' : '🔓';
|
||||
btn.classList.toggle('ativa');
|
||||
var isPriv = btn.classList.contains('ativa');
|
||||
document.getElementById('privadaLabel').textContent = isPriv ? 'Privada' : 'Normal';
|
||||
btn.querySelector('.priv-icon').textContent = isPriv ? '🔓' : '🔒';
|
||||
};
|
||||
|
||||
// ===== FINALIZAR COM CONFIRMAÇÃO =====
|
||||
window.confirmarFinalizar = function() {
|
||||
document.getElementById('modalConfirmarFinalizar').classList.add('show');
|
||||
};
|
||||
|
||||
// ===== MENU DE AÇÕES =====
|
||||
window.toggleActionMenu = function() {
|
||||
document.getElementById('actionMenu').classList.toggle('show');
|
||||
};
|
||||
|
||||
// ===== ACCORDION DO PAINEL DIREITO =====
|
||||
window.toggleAccordion = function(btn, bodyId) {
|
||||
btn.classList.toggle('open');
|
||||
document.getElementById(bodyId).classList.toggle('open');
|
||||
};
|
||||
|
||||
// ===== ATALHOS DE TECLADO =====
|
||||
document.addEventListener('keydown', function(e) {
|
||||
// Ctrl+J / Ctrl+K: navegar entre conversas
|
||||
if ((e.ctrlKey || e.metaKey) && (e.key === 'j' || e.key === 'k')) {
|
||||
e.preventDefault();
|
||||
var items = document.querySelectorAll('.conv-item');
|
||||
if (!items.length) return;
|
||||
var idx = -1;
|
||||
for (var i = 0; i < items.length; i++) {
|
||||
if (items[i].classList.contains('active')) { idx = i; break; }
|
||||
}
|
||||
if (e.key === 'j') idx = Math.min(idx + 1, items.length - 1);
|
||||
else idx = Math.max(idx - 1, 0);
|
||||
if (idx >= 0 && items[idx]) items[idx].click();
|
||||
}
|
||||
// Ctrl+F: focar busca de conversas
|
||||
if ((e.ctrlKey || e.metaKey) && e.key === 'f' && document.activeElement !== document.getElementById('msgInput')) {
|
||||
e.preventDefault();
|
||||
var s = document.getElementById('searchConv');
|
||||
if (s) { s.focus(); s.select(); }
|
||||
}
|
||||
// Esc: fechar modais e menus
|
||||
if (e.key === 'Escape') {
|
||||
document.getElementById('actionMenu').classList.remove('show');
|
||||
document.querySelectorAll('.modal-overlay.show').forEach(function(m) {
|
||||
m.classList.remove('show');
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
// Fechar menu de ações ao clicar fora
|
||||
document.addEventListener('click', function(e) {
|
||||
var menu = document.getElementById('actionMenu');
|
||||
var btn = document.getElementById('btnPlus');
|
||||
if (menu && btn && !menu.contains(e.target) && !btn.contains(e.target)) {
|
||||
menu.classList.remove('show');
|
||||
}
|
||||
});
|
||||
|
||||
// ===== ASSOCIAR CLIENTE =====
|
||||
window.mostrarModalAssociar = function() {
|
||||
document.getElementById('modalAssociar').classList.add('show');
|
||||
@@ -1777,5 +2133,8 @@ document.getElementById('msgInput').addEventListener('input', function() {
|
||||
})();
|
||||
</script>
|
||||
<script src="/js/dark-mode.js"></script>
|
||||
<!-- impeccable-live-start -->
|
||||
<script src="http://localhost:8400/live.js"></script>
|
||||
<!-- impeccable-live-end -->
|
||||
</body>
|
||||
</html>
|
||||
|
||||
Reference in New Issue
Block a user