feat(finalization): winner email + UI for terminal rounds
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m7s
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m7s
When finalizing a round with no further round to advance to, passing teams
are winners — not advancers. Detected for both special-award terminal rounds
(label = award name) and the main competition's terminal round (label =
competition name). Wording uses "a winner" so it works for both single-winner
awards and top-N main-track outcomes.
Adds AWARD_WINNER_NOTIFICATION email type + template ("Your project has won!"
with "our team will reach out about next steps" copy). Routes through the
notification dispatch table the same way ADVANCEMENT_NOTIFICATION does.
The FinalizationSummary gains a `winnerContext` field; the admin finalization
tab uses it to swap "X projects will advance to Y" → "X winners will be
notified for [label]" and renames "Advancement Message" → "Winner Message"
in the custom-message field. The email-preview button shows the winner
template when applicable.
In-app notification (bell icon) gets matching winner copy.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -666,7 +666,9 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
)}
|
||||
<div>
|
||||
<div className="flex items-center justify-between mb-1.5">
|
||||
<label className="text-sm font-medium">Advancement Message</label>
|
||||
<label className="text-sm font-medium">
|
||||
{summary.winnerContext ? 'Winner Message' : 'Advancement Message'}
|
||||
</label>
|
||||
<Button
|
||||
variant="ghost"
|
||||
size="sm"
|
||||
@@ -681,7 +683,11 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
</Button>
|
||||
</div>
|
||||
<Textarea
|
||||
placeholder="Custom message for projects that are advancing (added to the standard email template)..."
|
||||
placeholder={
|
||||
summary.winnerContext
|
||||
? 'Custom message for winners (added to the standard winner email template)...'
|
||||
: 'Custom message for projects that are advancing (added to the standard email template)...'
|
||||
}
|
||||
value={advancementMessage}
|
||||
onChange={(e) => setAdvancementMessage(e.target.value)}
|
||||
rows={3}
|
||||
@@ -715,7 +721,13 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
<CardContent className="pt-0">
|
||||
<div className="flex items-center justify-between border-t pt-4">
|
||||
<div className="text-sm text-muted-foreground">
|
||||
{summary.nextRound ? (
|
||||
{summary.winnerContext ? (
|
||||
<span>
|
||||
<strong>{passedCount}</strong>{' '}
|
||||
{passedCount !== 1 ? 'winners' : 'winner'} will be notified for{' '}
|
||||
<strong>{summary.winnerContext.label}</strong>
|
||||
</span>
|
||||
) : summary.nextRound ? (
|
||||
<span>
|
||||
<strong>{passedCount}</strong> project{passedCount !== 1 ? 's' : ''} will advance to{' '}
|
||||
<strong>{summary.nextRound.name}</strong>
|
||||
@@ -751,9 +763,11 @@ export function FinalizationTab({ roundId, roundStatus }: FinalizationTabProps)
|
||||
<ul className="list-disc pl-5 space-y-1">
|
||||
<li>Mark <strong>{passedCount}</strong> project{passedCount !== 1 ? 's' : ''} as <strong>PASSED</strong></li>
|
||||
<li>Mark <strong>{rejectedCount}</strong> project{rejectedCount !== 1 ? 's' : ''} as <strong>REJECTED</strong></li>
|
||||
{summary.nextRound && (
|
||||
{summary.winnerContext ? (
|
||||
<li>Notify <strong>{passedCount}</strong> {passedCount !== 1 ? 'winners' : 'winner'} for <strong>{summary.winnerContext.label}</strong> (no further round)</li>
|
||||
) : summary.nextRound ? (
|
||||
<li>Advance passed projects to <strong>{summary.nextRound.name}</strong></li>
|
||||
)}
|
||||
) : null}
|
||||
<li>Send email notifications to all affected teams</li>
|
||||
</ul>
|
||||
{undecidedCount > 0 && (
|
||||
|
||||
Reference in New Issue
Block a user