- Add DALL-E cover art generation for projects (Azure OpenAI + standard OpenAI)
- CoverArtSection component with generate/upload/remove/focus-point controls
- Client-side image compression (10MB input → WebP/JPEG, max 1920px)
- DALL-E settings in admin panel (deployment, endpoint, API key)
- MCP assistant tools for cover art (generate_project_cover, remove_project_cover)
- Rename "Planarchy" → "plANARCHY" across all UI-facing text (13 files)
- Fix hardcoded canEdit={true} on project detail page — now checks user role
- Computation graph visualization (2D/3D) for calculation rules
- OG image and OpenGraph metadata
Co-Authored-By: claude-flow <ruv@ruv.net>
18 KiB
Enterprise Notification & Task Management System
Anforderungsanalyse
Was wird gebaut?
Ein mehrstufiges Notification- und Task-Management-System, das die bestehende Notification-Infrastruktur (Prisma-Model, Bell-Icon, SSE, SMTP) zu einem vollwertigen Enterprise-System ausbaut. Vier Kernfähigkeiten:
- Personal Reminders — User legen eigene Erinnerungen an (Datum/Zeit, optionale Wiederholung, verknüpft mit Entity)
- Targeted Notifications — Admins/Manager senden Notifications an User, Rollen, Projektbeteiligte, OrgUnits
- Task Management — Actionable Tasks mit Status-Tracking, Dashboard-Widget, Entity-Verknüpfung
- AI Assistant Integration — Assistent liest offene Tasks, führt sie aus (Urlaub genehmigen, Allokation erstellen, etc.)
Bestehende Infrastruktur (wiederverwendbar)
| Komponente | Status | Datei |
|---|---|---|
Notification Prisma-Model |
Vorhanden (einfach) | packages/db/prisma/schema.prisma:1291 |
| Notification tRPC-Router | list, unreadCount, markRead, create | packages/api/src/router/notification.ts |
| NotificationBell + Drawer | Bell-Icon mit Badge, Dropdown-Panel | apps/web/src/components/notifications/NotificationBell.tsx |
| SSE EventBus (Redis Pub/Sub) | NOTIFICATION_CREATED Event |
packages/api/src/sse/event-bus.ts |
| SMTP Email | sendEmail() + SystemSettings |
packages/api/src/lib/email.ts |
| AI Assistant Tools | list_notifications, mark_notification_read |
packages/api/src/router/assistant-tools.ts |
| Dashboard Widget-Registry | 8 Widgets, Pattern etabliert | apps/web/src/components/dashboard/widgets/ |
Betroffene Pakete
- packages/db — Schema-Erweiterung (Notification -> Task-Felder, neues Broadcast-Model)
- packages/shared — Enums, Typen, Zod-Schemas
- packages/api — Router-Erweiterung (notification.ts, assistant-tools.ts), Targeting-Logik, Scheduler
- apps/web — UI (Task-Widget, Reminder-UI, Notification-Center, Admin-Panel)
Datenmodell-Design
Erweiterung des bestehenden Notification-Models
Das bestehende Model wird um Task-/Reminder-/Targeting-Felder erweitert. Kein neues Model nötig — ein einheitliches System für Notifications + Tasks + Reminders.
model Notification {
id String @id @default(cuid())
userId String
// -- Typ & Kategorie --
category NotificationCategory @default(NOTIFICATION) // NEU
type String // z.B. "VACATION_REQUESTED", "TASK_ASSIGNED", "REMINDER"
priority NotificationPriority @default(NORMAL) // NEU
// -- Inhalt --
title String
body String?
entityId String?
entityType String?
link String? // NEU: Deep-Link zur relevanten Seite
// -- Task-Felder (nur fuer category TASK / APPROVAL) --
taskStatus TaskStatus? // NEU: OPEN / IN_PROGRESS / DONE / DISMISSED
taskAction String? // NEU: maschinenlesbare Aktion z.B. "approve_vacation:clxyz123"
assigneeId String? // NEU: wem der Task zugewiesen ist
dueDate DateTime? // NEU: Faelligkeitsdatum
completedAt DateTime? // NEU: Zeitpunkt der Erledigung
completedBy String? // NEU: wer hat erledigt (User-ID, oder "ai-assistant")
// -- Reminder-Felder --
remindAt DateTime? // NEU: wann soll erinnert werden
recurrence String? // NEU: "daily" | "weekly" | "monthly" | null
nextRemindAt DateTime? // NEU: naechster Erinnerungszeitpunkt (berechnet)
// -- Targeting-Metadaten (fuer Bulk-Sends) --
sourceId String? // NEU: Referenz auf die urspruengliche Broadcast-Nachricht
senderId String? // NEU: wer hat die Notification erstellt (User-ID)
channel String @default("in_app") // NEU: "in_app" | "email" | "both"
// -- Timestamps --
readAt DateTime?
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt // NEU
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
assignee User? @relation("taskAssignee", fields: [assigneeId], references: [id])
sender User? @relation("notificationSender", fields: [senderId], references: [id])
@@index([userId, readAt])
@@index([userId, category, taskStatus]) // NEU: Task-Queries
@@index([nextRemindAt]) // NEU: Reminder-Scheduler
@@index([assigneeId, taskStatus]) // NEU: Assigned-Tasks
@@map("notifications")
}
enum NotificationCategory {
NOTIFICATION // System/Admin-Benachrichtigung (read-only)
REMINDER // Persoenliche Erinnerung (self-created)
TASK // Actionable Task mit Status-Tracking
APPROVAL // Genehmigungsworkflow (approve/reject)
}
enum NotificationPriority {
LOW
NORMAL
HIGH
URGENT
}
enum TaskStatus {
OPEN
IN_PROGRESS
DONE
DISMISSED
}
Broadcast-Model (fuer Gruppen-Notifications)
model NotificationBroadcast {
id String @id @default(cuid())
senderId String
title String
body String?
link String?
category NotificationCategory @default(NOTIFICATION)
priority NotificationPriority @default(NORMAL)
channel String @default("in_app")
// -- Targeting --
targetType String // "user" | "role" | "project" | "orgUnit" | "all"
targetValue String? // Role-Name, Project-ID, OrgUnit-ID, oder null fuer "all"
// -- Scheduling --
scheduledAt DateTime? // null = sofort
sentAt DateTime?
recipientCount Int @default(0)
createdAt DateTime @default(now())
sender User @relation(fields: [senderId], references: [id])
@@index([senderId])
@@index([scheduledAt, sentAt])
@@map("notification_broadcasts")
}
Task-Action Registry (Enterprise-Pattern)
Maschinenlesbare Aktionen ermoeglichen dem AI-Assistenten, Tasks direkt zu erledigen:
// packages/api/src/lib/task-actions.ts
const TASK_ACTION_REGISTRY: Record<string, TaskActionHandler> = {
"approve_vacation": { permission: "manageVacations", execute: ... },
"reject_vacation": { permission: "manageVacations", execute: ... },
"fill_demand": { permission: "manageAllocations", execute: ... },
"confirm_allocation":{ permission: "manageAllocations", execute: ... },
"review_budget": { permission: "manageProjects", execute: ... },
};
Format: "action_name:entity_id" — einfach parsbar, erweiterbar.
Betroffene Pakete & Dateien
| Paket | Dateien | Art |
|---|---|---|
packages/db |
prisma/schema.prisma |
edit — Notification erweitern, Enums, Broadcast-Model |
packages/shared |
src/types/notification.ts |
create — Typen, Enums, Zod-Schemas |
packages/shared |
src/types/enums.ts |
edit — re-exportieren |
packages/api |
src/router/notification.ts |
edit — Task-CRUD, Reminder-CRUD, Broadcast, Targeting |
packages/api |
src/router/index.ts |
edit — ggf. neuen Router registrieren |
packages/api |
src/router/assistant-tools.ts |
edit — neue Tools: list_tasks, execute_task_action, etc. |
packages/api |
src/router/assistant.ts |
edit — TOOL_PERMISSION_MAP + System-Prompt |
packages/api |
src/sse/event-bus.ts |
edit — neue Event-Types |
packages/api |
src/lib/email.ts |
edit — Notification-Email-Templates |
packages/api |
src/lib/notification-targeting.ts |
create — Recipient-Aufloesung |
packages/api |
src/lib/task-actions.ts |
create — Action-Registry |
packages/api |
src/lib/reminder-scheduler.ts |
create — Reminder-Dispatcher |
apps/web |
src/components/notifications/NotificationBell.tsx |
edit — Tabs, Task-Badge |
apps/web |
src/components/notifications/NotificationCenter.tsx |
create — Full-Page |
apps/web |
src/components/notifications/ReminderModal.tsx |
create |
apps/web |
src/components/notifications/BroadcastModal.tsx |
create |
apps/web |
src/components/notifications/TaskCard.tsx |
create |
apps/web |
src/components/dashboard/widgets/TaskWidget.tsx |
create |
apps/web |
src/app/(app)/notifications/page.tsx |
create |
apps/web |
src/app/(app)/admin/notifications/page.tsx |
create |
apps/web |
src/components/layout/AppShell.tsx |
edit — Nav-Links |
apps/web |
src/hooks/useTimelineSSE.ts |
edit — Task-Events |
Task-Liste (atomare Schritte)
Phase N.1 — Datenmodell & Shared Types
-
Task 1: Shared-Typen erstellen ->
packages/shared/src/types/notification.ts- NotificationCategory, NotificationPriority, TaskStatus Enums
- CreateReminderSchema, CreateBroadcastSchema, UpdateTaskStatusSchema (Zod)
- TaskAction Interface
-
Task 2: Prisma-Schema erweitern ->
packages/db/prisma/schema.prisma- Notification-Model: category, priority, taskStatus, taskAction, assigneeId, dueDate, completedAt, completedBy, remindAt, recurrence, nextRemindAt, sourceId, senderId, channel, link, updatedAt
- Enums: NotificationCategory, NotificationPriority, TaskStatus
- Model: NotificationBroadcast
- User-Relations: taskAssignee, notificationSender, broadcasts
- Indexes: [userId, category, taskStatus], [nextRemindAt], [assigneeId, taskStatus]
-
Task 3:
pnpm db:push+ Dev-Server neu starten
Phase N.2 — API: Router + Targeting + Scheduler
-
Task 4: SSE Event-Types erweitern ->
packages/api/src/sse/event-bus.ts- TASK_ASSIGNED, TASK_COMPLETED, TASK_STATUS_CHANGED, REMINDER_DUE, BROADCAST_SENT
- Emit-Helper: emitTaskAssigned(), emitTaskCompleted(), emitReminderDue()
-
Task 5: Notification-Router erweitern ->
packages/api/src/router/notification.ts- list: Filter nach category, taskStatus, priority
- listTasks (protectedProcedure): offene Tasks + zugewiesene Tasks
- taskCounts (protectedProcedure): Counts nach Status
- updateTaskStatus (protectedProcedure): OPEN->IN_PROGRESS->DONE/DISMISSED
- createReminder (protectedProcedure): eigene Erinnerung anlegen
- updateReminder / deleteReminder (protectedProcedure)
- createBroadcast (managerProcedure): Targeted Notification an Gruppe
- listBroadcasts (managerProcedure)
- createTask (managerProcedure): Task fuer User/Gruppe
- assignTask (managerProcedure): Task zuweisen
- delete (protectedProcedure): eigene Notifications loeschen
-
Task 6: Broadcast-Targeting ->
packages/api/src/lib/notification-targeting.ts(create)- resolveRecipients(targetType, targetValue, db): User-IDs aufloesen
- "user" -> einzelner User
- "role" -> alle User mit SystemRole
- "project" -> Ressourcen mit aktiver Allokation -> verknuepfte User
- "orgUnit" -> Ressourcen in OrgUnit -> verknuepfte User
- "all" -> alle aktiven User
- resolveRecipients(targetType, targetValue, db): User-IDs aufloesen
-
Task 7: Email-Templates ->
packages/api/src/lib/email.ts(edit)- sendNotificationEmail(userId, notification): HTML mit Title, Body, Deep-Link
- sendTaskEmail(userId, task): Template mit Task-Details + Action-Link
-
Task 8: Task-Action-Registry ->
packages/api/src/lib/task-actions.ts(create)- Registry-Pattern: action_name -> { permission, execute(entityId, ctx) }
- Initiale Actions: approve_vacation, reject_vacation, fill_demand, confirm_allocation
-
Task 9: Reminder-Scheduler ->
packages/api/src/lib/reminder-scheduler.ts(create)- Intervall (60s): WHERE nextRemindAt <= NOW()
- Fuer jeden faelligen Reminder: In-App Notification + optional Email
- nextRemindAt neu berechnen oder null setzen
- Catch-up bei Start (ueberfaellige sofort ausloesen)
Phase N.3 — AI Assistant Integration
-
Task 10: Neue Tool-Definitionen ->
packages/api/src/router/assistant-tools.ts- list_tasks: offene Tasks/Approvals mit Filter
- get_task_detail: Details inkl. verknuepfter Entity
- update_task_status: Status aendern
- execute_task_action: maschinenlesbare Aktion ausfuehren
- create_reminder: Erinnerung anlegen
- create_task_for_user: Task fuer anderen User (Manager-only)
- send_broadcast: Notification an Gruppe (Manager-only)
-
Task 11: Tool-Executors implementieren ->
packages/api/src/router/assistant-tools.ts- execute_task_action: parst taskAction-String, dispatcht an Action-Registry
- Permission-Check pro Action (nicht pauschal)
-
Task 12: Permission-Map + Prompt ->
packages/api/src/router/assistant.ts- TOOL_PERMISSION_MAP erweitern
- System-Prompt: Tasks/Reminders als Faehigkeit beschreiben
Phase N.4 — Frontend
-
Task 13: NotificationBell erweitern ->
apps/web/src/components/notifications/NotificationBell.tsx- Zweiter Badge: Task-Count (orange) neben Notification-Count (rot)
- Tabs: "Alle" | "Tasks" | "Erinnerungen"
- Task-Items mit Quick-Actions (Done/Dismiss)
- Link zu "/notifications"
-
Task 14: TaskCard-Komponente ->
apps/web/src/components/notifications/TaskCard.tsx(create)- Titel, Body, Due-Date, Priority-Badge, Entity-Link
- Aktionen: "Start" / "Done" / "Dismiss"
- Approval-Variante: "Approve" / "Reject"
- Priority-farbcodiert (URGENT=rot, HIGH=orange, NORMAL=blau, LOW=grau)
-
Task 15: ReminderModal ->
apps/web/src/components/notifications/ReminderModal.tsx(create)- Titel, Body, Datum/Uhrzeit, Wiederholung (keine/taeglich/woechentlich/monatlich)
- Optional: Entity-Verknuepfung (Projekt/Ressource Dropdown)
-
Task 16: BroadcastModal ->
apps/web/src/components/notifications/BroadcastModal.tsx(create)- Manager/Admin-only
- Targeting: Dropdown (Alle/Rolle/Projekt/OrgUnit) + Wert-Auswahl
- Inhalt: Titel, Body, Priority, Kategorie
- Kanal: In-App / Email / Beides
- Scheduling: Sofort / Zeitgesteuert
- Vorschau: "Wird an X Empfaenger gesendet"
-
Task 17: NotificationCenter ->
apps/web/src/app/(app)/notifications/page.tsx(create)- Tabs: Alle | Notifications | Tasks | Reminders | Approvals
- Filter: Status, Priority, Zeitraum
- Bulk: "Alle lesen", "Alle erledigt"
- "Neue Erinnerung" Button
-
Task 18: TaskWidget ->
apps/web/src/components/dashboard/widgets/TaskWidget.tsx(create)- Kompakte Liste offener Tasks (max 5-7)
- Sortiert: Priority -> Due-Date
- Quick-Actions: Done/Dismiss
- Footer: "X offene Tasks — Alle anzeigen"
- In Widget-Registry eintragen
-
Task 19: Admin Broadcast-Seite ->
apps/web/src/app/(app)/admin/notifications/page.tsx(create)- Liste gesendeter Broadcasts
- "Neue Benachrichtigung senden" Button
- Statistiken: gesendet/gelesen pro Broadcast
-
Task 20: AppShell Navigation ->
apps/web/src/components/layout/AppShell.tsx(edit)- "Notifications" fuer alle Rollen
- "Broadcast" unter Admin (ADMIN/MANAGER)
-
Task 21: SSE-Hook ->
apps/web/src/hooks/useTimelineSSE.ts(edit)- Auf TASK_ASSIGNED, TASK_COMPLETED, REMINDER_DUE reagieren
- React-Query invalidieren: notification.listTasks, notification.taskCounts
Phase N.5 — Auto-Tasks & Audit
-
Task 22: Automatische Task-Erzeugung bei Business-Events
- vacation.create -> Task "Urlaubsantrag genehmigen" an Manager (APPROVAL)
- Ueberallokation -> Task "Ueberallokation aufloesen" an Manager
- Projekt-Deadline < 30 Tage + offene Demands -> Task "Demands besetzen"
- demand.create -> Task "Demand besetzen" an Manager
-
Task 23: Audit-Trail ->
packages/api/src/lib/audit.ts(create)- logTaskAction(taskId, userId, action, details)
- completedBy: "ai-assistant" fuer AI-erledigte Tasks
Abhaengigkeiten
Task 1 (Shared Types) ---+
Task 2 (Schema) ---------+--> Task 3 (db:push)
|
Task 3 --> Task 4 (SSE) |
Task 3 --> Task 5 (Router)|
Task 3 --> Task 6 (Targeting)
Task 3 --> Task 7 (Email) |
Task 3 --> Task 8 (Actions)|
|
Task 5 + 6 --> Task 9 (Scheduler)
Task 5 + 8 --> Task 10-12 (AI)
|
Task 5 --> Task 13-21 (Frontend, parallel moeglich)
Task 5 --> Task 22 (Auto-Tasks)
Parallel:
- Task 4 + 5 + 6 + 7 + 8 (verschiedene Dateien)
- Task 13-21 (verschiedene Dateien, 13+14 vor 17+18 empfohlen)
Sequentiell:
- Task 1 -> 2 -> 3 (Schema)
- Task 5 -> 10 -> 11 (Router -> Tools -> Executors)
Akzeptanzkriterien
pnpm db:pushohne Fehlerpnpm --filter @planarchy/api exec tsc --noEmit— 0 Errorspnpm --filter @planarchy/web exec tsc --noEmit— 0 Errorspnpm test:unit— alle Tests gruen- User kann eigene Erinnerung anlegen (Datum, Wiederholung, Entity)
- Admin/Manager kann Broadcast an Rolle/Projekt/OrgUnit senden
- Broadcast erzeugt individuelle Notifications pro Empfaenger
- Tasks im Dashboard-Widget, sortiert nach Priority + Due-Date
- Task-Status aenderbar ueber UI (Open -> In Progress -> Done/Dismissed)
- AI-Assistent kann list_tasks aufrufen und offene Tasks anzeigen
- AI-Assistent kann execute_task_action ausfuehren (z.B. Urlaub genehmigen)
- Erledigte Tasks zeigen completedBy (User oder "AI-Assistent")
- Email-Versand bei channel "email" oder "both"
- SSE-Events invalidieren React-Query-Caches
- Reminder-Scheduler erzeugt puenktlich Notifications
- RBAC: User sehen nur eigene; Manager zugewiesene; Admin Broadcasts
Risiken & offene Fragen
Risiken
- Reminder-Scheduler Zuverlaessigkeit: Node.js-setInterval kann bei Restart verpassen. Mitigation: Catch-up bei Start (alle ueberfaelligen sofort ausloesen).
- Broadcast-Skalierung: "An alle" mit 500 Usern = 500 Rows. Mitigation: Batch-Insert (createMany).
- Task-Action-Sicherheit: Permissions pro Action pruefen, nicht pauschal. Mitigation: Action-Registry mit Permission pro Handler.
- Schema-Migration: Neue Felder nullable oder mit Default -> bestehende Notifications funktionieren weiter.
Offene Fragen
- Scheduler: setInterval im SSE-Handler oder separater Worker/Cron? Empfehlung: setInterval (reicht fuer <1000 User)
- Task-Delegation: User duerfen Tasks an andere weiterdelegieren? Empfehlung: Ja (Manager-only)
- Retention: Wie lange alte Notifications aufbewahren? Empfehlung: 90 Tage Auto-Cleanup
- Recurring Tasks: Tasks wiederkehrend wie Reminders? Empfehlung: Phase 2
- Approval-Chains: Mehrstufige Genehmigung? Empfehlung: Phase 2, erstmal einstufig