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:
@@ -6,30 +6,28 @@
|
||||
<title>Conversas - Chatc2</title>
|
||||
<link rel="stylesheet" href="/css/main.css">
|
||||
<style>
|
||||
body { background:#f3f4f6; display:flex; min-height:100vh; }
|
||||
body { background:var(--surface-3); display:flex; min-height:100vh; }
|
||||
.sidebar-nav .admin-only { display:none; }
|
||||
.main { flex:1; display:flex; flex-direction:column; min-width:0; }
|
||||
.container { flex:1; padding:24px; overflow-y:auto; }
|
||||
.filters { display:flex; gap:10px; margin-bottom:20px; flex-wrap:wrap; align-items:center; }
|
||||
.filters select, .filters input { padding:9px 14px; border:2px solid #e5e7eb; border-radius:8px; font-size:13px; outline:none; background:#fff; color:#374151; transition:border-color .15s; }
|
||||
.filters select:focus, .filters input:focus { border-color:#667eea; }
|
||||
.filters button { padding:9px 16px; background:#667eea; color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; transition:background .15s; }
|
||||
.filters button:hover { background:#5a67d8; }
|
||||
.filters select, .filters input { padding:9px 14px; border:2px solid var(--border); border-radius:8px; font-size:13px; outline:none; background:#fff; color:var(--text-secondary); transition:border-color .15s; }
|
||||
.filters select:focus, .filters input:focus { border-color:var(--primary); }
|
||||
.filters button { padding:9px 16px; background:var(--primary); color:#fff; border:none; border-radius:8px; font-size:13px; font-weight:600; cursor:pointer; transition:background .15s; }
|
||||
.filters button:hover { background:var(--primary-dark); }
|
||||
.status-badge { display:inline-block; padding:3px 10px; border-radius:10px; font-size:11px; font-weight:600; }
|
||||
.status-E { background:#fef3c7; color:#92400e; }
|
||||
.status-A { background:#d1fae5; color:#065f46; }
|
||||
.status-F { background:#f3f4f6; color:#6b7280; }
|
||||
.atender-btn { padding:5px 12px; border:none; border-radius:6px; background:#667eea; color:#fff; font-size:12px; font-weight:600; cursor:pointer; text-decoration:none; transition:background .15s; display:inline-block; }
|
||||
.atender-btn:hover { background:#5a67d8; }
|
||||
.loading { text-align:center; padding:40px; color:#9ca3af; }
|
||||
.status-E { background:var(--warning-bg); color:var(--warning-text); }
|
||||
.status-A { background:var(--success-bg); color:var(--success-text); }
|
||||
.status-F { background:var(--surface-3); color:var(--text-muted); }
|
||||
.atender-btn { padding:5px 12px; border:none; border-radius:6px; background:var(--primary); color:#fff; font-size:12px; font-weight:600; cursor:pointer; text-decoration:none; transition:background .15s; display:inline-block; }
|
||||
.atender-btn:hover { background:var(--primary-dark); }
|
||||
.loading { text-align:center; padding:40px; color:var(--text-faint); }
|
||||
</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>
|
||||
<aside class="sidebar">
|
||||
<aside class="sidebar" role="navigation" aria-label="Navegação principal">
|
||||
<div class="sidebar-brand"><div class="logo">C2</div><div><h2>Chatc2</h2><span id="sidebarAlias">-</span></div></div>
|
||||
<nav class="sidebar-nav">
|
||||
<div class="nav-label">Principal</div>
|
||||
@@ -41,16 +39,17 @@ window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')
|
||||
<a href="#" id="navRoutes" class="admin-only"><span class="icon">📡</span> Rotas</a>
|
||||
</nav>
|
||||
<div class="sidebar-footer">
|
||||
<button class="dark-mode-toggle" onclick="darkModeToggle()" style="width:100%;margin-bottom:8px;padding:8px">🌙 Escuro</button>
|
||||
<a onclick="logout()"><span class="icon">🚪</span> Sair</a></div>
|
||||
<button class="dark-mode-toggle" onclick="darkModeToggle()" style="width:100%;margin-bottom:8px;padding:8px" aria-label="Alternar modo escuro">🌙 Escuro</button>
|
||||
<a onclick="logout()" aria-label="Sair do sistema"><span class="icon">🚪</span> Sair</a></div>
|
||||
</aside>
|
||||
<div class="main">
|
||||
<main class="main" id="main-content">
|
||||
<div class="topbar">
|
||||
<span class="topbar-title">💬 Todas as Conversas</span>
|
||||
<span id="totalInfo" style="font-size:13px;color:#6b7280"></span>
|
||||
</div>
|
||||
<div class="container">
|
||||
<div class="filters">
|
||||
<label for="filterStatus" class="sr-only">Status</label>
|
||||
<select id="filterStatus" onchange="carregar()">
|
||||
<option value="A,E">Em aberto</option>
|
||||
<option value="E">Em espera</option>
|
||||
@@ -58,13 +57,15 @@ window.darkModeIsDark=function(){return localStorage.getItem('chatc2_dark_mode')
|
||||
<option value="F">Finalizadas</option>
|
||||
<option value="A,E,F">Todas</option>
|
||||
</select>
|
||||
<label for="filterAtribuicao" class="sr-only">Atribuição</label>
|
||||
<select id="filterAtribuicao" onchange="carregar()">
|
||||
<option value="">Todas</option>
|
||||
<option value="me">Minhas conversas</option>
|
||||
<option value="sem">Sem atendente</option>
|
||||
</select>
|
||||
<label for="buscaContato" class="sr-only">Buscar contato</label>
|
||||
<input type="text" id="buscaContato" placeholder="Buscar por nome ou número..." onkeyup="if(event.key==='Enter')carregar()">
|
||||
<button onclick="carregar()">🔄 Atualizar</button>
|
||||
<button onclick="carregar()" aria-label="Atualizar lista">🔄 Atualizar</button>
|
||||
</div>
|
||||
<div class="card" id="tabelaContainer">
|
||||
<div class="loading"><div class="spinner"></div><p style="margin-top:8px">Carregando conversas...</p></div>
|
||||
@@ -151,7 +152,7 @@ window.carregar = async function() {
|
||||
convs.forEach(function(c) {
|
||||
const statusLabel = c.status === 'E' ? '🟡 Espera' : c.status === 'A' ? '🟢 Atendimento' : '⚫ Finalizado';
|
||||
const labelsHtml = (c.labels || []).map(function(l) {
|
||||
return '<span style="display:inline-block;padding:0 6px;border-radius:3px;background:'+(l.cor||'#667eea')+';color:#fff;font-size:10px;margin:1px">'+(l.nome||'')+'</span>';
|
||||
return '<span style="display:inline-block;padding:0 6px;border-radius:4px;background:'+(l.cor||'#667eea')+';color:#fff;font-size:10px;margin:1px">'+(l.nome||'')+'</span>';
|
||||
}).join('');
|
||||
const ultima = (c.ultimaMsg || '').substring(0, 60);
|
||||
const usuarioNome = c.usuarioId ? 'Usuário ' + c.usuarioId : '-';
|
||||
@@ -178,5 +179,8 @@ setInterval(carregar, 10000);
|
||||
})();
|
||||
</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