Files
Webklar-Kundenbereich/docs/funktionen/termine-einbau.md
T
ClaudeandClaude Sonnet 5.5 938b7099dd Funktionen: Team-Route fuer die Terminplaner-Einbauanleitung
GET /api/team/funktionen/:customerId/termine/anleitung?projectId= liefert die volle
Einbauanleitung (ohne kurz) fuer das WOMS-Panel: stand, warnungen mit Drift-Warnung
gegen die Version im Ticket, abschnitte, text, state, ticketWoid, ticketVersion und
hindernis (nur in setup). Nur lesend, in jedem Zustand und Modus, ohne customerViewLog.
Die Teamansicht GET /:customerId traegt hindernis je Terminplaner in setup, mit einem
Abo-Lauf je Anfrage und ohne portalFeatures erneut zu lesen. Die Einbau-Doku nennt die
Anleitung je Kunde im WOMS-Panel und die Kurzfassung im Ticket.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
2026-10-05 01:18:30 +00:00

15 KiB
Raw Blame History

Terminplaner auf der Kundenwebsite einbauen

Anleitung für das WEBklar-Team. Das Widget lässt Gäste auf der Website des Kunden Termine buchen. Leistungen, Personen und Öffnungszeiten pflegt der Kunde im Portal (Tab „Funktionen“, Kachel „Terminplaner“). Den siteKey (16 Zeichen, a–z und 2–7) und den fertigen Einbau-Code findest Du im Portal: in der Kundenansicht des Kunden unter Terminplaner → Einstellungen, Abschnitt „Einbau auf deiner Website“ (mit Knopf „Code kopieren“). Den siteKey zeigt auch das Ticketsystem (WOMS): Kunde → Reiter „Funktionen“, Zeile Terminplaner, mit Kopierknopf. Ohne aktive Funktion, ohne gespeicherte Einstellung und ohne Telefonnummer des Betriebs (bei Verbrauchern auch ohne Anschrift) ist nichts buchbar: Das Widget zeigt dann den Ausweichtext der Seite. Die Anleitung für einen bestimmten Kunden erzeugt das Ticketsystem (WOMS) selbst: Kunde → Reiter „Funktionen“ → Anleitung des Projekts, mit den Einstellungen des Betriebs, dem fertigen Einbau-Code, dem CSS-Vorschlag, den Datenschutz-Absätzen, der Abnahme-Checkliste und den Warnungen. Im Einrichtungs-Ticket steht davon die Kurzfassung (Stand der Einstellungen, Einbau-Code, Schritte) mit dem Link auf diese Anleitung. Dieses Dokument ist die allgemeine Referenz dahinter: Es erklärt Variablen, Stile, Datenschutz und API, die für jeden Betrieb gelten.

1. Das Snippet

<div data-webklar-termine="SITEKEY" data-api="https://project.webklar.com" data-modus="inline">
  <p>Termine telefonisch unter 06301 1234.</p>   <!-- Ausweichtext, siehe Abschnitt 4 -->
</div>
<script src="https://project.webklar.com/embed/termine.js" defer></script>

Das Skript steht einmal pro Seite, vor </body>. Es hat keine Abhängigkeiten, setzt keine Cookies, speichert nichts im Browser und lädt nichts von Dritten. Texte der Kunden setzt es nur als Text ein, nie als HTML. Es wird 5 Minuten gecacht und darf von jeder Domain geladen werden.

2. Variablen (Attribute am div)

Attribut Pflicht Bedeutung
data-webklar-termine ja der siteKey des Projekts
data-api nein Herkunft des Portals, Standard https://project.webklar.com. Ohne Angabe nimmt das Skript die Herkunft seiner eigenen Adresse. Das klappt nur bei einem normalen <script src>. Bei Modul-Import, Tag-Manager oder nachgeladenem Skript ist data-api Pflicht.
data-modus nein inline (Standard): die Buchungsstrecke steht direkt in der Seite und lädt beim Aufruf der Seite. knopf: ein Knopf öffnet die Strecke in einem Dialog (<dialog>). Beim Laden der Seite ruft das Widget einmal die Konfiguration ab (für den Knopf „Vertrag widerrufen“, siehe Abschnitt 5), alles Weitere erst beim Klick.
data-knopf nein Text des Knopfes im Modus knopf, Standard „Termin buchen“.
data-leistung nein ID einer Leistung (s_ plus 8 Zeichen, steht im Portal bei der Leistung). Die Leistung ist vorgewählt, der Schritt „Leistung wählen“ entfällt. Praktisch für einen Knopf neben einer Leistung auf der Seite. Eine unbekannte ID wird ignoriert.

