|
1 | 1 | 'use client' |
2 | 2 |
|
3 | 3 | import { lazy, Suspense, useCallback, useEffect, useMemo } from 'react' |
| 4 | +import { createLogger } from '@sim/logger' |
4 | 5 | import { Square } from 'lucide-react' |
5 | 6 | import { useRouter } from 'next/navigation' |
6 | 7 | import { Button, PlayOutline, Skeleton, Tooltip } from '@/components/emcn' |
7 | | -import { FileX, SquareArrowUpRight, WorkflowX } from '@/components/emcn/icons' |
| 8 | +import { Download, FileX, SquareArrowUpRight, WorkflowX } from '@/components/emcn/icons' |
8 | 9 | import { |
9 | 10 | markRunToolManuallyStopped, |
10 | 11 | reportManualRunToolStop, |
11 | 12 | } from '@/lib/copilot/client-sse/run-tool-execution' |
| 13 | +import { downloadWorkspaceFile } from '@/lib/uploads/utils/file-utils' |
12 | 14 | import { |
13 | 15 | FileViewer, |
14 | 16 | type PreviewMode, |
@@ -93,6 +95,8 @@ export function ResourceActions({ workspaceId, resource }: ResourceActionsProps) |
93 | 95 | switch (resource.type) { |
94 | 96 | case 'workflow': |
95 | 97 | return <EmbeddedWorkflowActions workspaceId={workspaceId} workflowId={resource.id} /> |
| 98 | + case 'file': |
| 99 | + return <EmbeddedFileActions workspaceId={workspaceId} fileId={resource.id} /> |
96 | 100 | case 'knowledgebase': |
97 | 101 | return ( |
98 | 102 | <EmbeddedKnowledgeBaseActions workspaceId={workspaceId} knowledgeBaseId={resource.id} /> |
@@ -230,6 +234,68 @@ export function EmbeddedKnowledgeBaseActions({ |
230 | 234 | ) |
231 | 235 | } |
232 | 236 |
|
| 237 | +const fileLogger = createLogger('EmbeddedFileActions') |
| 238 | + |
| 239 | +interface EmbeddedFileActionsProps { |
| 240 | + workspaceId: string |
| 241 | + fileId: string |
| 242 | +} |
| 243 | + |
| 244 | +function EmbeddedFileActions({ workspaceId, fileId }: EmbeddedFileActionsProps) { |
| 245 | + const router = useRouter() |
| 246 | + const { data: files = [] } = useWorkspaceFiles(workspaceId) |
| 247 | + const file = useMemo(() => files.find((f) => f.id === fileId), [files, fileId]) |
| 248 | + |
| 249 | + const handleDownload = useCallback(async () => { |
| 250 | + if (!file) return |
| 251 | + try { |
| 252 | + await downloadWorkspaceFile(file) |
| 253 | + } catch (err) { |
| 254 | + fileLogger.error('Failed to download file:', err) |
| 255 | + } |
| 256 | + }, [file]) |
| 257 | + |
| 258 | + const handleOpenInFiles = useCallback(() => { |
| 259 | + router.push(`/workspace/${workspaceId}/files?fileId=${fileId}`) |
| 260 | + }, [router, workspaceId, fileId]) |
| 261 | + |
| 262 | + return ( |
| 263 | + <> |
| 264 | + <Tooltip.Root> |
| 265 | + <Tooltip.Trigger asChild> |
| 266 | + <Button |
| 267 | + variant='subtle' |
| 268 | + onClick={handleOpenInFiles} |
| 269 | + className={RESOURCE_TAB_ICON_BUTTON_CLASS} |
| 270 | + aria-label='Open in files' |
| 271 | + > |
| 272 | + <SquareArrowUpRight className={RESOURCE_TAB_ICON_CLASS} /> |
| 273 | + </Button> |
| 274 | + </Tooltip.Trigger> |
| 275 | + <Tooltip.Content side='bottom'> |
| 276 | + <p>Open in files</p> |
| 277 | + </Tooltip.Content> |
| 278 | + </Tooltip.Root> |
| 279 | + <Tooltip.Root> |
| 280 | + <Tooltip.Trigger asChild> |
| 281 | + <Button |
| 282 | + variant='subtle' |
| 283 | + onClick={() => void handleDownload()} |
| 284 | + disabled={!file} |
| 285 | + className={RESOURCE_TAB_ICON_BUTTON_CLASS} |
| 286 | + aria-label='Download file' |
| 287 | + > |
| 288 | + <Download className={RESOURCE_TAB_ICON_CLASS} /> |
| 289 | + </Button> |
| 290 | + </Tooltip.Trigger> |
| 291 | + <Tooltip.Content side='bottom'> |
| 292 | + <p>Download</p> |
| 293 | + </Tooltip.Content> |
| 294 | + </Tooltip.Root> |
| 295 | + </> |
| 296 | + ) |
| 297 | +} |
| 298 | + |
233 | 299 | interface EmbeddedWorkflowProps { |
234 | 300 | workspaceId: string |
235 | 301 | workflowId: string |
|
0 commit comments