Heatmap im Portal (Funktionen -> Analyse): die echte Website laeuft jetzt in realistischen
Geraeterahmen, gross MacBook oder iPad (Umschalter oben), daneben das iPhone, jeweils in echter
Breite (1512 / 820 / 402 px), verkleinert und im Geraet scrollbar, mit den Klicks der
passenden Geraeteklasse als Overlay. MacBook und iPhone stammen aus den Rahmen der neuen
Website (device-mockups), das iPad ist im gleichen Stil nachgebaut (public/geraete.css/.js).
- heatmap-link: embed: true haengt __wk_embed=1 an; w.js reicht das als data-embed an h.js.
- h.js eingebettet (nur in einem Rahmen): keine Leiste, Links und Formulare gesperrt (kein
Seitenwechsel, also keine Zaehlung und kein Banner), Scrollleisten aus, Klickzahl per
postMessage nur an die Herkunft von h.js (project.webklar.com).
- iframe mit Sandbox allow-scripts allow-same-origin (keine Formulare, Pop-ups, keine
Navigation des Portals); Laden erst, wenn die Heatmap in Sicht kommt. Laesst sich eine
Website nicht einbetten, kommt nach 20 s ein Hinweis mit "In neuem Tab oeffnen" (alter Weg).
Banner-Skript: der Einstellungsknopf nach der Wahl ist jetzt ein angeknabberter Keks unten
rechts (Inline-SVG, 44 px). Er dreht sich langsam, haelt beim Darueberfahren/Fokus an und
zeigt dann "Datenschutz-Einstellungen" (auch sein Name fuer Screenreader, passend zum
Bannertext); bei reduzierter Bewegung dreht er sich nicht.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der Tab "Rechnungen" las nur InvoiceNinja. Seit dem Finanzen-Livegang laufen Rechnungen
aber ueber Stripe; die bezahlte Abo-Rechnung eines Kunden fehlte deshalb im Portal.
- /api/invoices fuehrt Stripe (alle Stripe-Kunden des Kunden: Abo-Spiegel und Suche nach
metadata.webklarCustomerId) und InvoiceNinja zusammen, neueste zuerst; Entwuerfe und
0-EUR-Rechnungen bleiben verborgen. Faellt eine Quelle aus, kommt der Rest mit incomplete.
- /api/invoices/:id/pdf laedt Stripe-PDFs frisch ueber den Server, nur fuer eigene Rechnungen
(sonst 404) und nur von *.stripe.com.
- Seite: Status-Farben fuer Bezahlt/Offen/Ueberfaellig/Storniert, "Ansehen" statt
"Ansehen / Zahlen" bei bezahlten Rechnungen, Hinweis bei unvollstaendiger Liste; der
Abo-Hinweis verweist nur noch fuer die Zahlungsart auf "Abo verwalten & Rechnungen".
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ein in Gitea geloeschtes Org-Repo liess sein unverknuepftes Projekt-Dokument
liegen. Der Unique-Index auf repoFullName ignoriert Gross-/Kleinschreibung,
darum scheiterte beim Zuweisen eines gleich benannten neuen Repos das
Umbenennen des Projekts; alle Pushes landeten im Platzhalter ohne Ticket,
also ohne Arbeitsblatt und ohne Push-Zeitmail (WEBklar/Schmacko-napf).
- repoMove: vor dem Umbenennen ein unverknuepftes fremdes Dokument mit dem
Zielnamen entfernen (verknuepfte bleiben unangetastet)
- Webhook repository/deleted: unverknuepfte Platzhalter des geloeschten
Repos entfernen, nur wenn Gitea das Repo wirklich nicht mehr kennt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Messdienst: POST /a/admin/sites/:key/off setzt active=0 und offSince einmalig; Portal-Client stopSite.
analyse.js: endAnalyse als gemeinsamer Kern, endAnalyseForCustomer, analyseOnProjectChanged,
analyseDisabled (Sperre/Löschfrist) und täglicher Abgleich runAnalyseReconcile (Projekt/Kunde weg,
Domains nachziehen, K18-Wiederholung, Messdienst für off/disabled, fehlende Entfernen-Tickets nachholen).
Entfernen-Ticket je Abschaltung gekennzeichnet (kein Doppelticket, auch wenn das Speichern der ID scheiterte).
Hooks: Kunde löschen, updateWebsiteProject, withdrawOrder (Wechsel), siteFeatures.disable.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
M1: scheitert Appwrite nach dem Bezahlen, kommt 502 mit klarer Meldung statt eines blanken 500.
M2: Wiederaufnahme nur bei passendem bezahltem Betrag; eine Buchung, die schon an einer anderen
bezahlten Rechnung haengt, wird nicht ein zweites Mal vermerkt und bezahlt (409).
M3: Protokollzeile je erfolgreicher, nicht umkehrbarer Zuordnung (Buchung, Rechnung, Nutzer).
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
I2: Leseanfragen an Stripe mit timeout 15 s und einer Wiederholung (auch Folgeseiten), die Uebersicht
wartet hoechstens 20 s auf Stripe und rechnet sonst ohne Stripe weiter.
I3: idempotencyKey vom Aufrufer an stripe.invoices.create, Schutz im Prozess gegen gleichzeitiges und
30 s spaeteres Doppelanlegen (409), 502 mit invoiceId/Nummer wenn die fertige Rechnung nicht lesbar ist.
M9: Warnung, wenn die Rechnungsliste bei 1000 abgeschnitten wird.
M11: Ursache eines 502 an einer bestehenden Rechnung (Art, Status, Code) im Protokoll.
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
Neue Vorlage K17 im Stil von K16 mit Nachricht, Absender und Link
auf den Portal-Chat. Hoechstens eine Mail je Kunde im Zeitfenster
CHAT_NOTIFY_THROTTLE_MIN (15 Min.), ohne BCC ans Team, nicht ohne
E-Mail-Adresse oder Portalzugang. Das Admin-Panel zeigt unter dem
Kundennamen, ob der Hinweis rausging.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Live-Fehler: cs_live-Sitzungs-IDs (~66 Zeichen) passten nicht in das Feld
portalFeatures.stripeSessionId (64), die Bestellung brach nach dem Anlegen
der Stripe-Sitzung ab und der Kunde sah die Appwrite-Meldung.
- Setup-Skript: Feld 128 Zeichen, bestehendes Feld wird per ensureStringSize vergroessert
- Fehler von Appwrite oder unerwartete Fehler: Log + verstaendlicher Hinweis
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>