Hope: O Primeiro Impacto
<!DOCTYPE html>
<html lang="pt">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>HEINEKEN ON TRADE - GESTÃO</title>
<!-- Google Fonts & FontAwesome Icons -->
<link href="https://fonts.googleapis.com/css2?family=Roboto:wght@300;400;700&family=Playfair+Display:ital,wght@1,700&display=swap" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
:root {
--heineken-green: #00824A;
--heineken-red: #D61A22;
--dark-gray: #111111;
--light-gray: rgba(244, 246, 249, 0.9);
--white: #ffffff;
--warning-yellow: #ffc107;
--gold: #FFD700;
}
* {
box-sizing: border-box;
margin: 0;
padding: 0;
font-family: 'Roboto', sans-serif;
}
body {
background: linear-gradient(rgba(5, 22, 12, 0.6), rgba(5, 22, 12, 0.8)),
url('46174.jpg') no-repeat center center fixed;
background-size: cover;
color: #eeeeee;
overflow-x: hidden;
position: relative;
padding-top: 80px;
min-height: 100vh;
}
/* CABEÇALHO */
header {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 70px;
background-color: #00824A !important;
color: #ffffff !important;
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
box-shadow: 0 4px 15px rgba(0,0,0,0.5);
border-bottom: 3px solid var(--heineken-red);
z-index: 9999 !important;
}
.header-title {
display: flex;
align-items: center;
gap: 12px;
}
header h1 {
color: #ffffff !important;
font-size: 1.2rem;
font-weight: 800;
letter-spacing: 1.5px;
text-shadow: 1px 1px 3px rgba(0, 0, 0, 0.8);
}
@media (min-width: 576px) {
header h1 { font-size: 1.4rem; }
}
.heineken-star {
color: var(--heineken-red) !important;
font-size: 1.8rem;
animation: star-glow 2s infinite alternate;
}
@keyframes star-glow {
from { text-shadow: 0 0 5px rgba(214, 26, 34, 0.5); transform: scale(1); }
to { text-shadow: 0 0 15px rgba(214, 26, 34, 1), 0 0 25px rgba(214, 26, 34, 0.8); transform: scale(1.15); }
}
.user-panel {
display: flex;
align-items: center;
gap: 10px;
background: rgba(255, 255, 255, 0.2);
padding: 6px 12px;
border-radius: 20px;
}
.user-panel label {
font-size: 0.85rem;
font-weight: bold;
color: #ffffff;
}
.user-panel select {
background: var(--white);
color: var(--dark-gray);
border: none;
padding: 4px 8px;
border-radius: 4px;
font-weight: bold;
outline: none;
cursor: pointer;
}
main {
padding: 20px;
max-width: 1200px;
margin: 0 auto;
z-index: 1;
}
.welcome-box {
background-color: rgba(255, 255, 255, 0.95);
color: var(--dark-gray);
padding: 20px;
border-radius: 12px;
margin-bottom: 25px;
box-shadow: 0 4px 15px rgba(0,0,0,0.2);
display: flex;
justify-content: space-between;
align-items: center;
flex-wrap: wrap;
gap: 15px;
}
.welcome-box h2 {
font-size: 1.2rem;
color: var(--heineken-green);
}
.welcome-box p {
font-size: 0.85rem;
color: #444;
margin-top: 4px;
}
.action-buttons {
display: flex;
gap: 10px;
flex-wrap: wrap;
}
.dashboard-layout {
display: grid;
grid-template-columns: 1.6fr 1.4fr;
gap: 20px;
}
@media (max-width: 992px) {
.dashboard-layout {
grid-template-columns: 1fr;
}
}
.section-card {
background: rgba(255, 255, 255, 0.95);
color: var(--dark-gray);
padding: 22px;
border-radius: 12px;
box-shadow: 0 8px 20px rgba(0,0,0,0.15);
margin-bottom: 20px;
border-top: 4px solid var(--heineken-green);
}
.section-card h2 {
margin-bottom: 15px;
font-size: 1.15rem;
color: var(--heineken-green);
border-bottom: 2px solid #ddd;
padding-bottom: 8px;
display: flex;
align-items: center;
gap: 8px;
}
.form-grid {
display: grid;
grid-template-columns: 1fr 1fr;
gap: 12px;
}
@media (max-width: 768px) {
.form-grid { grid-template-columns: 1fr; }
}
.input-group {
text-align: left;
}
.input-group label {
display: block;
font-size: 0.8rem;
margin-bottom: 4px;
font-weight: bold;
color: #444;
}
.input-group input, .input-group select {
width: 100%;
padding: 11px;
border: 1px solid #ccc;
border-radius: 6px;
font-size: 0.95rem;
outline: none;
background: #fff;
}
.input-group input:focus, .input-group select:focus {
border-color: var(--heineken-green);
box-shadow: 0 0 5px rgba(0, 130, 74, 0.3);
}
.btn {
padding: 12px;
background-color: var(--heineken-green);
color: var(--white);
border: none;
border-radius: 6px;
font-size: 0.95rem;
font-weight: bold;
cursor: pointer;
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
transition: background 0.3s ease;
}
.btn:hover:not(:disabled) { opacity: 0.9; }
.btn:disabled { background-color: #cccccc; cursor: not-allowed; }
.btn-full { width: 100%; }
.btn-warning { background-color: var(--warning-yellow); color: #212529; }
.btn-secondary { background-color: #555; color: var(--white); }
.btn-gold { background-color: #b8860b; color: white; }
.quality-box {
margin-top: 8px;
padding: 10px;
border-radius: 6px;
display: none;
font-size: 0.85rem;
font-weight: bold;
}
.quality-good { background-color: #d4edda; color: #155724; border: 1px solid #c3e6cb; }
.quality-bad { background-color: #f8d7da; color: #721c24; border: 1px solid #f5c6cb; }
.brinde-box {
background-color: #e2f0d9;
color: #385723;
border: 1px solid #c5e0b4;
padding: 12px;
border-radius: 6px;
font-weight: bold;
font-size: 0.95rem;
margin-top: 5px;
display: flex;
align-items: center;
gap: 10px;
}
.ranking-list { list-style: none; }
.ranking-item {
display: flex;
justify-content: space-between;
align-items: center;
padding: 12px 10px;
border-bottom: 1px solid #eee;
font-size: 0.9rem;
}
.ranking-item.lider {
background: linear-gradient(135deg, #fff9e6 0%, #fff2cc 100%);
border-radius: 8px;
font-weight: bold;
border: 1.5px solid var(--gold);
}
.sales-board {
display: grid;
grid-template-columns: 1fr;
gap: 10px;
margin-top: 10px;
}
.sales-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px 15px;
background: #f8f9fa;
border-left: 4px solid #ccc;
border-radius: 4px;
font-size: 0.9rem;
}
.sales-row.winner {
background: #e2f0d9;
border-left-color: var(--heineken-green);
font-weight: bold;
}
.table-responsive { overflow-x: auto; }
table { width: 100%; border-collapse: collapse; margin-top: 10px; }
th, td { text-align: left; padding: 12px; border-bottom: 1px solid #ddd; font-size: 0.85rem; }
th { background-color: rgba(244, 246, 249, 0.8); color: #333; font-weight: bold; }
.badge { padding: 3.5px 7px; border-radius: 12px; font-size: 0.75rem; font-weight: bold; }
.badge-success { background-color: #d4edda; color: #155724; }
.badge-danger { background-color: #f8d7da; color: #721c24; }
.img-preview { width: 45px; height: 45px; object-fit: cover; border-radius: 6px; border: 1px solid #ccc; cursor: pointer; }
/* MODAL DO CERTIFICADO HORIZONTAL (LANDSCAPE) */
#certificateModal {
display: none;
position: fixed;
top: 0; left: 0; width: 100%; height: 100%;
background: rgba(0,0,0,0.85);
z-index: 10000;
justify-content: center;
align-items: center;
padding: 20px;
overflow-y: auto;
}
.certificate-container {
background: #fff;
color: #333;
width: 100%;
max-width: 1050px; /* Largura estendida para proporção horizontal 4:3 / 16:9 */
padding: 35px 50px;
border: 15px double #00824a;
border-radius: 4px;
text-align: center;
position: relative;
box-shadow: 0 10px 30px rgba(0,0,0,0.5);
display: flex;
flex-direction: column;
justify-content: space-between;
min-height: 680px;
}
/* Layout interno com a imagem lateral */
.cert-main-layout {
display: flex;
gap: 25px;
align-items: center;
text-align: center;
flex: 1;
}
.cert-image-left {
width: 110px;
height: auto;
object-fit: contain;
flex-shrink: 0;
}
.cert-content-area {
flex-grow: 1;
}
.cert-top-logo {
max-width: 380px;
height: auto;
margin: 0 auto 15px auto;
display: block;
object-fit: contain;
}
.cert-header {
font-family: 'Playfair Display', serif;
font-size: 2.4rem;
color: #00824a;
margin-bottom: 5px;
font-weight: 700;
}
.cert-sub {
font-size: 1.1rem;
text-transform: uppercase;
letter-spacing: 3px;
color: #d61a22;
margin-bottom: 25px;
font-weight: bold;
}
.cert-body {
font-size: 1.2rem;
line-height: 1.6;
margin-bottom: 25px;
color: #444;
}
.cert-body strong {
color: #00824a;
font-weight: 700;
}
/* ASSINATURAS EM TRÊS COLUNAS */
.cert-footer-signatures {
display: grid;
grid-template-columns: 1fr 1fr 1fr;
gap: 20px;
margin-top: 30px;
border-top: 1.5px solid #ddd;
padding-top: 20px;
font-size: 0.85rem;
}
.sig-block {
display: flex;
flex-direction: column;
align-items: center;
justify-content: flex-end;
}
.sig-line {
width: 80%;
border-top: 1px solid #888;
margin-bottom: 8px;
}
.sig-title {
font-weight: bold;
color: #222;
}
.sig-sub {
font-size: 0.75rem;
color: #666;
}
/* CONFIGURAÇÃO ESPECÍFICA PARA IMPRESSÃO HORIZONTAL */
@media print {
@page {
size: landscape;
margin: 0;
}
body * {
visibility: hidden;
}
#certificateModal, #certificateModal * {
visibility: visible;
}
#certificateModal {
position: absolute;
left: 0;
top: 0;
width: 100vw;
height: 100vh;
background: #fff;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
}
.certificate-container {
border: 15px double #00824a;
box-shadow: none;
width: 100%;
height: 100%;
max-width: 100%;
min-height: auto;
padding: 40px;
box-sizing: border-box;
}
.btn-print-action {
display: none !important;
}
}
</style>
</head>
<body>
<!-- HEADER -->
<header>
<div class="header-title">
<i class="fa-solid fa-star heineken-star"></i>
<h1>HEINEKEN ON TRADE</h1>
</div>
<div class="user-panel">
<label for="supervisorFilter"><i class="fa fa-user-tie"></i> Supervisor:</label>
<select id="supervisorFilter" onchange="mudarSupervisor()"></select>
</div>
</header>
<main>
<div class="welcome-box">
<div>
<h2>Painel Ativo de Ativações</h2>
<p>Registe e controle as vendas em campo e monitorize o desempenho de cada PDV.</p>
</div>
<div class="action-buttons">
<button class="btn btn-secondary" onclick="exportarDados()"><i class="fa-solid fa-download"></i> Backup</button>
<button id="syncBtn" class="btn btn-warning" onclick="sincronizarDados()"><i class="fa-solid fa-rotate"></i> Sinc (<span id="pendingCount">0</span>)</button>
</div>
</div>
<div class="dashboard-layout">
<!-- COLUNA ESQUERDA: FORMULÁRIO -->
<div>
<div class="section-card">
<h2><i class="fa fa-file-pen"></i> Registar Consumo do Cliente</h2>
<div class="form-grid">
<div class="input-group">
<label for="pdvSelector">Ponto de Venda (PDV)</label>
<select id="pdvSelector" onchange="atualizarRankingDoPdv()"></select>
</div>
<div class="input-group">
<label for="clienteNomeInput">Nome ou Mesa do Cliente</label>
<input type="text" id="clienteNomeInput" placeholder="Ex: Mesa 4" oninput="validarFormulario()">
</div>
<div class="input-group">
<label for="vendasInput">Cervejas Compradas</label>
<input type="number" id="vendasInput" min="1" placeholder="Ex: 4" oninput="calcularBrindesSugeridos(); validarFormulario();">
</div>
<div class="input-group">
<label for="tempSelector">Temperatura</label>
<select id="tempSelector">
<option value="Gelada">Estupidamente Gelada</option>
<option value="Fria">Fria</option>
<option value="Quente">Quente</option>
</select>
</div>
<div class="input-group" style="grid-column: span 2;">
<label>Brinde Imediato Merecido</label>
<div id="brindeSugeridoBox" class="brinde-box">
<i class="fa-solid fa-gift"></i> <span id="textoBrinde">Insira a quantidade...</span>
</div>
<input type="hidden" id="brindeFinalValue" value="">
</div>
<div class="input-group" style="grid-column: span 2;">
<label for="photoInput"><i class="fa-solid fa-camera"></i> Foto de Validação</label>
<input type="file" id="photoInput" accept="image/*" onchange="analisarQualidadeFoto(this)">
<div id="photoFeedback" class="quality-box"></div>
</div>
</div>
<button id="btnSalvarRegisto" class="btn btn-full" style="margin-top: 15px;" onclick="guardarConsumoLocal()" disabled>
<i class="fa fa-plus"></i> Preencha os Dados Obrigatórios
</button>
</div>
</div>
<!-- COLUNA DIREITA: MONITOR E RECONHECIMENTO -->
<div>
<div class="section-card">
<h2><i class="fa-solid fa-chart-line"></i> Controlo de Vendas por PDV</h2>
<div id="painelControloPDVs" class="sales-board"></div>
<button class="btn btn-full btn-gold" style="margin-top: 15px;" onclick="gerarCertificadoVencedor()">
<i class="fa-solid fa-award"></i> Emitir Certificado do Líder
</button>
</div>
<div class="section-card">
<h2><i class="fa-solid fa-trophy" style="color: var(--gold);"></i> Ranking de Mesas - PDV Ativo</h2>
<div id="rankingContainer">
<ul class="ranking-list" id="rankingListBody">
<li class="ranking-item" style="color: #888; text-align: center;">Sem vendas.</li>
</ul>
</div>
</div>
</div>
</div>
<!-- HISTÓRICO GLOBAL -->
<div class="section-card">
<h2><i class="fa-solid fa-list-check"></i> Histórico Completo de Registos</h2>
<div class="table-responsive">
<table>
<thead>
<tr>
<th>PDV</th>
<th>Foto</th>
<th>Promotores</th>
<th>Rep Sales</th>
<th>Cliente</th>
<th>Qtd Vendas</th>
<th>Brinde</th>
<th>Geleira</th>
<th>Sinc</th>
</tr>
</thead>
<tbody id="historicoTableBody"></tbody>
</table>
</div>
</div>
</main>
<!-- POP-UP DE CERTIFICADO EM FORMATO HORIZONTAL (LANDSCAPE) -->
<div id="certificateModal" onclick="fecharCertificado(event)">
<div class="certificate-container" onclick="event.stopPropagation()">
<!-- Logotipo do topo (Imagem 46205.jpg recortada conceitualmente ou inteira como cabeçalho) -->
<img src="46205.jpg" alt="Heineken Premium Quality" class="cert-top-logo">
<div class="cert-main-layout">
<!-- Logotipo Vertical da lateral esquerda (Imagem 46206.jpg) -->
<img src="46206.jpg" alt="Heineken" class="cert-image-left">
<!-- Área principal de Texto -->
<div class="cert-content-area">
<div class="cert-header">Certificado de Excelência</div>
<div class="cert-sub">Heineken On Trade Mozambique</div>
<div class="cert-body">
Este certificado é conferido com orgulho à equipa do ponto de venda:<br>
<strong id="certPdvName" style="font-size: 2rem; display: block; margin: 12px 0;">[Nome do PDV]</strong>
Pelo desempenho extraordinário, profissionalismo e liderança absoluta de vendas, registando um volume total de:
<strong id="certPdvTotal" style="font-size: 1.6rem; display: block; margin: 8px 0;">0 Cervejas Vendidas</strong>
<p style="font-size: 1.05rem; margin-top: 15px;">
Parabéns à equipa de Promotores: <strong id="certPromotores">--</strong>
</p>
</div>
</div>
</div>
<!-- Rodapé de Assinaturas (Três campos para garantir a validação oficial) -->
<div class="cert-footer-signatures">
<div class="sig-block">
<div class="sig-line"></div>
<span class="sig-title" id="certSupervisor">--</span>
<span class="sig-sub">Supervisor de Operações</span>
</div>
<div class="sig-block">
<div class="sig-line"></div>
<span class="sig-title">Helio Herasmo Cabral</span>
<span class="sig-sub">Rep Sales Heineken</span>
</div>
<div class="sig-block">
<div class="sig-line"></div>
<span class="sig-title">Direção de Trade Marketing</span>
<span class="sig-sub">Heineken Moçambique</span>
</div>
</div>
<button class="btn btn-full btn-secondary btn-print-action" style="margin-top: 25px;" onclick="window.print()">
<i class="fa-solid fa-print"></i> Imprimir Certificado (Horizontal)
</button>
</div>
</div>
<script>
const listaPDVs = [
{ "pdv": "Cervejaria Imperial", "promotores": "Texeira / Isabel Mussa", "supervisor": "Jessica Rafael", "rep": "Neutel" },
{ "pdv": "Sagres", "promotores": "Bejamim / Clotildes", "supervisor": "Jessica Rafael", "rep": "Neutel" },
{ "pdv": "Xima", "promotores": "Cintia karina / Gerson", "supervisor": "Mario", "rep": "Neutel" },
{ "pdv": "Volare", "promotores": "João Bonga / Nasma", "supervisor": "Agnaldo", "rep": "Neutel" },
{ "pdv": "Campo das Acacias", "promotores": "Cintia Malo / Idilson", "supervisor": "Monicia", "rep": "Neutel" },
{ "pdv": "Cantinho dos Artistas", "promotores": "Edilson / Carolina", "supervisor": "Tânia", "rep": "Neutel" },
{ "pdv": "Infinitos", "promotores": "Laque / Cândidoa", "supervisor": "Arnaldo", "rep": "Neutel" },
{ "pdv": "Guetto", "promotores": "Ermelinda / Eugénio", "supervisor": "Augusto", "rep": "Nelson" },
{ "pdv": "Nobre Paladar", "promotores": "Amarildo / Julina", "supervisor": "Telvino", "rep": "Jacinto" },
{ "pdv": "Witbank", "promotores": "Elsa / Alexandre", "supervisor": "Fred Junior", "rep": "Delivia" },
{ "pdv": "The Woods", "promotores": "Banu / Samuel", "supervisor": "Cleid", "rep": "Delivia" },
{ "pdv": "Babalaza", "promotores": "Glaucia / Leonildo", "supervisor": "Celma Mabunda", "rep": "Cintia" }
];
let historicoConsumo = [];
let fotoValida = false;
let fotoBase64 = "";
if (localStorage.getItem("heineken_consumo_planilha")) {
try { historicoConsumo = JSON.parse(localStorage.getItem("heineken_consumo_planilha")); } catch (e) { historicoConsumo = []; }
}
window.onload = function() {
carregarFiltroSupervisores();
mudarSupervisor();
};
function validarFormulario() {
const cliente = document.getElementById("clienteNomeInput").value.trim();
const qtd = parseInt(document.getElementById("vendasInput").value) || 0;
const btn = document.getElementById("btnSalvarRegisto");
if (cliente !== "" && qtd > 0 && fotoValida) {
btn.disabled = false;
btn.innerHTML = "<i class='fa fa-plus'></i> Guardar Registo";
} else {
btn.disabled = true;
btn.innerHTML = "<i class='fa fa-plus'></i> Preencha os Dados Obrigatórios";
}
}
function atualizarPainelControloPDVs() {
const container = document.getElementById("painelControloPDVs");
container.innerHTML = "";
const resumoPDVs = listaPDVs.map(p => {
const totalVendas = historicoConsumo
.filter(item => item.pdv === p.pdv)
.reduce((acc, curr) => acc + curr.qtd, 0);
return { ...p, total: totalVendas };
}).sort((a, b) => b.total - a.total);
const maxVendas = resumoPDVs[0].total;
resumoPDVs.forEach(p => {
const ehVencedor = p.total === maxVendas && maxVendas > 0;
const rowClass = ehVencedor ? "sales-row winner" : "sales-row";
container.innerHTML += `
<div class="${rowClass}">
<span>${ehVencedor ? '👑 ' : ''}<strong>${p.pdv}</strong> <small>(${p.supervisor})</small></span>
<span><strong>${p.total} un</strong></span>
</div>
`;
});
}
function gerarCertificadoVencedor() {
const resumoPDVs = listaPDVs.map(p => {
const total = historicoConsumo.filter(item => item.pdv === p.pdv).reduce((acc, curr) => acc + curr.qtd, 0);
return { ...p, total };
}).sort((a, b) => b.total - a.total);
if (resumoPDVs.length === 0 || resumoPDVs[0].total === 0) {
alert("Nenhuma venda registada para emitir reconhecimento!");
return;
}
const vencedor = resumoPDVs[0];
document.getElementById("certPdvName").innerText = vencedor.pdv;
document.getElementById("certPdvTotal").innerText = `${vencedor.total} Unidades`;
document.getElementById("certPromotores").innerText = vencedor.promotores;
document.getElementById("certSupervisor").innerText = vencedor.supervisor;
document.getElementById("certificateModal").style.display = "flex";
}
function fecharCertificado(e) {
document.getElementById("certificateModal").style.display = "none";
}
function carregarFiltroSupervisores() {
const filterSelect = document.getElementById("supervisorFilter");
let supervisores = [...new Set(listaPDVs.map(p => p.supervisor))];
let optTodos = document.createElement("option");
optTodos.value = "todos";
optTodos.text = "Todos (Visualizar Tudo)";
filterSelect.add(optTodos);
supervisores.forEach(sup => {
if(sup !== "Sem Supervisor") {
let opt = document.createElement("option");
opt.value = sup;
opt.text = sup;
if(sup.toLowerCase() === "telvino") opt.selected = true;
filterSelect.add(opt);
}
});
}
function mudarSupervisor() {
const supervisorSelecionado = document.getElementById("supervisorFilter").value;
const selectPdv = document.getElementById("pdvSelector");
selectPdv.innerHTML = "";
const pdvsFiltrados = supervisorSelecionado === "todos"
? listaPDVs
: listaPDVs.filter(p => p.supervisor === supervisorSelecionado);
pdvsFiltrados.forEach(p => {
let opt = document.createElement("option");
opt.value = p.pdv;
opt.text = p.pdv;
selectPdv.add(opt);
});
atualizarPainelGeral();
}
function obterTextoBrindePorQtd(quantidade) {
if (quantidade <= 0) return "";
if (quantidade >= 10) {
const blocks = Math.floor(quantidade / 10);
return `${blocks * 2} Cervejas + ${blocks} Copo(s)`;
} else if (quantidade >= 5) {
return "1 Copo";
} else {
return "1 Cerveja";
}
}
function calcularBrindesSugeridos() {
const qtd = parseInt(document.getElementById("vendasInput").value) || 0;
const texto = document.getElementById("textoBrinde");
const brindeFinal = document.getElementById("brindeFinalValue");
if (qtd <= 0) {
text.innerText = "Insira a quantidade de cervejas...";
brindeFinal.value = "";
} else {
const brindeGerado = obterTextoBrindePorQtd(qtd);
texto.innerHTML = qtd >= 10 ? `🎉 Super Prémio: <strong>${brindeGerado}</strong>` : `🎁 Prémio Intermédio: <strong>${brindeGerado}</strong>`;
brindeFinal.value = brindeGerado;
}
}
function analisarQualidadeFoto(input) {
const feedback = document.getElementById("photoFeedback");
feedback.style.display = "block";
if (!input.files || input.files.length === 0) { fotoValida = false; fotoBase64 = ""; validarFormulario(); return; }
const file = input.files[0];
if ((file.size / 1024) < 40) {
feedback.className = "quality-box quality-bad";
feedback.innerHTML = "❌ Imagem muito pequena ou sem foco!";
fotoValida = false;
fotoBase64 = "";
} else {
feedback.className = "quality-box quality-good";
feedback.innerHTML = "✔️ Foto validada!";
fotoValida = true;
const reader = new FileReader();
reader.onload = (e) => { fotoBase64 = e.target.result; };
reader.readAsDataURL(file);
}
validarFormulario();
}
function guardarConsumoLocal() {
const pdvNome = document.getElementById("pdvSelector").value;
const cliente = document.getElementById("clienteNomeInput").value.trim();
const qtd = parseInt(document.getElementById("vendasInput").value) || 0;
const brinde = document.getElementById("brindeFinalValue").value;
const temp = document.getElementById("tempSelector").value;
if (!cliente || qtd <= 0 || !fotoValida) { alert("Preencha todos os campos corretamente com foto válida."); return; }
const pdvInfo = listaPDVs.find(p => p.pdv === pdvNome);
const clienteExistente = historicoConsumo.find(item => item.pdv === pdvNome && item.cliente.toLowerCase() === cliente.toLowerCase());
if (clienteExistente) {
clienteExistente.qtd += qtd;
clienteExistente.brinde = obterTextoBrindePorQtd(clienteExistente.qtd);
if (fotoBase64) clienteExistente.foto = fotoBase64;
clienteExistente.sincronizado = false;
} else {
historicoConsumo.push({
pdv: pdvNome, promotores: pdvInfo.promotores, rep: pdvInfo.rep,
cliente, qtd, brinde: brinde || "Nenhum", temp,
sincronizado: false, supervisor: pdvInfo.supervisor, foto: fotoBase64
});
}
try {
localStorage.setItem("heineken_consumo_planilha", JSON.stringify(historicoConsumo));
alert(`✔️ Salvo! ${cliente} com acumulado de ${clienteExistente ? clienteExistente.qtd : qtd} un.`);
} catch (e) {
alert("Armazenamento cheio! Remova fotos pesadas.");
return;
}
document.getElementById("clienteNomeInput").value = "";
document.getElementById("vendasInput").value = "";
document.getElementById("photoInput").value = "";
document.getElementById("photoFeedback").style.display = "none";
document.getElementById("textoBrinde").innerText = "Insira a quantidade...";
fotoBase64 = "";
fotoValida = false;
validarFormulario();
atualizarPainelGeral();
}
function atualizarRankingDoPdv() {
const pdvAtual = document.getElementById("pdvSelector").value;
const rankingList = document.getElementById("rankingListBody");
rankingList.innerHTML = "";
const consumosDoPdv = historicoConsumo.filter(item => item.pdv === pdvAtual).sort((a, b) => b.qtd - a.qtd);
if (consumosDoPdv.length === 0) {
rankingList.innerHTML = `<li class="ranking-item" style="color: #888; text-align: center;">Nenhuma venda neste PDV.</li>`;
return;
}
const maxCervejas = consumosDoPdv[0].qtd;
consumosDoPdv.forEach((item, index) => {
let ehLider = item.qtd === maxCervejas;
rankingList.innerHTML += `
<li class="${ehLider ? 'ranking-item lider' : 'ranking-item'}">
<div>
<span>${index+1}º ${ehLider ? '👑 ' : ''}${item.cliente}</span>
</div>
<span><strong>${item.qtd} un</strong></span>
</li>
`;
});
}
function atualizarPainelGeral() {
atualizarRankingDoPdv();
atualizarPainelControloPDVs();
const pendentes = historicoConsumo.filter(item => !item.sincronizado).length;
document.getElementById("pendingCount").innerText = pendentes;
const tbody = document.getElementById("historicoTableBody");
tbody.innerHTML = "";
const supervisorSelecionado = document.getElementById("supervisorFilter").value;
const dadosVisiveis = supervisorSelecionado === "todos"
? historicoConsumo
: historicoConsumo.filter(item => item.supervisor === supervisorSelecionado);
if(dadosVisiveis.length === 0) {
tbody.innerHTML = `<tr><td colspan="9" style="text-align:center; color:#888;">Sem registos.</td></tr>`;
return;
}
dadosVisiveis.forEach(item => {
let row = `
<tr>
<td><strong>${item.pdv}</strong></td>
<td>${item.foto ? `<img src="${item.foto}" class="img-preview" onclick="abrirModalFoto('${item.foto}')">` : '<span style="color:#aaa;">Sem foto</span>'}</td>
<td>${item.promotores}</td>
<td>${item.rep}</td>
<td>${item.cliente}</td>
<td><strong>${item.qtd} un</strong></td>
<td>${item.brinde}</td>
<td><span class="badge badge-success">${item.temp}</span></td>
<td><span class="badge ${item.sincronizado ? 'badge-success':'badge-danger'}">${item.sincronizado ? 'Sim':'Não'}</span></td>
</tr>
`;
tbody.innerHTML += row;
});
}
function abrirModalFoto(srcBase64) {
const popup = window.open();
if (popup) popup.document.write(`<iframe src="${srcBase64}" frameborder="0" style="width:100%; height:100%;"></iframe>`);
}
function exportarDados() {
if (historicoConsumo.length === 0) { alert("Sem dados."); return; }
const downloadAnchor = document.createElement('a');
downloadAnchor.setAttribute("href", "data:text/json;charset=utf-8," + encodeURIComponent(JSON.stringify(historicoConsumo, null, 2)));
downloadAnchor.setAttribute("download", `heineken_backup.json`);
document.body.appendChild(downloadAnchor);
downloadAnchor.click();
downloadAnchor.remove();
}
function sincronizarDados() {
const pendentes = historicoConsumo.filter(item => !item.sincronizado);
if (pendentes.length === 0) { alert("Tudo sincronizado!"); return; }
const btn = document.getElementById("syncBtn");
btn.innerHTML = `<i class="fa-solid fa-spinner fa-spin"></i> A enviar...`;
btn.disabled = true;
setTimeout(() => {
historicoConsumo.forEach(item => item.sincronizado = true);
localStorage.setItem("heineken_consumo_planilha", JSON.stringify(historicoConsumo));
alert("🎉 Turno sincronizado com a central!");
btn.disabled = false;
btn.innerHTML = `<i class="fa-solid fa-rotate"></i> Sincronizar (0)`;
atualizarPainelGeral();
}, 1500);
}
</script>
</body>
</html>