Add juror quick actions to Members section, redistribute button, dropout emails, and transfer duplicate detection
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled

- Add mail/transfer/reshuffle/redistribute icons to each juror row in Members card
- New redistributeJurorAssignments procedure: reassign all pending projects without dropping juror from group
- New DROPOUT_REASSIGNED email template with project names, deadline, and dropped juror context
- Update reassignDroppedJuror to send per-juror DROPOUT_REASSIGNED emails instead of generic BATCH_ASSIGNED
- Transfer dialog now shows all candidates with "Already assigned" / "At cap" labels instead of hiding them
- SQL script for prod DB insertion of new notification setting without seeding

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-02-23 16:08:46 +01:00
parent 49e9405e01
commit 95d51e7de3
7 changed files with 570 additions and 15 deletions

View File

@@ -273,19 +273,28 @@ export function TransferAssignmentsDialog({
value={currentDest}
onValueChange={(v) => setDestOverrides((prev) => ({ ...prev, [assignment.id]: v }))}
>
<SelectTrigger className="w-[200px] h-8 text-xs">
<SelectTrigger className="w-[220px] h-8 text-xs">
<SelectValue placeholder="Select juror" />
</SelectTrigger>
<SelectContent>
{candidateData.candidates
.filter((c) => c.eligibleProjectIds.includes(assignment.projectId))
.map((c) => (
<SelectItem key={c.userId} value={c.userId}>
{candidateData.candidates.map((c) => {
const isEligible = c.eligibleProjectIds.includes(assignment.projectId)
const alreadyHas = c.alreadyAssignedProjectIds?.includes(assignment.projectId)
return (
<SelectItem
key={c.userId}
value={c.userId}
disabled={!isEligible}
className={cn(!isEligible && 'opacity-50')}
>
<span>{c.name}</span>
<span className="text-muted-foreground ml-1">({c.currentLoad}/{c.cap})</span>
{c.allCompleted && <span className="text-emerald-600 ml-1">Done</span>}
{alreadyHas && <span className="text-amber-600 ml-1">Already assigned</span>}
{!isEligible && !alreadyHas && c.currentLoad >= c.cap && <span className="text-red-500 ml-1">At cap</span>}
</SelectItem>
))}
)
})}
</SelectContent>
</Select>
</div>