Der Chat-Composer bringt den runden Senden-Knopf fuer die App-Ansicht selbst
mit (0d47b7c). Die alten Regeln der App-Ansicht fuer .chat-send und
.chat-input-row waren dadurch wirkungslos und sind entfernt; die
Eingabe-Pille bleibt.
- Verlauf: der ResizeObserver nutzt data-stick aus ChatComposer.trackScroll
statt eines eigenen Scroll-Zaehlers
- Hinweis unter dem Chat nur noch in der App-Ansicht mit Touch ausgeblendet
(vorher auf allen Touch-Geraeten, also auch auf Tablets quer)
- Handy quer: feste Hoehe der Eingabe entfernt, der Composer waechst selbst mit
Handy, Tablet hochkant und Desktop sind pixelgleich zu main (28 Ansichten).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Die App-Ansicht (kundenbereich.css) gilt bis 880 px und fuer Handys quer,
der runde Senden-Knopf mit Symbol galt bisher erst unter 640 px. Dazwischen
stand "Senden" in einem 46-px-Kreis und lief ueber den Rand (auch waehrend
der Sprachaufnahme). Jetzt gilt der runde Knopf unter derselben Bedingung
wie die App-Ansicht.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Auf dem Handy (unter 880 px und Handys quer) sieht der Kundenbereich aus wie
eine App; der Desktop bleibt pixelgleich.
- Kopfzeile als App-Leiste: Logo, beim Scrollen der Seitentitel, Avatar
- Navigation als Tab-Leiste unten mit Symbolen und Zaehlern; neuer Tab
"Wartung" mit der Abo-Karte, Stundenkonto/Bestellung und
Uebernahmegutschrift als Unterseiten mit Zurueck (auch Android-Geste)
- Konto als Bottom-Sheet (Ansprechpartner, Vertraege kuendigen/widerrufen,
Rechtliches, Abmelden), am Desktop als Popover unter dem Avatar
- Rechnungen als Karten, Fortschritt als Zeitleiste, Chat fuellt den
Bildschirm; bei offener Tastatur verschwindet die Tab-Leiste (kommt auch
nach der Android-Zurueck-Geste wieder)
- Installierbar: Manifest, App-Icons (WEBklar-Logo), Favicon, Hinweis
"Zum Home-Bildschirm"; Service Worker nur fuer die Offline-Seite,
Kundendaten werden nicht zwischengespeichert
- Installierte App: Kuendigen/Widerrufen mit Link zurueck in den
Kundenbereich (nur dort sichtbar, Texte unveraendert); Login-Links zu
Bedingungen/Datenschutz/Impressum oeffnen wie im Dashboard ein neues Fenster
- Login mit App-Icon und Abstaenden fuer Statusleiste/Home-Balken
- Hinweise "in der Seitenleiste" haben auf dem Handy eine eigene Fassung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kamerafotos in anderen Formaten (z. B. HEIF von Android-Kameras) werden
wie Dateien angenommen statt abgelehnt
- Dateinamen nach Zeichen kuerzen: ein halbiertes Emoji machte den Anhang
unabrufbar (encodeURIComponent)
- Endet eine Aufnahme von selbst (Headset getrennt, Anruf), bleibt die
Sprachnachricht erhalten statt "zu kurz"
- Kamera-App nur auf iOS/iPadOS/Android; Touch-Laptops bekommen die Kamera
im Browser statt eines Dateidialogs
- Knapp neben dem Chat abgelegte Dateien oeffnet der Browser nicht mehr
selbst (Portal bliebe sonst stehen); Datei-Felder bleiben Ablagen
- Fehler beim Ausliefern kommt als JSON statt mit dem Dateityp
- Server liest hoechstens 4 Uploads gleichzeitig ein (Speicher), weitere
warten; abgebrochene Wartende geben ihren Platz frei
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kundenbereich und Admin-Chat: Bueroklammer-Menue (Bild, Kamera, Datei),
Mikrofon fuer Sprachnachrichten, Vorschau vor dem Senden mit
Upload-Fortschritt, Einfuegen (Strg+V) und Hineinziehen von Dateien
- Kamera: am Handy/Tablet die Kamera-App, am Rechner die Kamera im Browser
mit Vorschau, Kamerawechsel und "Nochmal"
- Sprachnachricht: Aufnahme mit Zeit und Pegel, vorher anhoerbar oder
direkt senden, hoechstens 5 Minuten
- Verlauf: Bilder (einzeln bzw. Raster, Grossansicht), Player fuer
Sprachnachrichten, Datei-Kacheln mit Download
- Server: jede Datei einzeln roh hochgeladen (max. 25 MB, 10 je Nachricht,
max. 3 gleichzeitig), Typ aus dem Inhalt, nur erlaubte Endungen, keine
Programme; Ablage unter upload-data/chat/<Kunde>/ ausserhalb von public/,
Auslieferung nur mit Login; Bilder, Audio und PDF inline, alles andere
als Download (nosniff, CSP sandbox)
- Nicht gesendete Anhaenge werden nach 24 Stunden geloescht
- Benachrichtigungs-Mail nennt nur Art und Namen der Anhaenge
- Appwrite: neues Attribut portalMessages.attachments
(scripts/setup-chat-collection.mjs, vor dem Deploy ausfuehren)
- Datenschutzerklaerung Abschnitt 6 ergaenzt (Stand 26.09.2026), PDF neu
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Seitenleiste: Ueberschrift "Ihre Wartung" statt "Ihr Wartungs-Abo"
- Kein "aktiv"-Label mehr; ein Status steht nur noch da, wenn gekuendigt
- Zahlungsproblem bleibt ueber den roten Hinweis sichtbar, spaeterer
Beginn ueber die Datumszeile ("Beginn und erste Abbuchung am ...")
- Bei Kuendigung vor Leistungsbeginn keine "erste Abbuchung" mehr
- Ungenutzte Status-Styles entfernt
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Admin-Tab "Abo-Angebote" und Button "Angebot" in der Kundenliste: Preis, Monate,
Gueltigkeit, Hinweis fuer den Kunden, interne Notiz, Vorschau vor dem Anlegen
- Je Angebot sofort ein Stripe-Coupon (amount_off, repeating ueber die
Angebotsmonate, max. 1 Einloesung); danach automatisch wieder Normalpreis
- Kunde ohne Abo: Angebot im Portal, Angebotspreis in der Bestelluebersicht,
Coupon im Checkout; Angebot schaltet die Bestellung auch ausserhalb der
Pilotliste frei; Bestellung prueft, dass das angezeigte Angebot noch gilt
- Laufendes Abo: Coupon sofort am Abo fuer die naechsten Abbuchungen
- Testkonten: Testrabatt im Checkout, Angebot per Webhook zusaetzlich am Abo
- Angebot in Abo-Metadaten, Vertragsbestaetigung, Admin-Mail und Abo-Karte
- Zurueckziehen loescht den Coupon; abgelaufene Angebote nicht mehr bestellbar
- Stundenkontingent nach invoice.subtotal: auch voll rabattierte oder per
Guthaben bezahlte Monate bringen die Stunde, die Probezeit nicht
- setup-abo-collections.mjs: aboOffers, subscriptions.offerId/offerText,
Attribut-Abfrage mit Limit (sonst Haenger ab 26 Attributen)
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Kunde (Dashboard, nur mit laufendem Abo): Antrag mit Leistung, Anbieter,
Domain, Rechnungsbetrag/-zeitraum, Grund, Rechnungsnachweis (PDF/JPG/PNG)
und E-Mail fuer die Antwort; Eingangsbestaetigung per Mail, Status und
Antwort auf Rueckfragen im Portal
- Admin-Mail mit Link admin.html?gutschrift=<id> (Login leitet zurueck)
- Admin-Tab "Gutschriften": Kunde und Vertrag, Nachweise, Verlauf,
automatische Pruefungen (Duplikate per Datei-Hash/Domain, Abo, Zeitraum,
DNS-Abgleich Nameserver/MX mit Anbieter), Pflicht-Checkliste, Rechner
(tagesgenau anteilig), Genehmigen/Rueckfrage/Ablehnen mit Kundenmail
- Genehmigte Gutschrift als Stripe-Kundenguthaben (idempotent, Nachbuchen
bei Stripe-Stoerung)
- Nachweise in upload-data/ (gitignored), Loeschung abgelehnter nach 6 Monaten
- Stundenkontingent auch fuer Monate, die ein Guthaben komplett deckt
(invoice.total statt amount_paid)
- Datenschutzerklaerung Abschnitt 7 ergaenzt, PDF neu erzeugt
- Collection takeoverCredits in setup-abo-collections.mjs
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Webhook customer.subscription.updated erkennt neu gesetzte Kuendigungen.
Kuendigungs- und Widerrufsseite markieren ihre Kuendigung per Metadatum
cancelSource, damit keine doppelte Bestaetigung verschickt wird.
Admin-Hinweis auch bei zurueckgenommener Kuendigung.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Bestellseite: Pflichtangaben direkt ueber dem Button, Zahlungsarten,
Wahl des Leistungsbeginns (sofort / nach Widerrufsfrist via 15 Tage
Stripe-Probezeit), keine Vorauswahl
- Zustimmungsnachweis: Zeitpunkt, Version, Wahl, IP, Browser in Stripe-
Metadaten und Appwrite (neue Attribute, Setup-Skript ergaenzt)
- Checkout: Karte + SEPA (Fallback nur Karte), korrigierter Hinweistext
- Vertragsbestaetigung mit PDF der Vertragsunterlagen statt HTML
- Kuendigen/Widerrufen: Formular direkt (keine Zwischenseite), Wunsch-
termin, Druck/PDF der Bestaetigung, Vorbefuellung bei Login
- Widerrufsbutton im Abo waehrend der Frist, 'beginnt am'-Status
- Pilotbetrieb per ABO_PILOT_EMAILS
- Vertragsbedingungen v2, Datenschutzerklaerung, Kalkulator ohne MwSt
und mit 100/80 EUR
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Noch nicht freigegeben, nicht nach main pushen. Enthaelt amtliche
Muster-Widerrufsbelehrung (Fassung ab 19.06.2026), AVV mit TOM und
Unterauftragsverarbeitern, Stundenkontingent statt Guthaben, Wahl des
Leistungsbeginns, Sperre erst ab 100 EUR, Hetzner-Backup-Formulierung.
Platzhalter: E-Mail-Anbieter und Postfachgroesse.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- Bestellung auf der Portal-Startseite: Zusammenfassung, AGB- und
Vorab-Leistungs-Zustimmung, Button 'Zahlungspflichtig bestellen',
danach Stripe Checkout (Abo, Kleinunternehmer-Fusszeile)
- Stripe-Webhook /api/abo/webhook spiegelt Abos nach Appwrite
(subscriptions) und schreibt je bezahlter Rechnung 60 min gut
(hourCredits, idempotent ueber Invoice-ID, 12 Monate gueltig)
- Stundenkonto: FIFO-Verbrauch aus Worksheets mit countsToPlan,
15-Minuten-Takt, Mehraufwand separat ausgewiesen
- Stripe-Kundenportal fuer Zahlungsart/Rechnungen/Kuendigung
- Oeffentlicher Kuendigungsbutton (§ 312k) und Widerrufsbutton
(§ 356a) mit Eingangsbestaetigung per Mail
- Abo-Bedingungen inkl. Leistungsbeschreibung und Widerrufsbelehrung,
als Anhang der Vertragsbestaetigung
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Ohne Query.limit liefert Appwrite nur 25 Dokumente. Bei 110 Kunden fehlten
in WOMS alle Kunden ab 'Autowerkstatt ...' alphabetisch - neu angelegte
Leads wirkten dadurch, als waeren sie nie angelegt worden.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der im Gespraech vereinbarte Termin wird mit einem Klick am Akquise-Ticket
eingetragen (startDate/startTime/endTime/assignedTo/assignedName) - genau die
Felder, die das WOMS-Planboard liest. Kein zweiter Datensatz, damit Termin und
Ticket nicht auseinanderlaufen koennen.
- server/services/appointments.js: Ticket des Kunden finden, Termin schreiben,
belegte Zeiten lesen; Umrechnung ISO <-> "dd.mm.yyyy" und "hh:mm" <-> "hhmm"
- Routen GET /api/portal-admin/appointments und
POST /api/portal-admin/customers/:id/appointment
- Bereits vergebene Zeiten sind in der Terminauswahl gesperrt ("belegt"), der
eigene Termin bleibt waehlbar; steht am Ticket schon einer, ist er beim
Oeffnen vorausgewaehlt
- Zeiten, die jemand von Hand in WOMS gesetzt hat, tauchen mit auf
Sicherung gegen Datenverlust: bevorzugt wird das Akquise-Ticket. Gibt es keins,
kommt nur ein anderes offenes Ticket OHNE Datum in Frage - bei Webpage- und
Projekttickets bedeutet startDate den Projektstart, den wuerden wir sonst still
ueberschreiben. Die Rueckmeldung nennt immer Nummer und Art des Tickets.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Der Button "← Admin" und die Lead-Zeile in der Kopfleiste sind raus. Das
webklar-Logo ist jetzt selbst der Link zurueck ins Admin-Panel (mit title
als Hinweis). Der Lead steht weiterhin ueber dem Preis ("Angebot für …").
Bei Leads (customerStatus=lead) steht in der Kundenliste jetzt der Button
"Rechnungsanalyse". Er oeffnet /kalkulation.html?customer=<id> - die Seite,
die im Gespraech vor Ort oder am Telefon ausgefuellt wird.
- Positionen nach Bereich (Website, Onlineshop, Softwareprojekt,
Automatisierung/KI, laufende Kosten) mit Stundenwerten und Mengen
- Preis ueber die Stundenstaffel (150/130/115/100), Brutto mit 19 % MwSt.,
Zahlungsmodalitaeten ab 1.000 / 3.000 EUR
- Hinweis, ob selbst zusagbar (<= 5 Std), Termin abzustimmen oder ob Kenso
freigeben muss (Shop, Software, Automatisierung)
- Termin mit Justin/Kenso aus den hinterlegten Zeitfenstern, Notizen,
Kundenportal-Zugang mit QR-Code
- Zusammenfassung als Text zum Kopieren -> gehoert ins Akquise-Ticket
Bewusst ohne eigenen Datensatz: dokumentiert wird im Ticket. Der Stand liegt
pro Kunde nur im localStorage, damit ein angefangenes Gespraech nicht
verloren geht.
Fonts (Caprasimo, Figtree) liegen lokal unter public/fonts, kein Abruf beim
Google-CDN. Neue Route GET /api/portal-admin/customers/:customerId liefert
die Stammdaten zum Vorbefuellen.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Portal-Session (webklar_portal_session) lebt unabhaengig vom preview_session-JWT.
Fehlte das Preview-Cookie (WebView-Datenverlust, Ablauf), liefen alle
*.project.webklar.com-Hosts in eine 302-Login-Schleife (schwarze Seite im
Kundenportal), obwohl der Nutzer eingeloggt war - Re-Login war der einzige
Ausweg. Jetzt heilt jeder Aufruf der Projektuebersicht das Cookie automatisch.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- runDeploy: Repos mit package.json-build-Script ohne index.html werden
automatisch als node_build gebaut (Expo/Vite/React zeigen sonst nur den
Platzhalter); Build-Fehler fallen auf static+Platzhalter zurueck
- deployNodeBuild: npm-ci-Fallback auf npm install, Output-Erkennung
dist/build/out/web-build, maxBuffer+Timeout
- updateWebsiteProject: bestehenden type/index aus .webklar-preview.json
erhalten statt bei jedem Edit auf static zurueckzusetzen
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Fehlt im Repo eine index.html, zeigte die Preview nur die
'wird vorbereitet'-Platzhalterseite, obwohl das eigentliche Projekt
(z.B. prototyp-app.html in knso/videogen) laengst deployt war.
ensureIndexHtml befoerdert jetzt die beste vorhandene HTML-Datei
(bevorzugte Namen, sonst einzige/groesste) per Kopie zur index.html;
der Platzhalter kommt nur noch, wenn gar keine HTML-Datei existiert.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- runDeploy schreibt Platzhalter-index.html, wenn das Deploy-Verzeichnis
keine index.html enthaelt (leergeraeumtes Repo/Fehlbuild) -> nie mehr
nginx 403 Forbidden, Subdomain bleibt erreichbar.
- deployStatic: Fallback auf Repo-Wurzel bei fehlendem index-Unterordner,
.git wird nicht mehr mit ausgeliefert.
- Webhook: fehlt .webklar-preview.json im Repo, wird die Hosting-Config aus
dem Appwrite-Projektstand rekonstruiert, ins Repo zurueckgeschrieben und
normal deployt (ensureRepoPreviewConfig).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Statt nur Name + Link zeigt jede Projektkarte jetzt ein kleines,
skaliertes Live-Fenster der echten Webseite (iframe der previewUrl,
responsiv per ResizeObserver auf 1280px-Basisbreite herunterskaliert).
Klick aufs Fenster oeffnet die Seite im neuen Tab. Nicht-fertige
Projekte zeigen einen Platzhalter statt iframe.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- archiveRepo: fehlt das Gitea-Repo (404), wird nur das Appwrite-Dokument
(ent)archiviert statt hart zu scheitern -> orphaned Projekte lassen sich
aus der Liste entfernen
- Reaktivierungs-Redeploy wird bei fehlendem Repo uebersprungen
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- hasPreview existiert im Live-Schema nicht -> Appwrite lehnte Update ab
- syncPreviewTls muss NACH updateDocument laufen, sonst liest es isPublic
noch im alten Zustand (oeffentlicher Router bekam faelschlich preview-auth)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>