// Product catalog — frontend fixture until the PHP / MySQL backend takes over. // Shape and helper are the same surface the API module will eventually expose. export const products = [ { id: 'kaiser-natron-pulver-50-g-beutel', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Pulver', size: '50 g Beutel', category: 'Pulver', price: 1.29, image: '/products/kaiser-natron-pulver-50-g-beutel.jpg', href: '/shop/kaiser-natron-pulver-50-g-beutel', inStock: true, keywords: ['natron', 'backsoda', 'bicarbonat', 'natriumhydrogencarbonat', 'baking soda'], }, { id: 'kaiser-natron-pulver-250-g-grosspackung', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Pulver', size: '250 g Großpackung', category: 'Pulver', price: 4.49, image: '/products/cutouts/kaiser-natron-pulver-250-g-gro%C3%9Fpackung-removebg-preview%20%281%29.png', href: '/shop/kaiser-natron-pulver-250-g-grosspackung', inStock: true, keywords: ['natron', 'backsoda', 'großpackung', 'bicarbonat', 'baking soda', 'vorrat'], }, { id: 'kaiser-natron-pulver-3490-g-eimer', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Pulver', size: '3.490 g Eimer', category: 'Pulver', price: 29.9, image: '/products/kaiser-natron-pulver-3.490-g-eimer.jpg', href: '/shop/kaiser-natron-pulver-3490-g-eimer', inStock: true, keywords: ['natron', 'eimer', 'großgebinde', 'gastro', 'gewerbe', 'baking soda'], }, { id: 'kaiser-natron-tabletten-100-g-dose', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Tabletten', size: '100 g Dose', category: 'Tabletten', price: 3.49, image: '/products/kaiser-natron-tabletten-100-g-dose.jpg', href: '/shop/kaiser-natron-tabletten-100-g-dose', inStock: true, keywords: ['natron', 'tabletten', 'magen', 'sodbrennen', 'verdauung'], }, { id: 'kaiser-natron-bad-500-g', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Bad', size: '500 g', category: 'Körperpflege', price: 5.49, image: '/products/kaiser-natron-bad-500-g.jpg', href: '/shop/kaiser-natron-bad-500-g', inStock: true, keywords: ['basenbad', 'wellness', 'entspannung', 'haut', 'wanne'], }, { id: 'kaiser-natron-fussbad-500-g', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Fußbad', size: '500 g', category: 'Körperpflege', price: 5.49, image: '/products/kaiser-natron-fussbad-500-g.jpg', href: '/shop/kaiser-natron-fussbad-500-g', inStock: true, keywords: ['fusspflege', 'basenbad', 'wellness', 'entspannung'], }, { id: 'kaiser-natron-daunenwasch-250-ml', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Daunenwasch', size: '250 ml', category: 'Wäsche', price: 6.9, image: '/products/kaiser-natron-daunenwasch-250-ml.jpg', href: '/shop/kaiser-natron-daunenwasch-250-ml', inStock: true, keywords: ['daunen', 'wäsche', 'bettdecke', 'kissen', 'pflege'], }, { id: 'kaiser-natron-sport-profi-250-ml', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Sport Profi', size: '250 ml', category: 'Sport', price: 7.9, image: '/products/kaiser-natron-sport-profi-250-ml.jpg', href: '/shop/kaiser-natron-sport-profi-250-ml', inStock: true, keywords: ['sport', 'wäsche', 'funktionskleidung', 'geruch'], }, { id: 'kaiser-natron-spuelmittel-500-ml', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Spülmittel', size: '500 ml', category: 'Reinigung', price: 3.9, image: '/products/kaiser-natron-spuelmittel-500-ml.jpg', href: '/shop/kaiser-natron-spuelmittel-500-ml', inStock: true, keywords: ['geschirr', 'spülen', 'küche', 'reinigung'], }, { id: 'kaiser-natron-allzweck-reiniger-750-ml', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Allzweck-Reiniger', size: '750 ml', category: 'Reinigung', price: 4.49, image: '/products/kaiser-natron-allzweck-reiniger-750-ml.jpg', href: '/shop/kaiser-natron-allzweck-reiniger-750-ml', inStock: true, keywords: ['reinigung', 'allzweck', 'universal', 'haushalt'], }, { id: 'kaiser-natron-allzweck-spray-500-ml', brand: 'Kaiser-Natron', title: 'Kaiser-Natron® Allzweck-Spray', size: '500 ml', category: 'Reinigung', price: 4.9, image: '/products/kaiser-natron-allzweck-spray-500-ml.jpg', href: '/shop/kaiser-natron-allzweck-spray-500-ml', inStock: true, keywords: ['reinigung', 'allzweck', 'spray', 'haushalt'], }, { id: 'holste-wasch-soda-500-g-beutel', brand: 'Holste', title: 'Holste Wasch-Soda', size: '500 g Beutel', category: 'Wäsche', price: 2.9, image: '/products/holste-wasch-soda-500-g-beutel.jpg', href: '/shop/holste-wasch-soda-500-g-beutel', inStock: true, keywords: ['soda', 'wasch-soda', 'waschen', 'natriumcarbonat'], }, { id: 'holste-handwaschpaste-500-ml', brand: 'Holste', title: 'Holste Handwaschpaste', size: '500 ml', category: 'Reinigung', price: 6.9, image: '/products/holste-handwaschpaste-500-ml.jpg', href: '/shop/holste-handwaschpaste-500-ml', inStock: true, keywords: ['hände', 'werkstatt', 'arbeit', 'grobe verschmutzung'], }, { id: 'holste-kalk-und-urinsteinloeser-750-ml', brand: 'Holste', title: 'Holste Kalk- und Urinsteinlöser', size: '750 ml', category: 'Reinigung', price: 5.9, image: '/products/holste-kalk--und-urinsteinloeser-750-ml.jpg', href: '/shop/holste-kalk-und-urinsteinloeser-750-ml', inStock: true, keywords: ['kalk', 'urinstein', 'bad', 'wc', 'toilette', 'entkalker'], }, { id: 'holste-reisstaerke-250-g-faltschachtel', brand: 'Holste', title: 'Holste Reisstärke', size: '250 g Faltschachtel', category: 'Wäsche', price: 3.9, image: '/products/holste-reisstaerke-250-g-faltschachtel.jpg', href: '/shop/holste-reisstaerke-250-g-faltschachtel', inStock: true, keywords: ['stärke', 'reisstärke', 'wäsche', 'bügeln'], }, { id: 'holste-schmierseife-fluessig-1-l-flasche', brand: 'Holste', title: 'Holste Schmierseife flüssig', size: '1 l Flasche', category: 'Reinigung', price: 6.49, image: '/products/holste-schmierseife-fluessig-1-l-flasche.jpg', href: '/shop/holste-schmierseife-fluessig-1-l-flasche', inStock: true, keywords: ['schmierseife', 'naturseife', 'boden', 'reinigung'], }, { id: 'holste-zitronensaeure-entkalker-fluessig-500-ml', brand: 'Holste', title: 'Holste Zitronensäure-Entkalker flüssig', size: '500 ml', category: 'Reinigung', price: 4.9, image: '/products/holste-zitronensaeure-entkalker-fluessig-500-ml.jpg', href: '/shop/holste-zitronensaeure-entkalker-fluessig-500-ml', inStock: true, keywords: ['zitronensäure', 'entkalker', 'kalk', 'haushalt', 'küche'], }, { id: 'gazelle-waeschestaerke-1000-ml-flasche', brand: 'Gazelle', title: 'Gazelle Wäschestärke', size: '1.000 ml Flasche', category: 'Wäsche', price: 5.9, image: '/products/gazelle-waeschestaerke-1000-ml-flasche.jpg', href: '/shop/gazelle-waeschestaerke-1000-ml-flasche', inStock: true, keywords: ['wäschestärke', 'stärke', 'bügeln', 'tischwäsche'], }, { id: 'gruene-tante-mit-quarzmehl-500-ml-dose', brand: 'Grüne Tante', title: 'Grüne Tante mit Quarzmehl', size: '500 ml Dose', category: 'Reinigung', price: 7.9, image: '/products/gruene-tante-mit-quarzmehl-500-ml-dose.jpg', href: '/shop/gruene-tante-mit-quarzmehl-500-ml-dose', inStock: true, keywords: ['handreiniger', 'werkstatt', 'quarzmehl', 'grobe verschmutzung'], }, { id: 'linda-fleckenweg-200-ml-tube', brand: 'Linda', title: 'Linda Fleckenweg', size: '200 ml Tube', category: 'Wäsche', price: 4.9, image: '/products/linda-fleckenweg-200-ml-tube.jpg', href: '/shop/linda-fleckenweg-200-ml-tube', inStock: true, keywords: ['fleck', 'fleckenentferner', 'vorbehandlung', 'wäsche'], }, { id: 'linda-handreiniger-der-kraftvolle-200-g-tube', brand: 'Linda', title: 'Linda Handreiniger – Der Kraftvolle', size: '200 g Tube', category: 'Reinigung', price: 5.9, image: '/products/linda-handreiniger-der-kraftvolle-200-g-tube.jpg', href: '/shop/linda-handreiniger-der-kraftvolle-200-g-tube', inStock: true, keywords: ['handreiniger', 'werkstatt', 'öl', 'fett'], }, { id: 'linda-neutral-375-ml-dose', brand: 'Linda', title: 'Linda Neutral', size: '375 ml Dose', category: 'Reinigung', price: 5.49, image: '/products/linda-neutral-375-ml-dose.jpg', href: '/shop/linda-neutral-375-ml-dose', inStock: true, keywords: ['handreiniger', 'neutral', 'haut', 'empfindlich'], }, ] // Normalize for search: lowercase + fold German diacritics so "Spülmittel" // matches "spulmittel" and "großpackung" matches "grosspackung". function normalize(input) { if (!input) return '' return String(input) .toLowerCase() .replace(/ß/g, 'ss') .replace(/ä/g, 'ae') .replace(/ö/g, 'oe') .replace(/ü/g, 'ue') .normalize('NFD') .replace(/[̀-ͯ]/g, '') } function escapeRegExp(s) { return s.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') } // Ranked match: every query term must hit at least one field; fields have // weights so a title hit outranks a keyword hit. Exact > startsWith > // word-boundary > substring. Terms AND together (all must match something). export function searchProducts(query, list = products, limit = 8) { const q = normalize(query).trim() if (!q) return [] const terms = q.split(/\s+/).filter(Boolean) const scored = [] for (const product of list) { const haystacks = [ { text: normalize(product.title), weight: 3 }, { text: normalize(product.brand), weight: 2.2 }, { text: normalize(product.category), weight: 1.4 }, { text: normalize(product.size), weight: 1.2 }, { text: normalize((product.keywords || []).join(' ')), weight: 1 }, ] let total = 0 let allTermsMatched = true for (const term of terms) { const boundary = new RegExp(`\\b${escapeRegExp(term)}`) let best = 0 for (const { text, weight } of haystacks) { if (!text) continue let s = 0 if (text === term) s = 100 else if (text.startsWith(term)) s = 80 else if (boundary.test(text)) s = 55 else if (text.includes(term)) s = 25 if (s * weight > best) best = s * weight } if (best === 0) { allTermsMatched = false break } total += best } if (allTermsMatched) scored.push({ product, score: total }) } scored.sort((a, b) => b.score - a.score) return scored.slice(0, limit).map((s) => s.product) } export function formatPrice(amount, currency = '€') { const num = typeof amount === 'number' ? amount : Number(amount) if (!Number.isFinite(num)) return '' return `${currency} ${num.toFixed(2).replace('.', ',')}` }