Fix stale session redirect loop, filtering stats to reflect overrides
All checks were successful
Build and Push Docker Image / build (push) Successful in 8m56s

- Auth layout verifies user exists in DB before redirecting to dashboard,
  breaking infinite loop for deleted accounts with stale sessions
- Jury/Mentor layouts handle null user (deleted) by redirecting to login
- Filtering stats cards and result list now use effective outcome
  (finalOutcome ?? outcome) instead of raw AI outcome
- Award eligibility evaluation includes admin-overridden PASSED projects
- Award shortlist reasoning column shows full text instead of truncating

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
Matt
2026-02-18 10:01:31 +01:00
parent 1ec2247295
commit 6e9fcda45a
6 changed files with 72 additions and 44 deletions

View File

@@ -32,7 +32,6 @@ import {
Play,
Trophy,
AlertTriangle,
ChevronsUpDown,
} from 'lucide-react'
type AwardShortlistProps = {
@@ -59,7 +58,6 @@ export function AwardShortlist({
jobDone,
}: AwardShortlistProps) {
const [expanded, setExpanded] = useState(false)
const [expandedReasoning, setExpandedReasoning] = useState<Set<string>>(new Set())
const utils = trpc.useUtils()
const isRunning = jobStatus === 'PENDING' || jobStatus === 'PROCESSING'
@@ -130,17 +128,6 @@ export function AwardShortlist({
const allShortlisted = shortlist && shortlist.eligibilities.length > 0 && shortlist.eligibilities.every((e) => e.shortlisted)
const someShortlisted = shortlistedCount > 0 && !allShortlisted
const toggleReasoning = (id: string) => {
setExpandedReasoning((prev) => {
const next = new Set(prev)
if (next.has(id)) {
next.delete(id)
} else {
next.add(id)
}
return next
})
}
const handleBulkToggle = () => {
if (!shortlist) return
@@ -286,7 +273,7 @@ export function AwardShortlist({
<th className="px-3 py-2 text-left w-8">#</th>
<th className="px-3 py-2 text-left">Project</th>
<th className="px-3 py-2 text-left w-24">Score</th>
<th className="px-3 py-2 text-left w-44">Reasoning</th>
<th className="px-3 py-2 text-left min-w-[300px]">Reasoning</th>
<th className="px-3 py-2 text-center w-20">
<div className="flex items-center justify-center gap-1">
<Checkbox
@@ -304,7 +291,6 @@ export function AwardShortlist({
{shortlist.eligibilities.map((e, i) => {
const reasoning = (e.aiReasoningJson as Record<string, unknown>)?.reasoning as string | undefined
const isTop5 = i < shortlistSize
const isReasoningExpanded = expandedReasoning.has(e.id)
return (
<tr key={e.id} className={`border-t ${isTop5 ? 'bg-amber-50/50' : ''}`}>
<td className="px-3 py-2 text-muted-foreground font-mono">
@@ -337,18 +323,9 @@ export function AwardShortlist({
</td>
<td className="px-3 py-2">
{reasoning ? (
<button
onClick={() => toggleReasoning(e.id)}
className="text-left w-full group"
>
<p className={`text-xs text-muted-foreground ${isReasoningExpanded ? '' : 'line-clamp-2'}`}>
{reasoning}
</p>
<span className="text-xs text-blue-600 opacity-0 group-hover:opacity-100 transition-opacity flex items-center gap-0.5 mt-0.5">
<ChevronsUpDown className="h-3 w-3" />
{isReasoningExpanded ? 'Collapse' : 'Expand'}
</span>
</button>
<p className="text-xs text-muted-foreground whitespace-pre-wrap leading-relaxed">
{reasoning}
</p>
) : (
<span className="text-xs text-muted-foreground"></span>
)}