From a8b86439365523fcb48385f88bb6618d9d74fc4e Mon Sep 17 00:00:00 2001 From: Matt Date: Fri, 6 Mar 2026 16:24:33 +0100 Subject: [PATCH] feat: group observer project files by round 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 --- .../observer/observer-project-detail.tsx | 95 ++++++++++++------- src/server/routers/analytics.ts | 4 + 2 files changed, 64 insertions(+), 35 deletions(-) diff --git a/src/components/observer/observer-project-detail.tsx b/src/components/observer/observer-project-detail.tsx index 06b8aa9..ded9c8e 100644 --- a/src/components/observer/observer-project-detail.tsx +++ b/src/components/observer/observer-project-detail.tsx @@ -929,41 +929,66 @@ export function ObserverProjectDetail({ projectId }: { projectId: string }) { - {project.files && project.files.length > 0 ? ( - ({ - 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() + 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 ( + ({ + 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, + })), + }))} + /> + ) + })() : (

diff --git a/src/server/routers/analytics.ts b/src/server/routers/analytics.ts index 5f9ad9b..1969d28 100644 --- a/src/server/routers/analytics.ts +++ b/src/server/routers/analytics.ts @@ -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, },