feat: group observer project files by round
All checks were successful
Build and Push Docker Image / build (push) Successful in 7m15s

Files on the observer project detail page are now grouped by round
(e.g., "Application Intake", "Semi-Finals Document Submission") instead
of shown in a flat list. Uses FileViewer's existing groupedFiles prop.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-06 16:24:33 +01:00
parent 0390d05727
commit a8b8643936
2 changed files with 64 additions and 35 deletions

View File

@@ -929,41 +929,66 @@ export function ObserverProjectDetail({ projectId }: { projectId: string }) {
</CardTitle>
</CardHeader>
<CardContent>
{project.files && project.files.length > 0 ? (
<FileViewer
projectId={projectId}
files={project.files.map((f) => ({
id: f.id,
fileName: f.fileName,
fileType: f.fileType as
| 'EXEC_SUMMARY'
| 'PRESENTATION'
| 'VIDEO'
| 'OTHER'
| 'BUSINESS_PLAN'
| 'VIDEO_PITCH'
| 'SUPPORTING_DOC',
mimeType: f.mimeType,
size: f.size,
bucket: f.bucket,
objectKey: f.objectKey,
pageCount: f.pageCount,
textPreview: f.textPreview,
detectedLang: f.detectedLang,
langConfidence: f.langConfidence,
analyzedAt: f.analyzedAt ? String(f.analyzedAt) : null,
requirementId: f.requirementId,
requirement: f.requirement
? {
id: f.requirement.id,
name: f.requirement.name,
description: f.requirement.description,
isRequired: f.requirement.isRequired,
}
: null,
}))}
/>
) : (
{project.files && project.files.length > 0 ? (() => {
// Group files by round
type FileItem = (typeof project.files)[number]
const roundMap = new Map<string, { roundId: string | null; roundName: string; sortOrder: number; files: FileItem[] }>()
for (const f of project.files) {
const key = (f as any).roundId ?? '__none__'
if (!roundMap.has(key)) {
const round = (f as any).round as { id: string; name: string; sortOrder: number } | null
roundMap.set(key, {
roundId: round?.id ?? null,
roundName: round?.name ?? 'Other Files',
sortOrder: round?.sortOrder ?? 999,
files: [],
})
}
roundMap.get(key)!.files.push(f)
}
const groups = Array.from(roundMap.values()).sort((a, b) => a.sortOrder - b.sortOrder)
return (
<FileViewer
projectId={projectId}
groupedFiles={groups.map((g) => ({
roundId: g.roundId,
roundName: g.roundName,
sortOrder: g.sortOrder,
files: g.files.map((f) => ({
id: f.id,
fileName: f.fileName,
fileType: f.fileType as
| 'EXEC_SUMMARY'
| 'PRESENTATION'
| 'VIDEO'
| 'OTHER'
| 'BUSINESS_PLAN'
| 'VIDEO_PITCH'
| 'SUPPORTING_DOC',
mimeType: f.mimeType,
size: f.size,
bucket: f.bucket,
objectKey: f.objectKey,
pageCount: f.pageCount,
textPreview: f.textPreview,
detectedLang: f.detectedLang,
langConfidence: f.langConfidence,
analyzedAt: f.analyzedAt ? String(f.analyzedAt) : null,
requirementId: f.requirementId,
requirement: f.requirement
? {
id: f.requirement.id,
name: f.requirement.name,
description: f.requirement.description,
isRequired: f.requirement.isRequired,
}
: null,
})),
}))}
/>
)
})() : (
<div className="flex flex-col items-center justify-center py-8 text-center">
<FileText className="h-12 w-12 text-muted-foreground/50" />
<p className="mt-2 text-sm text-muted-foreground">

View File

@@ -1378,9 +1378,12 @@ export const analyticsRouter = router({
id: true, fileName: true, fileType: true, mimeType: true, size: true,
bucket: true, objectKey: true, pageCount: true, textPreview: true,
detectedLang: true, langConfidence: true, analyzedAt: true,
roundId: true,
round: { select: { id: true, name: true, roundType: true, sortOrder: true } },
requirementId: true,
requirement: { select: { id: true, name: true, description: true, isRequired: true } },
},
orderBy: [{ round: { sortOrder: 'asc' } }, { createdAt: 'asc' }],
},
teamMembers: {
include: {
@@ -1526,6 +1529,7 @@ export const analyticsRouter = router({
return {
project: {
...projectRaw,
files: projectRaw.files,
projectTags,
teamMembers: teamMembersWithAvatars,
},