2026-01-30 13:41:32 +01:00
|
|
|
'use client'
|
|
|
|
|
|
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
|
|
|
import { BookOpen, ClipboardList, GitCompare, Home } from 'lucide-react'
|
2026-02-05 21:09:06 +01:00
|
|
|
import { RoleNav, type NavItem, type RoleNavUser } from '@/components/layouts/role-nav'
|
2026-02-08 14:37:32 +01:00
|
|
|
import { trpc } from '@/lib/trpc/client'
|
|
|
|
|
import { Badge } from '@/components/ui/badge'
|
2026-01-30 13:41:32 +01:00
|
|
|
|
2026-02-05 21:09:06 +01:00
|
|
|
const navigation: NavItem[] = [
|
2026-01-30 13:41:32 +01:00
|
|
|
{
|
|
|
|
|
name: 'Dashboard',
|
2026-02-05 21:09:06 +01:00
|
|
|
href: '/jury',
|
2026-01-30 13:41:32 +01:00
|
|
|
icon: Home,
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
name: 'My Assignments',
|
2026-02-05 21:09:06 +01:00
|
|
|
href: '/jury/assignments',
|
2026-01-30 13:41:32 +01:00
|
|
|
icon: ClipboardList,
|
|
|
|
|
},
|
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
|
|
|
{
|
|
|
|
|
name: 'Compare',
|
|
|
|
|
href: '/jury/compare',
|
|
|
|
|
icon: GitCompare,
|
|
|
|
|
},
|
2026-01-30 13:41:32 +01:00
|
|
|
{
|
|
|
|
|
name: 'Learning Hub',
|
2026-02-05 21:09:06 +01:00
|
|
|
href: '/jury/learning',
|
2026-01-30 13:41:32 +01:00
|
|
|
icon: BookOpen,
|
|
|
|
|
},
|
|
|
|
|
]
|
|
|
|
|
|
2026-02-05 21:09:06 +01:00
|
|
|
interface JuryNavProps {
|
|
|
|
|
user: RoleNavUser
|
|
|
|
|
}
|
2026-01-30 13:41:32 +01:00
|
|
|
|
2026-02-08 14:37:32 +01:00
|
|
|
function RemainingBadge() {
|
|
|
|
|
const { data: assignments } = trpc.assignment.myAssignments.useQuery(
|
|
|
|
|
{},
|
|
|
|
|
{ refetchInterval: 60000 }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
if (!assignments) return null
|
|
|
|
|
|
|
|
|
|
const now = new Date()
|
|
|
|
|
const remaining = (assignments as Array<{
|
|
|
|
|
round: { status: string; votingStartAt: Date | null; votingEndAt: Date | null }
|
|
|
|
|
evaluation: { status: string } | null
|
|
|
|
|
}>).filter((a) => {
|
|
|
|
|
const isActive =
|
|
|
|
|
a.round.status === 'ACTIVE' &&
|
|
|
|
|
a.round.votingStartAt &&
|
|
|
|
|
a.round.votingEndAt &&
|
|
|
|
|
new Date(a.round.votingStartAt) <= now &&
|
|
|
|
|
new Date(a.round.votingEndAt) >= now
|
|
|
|
|
const isIncomplete = !a.evaluation || a.evaluation.status !== 'SUBMITTED'
|
|
|
|
|
return isActive && isIncomplete
|
|
|
|
|
}).length
|
|
|
|
|
|
|
|
|
|
if (remaining === 0) return null
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Badge variant="secondary" className="text-xs font-medium">
|
|
|
|
|
{remaining} remaining
|
|
|
|
|
</Badge>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-02-05 21:09:06 +01:00
|
|
|
export function JuryNav({ user }: JuryNavProps) {
|
2026-01-30 13:41:32 +01:00
|
|
|
return (
|
2026-02-05 21:09:06 +01:00
|
|
|
<RoleNav
|
|
|
|
|
navigation={navigation}
|
|
|
|
|
roleName="Jury"
|
|
|
|
|
user={user}
|
|
|
|
|
basePath="/jury"
|
2026-02-08 14:37:32 +01:00
|
|
|
statusBadge={<RemainingBadge />}
|
2026-02-05 21:09:06 +01:00
|
|
|
/>
|
2026-01-30 13:41:32 +01:00
|
|
|
)
|
|
|
|
|
}
|