From 7bb918390a529d766e955e6dfb3a51a880e5c392 Mon Sep 17 00:00:00 2001 From: Claude Date: Mon, 5 Oct 2026 05:30:23 +0000 Subject: [PATCH] =?UTF-8?q?=C3=96ffnungszeiten:=20Kachel=20und=20Bestell-/?= =?UTF-8?q?Einschaltweg=20im=20Portal?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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 --- public/app.js | 8 +- public/dashboard.html | 10 + public/funktionen.js | 114 ++++- public/zeiten-bestellen.js | 377 ++++++++++++++ public/zeiten.css | 35 ++ public/zeiten.js | 219 +++++++++ server/routes/funktionen.js | 14 +- test/funktionenZeitenTile.test.js | 310 ++++++++++++ test/zeitenBestellen.test.js | 791 ++++++++++++++++++++++++++++++ test/zeitenEinschalten.test.js | 8 +- 10 files changed, 1878 insertions(+), 8 deletions(-) create mode 100644 public/zeiten-bestellen.js create mode 100644 public/zeiten.css create mode 100644 public/zeiten.js create mode 100644 test/funktionenZeitenTile.test.js create mode 100644 test/zeitenBestellen.test.js diff --git a/public/app.js b/public/app.js index ce23af1..c9f388b 100644 --- a/public/app.js +++ b/public/app.js @@ -139,7 +139,7 @@ function projectState(project) { return ['kb-state-off', 'Noch keine Website'] } -const FEATURE_LABELS = { preview: 'Vorschau', chat: 'Chat', invoices: 'Rechnungen', news: 'News-Pinnwand', termine: 'Terminplaner' } +const FEATURE_LABELS = { preview: 'Vorschau', chat: 'Chat', invoices: 'Rechnungen', news: 'News-Pinnwand', termine: 'Terminplaner', zeiten: 'Öffnungszeiten' } function renderProjectCard(project, features) { const li = document.createElement('li') @@ -1910,7 +1910,7 @@ function initPasswordView() { // "projekt" (Projektdetails als eigene Seite) gibt es ebenfalls nur in der App-Ansicht. // "passwort" oeffnet das Konto-Menue (Profilbild). #fortschritt fuehrt zu Projekte und scrollt zum Abschnitt // "Was wir zuletzt erledigt haben" (#pj-activity); hinter dem Hash darf ein Query stehen (#news?bestellt=1, Rueckkehr von Stripe). -const VIEW_HASHES = { projects: '', funktionen: 'funktionen', news: 'news', analyse: 'analyse', termine: 'termine', invoices: 'rechnungen', chat: 'chat', vertraege: 'vertraege', stunden: 'stunden', 'bestellen-hosting': 'bestellen-hosting', 'bestellen-wartung': 'bestellen-wartung', wechsel: 'wechsel', takeover: 'gutschrift', projekt: 'projekt', passwort: 'passwort' } +const VIEW_HASHES = { projects: '', funktionen: 'funktionen', news: 'news', analyse: 'analyse', termine: 'termine', zeiten: 'zeiten', invoices: 'rechnungen', chat: 'chat', vertraege: 'vertraege', stunden: 'stunden', 'bestellen-hosting': 'bestellen-hosting', 'bestellen-wartung': 'bestellen-wartung', wechsel: 'wechsel', takeover: 'gutschrift', projekt: 'projekt', passwort: 'passwort' } const HASH_ALIASES = { wartung: 'vertraege', abo: 'vertraege' } const viewLoaders = { chat: initChat } // Funktionen und News laden bei jedem Oeffnen neu (Zustand aendert sich durch Bestellung und Freischaltung); Modul erst dann laden @@ -1919,6 +1919,8 @@ const viewRefreshers = { news: () => import('/funktionen.js').then((m) => m.initNews(document.getElementById('news-body'))), analyse: () => import('/funktionen.js').then((m) => m.initAnalyse(document.getElementById('analyse-body'))), termine: () => import('/termine.js').then((m) => m.initTermine(document.getElementById('termine-body'))), + // baut sich bei gleichem Projekt und Stand nicht neu auf, solange der Editor steht (ungespeicherte Eingaben bleiben; zeiten.js, initZeiten) + zeiten: () => import('/zeiten.js').then((m) => m.initZeiten(document.getElementById('zeiten-body'))), } const loadedViews = new Set() @@ -2111,7 +2113,7 @@ const appLayout = window.matchMedia('(max-width: 880px), (pointer: coarse) and ( const coarsePointer = window.matchMedia('(pointer: coarse)') // Unterseite -> Tab, zu dem sie gehoert (und Text neben dem Zurueck-Pfeil). // "passwort" setzt onAppViewShown: die Ansicht, aus der das Konto-Menue geoeffnet wurde. -const APP_PARENT = { news: 'funktionen', analyse: 'funktionen', termine: 'funktionen', stunden: 'vertraege', 'bestellen-hosting': 'vertraege', 'bestellen-wartung': 'vertraege', wechsel: 'vertraege', takeover: 'vertraege', projekt: 'projects' } +const APP_PARENT = { news: 'funktionen', analyse: 'funktionen', termine: 'funktionen', zeiten: 'funktionen', stunden: 'vertraege', 'bestellen-hosting': 'vertraege', 'bestellen-wartung': 'vertraege', wechsel: 'vertraege', takeover: 'vertraege', projekt: 'projects' } const APP_PARENT_LABEL = { vertraege: 'Verträge', projects: 'Projekte', funktionen: 'Funktionen', invoices: 'Rechnungen', chat: 'Chat' } const appShell = { view: 'projects', scroll: {}, fromHistory: false, sideCards: [], installPrompt: null } const TEXT_INPUT = 'textarea, input:not([type=checkbox]):not([type=radio]):not([type=file]):not([type=date]):not([type=button]):not([type=submit]):not([type=range]):not([type=color])' diff --git a/public/dashboard.html b/public/dashboard.html index 6b48c97..0a6a4c7 100644 --- a/public/dashboard.html +++ b/public/dashboard.html @@ -23,6 +23,7 @@ +