;(() => {
// 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 (
{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 ? (

) : (
[FOTO] {p.nome}
)}
{p.inmetro &&
INMETRO}
{p.categoria}
{(p.subcategorias || [p.subcategoria]).filter(Boolean).map(s => (
{s}
))}
{p.nome}
{subTitle}
{specs.slice(0, 3).map(s => (
- {s.k}: {s.v}
))}
);
}
function ArrowIconSm() {
return ();
}
function CloseIcon({ size = 14 }) {
return ();
}
function SearchIcon() {
return ();
}
function FilterIcon() {
return ();
}
window.ProdutosPage = ProdutosPage;
window.ProductCard = ProductCard;
})();