// ── CLUBE67 PREMIUM INTERACTIVE SCRIPT ──
document.addEventListener('DOMContentLoaded', () => {
// Initialize Lucide Icons
lucide.createIcons();
// ── STATE MANAGEMENT ──
let activeTab = 'catalog';
let benefits = [];
let activeVoucher = null;
let countdownInterval = null;
let timerTotalSeconds = 300; // 5 minutes
let timerRemainingSeconds = 300;
// Premium Mock Data in case backend returns empty/offline to guarantee flawless display
const mockBenefits = [
{ id: 1, title: "Consulta + Limpeza Geral", description: "Ganhe 50% de desconto na primeira consulta preventiva e profilaxia.", category: "odontologia", partner: "ScoreOdonto MS", icon: "🦷", rating: "4.9", discount: "50% OFF" },
{ id: 2, title: "Checkout Cardiológico Completo", description: "Desconto especial de 30% em exames de eletrocardiograma e ecocardiograma.", category: "saude", partner: "CardioClinic CG", icon: "🩺", rating: "4.8", discount: "30% OFF" },
{ id: 3, title: "Menu Degustação Premium (Casal)", description: "Compre um menu degustação de 5 etapas e ganhe uma garrafa de vinho importado.", category: "gastronomia", partner: "Le Jardin Bistrô", icon: "🍔", rating: "5.0", discount: "Vinho Cortesia" },
{ id: 4, title: "Day Use Completo no Eco Resort", description: "Acesso total às piscinas naturais, trilhas e almoço pantaneiro incluso com 25% OFF.", category: "lazer", partner: "Bonito Eco Resort", icon: "🌴", rating: "4.7", discount: "25% OFF" },
{ id: 5, title: "Fórmulas Manipuladas e Vitaminas", description: "Desconto exclusivo de 20% em qualquer fórmula médica manipulada.", category: "farmacia", partner: "Fórmula Ativa", icon: "💊", rating: "4.9", discount: "20% OFF" },
{ id: 6, title: "MBA Executivo & Especializações", description: "Isenção na taxa de matrícula e 35% de desconto nas mensalidades do curso.", category: "educacao", partner: "Unigran Capital", icon: "📚", rating: "4.6", discount: "35% OFF" }
];
// Analytics Dashboard Data
const analyticsData = {
members: 1248,
redemptions: 384,
savings: 14820,
goalPercentage: 96,
weeklyDistribution: {
seg: 25,
ter: 45,
qua: 70,
qui: 55,
sex: 85,
sab: 100,
dom: 35
}
};
// ── DOM ELEMENTS ──
const navTabs = {
catalog: document.getElementById('tab-catalog'),
redeem: document.getElementById('tab-redeem'),
analytics: document.getElementById('tab-analytics')
};
const sections = {
catalog: document.getElementById('section-catalog'),
redeem: document.getElementById('section-redeem'),
analytics: document.getElementById('section-analytics')
};
const searchInput = document.getElementById('search-input');
const categoryFilters = document.getElementById('category-filters');
const benefitsGrid = document.getElementById('benefits-grid');
// Redeem tab elements
const redeemCardEmpty = document.getElementById('redeem-card-empty');
const redeemCardActive = document.getElementById('redeem-card-active');
const voucherIcon = document.getElementById('voucher-icon');
const voucherTitle = document.getElementById('voucher-title');
const voucherPartner = document.getElementById('voucher-partner');
const voucherToken = document.getElementById('voucher-token');
const qrCodeImg = document.getElementById('qr-code-img');
const timerText = document.getElementById('timer-text');
const timerBar = document.getElementById('timer-bar');
const btnValidate = document.getElementById('btn-validate');
const btnCancel = document.getElementById('btn-cancel');
// Analytics elements
const statMembers = document.getElementById('stat-members');
const statRedemptions = document.getElementById('stat-redemptions');
const statSavings = document.getElementById('stat-savings');
const radialProgressBar = document.getElementById('radial-progress-bar');
const radialPercentage = document.getElementById('radial-percentage');
const barSeg = document.getElementById('bar-seg');
const barTer = document.getElementById('bar-ter');
const barQua = document.getElementById('bar-qua');
const barQui = document.getElementById('bar-qui');
const barSex = document.getElementById('bar-sex');
const barSab = document.getElementById('bar-sab');
const barDom = document.getElementById('bar-dom');
// ── NAVIGATION LOGIC ──
function switchTab(tabId) {
if (activeTab === tabId) return;
// Update tabs active state
Object.keys(navTabs).forEach(key => {
if (key === tabId) {
navTabs[key].classList.add('active');
} else {
navTabs[key].classList.remove('active');
}
});
// Hide old section, show new section
Object.keys(sections).forEach(key => {
if (key === tabId) {
sections[key].classList.remove('hidden');
} else {
sections[key].classList.add('hidden');
}
});
activeTab = tabId;
// Perform specific triggers per tab
if (tabId === 'analytics') {
loadAnalyticsCharts();
}
}
Object.keys(navTabs).forEach(key => {
navTabs[key].addEventListener('click', () => switchTab(key));
});
// ── BENEFIT FETCHING & RENDERING ──
async function loadBenefits() {
try {
// Sincronized backend API consume
const response = await fetch('/api/benefits');
if (response.ok) {
const data = await response.json();
benefits = data && data.length > 0 ? data : mockBenefits;
} else {
benefits = mockBenefits;
}
} catch (err) {
console.warn('[Clube67] API offline, running with premium fallback simulation.');
benefits = mockBenefits;
}
renderBenefits(benefits);
}
function renderBenefits(list) {
const loadingEl = document.getElementById('catalog-loading');
if (loadingEl) loadingEl.remove();
benefitsGrid.innerHTML = '';
if (list.length === 0) {
benefitsGrid.innerHTML = `
Nenhum benefício encontrado para esta categoria ou busca.
`;
lucide.createIcons();
return;
}
list.forEach(benefit => {
const card = document.createElement('div');
card.className = 'benefit-card';
// Mouse moves 3D lighting effect
card.addEventListener('mousemove', e => {
const rect = card.getBoundingClientRect();
const x = e.clientX - rect.left;
const y = e.clientY - rect.top;
card.style.setProperty('--mouse-x', `${x}px`);
card.style.setProperty('--mouse-y', `${y}px`);
});
card.innerHTML = `
${benefit.discount || 'Desconto'}
${benefit.icon || '🎁'}
${benefit.partner || 'Estabelecimento'}
${benefit.title}
${benefit.description}
${benefit.rating || '4.8'}
`;
benefitsGrid.appendChild(card);
});
// Add event listeners to voucher buttons
document.querySelectorAll('.btn-redeem-trigger').forEach(btn => {
btn.addEventListener('click', () => {
const id = parseInt(btn.getAttribute('data-id'), 10);
const selected = benefits.find(b => b.id === id);
if (selected) {
triggerVoucherRedemption(selected);
}
});
});
lucide.createIcons();
}
// ── SEARCH & FILTERING ──
let activeCategory = 'all';
function filterBenefits() {
const query = searchInput.value.toLowerCase().trim();
const filtered = benefits.filter(b => {
const matchesCategory = activeCategory === 'all' || b.category === activeCategory;
const matchesSearch = b.title.toLowerCase().includes(query) ||
b.description.toLowerCase().includes(query) ||
(b.partner && b.partner.toLowerCase().includes(query));
return matchesCategory && matchesSearch;
});
renderBenefits(filtered);
}
searchInput.addEventListener('input', filterBenefits);
categoryFilters.querySelectorAll('.filter-pill').forEach(pill => {
pill.addEventListener('click', () => {
categoryFilters.querySelector('.filter-pill.active').classList.remove('active');
pill.classList.add('active');
activeCategory = pill.getAttribute('data-category');
filterBenefits();
});
});
// ── VOUCHER REDEMPTION & SCANNER TIMER ──
function triggerVoucherRedemption(benefit) {
activeVoucher = benefit;
// Populate DOM elements
voucherIcon.textContent = benefit.icon || '🎁';
voucherTitle.textContent = benefit.title;
voucherPartner.textContent = benefit.partner || 'Estabelecimento';
// Generate dynamic unique token
const randToken = 'C67-' + Math.random().toString(36).substring(2, 7).toUpperCase();
voucherToken.textContent = randToken;
// Update QR Code Source with parameters
qrCodeImg.src = `https://api.qrserver.com/v1/create-qr-code/?size=160x160&data=${randToken}&color=0f172a`;
// Switch to redeem container view
redeemCardEmpty.classList.add('hidden');
redeemCardActive.classList.remove('hidden');
// Switch view tab
switchTab('redeem');
// Start 5 minutes expiration timer
startVoucherTimer();
}
function startVoucherTimer() {
if (countdownInterval) clearInterval(countdownInterval);
timerRemainingSeconds = timerTotalSeconds;
updateTimerDisplay();
countdownInterval = setInterval(() => {
timerRemainingSeconds--;
updateTimerDisplay();
if (timerRemainingSeconds <= 0) {
handleVoucherExpiration();
}
}, 1000);
}
function updateTimerDisplay() {
const min = Math.floor(timerRemainingSeconds / 60);
const sec = timerRemainingSeconds % 60;
timerText.textContent = `${min.toString().padStart(2, '0')}:${sec.toString().padStart(2, '0')}`;
// Progress bar percentage shrink
const pct = (timerRemainingSeconds / timerTotalSeconds) * 100;
timerBar.style.width = `${pct}%`;
// Danger colors for low time
if (timerRemainingSeconds < 60) {
timerBar.className = 'h-full bg-gradient-to-r from-red-500 to-rose-500 transition-all duration-1000';
timerText.className = 'text-red-400 font-mono';
} else {
timerBar.className = 'h-full bg-gradient-to-r from-indigo-500 to-violet-500 transition-all duration-1000';
timerText.className = 'text-indigo-400 font-mono';
}
}
function handleVoucherExpiration() {
clearInterval(countdownInterval);
alert('Este token de resgate expirou por limite de tempo de segurança. Por favor, gere um novo voucher.');
cancelVoucher();
}
function cancelVoucher() {
if (countdownInterval) clearInterval(countdownInterval);
activeVoucher = null;
redeemCardActive.classList.add('hidden');
redeemCardEmpty.classList.remove('hidden');
}
btnCancel.addEventListener('click', cancelVoucher);
// ── VOUCHER VALIDATION SIMULATOR (API CONSUME) ──
btnValidate.addEventListener('click', async () => {
if (!activeVoucher) return;
btnValidate.disabled = true;
btnValidate.textContent = 'VALIDANDO...';
try {
// Dynamic validation call to backend API
const response = await fetch('/api/benefits/validate', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify({
tokenId: voucherToken.textContent,
benefitId: activeVoucher.id
})
});
// Simulated UI validation popup for high wow-factor and offline resilience
setTimeout(() => {
alert(`✅ SUCESSO!
O Voucher "${activeVoucher.title}" foi validado com sucesso e consumido pelo parceiro!
Token: ${voucherToken.textContent}
Estabelecimento: ${activeVoucher.partner || 'Clube67'}`);
// Update analytics counters to reflect live usage!
analyticsData.redemptions++;
analyticsData.savings += 45; // average discount saving value
cancelVoucher();
btnValidate.disabled = false;
btnValidate.textContent = 'Simular Validação (Estabelecimento)';
// Automatically redirect to Analytics to see the metric update live!
switchTab('analytics');
}, 1200);
} catch (err) {
console.error('Validation API fail:', err);
btnValidate.disabled = false;
btnValidate.textContent = 'Simular Validação (Estabelecimento)';
}
});
// ── ANALYTICS CHARTS & COUNTERS ──
function loadAnalyticsCharts() {
// Load live dynamic counters
statMembers.textContent = analyticsData.members.toLocaleString();
statRedemptions.textContent = analyticsData.redemptions.toLocaleString();
statSavings.textContent = `R$ ${analyticsData.savings.toLocaleString('pt-BR')}`;
// Load SVG gauge radial chart progress
const radius = 64;
const circumference = 2 * Math.PI * radius; // 402.12
const pct = analyticsData.goalPercentage;
const offset = circumference - (pct / 100) * circumference;
radialProgressBar.style.strokeDasharray = `${circumference}`;
radialProgressBar.style.strokeDashoffset = `${offset}`;
radialPercentage.textContent = `${pct}%`;
// Load vertical bar charts heights
setTimeout(() => {
barSeg.style.height = `${analyticsData.weeklyDistribution.seg}%`;
barTer.style.height = `${analyticsData.weeklyDistribution.ter}%`;
barQua.style.height = `${analyticsData.weeklyDistribution.qua}%`;
barQui.style.height = `${analyticsData.weeklyDistribution.qui}%`;
barSex.style.height = `${analyticsData.weeklyDistribution.sex}%`;
barSab.style.height = `${analyticsData.weeklyDistribution.sab}%`;
barDom.style.height = `${analyticsData.weeklyDistribution.dom}%`;
}, 100);
}
// Initialize Catalog Fetching
loadBenefits();
});