Alle Klassen beginnen mit wkt-. Die Stile stehen einmalig in <style id="wkt-style"> und zwar in @layer wkt. Das heißt: jede Regel der Kundenseite (ohne Layer) gewinnt gegen die Standard-Stile. Das ist gewollt, hat aber eine Folge: Hat das Theme der Kundenseite globale Regeln wie button{…} oder input{…}, greifen sie auch im Widget. Prüfe das beim Abnehmen und überschreibe bei Bedarf gezielt, z. B. [data-webklar-termine] .wkt-btn{ … }.

Das Layout richtet sich nach der Breite des div (Container Query, braucht aktuelle Browser), nicht nach dem Bildschirm. Das div muss eine Breite haben (normaler Blockcontainer; in einem Flex- oder Grid-Element ohne Breite schrumpft es auf null).

Zwei Widgets auf einer Seite

Das geht. Jedes Widget bekommt eigene IDs, die Stile werden nur einmal eingefügt. Beispiel: zwei Knöpfe neben zwei Leistungen.

<div data-webklar-termine="SITEKEY" data-modus="knopf" data-knopf="Damenschnitt buchen" data-leistung="s_abcd1234"></div>
<div data-webklar-termine="SITEKEY" data-modus="knopf" data-knopf="Färben buchen" data-leistung="s_efgh5678"></div>

3. Aussehen anpassen (CSS-Variablen)

