feat: KöPi - nur spezifische Filialen anzeigen

This commit is contained in:
2026-07-09 00:02:57 +02:00
parent c544e14171
commit 73ce4264ae
2 changed files with 37 additions and 10 deletions

View File

@@ -56,12 +56,33 @@ function extractNextData(html) {
try { return JSON.parse(html.slice(start, end)); } catch { return null; } try { return JSON.parse(html.slice(start, end)); } catch { return null; }
} }
// ── Händler-Filter ──────────────────────────────────────────────────────────── // ── Gewünschte Filialen ──────────────────────────────────────────────────────
const TARGET_PUBLISHERS = ['rewe','edeka','netto','trinkgut','getränke','penny','aldi','lidl']; const TARGET_STORES = [
{ name: 'edeka', street: 'düsseldorfer landstr', label: 'EDEKA E center Angerbogen' },
{ name: 'edeka', street: 'angerbogen', label: 'EDEKA E center Angerbogen' },
{ name: 'e center', street: 'düsseldorfer', label: 'EDEKA E center Angerbogen' },
{ name: 'rewe', street: 'mündelheimer', label: 'REWE Mündelheimer Str.' },
{ name: 'rewe', street: 'keniastr', label: 'REWE XXL Schwinning' },
{ name: 'netto', street: 'bonnefeld', label: 'Netto Im Bonnefeld' },
{ name: 'kaufland', street: 'auf der höhe', label: 'Kaufland Kaßlerfeld' },
{ name: 'penny', street: 'sittardsberger', label: 'Penny Sittardsberger Allee' },
{ name: 'trinkgut', street: 'keniastr', label: 'Trinkgut Keniastraße' },
];
function matchesTargetStore(publisherName, storeName, street) {
const p = (publisherName || '').toLowerCase();
const s = (storeName || '').toLowerCase();
const r = (street || '').toLowerCase();
for (const t of TARGET_STORES) {
if ((p.includes(t.name) || s.includes(t.name)) && r.includes(t.street)) return t.label;
}
return null;
}
function isTargetPublisher(name) { function isTargetPublisher(name) {
if (!name) return false; if (!name) return false;
const n = name.toLowerCase(); const n = name.toLowerCase();
return TARGET_PUBLISHERS.some(p => n.includes(p)); return ['rewe','edeka','e center','netto','kaufland','penny','trinkgut'].some(p => n.includes(p));
} }
// ── Angebote via Puppeteer (BrochureBox_Basic + OfferGrid) ────────────────── // ── Angebote via Puppeteer (BrochureBox_Basic + OfferGrid) ──────────────────
@@ -171,10 +192,12 @@ async function scrapeProspekte() {
const cached = cacheGet(cacheKey); const cached = cacheGet(cacheKey);
if (cached) return cached; if (cached) return cached;
// Koordinaten rund um deine Filialen (alle im Süden Duisburgs)
const locations = [ const locations = [
{ lat: 51.4332, lng: 6.7625, city: 'Duisburg', zip: '47051' }, { lat: 51.3750, lng: 6.7680, city: 'Duisburg', zip: '47259' }, // Im Bonnefeld / Netto
{ lat: 51.5167, lng: 6.9833, city: 'Essen', zip: '45127' }, { lat: 51.4020, lng: 6.7616, city: 'Duisburg', zip: '47055' }, // REWE Mündelheimer
{ lat: 51.2217, lng: 6.7762, city: 'Düsseldorf', zip: '40213' }, { lat: 51.4406, lng: 6.7555, city: 'Duisburg', zip: '47059' }, // Kaufland / REWE Kenia / Trinkgut
{ lat: 51.3900, lng: 6.7900, city: 'Duisburg', zip: '47259' }, // Penny Sittardsberger
]; ];
const allBrochures = new Map(); const allBrochures = new Map();
@@ -199,20 +222,24 @@ async function scrapeProspekte() {
const publisher = c.publisher?.name || ''; const publisher = c.publisher?.name || '';
const store = c.closestStore; const store = c.closestStore;
const storeStreet = store ? `${store.street || ''} ${store.streetNumber || ''}`.trim() : '';
const matchLabel = matchesTargetStore(publisher, store?.name, storeStreet);
allBrochures.set(id, { allBrochures.set(id, {
id, id,
publisher, publisher,
title: c.title || '', title: c.title || '',
store: store?.name || publisher, store: store?.name || publisher,
storeLabel: matchLabel || store?.name || publisher,
city: store?.city || loc.city, city: store?.city || loc.city,
street: store ? `${store.street || ''} ${store.streetNumber || ''}`.trim() : '', street: storeStreet,
zip: store?.zip || loc.zip, zip: store?.zip || loc.zip,
image: c.brochureImages?.find(i => i.size === '260x270')?.url || c.brochureImage?.url || null, image: c.brochureImages?.find(i => i.size === '260x270')?.url || c.brochureImage?.url || null,
validFrom: c.validFrom || null, validFrom: c.validFrom || null,
validTo: c.validUntil || null, validTo: c.validUntil || null,
pageCount: c.pageCount || null, pageCount: c.pageCount || null,
url: `https://www.kaufda.de/webapp/brochure/${id}`, url: `https://www.kaufda.de/webapp/brochure/${id}`,
isTarget: isTargetPublisher(publisher), isTarget: !!matchLabel,
badges: c.contentBadges?.map(b => b.name) || [], badges: c.contentBadges?.map(b => b.name) || [],
}); });
} }

View File

@@ -183,11 +183,11 @@ function ProspektCard({ b }) {
<div style={{ display:'inline-block', background:`${color}22`, border:`1px solid ${color}44`, <div style={{ display:'inline-block', background:`${color}22`, border:`1px solid ${color}44`,
borderRadius:4, padding:'2px 8px', marginBottom:6, borderRadius:4, padding:'2px 8px', marginBottom:6,
color, fontFamily:'monospace', fontSize:10, fontWeight:700 }}> color, fontFamily:'monospace', fontSize:10, fontWeight:700 }}>
{b.publisher} {b.storeLabel || b.publisher}
</div> </div>
<div style={{ color:'#fff', fontFamily:'monospace', fontSize:11, marginBottom:3, lineHeight:1.4 }}>{b.title}</div> <div style={{ color:'#fff', fontFamily:'monospace', fontSize:11, marginBottom:3, lineHeight:1.4 }}>{b.title}</div>
<div style={{ color:'rgba(255,255,255,0.4)', fontFamily:'monospace', fontSize:10, marginBottom:2 }}> <div style={{ color:'rgba(255,255,255,0.4)', fontFamily:'monospace', fontSize:10, marginBottom:2 }}>
📍 {b.store}{b.city && b.store !== b.city ? ` · ${b.city}` : ''} 📍 {b.street || b.store}{b.city ? ` · ${b.city}` : ''}
</div> </div>
{b.street && <div style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:9 }}>{b.street}</div>} {b.street && <div style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:9 }}>{b.street}</div>}
{(b.validFrom || b.validTo) && ( {(b.validFrom || b.validTo) && (