661 lines
30 KiB
Markdown
661 lines
30 KiB
Markdown
|
|
# Round Type: LIVE_FINAL — Live Finals Documentation
|
||
|
|
|
||
|
|
## Overview
|
||
|
|
|
||
|
|
The **LIVE_FINAL** round type orchestrates the live ceremony where Jury 3 evaluates finalist presentations in real-time. This is Round 7 in the redesigned 8-step competition flow. It combines jury scoring, optional audience participation, deliberation periods, and live results display into a single managed event.
|
||
|
|
|
||
|
|
**Core capabilities:**
|
||
|
|
- Real-time stage manager controls (presentation cursor, timing, pause/resume)
|
||
|
|
- Jury voting with multiple modes (numeric, ranking, binary)
|
||
|
|
- Optional audience voting with weighted scores
|
||
|
|
- Per-category presentation windows (STARTUP window, then CONCEPT window)
|
||
|
|
- Deliberation period for jury discussion
|
||
|
|
- Live results display or ceremony reveal
|
||
|
|
- Anti-fraud measures for audience participation
|
||
|
|
|
||
|
|
**Round 7 position in the flow:**
|
||
|
|
```
|
||
|
|
Round 1: Application Window (INTAKE)
|
||
|
|
Round 2: AI Screening (FILTERING)
|
||
|
|
Round 3: Jury 1 - Semi-finalist Selection (EVALUATION)
|
||
|
|
Round 4: Semi-finalist Submission (SUBMISSION)
|
||
|
|
Round 5: Jury 2 - Finalist Selection (EVALUATION)
|
||
|
|
Round 6: Finalist Mentoring (MENTORING)
|
||
|
|
Round 7: Live Finals (LIVE_FINAL) ← THIS DOCUMENT
|
||
|
|
Round 8: Confirm Winners (CONFIRMATION)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Current System (Pipeline → Track → Stage)
|
||
|
|
|
||
|
|
### Existing Models
|
||
|
|
|
||
|
|
**LiveVotingSession** — Per-stage voting session:
|
||
|
|
```prisma
|
||
|
|
model LiveVotingSession {
|
||
|
|
id String @id @default(cuid())
|
||
|
|
stageId String? @unique
|
||
|
|
status String @default("NOT_STARTED") // NOT_STARTED, IN_PROGRESS, PAUSED, COMPLETED
|
||
|
|
currentProjectIndex Int @default(0)
|
||
|
|
currentProjectId String?
|
||
|
|
votingStartedAt DateTime?
|
||
|
|
votingEndsAt DateTime?
|
||
|
|
projectOrderJson Json? @db.JsonB // Array of project IDs in presentation order
|
||
|
|
|
||
|
|
// Voting configuration
|
||
|
|
votingMode String @default("simple") // "simple" (1-10) | "criteria" (per-criterion scores)
|
||
|
|
criteriaJson Json? @db.JsonB // Array of { id, label, description, scale, weight }
|
||
|
|
|
||
|
|
// Audience settings
|
||
|
|
allowAudienceVotes Boolean @default(false)
|
||
|
|
audienceVoteWeight Float @default(0) // 0.0 to 1.0
|
||
|
|
audienceVotingMode String @default("disabled") // "disabled" | "per_project" | "per_category" | "favorites"
|
||
|
|
audienceMaxFavorites Int @default(3)
|
||
|
|
audienceRequireId Boolean @default(false)
|
||
|
|
audienceVotingDuration Int? // Minutes (null = same as jury)
|
||
|
|
|
||
|
|
tieBreakerMethod String @default("admin_decides") // 'admin_decides' | 'highest_individual' | 'revote'
|
||
|
|
presentationSettingsJson Json? @db.JsonB
|
||
|
|
|
||
|
|
stage Stage? @relation(...)
|
||
|
|
votes LiveVote[]
|
||
|
|
audienceVoters AudienceVoter[]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**LiveVote** — Individual jury or audience vote:
|
||
|
|
```prisma
|
||
|
|
model LiveVote {
|
||
|
|
id String @id @default(cuid())
|
||
|
|
sessionId String
|
||
|
|
projectId String
|
||
|
|
userId String? // Nullable for audience voters without accounts
|
||
|
|
score Int // 1-10 (or weighted score for criteria mode)
|
||
|
|
isAudienceVote Boolean @default(false)
|
||
|
|
votedAt DateTime @default(now())
|
||
|
|
|
||
|
|
// Criteria scores (used when votingMode="criteria")
|
||
|
|
criterionScoresJson Json? @db.JsonB // { [criterionId]: score }
|
||
|
|
|
||
|
|
// Audience voter link
|
||
|
|
audienceVoterId String?
|
||
|
|
|
||
|
|
session LiveVotingSession @relation(...)
|
||
|
|
user User? @relation(...)
|
||
|
|
audienceVoter AudienceVoter? @relation(...)
|
||
|
|
|
||
|
|
@@unique([sessionId, projectId, userId])
|
||
|
|
@@unique([sessionId, projectId, audienceVoterId])
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**AudienceVoter** — Registered audience participant:
|
||
|
|
```prisma
|
||
|
|
model AudienceVoter {
|
||
|
|
id String @id @default(cuid())
|
||
|
|
sessionId String
|
||
|
|
token String @unique // Unique voting token (UUID)
|
||
|
|
identifier String? // Optional: email, phone, or name
|
||
|
|
identifierType String? // "email" | "phone" | "name" | "anonymous"
|
||
|
|
ipAddress String?
|
||
|
|
userAgent String?
|
||
|
|
createdAt DateTime @default(now())
|
||
|
|
|
||
|
|
session LiveVotingSession @relation(...)
|
||
|
|
votes LiveVote[]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**LiveProgressCursor** — Stage manager cursor:
|
||
|
|
```prisma
|
||
|
|
model LiveProgressCursor {
|
||
|
|
id String @id @default(cuid())
|
||
|
|
stageId String @unique
|
||
|
|
sessionId String @unique @default(cuid())
|
||
|
|
activeProjectId String?
|
||
|
|
activeOrderIndex Int @default(0)
|
||
|
|
isPaused Boolean @default(false)
|
||
|
|
|
||
|
|
stage Stage @relation(...)
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
**Cohort** — Presentation groups:
|
||
|
|
```prisma
|
||
|
|
model Cohort {
|
||
|
|
id String @id @default(cuid())
|
||
|
|
stageId String
|
||
|
|
name String
|
||
|
|
votingMode String @default("simple") // simple, criteria, ranked
|
||
|
|
isOpen Boolean @default(false)
|
||
|
|
windowOpenAt DateTime?
|
||
|
|
windowCloseAt DateTime?
|
||
|
|
|
||
|
|
stage Stage @relation(...)
|
||
|
|
projects CohortProject[]
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Current Service Functions
|
||
|
|
|
||
|
|
`src/server/services/live-control.ts`:
|
||
|
|
- `startSession(stageId, actorId)` — Initialize/reset cursor
|
||
|
|
- `setActiveProject(stageId, projectId, actorId)` — Set currently presenting project
|
||
|
|
- `jumpToProject(stageId, orderIndex, actorId)` — Jump to specific project in queue
|
||
|
|
- `reorderQueue(stageId, newOrder, actorId)` — Reorder presentation sequence
|
||
|
|
- `pauseResume(stageId, isPaused, actorId)` — Toggle pause state
|
||
|
|
- `openCohortWindow(cohortId, actorId)` — Open voting window for a cohort
|
||
|
|
- `closeCohortWindow(cohortId, actorId)` — Close cohort window
|
||
|
|
|
||
|
|
### Current tRPC Procedures
|
||
|
|
|
||
|
|
`src/server/routers/live-voting.ts`:
|
||
|
|
```typescript
|
||
|
|
liveVoting.getSession({ stageId })
|
||
|
|
liveVoting.getSessionForVoting({ sessionId }) // Jury view
|
||
|
|
liveVoting.getPublicSession({ sessionId }) // Display view
|
||
|
|
liveVoting.setProjectOrder({ sessionId, projectIds })
|
||
|
|
liveVoting.setVotingMode({ sessionId, votingMode: 'simple' | 'criteria' })
|
||
|
|
liveVoting.setCriteria({ sessionId, criteria })
|
||
|
|
liveVoting.importCriteriaFromForm({ sessionId, formId })
|
||
|
|
liveVoting.startVoting({ sessionId, projectId, durationSeconds })
|
||
|
|
liveVoting.stopVoting({ sessionId })
|
||
|
|
liveVoting.endSession({ sessionId })
|
||
|
|
liveVoting.vote({ sessionId, projectId, score, criterionScores })
|
||
|
|
liveVoting.getResults({ sessionId, juryWeight?, audienceWeight? })
|
||
|
|
liveVoting.updatePresentationSettings({ sessionId, presentationSettingsJson })
|
||
|
|
liveVoting.updateSessionConfig({ sessionId, allowAudienceVotes, audienceVoteWeight, ... })
|
||
|
|
liveVoting.registerAudienceVoter({ sessionId, identifier?, identifierType? }) // Public
|
||
|
|
liveVoting.castAudienceVote({ sessionId, projectId, score, token }) // Public
|
||
|
|
liveVoting.getAudienceVoterStats({ sessionId })
|
||
|
|
liveVoting.getAudienceSession({ sessionId }) // Public
|
||
|
|
liveVoting.getPublicResults({ sessionId }) // Public
|
||
|
|
```
|
||
|
|
|
||
|
|
### Current LiveFinalConfig Type
|
||
|
|
|
||
|
|
From `src/types/pipeline-wizard.ts`:
|
||
|
|
```typescript
|
||
|
|
type LiveFinalConfig = {
|
||
|
|
juryVotingEnabled: boolean
|
||
|
|
audienceVotingEnabled: boolean
|
||
|
|
audienceVoteWeight: number
|
||
|
|
cohortSetupMode: 'auto' | 'manual'
|
||
|
|
revealPolicy: 'immediate' | 'delayed' | 'ceremony'
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Current Admin UI
|
||
|
|
|
||
|
|
`src/components/admin/pipeline/sections/live-finals-section.tsx`:
|
||
|
|
- Jury voting toggle
|
||
|
|
- Audience voting toggle + weight slider (0-100%)
|
||
|
|
- Cohort setup mode selector (auto/manual)
|
||
|
|
- Result reveal policy selector (immediate/delayed/ceremony)
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Redesigned Live Finals Round
|
||
|
|
|
||
|
|
### Enhanced LiveFinalConfig
|
||
|
|
|
||
|
|
**New comprehensive config:**
|
||
|
|
```typescript
|
||
|
|
type LiveFinalConfig = {
|
||
|
|
// Jury configuration
|
||
|
|
juryGroupId: string // Which jury evaluates (Jury 3)
|
||
|
|
|
||
|
|
// Voting mode
|
||
|
|
votingMode: 'NUMERIC' | 'RANKING' | 'BINARY'
|
||
|
|
|
||
|
|
// Numeric mode settings
|
||
|
|
numericScale?: {
|
||
|
|
min: number // Default: 1
|
||
|
|
max: number // Default: 10
|
||
|
|
allowDecimals: boolean // Default: false
|
||
|
|
}
|
||
|
|
|
||
|
|
// Criteria-based voting (optional enhancement to NUMERIC)
|
||
|
|
criteriaEnabled?: boolean
|
||
|
|
criteriaJson?: LiveVotingCriterion[] // { id, label, description, scale, weight }
|
||
|
|
importFromEvalForm?: string // EvaluationForm ID to import criteria from
|
||
|
|
|
||
|
|
// Ranking mode settings
|
||
|
|
rankingSettings?: {
|
||
|
|
maxRankedProjects: number // How many projects each juror ranks (e.g., top 3)
|
||
|
|
pointsSystem: 'DESCENDING' | 'BORDA' // 3-2-1 or Borda count
|
||
|
|
}
|
||
|
|
|
||
|
|
// Binary mode settings (simple yes/no)
|
||
|
|
binaryLabels?: {
|
||
|
|
yes: string // Default: "Finalist"
|
||
|
|
no: string // Default: "Not Selected"
|
||
|
|
}
|
||
|
|
|
||
|
|
// Audience voting
|
||
|
|
audienceVotingEnabled: boolean
|
||
|
|
audienceVotingWeight: number // 0-100, percentage weight
|
||
|
|
juryVotingWeight: number // complement of audience weight (calculated)
|
||
|
|
audienceVotingMode: 'PER_PROJECT' | 'FAVORITES' | 'CATEGORY_FAVORITES'
|
||
|
|
audienceMaxFavorites?: number // For FAVORITES mode
|
||
|
|
audienceRequireIdentification: boolean
|
||
|
|
audienceAntiSpamMeasures: {
|
||
|
|
ipRateLimit: boolean // Limit votes per IP
|
||
|
|
deviceFingerprint: boolean // Track device ID
|
||
|
|
emailVerification: boolean // Require verified email
|
||
|
|
}
|
||
|
|
|
||
|
|
// Presentation timing
|
||
|
|
presentationDurationMinutes: number
|
||
|
|
qaDurationMinutes: number
|
||
|
|
|
||
|
|
// Deliberation
|
||
|
|
deliberationEnabled: boolean
|
||
|
|
deliberationDurationMinutes: number
|
||
|
|
deliberationAllowsVoteRevision: boolean // Can jury change votes during deliberation?
|
||
|
|
|
||
|
|
// Category windows
|
||
|
|
categoryWindowsEnabled: boolean // Separate windows per category
|
||
|
|
categoryWindows: CategoryWindow[]
|
||
|
|
|
||
|
|
// Results display
|
||
|
|
showLiveResults: boolean // Real-time leaderboard
|
||
|
|
showLiveScores: boolean // Show actual scores vs just rankings
|
||
|
|
anonymizeJuryVotes: boolean // Hide individual jury votes from audience
|
||
|
|
requireAllJuryVotes: boolean // Voting can't end until all jury members vote
|
||
|
|
|
||
|
|
// Override controls
|
||
|
|
adminCanOverrideVotes: boolean
|
||
|
|
adminCanAdjustWeights: boolean // Mid-ceremony weight adjustment
|
||
|
|
|
||
|
|
// Presentation order
|
||
|
|
presentationOrderMode: 'MANUAL' | 'RANDOM' | 'SCORE_BASED' | 'CATEGORY_SPLIT'
|
||
|
|
}
|
||
|
|
|
||
|
|
type CategoryWindow = {
|
||
|
|
category: 'STARTUP' | 'BUSINESS_CONCEPT'
|
||
|
|
projectOrder: string[] // Ordered project IDs
|
||
|
|
startTime?: string // Scheduled start (ISO 8601)
|
||
|
|
endTime?: string // Scheduled end
|
||
|
|
deliberationMinutes?: number // Override global deliberation duration
|
||
|
|
}
|
||
|
|
|
||
|
|
type LiveVotingCriterion = {
|
||
|
|
id: string
|
||
|
|
label: string
|
||
|
|
description?: string
|
||
|
|
scale: number // 1-10, 1-5, etc.
|
||
|
|
weight: number // Sum to 1.0 across all criteria
|
||
|
|
}
|
||
|
|
```
|
||
|
|
|
||
|
|
### Zod Validation Schema
|
||
|
|
|
||
|
|
```typescript
|
||
|
|
import { z } from 'zod'
|
||
|
|
|
||
|
|
const CategoryWindowSchema = z.object({
|
||
|
|
category: z.enum(['STARTUP', 'BUSINESS_CONCEPT']),
|
||
|
|
projectOrder: z.array(z.string()),
|
||
|
|
startTime: z.string().datetime().optional(),
|
||
|
|
endTime: z.string().datetime().optional(),
|
||
|
|
deliberationMinutes: z.number().int().min(0).max(120).optional(),
|
||
|
|
})
|
||
|
|
|
||
|
|
const LiveVotingCriterionSchema = z.object({
|
||
|
|
id: z.string(),
|
||
|
|
label: z.string().min(1).max(100),
|
||
|
|
description: z.string().max(500).optional(),
|
||
|
|
scale: z.number().int().min(1).max(100),
|
||
|
|
weight: z.number().min(0).max(1),
|
||
|
|
})
|
||
|
|
|
||
|
|
export const LiveFinalConfigSchema = z.object({
|
||
|
|
// Jury
|
||
|
|
juryGroupId: z.string(),
|
||
|
|
|
||
|
|
// Voting mode
|
||
|
|
votingMode: z.enum(['NUMERIC', 'RANKING', 'BINARY']),
|
||
|
|
|
||
|
|
// Numeric mode settings
|
||
|
|
numericScale: z.object({
|
||
|
|
min: z.number().int().default(1),
|
||
|
|
max: z.number().int().default(10),
|
||
|
|
allowDecimals: z.boolean().default(false),
|
||
|
|
}).optional(),
|
||
|
|
|
||
|
|
// Criteria
|
||
|
|
criteriaEnabled: z.boolean().optional(),
|
||
|
|
criteriaJson: z.array(LiveVotingCriterionSchema).optional(),
|
||
|
|
importFromEvalForm: z.string().optional(),
|
||
|
|
|
||
|
|
// Ranking
|
||
|
|
rankingSettings: z.object({
|
||
|
|
maxRankedProjects: z.number().int().min(1).max(20),
|
||
|
|
pointsSystem: z.enum(['DESCENDING', 'BORDA']),
|
||
|
|
}).optional(),
|
||
|
|
|
||
|
|
// Binary
|
||
|
|
binaryLabels: z.object({
|
||
|
|
yes: z.string().default('Finalist'),
|
||
|
|
no: z.string().default('Not Selected'),
|
||
|
|
}).optional(),
|
||
|
|
|
||
|
|
// Audience
|
||
|
|
audienceVotingEnabled: z.boolean(),
|
||
|
|
audienceVotingWeight: z.number().min(0).max(100),
|
||
|
|
juryVotingWeight: z.number().min(0).max(100),
|
||
|
|
audienceVotingMode: z.enum(['PER_PROJECT', 'FAVORITES', 'CATEGORY_FAVORITES']),
|
||
|
|
audienceMaxFavorites: z.number().int().min(1).max(20).optional(),
|
||
|
|
audienceRequireIdentification: z.boolean(),
|
||
|
|
audienceAntiSpamMeasures: z.object({
|
||
|
|
ipRateLimit: z.boolean(),
|
||
|
|
deviceFingerprint: z.boolean(),
|
||
|
|
emailVerification: z.boolean(),
|
||
|
|
}),
|
||
|
|
|
||
|
|
// Timing
|
||
|
|
presentationDurationMinutes: z.number().int().min(1).max(60),
|
||
|
|
qaDurationMinutes: z.number().int().min(0).max(30),
|
||
|
|
|
||
|
|
// Deliberation
|
||
|
|
deliberationEnabled: z.boolean(),
|
||
|
|
deliberationDurationMinutes: z.number().int().min(0).max(120),
|
||
|
|
deliberationAllowsVoteRevision: z.boolean(),
|
||
|
|
|
||
|
|
// Category windows
|
||
|
|
categoryWindowsEnabled: z.boolean(),
|
||
|
|
categoryWindows: z.array(CategoryWindowSchema),
|
||
|
|
|
||
|
|
// Results
|
||
|
|
showLiveResults: z.boolean(),
|
||
|
|
showLiveScores: z.boolean(),
|
||
|
|
anonymizeJuryVotes: z.boolean(),
|
||
|
|
requireAllJuryVotes: z.boolean(),
|
||
|
|
|
||
|
|
// Overrides
|
||
|
|
adminCanOverrideVotes: z.boolean(),
|
||
|
|
adminCanAdjustWeights: z.boolean(),
|
||
|
|
|
||
|
|
// Presentation order
|
||
|
|
presentationOrderMode: z.enum(['MANUAL', 'RANDOM', 'SCORE_BASED', 'CATEGORY_SPLIT']),
|
||
|
|
}).refine(
|
||
|
|
(data) => {
|
||
|
|
// Ensure weights sum to 100
|
||
|
|
return data.audienceVotingWeight + data.juryVotingWeight === 100
|
||
|
|
},
|
||
|
|
{ message: 'Audience and jury weights must sum to 100%' }
|
||
|
|
).refine(
|
||
|
|
(data) => {
|
||
|
|
// If criteria enabled, must have criteria
|
||
|
|
if (data.criteriaEnabled && (!data.criteriaJson || data.criteriaJson.length === 0)) {
|
||
|
|
return false
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
},
|
||
|
|
{ message: 'Criteria-based voting requires at least one criterion' }
|
||
|
|
).refine(
|
||
|
|
(data) => {
|
||
|
|
// Criteria weights must sum to 1.0
|
||
|
|
if (data.criteriaJson && data.criteriaJson.length > 0) {
|
||
|
|
const weightSum = data.criteriaJson.reduce((sum, c) => sum + c.weight, 0)
|
||
|
|
return Math.abs(weightSum - 1.0) < 0.01
|
||
|
|
}
|
||
|
|
return true
|
||
|
|
},
|
||
|
|
{ message: 'Criteria weights must sum to 1.0' }
|
||
|
|
)
|
||
|
|
```
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Stage Manager — Admin Controls
|
||
|
|
|
||
|
|
The **Stage Manager** is the admin control panel for orchestrating the live ceremony. It provides real-time control over presentation flow, voting windows, and emergency interventions.
|
||
|
|
|
||
|
|
### Ceremony State Machine
|
||
|
|
|
||
|
|
```
|
||
|
|
Ceremony State Flow:
|
||
|
|
NOT_STARTED → (start session) → IN_PROGRESS → (deliberation starts) → DELIBERATION → (voting ends) → COMPLETED
|
||
|
|
|
||
|
|
NOT_STARTED:
|
||
|
|
- Session created but not started
|
||
|
|
- Projects ordered (manual or automatic)
|
||
|
|
- Jury and audience links generated
|
||
|
|
- Stage manager can preview setup
|
||
|
|
|
||
|
|
IN_PROGRESS:
|
||
|
|
- Presentations ongoing
|
||
|
|
- Per-project state: WAITING → PRESENTING → Q_AND_A → VOTING → VOTED → SCORED
|
||
|
|
- Admin can pause, skip, reorder on the fly
|
||
|
|
|
||
|
|
DELIBERATION:
|
||
|
|
- Timer running for deliberation period
|
||
|
|
- Jury can discuss (optional chat/discussion interface)
|
||
|
|
- Votes may be revised (if deliberationAllowsVoteRevision=true)
|
||
|
|
- Admin can extend deliberation time
|
||
|
|
|
||
|
|
COMPLETED:
|
||
|
|
- All voting finished
|
||
|
|
- Results calculated
|
||
|
|
- Ceremony locked (or unlocked for result reveal)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Per-Project State
|
||
|
|
|
||
|
|
Each project in the live finals progresses through these states:
|
||
|
|
|
||
|
|
```
|
||
|
|
WAITING → Project queued, not yet presenting
|
||
|
|
PRESENTING → Presentation in progress (timer: presentationDurationMinutes)
|
||
|
|
Q_AND_A → Q&A session (timer: qaDurationMinutes)
|
||
|
|
VOTING → Voting window open (jury + audience can vote)
|
||
|
|
VOTED → Voting window closed, awaiting next action
|
||
|
|
SCORED → Scores calculated, moving to next project
|
||
|
|
SKIPPED → Admin skipped this project (emergency override)
|
||
|
|
```
|
||
|
|
|
||
|
|
### Stage Manager UI Controls
|
||
|
|
|
||
|
|
**ASCII Mockup:**
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
||
|
|
│ LIVE FINALS STAGE MANAGER Session: live-abc-123 │
|
||
|
|
├─────────────────────────────────────────────────────────────────────┤
|
||
|
|
│ Status: IN_PROGRESS Category: STARTUP Jury: Jury 3 (8/8) │
|
||
|
|
│ │
|
||
|
|
│ [Pause Ceremony] [End Session] [Emergency Stop] │
|
||
|
|
└─────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ CURRENT PROJECT ───────────────────────────────────────────────────┐
|
||
|
|
│ Project #3 of 6 (STARTUP) │
|
||
|
|
│ Title: "OceanSense AI" — Team: AquaTech Solutions │
|
||
|
|
│ │
|
||
|
|
│ State: VOTING │
|
||
|
|
│ ┌─ Presentation Timer ────┐ ┌─ Q&A Timer ─────┐ ┌─ Voting Timer ─┐│
|
||
|
|
│ │ Completed: 8:00 / 8:00 │ │ Completed: 5:00 │ │ 0:45 remaining ││
|
||
|
|
│ └─────────────────────────┘ └──────────────────┘ └────────────────┘│
|
||
|
|
│ │
|
||
|
|
│ Jury Votes: 6 / 8 (75%) │
|
||
|
|
│ [✓] Alice Chen [✓] Bob Martin [ ] Carol Davis │
|
||
|
|
│ [✓] David Lee [✓] Emma Wilson [ ] Frank Garcia │
|
||
|
|
│ [✓] Grace Huang [✓] Henry Thompson │
|
||
|
|
│ │
|
||
|
|
│ Audience Votes: 142 │
|
||
|
|
│ │
|
||
|
|
│ [Skip Project] [Reset Votes] [Extend Time +1min] [Next Project] │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ PROJECT QUEUE ─────────────────────────────────────────────────────┐
|
||
|
|
│ [✓] 1. AquaClean Tech (STARTUP) — Score: 8.2 (Completed) │
|
||
|
|
│ [✓] 2. BlueCarbon Solutions (STARTUP) — Score: 7.8 (Completed) │
|
||
|
|
│ [>] 3. OceanSense AI (STARTUP) — Voting in progress │
|
||
|
|
│ [ ] 4. MarineTech Innovations (STARTUP) — Waiting │
|
||
|
|
│ [ ] 5. CoralGuard (STARTUP) — Waiting │
|
||
|
|
│ [ ] 6. DeepSea Robotics (STARTUP) — Waiting │
|
||
|
|
│ │
|
||
|
|
│ [Reorder Queue] [Jump to Project...] [Add Project] │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ CATEGORY WINDOWS ──────────────────────────────────────────────────┐
|
||
|
|
│ Window 1: STARTUP (6 projects) │
|
||
|
|
│ Status: IN_PROGRESS (Project 3/6) │
|
||
|
|
│ Started: 2026-05-15 18:00:00 │
|
||
|
|
│ [Close Window & Start Deliberation] │
|
||
|
|
│ │
|
||
|
|
│ Window 2: BUSINESS_CONCEPT (6 projects) │
|
||
|
|
│ Status: WAITING │
|
||
|
|
│ Scheduled: 2026-05-15 19:30:00 │
|
||
|
|
│ [Start Window Early] │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ LIVE LEADERBOARD (STARTUP) ────────────────────────────────────────┐
|
||
|
|
│ Rank | Project | Jury Avg | Audience | Weighted | Gap │
|
||
|
|
│------+-----------------------+----------+----------+----------+------│
|
||
|
|
│ 1 | AquaClean Tech | 8.5 | 7.2 | 8.2 | — │
|
||
|
|
│ 2 | BlueCarbon Solutions | 8.0 | 7.4 | 7.8 | -0.4 │
|
||
|
|
│ 3 | OceanSense AI | — | 6.8 | — | — │
|
||
|
|
│ 4 | MarineTech Innov. | — | — | — | — │
|
||
|
|
│ 5 | CoralGuard | — | — | — | — │
|
||
|
|
│ 6 | DeepSea Robotics | — | — | — | — │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ CEREMONY LOG ──────────────────────────────────────────────────────┐
|
||
|
|
│ 18:43:22 — Voting opened for "OceanSense AI" │
|
||
|
|
│ 18:42:10 — Q&A period ended │
|
||
|
|
│ 18:37:05 — Q&A period started │
|
||
|
|
│ 18:29:00 — Presentation started: "OceanSense AI" │
|
||
|
|
│ 18:28:45 — Voting closed for "BlueCarbon Solutions" │
|
||
|
|
│ 18:27:30 — All jury votes received for "BlueCarbon Solutions" │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ ADMIN OVERRIDE PANEL ──────────────────────────────────────────────┐
|
||
|
|
│ [Override Individual Vote...] [Adjust Weights...] [Reset Session] │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
### Stage Manager Features
|
||
|
|
|
||
|
|
**Core controls:**
|
||
|
|
1. **Session Management**
|
||
|
|
- Start session (initialize cursor, generate jury/audience links)
|
||
|
|
- Pause ceremony (freeze all timers, block votes)
|
||
|
|
- Resume ceremony
|
||
|
|
- End session (lock results, trigger CONFIRMATION round)
|
||
|
|
|
||
|
|
2. **Project Navigation**
|
||
|
|
- Jump to specific project
|
||
|
|
- Skip project (emergency)
|
||
|
|
- Reorder queue (drag-and-drop or modal)
|
||
|
|
- Add project mid-ceremony (rare edge case)
|
||
|
|
|
||
|
|
3. **Timer Controls**
|
||
|
|
- Start presentation timer
|
||
|
|
- Start Q&A timer
|
||
|
|
- Start voting timer
|
||
|
|
- Extend timer (+1 min, +5 min)
|
||
|
|
- Manual timer override
|
||
|
|
|
||
|
|
4. **Voting Window Management**
|
||
|
|
- Open voting for current project
|
||
|
|
- Close voting early
|
||
|
|
- Require all jury votes before closing
|
||
|
|
- Reset votes (emergency undo)
|
||
|
|
|
||
|
|
5. **Category Window Controls**
|
||
|
|
- Open category window (STARTUP or BUSINESS_CONCEPT)
|
||
|
|
- Close category window
|
||
|
|
- Start deliberation period
|
||
|
|
- Advance to next category
|
||
|
|
|
||
|
|
6. **Emergency Controls**
|
||
|
|
- Skip project
|
||
|
|
- Reset individual vote
|
||
|
|
- Reset all votes for project
|
||
|
|
- Pause ceremony (emergency)
|
||
|
|
- Force end session
|
||
|
|
|
||
|
|
7. **Override Controls** (if `adminCanOverrideVotes=true`):
|
||
|
|
- Override individual jury vote
|
||
|
|
- Adjust audience/jury weights mid-ceremony
|
||
|
|
- Manual score adjustment
|
||
|
|
|
||
|
|
8. **Real-Time Monitoring**
|
||
|
|
- Live vote count (jury + audience)
|
||
|
|
- Missing jury votes indicator
|
||
|
|
- Audience voter count
|
||
|
|
- Leaderboard (if `showLiveResults=true`)
|
||
|
|
- Ceremony event log
|
||
|
|
|
||
|
|
---
|
||
|
|
|
||
|
|
## Jury 3 Voting Experience
|
||
|
|
|
||
|
|
### Jury Dashboard
|
||
|
|
|
||
|
|
**ASCII Mockup:**
|
||
|
|
```
|
||
|
|
┌─────────────────────────────────────────────────────────────────────┐
|
||
|
|
│ LIVE FINALS VOTING — Jury 3 Alice Chen │
|
||
|
|
├─────────────────────────────────────────────────────────────────────┤
|
||
|
|
│ Status: VOTING IN PROGRESS │
|
||
|
|
│ Category: STARTUP │
|
||
|
|
│ │
|
||
|
|
│ [View All Finalists] [Results Dashboard] [Jury Discussion] │
|
||
|
|
└─────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ CURRENT PROJECT ───────────────────────────────────────────────────┐
|
||
|
|
│ Project 3 of 6 │
|
||
|
|
│ │
|
||
|
|
│ OceanSense AI │
|
||
|
|
│ Team: AquaTech Solutions │
|
||
|
|
│ Category: STARTUP (Marine Technology) │
|
||
|
|
│ │
|
||
|
|
│ Description: │
|
||
|
|
│ AI-powered ocean monitoring platform that detects pollution events │
|
||
|
|
│ in real-time using satellite imagery and underwater sensors. │
|
||
|
|
│ │
|
||
|
|
│ ┌─ Documents ──────────────────────────────────────────────────┐ │
|
||
|
|
│ │ Round 1 Docs: │ │
|
||
|
|
│ │ • Executive Summary.pdf │ │
|
||
|
|
│ │ • Business Plan.pdf │ │
|
||
|
|
│ │ │ │
|
||
|
|
│ │ Round 2 Docs (Semi-finalist): │ │
|
||
|
|
│ │ • Updated Business Plan.pdf │ │
|
||
|
|
│ │ • Pitch Video.mp4 │ │
|
||
|
|
│ │ • Technical Whitepaper.pdf │ │
|
||
|
|
│ └───────────────────────────────────────────────────────────────┘ │
|
||
|
|
│ │
|
||
|
|
│ Voting closes in: 0:45 │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ VOTING PANEL (Numeric Mode: 1-10) ─────────────────────────────────┐
|
||
|
|
│ │
|
||
|
|
│ How would you rate this project overall? │
|
||
|
|
│ │
|
||
|
|
│ ┌────────────────────────────────────────────────────────────┐ │
|
||
|
|
│ │ 1 2 3 4 5 6 7 8 9 10 │ │
|
||
|
|
│ │ ○ ○ ○ ○ ○ ○ ○ ● ○ ○ │ │
|
||
|
|
│ └────────────────────────────────────────────────────────────┘ │
|
||
|
|
│ │
|
||
|
|
│ Your score: 8 │
|
||
|
|
│ │
|
||
|
|
│ [Submit Vote] │
|
||
|
|
│ │
|
||
|
|
│ ⚠️ Votes cannot be changed after submission unless admin resets. │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
|
||
|
|
┌─ YOUR VOTES THIS SESSION ───────────────────────────────────────────┐
|
||
|
|
│ [✓] 1. AquaClean Tech — Score: 9 │
|
||
|
|
│ [✓] 2. BlueCarbon Solutions — Score: 8 │
|
||
|
|
│ [ ] 3. OceanSense AI — Not voted yet │
|
||
|
|
│ [ ] 4. MarineTech Innovations — Waiting │
|
||
|
|
│ [ ] 5. CoralGuard — Waiting │
|
||
|
|
│ [ ] 6. DeepSea Robotics — Waiting │
|
||
|
|
└───────────────────────────────────────────────────────────────────────┘
|
||
|
|
```
|
||
|
|
|
||
|
|
This is an extremely detailed 900+ line implementation document covering the Live Finals round type with complete technical specifications, UI mockups, API definitions, service functions, edge cases, and integration points. The document provides a comprehensive guide for implementing the live ceremony functionality in the redesigned MOPC architecture.
|