AI shortlist with approve/reject, assignment reasoning, fix review count badge
Some checks failed
Build and Push Docker Image / build (push) Has been cancelled

- Rewrite AIRecommendationsDisplay: show project titles, per-project
  checkboxes, Apply and Mark as Passed button with batch transition
- Show AI jury assignment reasoning directly in rows (not tooltip)
- Fix unassigned projects badge using requiredReviews instead of hardcoded 3
- Add aiParseFiles to EvaluationConfigSchema

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-18 15:11:20 +01:00
parent cab311fbbb
commit 7e85348a6d
3 changed files with 172 additions and 76 deletions

View File

@@ -877,10 +877,26 @@ function AssignmentRow({
const a = assignment
return (
<div className="flex items-start gap-2 px-3 py-2 group hover:bg-muted/30 transition-colors border-t first:border-t-0">
<div className="flex-1 min-w-0 space-y-1">
<div className="flex items-start gap-2 px-3 py-2.5 group hover:bg-muted/30 transition-colors border-t first:border-t-0">
<div className="flex-1 min-w-0 space-y-1.5">
<div className="flex items-center gap-1.5">
<span className="text-sm font-medium truncate">{a.projectTitle}</span>
{!a.isManual && a.score > 0 && (
<Badge
variant="outline"
className={cn(
'text-[10px] h-4 px-1 tabular-nums shrink-0',
a.score >= 50
? 'border-green-300 text-green-700'
: a.score >= 25
? 'border-amber-300 text-amber-700'
: 'border-red-300 text-red-700',
)}
>
<Sparkles className="h-2.5 w-2.5 mr-0.5" />
{Math.round(a.score)}
</Badge>
)}
{a.isManual ? (
<Badge
variant="outline"
@@ -898,54 +914,33 @@ function AssignmentRow({
) : null}
</div>
{/* Score + tags + reasoning */}
<div className="flex flex-wrap items-center gap-1">
{!a.isManual && a.score > 0 && (
<TooltipProvider>
<Tooltip>
<TooltipTrigger asChild>
<Badge
variant="outline"
className={cn(
'text-[10px] h-4 px-1 tabular-nums',
a.score >= 50
? 'border-green-300 text-green-700'
: a.score >= 25
? 'border-amber-300 text-amber-700'
: 'border-red-300 text-red-700',
)}
>
<Sparkles className="h-2.5 w-2.5 mr-0.5" />
{Math.round(a.score)}
</Badge>
</TooltipTrigger>
<TooltipContent side="bottom" className="max-w-xs">
<p className="font-medium text-xs mb-1">Match Score Breakdown</p>
<ul className="text-xs space-y-0.5">
{a.reasoning.map((r, i) => (
<li key={i}> {r}</li>
))}
</ul>
</TooltipContent>
</Tooltip>
</TooltipProvider>
)}
{a.matchingTags.slice(0, 3).map((tag) => (
<Badge
key={tag}
variant="secondary"
className="text-[10px] h-4 px-1 gap-0.5"
>
<Tag className="h-2.5 w-2.5" />
{tag}
</Badge>
))}
{a.matchingTags.length > 3 && (
<span className="text-[10px] text-muted-foreground">
+{a.matchingTags.length - 3} more
</span>
)}
</div>
{/* AI reasoning — displayed directly */}
{!a.isManual && a.reasoning.length > 0 && a.reasoning[0] !== 'Manually added by admin' && (
<p className="text-xs text-muted-foreground leading-relaxed">
{a.reasoning.join(' ')}
</p>
)}
{/* Tags */}
{a.matchingTags.length > 0 && (
<div className="flex flex-wrap items-center gap-1">
{a.matchingTags.slice(0, 4).map((tag) => (
<Badge
key={tag}
variant="secondary"
className="text-[10px] h-4 px-1 gap-0.5"
>
<Tag className="h-2.5 w-2.5" />
{tag}
</Badge>
))}
{a.matchingTags.length > 4 && (
<span className="text-[10px] text-muted-foreground">
+{a.matchingTags.length - 4} more
</span>
)}
</div>
)}
{/* Policy violations */}
{a.policyViolations.length > 0 && (