feat: add email preview to award notification and finalization tab

- Award "Notify Pool" dialog now uses EmailPreviewDialog with live preview
- Button shows eligible project count: "Notify Pool (38)"
- Finalization tab email section has "Preview" buttons for both
  advancement and rejection messages
- EmailPreviewDialog supports previewOnly mode (close button, no send)
- Backend: previewAwardSelectionEmail, previewFinalizationAdvancementEmail,
  previewFinalizationRejectionEmail queries

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-03 22:57:52 +01:00
parent f79a6d1341
commit 924f8071e1
5 changed files with 212 additions and 84 deletions

View File

@@ -4,7 +4,7 @@ import { Prisma } from '@prisma/client'
import { router, protectedProcedure, adminProcedure } from '../trpc'
import { logAudit } from '../utils/audit'
import { processEligibilityJob } from '../services/award-eligibility-job'
import { sendStyledNotificationEmail } from '@/lib/email'
import { sendStyledNotificationEmail, getAwardSelectionNotificationTemplate } from '@/lib/email'
import { generateInviteToken, getInviteExpiryMs } from '@/server/utils/invite'
import type { PrismaClient } from '@prisma/client'
@@ -1229,6 +1229,31 @@ export const specialAwardRouter = router({
return { needsInvite, hasAccount, totalProjects: eligibilities.length }
}),
previewAwardSelectionEmail: adminProcedure
.input(z.object({
awardId: z.string(),
customMessage: z.string().optional(),
}))
.query(async ({ ctx, input }) => {
const award = await ctx.prisma.specialAward.findUniqueOrThrow({
where: { id: input.awardId },
select: { name: true },
})
const eligibleCount = await ctx.prisma.awardEligibility.count({
where: { awardId: input.awardId, eligible: true },
})
const template = getAwardSelectionNotificationTemplate(
'Team Member',
'Your Project',
award.name,
input.customMessage || undefined,
)
return { html: template.html, subject: template.subject, recipientCount: eligibleCount }
}),
/**
* Notify eligible projects that they've been selected for an award.
* Generates invite tokens for passwordless users.