Setze die Variablen auf dem div, in der Seiten-CSS oder auf :root, z. B. [data-webklar-termine]{ --wkt-akzent:#0f766e; }. Alle sind optional.

Variable Standard
--wkt-akzent (Knöpfe, aktive Tage, Rahmen der freien Tage) die Betriebsfarbe aus den Einstellungen, sonst #1d4ed8
--wkt-akzent-text (Schrift auf dem Akzent) passend zur Betriebsfarbe (hell oder dunkel), sonst #fff
--wkt-hintergrund #fff
--wkt-text #1f2937
--wkt-rand (Eingabefelder, Karten) #858f9e
--wkt-radius 10px
--wkt-schrift die Schrift der Seite (inherit)
--wkt-schrift-titel (Überschriften) wie --wkt-schrift

Die Betriebsfarbe gilt nur, solange --wkt-akzent nicht gesetzt ist; die Schrift darauf (Weiß, dunkles Grau oder Schwarz) wählt das Skript so, dass sie mindestens 4,5 : 1 erreicht. Setzt Du --wkt-akzent, setze auch --wkt-akzent-text. Im Modus knopf kennt das Widget die Betriebsfarbe erst nach dem Klick: Der Knopf selbst nimmt --wkt-akzent der Seite oder #1d4ed8. Setze --wkt-akzent dort also immer.

Kontrast (WCAG 2.1 AA): Text auf dem Hintergrund mindestens 4,5 : 1, Schrift auf dem Akzent mindestens 4,5 : 1, --wkt-rand gegen den Hintergrund mindestens 3 : 1 (sonst sind Eingabefelder schwer zu erkennen). Der Fokusrahmen nimmt die Textfarbe.

Stil 1: hell und freundlich (Salon, Kosmetik)

[data-webklar-termine]{
  --wkt-akzent:#9d4b6f;
  --wkt-akzent-text:#fff;
  --wkt-hintergrund:#fff;
  --wkt-text:#3b2a33;
  --wkt-rand:#8c7580;
  --wkt-radius:14px;
  --wkt-schrift:"Nunito", system-ui, sans-serif;
  --wkt-schrift-titel:"Fredoka", system-ui, sans-serif;
}

Stil 2: dunkel (Tattoo, Studio, Bar)

[data-webklar-termine]{
  --wkt-akzent:#f5c542;
  --wkt-akzent-text:#111;
  --wkt-hintergrund:#18181b;
  --wkt-text:#f4f4f5;
  --wkt-rand:#8b8b94;
  --wkt-radius:4px;
  --wkt-schrift:"Inter", system-ui, sans-serif;
}

Stil 3: schlicht und eckig (Kanzlei, Handwerk, Beratung)

[data-webklar-termine]{
  --wkt-akzent:#1f3a5f;
  --wkt-akzent-text:#fff;
  --wkt-hintergrund:#fff;
  --wkt-text:#1a1a1a;
  --wkt-rand:#767676;
  --wkt-radius:0;
  --wkt-schrift:Georgia, "Times New Roman", serif;
  --wkt-schrift-titel:system-ui, sans-serif;
}

Die Schriften lädt das Skript nicht selbst. Sind sie auf der Kundenseite nicht eingebunden, greift die Systemschrift.

4. Ausweichtext

Alles, was im div steht, gehört der Kundenwebsite. Das Skript ersetzt es nur, wenn die Konfiguration geladen werden konnte. Bei ausgeschaltetem JavaScript, unbekanntem Schlüssel, abgeschalteter Funktion, fehlenden Pflichtangaben des Betriebs oder Netzfehler beim Laden bleibt der Text stehen. Schreib dort die Telefonnummer des Betriebs hinein und gestalte den Text im Stil der Seite.

Im Modus knopf ersetzt der Knopf den Text auf der Seite. Der Text erscheint dafür im Dialog, wenn das Laden beim Klick scheitert.

Bricht das Netz mitten in der Strecke ab, zeigt das Widget „Gerade nicht erreichbar. Bitte rufen Sie an: {Telefon des Betriebs}“ (bei Anrede „du“: „Bitte ruf an“).

5. Was das Widget zeigt

  1. Leistung wählen (Name, Dauer, Preis, Kurzbeschreibung) mit der Zahlungsart („Bezahlung vor Ort: …“). Entfällt bei data-leistung.
  2. Person wählen („Egal“ zuerst). Nur wenn mehrere Personen infrage kommen und der Betrieb die Wahl erlaubt; bei Plätzen entfällt der Schritt.
  3. Tag (Monatsraster, nur Tage mit freien Zeiten sind wählbar) und Uhrzeit. Alle Daten und Uhrzeiten kommen vom Server in Berliner Zeit. Das Widget rechnet nie mit dem Datum oder der Zeitzone des Browsers.
  4. Angaben: Name, E-Mail, Telefon, Nachricht und Zusatzfelder nach den Einstellungen des Betriebs.
  5. Zusammenfassung mit Beschreibung der Leistung, Stornobedingungen (daneben der Link „AGB des Betriebs“, wenn im Portal eine https-Adresse hinterlegt ist), Widerrufsbelehrung (aufklappbar, bei Verbraucher-Betrieben), Datenschutz-Hinweis, den Haken (Storno, vorzeitiger Beginn, Erinnerung) und dem Knopf („Zahlungspflichtig buchen“, „Termin buchen“ oder „Termin anfragen“, kommt vom Server).
  6. „Fast geschafft“: Der Gast bestätigt den Termin über den Link in der E-Mail. Die Reservierung gilt 30 Minuten.

Im Fuß stehen „Vertrag widerrufen“ als zweiter Knopf (kein bloßer Textlink; für Verbraucher bei jeder Leistung, ob sofort buchbar oder als Anfrage, weil auch ein angenommener Anfrage-Vertrag widerrufen werden kann), „Datenschutz“ (Link auf die Datenschutzerklärung des Betriebs) und klein „Terminbuchung über WEBklar“. Im Modus knopf steht der Fuß nur im geöffneten Dialog. Damit die Widerrufsfunktion (§ 356a BGB) trotzdem dauerhaft auffindbar ist, lädt das Widget dort schon beim Laden der Seite die Konfiguration (ein einziger Abruf, sonst nichts) und setzt, wenn der Betrieb ein Widerrufsrecht hat, den Knopf „Vertrag widerrufen“ als zweiten Knopf neben den Buchungsknopf. Er öffnet den Dialog gleich beim Widerrufsformular. Anrede (Sie oder Du) und Betriebsfarbe kommen aus den Einstellungen. Die rechtlich vorgeschriebenen Texte (Haken, Belehrung) stehen immer in der Sie-Form.

Bedienung nur mit der Tastatur: Tab und Umschalt+Tab zwischen den Elementen, im Monatsraster Pfeiltasten (Tag), Pos1 und Ende (Wochenanfang und -ende), Bild ↑ und Bild ↓ (Monat), Eingabe oder Leertaste wählt den Tag. Im Dialog schließt Esc.

6. Datenschutzerklärung des Kunden

Das Skript lädt beim Aufruf der Seite die Konfiguration (Texte, Leistungen) von project.webklar.com; im Modus inline folgen die Termine beim Durchklicken, im Modus knopf erst beim Klick auf den Knopf. Dabei sieht der Server die IP-Adresse des Besuchers. Die Datenschutzerklärung der Website braucht deshalb den Absatz „Online-Terminbuchung“ aus docs/rechtliches/termine-datenschutz-absatz.md (zwei Fassungen: Sie und Du, nimm die zur Erklärung der Website passende). Der Absatz trägt den Kopf „ENTWURF – nur Pilot, vor Einsatz bei fremden Betrieben prüfen“. Solange der Kopf dort steht, gilt: nur beim Pilotkunden einsetzen, bei anderen Betrieben erst nach der Rechtsprüfung. Die [[wenn …]]-Sätze richten sich nach den Einstellungen des Betriebs (Telefon-Feld, Nachricht, Erinnerung, Verbraucher). Bau das Widget erst ein, wenn der Absatz in der Datenschutzerklärung steht. Die URL der Datenschutzerklärung (und des Impressums) muss in den Betriebsangaben im Portal stehen, sonst fehlt der Link im Fuß.

7. Abnahme-Checkliste

  • Funktion im Ticketsystem aktiviert (Zustand „aktiv“), TERMINE_ENABLED auf pilot oder on, Kunde im Pilot. siteKey und Einbau-Code aus dem Portal (Terminplaner → Einstellungen → „Einbau auf deiner Website“) übernommen.
  • Einstellungen im Portal gespeichert, Telefon und Anschrift des Betriebs eingetragen (das Portal weist darauf hin, solange etwas fehlt), URLs von Impressum und Datenschutz mit https://.
  • Datenschutz-Absatz steht in der Datenschutzerklärung der Website (Abschnitt 6).
  • Ausweichtext prüfen: JavaScript aus, Funktion im Ticketsystem ausgeschaltet oder falscher siteKey: Der Text steht, nichts ist kaputt.
  • Komplett durchbuchen bis „Fast geschafft“; die Bestätigungs-E-Mail kommt an, der Link bestätigt, die zweite E-Mail (mit Kalenderdatei) kommt an, im Portal steht der Termin. Danach den Testtermin absagen (Verwaltungslink aus der E-Mail oder im Portal).
  • Bei Verbraucher-Betrieb (auch mit nur Anfrage-Leistungen): Widerrufsbelehrung aufklappbar, bei Preis erscheint der Haken für vorzeitigen Beginn bei Terminen in den nächsten 14 Tagen, „Vertrag widerrufen“ steht als Knopf im Fuß und das Formular funktioniert.
  • Modus knopf bei Verbraucher-Betrieb: Neben dem Buchungsknopf steht dauerhaft der Knopf „Vertrag widerrufen“ (nicht erst im Dialog) und öffnet den Dialog gleich beim Widerrufsformular; ohne Widerrufsrecht (Unternehmen, Freizeitleistung) fehlt er.
  • Nur mit der Tastatur bedienen: Fokus immer sichtbar, Monatsraster mit Pfeilen, Pos1/Ende und Bild ↑/↓, Esc schließt den Dialog und der Fokus geht zum Knopf zurück.
  • Handy (390 px Breite): kein seitliches Scrollen, Knöpfe gut treffbar, Dialog passt in den Bildschirm.
  • Zoom 200 % und große Schrift: nichts wird abgeschnitten.
  • Kontrast der gesetzten Variablen geprüft (Abschnitt 3), globale Regeln des Themes (button, input) stören nicht.
  • Zwei Widgets auf einer Seite (falls geplant) stören sich nicht.
  • Anderer Browser, andere Zeitzone (z. B. im Browser auf New York stellen): dieselben Tage und Uhrzeiten wie in Berlin.
  • Entwicklerwerkzeuge: keine Cookies, nichts in Local/Session Storage, keine Anfrage an fremde Hosts außer dem Portal.
  • Automatische Prüfung gegen die Testseite: nice node test/ui/termine-widget.ui.mjs (startet einen eigenen Testserver, Screenshots unter /root/reports/terminplaner-ui/).

8. API-Kurzreferenz

Alle Pfade unter https://project.webklar.com/api/public/termine/v1. Antworten sind JSON. Fehler haben die Form { "fehler": { "feld": "Text" }, "code": "…" } (_ = Fehler ohne Feld). Ist der siteKey unbekannt, die Funktion nicht aktiv oder der Betrieb noch nicht buchbar, antworten alle Pfade unter /:siteKey mit 200 { "aktiv": false }. Appwrite nicht erreichbar: 503 { "fehler": { "_": "Gerade nicht erreichbar" } }. Die Antworten enthalten nie interne IDs.

Methode und Pfad Zweck
GET /:siteKey/config Betrieb (Name, Telefon, Adresse, URLs von Impressum, Datenschutz und AGB), Anrede, Farbe, Leistungen mit Knopftext, Personen, Regeln, Zahlungsart, Stornotext, widerruf, Texte der Haken und der Widerrufsbelehrung, Datenschutz-Hinweis, formToken. Eine Minute gecacht; das Widget lädt sie nach 100 Minuten neu.
GET /:siteKey/tage?leistung=&ressource=&monat=YYYY-MM Tage mit freien Zeiten plus Kalenderdaten des Monats (monat)
GET /:siteKey/zeiten?leistung=&ressource=&tag=YYYY-MM-DD freie Startzeiten, datumLang, vorzeitigNoetig (Haken für vorzeitigen Beginn an diesem Tag)
POST /:siteKey/buchungen Formular absenden. Antwort 201 { ok, email: "m***@gmail.com", minuten }. Honeypot kontakt_url und Formular-Token (Fehlercode neu_laden) schützen vor Automaten.
POST /:siteKey/widerruf Widerruf ohne Link: { name, email, nummer }, Antwort immer gleich { ok, eingang: { datum, uhrzeit } }
POST /termin/vorschau, bestaetigen, absagen, verschieben, widerruf, tage, zeiten Seiten der Gäste (Token aus dem Link der E-Mail, termin.html)
GET /kalender/:siteKey/:feedToken.ics Kalender-Abo des Betriebs je Person (geheimer Link aus dem Portal)

POST-Anfragen sind nur von Seiten des Betriebs erlaubt (Host des Projekts bzw. liveDomain samt www-Gegenstück) und vom Portal selbst; andere Herkunft: 403. Grenzen: 120 Abrufe pro Minute und 10 Buchungen pro 10 Minuten je IP, 60 Buchungen pro Stunde je Funktion. Mehr in server/routes/publicTermine.js.

9. Testen

/embed/termine-demo.html auf dem Portal ist eine nicht verlinkte Testseite (Strecke inline und als Knopf). Sie braucht einen Testbetrieb mit dem siteKey abcdefghijklmnop. Automatische Prüfungen: node --test test/termineEmbedStatic.test.js (Quelltext) und nice node test/ui/termine-widget.ui.mjs (Browser, eigener Testserver mit Nachbau der Datenbank; vorher free -m).