Skip to content

Commit 3180707

Browse files
author
Theodore Li
committed
Always set url to /file on file view
1 parent 88000f4 commit 3180707

File tree

5 files changed

+72
-25
lines changed

5 files changed

+72
-25
lines changed

apps/sim/app/api/workspaces/[id]/files/[fileId]/download/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,7 +42,7 @@ export async function POST(
4242

4343
const { getBaseUrl } = await import('@/lib/core/utils/urls')
4444
const serveUrl = `${getBaseUrl()}/api/files/serve/${encodeURIComponent(fileRecord.key)}?context=workspace`
45-
const viewerUrl = `${getBaseUrl()}/workspace/${workspaceId}/files/${fileId}/view`
45+
const viewerUrl = `${getBaseUrl()}/workspace/${workspaceId}/files/${fileId}`
4646

4747
logger.info(`[${requestId}] Generated download URL for workspace file: ${fileRecord.name}`)
4848

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
import type { Metadata } from 'next'
2+
import { redirect } from 'next/navigation'
3+
import { getSession } from '@/lib/auth'
4+
import { verifyWorkspaceMembership } from '@/app/api/workflows/utils'
5+
import { getUserPermissionConfig } from '@/ee/access-control/utils/permission-check'
6+
import { Files } from '../files'
7+
8+
export const metadata: Metadata = {
9+
title: 'Files',
10+
robots: { index: false },
11+
}
12+
13+
interface FileDetailPageProps {
14+
params: Promise<{
15+
workspaceId: string
16+
fileId: string
17+
}>
18+
}
19+
20+
export default async function FileDetailPage({ params }: FileDetailPageProps) {
21+
const { workspaceId } = await params
22+
const session = await getSession()
23+
24+
if (!session?.user?.id) {
25+
redirect('/')
26+
}
27+
28+
const hasPermission = await verifyWorkspaceMembership(session.user.id, workspaceId)
29+
if (!hasPermission) {
30+
redirect('/')
31+
}
32+
33+
const permissionConfig = await getUserPermissionConfig(session.user.id)
34+
if (permissionConfig?.hideFilesTab) {
35+
redirect(`/workspace/${workspaceId}`)
36+
}
37+
38+
return <Files />
39+
}

apps/sim/app/workspace/[workspaceId]/files/files.tsx

Lines changed: 29 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
44
import { createLogger } from '@sim/logger'
5-
import { useParams } from 'next/navigation'
5+
import { useParams, useRouter } from 'next/navigation'
66
import {
77
Button,
88
Columns2,
@@ -123,7 +123,10 @@ export function Files() {
123123
const saveRef = useRef<(() => Promise<void>) | null>(null)
124124

125125
const params = useParams()
126+
const router = useRouter()
126127
const workspaceId = params?.workspaceId as string
128+
const fileIdFromRoute =
129+
typeof params?.fileId === 'string' && params.fileId.length > 0 ? params.fileId : null
127130
const userPermissions = useUserPermissionsContext()
128131

129132
const { data: files = [], isLoading, error } = useWorkspaceFiles(workspaceId)
@@ -157,7 +160,6 @@ export function Files() {
157160
const [uploading, setUploading] = useState(false)
158161
const [uploadProgress, setUploadProgress] = useState({ completed: 0, total: 0 })
159162
const [searchTerm, setSearchTerm] = useState('')
160-
const [selectedFileId, setSelectedFileId] = useState<string | null>(null)
161163
const [creatingFile, setCreatingFile] = useState(false)
162164
const [isDirty, setIsDirty] = useState(false)
163165
const [saveStatus, setSaveStatus] = useState<SaveStatus>('idle')
@@ -178,8 +180,8 @@ export function Files() {
178180
})
179181

180182
const selectedFile = useMemo(
181-
() => (selectedFileId ? files.find((f) => f.id === selectedFileId) : null),
182-
[selectedFileId, files]
183+
() => (fileIdFromRoute ? files.find((f) => f.id === fileIdFromRoute) : null),
184+
[fileIdFromRoute, files]
183185
)
184186

185187
const filteredFiles = useMemo(() => {
@@ -297,15 +299,15 @@ export function Files() {
297299
})
298300
setShowDeleteConfirm(false)
299301
setDeleteTargetFile(null)
300-
if (selectedFileId === target.id) {
302+
if (fileIdFromRoute === target.id) {
301303
setIsDirty(false)
302304
setSaveStatus('idle')
303-
setSelectedFileId(null)
305+
router.push(`/workspace/${workspaceId}/files`)
304306
}
305307
} catch (err) {
306308
logger.error('Failed to delete file:', err)
307309
}
308-
}, [deleteTargetFile, workspaceId, selectedFileId])
310+
}, [deleteTargetFile, workspaceId, fileIdFromRoute, router])
309311

310312
const handleSave = useCallback(async () => {
311313
if (!saveRef.current || !isDirty || saveStatus === 'saving') return
@@ -317,9 +319,9 @@ export function Files() {
317319
setShowUnsavedChangesAlert(true)
318320
} else {
319321
setPreviewMode('editor')
320-
setSelectedFileId(null)
322+
router.push(`/workspace/${workspaceId}/files`)
321323
}
322-
}, [isDirty])
324+
}, [isDirty, router, workspaceId])
323325

324326
const handleStartHeaderRename = useCallback(() => {
325327
if (selectedFile) headerRename.startRename(selectedFile.id, selectedFile.name)
@@ -391,8 +393,8 @@ export function Files() {
391393
setIsDirty(false)
392394
setSaveStatus('idle')
393395
setPreviewMode('editor')
394-
setSelectedFileId(null)
395-
}, [])
396+
router.push(`/workspace/${workspaceId}/files`)
397+
}, [router, workspaceId])
396398

