feat: semi-finalist tracker dashboard, account reminders, search + UX fixes

- Add getSemiFinalistStats query with per-category/per-award breakdown
- Add sendAccountReminders mutation with invite token generation and dedup
- Add SemiFinalistTracker dashboard widget with progress bars and remind buttons
- Add ACCOUNT_REMINDER email template
- Extend project search to match team member name/email (7 locations)
- Fix Passed count deduplication: count distinct projects, not round-state rows
- Fix role switcher: visible pills above user section, auto-refresh session on mount

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-04 15:41:03 +01:00
parent af03c12ae5
commit 43e21c6c6e
10 changed files with 622 additions and 14 deletions

View File

@@ -1959,6 +1959,32 @@ export function getEmailPreviewHtml(subject: string, body: string): string {
return getEmailWrapper(content)
}
/**
* Generate "Account Setup Reminder" email template
* Sent to semi-finalist team members who haven't set up their account yet.
*/
export function getAccountReminderTemplate(
name: string,
projectName: string,
accountUrl: string,
): EmailTemplate {
const greeting = name ? `Hello ${name},` : 'Hello,'
const content = `
${sectionTitle(greeting)}
${paragraph(`Your project <strong>"${projectName}"</strong> has been selected as a semi-finalist in the Monaco Ocean Protection Challenge.`)}
${infoBox('Please set up your account to access your applicant dashboard and stay up to date with the competition.', 'warning')}
${ctaButton(accountUrl, 'Set Up Your Account')}
${paragraph('If you have any questions, please contact the MOPC team.')}
`
return {
subject: `Action Required: Set up your MOPC account — "${projectName}"`,
html: getEmailWrapper(content),
text: `${greeting}\n\nYour project "${projectName}" has been selected as a semi-finalist in the Monaco Ocean Protection Challenge.\n\nPlease set up your account to access your applicant dashboard.\n\nSet up your account: ${getBaseUrl()}${accountUrl}\n\nIf you have any questions, please contact the MOPC team.\n\n---\nMonaco Ocean Protection Challenge\nTogether for a healthier ocean.`,
}
}
/**
* Template registry mapping notification types to template generators
*/
@@ -2137,6 +2163,13 @@ export const NOTIFICATION_EMAIL_TEMPLATES: Record<string, TemplateGenerator> = {
ctx.metadata?.accountUrl as string | undefined,
),
ACCOUNT_REMINDER: (ctx) =>
getAccountReminderTemplate(
ctx.name || '',
(ctx.metadata?.projectName as string) || 'Your Project',
(ctx.metadata?.accountUrl as string) || '/accept-invite',
),
// Admin templates
NEW_APPLICATION: (ctx) =>
getNewApplicationTemplate(