// Configurações - CATÁLOGO LABERI KIDS
const CONFIG = {
SUPABASE_URL: 'https://ydhzylfnpqlxnzfcclla.supabase.co',
SUPABASE_ANON_KEY: 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJzdXBhYmFzZSIsInJlZiI6InlkaHp5bGZucHFseG56ZmNjbGxhIiwicm9sZSI6ImFub24iLCJpYXQiOjE3NjA1NDA1NjIsImV4cCI6MjA3NjExNjU2Mn0.gIHxyAYngqkJ8z2Gt5ESYmG605vhY_LGTQB7Cjp4ZTA',
WHATSAPP_NUMBER: '5543999762754',
VENDEDORA_NOME: 'Maiara'
};
// Estado global
let produtos = [];
let carrinho = [];
let filtros = { tamanho: '', genero: '', destaque: '' };
let supabaseClient = null;
let currentUser = null;
let catalogoConfig = {
catalogoAtivo: false,
exibirPrecos: true,
exibirEstoque: false,
exibirNovidades: true,
exibirPromocoes: true
};
const atualizarViewportCSSVar = (() => {
let rafId = null;
const setVar = () => {
rafId = null;
const viewport = window.visualViewport;
const height = viewport && viewport.height ? viewport.height : window.innerHeight;
if (!height) {
return;
}
document.documentElement.style.setProperty('--visual-vh', `${height}px`);
};
const schedule = () => {
if (rafId !== null) {
return;
}
rafId = window.requestAnimationFrame(setVar);
};
return {
update: schedule,
immediate: () => {
if (rafId !== null) {
cancelAnimationFrame(rafId);
rafId = null;
}
setVar();
}
};
})();
atualizarViewportCSSVar.immediate();
window.addEventListener('resize', atualizarViewportCSSVar.update);
window.addEventListener('orientationchange', atualizarViewportCSSVar.update);
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', atualizarViewportCSSVar.update);
window.visualViewport.addEventListener('scroll', atualizarViewportCSSVar.update);
}
// Inicialização
document.addEventListener('DOMContentLoaded', async function() {
atualizarViewportCSSVar.immediate();
inicializarSupabase();
inicializarEventListeners();
inicializarImageViewer();
carregarCarrinho();
atualizarContadorCarrinho();
await carregarConfiguracoesCatalogo();
await carregarProdutos();
await verificarAutenticacao();
});
// Carregar carrinho do localStorage
function carregarCarrinho() {
const carrinhoSalvo = localStorage.getItem('liberi_carrinho');
if (carrinhoSalvo) {
try {
carrinho = JSON.parse(carrinhoSalvo);
} catch (error) {
console.error('Erro ao carregar carrinho:', error);
carrinho = [];
}
}
}
function criarDataLocal(date) {
if (!date) return new Date();
if (date instanceof Date) {
return new Date(date.getFullYear(), date.getMonth(), date.getDate());
}
if (typeof date === 'string') {
const partes = date.split('-');
if (partes.length === 3) {
const [ano, mes, dia] = partes.map((parte) => parseInt(parte, 10));
if (!Number.isNaN(ano) && !Number.isNaN(mes) && !Number.isNaN(dia)) {
return new Date(ano, mes - 1, dia);
}
}
}
const data = new Date(date);
if (Number.isNaN(data.getTime())) {
return new Date();
}
return new Date(data.getFullYear(), data.getMonth(), data.getDate());
}
function formatarDataInput(date) {
if (!date) return '';
const data = criarDataLocal(date);
const ano = data.getFullYear();
const mes = String(data.getMonth() + 1).padStart(2, '0');
const dia = String(data.getDate()).padStart(2, '0');
return `${ano}-${mes}-${dia}`;
}
function obterHojeISO() {
return formatarDataInput(new Date());
}
function adicionarDias(dataBase, dias) {
const data = criarDataLocal(dataBase || new Date());
data.setDate(data.getDate() + dias);
return formatarDataInput(data);
}
function formatarMoedaBR(valor) {
const numero = Number(valor) || 0;
return numero.toLocaleString('pt-BR', { style: 'currency', currency: 'BRL' });
}
function formatarDataHumanaBR(date) {
const data = criarDataLocal(date);
const dia = String(data.getDate()).padStart(2, '0');
const mes = String(data.getMonth() + 1).padStart(2, '0');
const ano = data.getFullYear();
return `${dia}/${mes}/${ano}`;
}
function gerarIdVendaCatalogo() {
const agora = new Date();
const ano = agora.getFullYear();
const mes = String(agora.getMonth() + 1).padStart(2, '0');
const dia = String(agora.getDate()).padStart(2, '0');
const hora = String(agora.getHours()).padStart(2, '0');
const minuto = String(agora.getMinutes()).padStart(2, '0');
const segundo = String(agora.getSeconds()).padStart(2, '0');
return `VDWEB${ano}${mes}${dia}${hora}${minuto}${segundo}`;
}
// Funções relacionadas a pagamentos foram removidas — o pedido apenas envia mensagem à vendedora.
// Inicializar Supabase
function inicializarSupabase() {
if (!CONFIG.SUPABASE_URL || !CONFIG.SUPABASE_ANON_KEY) {
console.error('Configurações do Supabase não encontradas');
return;
}
if (!window.supabase) {
console.error('Biblioteca do Supabase não encontrada');
return;
}
try {
supabaseClient = window.supabase.createClient(CONFIG.SUPABASE_URL, CONFIG.SUPABASE_ANON_KEY);
console.log('Supabase inicializado com sucesso');
} catch (error) {
console.error('Erro ao inicializar Supabase:', error);
}
}
// Carregar configurações do catálogo
async function carregarConfiguracoesCatalogo() {
try {
if (!supabaseClient) return;
const { data, error } = await supabaseClient
.from('configuracoes')
.select('valor')
.eq('chave', 'catalogo_config')
.single();
if (!error && data && data.valor) {
catalogoConfig = {
...catalogoConfig,
...data.valor
};
console.log('Configurações do catálogo carregadas:', catalogoConfig);
// Aplicar configurações visuais
if (!catalogoConfig.exibirPrecos) {
document.body.classList.add('hide-prices');
}
}
} catch (error) {
console.error('Erro ao carregar configurações do catálogo:', error);
}
}
// Event Listeners
function inicializarEventListeners() {
const filterPanel = document.getElementById('filterPanel');
if (filterPanel) {
filterPanel.addEventListener('click', handleFilterChipClick);
}
}
function inicializarImageViewer() {
const viewer = document.getElementById('produtoImageViewer');
const viewerImg = document.getElementById('produtoImageViewerImg');
if (!viewer) {
return;
}
viewer.addEventListener('click', event => {
if (event.target === viewer) {
fecharImagemExpandida();
}
});
if (viewerImg) {
viewerImg.addEventListener('click', event => {
event.stopPropagation();
});
}
// Navegação por teclado
document.addEventListener('keydown', event => {
if (!viewer.classList.contains('active')) return;
if (event.key === 'Escape') {
fecharImagemExpandida();
} else if (event.key === 'ArrowLeft') {
navegarImagemViewer(-1);
} else if (event.key === 'ArrowRight') {
navegarImagemViewer(1);
}
});
}
function atualizarEstadoFiltro() {
const filterBtn = document.querySelector('.filter-btn');
if (!filterBtn) return;
const algumFiltroAtivo = Boolean(filtros.tamanho || filtros.genero || filtros.destaque);
filterBtn.classList.toggle('has-filter', algumFiltroAtivo);
}
function escapeHtmlAttr(value = '') {
return value
.toString()
.replace(/&/g, '&')
.replace(/"/g, '"')
.replace(/'/g, ''')
.replace(//g, '>');
}
// Carregar produtos
async function carregarProdutos() {
const loading = document.getElementById('loading');
const grid = document.getElementById('produtosGrid');
const noProducts = document.getElementById('noProducts');
try {
mostrarLoading(true);
if (supabaseClient) {
const { data, error } = await supabaseClient
.from('produtos')
.select(`
*,
produto_variacoes (
id, tamanho, cor, quantidade, fotos
)
`)
.eq('ativo', true)
.eq('visivel_catalogo', true)
.order('created_at', { ascending: false });
if (error) throw error;
// Carregar fotos adicionais do bucket 'catalogo' para cada produto
const produtosComFotos = await Promise.all((data || []).map(async (produto) => {
try {
const { data: fotosAdicionais } = await supabaseClient
.storage
.from('catalogo')
.list(`produto_${produto.id}`, {
limit: 10,
sortBy: { column: 'name', order: 'asc' }
});
if (fotosAdicionais && fotosAdicionais.length > 0) {
produto.fotosAdicionais = fotosAdicionais.map(foto => {
const { data: urlData } = supabaseClient
.storage
.from('catalogo')
.getPublicUrl(`produto_${produto.id}/${foto.name}`);
return urlData.publicUrl;
});
} else {
produto.fotosAdicionais = [];
}
} catch (err) {
console.log('Sem fotos adicionais para produto', produto.id);
produto.fotosAdicionais = [];
}
return produto;
}));
produtos = mapearProdutos(produtosComFotos);
}
if (!produtos.length) {
noProducts.style.display = 'block';
grid.innerHTML = '';
} else {
noProducts.style.display = 'none';
popularFiltros();
renderizarProdutos();
}
} catch (error) {
console.error('Erro ao carregar produtos:', error);
mostrarPopup('Erro ao carregar produtos: ' + error.message, 'error');
} finally {
mostrarLoading(false);
}
}
// Mapear produtos do Supabase
function mapearProdutos(produtosData) {
return (produtosData || []).map(produto => {
const variacoes = (produto.produto_variacoes || []).map(variacao => ({
...variacao,
quantidade: Number(variacao.quantidade || 0),
tamanhoNormalizado: (variacao.tamanho || '').toString().toLowerCase()
}));
const generoLabel = (produto.genero || '').trim();
// Construir galeria de fotos: foto principal + fotos das variações + fotos adicionais
const fotosDasVariacoes = variacoes.flatMap(v => v.fotos || []).filter(Boolean);
const fotosAdicionais = produto.fotosAdicionais || [];
const todasFotos = [
produto.foto_principal,
...fotosDasVariacoes,
...fotosAdicionais
].filter(Boolean);
// Remover duplicatas
const galeriaFotos = [...new Set(todasFotos)];
return {
id: produto.id?.toString() ?? '',
id_produto: produto.id_produto || '',
codigo: produto.id_produto || '',
nome: produto.nome,
marca: produto.marca,
genero: generoLabel,
generoFiltro: generoLabel.toLowerCase(),
estacao: produto.estacao,
descricao: produto.descricao,
preco_venda: Number(produto.valor_revenda ?? produto.valor_compra ?? 0),
preco_promocional: produto.preco_promocional ? Number(produto.preco_promocional) : null,
em_promocao: produto.em_promocao || false,
novidade: produto.novidade || false,
variacoes,
imagemPrincipal: galeriaFotos[0] || null,
galeriaFotos: galeriaFotos,
ativo: produto.ativo,
visivelCatalogo: produto.visivel_catalogo
};
});
}
// Renderizar produtos
function renderizarProdutos() {
const container = document.getElementById('produtosGrid');
if (!container) return;
const produtosFiltrados = obterProdutosFiltrados();
const noProducts = document.getElementById('noProducts');
if (!produtosFiltrados.length) {
container.innerHTML = '';
if (noProducts) noProducts.style.display = 'block';
return;
}
if (noProducts) noProducts.style.display = 'none';
container.innerHTML = produtosFiltrados.map(produto => {
const variacoesDisponiveis = produto.variacoes || [];
const tamanhosDisponiveis = [...new Set(variacoesDisponiveis.map(v => v.tamanho).filter(Boolean))]
.sort((a, b) => a.toString().localeCompare(b.toString(), 'pt-BR', { numeric: true, sensitivity: 'base' }));
const temEstoque = variacoesDisponiveis.some(v => v.quantidade > 0);
// Preços
const precoNormal = produto.preco_venda;
const precoPromo = produto.preco_promocional;
const emPromocao = produto.em_promocao && precoPromo && precoPromo > 0;
const precoExibir = emPromocao ? precoPromo : precoNormal;
const precoFormatado = `R$ ${precoExibir.toFixed(2).replace('.', ',')}`;
const precoNormalFormatado = `R$ ${precoNormal.toFixed(2).replace('.', ',')}`;
const tamanhosVisiveis = tamanhosDisponiveis.slice(0, 4);
const possuiMaisTamanhos = tamanhosDisponiveis.length > tamanhosVisiveis.length;
// Badges
const exibirNovidades = catalogoConfig.exibirNovidades !== false;
const exibirPromocoes = catalogoConfig.exibirPromocoes !== false;
const mostrarBadgeNovidade = produto.novidade && exibirNovidades;
const mostrarBadgePromocao = emPromocao && exibirPromocoes;
return `
${produto.imagemPrincipal ?
`

` :
`
`
}
${(mostrarBadgePromocao || mostrarBadgeNovidade || !temEstoque) ? `
${!temEstoque ? 'ESGOTADO' : ''}
${mostrarBadgePromocao ? '🏷️ PROMOÇÃO' : ''}
${mostrarBadgeNovidade ? '✨ NOVO' : ''}
` : ''}
${produto.nome}
${emPromocao ? `${precoNormalFormatado}` : ''}
${precoFormatado}
${tamanhosDisponiveis.length
? tamanhosVisiveis.map(tamanho => `${tamanho}`).join('')
: 'Único'}
${possuiMaisTamanhos ? '+' : ''}
`;
}).join('');
}
function obterProdutosFiltrados() {
const generoFiltro = filtros.genero;
const tamanhoFiltro = filtros.tamanho;
const destaqueFiltro = filtros.destaque;
return produtos.filter(produto => {
const generoConfere = !generoFiltro || produto.generoFiltro === generoFiltro;
const tamanhoConfere = !tamanhoFiltro || produto.variacoes.some(variacao => {
return (variacao.tamanhoNormalizado || (variacao.tamanho || '').toString().toLowerCase()) === tamanhoFiltro;
});
let destaqueConfere = true;
if (destaqueFiltro === 'promocao') {
destaqueConfere = produto.em_promocao && produto.preco_promocional > 0;
} else if (destaqueFiltro === 'novo') {
destaqueConfere = produto.novidade;
}
return generoConfere && tamanhoConfere && destaqueConfere;
});
}
// Função de extração de cor desativada (não utilizada no layout atual)
// function extrairCorDominante(img) { ... }
// Verificar autenticação com localStorage
async function verificarAutenticacao() {
try {
// Verificar se há dados salvos no localStorage
const savedUser = localStorage.getItem('liberi_user');
if (savedUser) {
const userData = JSON.parse(savedUser);
// Verificar se o cliente ainda existe no banco
const { data: cliente, error } = await supabaseClient
.from('clientes')
.select('*')
.eq('id', userData.cliente.id)
.single();
if (!error && cliente) {
currentUser = { cliente };
atualizarInterfaceUsuario(true);
console.log('✅ Login restaurado:', cliente.nome_completo);
return true;
} else {
// Cliente não existe mais, limpar localStorage
localStorage.removeItem('liberi_user');
}
}
// Não há login salvo ou cliente não existe
currentUser = null;
atualizarInterfaceUsuario(false);
return false;
} catch (error) {
console.error('Erro ao verificar autenticação:', error);
localStorage.removeItem('liberi_user');
currentUser = null;
atualizarInterfaceUsuario(false);
return false;
}
}
// Atualizar interface do usuário
function atualizarInterfaceUsuario(isLoggedIn) {
const userNotLogged = document.getElementById('userNotLogged');
const userLogged = document.getElementById('userLogged');
const userName = document.getElementById('userName');
if (isLoggedIn && currentUser?.cliente) {
userNotLogged.style.display = 'none';
userLogged.style.display = 'flex';
userName.textContent = currentUser.cliente.nome_completo.split(' ')[0];
} else {
userNotLogged.style.display = 'block';
userLogged.style.display = 'none';
}
}
// Função de login
async function handleLogin(event) {
event.preventDefault();
const phone = document.getElementById('loginPhone').value.trim();
const password = document.getElementById('loginPassword').value.trim();
const loginBtn = document.getElementById('loginBtn');
if (!phone || !password) {
mostrarPopup('Por favor, digite seu WhatsApp e senha', 'error');
return;
}
const cleanPhone = phone.replace(/\D/g, '');
loginBtn.innerHTML = ' Entrando...';
loginBtn.disabled = true;
try {
// Buscar cliente na tabela
const { data: cliente, error: clienteError } = await supabaseClient
.from('clientes')
.select('*')
.eq('whatsapp', cleanPhone)
.single();
if (clienteError || !cliente) {
throw new Error('Cliente não encontrado. Verifique seu número ou cadastre-se.');
}
// Verificar senha diretamente (sem Supabase Auth)
if (cliente.senha_hash !== password) {
throw new Error('Senha incorreta. Tente novamente.');
}
// Login bem-sucedido
currentUser = { cliente };
// Salvar no localStorage para manter login
localStorage.setItem('liberi_user', JSON.stringify(currentUser));
// Mostrar popup de confirmação
mostrarConfirmacao(
'Login realizado!',
`Bem-vindo(a) de volta, ${cliente.nome_completo.split(' ')[0]}!`,
() => {
closeLoginModal();
atualizarInterfaceUsuario(true);
}
);
} catch (error) {
console.error('Erro no login:', error);
mostrarPopup(error.message, 'error');
} finally {
loginBtn.innerHTML = ' Entrar';
loginBtn.disabled = false;
}
}
// Função de cadastro
async function handleRegister(event) {
event.preventDefault();
const formData = {
nome_completo: document.getElementById('registerName').value.trim(),
email: document.getElementById('registerEmail').value.trim() || null,
whatsapp: document.getElementById('registerWhatsapp').value.replace(/\D/g, ''),
endereco: document.getElementById('registerAddress').value.trim(),
senha: document.getElementById('registerPassword').value.trim()
};
const registerBtn = document.getElementById('registerBtn');
if (!formData.nome_completo || !formData.whatsapp || !formData.endereco || !formData.senha) {
mostrarPopup('Preencha todos os campos obrigatórios', 'error');
return;
}
if (formData.senha.length < 6) {
mostrarPopup('A senha deve ter pelo menos 6 caracteres', 'error');
return;
}
registerBtn.innerHTML = ' Criando conta...';
registerBtn.disabled = true;
try {
const { data: existingClient, error: checkError } = await supabaseClient
.from('clientes')
.select('id')
.eq('whatsapp', formData.whatsapp)
.single();
if (existingClient) {
throw new Error('Este WhatsApp já está cadastrado. Faça login.');
}
// Inserir cliente com senha diretamente na tabela
const { data: cliente, error: clienteError } = await supabaseClient
.from('clientes')
.insert([{
nome_completo: formData.nome_completo,
email: formData.email,
whatsapp: formData.whatsapp,
endereco: formData.endereco,
senha_hash: formData.senha
}])
.select()
.single();
if (clienteError) throw clienteError;
// Login automático após cadastro
currentUser = { cliente };
// Salvar no localStorage para manter login
localStorage.setItem('liberi_user', JSON.stringify(currentUser));
// Mostrar popup de confirmação
mostrarConfirmacao(
'Conta criada com sucesso!',
`Bem-vindo(a), ${formData.nome_completo.split(' ')[0]}! Sua conta foi criada e você já está logado(a).`,
() => {
closeRegisterModal();
atualizarInterfaceUsuario(true);
}
);
} catch (error) {
console.error('Erro no cadastro:', error);
mostrarPopup(error.message, 'error');
} finally {
registerBtn.innerHTML = ' Criar Conta';
registerBtn.disabled = false;
}
}
// Funções dos modais de autenticação com animação
function ativarAuthModal(modal) {
if (!modal) return;
modal.classList.add('pre-active');
requestAnimationFrame(() => {
modal.classList.remove('pre-active');
modal.classList.add('active');
});
}
function desativarAuthModal(modal) {
if (!modal) return;
if (!modal.classList.contains('active')) {
modal.classList.remove('pre-active');
return;
}
const finalizar = (event) => {
if (event.target !== modal) {
return;
}
modal.removeEventListener('transitionend', finalizar);
modal.classList.remove('pre-active');
};
modal.addEventListener('transitionend', finalizar);
modal.classList.remove('active');
}
function showRegisterModal() {
const loginModal = document.getElementById('loginModal');
const registerModal = document.getElementById('registerModal');
if (loginModal) {
desativarAuthModal(loginModal);
}
if (registerModal) {
ativarAuthModal(registerModal);
}
}
function closeRegisterModal() {
desativarAuthModal(document.getElementById('registerModal'));
}
function closeLoginModal() {
desativarAuthModal(document.getElementById('loginModal'));
}
// Funções auxiliares
function mostrarLoading(show) {
const loading = document.getElementById('loading');
const grid = document.getElementById('produtosGrid');
if (loading) loading.style.display = show ? 'block' : 'none';
if (grid) grid.style.display = show ? 'none' : 'grid';
}
function atualizarContadorCarrinho() {
const cartCount = document.querySelector('.cart-count');
if (cartCount) {
const totalItens = carrinho.reduce((sum, item) => sum + item.quantidade, 0);
cartCount.textContent = totalItens;
}
}
function popularFiltros() {
const tamanhoContainer = document.getElementById('tamanhoFilterChips');
const generoContainer = document.getElementById('generoFilterChips');
if (!tamanhoContainer || !generoContainer) return;
const tamanhos = new Set();
const generos = new Set();
produtos.forEach(produto => {
(produto.variacoes || []).forEach(variacao => {
if (variacao.tamanho) {
tamanhos.add(variacao.tamanho.toString());
}
});
if (produto.genero) {
generos.add(produto.genero);
}
});
const ordenar = (a, b) => a.toString().localeCompare(b.toString(), 'pt-BR', { numeric: true, sensitivity: 'base' });
const tamanhosOrdenados = Array.from(tamanhos).sort(ordenar);
const generosOrdenados = Array.from(generos).sort(ordenar);
const construirChips = (valores, tipo) => {
const ativo = filtros[tipo] || '';
const itens = [''].concat(valores);
return itens.map(valor => {
const normalizado = valor ? valor.toString().toLowerCase() : '';
const label = valor || 'Todos';
const ativoChip = normalizado === ativo;
return ``;
}).join('');
};
tamanhoContainer.innerHTML = construirChips(tamanhosOrdenados, 'tamanho');
generoContainer.innerHTML = construirChips(generosOrdenados, 'genero');
// Atualizar filtros de destaque (Promoção e Novo)
const destaquesContainer = document.getElementById('destaquesFilterChips');
if (destaquesContainer) {
const destaqueAtivo = filtros.destaque || '';
destaquesContainer.querySelectorAll('.filter-chip').forEach(chip => {
const valorChip = (chip.dataset.value || '').toLowerCase();
if (valorChip === destaqueAtivo) {
chip.classList.add('active');
} else {
chip.classList.remove('active');
}
});
}
atualizarEstadoFiltro();
}
function handleFilterChipClick(event) {
const chip = event.target.closest('.filter-chip');
if (!chip) return;
const tipo = chip.dataset.filter;
if (!tipo) return;
const valorOriginal = (chip.dataset.value || '').toString();
const valorNormalizado = valorOriginal.toLowerCase();
if (filtros[tipo] === valorNormalizado) {
filtros[tipo] = '';
} else {
filtros[tipo] = valorNormalizado;
}
popularFiltros();
renderizarProdutos();
}
// Estado do modal
let produtoSelecionado = null;
let variacaoSelecionada = null;
function abrirProdutoModal(produtoId) {
const produto = produtos.find(p => p.id === produtoId);
if (!produto) return;
produtoSelecionado = produto;
variacaoSelecionada = null;
const modal = document.getElementById('produtoModal');
const modalImage = document.getElementById('produtoModalImage');
const modalNome = document.getElementById('produtoModalNome');
const modalMarca = document.getElementById('produtoModalMarca');
const modalPreco = document.getElementById('produtoModalPreco');
const modalDescricao = document.getElementById('produtoModalDescricao');
const modalVariacoesGrid = document.getElementById('produtoModalVariacoesGrid');
const modalBotao = document.getElementById('produtoModalBotao');
// Preencher informações básicas
modalNome.textContent = produto.nome;
modalMarca.textContent = produto.marca;
// Preço (considerar promoção)
const precoNormal = produto.preco_venda;
const precoPromo = produto.preco_promocional;
const emPromocao = produto.em_promocao && precoPromo && precoPromo > 0;
const precoExibir = emPromocao ? precoPromo : precoNormal;
const precoFormatado = `R$ ${precoExibir.toFixed(2).replace('.', ',')}`;
if (emPromocao) {
const precoNormalFormatado = `R$ ${precoNormal.toFixed(2).replace('.', ',')}`;
modalPreco.innerHTML = `
${precoNormalFormatado}
${precoFormatado}
`;
} else {
modalPreco.textContent = precoFormatado;
}
modalDescricao.textContent = produto.descricao || 'Produto de qualidade superior para o conforto e estilo do seu filho.';
// Preencher imagem e galeria
const galeriaFotos = produto.galeriaFotos || [];
const imagemPrincipal = galeriaFotos[0] || '';
if (imagemPrincipal) {
const imagemEscapada = escapeHtmlAttr(imagemPrincipal);
const nomeEscapado = escapeHtmlAttr(produto.nome || '');
modalImage.dataset.imageUrl = imagemPrincipal;
modalImage.innerHTML = `
${galeriaFotos.length > 1 ? `
${galeriaFotos.map((foto, index) => `
})
`).join('')}
` : ''}
`;
const trigger = modalImage.querySelector('.produto-modal-image-trigger');
if (trigger) {
trigger.addEventListener('click', expandirImagemProduto);
}
} else {
modalImage.dataset.imageUrl = '';
modalImage.innerHTML = `
`;
}
// Preencher variações
const variacoesComEstoque = produto.variacoes.filter(v => v.quantidade > 0);
if (variacoesComEstoque.length > 0) {
modalVariacoesGrid.innerHTML = variacoesComEstoque.map(variacao => `
${variacao.tamanho}
${variacao.cor}
${variacao.quantidade > 0 ? `${variacao.quantidade} disponível${variacao.quantidade > 1 ? 'is' : ''}` : 'Esgotado'}
`).join('');
modalBotao.disabled = true;
modalBotao.style.opacity = '0.5';
} else {
modalVariacoesGrid.innerHTML = 'Produto esgotado
';
modalBotao.disabled = true;
modalBotao.style.opacity = '0.5';
}
// Exibir modal
if (!modal.classList.contains('active')) {
modal.classList.add('pre-active');
requestAnimationFrame(() => {
modal.classList.remove('pre-active');
modal.classList.add('active');
});
}
}
function trocarImagemModal(novaUrl, elemento) {
const imagemPrincipal = document.getElementById('modalImagemPrincipal');
const modalImage = document.getElementById('produtoModalImage');
if (imagemPrincipal) {
imagemPrincipal.src = novaUrl;
modalImage.dataset.imageUrl = novaUrl;
}
// Atualizar classe active nas miniaturas
const miniaturas = document.querySelectorAll('.galeria-miniatura');
miniaturas.forEach(min => min.classList.remove('active'));
if (elemento) {
elemento.classList.add('active');
}
}
function fecharProdutoModal(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
const modal = document.getElementById('produtoModal');
if (modal) {
modal.classList.remove('active');
const remover = () => {
modal.removeEventListener('transitionend', remover);
if (!modal.classList.contains('active')) {
modal.style.display = 'none';
void modal.offsetWidth;
modal.style.display = '';
}
};
modal.addEventListener('transitionend', remover);
}
fecharImagemExpandida();
produtoSelecionado = null;
variacaoSelecionada = null;
}
function selecionarVariacao(variacaoId) {
if (!produtoSelecionado) return;
const variacao = produtoSelecionado.variacoes.find(v => v.id.toString() === variacaoId.toString());
if (!variacao || variacao.quantidade === 0) return;
// Remover seleção anterior
document.querySelectorAll('.variacao-item').forEach(item => {
item.classList.remove('selected');
});
// Adicionar seleção atual
const item = document.querySelector(`[data-variacao-id="${variacaoId}"]`);
if (item) {
item.classList.add('selected');
variacaoSelecionada = variacao;
const modalImage = document.getElementById('produtoModalImage');
if (modalImage) {
const fallbackImagem = produtoSelecionado.imagemPrincipal || (produtoSelecionado.variacoes || []).find(v => Array.isArray(v.fotos) && v.fotos.length)?.fotos?.[0] || '';
const novaImagem = (variacao.fotos && variacao.fotos.length > 0)
? variacao.fotos[0]
: fallbackImagem;
if (novaImagem) {
let trigger = modalImage.querySelector('.produto-modal-image-trigger');
if (!trigger) {
const imagemEscapada = escapeHtmlAttr(novaImagem);
const nomeEscapado = escapeHtmlAttr(produtoSelecionado.nome || '');
modalImage.innerHTML = `
`;
trigger = modalImage.querySelector('.produto-modal-image-trigger');
if (trigger) {
trigger.addEventListener('click', expandirImagemProduto);
}
}
const imgElement = modalImage.querySelector('img');
if (imgElement) {
modalImage.dataset.imageUrl = novaImagem;
imgElement.src = novaImagem;
const descricaoImagem = `${produtoSelecionado.nome || ''} - ${variacao.tamanho || ''} ${variacao.cor || ''}`.trim();
if (descricaoImagem) {
imgElement.alt = descricaoImagem;
}
}
} else {
modalImage.dataset.imageUrl = '';
}
}
// Habilitar botão
const modalBotao = document.getElementById('produtoModalBotao');
modalBotao.disabled = false;
modalBotao.style.opacity = '1';
}
}
let currentImageIndex = 0;
let viewerImages = [];
function expandirImagemProduto(event) {
if (event) {
event.preventDefault();
event.stopPropagation();
}
const viewer = document.getElementById('produtoImageViewer');
const viewerImg = document.getElementById('produtoImageViewerImg');
const modalImage = document.getElementById('produtoModalImage');
if (!viewer || !viewerImg || !modalImage) {
return;
}
const imageUrl = modalImage.dataset.imageUrl;
if (!imageUrl) {
return;
}
// Obter galeria de fotos do produto
if (produtoSelecionado && produtoSelecionado.galeriaFotos) {
viewerImages = produtoSelecionado.galeriaFotos;
currentImageIndex = viewerImages.indexOf(imageUrl);
if (currentImageIndex === -1) currentImageIndex = 0;
} else {
viewerImages = [imageUrl];
currentImageIndex = 0;
}
viewerImg.src = imageUrl;
viewer.classList.add('active');
// Mostrar/ocultar controles de navegação
const prevBtn = viewer.querySelector('.viewer-prev');
const nextBtn = viewer.querySelector('.viewer-next');
const counter = viewer.querySelector('.viewer-counter');
if (viewerImages.length > 1) {
if (prevBtn) prevBtn.style.display = 'flex';
if (nextBtn) nextBtn.style.display = 'flex';
if (counter) {
counter.style.display = 'block';
counter.textContent = `${currentImageIndex + 1} / ${viewerImages.length}`;
}
} else {
if (prevBtn) prevBtn.style.display = 'none';
if (nextBtn) nextBtn.style.display = 'none';
if (counter) counter.style.display = 'none';
}
}
function navegarImagemViewer(direction) {
if (viewerImages.length <= 1) return;
currentImageIndex += direction;
// Loop circular
if (currentImageIndex < 0) {
currentImageIndex = viewerImages.length - 1;
} else if (currentImageIndex >= viewerImages.length) {
currentImageIndex = 0;
}
const viewerImg = document.getElementById('produtoImageViewerImg');
const counter = document.querySelector('.viewer-counter');
if (viewerImg) {
viewerImg.src = viewerImages[currentImageIndex];
}
if (counter) {
counter.textContent = `${currentImageIndex + 1} / ${viewerImages.length}`;
}
}
function fecharImagemExpandida() {
const viewer = document.getElementById('produtoImageViewer');
const viewerImg = document.getElementById('produtoImageViewerImg');
if (!viewer || !viewerImg) {
return;
}
viewer.classList.remove('active');
viewerImg.src = '';
viewerImages = [];
currentImageIndex = 0;
}
function adicionarAoCarrinhoModal() {
if (!produtoSelecionado || !variacaoSelecionada) {
mostrarPopup('Selecione um tamanho e cor antes de adicionar ao carrinho', 'error');
return;
}
// Verificar se já existe no carrinho
const itemExistente = carrinho.find(item =>
item.produtoId === produtoSelecionado.id &&
item.variacaoId === variacaoSelecionada.id
);
if (itemExistente) {
// Verificar se há estoque disponível
if (itemExistente.quantidade < variacaoSelecionada.quantidade) {
itemExistente.quantidade++;
} else {
mostrarPopup('Quantidade máxima em estoque já adicionada', 'error');
return;
}
} else {
// Adicionar novo item
carrinho.push({
produtoId: produtoSelecionado.id,
variacaoId: variacaoSelecionada.id,
codigo: produtoSelecionado.codigo || produtoSelecionado.id_produto || '',
nome: `${produtoSelecionado.marca} ${produtoSelecionado.nome}`,
tamanho: variacaoSelecionada.tamanho,
cor: variacaoSelecionada.cor,
preco: produtoSelecionado.preco_venda,
quantidade: 1,
imagem: produtoSelecionado.imagemPrincipal
});
}
// Salvar carrinho no localStorage
localStorage.setItem('liberi_carrinho', JSON.stringify(carrinho));
// Atualizar interface
atualizarContadorCarrinho();
renderizarCarrinho();
// Fechar modal e mostrar confirmação
fecharProdutoModal();
mostrarPopup('Produto adicionado ao carrinho!', 'success');
}
function toggleFilterPanel() {
const panel = document.getElementById('filterPanel');
const filterBtn = document.querySelector('.filter-btn');
if (panel) {
const isOpen = panel.classList.toggle('open');
if (filterBtn) {
filterBtn.classList.toggle('active', isOpen);
}
if (isOpen) {
window.scrollTo({ top: 0, behavior: 'smooth' });
}
}
}
function toggleCart() {
const cartModal = document.getElementById('cartModal');
if (cartModal) {
const isOpen = cartModal.classList.contains('active');
if (isOpen) {
desativarAuthModal(cartModal);
} else {
renderizarCarrinho();
ativarAuthModal(cartModal);
}
}
}
function renderizarCarrinho() {
const cartContent = document.getElementById('cartContent');
const cartFooter = document.getElementById('cartFooter');
const cartTotal = document.getElementById('cartTotal');
if (carrinho.length === 0) {
cartContent.innerHTML = `
Seu carrinho está vazio
Adicione produtos para começar!
`;
cartFooter.style.display = 'none';
if (cartTotal) {
cartTotal.textContent = '0,00';
}
return;
}
let total = 0;
cartContent.innerHTML = carrinho.map((item, index) => {
const subtotal = item.preco * item.quantidade;
total += subtotal;
return `
${item.imagem ?
`

` :
`
`
}
${item.nome}
${item.tamanho} - ${item.cor}
R$ ${item.preco.toFixed(2).replace('.', ',')}
`;
}).join('');
if (cartTotal) {
cartTotal.textContent = total.toFixed(2).replace('.', ',');
}
cartFooter.style.display = 'flex';
}
function alterarQuantidade(index, delta) {
if (index < 0 || index >= carrinho.length) return;
const item = carrinho[index];
const produto = produtos.find(p => p.id === item.produtoId);
const variacao = produto?.variacoes.find(v => v.id === item.variacaoId);
const novaQuantidade = item.quantidade + delta;
if (novaQuantidade <= 0) {
removerDoCarrinho(index);
return;
}
if (variacao && novaQuantidade > variacao.quantidade) {
mostrarPopup('Quantidade máxima em estoque atingida', 'error');
return;
}
item.quantidade = novaQuantidade;
localStorage.setItem('liberi_carrinho', JSON.stringify(carrinho));
atualizarContadorCarrinho();
renderizarCarrinho();
}
function removerDoCarrinho(index) {
if (index < 0 || index >= carrinho.length) return;
carrinho.splice(index, 1);
localStorage.setItem('liberi_carrinho', JSON.stringify(carrinho));
atualizarContadorCarrinho();
renderizarCarrinho();
if (carrinho.length === 0) {
toggleCart();
}
}
async function finalizarPedido() {
if (!currentUser) {
toggleCart();
mostrarPopup('Faça login para finalizar seu pedido', 'error');
setTimeout(() => showLoginModal(), 500);
return;
}
if (carrinho.length === 0) {
mostrarPopup('Adicione produtos ao carrinho antes de finalizar', 'error');
return;
}
const checkoutBtn = document.querySelector('.checkout-btn');
const btnOriginalHTML = checkoutBtn ? checkoutBtn.innerHTML : null;
if (checkoutBtn) {
checkoutBtn.disabled = true;
checkoutBtn.innerHTML = ' Enviando...';
}
const cliente = currentUser.cliente;
const totalPedido = carrinho.reduce((sum, item) => sum + (item.preco * item.quantidade), 0);
let mensagem = `*Novo Pedido - Liberi Kids*\n\n`;
mensagem += `*Cliente:* ${cliente.nome_completo}\n`;
mensagem += `*WhatsApp:* ${cliente.whatsapp}\n`;
if (cliente.endereco) {
mensagem += `*Endereço:* ${cliente.endereco}\n`;
}
mensagem += `\n*Itens do Pedido:*\n`;
carrinho.forEach((item, index) => {
const subtotal = item.preco * item.quantidade;
const codigoItem = item.codigo || item.produtoId || 'N/A';
mensagem += `\n${index + 1}. ${item.nome}\n`;
mensagem += ` ID: ${codigoItem}\n`;
mensagem += ` Tamanho: ${item.tamanho} | Cor: ${item.cor}\n`;
mensagem += ` Qtd: ${item.quantidade} x ${formatarMoedaBR(item.preco)} = ${formatarMoedaBR(subtotal)}\n`;
});
mensagem += `\n*Total:* ${formatarMoedaBR(totalPedido)}\n`;
mensagem += `\nPor favor, confirme o pagamento com a vendedora. Obrigado!`;
const pedidoPayload = {
cliente: {
nome: cliente.nome_completo,
whatsapp: cliente.whatsapp,
endereco: cliente.endereco || null
},
itens: carrinho.map((item) => ({
nome: item.nome,
codigo: item.codigo || item.produtoId || null,
tamanho: item.tamanho,
cor: item.cor,
quantidade: item.quantidade,
preco: Number(item.preco) || 0,
subtotal: Number((item.preco * item.quantidade).toFixed(2))
})),
total: totalPedido,
mensagem
};
try {
const response = await fetch('/api/catalogo/pedidos', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(pedidoPayload)
});
if (!response.ok) {
const erroServidor = await response.json().catch(() => ({}));
throw new Error(erroServidor.error || 'Não foi possível registrar o pedido no sistema.');
}
} catch (error) {
console.error('Erro ao salvar pedido do catálogo:', error);
mostrarPopup('Pedido enviado para a vendedora, mas não foi possível registrar na plataforma. ' + (error.message || ''), 'error');
}
const mensagemCodificada = encodeURIComponent(mensagem);
const whatsappUrl = `https://wa.me/${CONFIG.WHATSAPP_NUMBER}?text=${mensagemCodificada}`;
window.open(whatsappUrl, '_blank');
mostrarConfirmacao(
'Pedido enviado!',
'A vendedora receberá os detalhes do pedido e entrará em contato com você pelo WhatsApp.',
() => {
carrinho = [];
localStorage.setItem('liberi_carrinho', JSON.stringify(carrinho));
atualizarContadorCarrinho();
renderizarCarrinho();
toggleCart();
}
);
if (checkoutBtn) {
checkoutBtn.disabled = false;
checkoutBtn.innerHTML = btnOriginalHTML || ' Finalizar Pedido';
}
}
function abrirWhatsApp() {
const whatsappUrl = `https://wa.me/${CONFIG.WHATSAPP_NUMBER}?text=Olá! Gostaria de saber mais sobre os produtos da Laberi Kids.`;
window.open(whatsappUrl, '_blank');
}
// ========================================
// SISTEMA DE POPUPS E NOTIFICAÇÕES
// ========================================
// Função para mostrar popup de erro/sucesso
function mostrarPopup(mensagem, tipo = 'info', callback = null) {
// Remover popup existente se houver
const existingPopup = document.querySelector('.custom-popup');
if (existingPopup) {
existingPopup.remove();
}
// Criar popup
const popup = document.createElement('div');
popup.className = `custom-popup ${tipo}`;
const icon = tipo === 'error' ? 'fas fa-exclamation-circle' :
tipo === 'success' ? 'fas fa-check-circle' :
'fas fa-info-circle';
popup.innerHTML = `
`;
document.body.appendChild(popup);
// Animar entrada
setTimeout(() => popup.classList.add('show'), 10);
// Callback se fornecido
if (callback) {
popup.setAttribute('data-callback', 'true');
popup.callback = callback;
}
}
// Função para fechar popup
function fecharPopup(btn) {
const popup = btn.closest('.custom-popup');
if (popup) {
popup.classList.remove('show');
setTimeout(() => {
if (popup.callback) {
popup.callback();
}
popup.remove();
}, 300);
}
}
// Função para mostrar confirmação
function mostrarConfirmacao(titulo, mensagem, callback = null) {
mostrarPopup(`${titulo}
${mensagem}`, 'success', callback);
}
// Melhorar função de login para verificar se já está logado
function showLoginModal() {
// Se já estiver logado, mostrar status
if (currentUser) {
const nomeUsuario = currentUser.cliente.nome_completo.split(' ')[0];
mostrarPopupConfirmacao(
'Você já está logado!',
`Olá, ${nomeUsuario}! 👋
Deseja sair da sua conta?`,
() => logout()
);
return;
}
// Se não estiver logado, mostrar modal de login
const loginModal = document.getElementById('loginModal');
const registerModal = document.getElementById('registerModal');
if (registerModal) {
desativarAuthModal(registerModal);
}
if (loginModal) {
ativarAuthModal(loginModal);
}
}
// Função para popup de confirmação com Sim/Não
function mostrarPopupConfirmacao(titulo, mensagem, callbackSim = null, callbackNao = null) {
// Remover popup existente se houver
const existingPopup = document.querySelector('.custom-popup');
if (existingPopup) {
existingPopup.remove();
}
// Criar popup de confirmação
const popup = document.createElement('div');
popup.className = 'custom-popup confirmation';
popup.innerHTML = `
`;
document.body.appendChild(popup);
// Animar entrada
setTimeout(() => popup.classList.add('show'), 10);
// Callbacks
popup.callbackSim = callbackSim;
popup.callbackNao = callbackNao;
}
// Função para fechar popup de confirmação
function fecharPopupConfirmacao(btn, resposta) {
const popup = btn.closest('.custom-popup');
if (popup) {
popup.classList.remove('show');
setTimeout(() => {
if (resposta && popup.callbackSim) {
popup.callbackSim();
} else if (!resposta && popup.callbackNao) {
popup.callbackNao();
}
popup.remove();
}, 300);
}
}
// Função de logout melhorada (substituindo a anterior)
function logout() {
// Limpar dados locais e localStorage
currentUser = null;
carrinho = [];
localStorage.removeItem('liberi_user');
atualizarInterfaceUsuario(false);
atualizarContadorCarrinho();
mostrarPopup('Você saiu da sua conta com sucesso!', 'success');
}