Add styled notification emails and round-attached notifications

- Add 15+ styled email templates matching existing invite email design
- Wire up notification triggers in all routers (assignment, round, project, mentor, application, onboarding)
- Add test email button for each notification type in admin settings
- Add round-attached notifications: admins can configure which notification to send when projects enter a round
- Fall back to status-based notifications when round has no configured notification

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
2026-02-04 00:10:51 +01:00
parent 3be6a743ed
commit b0189cad92
13 changed files with 1892 additions and 28 deletions

View File

@@ -2,6 +2,11 @@ import { z } from 'zod'
import { TRPCError } from '@trpc/server'
import { router, publicProcedure } from '../trpc'
import { CompetitionCategory, OceanIssue, TeamMemberRole } from '@prisma/client'
import {
createNotification,
notifyAdmins,
NotificationTypes,
} from '../services/in-app-notification'
// Zod schemas for the application form
const teamMemberSchema = z.object({
@@ -308,6 +313,35 @@ export const applicationRouter = router({
},
})
// Notify applicant of successful submission
await createNotification({
userId: user.id,
type: NotificationTypes.APPLICATION_SUBMITTED,
title: 'Application Received',
message: `Your application for "${data.projectName}" has been successfully submitted to ${round.program.name}.`,
linkUrl: `/team/projects/${project.id}`,
linkLabel: 'View Application',
metadata: {
projectName: data.projectName,
programName: round.program.name,
},
})
// Notify admins of new application
await notifyAdmins({
type: NotificationTypes.NEW_APPLICATION,
title: 'New Application',
message: `New application received: "${data.projectName}" from ${data.contactName}.`,
linkUrl: `/admin/projects/${project.id}`,
linkLabel: 'Review Application',
metadata: {
projectName: data.projectName,
applicantName: data.contactName,
applicantEmail: data.contactEmail,
programName: round.program.name,
},
})
return {
success: true,
projectId: project.id,