Visitenkarten (BusinessCards)
Zweck
BusinessCard ist die digitale Visitenkarte eines Mitarbeiters oder eines Kontakts. Pro Karte gibt es:
- einen eigenen Slug mit Public-URL
/c/{slug}— funktioniert ohne SpeamCore-Login - Override-Felder für Anzeige (Titel, Telefonnummern, Mail, Social-Links, Farbe)
- einen vCard-Export (3.0 oder 4.0), z. B. zum Direkt-Speichern im Smartphone
- ein anonymes Tracking für Page-Views und vCard-Downloads
- optional die Verknüpfung als Ziel eines QR-Links (Aufkleber → BC statt externer URL)
Pro Mitarbeiter oder Kontakt eine Visitenkarte (1:1 pro Mandant).
Voraussetzungen
Berechtigungen (CASL)
| Action | Subject | Wirkung |
|---|---|---|
view | FE_BusinessCard, BusinessCard | Liste + Editor aufrufbar |
create | BusinessCard | Neue Karte anlegen (Slug auto-gen) |
update | BusinessCard | Felder ändern (Slug ist nach Anlage unveränderbar) |
delete | BusinessCard | Soft-Delete — Events bleiben für Statistik erhalten, Public-Resolve liefert 404 |
view | Employee / Contact | Parent-Auswahl |
view | Mailbox | Mailbox-Override |
Datenmodell
BusinessCard
| Feld | Pflicht | Typ | Wirkung |
|---|---|---|---|
slug | auto bei Create | String 3–64, regex ^[a-z0-9](?:[a-z0-9-]*[a-z0-9])?$ | URL-Teil hinter /c/. Tenant-eindeutig. Unveränderbar nach Anlage. |
parentType | ja | ENUM (Employee | Contact) | Polymorphe Bindung an einen Mitarbeiter oder Kontakt. |
parentId | ja | UUID | Mitarbeiter-/Kontakt-ID. |
active | ja | Boolean, default true | false → /c/:slug liefert 404. |
displayTitle | nein | String 255 | Titel-Zeile auf der Karte. Fallback: EmployeeContract.jobTitle. |
displaySubtitle | nein | String 255 | Zweite Zeile (z. B. Niederlassung). |
phoneWork, phoneMobile | nein | String 64 | Override gegenüber Mitarbeiter-Attributen. |
emailOverride | nein | String 255 | Fallback: Mailbox.emailAddress oder Employee.email. |
website, linkedinUrl, xingUrl, instagramUrl | nein | String 255 | Social-Media-Links. Fallback: Social-Link-Policy. |
themeColor | nein | Hex-String | Akzent-Farbe der Karte. Fallback: UiConfig.primaryColor. |
mailboxId | nein | UUID → Mailbox | Optional: spezifisches Postfach für die Kontakt-Mail (statt Employee.email). |
showAbsenceStatus | nein | Boolean, default true | Zeigt einen dezenten Hinweis, wenn der Mitarbeiter aktuell in einer genehmigten Abwesenheit ist. |
showPoweredBy | nein | Boolean, default true | „powered by SpeamCore"-Footer ein-/ausblenden. |
BusinessCardEvent
Append-only Event-Log (kein Soft-Delete):
| Feld | Wirkung |
|---|---|
kind | ENUM impression (Page-View) | exchange (vCard-Download). |
occurredAt | Zeitstempel. |
vcardVersion | 3 oder 4 — nur bei kind = exchange. |
userAgent, uaFamily, osFamily, deviceType | Telemetrie aus dem Browser. |
countryCode | ISO-2 aus Cloudflare-Header. |
Schritt-für-Schritt-Anleitung
Visitenkarte anlegen
- Visitenkarten (
/business-cards) → + Hinzufügen. - SpeamCore generiert einen 8–10 Zeichen langen Slug (z. B.
kvj8m21h) — vor dem Speichern überschreibbar, danach fix. - Parent wählen: Mitarbeiter oder Kontakt.
- Override-Felder pflegen (Titel, Telefon, Mail, Socials, Farbe) — leer lassen, wenn Fallback aus Mitarbeiter-Daten reicht.
- Active = true setzen, sobald die Karte veröffentlicht werden soll.
Initial-Backfill (Welle 137): Beim ersten Aktivieren des Moduls auf einem bestehenden Mandanten erzeugt die Migration
20260520200001-backfill-employee-business-cards.jspro Mitarbeiter eine inactive Karte. Aktivierung geschieht manuell oder per Bulk-Update.
Style + Mandanten-Branding
Pro Karte können Sie:
- die
themeColorals Akzent setzen (z. B. die Firmenfarbe einer Branch), - ein eigenes
Documentals Hintergrund hochladen (über die Customization), showPoweredBydeaktivieren (für reine White-Label-Cards).
Falls die Felder leer bleiben, greifen die mandantenweiten Default-Werte aus UiConfig.
vCard-Export
Die Public-Seite bietet zwei Format-Buttons:
- vCard 3.0 — kompatibel mit älteren iOS-/Android-Versionen
- vCard 4.0 — moderner Standard
Beide laden eine .vcf-Datei herunter, die der Empfänger direkt in sein Adressbuch importieren kann. Jeder Download wird als BusinessCardEvent mit kind = exchange getrackt.
Statistik-Tab
/business-cards/:id/stats zeigt analog zur QR-Link-Stats-Page:
| KPI | Berechnung |
|---|---|
| Impressions | COUNT(kind = 'impression') — Page-Views |
| vCard-Downloads | COUNT(kind = 'exchange') — echte „Adressbuch-Aufnahme" |
| By Country / UA / Device | analog zum QR-Link-Tracking |
| Tagesverlauf | Impressions + Downloads pro Tag |
QR-Link → Visitenkarte (Welle 137)
Ein QR-Link kann statt einer externen URL eine Visitenkarte als Ziel haben:
QrLink {
targetType: "businessCard",
targetBusinessCardId: <UUID>,
targetUrl: null
}
Beim Scan resolvet SpeamCore die BC-Slug, leitet auf /c/:slug weiter und forciert intern den anonymen Tracking-Modus (kein Bestätigungs-Screen, keine Confirmed-Stufe). Die Impressions werden im BusinessCardEvent geloggt, nicht im QrLinkScan — die Statistik liegt also bei der Visitenkarte, nicht beim QR-Link.
Verhalten:
| BC-Status | QR-Scan-Ergebnis |
|---|---|
active = true | Redirect auf /c/:slug, Event geloggt |
active = false oder gelöscht | 404 |
Anwendungsfall: Druck-Aufkleber an Servicewagen oder Anlagen-Kästen → QR scannt → direkt zur Visitenkarte des zuständigen Außendienstlers (statt zu einer Marketing-URL).
Resolver-Hierarchie
Die Public-Sicht /c/:slug merged Daten aus zehn Quellen, jeweils mit Fallback:
displayTitle → BusinessCard.displayTitle
→ EmployeeContract.jobTitle
→ leer
email → BusinessCard.emailOverride
→ Mailbox.emailAddress
→ Employee.email
socials → BusinessCard.linkedinUrl/xingUrl/instagramUrl
→ SocialLink (Mitarbeiter)
→ SocialLink (Tenant-Fallback aus SocialLinkPolicy)
themeColor → BusinessCard.themeColor
→ UiConfig.primaryColor
absenceStatus → aktuelle genehmigte Absence (nur wenn showAbsenceStatus=true)
→ kein Hinweis
calendarPage → CalendarPage des Mitarbeiters (nur Employee, Welle 138)
→ kein Buchungs-Button
photo → Employee.profileImageDocument
→ kein Foto
Public-Page-Shell (Welle 137)
Visitenkarten und QR-Links nutzen seit Welle 137 ein gemeinsames PublicPageShell-Layout:
- Mandanten-Logo + Branding-Farben aus UiConfig
- Footer links: Firmen-Stammdaten (Name, Adresse, Telefon)
- Footer rechts: Legal-Links (Impressum, Datenschutz, AGB) aus
LegalDocument - Optional Background-Image und Overlay
Damit sehen alle Public-Pages (Visitenkarten, QR-Code-Landing, später auch Buchungsseite und Zertifikat-Verifizierung) einheitlich aus.
Tracking-Modell
Analog zum QR-Link-Tracking:
impression— anonymer Page-View. Keine IP-Speicherung, neue UUID pro Aufruf.exchange— echte Conversion (vCard heruntergeladen). Wird mit Browser-/Device-/Geo-Metadaten, aber ohne Klartext-IP geloggt.
Append-only — Soft-Delete einer Karte entfernt die Events nicht, damit historische Auswertungen erhalten bleiben.
API/Schnittstellen
Authentifiziert
| Methode | Endpoint | Zweck | CASL |
|---|---|---|---|
GET | /api/business-cards | Liste, paginiert + filterbar | view BusinessCard |
POST | /api/business-cards | Anlegen (Slug auto-gen) | create BusinessCard |
GET | /api/business-cards/:id | Detail (Roh-Felder) | view BusinessCard |
GET | /api/business-cards/:id/resolved | Merged-Sicht — auch wenn active=false | view BusinessCard |
GET | /api/business-cards/:id/stats?from=&to= | Event-Statistik | view BusinessCard |
PATCH | /api/business-cards/:id | Update — Slug bleibt fix | update BusinessCard |
DELETE | /api/business-cards/:id | Soft-Delete | delete BusinessCard |
Public (keine Auth)
| Methode | Endpoint | Zweck |
|---|---|---|
GET | /api/c/:slug | Resolved-Payload (nur active = true) |
GET | /api/c/:slug/vcard?v=3|4 | vCard-Download (.vcf) |
POST | /api/c/:slug/impression | Anonyme Page-View-Erfassung |
GET | /api/c/:slug/photo | Profilbild-Stream (auch bei active=false; Slug ist das Token) |
Social-Media-Settings auf Tenant-Ebene (Welle 142)
In den Mandanten-Einstellungen unter Settings → Social Media (/settings/social-media) lassen sich die für Visitenkarten erlaubten Plattformen zentral steuern:
- Whitelist: Welche der 10 unterstützten Plattformen darf ein Mitarbeiter auf seiner BC pflegen — LinkedIn, Xing, Instagram, Facebook, X, GitHub, YouTube, TikTok, Threads, Bluesky.
- Unternehmens-Defaults pro Plattform mit drei Modi:
- Force (rot) — der Unternehmens-Link wird immer angezeigt, Mitarbeiter-Eintrag wird ignoriert
- Fallback (blau) — wird nur gerendert, wenn der Mitarbeiter selbst keinen Link gepflegt hat
- Hidden — Plattform ist deaktiviert, taucht auf keiner BC auf
Damit lässt sich ein einheitliches Corporate-Profil (z. B. „LinkedIn → immer Firmen-Page, Instagram → erlaubt aber kein Default") mandantenweit durchsetzen.
Verknüpfungen zu anderen Modulen
- QR-Links — können eine BusinessCard als Ziel haben (
targetType = "businessCard"). - QR-Link-Tracking — Konzept-Hintergrund zum Event-Modell.
- Mitarbeiter, Kontakte — Parent-Quellen.
- Mail-Konten —
mailboxIdfür E-Mail-Override. - Customization / Allgemein — Mandanten-Farben + Logo + Social-Link-Policy.
Häufige Fehler und Lösungen
| Fehler | Lösung |
|---|---|
/c/:slug zeigt 404 | active = false, Karte gelöscht oder Slug-Tippfehler. Im Editor reaktivieren oder neuen Slug nutzen. |
| Telefonnummer fehlt obwohl im Mitarbeiter gepflegt | Override-Feld phoneWork/phoneMobile ist leer und das Mitarbeiter-Attribut phone ist nicht auf „public" gesetzt. Im Mitarbeiter-Detail prüfen. |
| Profilbild fehlt | Employee.profileImageDocument ist nicht gepflegt — Document im Dokumenten-Modul hochladen und als Profilbild markieren. |
| Slug-Eingabe ist disabled | Slug ist nach dem Speichern unveränderbar (Datenschutz: gedruckte Aufkleber sollen stabil bleiben). Neue Karte anlegen, alte deaktivieren. |
| QR-Code zeigt 404, obwohl Visitenkarte existiert | BusinessCard.active = false oder Soft-Delete — QR-Resolver lehnt inaktive BCs ab. |
| vCard sieht im iPhone-Adressbuch falsch aus | ?v=4 (vCard 4.0) hat strengere Felder. ?v=3 (3.0) ist toleranter bei Sonderzeichen. |
| Mehrere Visitenkarten pro Mitarbeiter | Nicht erlaubt — 1:1 pro parentType + parentId (Index uq_business_cards_parent_deletedAt). |
Versionshinweise
- 2026-05-21 (Welle 137): Initiale Veröffentlichung. Quelle: BE-Commit
179f8adc(3 neue Models, 7 Auth-Endpoints, 4 Public-Endpoints, 6 Migrationen) + FE-Commit6430804b(BusinessCardEditor,BusinessCardStatsPanel,BusinessCardPublicPage,PublicPageShellals Shared Layout). Migration20260520200001-backfill-employee-business-cards.jslegt pro Mitarbeiter eine inaktive Karte an. QR-Link um Ziel-TypbusinessCarderweitert — Scans auf eine BC-Card werden anonym getrackt (kein Bestätigungs-Screen) und imBusinessCardEventgeloggt. - 2026-05-21 (Welle 142): Neue Social-Media-Settings-Seite (
/settings/social-media) auf Tenant-Ebene — Whitelist der 10 Plattformen plus Force/Fallback/Hidden-Modi pro Plattform. Bestimmt, welche Links auf der BC erscheinen.