Round system redesign: criteria voting, audience voting, pipeline view, and admin UX improvements
- Schema: Extend LiveVotingSession with votingMode, criteriaJson, audience fields; add AudienceVoter model; make LiveVote.userId nullable for audience voters - Backend: Criteria-based voting with weighted scores, audience registration/voting with token-based dedup, configurable jury/audience weight in results - Jury UI: Criteria scoring with per-criterion sliders alongside simple 1-10 mode - Public audience voting page at /vote/[sessionId] with mobile-first design - Admin live voting: Tabbed layout (Session/Config/Results), criteria config, audience settings, weight-adjustable results with tie detection - Round type settings: Visual card selector replacing dropdown, feature tags - Round detail page: Live event status section, type-specific stats and actions - Round pipeline view: Horizontal visualization with bottleneck detection, List/Pipeline toggle on rounds page - SSE: Separate jury/audience vote events, audience vote tracking - Field visibility: Hide irrelevant fields per round type in create/edit forms Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -34,6 +34,7 @@ import {
|
||||
FormMessage,
|
||||
} from '@/components/ui/form'
|
||||
import { RoundTypeSettings } from '@/components/forms/round-type-settings'
|
||||
import { ROUND_FIELD_VISIBILITY } from '@/types/round-settings'
|
||||
import { ArrowLeft, Loader2, AlertCircle, Bell, LayoutTemplate } from 'lucide-react'
|
||||
import { toast } from 'sonner'
|
||||
import { DateTimePicker } from '@/components/ui/datetime-picker'
|
||||
@@ -124,14 +125,15 @@ function CreateRoundContent() {
|
||||
})
|
||||
|
||||
const onSubmit = async (data: CreateRoundForm) => {
|
||||
const visibility = ROUND_FIELD_VISIBILITY[roundType]
|
||||
await createRound.mutateAsync({
|
||||
programId: data.programId,
|
||||
name: data.name,
|
||||
roundType,
|
||||
requiredReviews: roundType === 'FILTERING' ? 0 : data.requiredReviews,
|
||||
requiredReviews: visibility?.showRequiredReviews ? data.requiredReviews : 0,
|
||||
settingsJson: roundSettings,
|
||||
votingStartAt: data.votingStartAt ?? undefined,
|
||||
votingEndAt: data.votingEndAt ?? undefined,
|
||||
votingStartAt: visibility?.showVotingWindow ? (data.votingStartAt ?? undefined) : undefined,
|
||||
votingEndAt: visibility?.showVotingWindow ? (data.votingEndAt ?? undefined) : undefined,
|
||||
entryNotificationType: entryNotificationType || undefined,
|
||||
})
|
||||
}
|
||||
@@ -291,7 +293,7 @@ function CreateRoundContent() {
|
||||
)}
|
||||
/>
|
||||
|
||||
{roundType !== 'FILTERING' && (
|
||||
{ROUND_FIELD_VISIBILITY[roundType]?.showRequiredReviews && (
|
||||
<FormField
|
||||
control={form.control}
|
||||
name="requiredReviews"
|
||||
@@ -326,6 +328,7 @@ function CreateRoundContent() {
|
||||
onSettingsChange={setRoundSettings}
|
||||
/>
|
||||
|
||||
{ROUND_FIELD_VISIBILITY[roundType]?.showVotingWindow && (
|
||||
<Card>
|
||||
<CardHeader>
|
||||
<CardTitle className="text-lg">Voting Window</CardTitle>
|
||||
@@ -377,6 +380,7 @@ function CreateRoundContent() {
|
||||
</p>
|
||||
</CardContent>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
{/* Team Notification */}
|
||||
<Card>
|
||||
|
||||
Reference in New Issue
Block a user