v5: Mollie (EU) statt Stripe, Sitemap + llms-full.txt, /prognose-Methodik, ausgebauter Datenschutz/Transparenz, Scraper-Adapter toom/bauhaus/hornbach + compose/cron-ready, checkedAt-Stempel im Monitor
This commit is contained in:
@@ -2,9 +2,27 @@
|
||||
import Base from '../layouts/Base.astro';
|
||||
---
|
||||
<Base title="Datenschutz | Kühlfinder">
|
||||
<section class="wrap prose" style="margin-top:1.6rem;max-width:720px">
|
||||
<section class="wrap prose" style="margin-top:1.6rem;max-width:740px">
|
||||
<h1>Datenschutz</h1>
|
||||
<p><em>Platzhalter, vor Go-Live finalisieren.</em></p>
|
||||
<p>Diese Seite wird statisch ausgeliefert. Kartenkacheln werden von OpenFreeMap geladen; dabei wird deine IP an den Tile-Server übertragen (vor Go-Live ggf. self-hosten). Standortabfrage erfolgt nur nach deiner ausdrücklichen Zustimmung im Browser. Der Verfügbarkeits-Alarm speichert E-Mail und PLZ ausschließlich zur Benachrichtigung (Backend folgt, EU-Hosting).</p>
|
||||
<p class="note"><em>Vorlage – vor Go-Live mit Verantwortlichem/Rechtsberatung finalisieren.</em></p>
|
||||
<h2>Verantwortlicher</h2>
|
||||
<p>Heidrich Digital, [Adresse], mail@tillheidrich.de. (Platzhalter)</p>
|
||||
<h2>Hosting</h2>
|
||||
<p>Die Seite wird auf Servern in der EU (Hetzner, Deutschland) via Coolify ausgeliefert. Es werden Server-Logs zur Sicherheit verarbeitet.</p>
|
||||
<h2>Analyse: Umami</h2>
|
||||
<p>Wir nutzen das selbst gehostete, <strong>cookielose</strong> Analyse-Tool Umami (EU-Server). Es speichert keine personenbezogenen Profile und setzt keine Tracking-Cookies; erfasst werden anonymisierte Nutzungsdaten (Seitenaufrufe, Klick-Events wie „Preis prüfen"). Daher i. d. R. kein Cookie-Banner nötig.</p>
|
||||
<h2>Karte & Standort</h2>
|
||||
<p>Kartenkacheln werden von <strong>OpenFreeMap</strong> geladen (dabei wird deine IP an den Tile-Server übertragen). Die Standortbestimmung erfolgt nur nach deiner ausdrücklichen Zustimmung im Browser und wird nicht gespeichert.</p>
|
||||
<h2>Wetter</h2>
|
||||
<p>Für den Wetterhinweis rufen wir die <strong>Open-Meteo</strong>-API auf (Standort grob bzw. Default). Es werden keine personenbezogenen Daten dauerhaft gespeichert.</p>
|
||||
<h2>Alarm, Preiswecker & Konto</h2>
|
||||
<p>Für Benachrichtigungen speichern wir E-Mail, PLZ/Umkreis und deine Watchlist – ausschließlich zur Zustellung. Versand über EU-Dienst (Listmonk, self-hosted) und optional Telegram. Abbestellung jederzeit.</p>
|
||||
<h2>Zahlung</h2>
|
||||
<p>Der Pro-Pass wird über <strong>Mollie</strong> (Mollie B.V., Niederlande/EU) abgewickelt. Zahlungsdaten verarbeitet Mollie als Zahlungsdienstleister; wir erhalten nur den Zahlungsstatus.</p>
|
||||
<h2>Affiliate-Links</h2>
|
||||
<p>Ausgehende Kauf-/Preis-Links können Partner-Parameter (Affiliate/UTM) enthalten. Erst beim Klick wird zum jeweiligen Händler weitergeleitet; dort gelten dessen Datenschutzbestimmungen.</p>
|
||||
<h2>Deine Rechte</h2>
|
||||
<p>Auskunft, Berichtigung, Löschung, Widerspruch – Kontakt: mail@tillheidrich.de.</p>
|
||||
</section>
|
||||
</Base>
|
||||
<style>.prose h2{margin-top:1.3rem;font-size:1.15rem}</style>
|
||||
|
||||
@@ -166,7 +166,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
||||
map.addControl(new maplibregl.NavigationControl(),'top-right');
|
||||
function setMap(list){
|
||||
const all={type:'FeatureCollection',features:list.map(s=>({type:'Feature',geometry:{type:'Point',coordinates:[s.lo,s.la]},properties:{}}))};
|
||||
const av={type:'FeatureCollection',features:list.filter(s=>status(s)?.a).map(s=>({type:'Feature',geometry:{type:'Point',coordinates:[s.lo,s.la]},properties:{n:s.n,ci:s.ci}}))};
|
||||
const av={type:'FeatureCollection',features:list.filter(s=>status(s)?.a).map(s=>({type:'Feature',geometry:{type:'Point',coordinates:[s.lo,s.la]},properties:{n:s.n,ci:s.ci,t:s.t||null}}))};
|
||||
map.getSource('all').setData(all);map.getSource('av').setData(av);
|
||||
}
|
||||
function priceStrip(){
|
||||
@@ -191,7 +191,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
||||
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 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}</div></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>
|
||||
<td class="ce">${stk}</td><td class="ce num">${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>`);
|
||||
}
|
||||
@@ -219,7 +219,7 @@ const jsonld={'@context':'https://schema.org','@type':'WebApplication',name:'Kü
|
||||
map.addLayer({id:'pt',type:'circle',source:'all',filter:['!',['has','point_count']],paint:{'circle-color':'#2b6a72','circle-opacity':.6,'circle-radius':4,'circle-stroke-width':1,'circle-stroke-color':'#fff'}});
|
||||
map.addSource('av',{type:'geojson',data:{type:'FeatureCollection',features:[]}});
|
||||
map.addLayer({id:'av',type:'circle',source:'av',paint:{'circle-color':'#1d9e75','circle-radius':7,'circle-stroke-width':2,'circle-stroke-color':'#fff'}});
|
||||
map.on('click','av',e=>{const p=e.features[0].properties;new maplibregl.Popup().setLngLat(e.lngLat).setHTML(`<b>${p.n}</b><br>${p.ci} – verfügbar`).addTo(map);});
|
||||
map.on('click','av',e=>{const p=e.features[0].properties;new maplibregl.Popup().setLngLat(e.lngLat).setHTML(`<b>${p.n}</b><br>${p.ci} – verfügbar<br><span style=\"color:#666;font-size:.8em\">Stand vor ${p.t||'?'} Min</span>`).addTo(map);});
|
||||
map.on('click','cl',e=>{const f=map.queryRenderedFeatures(e.point,{layers:['cl']});map.getSource('all').getClusterExpansionZoom(f[0].properties.cluster_id).then(z=>map.easeTo({center:f[0].geometry.coordinates,zoom:z}));});
|
||||
if(DATA)render();
|
||||
});
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
import { allProducts, priceIntel, euro } from '../lib/pricing';
|
||||
export const GET = () => {
|
||||
const L:string[] = [
|
||||
'# Kühlfinder – Vollständige Referenz',
|
||||
'> Kontrollzentrum für Klimagerät-Verfügbarkeit in DE/AT/CH/NL/PL/DK: Live-Bestand in Bau- und Elektromärkten + Online/Amazon/eBay, Verfügbarkeits-Prognose, Preis-Check mit Wucher-Warnung, Custom-Alarm + Preiswecker. Eigener Scraper, keine Drittdaten.',
|
||||
'',
|
||||
'## Kernfunktionen',
|
||||
'- Live-Karte & Tabelle aller Märkte mit Bestand (idealo zeigt keinen Filialbestand).',
|
||||
'- Verfügbarkeits-Prognose (Chance 7 Tage) aus Restock-Historie. Methodik: /prognose',
|
||||
'- Preis-Intelligenz: günstigster/üblicher/teuerster Preis, eBay-Range, Retoure/refurbished, Wucher ab UVP×1,3.',
|
||||
'- Custom Multi-Artikel-Watchlist, Alarm + Preiswecker (E-Mail kostenlos, Telegram/Echtzeit im Pro-Pass).',
|
||||
'- Beratung: 60-Sekunden-Finder, Klima-Rechner (kW/BTU), Soforthilfe, feste Anlage (SHK).',
|
||||
'',
|
||||
'## Geräte (kuratiert)',
|
||||
];
|
||||
for (const p of allProducts){ const pi=priceIntel(p); L.push(`- ${p.name} — ${p.type==='mobile_split'?'mobile Split':'Monoblock'}, ${p.btuh} BTU, ${p.roomSizeM2[0]}–${p.roomSizeM2[1]} m², EEK ${p.efficiencyCooling}. ${p.shortVerdict} Üblicher Preis ${euro(pi.fairPrice)}. /geraete/${p.slug}`); }
|
||||
L.push('', '## Seiten',
|
||||
'- / – Startseite (availability-first)',
|
||||
'- /karte – Live-Monitor (Karte, Tabelle, Prognose, Preis-Strip)',
|
||||
'- /rechner – Klima-Rechner (Leistung + Empfehlung)',
|
||||
'- /finder – 60-Sekunden-Empfehlung',
|
||||
'- /geraete – alle Geräte; /geraete/<slug> – Details',
|
||||
'- /portasplit – PortaSplit-Status & Alternativen',
|
||||
'- /alarm – Custom-Alarm + Preiswecker',
|
||||
'- /pro – Pro-Pass (Echtzeit, Telegram, Frühwarnung)',
|
||||
'- /soforthilfe – Hitze-Tipps (Dachgeschoss/Haus)',
|
||||
'- /klimaanlage-installieren – feste Anlage (SHK), Preise & Partner',
|
||||
'- /prognose – Prognose-Methodik',
|
||||
'- /hitzeschutz – Klimawandel-Kontext',
|
||||
'- /transparenz – Methodik, Affiliate, Tracking, Zahlung');
|
||||
return new Response(L.join('\n'), { headers: { 'Content-Type':'text/plain; charset=utf-8' } });
|
||||
};
|
||||
+1
-1
@@ -36,7 +36,7 @@ const jsonld={'@context':'https://schema.org','@type':'FAQPage',mainEntity:[
|
||||
<li>★ Beste-Chancen-Route in deinem Umkreis</li>
|
||||
</ul>
|
||||
<a class="btn" href="/alarm" data-umami-event="pro_pass_cta">Pro-Pass holen</a>
|
||||
<p class="note" style="margin:.5rem 0 0">Zahlung per Stripe (folgt). Keine automatische Verlängerung.</p>
|
||||
<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>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -0,0 +1,26 @@
|
||||
---
|
||||
import Base from '../layouts/Base.astro';
|
||||
const jsonld={'@context':'https://schema.org','@type':'FAQPage',mainEntity:[
|
||||
{'@type':'Question',name:'Wie funktioniert die Verfügbarkeits-Prognose?',acceptedAnswer:{'@type':'Answer',text:'Wir protokollieren, wann ein Markt ein Gerät wieder auf Lager hatte (Restock-Ereignisse). Aus der Häufigkeit der letzten 30 Tage schätzen wir die Wahrscheinlichkeit, dass es in den nächsten 7 Tagen wieder verfügbar ist.'}}]};
|
||||
---
|
||||
<Base title="Prognose-Methodik: Wie Kühlfinder Verfügbarkeit schätzt | Kühlfinder" description="Transparent erklärt: Wie die Verfügbarkeits-Prognose (Chance in 7 Tagen) aus der Restock-Historie berechnet wird – inklusive Grenzen." jsonld={jsonld}>
|
||||
<section class="wrap prose" style="margin-top:1.6rem;max-width:720px">
|
||||
<span class="eyebrow">Methodik · transparent</span>
|
||||
<h1>Wie die Verfügbarkeits-Prognose funktioniert</h1>
|
||||
<p class="lead">Kein Orakel, sondern Statistik aus echten Daten. So entsteht die „Chance in 7 Tagen".</p>
|
||||
<h2>1. Wir messen Restocks</h2>
|
||||
<p>Unser Scraper prüft regelmäßig Bestand und Preis direkt bei den Händlern. Wechselt ein Markt für ein Gerät von „vergriffen" auf „verfügbar", protokollieren wir ein <strong>Restock-Ereignis</strong> mit Zeitstempel.</p>
|
||||
<h2>2. Wir schätzen die Rate</h2>
|
||||
<p>Aus den Ereignissen der letzten 30 Tage berechnen wir je Markt & Gerät eine Restock-Rate (Ereignisse pro Woche). Daraus ergibt sich die Wahrscheinlichkeit für die nächsten 7 Tage nach dem Modell <code>P = 1 − e^(−Rate × 1 Woche)</code>.</p>
|
||||
<h2>3. Wir zeigen den besten Zeitpunkt</h2>
|
||||
<p>Wo Daten es zulassen, erkennen wir typische Liefer-Wochentage/-zeiten. Pro-Nutzer bekommen die <strong>Frühwarnung</strong> bereits, wenn ein typisches Restock-Fenster bevorsteht – nicht erst, wenn es (kurz) verfügbar ist.</p>
|
||||
<h2>Grenzen</h2>
|
||||
<ul>
|
||||
<li>Eine Prognose ist eine <strong>Schätzung</strong>, keine Garantie – Lieferungen schwanken.</li>
|
||||
<li>Neue Märkte/Geräte ohne Historie zeigen niedrige/keine Prognose, bis Daten vorliegen.</li>
|
||||
<li>Aktuell teils Seed-Daten; mit dem Live-Crawler wird die Historie echt und die Prognose schärfer.</li>
|
||||
</ul>
|
||||
<p class="note">Quellen: eigener Kühlfinder-Scraper (Händlerseiten direkt). Wetterkontext: <a href="https://open-meteo.com/" rel="nofollow">Open-Meteo</a>.</p>
|
||||
</section>
|
||||
</Base>
|
||||
<style>.prose .lead{font-size:1.15rem;color:var(--ink-2)}.prose h2{margin-top:1.5rem}.prose ul{padding-left:1.2rem}.prose code{font-family:var(--mono);background:var(--surface-2);padding:.1rem .3rem;border-radius:4px}</style>
|
||||
@@ -11,6 +11,12 @@ import { WUCHER_FACTOR } from '../lib/pricing';
|
||||
<p>Als „üblicher Preis“ dient die UVP bzw. der normale Marktpreis. Liegt ein verfügbares Angebot mehr als {Math.round((WUCHER_FACTOR-1)*100)} % darüber, markieren wir es als Wucher. Diese Angebote zählen nicht in den Durchschnitt.</p>
|
||||
<h2>Verfügbarkeit</h2>
|
||||
<p>Wir zeigen Verfügbarkeit pro Markt so aktuell wie möglich. Wenn Daten nicht automatisch geprüft sind, behaupten wir nie „verfügbar“, sondern nur „prüfen“. Preise und Bestände ändern sich während Hitzewellen sehr schnell.</p>
|
||||
<h2>Tracking</h2>
|
||||
<p>Cookielose Analyse mit selbst gehostetem Umami (EU). Keine Werbe-Cookies, keine Weitergabe an Werbenetzwerke.</p>
|
||||
<h2>Zahlung</h2>
|
||||
<p>Pro-Pass über Mollie (EU/Niederlande) – europäische Zahlungsabwicklung. Kein Abo, keine automatische Verlängerung.</p>
|
||||
<h2>Partner-Empfehlungen</h2>
|
||||
<p>Bei festen Anlagen empfehlen wir regionale Fachbetriebe (z. B. Stöver, TGA Solutions) mit UTM-Verlinkung – als redaktionelle Empfehlung, nicht als bezahlte Platzierung.</p>
|
||||
<h2>Affiliate</h2>
|
||||
<p>Einige Links sind Affiliate-Links. Kaufst du darüber, erhalten wir ggf. eine Provision – für dich ändert sich der Preis nicht. Empfehlungen hängen nicht von der Provision ab.</p>
|
||||
</section>
|
||||
|
||||
Reference in New Issue
Block a user