Zum Hauptinhalt springen

Buchungsseiten (CalendarPage)

Zweck

CalendarPage ist die öffentliche Terminbuchungs-Seite pro Mitarbeiter — Calendly-ähnlich, aber direkt aus SpeamCore heraus mit Bezug zu Arbeitszeit-Modellen und Abwesenheiten. Gäste rufen die Seite ohne SpeamCore-Login auf, wählen einen Meeting-Type (z. B. „15 Minuten Vorgespräch"), sehen freie Slots und buchen mit Namen + E-Mail.

Pro Mitarbeiter eine Buchungsseite (1:1).

Voraussetzungen

- Berechtigung `view:Employee` (für die Admin-Liste/Detail), `update:Employee` (zum Anlegen/Bearbeiten der eigenen CalendarPage). - Eine `view:CalendarPage`-/`update:CalendarPage`-Permission auf Daten-Ebene. - Mitarbeiter mit einem aktiven [Vertrag](/employees-contracts) und zugeordnetem [Arbeitszeit-Modell](/work-time-models) — sonst kann die Verfügbarkeits-Engine keine Slot-Fenster berechnen. - Optional: ein Profilbild als [Dokument](/documents).

Berechtigungen (CASL)

ActionSubjectWirkung
viewCalendarPageAdmin-Liste + Detail aufrufbar
updateCalendarPageEinstellungen ändern (Begrüßungstext, Buffer, Slot-Fenster)
view / update / deleteCalendarMeetingTypeMeeting-Types verwalten
view / updateEmployeePage-Gate + Verantwortung für eigene CalendarPage

Die Public-Endpoints (/ca/:slug/*) brauchen keine Authentifizierung.

Datenmodell

CalendarPage

FeldPflichtTypWirkung
employeeIdjaUUID → EmployeeInhaber der Seite
slugauto bei CreateString (URL-safe, random 8–10 Zeichen)Public-URL /ca/{slug}. Unveränderbar nach Anlage.
activejaBoolean, default truefalse/ca/:slug liefert 404
welcomeTextneinTEXTBegrüßungs-Text auf der Public-Seite
bufferMinutesneinInteger, default 0Pause zwischen aufeinanderfolgenden Slots
minNoticeMinutesneinInteger, default 60Mindestvorlauf — Slots „in der nächsten Stunde" verschwinden
maxLeadDaysneinInteger, default 60Wie weit voraus überhaupt gebucht werden kann
dayStartMinutesneinInteger, default 540 (= 09:00)Frühester Slot-Beginn (Ortszeit)
dayEndMinutesneinInteger, default 1020 (= 17:00)Spätestes Slot-Ende (Ortszeit)

CalendarMeetingType

FeldPflichtTypWirkung
namejaString 255Anzeigename („30 Minuten Termin")
descriptionneinTEXTErklärtext im Picker
durationMinutesjaInteger, default 30Slot-Länge
locationneinString 512Adresse, Telefonnummer oder Video-Link (z. B. Zoom)
colorneinHex-StringAkzent-Farbe im Picker
activejaBoolean, default truefalse → blendet den Typ im Picker aus
positionneinIntegerSortier-Reihenfolge

Default-Seed beim Anlegen einer neuen Page: 15 Min (grün), 30 Min (blau), 60 Min (violett).

CalendarBooking

FeldWirkung
startAt, endAtZeitraum in UTC
attendeeFirstName, -LastName, -EmailDaten des Buchenden
attendeePhone, subjectoptional
statusconfirmed (Default) | cancelled
accessKeyToken, mit dem der Gast seine Buchung später wieder aufruft (via E-Mail-Link)
cancellationReason, cancelledAtAudit bei Storno

Verfügbarkeits-Engine

Der Public-Endpoint GET /api/ca/:slug/availability berechnet pro Zeitraum (max. maxLeadDays voraus) alle freien Slots:

  1. Zeit-Range validierenminNoticeMinutes schiebt den Start nach vorn, maxLeadDays deckelt das Ende.
  2. Arbeitszeit-Modell laden — pro Wochentag WorkTimeModelDay.coreTimeStartMinutes / -End aus dem aktiven Vertrag.
  3. Abwesenheiten mit status = approved sperren ganze Tage (siehe Abwesenheiten).
  4. Slot-Fenster pro Tag = max(dayStartMinutes, coreTimeStart) … min(dayEndMinutes, coreTimeEnd).
  5. Slots ausspielen mit Schrittweite = durationMinutes; nach jedem Slot zusätzlich bufferMinutes Pause.
  6. Existierende Buchungen mit status = confirmed blockieren überlappende Slots (inkl. Buffer-Zone).
  7. Ergebnis: Liste [ { startAt, endAt }, … ] in UTC.

Zeitzonen-Hinweis: Die Minuten-Werte in WorkTimeModelDay und CalendarPage sind in Ortszeit des Servers, die Buchungs-Zeitstempel werden in UTC ausgeliefert. Der Public-Page-Picker konvertiert pro Browser-Locale.

Schritt-für-Schritt-Anleitung

Buchungsseite aktivieren

  1. /calendar-pages öffnen → Mitarbeiter ohne Page erscheinen mit Button „+ Seite anlegen".
  2. SpeamCore generiert automatisch:
    • einen Slug (z. B. m6r3aef9)
    • die drei Standard-Meeting-Types (15/30/60 Minuten)
    • Slot-Fenster dayStartMinutes/-End aus dem Arbeitszeit-Modell des Mitarbeiters
  3. Optional welcomeText, bufferMinutes, minNoticeMinutes pflegen.
  4. Active = true setzen, sobald die Seite live gehen soll.

Meeting-Types anpassen

In der Detail-Seite unter Meeting-Types:

  • Neuen Typ hinzufügen (z. B. „60 Min Strategie-Call" mit eigener Farbe).
  • Bestehende Typen umbenennen, deaktivieren, Reihenfolge ändern (position).
  • location kann Adresse, Telefonnummer oder Zoom/Teams-Link sein — wird in der Bestätigungsmail an den Gast verschickt.

Slug + Public-URL

Der Slug ist nach Anlage unveränderbar (analog zu QR-Links und Visitenkarten). Die Public-URL lautet:

https://app.speamcore.com/ca/{slug}

Verknüpfung mit anderen Modulen:

  • Visitenkarte des Mitarbeiters → zeigt automatisch einen „Termin buchen"-Button mit Verweis auf die CalendarPage, wenn beide aktiv sind.
  • QR-Link → kann mit targetType = "url" und der CalendarPage-URL als targetUrl direkt zur Buchung führen.

Public-Workflow für Gäste

  1. Gast öffnet /ca/{slug} — sieht Foto, Name, Begrüßungstext, Meeting-Type-Auswahl.
  2. Wählt einen Meeting-Type → Slot-Picker zeigt Monatsnavigation und freie Zeiten.
  3. Wählt einen Slot → Buchungsformular: Vor-/Nachname, E-Mail, Telefon (optional), Notiz.
  4. Klick „Termin buchen" → POST /api/ca/:slug/book.
  5. Server erstellt CalendarBooking mit accessKey, schickt Bestätigungs-Mail.
  6. Gast landet auf /ca/bookings/:accessKey — Bestätigungsseite mit Termin-Details und ICS-Download (.ics-Datei direkt importierbar in Outlook/Google/Apple).

Storno / Reschedule durch Gast

Der Gast erreicht über den accessKey-Link (in der Mail) jederzeit seine Buchung und kann sie:

  • StornierenDELETE /api/ca/bookings/:accessKey (optional mit cancellationReason).
  • VerschiebenPOST /api/ca/bookings/:accessKey/reschedule (Slot-Picker erneut, Buchung wird umgesetzt).

Verknüpfungen zu anderen Modulen

  • Visitenkarten — verlinken zur CalendarPage des Mitarbeiters über „Termin buchen"-Button.
  • QR-Links — können zur CalendarPage zeigen (targetType = "url" mit der /ca/:slug-URL).
  • Arbeitszeit-Modelle — Quelle für coreTimeStartMinutes/-End zur Slot-Berechnung.
  • Abwesenheiten — sperren ganze Tage in der Slot-Engine.
  • Kalender — interne Termin-Sicht; CalendarPage-Buchungen erscheinen dort als reguläre Termine.
  • Mitarbeiter — Inhaber der Buchungsseite, 1:1.

API/Schnittstellen

Admin (auth)

MethodeEndpointZweckCASL
GET/api/calendar-pagesListe aller Buchungsseitenview Employee
GET/api/calendar-pages/:idDetailview Employee
PATCH/api/calendar-pages/:idSettings ändernupdate Employee
GET/api/calendar-pages/:id/meeting-typesMeeting-Types des Mitarbeitersview Employee
POST/api/calendar-pages/:id/meeting-typesMeeting-Type anlegenupdate Employee
PATCH/api/calendar-meeting-types/:idMeeting-Type ändernupdate Employee
DELETE/api/calendar-meeting-types/:idMeeting-Type entfernenupdate Employee

Public (keine Auth)

MethodeEndpointZweck
GET/api/ca/:slugResolved Page + Meeting-Types + Employee-Daten
GET/api/ca/:slug/photoProfilbild-Stream
GET/api/ca/:slug/availability?meetingTypeId=&from=&to=Verfügbare Slots
POST/api/ca/:slug/bookNeue Buchung anlegen + Mail
GET/api/ca/bookings/:accessKeyDetail für den Gast
DELETE/api/ca/bookings/:accessKeyStornieren
GET/api/ca/bookings/:accessKey/icsICS-Download
POST/api/ca/bookings/:accessKey/rescheduleTermin verschieben

Häufige Fehler und Lösungen

FehlerLösung
/ca/:slug zeigt 404active = false oder Page gelöscht. Im Editor reaktivieren oder neu anlegen.
Slot-Picker zeigt kaum freie TerminedayStartMinutes/dayEndMinutes zu eng gesetzt, oder das Arbeitszeit-Modell des Mitarbeiters hat enge coreTime-Fenster. Beides prüfen.
Gast bekommt keine Bestätigungs-MailMail-System des Mandanten prüfen (siehe Mail-Konten). attendeeEmail ggf. typo.
Abwesenheits-Tag erscheint trotzdem buchbarAbsence.status muss approved sein — requested blockiert nicht.
Doppel-Buchung im gleichen SlotSollte durch Buffer-/Overlap-Check unmöglich sein. Wenn doch: in /calendar-Modul beide Termine prüfen, manuell einen stornieren.
Slug-Eingabe disabledKorrekt — nach Anlage unveränderbar, damit gedruckte Links stabil bleiben.

Versionshinweise

  • 2026-05-21 (Welle 138): Initiale Veröffentlichung. Quelle: BE 80fb7ca0 (3 Models CalendarPage/-MeetingType/-Booking, 8 Admin- + 8 Public-Endpoints), FE 1c4bb924 (Admin-Editor + Public-Page mit Slot-Picker, Bestätigungs-Seite mit ICS-Download / Reschedule / Cancel). Slot-Berechnung gegen WorkTimeModelDay + Absence-Sperren, bufferMinutes/minNoticeMinutes/maxLeadDays als Steuerungs-Felder. Public-Page nutzt das in Welle 137 eingeführte PublicPageShell. Default-Seed: 3 Standard-Meeting-Types beim Anlegen einer neuen Page.