397399
const handleCreateFile = useCallback(async () => {
398400
if (creatingFile) return
@@ -414,14 +416,14 @@ export function Files() {
414416
const fileId = result.file?.id
415417
if (fileId) {
416418
justCreatedFileIdRef.current = fileId
417-
setSelectedFileId(fileId)
419+
router.push(`/workspace/${workspaceId}/files/${fileId}`)
418420
}
419421
} catch (err) {
420422
logger.error('Failed to create file:', err)
421423
} finally {
422424
setCreatingFile(false)
423425
}
424-
}, [creatingFile, files, workspaceId])
426+
}, [creatingFile, files, workspaceId, router])
425427

426428
const handleRowContextMenu = useCallback(
427429
(e: React.MouseEvent, rowId: string) => {
@@ -436,9 +438,9 @@ export function Files() {
436438

437439
const handleContextMenuOpen = useCallback(() => {
438440
if (!contextMenuFile) return
439-
setSelectedFileId(contextMenuFile.id)
441+
router.push(`/workspace/${workspaceId}/files/${contextMenuFile.id}`)
440442
closeContextMenu()
441-
}, [contextMenuFile, closeContextMenu])
443+
}, [contextMenuFile, closeContextMenu, router, workspaceId])
442444

443445
const handleContextMenuDownload = useCallback(() => {
444446
if (!contextMenuFile) return
@@ -478,18 +480,19 @@ export function Files() {
478480
}, [closeListContextMenu])
479481

480482
useEffect(() => {
481-
const isJustCreated = selectedFileId != null && justCreatedFileIdRef.current === selectedFileId
483+
const isJustCreated =
484+
fileIdFromRoute != null && justCreatedFileIdRef.current === fileIdFromRoute
482485
if (justCreatedFileIdRef.current && !isJustCreated) {
483486
justCreatedFileIdRef.current = null
484487
}
485488
if (isJustCreated) {
486489
setPreviewMode('editor')
487490
} else {
488-
const file = selectedFileId ? filesRef.current.find((f) => f.id === selectedFileId) : null
491+
const file = fileIdFromRoute ? filesRef.current.find((f) => f.id === fileIdFromRoute) : null
489492
const canPreview = file ? isPreviewable(file) : false
490493
setPreviewMode(canPreview ? 'preview' : 'editor')
491494
}
492-
}, [selectedFileId])
495+
}, [fileIdFromRoute])
493496

494497
useEffect(() => {
495498
if (!selectedFile) return
@@ -597,13 +600,16 @@ export function Files() {
597600
handleDeleteSelected,
598601
])
599602

600-
if (selectedFileId && !selectedFile) {
603+
if (fileIdFromRoute && !selectedFile) {
601604
return (
602605
<div className='flex h-full flex-1 flex-col overflow-hidden bg-[var(--bg)]'>
603606
<ResourceHeader
604607
icon={FilesIcon}
605608
breadcrumbs={[
606-
{ label: 'Files', onClick: () => setSelectedFileId(null) },
609+
{
610+
label: 'Files',
611+
onClick: () => router.push(`/workspace/${workspaceId}/files`),
612+
},
607613
{ label: '...' },
608614
]}
609615
/>
@@ -699,7 +705,9 @@ export function Files() {
699705
columns={COLUMNS}
700706
rows={rows}
701707
onRowClick={(id) => {
702-
if (listRename.editingId !== id && !headerRename.editingId) setSelectedFileId(id)
708+
if (listRename.editingId !== id && !headerRename.editingId) {
709+
router.push(`/workspace/${workspaceId}/files/${id}`)
710+
}
703711
}}
704712
onRowContextMenu={handleRowContextMenu}
705713
isLoading={isLoading}

apps/sim/app/workspace/[workspaceId]/home/components/mothership-view/components/resource-content/resource-content.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -299,7 +299,7 @@ function EmbeddedFileActions({ workspaceId, fileId }: EmbeddedFileActionsProps)
299299
}, [file])
300300

301301
const handleOpenInFiles = useCallback(() => {
302-
router.push(`/workspace/${workspaceId}/files?fileId=${encodeURIComponent(fileId)}`)
302+
router.push(`/workspace/${workspaceId}/files/${encodeURIComponent(fileId)}`)
303303
}, [router, workspaceId, fileId])
304304

305305
return (

apps/sim/lib/uploads/utils/file-utils.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -736,7 +736,7 @@ export function extractWorkspaceIdFromExecutionKey(key: string): string | null {
736736

737737
/**
738738
* Construct viewer URL for a file
739-
* Viewer URL format: /workspace/{workspaceId}/files/{fileKey}/view
739+
* Viewer URL format: /workspace/{workspaceId}/files/{fileKey}
740740
* @param fileKey File storage key
741741
* @param workspaceId Optional workspace ID (will be extracted from key if not provided)
742742
* @returns Viewer URL string or null if workspaceId cannot be determined
@@ -748,7 +748,7 @@ export function getViewerUrl(fileKey: string, workspaceId?: string): string | nu
748748
return null
749749
}
750750

751-
return `/workspace/${resolvedWorkspaceId}/files/${fileKey}/view`
751+
return `/workspace/${resolvedWorkspaceId}/files/${fileKey}`
752752
}
753753

754754
/**

0 commit comments

Comments
 (0)