;(() => { // PRODUTOS PAGE — catalog with filters const { Link, navigate, WhatsIcon, WHATSAPP_URL, formatSpec } = window.FaukoChrome; // Parse URL query from clean URLs like /produtos?cat=Bombas&seg=Combustíveis function parseQuery() { const qs = window.location.search.replace(/^\?/, ''); if (!qs) return {}; const out = {}; for (const part of qs.split('&')) { if (!part) continue; const [k, v] = part.split('='); out[decodeURIComponent(k)] = decodeURIComponent(v || ''); } return out; } function setQuery(params) { const base = window.location.pathname || '/produtos'; const qs = Object.entries(params).filter(([, v]) => v != null && v !== '').map(([k, v]) => `${encodeURIComponent(k)}=${encodeURIComponent(v)}`).join('&'); window.history.replaceState(null, '', base + (qs ? '?' + qs : '')); } const ALL_CATEGORIES = ['Bombas', 'Medidores de Vazão', 'Bicos de Abastecimento', 'Mangueiras e Carretéis', 'Filtros', 'Tanques']; // ---------- Smart search helpers ---------- // Normalize: lowercase + strip accents function searchNorm(s) { return String(s).toLowerCase().normalize('NFD').replace(/[\u0300-\u036f]/g, ''); } // Build a searchable haystack from ALL product fields + a set of normalized numeric tokens // ("220.0" → "220") so numeric queries match regardless of stored format. function buildSearchIndex(p) { const parts = []; for (const [k, v] of Object.entries(p)) { if (k === 'images' || k === 'id') continue; if (v == null) continue; if (Array.isArray(v)) parts.push(v.join(' ')); else if (typeof v === 'string' || typeof v === 'number') parts.push(String(v)); } if (p.inmetro) parts.push('inmetro certificado'); const norm = searchNorm(parts.join(' ')); const nums = new Set(); for (const m of norm.matchAll(/\d+(?:[.,]\d+)?/g)) { const n = parseFloat(m[0].replace(',', '.')); if (!Number.isNaN(n)) nums.add(String(n)); } return { norm, nums }; } // Single term against one product index. Strips common unit suffixes (v, hp, lpm, w, kg) // so "220v" matches voltagem stored as "220.0". function termMatches(term, index) { const t = searchNorm(term); if (!t) return true; if (index.norm.includes(t)) return true; const stripped = t.replace(/(l\/min|lpm|hp|kg|v|w)$/, ''); if (/^\d+(?:[.,]\d+)?$/.test(stripped)) { const num = String(parseFloat(stripped.replace(',', '.'))); if (index.nums.has(num)) return true; } return false; } // ---------- end search helpers ---------- const SUBCATS = { 'Bombas': ['Elétricas', 'Bateria', 'Manuais', 'Lubrificantes', 'Magnéticas'], 'Medidores de Vazão': ['Mecânicos', 'Digital', 'Turbina Digital', 'Engrenagem Oval'], 'Bicos de Abastecimento': ['Automático', 'Manual'], 'Mangueiras e Carretéis': ['Mangueira', 'Carretel'], 'Filtros': ['Filtro'], 'Tanques': ['Tanque', 'Tanque e Bacia'], }; const ALL_SEGMENTS = ['Combustíveis', 'ARLA', 'Lubrificantes']; const ALL_VOLT = ['12V', '24V', '110V', '220V', '380V', 'Manual', 'Bateria']; const ALL_BRANDS = ['FILL-RITE', 'Adam Pumps', 'GPI', 'Gespasa', 'Maide Machine', 'Hi-Tech', 'DYE']; function ProdutosPage({ products }) { // Read initial filters from URL once (deep links from Home), then keep state local. const initial = React.useMemo(() => { const q = parseQuery(); return { cat: q.cat || '', sub: q.sub || '', seg: q.seg || '', volt: q.volt || '', brand: q.brand || '', inmetro: q.inmetro === '1', search: q.search || '', }; }, []); const [filters, setFilters] = React.useState(initial); const [drawerOpen, setDrawerOpen] = React.useState(false); // NOTE: filters are local-only. We deliberately do NOT sync them to the URL hash — // doing so causes ScrollToTop (listening to route) to fire on every filter change. const update = (key, val) => setFilters(f => ({ ...f, [key]: val, ...(key === 'cat' ? { sub: '' } : {}) })); const reset = () => setFilters({ cat: '', sub: '', seg: '', volt: '', brand: '', inmetro: false, search: '' }); const searchIndexes = React.useMemo(() => { const m = new Map(); for (const p of products) m.set(p.id, buildSearchIndex(p)); return m; }, [products]); const filtered = React.useMemo(() => { const terms = filters.search.trim().split(/\s+/).filter(Boolean); return products.filter(p => { if (filters.cat && p.categoria !== filters.cat) return false; if (filters.sub && !(p.subcategorias || [p.subcategoria]).includes(filters.sub)) return false; if (filters.seg && !(p.segments || []).includes(filters.seg)) return false; if (filters.volt && !(p.voltagemTags || []).includes(filters.volt)) return false; if (filters.brand && p.brand !== filters.brand) return false; if (filters.inmetro && !p.inmetro) return false; if (terms.length > 0) { const index = searchIndexes.get(p.id); // AND logic: every typed term must match some field if (!terms.every(t => termMatches(t, index))) return false; } return true; }); }, [products, filters, searchIndexes]); // Counters per cat const counts = React.useMemo(() => { const c = { all: products.length }; for (const p of products) c[p.categoria] = (c[p.categoria] || 0) + 1; return c; }, [products]); const activeChips = []; if (filters.cat) activeChips.push({ key: 'cat', label: filters.cat }); if (filters.sub) activeChips.push({ key: 'sub', label: filters.sub }); if (filters.seg) activeChips.push({ key: 'seg', label: filters.seg }); if (filters.volt) activeChips.push({ key: 'volt', label: filters.volt }); if (filters.brand) activeChips.push({ key: 'brand', label: filters.brand }); if (filters.inmetro) activeChips.push({ key: 'inmetro', label: 'INMETRO' }); return (
Catálogo

Catálogo completo de equipamentos para abastecimento

86 equipamentos das principais marcas mundiais — bombas, medidores, bicos, mangueiras, filtros e tanques para combustíveis, ARLA 32 e lubrificantes.

update('search', e.target.value)} />
{filtered.length} de {products.length} equipamentos
{activeChips.length > 0 && (
{activeChips.map(c => ( ))}
)}
{filtered.map(p => )} {filtered.length === 0 && (

Nenhum equipamento encontrado

Tente outros filtros ou fale com a equipe — temos itens fora do catálogo padrão.

Fale com a Fauko
)}
); } function FilterGroup({ label, children }) { return (

{label}

{children}
); } function FilterOption({ label, count, active, onClick }) { return ( ); } function ProductCard({ product: p }) { const subTitle = [p.codigo && `Cód. ${p.codigo}`, p.brand].filter(Boolean).join(' · '); const specs = []; if (p.vazao) specs.push({ k: 'Vazão', v: formatSpec(p.vazao) }); if (p.voltagem) { const volt = String(formatSpec(p.voltagem)); specs.push({ k: 'Voltagem', v: volt + (/v$/i.test(volt) ? '' : 'V') }); } if (p.motor) specs.push({ k: 'Motor', v: formatSpec(p.motor) }); if (p.capacidade) specs.push({ k: 'Capacidade', v: formatSpec(p.capacidade) }); if (p.entrada) specs.push({ k: 'Entrada', v: formatSpec(p.entrada) }); return (
navigate(window.FaukoSEO.productPath(p))} role="link" tabIndex={0} aria-label={p.nome} onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') { e.preventDefault(); navigate(window.FaukoSEO.productPath(p)); } }}>
{p.images && p.images.length > 0 ? ( {p.nome} ) : (
[FOTO] {p.nome}
)} {p.inmetro && INMETRO}
{p.categoria} {(p.subcategorias || [p.subcategoria]).filter(Boolean).map(s => ( {s} ))}

{p.nome}

{subTitle}
Ver detalhes e.stopPropagation()}> Orçar
); } function ArrowIconSm() { return (); } function CloseIcon({ size = 14 }) { return (); } function SearchIcon() { return (); } function FilterIcon() { return (); } window.ProdutosPage = ProdutosPage; window.ProductCard = ProductCard; })();