import React, { useState, useEffect } from 'react'; import { FiGlobe, FiPackage, FiImage, FiEye, FiEyeOff, FiRefreshCw, FiSave, FiSettings } from 'react-icons/fi'; import toast from 'react-hot-toast'; import '../styles/site-catalogo.css'; import '../styles/site-catalogo-table.css'; const SiteCatalogo = () => { const [loading, setLoading] = useState(false); const [produtos, setProdutos] = useState([]); const [precosPromocionais, setPrecosPromocionais] = useState({}); const [precosOriginais, setPrecosOriginais] = useState({}); const [precosAlterados, setPrecosAlterados] = useState({}); const [precosSalvando, setPrecosSalvando] = useState({}); const [catalogoConfig, setCatalogoConfig] = useState({ catalogoAtivo: false, exibirPrecos: true, exibirEstoque: false, exibirNovidades: true, exibirPromocoes: true }); const [modalFotosOpen, setModalFotosOpen] = useState(false); const [produtoSelecionado, setProdutoSelecionado] = useState(null); const [fotosAdicionais, setFotosAdicionais] = useState([]); const [uploadingPhoto, setUploadingPhoto] = useState(false); useEffect(() => { carregarProdutos(); carregarConfiguracoes(); }, []); const carregarProdutos = async () => { try { setLoading(true); const response = await fetch('/api/produtos'); if (response.ok) { const data = await response.json(); setProdutos(data); const precos = {}; const originais = {}; data.forEach((produto) => { const valor = produto.preco_promocional; const valorFormatado = valor === null || valor === undefined || valor === '' ? '' : Number(valor).toFixed(2); precos[produto.id] = valorFormatado; originais[produto.id] = valorFormatado; }); setPrecosPromocionais(precos); setPrecosOriginais(originais); setPrecosAlterados({}); setPrecosSalvando({}); } } catch (error) { console.error('Erro ao carregar produtos:', error); toast.error('Erro ao carregar produtos'); } finally { setLoading(false); } }; const carregarConfiguracoes = async () => { try { const response = await fetch('/api/configuracoes/catalogo'); if (response.ok) { const data = await response.json(); setCatalogoConfig(data); } } catch (error) { console.error('Erro ao carregar configurações:', error); } }; const toggleProdutoPromocao = async (produtoId, emPromocao) => { try { const response = await fetch(`/api/produtos/${produtoId}/promocao`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ emPromocao: !emPromocao }), }); if (response.ok) { toast.success('Promoção atualizada!'); carregarProdutos(); } } catch (error) { console.error('Erro ao atualizar promoção:', error); toast.error('Erro ao atualizar promoção'); } }; const toggleProdutoNovidade = async (produtoId, novidade) => { try { const response = await fetch(`/api/produtos/${produtoId}/novidade`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ novidade: !novidade }), }); if (response.ok) { toast.success('Novidade atualizada!'); carregarProdutos(); } } catch (error) { console.error('Erro ao atualizar novidade:', error); toast.error('Erro ao atualizar novidade'); } }; const handlePrecoPromocionalChange = (produtoId, valor) => { setPrecosPromocionais(prev => ({ ...prev, [produtoId]: valor })); setPrecosAlterados(prev => ({ ...prev, [produtoId]: valor !== (precosOriginais[produtoId] ?? '') })); }; const handlePrecoPromocionalBlur = (produtoId) => { const valorAtual = precosPromocionais[produtoId]; if (valorAtual === '' || valorAtual === null || valorAtual === undefined) { setPrecosAlterados(prev => ({ ...prev, [produtoId]: (precosOriginais[produtoId] ?? '') !== '' })); return; } const numero = Number(valorAtual); if (Number.isNaN(numero)) { toast.error('Informe um valor numérico válido'); return; } const formatado = numero.toFixed(2); setPrecosPromocionais(prev => ({ ...prev, [produtoId]: formatado })); setPrecosAlterados(prev => ({ ...prev, [produtoId]: formatado !== (precosOriginais[produtoId] ?? '') })); }; const salvarPrecoPromocional = async (produtoId) => { const valorAtual = precosPromocionais[produtoId]; const precoFormatado = valorAtual === '' || valorAtual === null || valorAtual === undefined ? '' : Number(valorAtual).toFixed(2); const precoPayload = valorAtual === '' || valorAtual === null || valorAtual === undefined ? null : Number(valorAtual); if (precoPayload !== null && (Number.isNaN(precoPayload) || precoPayload < 0)) { toast.error('Informe um valor válido para o desconto'); return; } setPrecosSalvando(prev => ({ ...prev, [produtoId]: true })); try { const response = await fetch(`/api/produtos/${produtoId}/preco-promocional`, { method: 'PATCH', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify({ precoPromocional: precoPayload }), }); if (response.ok) { toast.success('Preço promocional atualizado!'); setPrecosOriginais(prev => ({ ...prev, [produtoId]: precoFormatado })); setPrecosPromocionais(prev => ({ ...prev, [produtoId]: precoFormatado })); setPrecosAlterados(prev => ({ ...prev, [produtoId]: false })); setProdutos(prev => prev.map(produto => produto.id === produtoId ? { ...produto, preco_promocional: precoPayload } : produto ) ); } else { const data = await response.json().catch(() => ({})); throw new Error(data.message || 'Erro ao atualizar preço'); } } catch (error) { console.error('Erro ao atualizar preço:', error); toast.error('Erro ao atualizar preço'); } finally { setPrecosSalvando(prev => ({ ...prev, [produtoId]: false })); } }; const salvarConfiguracoes = async () => { try { setLoading(true); const response = await fetch('/api/configuracoes/catalogo', { method: 'POST', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify(catalogoConfig), }); if (response.ok) { toast.success('Configurações salvas com sucesso!'); } else { throw new Error('Erro ao salvar configurações'); } } catch (error) { console.error('Erro ao salvar configurações:', error); toast.error('Erro ao salvar configurações'); } finally { setLoading(false); } }; const toggleProdutoVisivel = async (produtoId, visivelAtual) => { try { const response = await fetch(`/api/produtos/${produtoId}/visibilidade`, { method: 'PATCH', headers: { 'Content-Type': 'application/json', }, body: JSON.stringify({ visivelCatalogo: !visivelAtual }), }); if (response.ok) { toast.success('Visibilidade atualizada!'); carregarProdutos(); } } catch (error) { console.error('Erro ao atualizar visibilidade:', error); toast.error('Erro ao atualizar visibilidade'); } }; const abrirGerenciarFotos = async (produto) => { setProdutoSelecionado(produto); setModalFotosOpen(true); await carregarFotosAdicionais(produto.id); }; const carregarFotosAdicionais = async (produtoId) => { try { const response = await fetch(`/api/produtos/${produtoId}/fotos-catalogo`); if (response.ok) { const data = await response.json(); setFotosAdicionais(data.fotos || []); } } catch (error) { console.error('Erro ao carregar fotos adicionais:', error); } }; const handleUploadFoto = async (event) => { const file = event.target.files?.[0]; if (!file || !produtoSelecionado) return; // Validar tipo de arquivo const tiposPermitidos = ['image/jpeg', 'image/jpg', 'image/png', 'image/webp', 'image/gif']; if (!tiposPermitidos.includes(file.type)) { toast.error('Tipo de arquivo não permitido. Use JPEG, PNG, WebP ou GIF'); return; } // Validar tamanho (5MB) if (file.size > 5 * 1024 * 1024) { toast.error('Arquivo muito grande. Máximo 5MB'); return; } const formData = new FormData(); formData.append('foto', file); try { setUploadingPhoto(true); const response = await fetch(`/api/produtos/${produtoSelecionado.id}/fotos-catalogo`, { method: 'POST', body: formData, }); const data = await response.json(); if (response.ok) { toast.success('Foto adicionada com sucesso!'); await carregarFotosAdicionais(produtoSelecionado.id); await carregarProdutos(); // Limpar input event.target.value = ''; } else { const errorMsg = data.error || 'Erro ao fazer upload'; console.error('Erro do servidor:', data); toast.error(errorMsg); } } catch (error) { console.error('Erro ao fazer upload:', error); toast.error('Erro ao adicionar foto: ' + error.message); } finally { setUploadingPhoto(false); } }; const deletarFoto = async (fileName) => { if (!produtoSelecionado) return; if (!window.confirm('Tem certeza que deseja remover esta foto?')) return; try { const response = await fetch( `/api/produtos/${produtoSelecionado.id}/fotos-catalogo/${fileName}`, { method: 'DELETE' } ); if (response.ok) { toast.success('Foto removida!'); await carregarFotosAdicionais(produtoSelecionado.id); await carregarProdutos(); } } catch (error) { console.error('Erro ao deletar foto:', error); toast.error('Erro ao remover foto'); } }; if (loading && produtos.length === 0) { return (
Carregando catálogo...
); } const produtosVisiveis = produtos.filter(p => p.visivel_catalogo); const produtosPromocao = produtos.filter(p => p.em_promocao && p.visivel_catalogo); const produtosNovidade = produtos.filter(p => p.novidade && p.visivel_catalogo); return (

Site / Catálogo

Gerencie os produtos visíveis no catálogo online

Ver Catálogo
{/* Configurações do Catálogo */}

Configurações do Catálogo

Configure as opções de exibição do catálogo online

As opções de exibição do catálogo são gerenciadas automaticamente. Utilize o botão abaixo caso precise sincronizar as configurações.

{/* Estatísticas */}
{produtos.length} Total de Produtos
{produtosVisiveis.length} Visíveis
{produtos.length - produtosVisiveis.length} Ocultos
🏷️
{produtosPromocao.length} Em Promoção
{produtosNovidade.length} Novidades
{/* Lista de Produtos */}

Produtos do Catálogo

{produtos.length === 0 ? (

Nenhum produto cadastrado

) : (
{produtos.map((produto) => ( ))}
Foto Produto Preço Normal Preço Promocional Estoque Status Ações
{produto.foto_principal_url || produto.imagem ? ( {produto.nome} ) : (
)}
{produto.nome} {produto.marca || 'Sem marca'}
R$ {parseFloat(produto.valor_revenda || 0).toFixed(2)}
handlePrecoPromocionalChange(produto.id, e.target.value)} onBlur={() => handlePrecoPromocionalBlur(produto.id)} />
0 ? 'in-stock' : 'out-stock'}`}> {produto.estoque_total || 0}
{produto.visivel_catalogo ? : } toggleProdutoNovidade(produto.id, produto.novidade)} > ✨ {produto.novidade ? 'NOVO' : ''} toggleProdutoPromocao(produto.id, produto.em_promocao)} > 🏷️ {produto.em_promocao ? 'PROMO' : ''}
)}
{/* Modal de Gerenciamento de Fotos */} {modalFotosOpen && produtoSelecionado && (
setModalFotosOpen(false)}>
e.stopPropagation()}>

Gerenciar Fotos - {produtoSelecionado.nome}

Adicione fotos extras que aparecerão no catálogo online

{fotosAdicionais.length === 0 ? (

Nenhuma foto adicional

Adicione fotos para exibir no catálogo
) : ( fotosAdicionais.map((foto) => (
{foto.name}
)) )}
)}
); }; export default SiteCatalogo;