Comprehensive round system audit: fix 27 logic bugs, add manual project/assignment features, improve UI/UX
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m23s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m23s
## Critical Logic Fixes (Tier 1) - Fix requiredReviews config key mismatch (always defaulted to 3) - Fix double-email + stageName/roundName metadata mismatch in notifications - Fix snake_case config reads in peer review (peerReviewEnabled was always blocked) - Add server-side COI check to evaluation submit (was client-only) - Fix hard-coded feedbackText.min(10) — now uses config values - Fix binaryDecision corruption in non-binary scoring modes - Fix advanceProjects: add competition/sort-order/status validations, move autoPass into tx - Fix removeFromRound: now cleans up orphaned Assignment records - Fix 3-day reminder sending wrong email template (was using 24h template) ## High-Priority Logic Fixes (Tier 2) - Add project state transition whitelist (prevent invalid transitions like REJECTED→PASSED) - Scope AI assignment job to jury group members (was querying all JURY_MEMBERs) - Add COI awareness to AI assignment generation - Enforce requireAllCriteriaScored server-side - Fix expireIntentsForRound nested transaction (now uses caller's tx) - Implement notifyOnEntry for advancement path - Implement notifyOnAdvance (was dead config) - Fix checkRequirementsAndTransition for SubmissionFileRequirement model ## New Features (Tier 3) - Add Project to Round: dialog with "Create New" and "From Pool" tabs - Assignment "By Project" mode: select project → assign multiple jurors - Backend: project.createAndAssignToRound procedure ## UI/UX Improvements (Tier 4+5) - Add AlertDialog confirmation to header status dropdown - Replace native confirm() with AlertDialog in assignments table - Jury stats card now display-only with "Change" link - Assignments tab restructured into logical card groups - Inline-editable round name in header - Back button shows destination label - Readiness checklist: green check instead of strikethrough - Gate assignments tab when no jury group assigned - Relative time on window stats card - Toast feedback on date saves - Disable advance button when no target round - COI section shows placeholder when empty - Round position shown as "Round X of Y" - InlineMemberCap edit icon always visible - Status badge tooltip with description - Add REMINDER_3_DAYS email template - Fix maybeSendEmail to respect notification preferences - Optimize bulk notification email loop Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -1083,6 +1083,60 @@ Together for a healthier ocean.
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate "3 Days Remaining" email template (for jury)
|
||||
*/
|
||||
function getReminder3DaysTemplate(
|
||||
name: string,
|
||||
pendingCount: number,
|
||||
roundName: string,
|
||||
deadline: string,
|
||||
assignmentsUrl?: string
|
||||
): EmailTemplate {
|
||||
const greeting = name ? `Hello ${name},` : 'Hello,'
|
||||
|
||||
const urgentBox = `
|
||||
<table role="presentation" width="100%" cellspacing="0" cellpadding="0" border="0" style="margin: 20px 0;">
|
||||
<tr>
|
||||
<td style="background-color: #fef3c7; border-left: 4px solid #f59e0b; border-radius: 0 8px 8px 0; padding: 16px 20px;">
|
||||
<p style="color: #92400e; margin: 0; font-size: 14px; font-weight: 600;">⚠ 3 Days Remaining</p>
|
||||
</td>
|
||||
</tr>
|
||||
</table>
|
||||
`
|
||||
|
||||
const content = `
|
||||
${sectionTitle(greeting)}
|
||||
${urgentBox}
|
||||
${paragraph(`This is a reminder that <strong style="color: ${BRAND.darkBlue};">${roundName}</strong> closes in 3 days.`)}
|
||||
${statCard('Pending Evaluations', pendingCount)}
|
||||
${infoBox(`<strong>Deadline:</strong> ${deadline}`, 'warning')}
|
||||
${paragraph('Please plan to complete your remaining evaluations before the deadline to ensure your feedback is included in the selection process.')}
|
||||
${assignmentsUrl ? ctaButton(assignmentsUrl, 'Complete Evaluations') : ''}
|
||||
`
|
||||
|
||||
return {
|
||||
subject: `Reminder: ${pendingCount} evaluation${pendingCount !== 1 ? 's' : ''} due in 3 days`,
|
||||
html: getEmailWrapper(content),
|
||||
text: `
|
||||
${greeting}
|
||||
|
||||
This is a reminder that ${roundName} closes in 3 days.
|
||||
|
||||
You have ${pendingCount} pending evaluation${pendingCount !== 1 ? 's' : ''}.
|
||||
Deadline: ${deadline}
|
||||
|
||||
Please plan to complete your remaining evaluations before the deadline.
|
||||
|
||||
${assignmentsUrl ? `Complete evaluations: ${assignmentsUrl}` : ''}
|
||||
|
||||
---
|
||||
Monaco Ocean Protection Challenge
|
||||
Together for a healthier ocean.
|
||||
`,
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate "1 Hour Reminder" email template (for jury)
|
||||
*/
|
||||
@@ -1457,6 +1511,14 @@ export const NOTIFICATION_EMAIL_TEMPLATES: Record<string, TemplateGenerator> = {
|
||||
ctx.metadata?.deadline as string | undefined,
|
||||
ctx.linkUrl
|
||||
),
|
||||
REMINDER_3_DAYS: (ctx) =>
|
||||
getReminder3DaysTemplate(
|
||||
ctx.name || '',
|
||||
(ctx.metadata?.pendingCount as number) || 0,
|
||||
(ctx.metadata?.roundName as string) || 'this round',
|
||||
(ctx.metadata?.deadline as string) || 'Soon',
|
||||
ctx.linkUrl
|
||||
),
|
||||
REMINDER_24H: (ctx) =>
|
||||
getReminder24HTemplate(
|
||||
ctx.name || '',
|
||||
|
||||
Reference in New Issue
Block a user