fix: ranking shows all reviewed projects, fix override badge sync issue
All checks were successful
Build and Push Docker Image / build (push) Successful in 10m17s

- AI ranking now includes ALL projects (never filters/excludes any)
- Updated system prompt: filter criteria inform priority, not exclusion
- Dynamic maxTokens scaling for large project pools (80 tokens/project)
- Fallback: projects AI omits are appended sorted by composite score
- Override badge uses snapshotOrder state (synced with localOrder in same
  useEffect) instead of rankingMap.originalIndex to prevent stale-render
  mismatch where all items incorrectly showed as overridden

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-02 14:10:48 +01:00
parent d2e0dbdc94
commit 80a7bedddc
2 changed files with 47 additions and 9 deletions

View File

@@ -90,6 +90,7 @@ type SortableProjectRowProps = {
jurorScores: JurorScore[] | undefined
onSelect: () => void
isSelected: boolean
originalRank: number | undefined // from snapshotOrder — always in sync with localOrder
}
// ─── Sub-component: SortableProjectRow ────────────────────────────────────────
@@ -102,6 +103,7 @@ function SortableProjectRow({
jurorScores,
onSelect,
isSelected,
originalRank,
}: SortableProjectRowProps) {
const {
attributes,
@@ -117,8 +119,9 @@ function SortableProjectRow({
transition,
}
// isOverridden: admin drag-reordered this project from its original snapshot position
const isOverridden = entry !== undefined && currentRank !== entry.originalIndex
// isOverridden: admin drag-reordered this project from its original snapshot position.
// Uses snapshotOrder (set in same effect as localOrder) so they are always in sync.
const isOverridden = originalRank !== undefined && currentRank !== originalRank
// Compute yes count from juror scores
const yesCount = jurorScores?.filter((j) => j.decision === true).length ?? 0
@@ -236,6 +239,9 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
STARTUP: [],
BUSINESS_CONCEPT: [],
})
// Track the original snapshot order (projectId → 1-based rank) for override detection.
// Updated in the same effect as localOrder so they are always in sync.
const [snapshotOrder, setSnapshotOrder] = useState<Record<string, number>>({})
const initialized = useRef(false)
const pendingReorderCount = useRef(0)
@@ -404,6 +410,11 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
STARTUP: startup.map((r) => r.projectId),
BUSINESS_CONCEPT: concept.map((r) => r.projectId),
})
// Track original order for override detection (same effect = always in sync)
const order: Record<string, number> = {}
startup.forEach((r, i) => { order[r.projectId] = i + 1 })
concept.forEach((r, i) => { order[r.projectId] = i + 1 })
setSnapshotOrder(order)
initialized.current = true
}
}, [snapshot])
@@ -853,6 +864,7 @@ export function RankingDashboard({ competitionId: _competitionId, roundId }: Ran
jurorScores={evalScores?.[projectId]}
onSelect={() => setSelectedProjectId(projectId)}
isSelected={selectedProjectId === projectId}
originalRank={snapshotOrder[projectId]}
/>
</motion.div>
{isCutoffRow && (