12d419de7c
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
212 lines
6.1 KiB
HTML
212 lines
6.1 KiB
HTML
<!DOCTYPE html>
|
|
<html lang="pt-BR">
|
|
<head>
|
|
<meta charset="UTF-8">
|
|
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
<title>Avalie seu Atendimento</title>
|
|
<style>
|
|
* { margin:0; padding:0; box-sizing:border-box; }
|
|
body {
|
|
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
|
min-height: 100vh;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
padding: 20px;
|
|
}
|
|
.container {
|
|
background: #fff;
|
|
border-radius: 20px;
|
|
padding: 40px;
|
|
max-width: 480px;
|
|
width: 100%;
|
|
text-align: center;
|
|
box-shadow: 0 20px 60px rgba(0,0,0,0.15);
|
|
}
|
|
.logo { font-size: 48px; margin-bottom: 16px; }
|
|
h1 { font-size: 24px; color: var(--text-primary); margin-bottom: 8px; }
|
|
.subtitle { font-size: 14px; color: var(--text-muted); margin-bottom: 32px; }
|
|
|
|
.stars {
|
|
display: flex;
|
|
justify-content: center;
|
|
gap: 8px;
|
|
margin-bottom: 32px;
|
|
direction: rtl;
|
|
}
|
|
.stars input { display: none; }
|
|
.stars label {
|
|
font-size: 48px;
|
|
cursor: pointer;
|
|
color: #d1d5db;
|
|
transition: color .2s, transform .15s;
|
|
}
|
|
.stars label:hover,
|
|
.stars label:hover ~ label,
|
|
.stars input:checked ~ label {
|
|
color: #f59e0b;
|
|
transform: scale(1.1);
|
|
}
|
|
.stars input:checked + label {
|
|
color: #f59e0b;
|
|
}
|
|
|
|
textarea {
|
|
width: 100%;
|
|
padding: 14px 16px;
|
|
border: 2px solid var(--border);
|
|
border-radius: 12px;
|
|
font-size: 14px;
|
|
font-family: inherit;
|
|
resize: vertical;
|
|
min-height: 80px;
|
|
outline: none;
|
|
transition: border-color .2s;
|
|
margin-bottom: 20px;
|
|
}
|
|
textarea:focus { border-color: var(--primary); }
|
|
|
|
.btn {
|
|
width: 100%;
|
|
padding: 14px;
|
|
background: linear-gradient(135deg, var(--primary) 0%, #764ba2 100%);
|
|
color: #fff;
|
|
border: none;
|
|
border-radius: 12px;
|
|
font-size: 16px;
|
|
font-weight: 600;
|
|
cursor: pointer;
|
|
transition: opacity .2s;
|
|
}
|
|
.btn:hover { opacity: .9; }
|
|
.btn:disabled { opacity: .5; cursor: not-allowed; }
|
|
|
|
.rating-text {
|
|
font-size: 14px;
|
|
color: var(--text-muted);
|
|
margin-bottom: 24px;
|
|
min-height: 20px;
|
|
}
|
|
|
|
.success { display: none; }
|
|
.success .icon { font-size: 64px; margin-bottom: 16px; }
|
|
.success h2 { color: var(--success); margin-bottom: 8px; }
|
|
.success p { color: var(--text-muted); }
|
|
|
|
.erro {
|
|
color: var(--danger);
|
|
font-size: 14px;
|
|
margin-top: 12px;
|
|
display: none;
|
|
}
|
|
</style>
|
|
</head>
|
|
<body>
|
|
<div class="container" id="app">
|
|
<div class="logo">💬</div>
|
|
<h1>Avalie seu Atendimento</h1>
|
|
<p class="subtitle">Sua opinião é muito importante para melhorarmos nosso serviço</p>
|
|
|
|
<div class="rating-text" id="ratingText">Toque nas estrelas para avaliar</div>
|
|
|
|
<div class="stars" id="starContainer">
|
|
<input type="radio" name="star" id="star5" value="5">
|
|
<label for="star5" title="Excelente">★</label>
|
|
<input type="radio" name="star" id="star4" value="4">
|
|
<label for="star4" title="Bom">★</label>
|
|
<input type="radio" name="star" id="star3" value="3">
|
|
<label for="star3" title="Regular">★</label>
|
|
<input type="radio" name="star" id="star2" value="2">
|
|
<label for="star2" title="Ruim">★</label>
|
|
<input type="radio" name="star" id="star1" value="1">
|
|
<label for="star1" title="Péssimo">★</label>
|
|
</div>
|
|
|
|
<textarea id="comentario" placeholder="Deixe seu comentário (opcional)..."></textarea>
|
|
<button class="btn" id="btnEnviar" onclick="enviar()">Enviar Avaliação</button>
|
|
<div class="erro" id="erro"></div>
|
|
|
|
<div class="success" id="success">
|
|
<div class="icon">✅</div>
|
|
<h2>Agradecemos sua avaliação!</h2>
|
|
<p>Seu feedback nos ajuda a melhorar cada vez mais.</p>
|
|
</div>
|
|
</div>
|
|
|
|
<script>
|
|
var alias, conversaId, empresaId;
|
|
|
|
function getParams() {
|
|
var params = new URLSearchParams(window.location.search);
|
|
alias = params.get('alias');
|
|
conversaId = params.get('conversa');
|
|
empresaId = params.get('empresa');
|
|
|
|
if (!alias || !conversaId || !empresaId) {
|
|
document.getElementById('erro').textContent = 'Link inválido. Entre em contato conosco.';
|
|
document.getElementById('erro').style.display = 'block';
|
|
document.querySelector('.stars').style.display = 'none';
|
|
document.querySelector('textarea').style.display = 'none';
|
|
document.getElementById('btnEnviar').style.display = 'none';
|
|
}
|
|
}
|
|
|
|
var nota = 0;
|
|
document.querySelectorAll('.stars input').forEach(function(input) {
|
|
input.addEventListener('change', function() {
|
|
nota = parseInt(this.value);
|
|
var textos = ['', 'Péssimo', 'Ruim', 'Regular', 'Bom', 'Excelente!'];
|
|
document.getElementById('ratingText').textContent = textos[nota] || '';
|
|
});
|
|
});
|
|
|
|
async function enviar() {
|
|
if (nota === 0) {
|
|
document.getElementById('erro').textContent = 'Selecione uma avaliação de 1 a 5 estrelas.';
|
|
document.getElementById('erro').style.display = 'block';
|
|
return;
|
|
}
|
|
|
|
var btn = document.getElementById('btnEnviar');
|
|
btn.disabled = true;
|
|
btn.textContent = 'Enviando...';
|
|
document.getElementById('erro').style.display = 'none';
|
|
|
|
try {
|
|
var resp = await fetch('/api/' + alias + '/csat/avaliar', {
|
|
method: 'POST',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
conversaId: parseInt(conversaId),
|
|
empresaId: parseInt(empresaId),
|
|
nota: nota,
|
|
comentario: document.getElementById('comentario').value.trim()
|
|
})
|
|
});
|
|
var data = await resp.json();
|
|
if (data.success) {
|
|
document.getElementById('app').querySelector('.stars').style.display = 'none';
|
|
document.querySelector('textarea').style.display = 'none';
|
|
document.getElementById('btnEnviar').style.display = 'none';
|
|
document.getElementById('ratingText').style.display = 'none';
|
|
document.getElementById('success').style.display = 'block';
|
|
} else {
|
|
throw new Error(data.error || 'Erro ao enviar');
|
|
}
|
|
} catch(e) {
|
|
document.getElementById('erro').textContent = 'Erro ao enviar: ' + e.message;
|
|
document.getElementById('erro').style.display = 'block';
|
|
btn.disabled = false;
|
|
btn.textContent = 'Enviar Avaliação';
|
|
}
|
|
}
|
|
|
|
getParams();
|
|
</script>
|
|
<!-- impeccable-live-start -->
|
|
<script src="http://localhost:8400/live.js"></script>
|
|
<!-- impeccable-live-end -->
|
|
</body>
|
|
</html>
|