From d0c0983e9392759059a3ecb3a117d5c54d9746d0 Mon Sep 17 00:00:00 2001 From: waleed Date: Sun, 11 Jan 2026 20:57:19 -0800 Subject: [PATCH 1/7] feat(export): added the ability to export workflow --- .../components/folder-item/folder-item.tsx | 12 +- .../workflow-list/workflow-list.tsx | 11 +- .../w/components/sidebar/sidebar.tsx | 52 ++-- .../workspace/[workspaceId]/w/hooks/index.ts | 1 + .../w/hooks/use-export-folder.ts | 251 ++++++++++++++++++ .../emcn/icons/animate/download.module.css | 22 ++ apps/sim/components/emcn/icons/download.tsx | 42 +++ apps/sim/components/emcn/icons/index.ts | 1 + 8 files changed, 362 insertions(+), 30 deletions(-) create mode 100644 apps/sim/app/workspace/[workspaceId]/w/hooks/use-export-folder.ts create mode 100644 apps/sim/components/emcn/icons/animate/download.module.css create mode 100644 apps/sim/components/emcn/icons/download.tsx diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx index 1dc249088d..652d7365b8 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/folder-item/folder-item.tsx @@ -20,6 +20,7 @@ import { useCanDelete, useDeleteFolder, useDuplicateFolder, + useExportFolder, } from '@/app/workspace/[workspaceId]/w/hooks' import { useCreateFolder, useUpdateFolder } from '@/hooks/queries/folders' import { useCreateWorkflow } from '@/hooks/queries/workflows' @@ -73,6 +74,12 @@ export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) { getFolderIds: () => folder.id, }) + // Export folder hook + const { isExporting, hasWorkflows, handleExportFolder } = useExportFolder({ + workspaceId, + getFolderId: () => folder.id, + }) + // Folder expand hook - must be declared before callbacks that use expandFolder const { isExpanded, @@ -365,13 +372,16 @@ export function FolderItem({ folder, level, hoverHandlers }: FolderItemProps) { onCreate={handleCreateWorkflowInFolder} onCreateFolder={handleCreateFolderInFolder} onDuplicate={handleDuplicateFolder} + onExport={handleExportFolder} onDelete={() => setIsDeleteModalOpen(true)} showCreate={true} showCreateFolder={true} + showExport={true} disableRename={!userPermissions.canEdit} disableCreate={!userPermissions.canEdit || createWorkflowMutation.isPending} disableCreateFolder={!userPermissions.canEdit || createFolderMutation.isPending} - disableDuplicate={!userPermissions.canEdit} + disableDuplicate={!userPermissions.canEdit || !hasWorkflows} + disableExport={!userPermissions.canEdit || isExporting || !hasWorkflows} disableDelete={!userPermissions.canEdit || !canDelete} /> diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/workflow-list.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/workflow-list.tsx index 78385ad873..468960c20a 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/workflow-list.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/workflow-list.tsx @@ -9,7 +9,6 @@ import { useDragDrop, useWorkflowSelection, } from '@/app/workspace/[workspaceId]/w/components/sidebar/hooks' -import { useImportWorkflow } from '@/app/workspace/[workspaceId]/w/hooks/use-import-workflow' import { useFolders } from '@/hooks/queries/folders' import { useFolderStore } from '@/stores/folders/store' import type { FolderTreeNode } from '@/stores/folders/types' @@ -25,15 +24,13 @@ const TREE_SPACING = { interface WorkflowListProps { regularWorkflows: WorkflowMetadata[] isLoading?: boolean - isImporting: boolean - setIsImporting: (value: boolean) => void + handleFileChange: (event: React.ChangeEvent) => void fileInputRef: React.RefObject scrollContainerRef: React.RefObject } /** * WorkflowList component displays workflows organized by folders with drag-and-drop support. - * Uses the workflow import hook for handling JSON imports. * * @param props - Component props * @returns Workflow list with folders and drag-drop support @@ -41,8 +38,7 @@ interface WorkflowListProps { export function WorkflowList({ regularWorkflows, isLoading = false, - isImporting, - setIsImporting, + handleFileChange, fileInputRef, scrollContainerRef, }: WorkflowListProps) { @@ -65,9 +61,6 @@ export function WorkflowList({ createFolderHeaderHoverHandlers, } = useDragDrop() - // Workflow import hook - const { handleFileChange } = useImportWorkflow({ workspaceId }) - // Set scroll container when ref changes useEffect(() => { if (scrollContainerRef.current) { diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx index 6acab60c56..eb8248e888 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/sidebar.tsx @@ -2,10 +2,10 @@ import { useCallback, useEffect, useMemo, useRef, useState } from 'react' import { createLogger } from '@sim/logger' -import { ArrowDown, Database, HelpCircle, Layout, Plus, Search, Settings } from 'lucide-react' +import { Database, HelpCircle, Layout, Plus, Search, Settings } from 'lucide-react' import Link from 'next/link' import { useParams, usePathname, useRouter } from 'next/navigation' -import { Button, FolderPlus, Library, Tooltip } from '@/components/emcn' +import { Button, Download, FolderPlus, Library, Tooltip } from '@/components/emcn' import { useSession } from '@/lib/auth/auth-client' import { getEnv, isTruthy } from '@/lib/core/config/env' import { useRegisterGlobalCommands } from '@/app/workspace/[workspaceId]/providers/global-commands-provider' @@ -30,6 +30,7 @@ import { import { useDuplicateWorkspace, useExportWorkspace, + useImportWorkflow, useImportWorkspace, } from '@/app/workspace/[workspaceId]/w/hooks' import { usePermissionConfig } from '@/hooks/use-permission-config' @@ -85,9 +86,11 @@ export function Sidebar() { const isCollapsed = hasHydrated ? isCollapsedStore : false const isOnWorkflowPage = !!workflowId - const [isImporting, setIsImporting] = useState(false) const workspaceFileInputRef = useRef(null) + const { isImporting, handleFileChange: handleImportFileChange } = useImportWorkflow({ + workspaceId, + }) const { isImporting: isImportingWorkspace, handleImportWorkspace: importWorkspace } = useImportWorkspace() const { handleExportWorkspace: exportWorkspace } = useExportWorkspace() @@ -565,21 +568,31 @@ export function Sidebar() { Workflows
- - - - - -

{isImporting ? 'Importing workflow...' : 'Import workflow'}

-
-
+ {isImporting ? ( + + ) : ( + + + + + +

Import workflows

+
+
+ )} ) : ( From e34a28c33133175b7aa80f2d479e65080762e29c Mon Sep 17 00:00:00 2001 From: waleed Date: Sun, 11 Jan 2026 21:15:50 -0800 Subject: [PATCH 3/7] fixed flicker on importing multiple workflows --- .../components/context-menu/context-menu.tsx | 2 +- .../w/hooks/use-import-workflow.ts | 6 ++++-- apps/sim/stores/workflows/registry/store.ts | 20 +++++++++++++------ 3 files changed, 19 insertions(+), 9 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx index 769fe71d70..6be102415c 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu.tsx @@ -373,7 +373,7 @@ export function ContextMenu({ onKeyDown={handleHexKeyDown} onFocus={handleHexFocus} onClick={(e) => e.stopPropagation()} - className='h-[20px] min-w-0 flex-1 rounded-[4px] bg-[#363636] px-[6px] text-[11px] text-white uppercase focus:outline-none' + className='h-[20px] min-w-0 flex-1 rounded-[4px] bg-[#363636] px-[6px] text-[11px] text-white uppercase caret-white focus:outline-none' />