feat: enterprise notification & task management system

Phase N.1 — Data Model:
- Extend Notification model with category, priority, task fields (status, action,
  assignee, dueDate, completedAt/By), reminder fields (remindAt, recurrence,
  nextRemindAt), and targeting metadata (sourceId, senderId, channel)
- Add NotificationCategory, NotificationPriority, TaskStatus enums
- Add NotificationBroadcast model for group notifications
- Shared types with parseTaskAction()/buildTaskAction() helpers

Phase N.2 — API:
- Extend notification router: listTasks, taskCounts, updateTaskStatus,
  createReminder/update/delete/list, createBroadcast/listBroadcasts,
  createTask, assignTask, delete
- Broadcast targeting: resolve recipients by user/role/project/orgUnit/all
- Task-action registry: approve_vacation, reject_vacation, confirm_assignment
- Reminder scheduler: 60s poll interval, recurring support, catch-up on start
- SSE events: TASK_ASSIGNED, TASK_COMPLETED, TASK_STATUS_CHANGED,
  REMINDER_DUE, BROADCAST_SENT

Phase N.3 — AI Assistant:
- 7 new tools: list_tasks, get_task_detail, update_task_status,
  execute_task_action, create_reminder, create_task_for_user, send_broadcast
- execute_task_action dispatches to task-action registry with per-action
  permission checks, marks tasks as completed by AI

Phase N.4 — Frontend:
- Enhanced NotificationBell with task badge, tabs (All/Tasks/Reminders)
- TaskCard component with priority badges, due dates, action buttons
- ReminderModal for creating/editing personal reminders
- BroadcastModal for targeted group notifications (manager+)
- NotificationCenter full-page with 5 tabs and bulk actions
- TaskWidget dashboard widget showing open tasks
- Admin broadcast management page
- AppShell nav links for Notifications and Broadcasts
- SSE hook handlers for task/reminder events

Phase N.5 — Auto-Tasks:
- Vacation create → APPROVAL tasks for all managers
- Vacation approve/reject → mark approval tasks as DONE
- Demand create → TASK for managers to fill staffing needs

Co-Authored-By: claude-flow <ruv@ruv.net>
This commit is contained in:
2026-03-18 11:51:49 +01:00
parent 093e13b88f
commit d0f04f13f8
26 changed files with 3404 additions and 54 deletions
+86 -1
View File
@@ -185,6 +185,9 @@ model User {
vacationsApproved Vacation[] @relation("vacation_approved")
resource Resource?
notifications Notification[]
tasksAssigned Notification[] @relation("taskAssignee")
notificationsSent Notification[] @relation("notificationSender")
broadcasts NotificationBroadcast[] @relation("broadcastSender")
createdAt DateTime @default(now())
updatedAt DateTime @updatedAt
@@ -1286,6 +1289,29 @@ model VacationEntitlement {
@@map("vacation_entitlements")
}
// ─── Notification Enums ──────────────────────────────────────────────────────
enum NotificationCategory {
NOTIFICATION
REMINDER
TASK
APPROVAL
}
enum NotificationPriority {
LOW
NORMAL
HIGH
URGENT
}
enum TaskStatus {
OPEN
IN_PROGRESS
DONE
DISMISSED
}
// ─── Notification ─────────────────────────────────────────────────────────────
model Notification {
@@ -1299,12 +1325,71 @@ model Notification {
readAt DateTime?
createdAt DateTime @default(now())
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
// Extended fields
category NotificationCategory @default(NOTIFICATION)
priority NotificationPriority @default(NORMAL)
link String?
// Task fields
taskStatus TaskStatus?
taskAction String?
assigneeId String?
dueDate DateTime?
completedAt DateTime?
completedBy String?
// Reminder fields
remindAt DateTime?
recurrence String? // "daily" | "weekly" | "monthly" | null
nextRemindAt DateTime?
// Targeting
sourceId String?
senderId String?
channel String @default("in_app")
updatedAt DateTime @updatedAt
// Relations
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])
@@index([nextRemindAt])
@@index([assigneeId, taskStatus])
@@map("notifications")
}
// ─── Notification Broadcast ─────────────────────────────────────────────────
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")
targetType String // "user" | "role" | "project" | "orgUnit" | "all"
targetValue String?
scheduledAt DateTime?
sentAt DateTime?
recipientCount Int @default(0)
createdAt DateTime @default(now())
sender User @relation("broadcastSender", fields: [senderId], references: [id])
@@index([senderId])
@@index([scheduledAt, sentAt])
@@map("notification_broadcasts")
}
// ─── System Settings ──────────────────────────────────────────────────────────
model SystemSettings {