feat: extend notification system with batch sender, bulk dialog, and logging

Add NotificationLog schema extensions (nullable userId, email, roundId,
projectId, batchId fields), batch notification sender service, and bulk
notification dialog UI. Include utility scripts for debugging and seeding.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 13:29:06 +01:00
parent 8f2f054c57
commit f24bea3df2
15 changed files with 1316 additions and 16 deletions

View File

@@ -0,0 +1,31 @@
-- DropForeignKey
ALTER TABLE "NotificationLog" DROP CONSTRAINT "NotificationLog_userId_fkey";
-- AlterTable
ALTER TABLE "NotificationLog" ADD COLUMN "batchId" TEXT,
ADD COLUMN "email" TEXT,
ADD COLUMN "projectId" TEXT,
ADD COLUMN "roundId" TEXT,
ALTER COLUMN "userId" DROP NOT NULL,
ALTER COLUMN "channel" SET DEFAULT 'EMAIL';
-- CreateIndex
CREATE INDEX "NotificationLog_roundId_type_idx" ON "NotificationLog"("roundId", "type");
-- CreateIndex
CREATE INDEX "NotificationLog_projectId_idx" ON "NotificationLog"("projectId");
-- CreateIndex
CREATE INDEX "NotificationLog_batchId_idx" ON "NotificationLog"("batchId");
-- CreateIndex
CREATE INDEX "NotificationLog_email_idx" ON "NotificationLog"("email");
-- AddForeignKey
ALTER TABLE "NotificationLog" ADD CONSTRAINT "NotificationLog_userId_fkey" FOREIGN KEY ("userId") REFERENCES "User"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "NotificationLog" ADD CONSTRAINT "NotificationLog_roundId_fkey" FOREIGN KEY ("roundId") REFERENCES "Round"("id") ON DELETE SET NULL ON UPDATE CASCADE;
-- AddForeignKey
ALTER TABLE "NotificationLog" ADD CONSTRAINT "NotificationLog_projectId_fkey" FOREIGN KEY ("projectId") REFERENCES "Project"("id") ON DELETE SET NULL ON UPDATE CASCADE;

View File

@@ -638,6 +638,7 @@ model Project {
deliberationVotes DeliberationVote[]
deliberationResults DeliberationResult[]
submissionPromotions SubmissionPromotionEvent[]
notificationLogs NotificationLog[]
@@index([programId])
@@index([status])
@@ -931,22 +932,34 @@ model AIUsageLog {
model NotificationLog {
id String @id @default(cuid())
userId String
channel NotificationChannel
userId String?
channel NotificationChannel @default(EMAIL)
provider String? // META, TWILIO, SMTP
type String // MAGIC_LINK, REMINDER, ANNOUNCEMENT, JURY_INVITATION
type String // MAGIC_LINK, REMINDER, ANNOUNCEMENT, JURY_INVITATION, ADVANCEMENT_NOTIFICATION, etc.
status String // PENDING, SENT, DELIVERED, FAILED
externalId String? // Message ID from provider
errorMsg String? @db.Text
// Bulk notification tracking
email String? // Recipient email address
roundId String?
projectId String?
batchId String? // Groups emails from same send operation
createdAt DateTime @default(now())
// Relations
user User @relation(fields: [userId], references: [id], onDelete: Cascade)
user User? @relation(fields: [userId], references: [id], onDelete: SetNull)
round Round? @relation(fields: [roundId], references: [id], onDelete: SetNull)
project Project? @relation(fields: [projectId], references: [id], onDelete: SetNull)
@@index([userId])
@@index([status])
@@index([createdAt])
@@index([roundId, type])
@@index([projectId])
@@index([batchId])
@@index([email])
}
// =============================================================================
@@ -2233,6 +2246,7 @@ model Round {
evaluationSummaries EvaluationSummary[]
evaluationDiscussions EvaluationDiscussion[]
messages Message[]
notificationLogs NotificationLog[]
cohorts Cohort[]
liveCursor LiveProgressCursor?