feat(mentor-workspace): inline document preview matching applicant docs pattern

- Eye toggle expands the row below to embed FilePreview from
  @/components/shared/file-viewer (PDF iframe, image, video, Office docs)
- Download button uses explicit Content-Disposition: attachment via a
  new `disposition` input on workspaceGetFileDownloadUrl
- getPresignedUrl learns `inline: true` and optional `response-content-type`
  override so PDFs/images don't get force-downloaded by MinIO's default
- Eye button only renders for previewable mime types

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matt
2026-05-22 18:26:20 +02:00
parent ec92b03006
commit 48e48f058d
3 changed files with 92 additions and 14 deletions

View File

@@ -2307,16 +2307,21 @@ export const mentorRouter = router({
* Issue a short-lived presigned GET URL to download a workspace file.
*/
workspaceGetFileDownloadUrl: protectedProcedure
.input(z.object({ mentorFileId: z.string() }))
.input(z.object({
mentorFileId: z.string(),
disposition: z.enum(['inline', 'attachment']).default('attachment'),
}))
.mutation(async ({ ctx, input }) => {
const file = await ctx.prisma.mentorFile.findUnique({
where: { id: input.mentorFileId },
select: { bucket: true, objectKey: true, fileName: true, projectId: true },
select: { bucket: true, objectKey: true, fileName: true, mimeType: true, projectId: true },
})
if (!file) throw new TRPCError({ code: 'NOT_FOUND', message: 'File not found' })
await assertProjectWorkspaceAccess(ctx.prisma, ctx.user.id, file.projectId)
const url = await getPresignedUrl(file.bucket, file.objectKey, 'GET', 900,
{ downloadFileName: file.fileName })
input.disposition === 'inline'
? { inline: true, contentType: file.mimeType }
: { downloadFileName: file.fileName })
return { url }
}),