Codex-Handover P0: Daten-Ehrlichkeit (Seed=Demo statt 'live/verfügbar', Fake-Namen+Mojibake raus), Finder modell-Param, LIVE_URL via Env, 404-Seite+Favicon, Umami nur mit echter ID, ehrliche Texte/Semantik, Demo-CTA entschärft
This commit is contained in:
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -0,0 +1 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 64 64"><rect width="64" height="64" rx="14" fill="#1f5a62"/><g fill="none" stroke="#fff" stroke-width="4" stroke-linecap="round"><path d="M32 14v36M16 23l32 18M48 23 16 41"/></g></svg>
|
||||||
|
After Width: | Height: | Size: 238 B |
@@ -11,12 +11,12 @@ import '../styles/global.css';
|
|||||||
<title>{title}</title>
|
<title>{title}</title>
|
||||||
<meta name="description" content={desc} />
|
<meta name="description" content={desc} />
|
||||||
<link rel="canonical" href={Astro.url.href} />
|
<link rel="canonical" href={Astro.url.href} />
|
||||||
|
<link rel="icon" href="/favicon.svg" type="image/svg+xml" />
|
||||||
|
<link rel="apple-touch-icon" href="/favicon.svg" />
|
||||||
<meta property="og:title" content={title} />
|
<meta property="og:title" content={title} />
|
||||||
<meta property="og:description" content={desc} />
|
<meta property="og:description" content={desc} />
|
||||||
<meta property="og:type" content="website" />
|
<meta property="og:type" content="website" />
|
||||||
{jsonld && <script type="application/ld+json" set:html={JSON.stringify(jsonld)} />}
|
{jsonld && <script type="application/ld+json" set:html={JSON.stringify(jsonld)} />}
|
||||||
<!-- Umami Analytics (self-hosted). TODO: data-website-id setzen, Host prüfen. -->
|
|
||||||
<script defer is:inline src="https://umami.heidrich-digital.de/script.js" data-website-id="REPLACE_WITH_UMAMI_WEBSITE_ID" data-domains="kuehlfinder.de"></script>
|
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
<header class="site"><div class="wrap bar">
|
<header class="site"><div class="wrap bar">
|
||||||
|
|||||||
@@ -0,0 +1,15 @@
|
|||||||
|
---
|
||||||
|
import Base from '../layouts/Base.astro';
|
||||||
|
---
|
||||||
|
<Base title="Seite nicht gefunden – Kühlfinder" description="Diese Seite gibt es nicht.">
|
||||||
|
<section class="wrap" style="margin:3rem auto;max-width:560px;text-align:center">
|
||||||
|
<span class="eyebrow">404</span>
|
||||||
|
<h1>Seite nicht gefunden</h1>
|
||||||
|
<p class="muted">Diese Seite existiert nicht (mehr). Weiter geht's hier:</p>
|
||||||
|
<p style="display:flex;gap:.6rem;justify-content:center;flex-wrap:wrap;margin-top:1rem">
|
||||||
|
<a class="btn" href="/karte">Zur Live-Karte</a>
|
||||||
|
<a class="btn ghost" href="/">Startseite</a>
|
||||||
|
<a class="btn ghost" href="/finder">Finder</a>
|
||||||
|
</p>
|
||||||
|
</section>
|
||||||
|
</Base>
|
||||||
@@ -50,7 +50,7 @@ const products = allProducts.map(p=>({slug:p.slug,name:p.name,type:p.type,badge:
|
|||||||
<div class="reco"><span class="badge">Budget</span>${card(r.budget)}</div>
|
<div class="reco"><span class="badge">Budget</span>${card(r.budget)}</div>
|
||||||
${r.warn?`<p class="warn">${r.warn}</p>`:''}
|
${r.warn?`<p class="warn">${r.warn}</p>`:''}
|
||||||
<div class="rcta">
|
<div class="rcta">
|
||||||
<a class="btn" href="/karte?produkt=${r.primary.slug}">Verfügbarkeit auf Karte</a>
|
<a class="btn" href="/karte?modell=${r.primary.slug}">Verfügbarkeit auf Karte</a>
|
||||||
<a class="btn ghost" href="/alarm?produkt=${r.primary.slug}">Alarm setzen</a>
|
<a class="btn ghost" href="/alarm?produkt=${r.primary.slug}">Alarm setzen</a>
|
||||||
<button class="btn ghost" id="again">Nochmal</button>
|
<button class="btn ghost" id="again">Nochmal</button>
|
||||||
</div>`;
|
</div>`;
|
||||||
|
|||||||
+12
-9
@@ -15,7 +15,7 @@ const Bell='<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="
|
|||||||
<WeatherBanner />
|
<WeatherBanner />
|
||||||
<span class="eyebrow" style="margin-top:1rem"><span class="dot ok live"></span>Live-Verfügbarkeit · eigener Scraper</span>
|
<span class="eyebrow" style="margin-top:1rem"><span class="dot ok live"></span>Live-Verfügbarkeit · eigener Scraper</span>
|
||||||
<h1>Wo gibt es gerade ein Klimagerät?</h1>
|
<h1>Wo gibt es gerade ein Klimagerät?</h1>
|
||||||
<p class="lead">Bei Hitze ist alles vergriffen. Kühlfinder ist dein Kontrollzentrum: <strong>live sehen, welcher Markt in der Nähe eins auf Lager hat</strong> – Baumärkte, Elektromärkte, Online & eBay – mit Prognose, Preis-Check gegen Wucher und Alarm. <em>Die nächste Hitzewelle kommt bestimmt.</em></p>
|
<p class="lead">Bei Hitze ist alles vergriffen. Kühlfinder prüft <strong>Preise live direkt bei Händlern</strong> und zeigt ehrlich, wo geprüft ist – plus Standort-Abdeckung, Preis-Check gegen Wucher und Alarm. <em>Die nächste Hitzewelle kommt bestimmt.</em></p>
|
||||||
<form class="herobar" action="/karte" method="get">
|
<form class="herobar" action="/karte" method="get">
|
||||||
<input name="plz" inputmode="numeric" placeholder="PLZ oder Ort …" aria-label="PLZ oder Ort" />
|
<input name="plz" inputmode="numeric" placeholder="PLZ oder Ort …" aria-label="PLZ oder Ort" />
|
||||||
<button class="btn" type="submit" data-umami-event="hero_plz_suche">Verfügbarkeit zeigen</button>
|
<button class="btn" type="submit" data-umami-event="hero_plz_suche">Verfügbarkeit zeigen</button>
|
||||||
@@ -23,12 +23,12 @@ const Bell='<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="
|
|||||||
</form>
|
</form>
|
||||||
<div class="livewrap reveal">
|
<div class="livewrap reveal">
|
||||||
<div class="pills">
|
<div class="pills">
|
||||||
<span class="pill-stat"><b class="num" id="p-ac" data-count="0">…</b> Märkte mit Klimagerät jetzt</span>
|
<span class="pill-stat"><b class="num" id="p-ac">…</b> Live-Preise geprüft</span>
|
||||||
<span class="pill-stat"><b class="num" id="p-total" data-count="0">…</b> Märkte überwacht</span>
|
<span class="pill-stat"><b class="num" id="p-total" data-count="0">…</b> Märkte (Abdeckung)</span>
|
||||||
<span class="pill-stat"><b class="num">6</b> Länder · <span id="p-chains">…</span> Ketten</span>
|
<span class="pill-stat"><b class="num">6</b> Länder · <span id="p-chains">…</span> Ketten</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="livecard card">
|
<div class="livecard card">
|
||||||
<div class="lc-head"><span class="dot ok live"></span> Gerade verfügbar</div>
|
<div class="lc-head"><span class="dot ok live"></span> Live geprüfte Preise</div>
|
||||||
<ul class="livelist" id="livelist"><li class="skel" style="height:40px"></li><li class="skel" style="height:40px"></li><li class="skel" style="height:40px"></li></ul>
|
<ul class="livelist" id="livelist"><li class="skel" style="height:40px"></li><li class="skel" style="height:40px"></li><li class="skel" style="height:40px"></li></ul>
|
||||||
<a class="btn sm" href="/karte">Alle auf der Karte →</a>
|
<a class="btn sm" href="/karte">Alle auf der Karte →</a>
|
||||||
</div>
|
</div>
|
||||||
@@ -122,13 +122,16 @@ const Bell='<svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="
|
|||||||
<script>
|
<script>
|
||||||
const euro=(n,c)=>n==null?'–':new Intl.NumberFormat('de-DE',{style:'currency',currency:c||'EUR',maximumFractionDigits:0}).format(n);
|
const euro=(n,c)=>n==null?'–':new Intl.NumberFormat('de-DE',{style:'currency',currency:c||'EUR',maximumFractionDigits:0}).format(n);
|
||||||
document.getElementById('locbtn')?.addEventListener('click',e=>{ if(navigator.geolocation){ /* Karte hat eigene Standortfunktion */ } });
|
document.getElementById('locbtn')?.addEventListener('click',e=>{ if(navigator.geolocation){ /* Karte hat eigene Standortfunktion */ } });
|
||||||
|
const CRAWLER=(import.meta.env.PUBLIC_CRAWLER_URL)||'http://vnqwgi4gq520hs1p4bsgi4w6.46.224.49.66.sslip.io';
|
||||||
fetch('/data/monitor.json').then(r=>r.json()).then(d=>{
|
fetch('/data/monitor.json').then(r=>r.json()).then(d=>{
|
||||||
const ac=document.getElementById('p-ac'), tot=document.getElementById('p-total');
|
const tot=document.getElementById('p-total');
|
||||||
ac.setAttribute('data-count', d.stats.withAC); ac.removeAttribute('data-done');
|
|
||||||
tot.setAttribute('data-count', d.stats.storesTotal); tot.removeAttribute('data-done');
|
tot.setAttribute('data-count', d.stats.storesTotal); tot.removeAttribute('data-done');
|
||||||
const chains=new Set(d.stores.map(s=>s.c)); document.getElementById('p-chains').textContent=chains.size;
|
document.getElementById('p-chains').textContent=new Set(d.stores.map(s=>s.c)).size;
|
||||||
window.kfAnim && window.kfAnim();
|
window.kfAnim && window.kfAnim();
|
||||||
const avail=d.stores.filter(s=>s.any.a && s.any.minP).sort(()=>Math.random()-0.5).slice(0,6);
|
|
||||||
document.getElementById('livelist').innerHTML = avail.map(s=>`<li><span><span class="flag">${s.flag||''}</span> <span class="lm">${s.c}</span> <span class="lc">${s.ci}</span></span><span class="lp">${euro(s.any.minP,s.cur)}</span></li>`).join('') || '<li class="lc">Aktuell sehr knapp – Alarm setzen lohnt sich.</li>';
|
|
||||||
}).catch(()=>{});
|
}).catch(()=>{});
|
||||||
|
fetch(CRAWLER+'/api/live').then(r=>r.json()).then(d=>{
|
||||||
|
const offers=(d.offers||[]).filter(o=>o.price);
|
||||||
|
document.getElementById('p-ac').textContent=offers.length;
|
||||||
|
document.getElementById('livelist').innerHTML = offers.slice(0,6).map(o=>`<li><span><span class="lm">${o.chain}</span> <span class="lc">${o.name}</span></span><span class="lp">${euro(o.price)}</span></li>`).join('') || '<li class="lc">Crawler startet – gleich wieder reinschauen.</li>';
|
||||||
|
}).catch(()=>{document.getElementById('p-ac').textContent='–';document.getElementById('livelist').innerHTML='<li class="lc">Live-Preise gerade nicht erreichbar.</li>';});
|
||||||
</script>
|
</script>
|
||||||
|
|||||||
+15
-13
@@ -11,7 +11,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
<WeatherBanner />
|
<WeatherBanner />
|
||||||
<div class="mon-hero-row" style="margin-top:1rem">
|
<div class="mon-hero-row" style="margin-top:1rem">
|
||||||
<div>
|
<div>
|
||||||
<span class="eyebrow"><span class="dot ok live"></span>Live · eigener Scraper · 6 Länder</span>
|
<span class="eyebrow"><span class="dot ok live"></span>Live-Preis-Crawler · Standort-Abdeckung (Demo) · 6 Länder</span>
|
||||||
<h1>Dein Klimagerät-Kontrollzentrum</h1>
|
<h1>Dein Klimagerät-Kontrollzentrum</h1>
|
||||||
<p class="lead">Live-Bestand in Baumärkten, Elektromärkten, Online & eBay – mit <strong>Verfügbarkeits-Prognose</strong> und <strong>Preis-Check gegen Wucher</strong>.</p>
|
<p class="lead">Live-Bestand in Baumärkten, Elektromärkten, Online & eBay – mit <strong>Verfügbarkeits-Prognose</strong> und <strong>Preis-Check gegen Wucher</strong>.</p>
|
||||||
</div>
|
</div>
|
||||||
@@ -19,11 +19,11 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
</div>
|
</div>
|
||||||
<div class="delaybar reveal">
|
<div class="delaybar reveal">
|
||||||
<span class="dot ok live"></span>
|
<span class="dot ok live"></span>
|
||||||
<span>Kostenlos siehst du Live-Daten <strong>~10 Min zeitversetzt</strong>. <a href="/pro">Mit Pro in Echtzeit + Push →</a></span>
|
<span><strong>Filialbestände sind Demo-Abdeckung (nicht verifiziert).</strong> Verifizierte <strong>Live-Preise</strong> (eigener Crawler) stehen unten in der Preis-Leiste. <a href="/pro">Pro: Echtzeit + Alarm →</a></span>
|
||||||
</div>
|
</div>
|
||||||
<div class="stats" id="stats">
|
<div class="stats" id="stats">
|
||||||
<div class="stat"><span class="note">Märkte</span><b class="num" data-k="storesTotal">–</b></div>
|
<div class="stat"><span class="note">Märkte (Abdeckung)</span><b class="num" data-k="storesTotal">–</b></div>
|
||||||
<div class="stat ok"><span class="note">mit Klimagerät</span><b class="num" data-k="withAC">–</b></div>
|
<div class="stat ok"><span class="note">Live-Preise geprüft</span><b class="num" id="s-live">–</b></div>
|
||||||
<div class="stat"><span class="note">Baumärkte</span><b class="num" data-k="baumarkt">–</b></div>
|
<div class="stat"><span class="note">Baumärkte</span><b class="num" data-k="baumarkt">–</b></div>
|
||||||
<div class="stat"><span class="note">Elektromärkte</span><b class="num" data-k="elektro">–</b></div>
|
<div class="stat"><span class="note">Elektromärkte</span><b class="num" data-k="elektro">–</b></div>
|
||||||
<div class="stat"><span class="note">Länder</span><b class="num" data-k="countries">–</b></div>
|
<div class="stat"><span class="note">Länder</span><b class="num" data-k="countries">–</b></div>
|
||||||
@@ -52,7 +52,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
<label class="rad">Umkreis <span class="num" id="r-out">–</span><input id="radius" type="range" min="10" max="500" step="10" value="500" disabled /></label>
|
<label class="rad">Umkreis <span class="num" id="r-out">–</span><input id="radius" type="range" min="10" max="500" step="10" value="500" disabled /></label>
|
||||||
</div>
|
</div>
|
||||||
<div class="tb-row chips">
|
<div class="tb-row chips">
|
||||||
<button class="chip ghost on" data-toggle="avail">Nur verfügbare</button>
|
<button class="chip ghost" data-toggle="avail">Nur verfügbare (live)</button>
|
||||||
<button class="chip ghost" data-toggle="chance">Beste Prognose</button>
|
<button class="chip ghost" data-toggle="chance">Beste Prognose</button>
|
||||||
<span class="sep"></span>
|
<span class="sep"></span>
|
||||||
<button class="chip on" data-kind="all">Alle Typen</button>
|
<button class="chip on" data-kind="all">Alle Typen</button>
|
||||||
@@ -79,7 +79,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
<div><h2 class="h-sm">Nichts in der Nähe?</h2><p class="note" style="margin:0">Verfügbarkeits-Alarm + Preiswecker – Mail & Telegram, mit Prognose-Frühwarnung.</p></div>
|
<div><h2 class="h-sm">Nichts in der Nähe?</h2><p class="note" style="margin:0">Verfügbarkeits-Alarm + Preiswecker – Mail & Telegram, mit Prognose-Frühwarnung.</p></div>
|
||||||
<a class="btn" href="/alarm">Alarm & Preiswecker</a>
|
<a class="btn" href="/alarm">Alarm & Preiswecker</a>
|
||||||
</div>
|
</div>
|
||||||
<p class="note" style="margin-top:.6rem">Datenquelle: <strong>eigener Kühlfinder-Scraper</strong> (Händler direkt) – keine Drittdaten. Aktuell Seed-Stand; Live-Crawler + echte Historie folgen. Prognose = statistische Schätzung. Quellen: Wetter <a href="https://open-meteo.com/" rel="nofollow">Open-Meteo</a>, Hitze/Gesundheit <a href="https://www.umweltbundesamt.de/themen/gesundheit/umwelteinfluesse-auf-den-menschen/hitze" rel="nofollow">UBA</a>.</p>
|
<p class="note" style="margin-top:.6rem">Standort-Abdeckung ist <strong>Demo</strong> (keine verifizierten Filialbestände). <strong>Verifizierte Live-Preise</strong> kommen vom eigenen Crawler (ausgewählte Produktseiten). Filial-Live folgt mit Händler-Anbindung. Prognose = statistische Schätzung. Quellen: Wetter <a href="https://open-meteo.com/" rel="nofollow">Open-Meteo</a>, Hitze/Gesundheit <a href="https://www.umweltbundesamt.de/themen/gesundheit/umwelteinfluesse-auf-den-menschen/hitze" rel="nofollow">UBA</a>.</p>
|
||||||
</section>
|
</section>
|
||||||
</Base>
|
</Base>
|
||||||
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4.7.1/dist/maplibre-gl.css" />
|
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/maplibre-gl@4.7.1/dist/maplibre-gl.css" />
|
||||||
@@ -143,11 +143,11 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
<script>
|
<script>
|
||||||
import maplibregl from 'maplibre-gl';
|
import maplibregl from 'maplibre-gl';
|
||||||
const FLAG={DE:'🇩🇪',AT:'🇦🇹',CH:'🇨🇭',NL:'🇳🇱',PL:'🇵🇱',DK:'🇩🇰'};
|
const FLAG={DE:'🇩🇪',AT:'🇦🇹',CH:'🇨🇭',NL:'🇳🇱',PL:'🇵🇱',DK:'🇩🇰'};
|
||||||
const LIVE_URL='http://vnqwgi4gq520hs1p4bsgi4w6.46.224.49.66.sslip.io';
|
const LIVE_URL=(import.meta.env.PUBLIC_CRAWLER_URL)||'http://vnqwgi4gq520hs1p4bsgi4w6.46.224.49.66.sslip.io';
|
||||||
const fmt=(n,c)=>n==null?'–':new Intl.NumberFormat('de-DE',{style:'currency',currency:c||'EUR',maximumFractionDigits:0}).format(n);
|
const fmt=(n,c)=>n==null?'–':new Intl.NumberFormat('de-DE',{style:'currency',currency:c||'EUR',maximumFractionDigits:0}).format(n);
|
||||||
const $=s=>document.querySelector(s),$$=s=>Array.from(document.querySelectorAll(s));
|
const $=s=>document.querySelector(s),$$=s=>Array.from(document.querySelectorAll(s));
|
||||||
let DATA=null,center=null,shown=120;
|
let DATA=null,center=null,shown=120;
|
||||||
const state={q:'',model:'all',countries:new Set(),kind:'all',avail:true,chance:false,radius:500};
|
const state={q:'',model:'all',countries:new Set(),kind:'all',avail:false,chance:false,radius:500};
|
||||||
const params=new URLSearchParams(location.search);
|
const params=new URLSearchParams(location.search);
|
||||||
if(params.get('modell'))state.model=params.get('modell');
|
if(params.get('modell'))state.model=params.get('modell');
|
||||||
if(params.get('plz'))state.q=params.get('plz');
|
if(params.get('plz'))state.q=params.get('plz');
|
||||||
@@ -191,10 +191,11 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
const tb=$('#rows');tb.innerHTML='';
|
const tb=$('#rows');tb.innerHTML='';
|
||||||
for(const s of list.slice(0,shown)){
|
for(const s of list.slice(0,shown)){
|
||||||
const st=status(s);
|
const st=status(s);
|
||||||
const stk = st.a?`<span class="stk ok"><span class="dot ok"></span> ${state.model==='all'?st.n+' verfügbar':'verfügbar'}</span>`:`<span class="stk off"><span class="dot off"></span> vergriffen</span>`;
|
const stk = st.a?`<span class="stk ok"><span class="dot ok"></span> live verfügbar</span>`:`<span class="stk off"><span class="dot off"></span> Demo · beim Händler prüfen</span>`;
|
||||||
const distTd=center?`<td class="ce num">${dist(center[1],center[0],s.la,s.lo).toFixed(0)} km</td>`:'';
|
const distTd=center?`<td class="ce num">${dist(center[1],center[0],s.la,s.lo).toFixed(0)} km</td>`:'';
|
||||||
tb.insertAdjacentHTML('beforeend',`<tr><td><span class="flag">${s.flag||FLAG[s.cc]||''}</span> <span class="fname">${s.n}</span><span class="kindb">${s.kind==='elektro'?'Elektro':'Baumarkt'}</span><div class="faddr">${s.z} ${s.ci} · <span title="zuletzt geprüft">Stand vor ${s.t||'?'} Min</span></div></td>
|
const surl="https://www.google.com/search?q="+encodeURIComponent(s.c+" "+s.ci+" Klimagerät");
|
||||||
<td class="ce">${stk}</td><td class="ce num">${fmt(st.p,s.cur)}</td>
|
tb.insertAdjacentHTML('beforeend',`<tr><td><span class="flag">${s.flag||FLAG[s.cc]||''}</span> <a class="fname" href="${surl}" target="_blank" rel="noopener">${s.n}</a><span class="kindb">${s.kind==='elektro'?'Elektro':'Baumarkt'}</span><div class="faddr">${s.z} ${s.ci} · <span title="zuletzt geprüft">Stand vor ${s.t||'?'} Min</span></div></td>
|
||||||
|
<td class="ce">${stk}</td><td class="ce num" title="Referenzpreis (Demo)">${st.p?fmt(st.p,s.cur):"–"}</td>
|
||||||
<td class="ce"><span class="prog"><span class="progbar"><i style="width:${st.q||0}%"></i></span><span class="num">${st.q||0}%</span></span></td>${distTd}</tr>`);
|
<td class="ce"><span class="prog"><span class="progbar"><i style="width:${st.q||0}%"></i></span><span class="num">${st.q||0}%</span></span></td>${distTd}</tr>`);
|
||||||
}
|
}
|
||||||
$('#rowcount').textContent=`${Math.min(shown,list.length)} von ${list.length} Märkten`;
|
$('#rowcount').textContent=`${Math.min(shown,list.length)} von ${list.length} Märkten`;
|
||||||
@@ -204,7 +205,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
function fillStatic(){
|
function fillStatic(){
|
||||||
$('#h-updated').textContent=DATA.stats.updated;
|
$('#h-updated').textContent=DATA.stats.updated;
|
||||||
$$('#stats b[data-k]').forEach(b=>b.textContent=(DATA.stats[b.dataset.k]??'–').toLocaleString('de-DE'));
|
$$('#stats b[data-k]').forEach(b=>b.textContent=(DATA.stats[b.dataset.k]??'–').toLocaleString('de-DE'));
|
||||||
$('#s-wucher').textContent=DATA.online.filter(o=>o.wucher).length;
|
$('#s-wucher').textContent=(DATA.online||[]).filter(o=>o.wucher).length;
|
||||||
$('#h-feednote').textContent='Feed live';
|
$('#h-feednote').textContent='Feed live';
|
||||||
$('#feed').innerHTML=DATA.feed.slice(0,18).map(f=>`<li><div class="fl"><b>${FLAG[f.cc]||''} ${f.name}</b><span>${f.model}</span></div><span class="ft">${rel(f.at)}</span></li>`).join('');
|
$('#feed').innerHTML=DATA.feed.slice(0,18).map(f=>`<li><div class="fl"><b>${FLAG[f.cc]||''} ${f.name}</b><span>${f.model}</span></div><span class="ft">${rel(f.at)}</span></li>`).join('');
|
||||||
$('#onlinebar').innerHTML='<span class="note" style="align-self:center">Online & eBay:</span>'+DATA.online.map(o=>`<a class="osrc ${o.wucher?'wucher':''}" href="${o.url}" target="_blank" rel="nofollow noopener"><b>${o.src}${o.wucher?' ⚠':''}</b><span class="num">${fmt(o.p)}</span><small>${o.cond?o.cond+' · ':''}${o.a?(o.wucher?'Wucher':'verfügbar'):'nicht verf.'}</small><small class="trust" title="Seriosität">${stars(o.trust||3)}</small></a>`).join('');
|
$('#onlinebar').innerHTML='<span class="note" style="align-self:center">Online & eBay:</span>'+DATA.online.map(o=>`<a class="osrc ${o.wucher?'wucher':''}" href="${o.url}" target="_blank" rel="nofollow noopener"><b>${o.src}${o.wucher?' ⚠':''}</b><span class="num">${fmt(o.p)}</span><small>${o.cond?o.cond+' · ':''}${o.a?(o.wucher?'Wucher':'verfügbar'):'nicht verf.'}</small><small class="trust" title="Seriosität">${stars(o.trust||3)}</small></a>`).join('');
|
||||||
@@ -221,11 +222,12 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
|||||||
const when=d.generatedAt?new Date(d.generatedAt):null;
|
const when=d.generatedAt?new Date(d.generatedAt):null;
|
||||||
const mins=when?Math.max(1,Math.round((Date.now()-when)/60000)):'?';
|
const mins=when?Math.max(1,Math.round((Date.now()-when)/60000)):'?';
|
||||||
const items=d.offers.filter(o=>o.price||o.availability==='discontinued').map(o=>{
|
const items=d.offers.filter(o=>o.price||o.availability==='discontinued').map(o=>{
|
||||||
const av=o.availability==='in_stock'?'im Markt':(o.availability==='online_available'?'online':(o.availability==='discontinued'?'⚠ Seite entfernt (404)':'vergriffen'));
|
const av=o.availability==='in_stock'?'Produktseite: verfügbar':(o.availability==='online_available'?'online verfügbar':(o.availability==='discontinued'?'⚠ Seite entfernt (404)':(o.availability==='blocked'?'blockiert':'vergriffen')));
|
||||||
const cls=(o.availability==='out_of_stock')?'':'';
|
const cls=(o.availability==='out_of_stock')?'':'';
|
||||||
return `<a class="osrc ${o.availability==='discontinued'?'wucher':''}" href="${o.url}" target="_blank" rel="sponsored nofollow noopener"><b>${o.chain} · LIVE</b><span class="num">${fmt(o.price)}</span><small>${o.name} · ${av}</small></a>`;
|
return `<a class="osrc ${o.availability==='discontinued'?'wucher':''}" href="${o.url}" target="_blank" rel="sponsored nofollow noopener"><b>${o.chain} · LIVE</b><span class="num">${fmt(o.price)}</span><small>${o.name} · ${av}</small></a>`;
|
||||||
}).join('');
|
}).join('');
|
||||||
const bar=$('#onlinebar');
|
const bar=$('#onlinebar');
|
||||||
|
const liveN=d.offers.filter(o=>o.price).length; var le=document.getElementById('s-live'); if(le)le.textContent=liveN;
|
||||||
const note = d.blocked?` · ${d.blocked} Quelle(n) blockiert (Proxy nötig)`:'';
|
const note = d.blocked?` · ${d.blocked} Quelle(n) blockiert (Proxy nötig)`:'';
|
||||||
bar.insertAdjacentHTML('afterbegin', `<span class="livebadge" title="eigener Live-Crawler"><span class="dot ok live"></span> LIVE gescrapt · Stand vor ${mins} Min${note}</span>`+items);
|
bar.insertAdjacentHTML('afterbegin', `<span class="livebadge" title="eigener Live-Crawler"><span class="dot ok live"></span> LIVE gescrapt · Stand vor ${mins} Min${note}</span>`+items);
|
||||||
}).catch(()=>{});
|
}).catch(()=>{});
|
||||||
|
|||||||
+2
-2
@@ -35,8 +35,8 @@ const jsonld={'@context':'https://schema.org','@type':'FAQPage',mainEntity:[
|
|||||||
<li>★ <strong>Preiswecker</strong>: Alarm, wenn Preis unter dein Limit fällt</li>
|
<li>★ <strong>Preiswecker</strong>: Alarm, wenn Preis unter dein Limit fällt</li>
|
||||||
<li>★ Beste-Chancen-Route in deinem Umkreis</li>
|
<li>★ Beste-Chancen-Route in deinem Umkreis</li>
|
||||||
</ul>
|
</ul>
|
||||||
<a class="btn" href="/alarm" data-umami-event="pro_pass_cta">Pro-Pass holen</a>
|
<a class="btn" href="/alarm" data-umami-event="pro_pass_cta">Auf die Warteliste</a>
|
||||||
<p class="note" style="margin:.5rem 0 0">Zahlung per <strong>Mollie</strong> (EU, NL) – iDEAL, SEPA, Karte, Apple/Google Pay. Folgt. Keine automatische Verlängerung.</p>
|
<p class="note" style="margin:.5rem 0 0">Vorschau – Bezahlung per <strong>Mollie</strong> (EU) kommt zum Verkaufsstart. Noch nichts zu zahlen, keine automatische Verlängerung.</p>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user