feat: KoePi bestellmagazin-Filter, KW ohne Klammern erkannt, kommende Prospekte nach Woche unterteilt, Bier-Icon-Prospekte nach oben sortiert
This commit is contained in:
@@ -166,11 +166,15 @@ async function fetchLeafletDetail(leafletId, cache) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Extrahiert "(KW29 Do-Sa)"-artige Klammerzusätze aus dem Leaflet-Namen, falls
|
// Extrahiert eine "(KW29 Do-Sa)"-artige Angabe aus dem Leaflet-Namen. Manche
|
||||||
// vorhanden (nicht jede Filiale/jeder Prospekt hat sowas)
|
// Ketten (z.B. ALDI) schreiben die KW-Angabe ohne umschließende Klammern
|
||||||
|
// ("... KW32 ...") — die wird dann selbst eingeklammert, damit die Anzeige
|
||||||
|
// überall einheitlich aussieht.
|
||||||
function extractWeekInfo(name) {
|
function extractWeekInfo(name) {
|
||||||
const m = (name || '').match(/\([^)]*KW\s*\d+[^)]*\)/i);
|
const bracket = (name || '').match(/\([^)]*KW\s*\d+[^)]*\)/i);
|
||||||
return m ? m[0] : '';
|
if (bracket) return bracket[0];
|
||||||
|
const bare = (name || '').match(/KW\s*\d+(?:\s*[A-Za-zÄÖÜäöüß-]+)?/i);
|
||||||
|
return bare ? `(${bare[0]})` : '';
|
||||||
}
|
}
|
||||||
|
|
||||||
// Löst die feste Liste gewünschter Filialen (LOCAL_STORE_SEED_LEAFLETS) einmalig
|
// Löst die feste Liste gewünschter Filialen (LOCAL_STORE_SEED_LEAFLETS) einmalig
|
||||||
@@ -561,7 +565,7 @@ async function scrapeProspekte() {
|
|||||||
|
|
||||||
// "Sondermagazin"-Prospekte ausblenden (keine normalen Wochenangebote)
|
// "Sondermagazin"-Prospekte ausblenden (keine normalen Wochenangebote)
|
||||||
// Unerwünschte Prospekt-Typen ausblenden (Sondermagazine, Reise-Prospekte etc.)
|
// Unerwünschte Prospekt-Typen ausblenden (Sondermagazine, Reise-Prospekte etc.)
|
||||||
const EXCLUDED_TITLE_KEYWORDS = ['sondermagazin', 'reisen'];
|
const EXCLUDED_TITLE_KEYWORDS = ['sondermagazin', 'reisen', 'bestellmagazin'];
|
||||||
const withoutSondermagazin = all.filter(b => {
|
const withoutSondermagazin = all.filter(b => {
|
||||||
const t = (b.title || '').toLowerCase();
|
const t = (b.title || '').toLowerCase();
|
||||||
return !EXCLUDED_TITLE_KEYWORDS.some(kw => t.includes(kw));
|
return !EXCLUDED_TITLE_KEYWORDS.some(kw => t.includes(kw));
|
||||||
|
|||||||
@@ -45,6 +45,39 @@ function fmtDate(str) {
|
|||||||
catch { return str; }
|
catch { return str; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Prospekte mit Bier-Icon (König Pilsener im Angebot) immer nach oben,
|
||||||
|
// ansonsten Reihenfolge vom Backend (chronologisch) beibehalten
|
||||||
|
function sortBeerFirst(arr) {
|
||||||
|
return [...arr].sort((a, b) => (b.hasKoenigPilsener?1:0) - (a.hasKoenigPilsener?1:0));
|
||||||
|
}
|
||||||
|
|
||||||
|
// ISO-Kalenderwoche aus einem Datum berechnen
|
||||||
|
function getISOWeek(dateStr) {
|
||||||
|
const d = new Date(dateStr);
|
||||||
|
d.setHours(0,0,0,0);
|
||||||
|
d.setDate(d.getDate() + 4 - (d.getDay() || 7));
|
||||||
|
const yearStart = new Date(d.getFullYear(), 0, 1);
|
||||||
|
return { week: Math.ceil((((d - yearStart) / 86400000) + 1) / 7), year: d.getFullYear() };
|
||||||
|
}
|
||||||
|
|
||||||
|
// Kommende Prospekte nach Kalenderwoche gruppieren, Gruppen chronologisch sortiert
|
||||||
|
function groupByWeek(arr) {
|
||||||
|
const groups = new Map();
|
||||||
|
for (const b of arr) {
|
||||||
|
if (!b.validFrom) { // kein Datum -> eigene Sammelgruppe ganz unten
|
||||||
|
const key = 'unbekannt';
|
||||||
|
if (!groups.has(key)) groups.set(key, { label: 'Ohne Datum', sortKey: Infinity, items: [] });
|
||||||
|
groups.get(key).items.push(b);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
const { week, year } = getISOWeek(b.validFrom);
|
||||||
|
const key = `${year}-${week}`;
|
||||||
|
if (!groups.has(key)) groups.set(key, { label: `KW${week}`, sortKey: year*100+week, items: [] });
|
||||||
|
groups.get(key).items.push(b);
|
||||||
|
}
|
||||||
|
return [...groups.values()].sort((a,b) => a.sortKey - b.sortKey);
|
||||||
|
}
|
||||||
|
|
||||||
// ── Angebots-Karte (marktguru) ────────────────────────────────────────────────
|
// ── Angebots-Karte (marktguru) ────────────────────────────────────────────────
|
||||||
function OfferCard({ offer }) {
|
function OfferCard({ offer }) {
|
||||||
const color = publisherColor(offer.retailer);
|
const color = publisherColor(offer.retailer);
|
||||||
@@ -301,7 +334,7 @@ export default function Koepi({ toast }) {
|
|||||||
<>
|
<>
|
||||||
<div style={{ ...S.head, marginBottom:12 }}>DEINE MÄRKTE — AKTUELL ({prospekte.current.length})</div>
|
<div style={{ ...S.head, marginBottom:12 }}>DEINE MÄRKTE — AKTUELL ({prospekte.current.length})</div>
|
||||||
<div style={{ display:'grid', gridTemplateColumns:'repeat(2,1fr)', gap:12, marginBottom:24 }}>
|
<div style={{ display:'grid', gridTemplateColumns:'repeat(2,1fr)', gap:12, marginBottom:24 }}>
|
||||||
{prospekte.current.map(b=><ProspektCard key={b.id} b={b}/>)}
|
{sortBeerFirst(prospekte.current).map(b=><ProspektCard key={b.id} b={b}/>)}
|
||||||
</div>
|
</div>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
@@ -311,9 +344,16 @@ export default function Koepi({ toast }) {
|
|||||||
color:'rgba(255,255,255,0.35)' }}>
|
color:'rgba(255,255,255,0.35)' }}>
|
||||||
KOMMENDE PROSPEKTE ({prospekte.future.length})
|
KOMMENDE PROSPEKTE ({prospekte.future.length})
|
||||||
</div>
|
</div>
|
||||||
<div style={{ display:'grid', gridTemplateColumns:'repeat(2,1fr)', gap:12, opacity:0.75 }}>
|
{groupByWeek(prospekte.future).map(group => (
|
||||||
{prospekte.future.map(b=><ProspektCard key={b.id} b={b}/>)}
|
<div key={group.label} style={{ marginBottom:20 }}>
|
||||||
</div>
|
<div style={{ color:'rgba(255,255,255,0.3)', fontFamily:'monospace', fontSize:10, letterSpacing:1, marginBottom:8 }}>
|
||||||
|
{group.label}
|
||||||
|
</div>
|
||||||
|
<div style={{ display:'grid', gridTemplateColumns:'repeat(2,1fr)', gap:12, opacity:0.75 }}>
|
||||||
|
{sortBeerFirst(group.items).map(b=><ProspektCard key={b.id} b={b}/>)}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
{!prospekte.current?.length && !prospekte.future?.length && (
|
{!prospekte.current?.length && !prospekte.future?.length && (
|
||||||
|
|||||||
Reference in New Issue
Block a user