2026-01-30 13:41:32 +01:00
|
|
|
'use client'
|
|
|
|
|
|
|
|
|
|
import { useState } from 'react'
|
|
|
|
|
import { trpc } from '@/lib/trpc/client'
|
|
|
|
|
import { Button } from '@/components/ui/button'
|
|
|
|
|
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'
|
|
|
|
|
import { Badge } from '@/components/ui/badge'
|
|
|
|
|
import { Skeleton } from '@/components/ui/skeleton'
|
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 {
|
|
|
|
|
Dialog,
|
|
|
|
|
DialogContent,
|
|
|
|
|
DialogHeader,
|
|
|
|
|
DialogTitle,
|
|
|
|
|
DialogTrigger,
|
|
|
|
|
} from '@/components/ui/dialog'
|
2026-01-30 13:41:32 +01:00
|
|
|
import {
|
|
|
|
|
FileText,
|
|
|
|
|
Video,
|
|
|
|
|
File,
|
|
|
|
|
Download,
|
|
|
|
|
ExternalLink,
|
|
|
|
|
Play,
|
|
|
|
|
FileImage,
|
|
|
|
|
Loader2,
|
|
|
|
|
AlertCircle,
|
|
|
|
|
X,
|
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
|
|
|
History,
|
|
|
|
|
PackageOpen,
|
2026-01-30 13:41:32 +01:00
|
|
|
} from 'lucide-react'
|
|
|
|
|
import { cn } from '@/lib/utils'
|
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 { toast } from 'sonner'
|
2026-01-30 13:41:32 +01:00
|
|
|
|
|
|
|
|
interface ProjectFile {
|
|
|
|
|
id: string
|
|
|
|
|
fileType: 'EXEC_SUMMARY' | 'PRESENTATION' | 'VIDEO' | 'OTHER' | 'BUSINESS_PLAN' | 'VIDEO_PITCH' | 'SUPPORTING_DOC'
|
|
|
|
|
fileName: string
|
|
|
|
|
mimeType: string
|
|
|
|
|
size: number
|
|
|
|
|
bucket: string
|
|
|
|
|
objectKey: 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
|
|
|
version?: number
|
2026-01-30 13:41:32 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
interface FileViewerProps {
|
|
|
|
|
files: ProjectFile[]
|
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
|
|
|
projectId?: string
|
2026-01-30 13:41:32 +01:00
|
|
|
className?: string
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function formatFileSize(bytes: number): string {
|
|
|
|
|
if (bytes === 0) return '0 Bytes'
|
|
|
|
|
const k = 1024
|
|
|
|
|
const sizes = ['Bytes', 'KB', 'MB', 'GB']
|
|
|
|
|
const i = Math.floor(Math.log(bytes) / Math.log(k))
|
|
|
|
|
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getFileIcon(fileType: string, mimeType: string) {
|
|
|
|
|
if (mimeType.startsWith('video/')) return Video
|
|
|
|
|
if (mimeType.startsWith('image/')) return FileImage
|
|
|
|
|
if (mimeType === 'application/pdf') return FileText
|
|
|
|
|
if (fileType === 'EXEC_SUMMARY' || fileType === 'PRESENTATION') return FileText
|
|
|
|
|
if (fileType === 'VIDEO') return Video
|
|
|
|
|
return File
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function getFileTypeLabel(fileType: string) {
|
|
|
|
|
switch (fileType) {
|
|
|
|
|
case 'EXEC_SUMMARY':
|
|
|
|
|
return 'Executive Summary'
|
|
|
|
|
case 'PRESENTATION':
|
|
|
|
|
return 'Presentation'
|
|
|
|
|
case 'VIDEO':
|
|
|
|
|
return 'Video'
|
|
|
|
|
case 'BUSINESS_PLAN':
|
|
|
|
|
return 'Business Plan'
|
|
|
|
|
case 'VIDEO_PITCH':
|
|
|
|
|
return 'Video Pitch'
|
|
|
|
|
case 'SUPPORTING_DOC':
|
|
|
|
|
return 'Supporting Document'
|
|
|
|
|
default:
|
|
|
|
|
return 'Attachment'
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
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 function FileViewer({ files, projectId, className }: FileViewerProps) {
|
2026-01-30 13:41:32 +01:00
|
|
|
if (files.length === 0) {
|
|
|
|
|
return (
|
|
|
|
|
<Card className={className}>
|
|
|
|
|
<CardContent className="flex flex-col items-center justify-center py-8 text-center">
|
|
|
|
|
<File className="h-12 w-12 text-muted-foreground/50" />
|
|
|
|
|
<p className="mt-2 font-medium">No files attached</p>
|
|
|
|
|
<p className="text-sm text-muted-foreground">
|
|
|
|
|
This project has no files uploaded yet
|
|
|
|
|
</p>
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Sort files by type order
|
|
|
|
|
const sortOrder = ['EXEC_SUMMARY', 'BUSINESS_PLAN', 'PRESENTATION', 'VIDEO', 'VIDEO_PITCH', 'SUPPORTING_DOC', 'OTHER']
|
|
|
|
|
const sortedFiles = [...files].sort(
|
|
|
|
|
(a, b) => sortOrder.indexOf(a.fileType) - sortOrder.indexOf(b.fileType)
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Card className={className}>
|
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
|
|
|
<CardHeader className="flex flex-row items-center justify-between space-y-0">
|
2026-01-30 13:41:32 +01:00
|
|
|
<CardTitle className="text-lg">Project Files</CardTitle>
|
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
|
|
|
{projectId && files.length > 1 && (
|
|
|
|
|
<BulkDownloadButton projectId={projectId} fileIds={files.map((f) => f.id)} />
|
|
|
|
|
)}
|
2026-01-30 13:41:32 +01:00
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-3">
|
|
|
|
|
{sortedFiles.map((file) => (
|
|
|
|
|
<FileItem key={file.id} file={file} />
|
|
|
|
|
))}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function FileItem({ file }: { file: ProjectFile }) {
|
|
|
|
|
const [showPreview, setShowPreview] = useState(false)
|
|
|
|
|
const Icon = getFileIcon(file.fileType, file.mimeType)
|
|
|
|
|
|
|
|
|
|
const { data: urlData, isLoading: isLoadingUrl } = trpc.file.getDownloadUrl.useQuery(
|
|
|
|
|
{ bucket: file.bucket, objectKey: file.objectKey },
|
|
|
|
|
{ enabled: showPreview }
|
|
|
|
|
)
|
|
|
|
|
|
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
|
|
|
const canPreview =
|
|
|
|
|
file.mimeType.startsWith('video/') ||
|
|
|
|
|
file.mimeType === 'application/pdf' ||
|
|
|
|
|
file.mimeType.startsWith('image/')
|
2026-01-30 13:41:32 +01:00
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
<div className="flex items-center gap-3 rounded-lg border p-3">
|
|
|
|
|
<div className="flex h-10 w-10 shrink-0 items-center justify-center rounded-lg bg-muted">
|
|
|
|
|
<Icon className="h-5 w-5 text-muted-foreground" />
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
<div className="flex-1 min-w-0">
|
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
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<p className="font-medium truncate">{file.fileName}</p>
|
|
|
|
|
{file.version != null && file.version > 1 && (
|
|
|
|
|
<Badge variant="outline" className="text-xs shrink-0">
|
|
|
|
|
v{file.version}
|
|
|
|
|
</Badge>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
2026-01-30 13:41:32 +01:00
|
|
|
<div className="flex items-center gap-2 text-sm text-muted-foreground">
|
|
|
|
|
<Badge variant="secondary" className="text-xs">
|
|
|
|
|
{getFileTypeLabel(file.fileType)}
|
|
|
|
|
</Badge>
|
|
|
|
|
<span>{formatFileSize(file.size)}</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
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
|
|
|
<div className="flex items-center gap-1">
|
|
|
|
|
{file.version != null && file.version > 1 && (
|
|
|
|
|
<VersionHistoryButton fileId={file.id} />
|
|
|
|
|
)}
|
2026-01-30 13:41:32 +01:00
|
|
|
{canPreview && (
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={() => setShowPreview(!showPreview)}
|
|
|
|
|
>
|
|
|
|
|
{showPreview ? (
|
|
|
|
|
<>
|
|
|
|
|
<X className="mr-2 h-4 w-4" />
|
|
|
|
|
Close
|
|
|
|
|
</>
|
|
|
|
|
) : (
|
|
|
|
|
<>
|
|
|
|
|
<Play className="mr-2 h-4 w-4" />
|
|
|
|
|
Preview
|
|
|
|
|
</>
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
)}
|
|
|
|
|
<FileDownloadButton file={file} />
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
{/* Preview area */}
|
|
|
|
|
{showPreview && (
|
|
|
|
|
<div className="rounded-lg border bg-muted/50 overflow-hidden">
|
|
|
|
|
{isLoadingUrl ? (
|
|
|
|
|
<div className="flex items-center justify-center py-8">
|
|
|
|
|
<Loader2 className="h-8 w-8 animate-spin text-muted-foreground" />
|
|
|
|
|
</div>
|
|
|
|
|
) : urlData?.url ? (
|
|
|
|
|
<FilePreview file={file} url={urlData.url} />
|
|
|
|
|
) : (
|
|
|
|
|
<div className="flex items-center justify-center py-8 text-muted-foreground">
|
|
|
|
|
<AlertCircle className="mr-2 h-4 w-4" />
|
|
|
|
|
Failed to load preview
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
function VersionHistoryButton({ fileId }: { fileId: string }) {
|
|
|
|
|
const [open, setOpen] = useState(false)
|
|
|
|
|
|
|
|
|
|
const { data: versions, isLoading } = trpc.file.getVersionHistory.useQuery(
|
|
|
|
|
{ fileId },
|
|
|
|
|
{ enabled: open }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Dialog open={open} onOpenChange={setOpen}>
|
|
|
|
|
<DialogTrigger asChild>
|
|
|
|
|
<Button variant="ghost" size="sm" title="Version history">
|
|
|
|
|
<History className="h-4 w-4" />
|
|
|
|
|
</Button>
|
|
|
|
|
</DialogTrigger>
|
|
|
|
|
<DialogContent className="max-w-md">
|
|
|
|
|
<DialogHeader>
|
|
|
|
|
<DialogTitle>Version History</DialogTitle>
|
|
|
|
|
</DialogHeader>
|
|
|
|
|
<div className="space-y-2 max-h-[60vh] overflow-y-auto">
|
|
|
|
|
{isLoading ? (
|
|
|
|
|
<div className="space-y-2">
|
|
|
|
|
{[1, 2, 3].map((i) => (
|
|
|
|
|
<Skeleton key={i} className="h-14 w-full" />
|
|
|
|
|
))}
|
|
|
|
|
</div>
|
|
|
|
|
) : versions && (versions as Array<Record<string, unknown>>).length > 0 ? (
|
|
|
|
|
(versions as Array<Record<string, unknown>>).map((v) => (
|
|
|
|
|
<div
|
|
|
|
|
key={String(v.id)}
|
|
|
|
|
className={cn(
|
|
|
|
|
'flex items-center gap-3 rounded-lg border p-3',
|
|
|
|
|
String(v.id) === fileId && 'border-primary bg-primary/5'
|
|
|
|
|
)}
|
|
|
|
|
>
|
|
|
|
|
<div className="flex-1 min-w-0">
|
|
|
|
|
<div className="flex items-center gap-2">
|
|
|
|
|
<p className="text-sm font-medium truncate">
|
|
|
|
|
{String(v.fileName)}
|
|
|
|
|
</p>
|
|
|
|
|
<Badge variant={String(v.id) === fileId ? 'default' : 'outline'} className="text-xs shrink-0">
|
|
|
|
|
v{String(v.version)}
|
|
|
|
|
</Badge>
|
|
|
|
|
</div>
|
|
|
|
|
<div className="flex items-center gap-2 text-xs text-muted-foreground">
|
|
|
|
|
<span>{formatFileSize(Number(v.size))}</span>
|
|
|
|
|
<span>
|
|
|
|
|
{v.createdAt
|
|
|
|
|
? new Date(String(v.createdAt)).toLocaleDateString(undefined, {
|
|
|
|
|
year: 'numeric',
|
|
|
|
|
month: 'short',
|
|
|
|
|
day: 'numeric',
|
|
|
|
|
hour: '2-digit',
|
|
|
|
|
minute: '2-digit',
|
|
|
|
|
})
|
|
|
|
|
: ''}
|
|
|
|
|
</span>
|
|
|
|
|
</div>
|
|
|
|
|
</div>
|
|
|
|
|
<VersionDownloadButton
|
|
|
|
|
bucket={String(v.bucket)}
|
|
|
|
|
objectKey={String(v.objectKey)}
|
|
|
|
|
/>
|
|
|
|
|
</div>
|
|
|
|
|
))
|
|
|
|
|
) : (
|
|
|
|
|
<p className="text-sm text-muted-foreground text-center py-4">
|
|
|
|
|
No version history available
|
|
|
|
|
</p>
|
|
|
|
|
)}
|
|
|
|
|
</div>
|
|
|
|
|
</DialogContent>
|
|
|
|
|
</Dialog>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function VersionDownloadButton({ bucket, objectKey }: { bucket: string; objectKey: string }) {
|
|
|
|
|
const [downloading, setDownloading] = useState(false)
|
|
|
|
|
|
|
|
|
|
const { refetch } = trpc.file.getDownloadUrl.useQuery(
|
|
|
|
|
{ bucket, objectKey },
|
|
|
|
|
{ enabled: false }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleDownload = async () => {
|
|
|
|
|
setDownloading(true)
|
|
|
|
|
try {
|
|
|
|
|
const result = await refetch()
|
|
|
|
|
if (result.data?.url) {
|
|
|
|
|
window.open(result.data.url, '_blank')
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
toast.error('Failed to get download URL')
|
|
|
|
|
} finally {
|
|
|
|
|
setDownloading(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Button
|
|
|
|
|
variant="ghost"
|
|
|
|
|
size="icon"
|
|
|
|
|
className="h-8 w-8 shrink-0"
|
|
|
|
|
onClick={handleDownload}
|
|
|
|
|
disabled={downloading}
|
|
|
|
|
aria-label="Download this version"
|
|
|
|
|
>
|
|
|
|
|
{downloading ? (
|
|
|
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
|
|
|
) : (
|
|
|
|
|
<Download className="h-4 w-4" />
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function BulkDownloadButton({ projectId, fileIds }: { projectId: string; fileIds: string[] }) {
|
|
|
|
|
const [downloading, setDownloading] = useState(false)
|
|
|
|
|
|
|
|
|
|
const { refetch } = trpc.file.getBulkDownloadUrls.useQuery(
|
|
|
|
|
{ projectId, fileIds },
|
|
|
|
|
{ enabled: false }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleBulkDownload = async () => {
|
|
|
|
|
setDownloading(true)
|
|
|
|
|
try {
|
|
|
|
|
const result = await refetch()
|
|
|
|
|
if (result.data && Array.isArray(result.data)) {
|
|
|
|
|
// Open each download URL with a small delay to avoid popup blocking
|
|
|
|
|
for (let i = 0; i < result.data.length; i++) {
|
|
|
|
|
const item = result.data[i] as { downloadUrl: string }
|
|
|
|
|
if (item.downloadUrl) {
|
|
|
|
|
// Use link element to trigger download without popup
|
|
|
|
|
const link = document.createElement('a')
|
|
|
|
|
link.href = item.downloadUrl
|
|
|
|
|
link.target = '_blank'
|
|
|
|
|
link.rel = 'noopener noreferrer'
|
|
|
|
|
document.body.appendChild(link)
|
|
|
|
|
link.click()
|
|
|
|
|
document.body.removeChild(link)
|
|
|
|
|
// Small delay between downloads
|
|
|
|
|
if (i < result.data.length - 1) {
|
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 300))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
toast.success(`Downloading ${result.data.length} files`)
|
|
|
|
|
}
|
|
|
|
|
} catch {
|
|
|
|
|
toast.error('Failed to download files')
|
|
|
|
|
} finally {
|
|
|
|
|
setDownloading(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={handleBulkDownload}
|
|
|
|
|
disabled={downloading}
|
|
|
|
|
>
|
|
|
|
|
{downloading ? (
|
|
|
|
|
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
|
|
|
) : (
|
|
|
|
|
<PackageOpen className="mr-2 h-4 w-4" />
|
|
|
|
|
)}
|
|
|
|
|
Download All
|
|
|
|
|
</Button>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 13:41:32 +01:00
|
|
|
function FileDownloadButton({ file }: { file: ProjectFile }) {
|
|
|
|
|
const [downloading, setDownloading] = useState(false)
|
|
|
|
|
|
|
|
|
|
const { refetch } = trpc.file.getDownloadUrl.useQuery(
|
|
|
|
|
{ bucket: file.bucket, objectKey: file.objectKey },
|
|
|
|
|
{ enabled: false }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleDownload = async () => {
|
|
|
|
|
setDownloading(true)
|
|
|
|
|
try {
|
|
|
|
|
const result = await refetch()
|
|
|
|
|
if (result.data?.url) {
|
|
|
|
|
// Open in new tab for download
|
|
|
|
|
window.open(result.data.url, '_blank')
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to get download URL:', error)
|
|
|
|
|
} finally {
|
|
|
|
|
setDownloading(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<Button
|
|
|
|
|
variant="outline"
|
|
|
|
|
size="sm"
|
|
|
|
|
onClick={handleDownload}
|
|
|
|
|
disabled={downloading}
|
|
|
|
|
aria-label="Download file"
|
|
|
|
|
>
|
|
|
|
|
{downloading ? (
|
|
|
|
|
<Loader2 className="h-4 w-4 animate-spin" />
|
|
|
|
|
) : (
|
|
|
|
|
<Download className="h-4 w-4" />
|
|
|
|
|
)}
|
|
|
|
|
</Button>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function FilePreview({ file, url }: { file: ProjectFile; url: string }) {
|
|
|
|
|
if (file.mimeType.startsWith('video/')) {
|
|
|
|
|
return (
|
|
|
|
|
<video
|
|
|
|
|
src={url}
|
|
|
|
|
controls
|
|
|
|
|
className="w-full max-h-[500px]"
|
|
|
|
|
preload="metadata"
|
|
|
|
|
>
|
|
|
|
|
Your browser does not support the video tag.
|
|
|
|
|
</video>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (file.mimeType === 'application/pdf') {
|
|
|
|
|
return (
|
|
|
|
|
<div className="relative">
|
|
|
|
|
<iframe
|
|
|
|
|
src={`${url}#toolbar=0`}
|
|
|
|
|
className="w-full h-[600px]"
|
|
|
|
|
title={file.fileName}
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
variant="secondary"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="absolute top-2 right-2"
|
|
|
|
|
asChild
|
|
|
|
|
>
|
|
|
|
|
<a href={url} target="_blank" rel="noopener noreferrer">
|
|
|
|
|
<ExternalLink className="mr-2 h-4 w-4" />
|
|
|
|
|
Open in new tab
|
|
|
|
|
</a>
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
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
|
|
|
if (file.mimeType.startsWith('image/')) {
|
|
|
|
|
return (
|
|
|
|
|
<div className="relative flex items-center justify-center p-4">
|
|
|
|
|
<img
|
|
|
|
|
src={url}
|
|
|
|
|
alt={file.fileName}
|
|
|
|
|
className="max-w-full max-h-[500px] object-contain rounded-md"
|
|
|
|
|
/>
|
|
|
|
|
<Button
|
|
|
|
|
variant="secondary"
|
|
|
|
|
size="sm"
|
|
|
|
|
className="absolute top-2 right-2"
|
|
|
|
|
asChild
|
|
|
|
|
>
|
|
|
|
|
<a href={url} target="_blank" rel="noopener noreferrer">
|
|
|
|
|
<ExternalLink className="mr-2 h-4 w-4" />
|
|
|
|
|
Open in new tab
|
|
|
|
|
</a>
|
|
|
|
|
</Button>
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
2026-01-30 13:41:32 +01:00
|
|
|
return (
|
|
|
|
|
<div className="flex items-center justify-center py-8 text-muted-foreground">
|
|
|
|
|
Preview not available for this file type
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Compact file list for smaller views
|
|
|
|
|
export function FileList({ files, className }: FileViewerProps) {
|
|
|
|
|
if (files.length === 0) return null
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<div className={cn('space-y-2', className)}>
|
|
|
|
|
{files.map((file) => {
|
|
|
|
|
const Icon = getFileIcon(file.fileType, file.mimeType)
|
|
|
|
|
return (
|
|
|
|
|
<CompactFileItem key={file.id} file={file} />
|
|
|
|
|
)
|
|
|
|
|
})}
|
|
|
|
|
</div>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
function CompactFileItem({ file }: { file: ProjectFile }) {
|
|
|
|
|
const [loading, setLoading] = useState(false)
|
|
|
|
|
const Icon = getFileIcon(file.fileType, file.mimeType)
|
|
|
|
|
|
|
|
|
|
const { refetch } = trpc.file.getDownloadUrl.useQuery(
|
|
|
|
|
{ bucket: file.bucket, objectKey: file.objectKey },
|
|
|
|
|
{ enabled: false }
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const handleClick = async () => {
|
|
|
|
|
setLoading(true)
|
|
|
|
|
try {
|
|
|
|
|
const result = await refetch()
|
|
|
|
|
if (result.data?.url) {
|
|
|
|
|
window.open(result.data.url, '_blank')
|
|
|
|
|
}
|
|
|
|
|
} catch (error) {
|
|
|
|
|
console.error('Failed to get download URL:', error)
|
|
|
|
|
} finally {
|
|
|
|
|
setLoading(false)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return (
|
|
|
|
|
<button
|
|
|
|
|
onClick={handleClick}
|
|
|
|
|
disabled={loading}
|
|
|
|
|
className="flex w-full items-center gap-2 rounded-md border p-2 text-left hover:bg-muted transition-colors disabled:opacity-50"
|
|
|
|
|
>
|
|
|
|
|
{loading ? (
|
|
|
|
|
<Loader2 className="h-4 w-4 animate-spin shrink-0" />
|
|
|
|
|
) : (
|
|
|
|
|
<Icon className="h-4 w-4 shrink-0 text-muted-foreground" />
|
|
|
|
|
)}
|
|
|
|
|
<span className="flex-1 truncate text-sm">{file.fileName}</span>
|
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
|
|
|
{file.version != null && file.version > 1 && (
|
|
|
|
|
<Badge variant="outline" className="text-xs shrink-0">
|
|
|
|
|
v{file.version}
|
|
|
|
|
</Badge>
|
|
|
|
|
)}
|
2026-01-30 13:41:32 +01:00
|
|
|
<span className="text-xs text-muted-foreground shrink-0">
|
|
|
|
|
{formatFileSize(file.size)}
|
|
|
|
|
</span>
|
|
|
|
|
</button>
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
export function FileViewerSkeleton() {
|
|
|
|
|
return (
|
|
|
|
|
<Card>
|
|
|
|
|
<CardHeader>
|
|
|
|
|
<Skeleton className="h-5 w-28" />
|
|
|
|
|
</CardHeader>
|
|
|
|
|
<CardContent className="space-y-3">
|
|
|
|
|
{[1, 2, 3].map((i) => (
|
|
|
|
|
<div key={i} className="flex items-center gap-3 rounded-lg border p-3">
|
|
|
|
|
<Skeleton className="h-10 w-10 rounded-lg" />
|
|
|
|
|
<div className="flex-1 space-y-2">
|
|
|
|
|
<Skeleton className="h-4 w-48" />
|
|
|
|
|
<Skeleton className="h-3 w-24" />
|
|
|
|
|
</div>
|
|
|
|
|
<Skeleton className="h-9 w-20" />
|
|
|
|
|
</div>
|
|
|
|
|
))}
|
|
|
|
|
</CardContent>
|
|
|
|
|
</Card>
|
|
|
|
|
)
|
|
|
|
|
}
|