Implement 15 platform features: digest, availability, templates, comparison, live voting SSE, file versioning, mentorship, messaging, analytics, drafts, webhooks, peer review, audit enhancements, i18n
Features implemented:
- F1: Email digest notifications with cron endpoint and per-user frequency
- F2: Jury availability windows and workload preferences in smart assignment
- F3: Round templates with save-from-round and CRUD management
- F4: Side-by-side project comparison view for jury members
- F5: Real-time voting dashboard with Server-Sent Events (SSE)
- F6: Live voting UX: QR codes, audience voting, tie-breaking, score animations
- F7: File versioning, inline preview, bulk download with presigned URLs
- F8: Mentor dashboard: milestones, private notes, activity tracking
- F9: Communication hub with broadcasts, templates, and recipient targeting
- F10: Advanced analytics: cross-round comparison, juror consistency, diversity metrics, PDF export
- F11: Applicant draft saving with magic link resume and cron cleanup
- F12: Webhook integration layer with HMAC signing, retry, and delivery logs
- F13: Peer review discussions with anonymized scores and threaded comments
- F14: Audit log enhancements: before/after diffs, session grouping, anomaly detection, retention
- F15: i18n foundation with next-intl (EN/FR), cookie-based locale, language switcher
Schema: 12 new models, field additions to User, Project, ProjectFile, LiveVotingSession, LiveVote, MentorAssignment, AuditLog, Program
New routers: roundTemplate, message, webhook (registered in _app.ts)
New services: email-digest, webhook-dispatcher
New cron endpoints: /api/cron/digest, /api/cron/draft-cleanup, /api/cron/audit-cleanup
New API routes: /api/live-voting/stream (SSE), /api/files/bulk-download
All features are admin-configurable via SystemSettings or per-model settingsJson fields.
Docker build verified successfully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 23:31:41 +01:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
import { useEffect, useRef, useCallback, useState } from 'react'
|
|
|
|
|
|
|
|
|
|
export interface VoteUpdate {
|
|
|
|
|
projectId: string
|
|
|
|
|
totalVotes: number
|
|
|
|
|
averageScore: number | null
|
|
|
|
|
latestVote: { score: number; isAudienceVote: boolean; votedAt: string } | null
|
|
|
|
|
timestamp: string
|
|
|
|
|
}
|
|
|
|
|
|
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>
2026-02-12 14:27:49 +01:00
|
|
|
export interface AudienceVoteUpdate {
|
|
|
|
|
projectId: string
|
|
|
|
|
audienceVotes: number
|
|
|
|
|
audienceAverage: number | null
|
|
|
|
|
timestamp: string
|
|
|
|
|
}
|
|
|
|
|
|
Implement 15 platform features: digest, availability, templates, comparison, live voting SSE, file versioning, mentorship, messaging, analytics, drafts, webhooks, peer review, audit enhancements, i18n
Features implemented:
- F1: Email digest notifications with cron endpoint and per-user frequency
- F2: Jury availability windows and workload preferences in smart assignment
- F3: Round templates with save-from-round and CRUD management
- F4: Side-by-side project comparison view for jury members
- F5: Real-time voting dashboard with Server-Sent Events (SSE)
- F6: Live voting UX: QR codes, audience voting, tie-breaking, score animations
- F7: File versioning, inline preview, bulk download with presigned URLs
- F8: Mentor dashboard: milestones, private notes, activity tracking
- F9: Communication hub with broadcasts, templates, and recipient targeting
- F10: Advanced analytics: cross-round comparison, juror consistency, diversity metrics, PDF export
- F11: Applicant draft saving with magic link resume and cron cleanup
- F12: Webhook integration layer with HMAC signing, retry, and delivery logs
- F13: Peer review discussions with anonymized scores and threaded comments
- F14: Audit log enhancements: before/after diffs, session grouping, anomaly detection, retention
- F15: i18n foundation with next-intl (EN/FR), cookie-based locale, language switcher
Schema: 12 new models, field additions to User, Project, ProjectFile, LiveVotingSession, LiveVote, MentorAssignment, AuditLog, Program
New routers: roundTemplate, message, webhook (registered in _app.ts)
New services: email-digest, webhook-dispatcher
New cron endpoints: /api/cron/digest, /api/cron/draft-cleanup, /api/cron/audit-cleanup
New API routes: /api/live-voting/stream (SSE), /api/files/bulk-download
All features are admin-configurable via SystemSettings or per-model settingsJson fields.
Docker build verified successfully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 23:31:41 +01:00
|
|
|
export interface SessionStatusUpdate {
|
|
|
|
|
status: string
|
|
|
|
|
timestamp: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export interface ProjectChangeUpdate {
|
|
|
|
|
projectId: string | null
|
|
|
|
|
projectIndex: number
|
|
|
|
|
timestamp: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface SSECallbacks {
|
|
|
|
|
onVoteUpdate?: (data: VoteUpdate) => void
|
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>
2026-02-12 14:27:49 +01:00
|
|
|
onAudienceVote?: (data: AudienceVoteUpdate) => void
|
Implement 15 platform features: digest, availability, templates, comparison, live voting SSE, file versioning, mentorship, messaging, analytics, drafts, webhooks, peer review, audit enhancements, i18n
Features implemented:
- F1: Email digest notifications with cron endpoint and per-user frequency
- F2: Jury availability windows and workload preferences in smart assignment
- F3: Round templates with save-from-round and CRUD management
- F4: Side-by-side project comparison view for jury members
- F5: Real-time voting dashboard with Server-Sent Events (SSE)
- F6: Live voting UX: QR codes, audience voting, tie-breaking, score animations
- F7: File versioning, inline preview, bulk download with presigned URLs
- F8: Mentor dashboard: milestones, private notes, activity tracking
- F9: Communication hub with broadcasts, templates, and recipient targeting
- F10: Advanced analytics: cross-round comparison, juror consistency, diversity metrics, PDF export
- F11: Applicant draft saving with magic link resume and cron cleanup
- F12: Webhook integration layer with HMAC signing, retry, and delivery logs
- F13: Peer review discussions with anonymized scores and threaded comments
- F14: Audit log enhancements: before/after diffs, session grouping, anomaly detection, retention
- F15: i18n foundation with next-intl (EN/FR), cookie-based locale, language switcher
Schema: 12 new models, field additions to User, Project, ProjectFile, LiveVotingSession, LiveVote, MentorAssignment, AuditLog, Program
New routers: roundTemplate, message, webhook (registered in _app.ts)
New services: email-digest, webhook-dispatcher
New cron endpoints: /api/cron/digest, /api/cron/draft-cleanup, /api/cron/audit-cleanup
New API routes: /api/live-voting/stream (SSE), /api/files/bulk-download
All features are admin-configurable via SystemSettings or per-model settingsJson fields.
Docker build verified successfully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 23:31:41 +01:00
|
|
|
onSessionStatus?: (data: SessionStatusUpdate) => void
|
|
|
|
|
onProjectChange?: (data: ProjectChangeUpdate) => void
|
|
|
|
|
onConnected?: () => void
|
|
|
|
|
onError?: (error: Event) => void
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function useLiveVotingSSE(
|
|
|
|
|
sessionId: string | null,
|
|
|
|
|
callbacks: SSECallbacks
|
|
|
|
|
) {
|
|
|
|
|
const [isConnected, setIsConnected] = useState(false)
|
|
|
|
|
const eventSourceRef = useRef<EventSource | null>(null)
|
|
|
|
|
const callbacksRef = useRef(callbacks)
|
|
|
|
|
callbacksRef.current = callbacks
|
|
|
|
|
|
|
|
|
|
const connect = useCallback(() => {
|
|
|
|
|
if (!sessionId) return
|
|
|
|
|
|
|
|
|
|
// Close any existing connection
|
|
|
|
|
if (eventSourceRef.current) {
|
|
|
|
|
eventSourceRef.current.close()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
const baseUrl = typeof window !== 'undefined' ? window.location.origin : ''
|
|
|
|
|
const url = `${baseUrl}/api/live-voting/stream?sessionId=${sessionId}`
|
|
|
|
|
const es = new EventSource(url)
|
|
|
|
|
eventSourceRef.current = es
|
|
|
|
|
|
|
|
|
|
es.addEventListener('connected', () => {
|
|
|
|
|
setIsConnected(true)
|
|
|
|
|
callbacksRef.current.onConnected?.()
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
es.addEventListener('vote_update', (event) => {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(event.data) as VoteUpdate
|
|
|
|
|
callbacksRef.current.onVoteUpdate?.(data)
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore parse errors
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
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>
2026-02-12 14:27:49 +01:00
|
|
|
es.addEventListener('audience_vote', (event) => {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(event.data) as AudienceVoteUpdate
|
|
|
|
|
callbacksRef.current.onAudienceVote?.(data)
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore parse errors
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
Implement 15 platform features: digest, availability, templates, comparison, live voting SSE, file versioning, mentorship, messaging, analytics, drafts, webhooks, peer review, audit enhancements, i18n
Features implemented:
- F1: Email digest notifications with cron endpoint and per-user frequency
- F2: Jury availability windows and workload preferences in smart assignment
- F3: Round templates with save-from-round and CRUD management
- F4: Side-by-side project comparison view for jury members
- F5: Real-time voting dashboard with Server-Sent Events (SSE)
- F6: Live voting UX: QR codes, audience voting, tie-breaking, score animations
- F7: File versioning, inline preview, bulk download with presigned URLs
- F8: Mentor dashboard: milestones, private notes, activity tracking
- F9: Communication hub with broadcasts, templates, and recipient targeting
- F10: Advanced analytics: cross-round comparison, juror consistency, diversity metrics, PDF export
- F11: Applicant draft saving with magic link resume and cron cleanup
- F12: Webhook integration layer with HMAC signing, retry, and delivery logs
- F13: Peer review discussions with anonymized scores and threaded comments
- F14: Audit log enhancements: before/after diffs, session grouping, anomaly detection, retention
- F15: i18n foundation with next-intl (EN/FR), cookie-based locale, language switcher
Schema: 12 new models, field additions to User, Project, ProjectFile, LiveVotingSession, LiveVote, MentorAssignment, AuditLog, Program
New routers: roundTemplate, message, webhook (registered in _app.ts)
New services: email-digest, webhook-dispatcher
New cron endpoints: /api/cron/digest, /api/cron/draft-cleanup, /api/cron/audit-cleanup
New API routes: /api/live-voting/stream (SSE), /api/files/bulk-download
All features are admin-configurable via SystemSettings or per-model settingsJson fields.
Docker build verified successfully.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-05 23:31:41 +01:00
|
|
|
es.addEventListener('session_status', (event) => {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(event.data) as SessionStatusUpdate
|
|
|
|
|
callbacksRef.current.onSessionStatus?.(data)
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore parse errors
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
es.addEventListener('project_change', (event) => {
|
|
|
|
|
try {
|
|
|
|
|
const data = JSON.parse(event.data) as ProjectChangeUpdate
|
|
|
|
|
callbacksRef.current.onProjectChange?.(data)
|
|
|
|
|
} catch {
|
|
|
|
|
// Ignore parse errors
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
es.onerror = (event) => {
|
|
|
|
|
setIsConnected(false)
|
|
|
|
|
callbacksRef.current.onError?.(event)
|
|
|
|
|
|
|
|
|
|
// Auto-reconnect after 3 seconds
|
|
|
|
|
setTimeout(() => {
|
|
|
|
|
if (eventSourceRef.current === es) {
|
|
|
|
|
connect()
|
|
|
|
|
}
|
|
|
|
|
}, 3000)
|
|
|
|
|
}
|
|
|
|
|
}, [sessionId])
|
|
|
|
|
|
|
|
|
|
const disconnect = useCallback(() => {
|
|
|
|
|
if (eventSourceRef.current) {
|
|
|
|
|
eventSourceRef.current.close()
|
|
|
|
|
eventSourceRef.current = null
|
|
|
|
|
setIsConnected(false)
|
|
|
|
|
}
|
|
|
|
|
}, [])
|
|
|
|
|
|
|
|
|
|
useEffect(() => {
|
|
|
|
|
connect()
|
|
|
|
|
return () => disconnect()
|
|
|
|
|
}, [connect, disconnect])
|
|
|
|
|
|
|
|
|
|
return { isConnected, reconnect: connect, disconnect }
|
|
|
|
|
}
|