Kontakte ⊕ Personalbüro — Verschmelzung der Portal-Funktionen
Ausgangslage
Das alte Portal prilog-portal hat ein ausgereiftes "Personalbüro" (/users/accounts/office/) das den Schulalltag gut bedient:
- Fristensteuerung — abgelaufene Konten erkennen + verlängern/deaktivieren
- Bereinigungsvorschläge — Karteileichen mit Space-Zuordnungen
- CSV-Bulk-Import mit reichem Schema (username, fullName, birthDate, email, phone, street, postalCode, city, country, expiresAt, isPermanent, password, admin)
- Quick-Actions für Admins: +3 Mon / +6 Mon / +1 Jahr verlängern, deaktivieren
- Einladungs-Links per Email
- UserType-Manager (Lehrer/Schüler/Eltern/Sekretariat …)
- Passwort-Reset durch Admin
Im Web-Client haben wir Settings → Workspace → Mitglieder, das wesentlich ist aber die Personalbüro-Logik (Fristen, Bulk-Import, Quick-Actions) fehlt noch. Gleichzeitig haben wir die neue Kontakte-CRM-App.
Problem: Der CRM-Hub und das Mitglieder-Settings sind getrennt — der Sekretariats-Mitarbeiter wechselt zwischen zwei Orten. Der Portal-Office-View war ein Single-Pane-Of-Glass.
Ziel
Eine "Kontakte"-Welt im Web-Client vereint:
- CRM (externe Kontakte) — schon live
- Mitglieder-Verzeichnis — schon live (vereinheitlicht)
- Personalbüro-Funktionen — noch zu migrieren
- Activity-Verlauf auch für Mitglieder (aus dem CRM-Modell, polymorph)
Sodass das Sekretariat alles an einem Ort macht: anlegen, importieren, verlängern, suchen, kontaktieren.
Was migriert werden soll
Block 1 — Fristen + Karteileichen (MVP)
- Filter-Chip "Läuft bald ab" — Mitglieder mit
expiresAt < now+30d - Filter-Chip "Abgelaufen aktiv" —
expiresAt < now AND active - Filter-Chip "Ohne Space" — keine Membership
- Quick-Action im Detail-Pane (Admin only): Verlängern-Dropdown (+3 Mon / +6 Mon / +1 Jahr / 31.07. nächstes Jahr) — letzte Option ist deutscher Schul-Standard
- Bulk-Aktion "Abgelaufene deaktivieren" — Toolbar-Button im Office-Filter-Modus (Confirmation-Dialog)
- Ablauf-Pill in der Liste — kleine Badge "läuft in 14 Tagen" oder "abgelaufen" auf Mitgliedern
Block 2 — Mitglieder-Bulk-Import (mit Auto-Anlage)
- Bestehender CSV-Import-Wizard wird erweitert mit Toggle "Als Mitglied (mit Login) anlegen"
- Header-Auto-Detect erweitert um:
username, password, expiresAt, isPermanent, admin - UserType + Space-Pre-Selection (genau wie im Portal)
- Bei Erstellung:
UserDirectoryEntry+ Matrix-User über Synapse-Admin-API + erste Membership - Validierung: Username-Konflikt-Check, E-Mail-Eindeutigkeit
- Schul-typische CSV-Vorlagen (downloadbar): "Lehrkräfte", "Schüler einer Klasse", "Eltern" — als CSV mit Header und Beispielzeile
Block 3 — Einladungs-Link statt Passwort
- "Einladen"-Button im Create-Modal als Alternative zur Passwort-Eingabe
- Nutzer kriegt Email mit magischem Link → setzt selbst Passwort
- Im Portal vorhanden, im Web-Client kennen wir die Logik schon (siehe
prilog-web-client/src/features/invite/)
Block 4 — Polymorphe Activities (Member-Verlauf)
ContactActivitybekommt optionalenuserMatrixId-Verweis (alternativ zucontactId)- Verlauf-Tab funktioniert für Mitglieder genauso — manuelle Notizen, Telefonate, Treffen
- Auto-Aktivitäten für admin-relevante Events:
- "Konto angelegt von X"
- "Konto verlängert um 12 Monate"
- "Passwort zurückgesetzt"
- "Deaktiviert"
- Vorteil: das Sekretariat sieht wer wann was mit dem Konto gemacht hat — Audit + Workflow-Ablauf
Block 5 — Geburtstage-Widget
- Sidebar-Card "Geburtstage diese Woche" mit Foto + Tag → Klick öffnet Detail
- Spielt für Schul-Sekretariat eine zentrale Rolle ("Maria hat morgen!")
- Aus
birthDateund/oderbirthYearaller Mitglieder + externer Kontakte
Block 6 — Smart-Office-Modi
Neuer Filter-Chip-Strang neben Alle/Mitglieder/Externe:
[Alle] [Mitglieder] [Externe] [Geburtstage] [Läuft ab] [Karteileichen] [Ohne Space]Klick auf einen Office-Filter zeigt die Liste passend gefiltert + spezifische Bulk-Aktionen oben in der Toolbar (z.B. "Alle markierten deaktivieren").
Block 7 — Passwort-Reset + Admin-Toggle
Im Detail-Pane für Mitglieder (Admin only):
- Passwort zurücksetzen (Bestätigungsdialog mit kopierbarem temporärem Passwort)
- Admin-Status toggeln (Bestätigung)
- Konto deaktivieren / reaktivieren
- Konto löschen mit DSGVO-Hinweis
Diese sind heute in Settings → Mitglieder, gehören aber zum Kontakt-Detail (man arbeitet mit dem Menschen, nicht mit der Sektion).
Datenmodell-Erweiterungen
UserDirectoryEntry — bereits vorhanden:
expiresAt,isPermanent,admin,birthDate,birthYear,phone,street,postalCode,city,country,source
Keine Migration nötig — Daten existieren schon, müssen nur in Hub kommen.
ContactActivity — Erweiterung (Block 4):
model ContactActivity {
...
contactId String? // bestehend, jetzt optional
userMatrixId String? // NEU — alternativ
// Constraint: genau eines der beiden gefuellt
}Migration 0061 — additiv, mit Constraint via CHECK.
MemberInvitation — bereits da (UserInvitation Tabelle), nur Frontend-Anbindung fehlt.
UI-Vision
┌─ Toolbar ─────────────────────────────────────────────────────────┐
│ 🔍 Suche... [⬆ CSV-Import] [📥 vCard-Export] [+ Neu ▾] ⚙️ ⛶ │
├─ Quellen-Filter ──────────────────────────────────────────────────┤
│ [Alle 245] [Mitglieder 187] [Externe 58] │
├─ Office-Filter ───────────────────────────────────────────────────┤
│ [🎂 Geburtstage 3] [⏰ Läuft ab 12] [💀 Karteileichen 4] [📭 Ohne Space 2] │
├─ Tag-Pills ───────────────────────────────────────────────────────┤
│ [Eltern] [Lehrer] [Schüler] [Behörde] ... │
├───────────────────────────────────────────────────────────────────┤
│ ┌─ Liste ─────────────────────┬─ Detail ─────────────────────────┐ │
│ │ 🟢 Maria Meyer (Lehrer) │ 📷 Maria Meyer │ │
│ │ maria@schule.de 3d │ Lehrkraft Mathematik │ │
│ │ 🟡 Klaus Schmidt ⏰ 5T │ [📞] [📧] [✉️ Einladen] │ │
│ │ klaus@schule.de │ ───────────────────────────── │ │
│ │ 👤 Frau Müller · Eltern │ [Stammdaten | Verlauf | Verkn.] │ │
│ │ +49 171 2345678 │ │ │
│ │ │ Stammdaten: │ │
│ │ │ 📧 maria@schule.de │ │
│ │ │ 📞 +49 171 ... │ │
│ │ │ 🏠 Musterstr. 1, 80000 ... │ │
│ │ │ 🎂 14.05.1980 │ │
│ │ │ ⏰ Läuft ab am 31.07.2026 │ │
│ │ │ Admin-Aktionen: │ │
│ │ │ [+3 M] [+6 M] [+1 J] [Deakt.] │ │
│ │ │ [Passwort zurücksetzen] │ │
└───────────────────────────────┴───────────────────────────────────┘Sekretariats-Workflow (Beispiel):
- Klick "Geburtstage" → Liste der Personen mit Geburtstag in 7 Tagen
- Klick auf Maria → Detail mit Verlauf
- "Notiz" im Verlauf hinzufügen: "Geschenk besorgt"
- Klick "Läuft ab" → Klaus erscheint
- Klick "+1 Jahr" im Detail → Verlängert + Activity-Eintrag automatisch
- "CSV-Import" öffnen → Klassenliste 5b einlesen → 24 Schüler + 24 Eltern in einem Schwung
Phasenplan
| Phase | Inhalt | Aufwand |
|---|---|---|
| A | Office-Filter-Chips (Geburtstage / Läuft ab / Karteileichen / Ohne Space) — clientseitige Logik auf vorhandenen Daten | ~3h |
| B | Detail-Pane Admin-Aktionen für Mitglieder (Verlängern / Deaktivieren / Passwort-Reset / Admin-Toggle) | ~4h |
| C | Polymorphe ContactActivity (Migration 0061 + UI für Member-Verlauf) | ~3h |
| D | CSV-Bulk-Import erweitert: Toggle "Als Mitglied anlegen" + Username/Password/expiresAt/admin Felder + Schul-Vorlagen-Downloads | ~5h |
| E | Einladungs-Link statt Passwort + UI im Create-Modal | ~2h |
| F | Geburtstage-Widget (Sidebar + Mobile-First-Card) | ~2h |
| G | Auto-Activities für admin-relevante Events (Konto-Anlage, Verlängerung, Deaktivierung, Passwort-Reset) | ~2h |
MVP = A+B+G (~9h, ein Tag) — löst die wichtigsten Personalbüro-Workflows + macht Verlauf für Mitglieder sichtbar. Ausbau = +C+D+E+F (~12h zusätzlich) — kompletter Office-Modus, Bulk-Anlage, Einladungs-Flow, Geburtstage.
Status 2026-05-07 ALLE PHASEN LIVE (A–G):
- Migration 0061 (ContactActivity polymorph: contactId? + userMatrixId?)
- Office-Filter-Chips: 🎂 Geburtstage / ⏰ Läuft ab / 💀 Karteileichen / 📭 Ohne Space
- Status-Pills in der Liste (rot/gelb/blau)
- Admin-Aktionen im Detail-Pane: Verlängern (+3M/+6M/+1J/31.07.), Aktivieren/Deaktivieren, Admin-Toggle, Passwort-Reset (mit Modal)
- Verlauf-Tab für Mitglieder (manuelle Einträge + Auto-Activities)
- Auto-Activity-Logging bei Admin-Aktionen für Audit-Trail
- Phase D LIVE: CSV-Bulk-Import erweitert mit Toggle "Externe / Mitglieder", Schul-Vorlagen-Downloads (Lehrkräfte, Klasse, Eltern), member-spezifische Felder (username/password/expiresAt/admin), Backend
/workspace/users/bulk-import - Phase E LIVE: InviteMemberModal mit Token-Link-Erstellung (14d gültig), kopierbarer Link, Backend
/workspace/users/invite. "Neu"-Dropdown im Hub mit drei Optionen: Mitglied einladen / Externer Kontakt / CSV-Bulk-Import - Phase F LIVE: BirthdaysBox als Dashboard-Widget (Geburtstage 7d voraus, Mitglieder + Externe gemixt, Klick navigiert in den Kontakte-Hub)
"Spass mit Prilog" — Design-Prinzipien
- Ein Ort, eine Logik — Mitarbeiter denken nicht "wo ist das jetzt", sondern "ich klick im Kontakt"
- Aktion folgt Person — alle Operationen am Detail-Pane, nie verstreut über Settings
- Sichtbar bevor Klick — Ablauf-Pill in der Liste, nicht erst beim Öffnen
- Bulk ist ein Modus, kein Modal — Office-Filter aktiv → Liste hat Checkboxen + Sammel-Aktionen oben, raus aus dem Modus → wieder normal
- Keine Tabs für Modi — Filter-Chips, weil flexibler kombinierbar
- Konsistente Akzente — gelbe Pills für "Achtung" (Ablauf), rote für "kritisch" (abgelaufen+aktiv), grüne für Mitglied-Marker
Was wir NICHT migrieren
- Tenant-weite User-Type-Verwaltung — bleibt in Settings (Konfiguration ist nicht Tagesgeschäft)
- Server-Tier-Statistiken — bleibt in Settings → System
- Space-Templates-Editor — bleibt in Spaces-Hub (gehört zum Space, nicht zum Mitglied)
- Tenant-weite Health-Übersichten — bleibt im Admin-Portal (wenn überhaupt)
Offene Fragen
- Bulk-Anlage von Membern via CSV — sollen wir Matrix-Accounts wirklich automatisch erzeugen, oder nur "Einladungen" verschicken (Pull-Pattern)? — Empfehlung: Toggle im Wizard, Schule entscheidet.
- Office-Modi mit Selektions-Modus: brauchen wir Mehrfach-Selektion mit Checkboxen, oder reicht "alle Vorgeschlagene deaktivieren"-Bulk-Aktion? — Empfehlung: erst Bulk-Aktion, später Mehrfach-Selektion wenn gefordert.
- Geburtstag-Privacy: zeigen wir bei externer Person das Geburtsjahr per Default? — Empfehlung: nein, optional aktivierbar.
- Wann Einladung vs. Passwort: Standard-Pfad sollte einer sein. — Empfehlung: Einladungs-Link als Default (modern, sicher), Passwort als "Erweitert"-Toggle.