Zusammenführung von TP2-Abschluss und Webshop (origin/main cab9c33) mit Öffnungszeiten live. Beide
Funktionen bleiben vollständig: jeder Konflikt ist als Vereinigung gelöst, Reihenfolge der Funktionsschlüssel
überall news, termine, shop, zeiten.
Konfliktdateien (28): public/app.js, dashboard.html, funktionen.js, widerrufen.html; scripts/stripe-products.mjs;
server/config.js, index.js, middleware/kundenansichtGuard.js, routes/abo.js, routes/admin/funktionenAdmin.js,
routes/funktionen.js, services/customerAdmin.js, customerViewLog.js, featureRegistry.js, retention.js,
siteFeatures.js, stripeBilling.js; test/customerViewLog, featureFreischaltung, featureRegistry, funktionenRoutes,
funktionenTermine, kundenansichtGuard, mailTemplates, publicForms, siteFeatures, stripeProductsScript (je
.test.js) und test/support/schema.js.
Entscheidungen: Mailvorlagen G01–G15, K01–K30, T01–T21 lückenlos (K24–K26 Webshop, K27–K30 Öffnungszeiten);
Stripe-Katalog mit shopSetup und zeitenSetup; Widerruf kennt shop und zeiten; ABO_FUNKTIONEN bleibt analyse
und termine (kein zeiten); activate() schreibt die Zeiten-Felder nur über extra; kein Halter von
feature-activate:<id> ruft disable() auf; die Aufbewahrungs-Löschung löscht Zeiten-Daten vor dem Abschalten und
hält den Kunden bei einem Fehler (held); die Admin-Antwort trägt ersatzTicket und warnung; Hash-Routen webshop
und zeiten, Ansichten view-webshop und view-zeiten; assertOwnProject behält die Meldung von main.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kachel „Öffnungszeiten live“ inline in funktionen.js (keine neuen Importe, keine Zusatzabfrage),
Ansicht #zeiten (zeiten.js, zeiten-bestellen.js, zeiten.css) mit Einschalten im Wartungs-Abo,
Bestellen für 30 € (Widerrufsbelehrung vor dem Knopf, Ruling R32), Behalten nach dem Abo-Ende,
Statuskarte je Zustand, Rückkehr von Stripe und Platz für den Editor (Aufgabe 29).
GET /api/funktionen liefert dafür zeiten.belehrung (Haken belehrung der Funktion).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
analyse.js setzt die Bausteine aus analyse-cards.js zusammen: Kopfleiste mit
Stand und Zeitraum, Reiter (Pfeiltasten), Übersicht mit Ladegerüst und
Fehlerkarte, Diagramm-Blase, Ranglisten-Umschalter, Heatmap erst beim Öffnen
des Reiters, Einstellungen. Einschalten, gesperrt, nicht verfügbar und
unbekannt erscheinen im selben Rahmen, Texte unverändert.
analyse.css (neu) trägt alle Analyse-Regeln unter .an mit eigenem türkisen
Akzent; der Analyse-Abschnitt in funktionen.css entfällt. Der Client-Test des
Banners liest die Banner-Regeln jetzt aus analyse.css.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Kachel in Funktionen (Kommt bald / Aus / Aktiv mit Anfragen-Zahl / Abgeschaltet),
Ansicht #termine als Unterseite von Funktionen: Projektauswahl, Vorlagenauswahl
beim ersten Oeffnen, Reiter-Geruest (Platzhalter fuer Leistungen, Team & Plaetze,
Einstellungen) und der Reiter Termine mit Tag/Woche/Monat, Filtern, Aktionen mit
Rueckfragen in der Seite, Termin eintragen und Zeit sperren.
Co-Authored-By: Claude Sonnet 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>
Im Konto-Menü (Profilbild) gibt es unter "Konto" jetzt "Passwort
ändern". Die Seite fragt aktuelles Passwort, neues Passwort und
Wiederholung ab (mindestens 8 Zeichen, "Passwörter anzeigen"). Am
Handy ist sie eine Unterseite des Tabs, aus dem man kam; Direktlink
/dashboard.html#passwort. Im Ansichtsmodus ist sie gesperrt.
Server (POST /api/auth/password):
- Das aktuelle Passwort wird über eine kurze Appwrite-Session geprüft,
die gleich wieder gelöscht wird. Tippfehler lösen nicht die globale
Login-Sperre aus. Es gibt höchstens 5 Prüfungen je Kunde und Stunde,
das bleibt unter Appwrites Grenze (10 Anmeldungen je E-Mail und
Stunde).
- Das neue Passwort wird über die Users-API gesetzt. Fehler von
Appwrite (zuletzt benutzt, zu häufig, persönliche Daten) kommen als
verständliche Meldung.
- Die Klartext-Kopie in customers.portalPassword und ein Alt-Eintrag
in den Notizen werden geleert. Ein selbst gewähltes Passwort wird
nie im Klartext gespeichert; das Ticketsystem zeigt dann "–".
- Der Kunde bekommt eine Bestätigungsmail (Kopie an die Admins wie
bei allen Kundenmails).
Andere Geräte werden abgemeldet: Kunden-Sessions merken sich beim
Login den Passwortstand (Appwrite passwordUpdate, pwv).
requireSession und /api/auth/me prüfen ihn, 60 s zwischengespeichert.
Das gilt auch, wenn wir im Ticketsystem ein neues Passwort setzen. Ist
Appwrite nicht erreichbar, bleibt die Session gültig. Sessions von
vor diesem Update haben keinen Stand und laufen normal aus (höchstens
7 Tage).
Login-Seite: Hinweis auf "Passwort ändern" statt "über das
Ticketsystem", dazu "Passwort vergessen?" mit support@webklar.com.
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der Server bereitet Commits, Tickets und Arbeitseinträge für Kunden auf
(server/services/customerProjectView.js), die Rohdaten bleiben intern:
- keine Co-Authored-By-/Signed-off-by-Zeilen und KI-Hinweise, keine
Commit-Kürzel, Kontonamen, Git-Links oder Branch
- technische Commits (Preview-Config, Merges) ausgeblendet, bekannte
Automatik-Texte übersetzt, "fix"/"upload" -> "Website aktualisiert"
- Ticketstatus und Arbeitsarten auf Deutsch (Open -> Eingegangen,
Remote -> Online erledigt, ...), Fortschritt in 4 Schritten
- Akquise-Tickets auch in den Projektdetails verborgen
- /api/projects ohne Gitea-URL, Repo- und Vorlagennamen
Darstellung: Auftragskarte mit Status und Fortschrittsleiste, Zeitleiste
"Letzte Änderungen" nach Tagen, Arbeiten mit Farbe/Symbol je Art und
Arbeitszeit; der Tab "Fortschritt" nutzt dieselben Bausteine. In der
App-Ansicht öffnen sich die Projektdetails als eigene Seite mit Zurück
zu "Projekte".
Tests: npm test (node --test, 22 Fälle).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
- "Abo verwalten & Rechnungen" (Stripe-Kundenportal) steht nur noch im Konto-Menü
(Gruppe "Verträge", nur mit Abo). In der Wartungskarte bleibt nur bei fehlgeschlagener
Abbuchung "Zahlungsart aktualisieren".
- "Verträge hier kündigen" und "Vertrag widerrufen": raus aus Seitenfuß, Wartungskarte und
Abo-Seite; im Kundenbereich nur noch im Konto-Menü. Ausnahme: während der Widerrufsfrist
zeigt die Wartungskarte weiter "Vertrag widerrufen · bis TT.MM." (§ 356a BGB: hervorgehoben
platziert). Öffentlich bleiben beide auf der Login-Seite und im Footer von webklar.com.
- "Abmelden" nicht mehr in der Kopfzeile, nur noch im Konto-Menü.
- Hinweis unter den Rechnungen verweist aufs Profilbild und öffnet das Menü.
- Browser ohne <dialog>: Menü klappt als Box auf (statt Abmelden in der Kopfzeile).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Die Statusleiste ist wieder durchsichtig (black-translucent), damit der
lila Hintergrund hinter Uhrzeit und Notch weitergeht. Dass die Tab-Leiste
damit auf kurzen Seiten 59 px zu hoch hing, liegt an einem bekannten
iOS-Verhalten: In der installierten App bekommen fixierte Elemente die
volle Bildschirmhoehe nur, solange die Seite scrollbar ist.
- Installierte App auf iOS (display-mode standalone, nur iOS): Die Seite
ist immer mindestens 1 px hoeher als der Bildschirm (100lvh + 1px).
100lvh ist dort die volle Hoehe, 100%, 100svh und 100dvh sind zu kurz.
- Die Chat-Hoehe kommt aus 100lvh.
- Das Konto-Sheet sperrt die Seite dort nicht mehr (overflow bleibt
sichtbar); es ist von oben mit voller Hoehe verankert, das Panel sitzt
unten.
- Bild-Grossansicht und Kamera des Chats sind von oben mit voller Hoehe
verankert statt mit inset: 0.
- Kuendigen/Widerrufen: dunkler Streifen hinter der weissen Statusleiste.
Browser, Android und Desktop unveraendert (pixelgleich).
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Der Fix aus bd7b1fc (Seite mindestens bildschirmhoch) hat nicht gewirkt
und wird zurueckgenommen. Die Ursache ist nicht die Seitenhoehe:
Bei black-translucent ist der Bereich fuer fixierte Elemente in der
installierten iOS-App um die Statusleiste (59 pt) kuerzer als der
Bildschirm. Solange die Seite oben steht, klebt dieser Bereich oben, und
die Tab-Leiste haengt ueber dem unteren Rand. Nach dem Herunterscrollen
sitzt sie richtig, deshalb sah es nur im Chat gut aus.
Mit undurchsichtiger Statusleiste (black) beginnt die App unterhalb der
Statusleiste, und der Bereich ist korrekt bemessen. Nachteil: Der lila
Schimmer reicht nicht mehr hinter die Uhrzeit. Die Abstaende oben kommen
weiter aus env(safe-area-inset-top), das dann 0 ist.
Browser, Android und Desktop unveraendert (pixelgleich).
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>
- 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>
- 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>
- 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>
- 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>
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>
- Login: Appwrite-User mit Label 'admin' (Ticketsystem-Mitarbeiter) bekommen
eine Admin-Session und landen auf /admin.html
- Admin-Dashboard: Statistik-Uebersicht, Projektsuche ueber alle Projekte
(inkl. Preview-/Gitea-Links, Vorschau ohne Kunden-Login), Kundenliste mit
Portal-Status, letztem Login und Projektanzahl
- Kundennummer (code) direkt im Admin-Bereich pflegbar
- 'Als Kunde ansehen': Admin sieht das Portal exakt wie der Kunde (Banner
mit Rueckweg zur Admin-Uebersicht)
- Kunden-Dashboard: neue Bereiche 'Meine Rechnungen' (InvoiceNinja inkl.
Link ins Rechnungsportal) und 'Was wurde gemacht' (Tickets + Worksheets
aus dem Ticketsystem, ohne interne Akquise-Tickets); Kundennummer im Header
- Admin-API /api/portal-admin/* (Session-basiert), Kunden-API /api/invoices
und /api/activity
- appwriteClient: uebrig gebliebene Debug-Instrumentierung entfernt
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Express-Server für Appwrite-Auth, Session, Projekt-Dashboard und Gitea-Webhook; statisches Frontend und Schema-Dokumentation für woms-database.
Co-authored-by: Cursor <cursoragent@cursor.com>