Comprehensive platform audit: security, UX, performance, and visual polish
Phase 1: Security - status transition validation, Zod tightening, DB indexes, transactions Phase 2: Admin UX - search/filter for awards, learning, partners pages Phase 3: Dashboard - Recent Activity feed, Pending Actions card, quick actions Phase 4: Jury - assignments progress/urgency, autosave indicator, divergence highlighting Phase 5: Portals - observer charts, mentor search, login/onboarding polish Phase 6: Messages preview dialog, CsvExportDialog with column selection Phase 7: Performance - query optimizations, loading skeletons, useDebounce hook Phase 8: Visual - AnimatedCard, hover effects, StatusBadge, empty state CTAs Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -8,6 +8,14 @@ import {
|
||||
} from '../services/in-app-notification'
|
||||
import { logAudit } from '@/server/utils/audit'
|
||||
|
||||
// Valid round status transitions (state machine)
|
||||
const VALID_ROUND_TRANSITIONS: Record<string, string[]> = {
|
||||
DRAFT: ['ACTIVE', 'ARCHIVED'], // Draft can be activated or archived
|
||||
ACTIVE: ['CLOSED'], // Active rounds can only be closed
|
||||
CLOSED: ['ARCHIVED'], // Closed rounds can be archived
|
||||
ARCHIVED: [], // Archived is terminal — no transitions out
|
||||
}
|
||||
|
||||
export const roundRouter = router({
|
||||
/**
|
||||
* List rounds for a program
|
||||
@@ -296,6 +304,15 @@ export const roundRouter = router({
|
||||
select: { status: true, votingStartAt: true, votingEndAt: true },
|
||||
})
|
||||
|
||||
// Validate status transition
|
||||
const allowedTransitions = VALID_ROUND_TRANSITIONS[previousRound.status] || []
|
||||
if (!allowedTransitions.includes(input.status)) {
|
||||
throw new TRPCError({
|
||||
code: 'BAD_REQUEST',
|
||||
message: `Invalid status transition: cannot change from ${previousRound.status} to ${input.status}. Allowed transitions: ${allowedTransitions.join(', ') || 'none (terminal state)'}`,
|
||||
})
|
||||
}
|
||||
|
||||
const now = new Date()
|
||||
|
||||
// When activating a round, if votingStartAt is in the future, update it to now
|
||||
|
||||
Reference in New Issue
Block a user