mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
improvement(sidebar): memoize workflow/folder rows for faster tab navigation (#5428)
* improvement(sidebar): memoize workflow/folder rows for faster tab navigation Switching between workspace tabs re-rendered every workflow and folder row in the sidebar because the rows (and the shared export hooks they call) subscribed to useParams, which re-renders on every navigation. - Wrap WorkflowItem and FolderItem in React.memo (the only un-memoized leaf rows; every sibling row was already memoized). - Decouple the rows from useParams: thread workspaceId as a stable prop from WorkflowList (matching the FileList convention), and expose the live active workflowId through a stable activeWorkflowIdRef on SidebarListContext, read only in delete callbacks — never during render. - Refactor the three shared export hooks (used only by these two rows) to take workspaceId as a param instead of calling useParams internally. - Stabilize handleWorkflowClick's identity via refs so the shared list context no longer changes identity on navigation. - Lazy-init the drag-drop siblings Map ref. On a tab switch only the two rows whose active state flips now re-render. * fix(sidebar): add workspaceId to render-callback deps renderWorkflowItem/renderFolderSection now pass workspaceId into the rows, so they must list it as a dependency — otherwise a workspace switch that doesn't also change workflowId would leave the callbacks closing over a stale workspaceId (wrong-workspace deletes/exports).
This commit is contained in:
+18
-10
@@ -1,13 +1,13 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { memo, useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { chipVariants, cn } from '@sim/emcn'
|
||||
import { Lock } from '@sim/emcn/icons'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { generateId } from '@sim/utils/id'
|
||||
import clsx from 'clsx'
|
||||
import { ChevronRight, Folder, FolderOpen, MoreHorizontal } from 'lucide-react'
|
||||
import { useParams, useRouter } from 'next/navigation'
|
||||
import { useRouter } from 'next/navigation'
|
||||
import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu'
|
||||
@@ -49,15 +49,20 @@ import { generateCreativeWorkflowName } from '@/stores/workflows/registry/utils'
|
||||
const logger = createLogger('FolderItem')
|
||||
|
||||
interface FolderItemProps {
|
||||
workspaceId: string
|
||||
folder: FolderTreeNode
|
||||
}
|
||||
|
||||
export function FolderItem({ folder }: FolderItemProps) {
|
||||
const { isAnyDragActive, dragDisabled, onFolderClick, onItemDragStart, onItemDragEnd } =
|
||||
useSidebarListContext()
|
||||
const params = useParams()
|
||||
export const FolderItem = memo(function FolderItem({ workspaceId, folder }: FolderItemProps) {
|
||||
const {
|
||||
isAnyDragActive,
|
||||
dragDisabled,
|
||||
activeWorkflowIdRef,
|
||||
onFolderClick,
|
||||
onItemDragStart,
|
||||
onItemDragEnd,
|
||||
} = useSidebarListContext()
|
||||
const router = useRouter()
|
||||
const workspaceId = params.workspaceId as string
|
||||
const updateFolderMutation = useUpdateFolder()
|
||||
const createWorkflowMutation = useCreateWorkflow()
|
||||
const createFolderMutation = useCreateFolder()
|
||||
@@ -95,7 +100,7 @@ export function FolderItem({ folder }: FolderItemProps) {
|
||||
workspaceId,
|
||||
workflowIds: capturedSelectionRef.current?.workflowIds || [],
|
||||
folderIds: capturedSelectionRef.current?.folderIds || [],
|
||||
isActiveWorkflow: (id) => id === params.workflowId,
|
||||
isActiveWorkflow: (id) => id === activeWorkflowIdRef.current,
|
||||
onSuccess: () => setIsDeleteModalOpen(false),
|
||||
})
|
||||
|
||||
@@ -117,10 +122,13 @@ export function FolderItem({ folder }: FolderItemProps) {
|
||||
hasWorkflows,
|
||||
handleExportFolder: handleExportThisFolder,
|
||||
} = useExportFolder({
|
||||
workspaceId,
|
||||
folderId: folder.id,
|
||||
})
|
||||
|
||||
const { isExporting: isExportingSelection, handleExportSelection } = useExportSelection()
|
||||
const { isExporting: isExportingSelection, handleExportSelection } = useExportSelection({
|
||||
workspaceId,
|
||||
})
|
||||
|
||||
const isExporting = isExportingThisFolder || isExportingSelection
|
||||
|
||||
@@ -606,4 +614,4 @@ export function FolderItem({ folder }: FolderItemProps) {
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
+20
-12
@@ -1,12 +1,11 @@
|
||||
'use client'
|
||||
|
||||
import { useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { memo, useCallback, useMemo, useRef, useState } from 'react'
|
||||
import { chipVariants, cn } from '@sim/emcn'
|
||||
import { Lock } from '@sim/emcn/icons'
|
||||
import clsx from 'clsx'
|
||||
import { MoreHorizontal } from 'lucide-react'
|
||||
import Link from 'next/link'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { SIM_RESOURCES_DRAG_TYPE } from '@/lib/copilot/resource-types'
|
||||
import { useUserPermissionsContext } from '@/app/workspace/[workspaceId]/providers/workspace-permissions-provider'
|
||||
import { ContextMenu } from '@/app/workspace/[workspaceId]/w/components/sidebar/components/workflow-list/components/context-menu/context-menu'
|
||||
@@ -43,6 +42,7 @@ import { useFolderStore } from '@/stores/folders/store'
|
||||
import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
|
||||
|
||||
interface WorkflowItemProps {
|
||||
workspaceId: string
|
||||
workflow: WorkflowMetadata
|
||||
active: boolean
|
||||
}
|
||||
@@ -55,11 +55,19 @@ interface WorkflowItemProps {
|
||||
* @param props - Component props
|
||||
* @returns Workflow item with drag and selection support
|
||||
*/
|
||||
export function WorkflowItem({ workflow, active }: WorkflowItemProps) {
|
||||
const { isAnyDragActive, dragDisabled, onWorkflowClick, onItemDragStart, onItemDragEnd } =
|
||||
useSidebarListContext()
|
||||
const params = useParams()
|
||||
const workspaceId = params.workspaceId as string
|
||||
export const WorkflowItem = memo(function WorkflowItem({
|
||||
workspaceId,
|
||||
workflow,
|
||||
active,
|
||||
}: WorkflowItemProps) {
|
||||
const {
|
||||
isAnyDragActive,
|
||||
dragDisabled,
|
||||
activeWorkflowIdRef,
|
||||
onWorkflowClick,
|
||||
onItemDragStart,
|
||||
onItemDragEnd,
|
||||
} = useSidebarListContext()
|
||||
const selectedWorkflows = useFolderStore((state) => state.selectedWorkflows)
|
||||
const updateWorkflowMutation = useUpdateWorkflow()
|
||||
const userPermissions = useUserPermissionsContext()
|
||||
@@ -105,7 +113,7 @@ export function WorkflowItem({ workflow, active }: WorkflowItemProps) {
|
||||
useDeleteWorkflow({
|
||||
workspaceId,
|
||||
workflowIds: capturedSelectionRef.current?.workflowIds || [],
|
||||
isActive: (workflowIds) => workflowIds.includes(params.workflowId as string),
|
||||
isActive: (workflowIds) => workflowIds.includes(activeWorkflowIdRef.current ?? ''),
|
||||
onSuccess: () => setIsDeleteModalOpen(false),
|
||||
})
|
||||
|
||||
@@ -113,7 +121,7 @@ export function WorkflowItem({ workflow, active }: WorkflowItemProps) {
|
||||
workspaceId,
|
||||
workflowIds: capturedSelectionRef.current?.workflowIds || [],
|
||||
folderIds: capturedSelectionRef.current?.folderIds || [],
|
||||
isActiveWorkflow: (id) => id === params.workflowId,
|
||||
isActiveWorkflow: (id) => id === activeWorkflowIdRef.current,
|
||||
onSuccess: () => setIsDeleteModalOpen(false),
|
||||
})
|
||||
|
||||
@@ -136,8 +144,8 @@ export function WorkflowItem({ workflow, active }: WorkflowItemProps) {
|
||||
{ workspaceId }
|
||||
)
|
||||
|
||||
const { handleExportWorkflow: handleExportWorkflows } = useExportWorkflow()
|
||||
const { handleExportSelection } = useExportSelection()
|
||||
const { handleExportWorkflow: handleExportWorkflows } = useExportWorkflow({ workspaceId })
|
||||
const { handleExportSelection } = useExportSelection({ workspaceId })
|
||||
|
||||
const handleDuplicate = useCallback(() => {
|
||||
if (!capturedSelectionRef.current) return
|
||||
@@ -507,4 +515,4 @@ export function WorkflowItem({ workflow, active }: WorkflowItemProps) {
|
||||
/>
|
||||
</>
|
||||
)
|
||||
}
|
||||
})
|
||||
|
||||
+14
-4
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { memo, useCallback, useEffect, useMemo } from 'react'
|
||||
import { memo, useCallback, useEffect, useMemo, useRef } from 'react'
|
||||
import clsx from 'clsx'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { buildFolderTree, getFolderPath } from '@/lib/folders/tree'
|
||||
@@ -341,9 +341,14 @@ export const WorkflowList = memo(function WorkflowList({
|
||||
folderDescendantIds,
|
||||
})
|
||||
|
||||
/** Mirror `workflowId` into a stable ref so the list context stays referentially stable across navigation. */
|
||||
const activeWorkflowIdRef = useRef(workflowId)
|
||||
activeWorkflowIdRef.current = workflowId
|
||||
|
||||
const listContextValue = useSidebarListContextValue({
|
||||
isAnyDragActive: isDragging,
|
||||
dragDisabled,
|
||||
activeWorkflowIdRef,
|
||||
onWorkflowClick: handleWorkflowClick,
|
||||
onFolderClick: handleFolderClick,
|
||||
onItemDragStart: handleDragStart,
|
||||
@@ -380,13 +385,17 @@ export const WorkflowList = memo(function WorkflowList({
|
||||
style={{ paddingLeft: `${level * TREE_SPACING.INDENT_PER_LEVEL}px` }}
|
||||
{...createWorkflowDragHandlers(workflow.id, folderId)}
|
||||
>
|
||||
<WorkflowItem workflow={workflow} active={isWorkflowActive(workflow.id)} />
|
||||
<WorkflowItem
|
||||
workspaceId={workspaceId}
|
||||
workflow={workflow}
|
||||
active={isWorkflowActive(workflow.id)}
|
||||
/>
|
||||
</div>
|
||||
<DropIndicatorLine show={showAfter} level={level} position='after' />
|
||||
</div>
|
||||
)
|
||||
},
|
||||
[dropIndicator, isWorkflowActive, createWorkflowDragHandlers]
|
||||
[workspaceId, dropIndicator, isWorkflowActive, createWorkflowDragHandlers]
|
||||
)
|
||||
|
||||
const renderFolderSection = useCallback(
|
||||
@@ -445,7 +454,7 @@ export const WorkflowList = memo(function WorkflowList({
|
||||
style={{ paddingLeft: `${level * TREE_SPACING.INDENT_PER_LEVEL}px` }}
|
||||
{...createFolderDragHandlers(folder.id, parentFolderId)}
|
||||
>
|
||||
<FolderItem folder={folder} />
|
||||
<FolderItem workspaceId={workspaceId} folder={folder} />
|
||||
</div>
|
||||
<DropIndicatorLine show={showAfter} level={level} position='after' />
|
||||
|
||||
@@ -471,6 +480,7 @@ export const WorkflowList = memo(function WorkflowList({
|
||||
)
|
||||
},
|
||||
[
|
||||
workspaceId,
|
||||
workflowsByFolder,
|
||||
expandedFolders,
|
||||
dropIndicator,
|
||||
|
||||
@@ -65,7 +65,7 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
const hoverExpandTimerRef = useRef<number | null>(null)
|
||||
const lastDragYRef = useRef<number>(0)
|
||||
const draggedSourceFolderRef = useRef<string | null>(null)
|
||||
const siblingsCacheRef = useRef<Map<string, SiblingItem[]>>(new Map())
|
||||
const siblingsCacheRef = useRef<Map<string, SiblingItem[]> | null>(null)
|
||||
const isDraggingRef = useRef(false)
|
||||
|
||||
const params = useParams()
|
||||
@@ -152,7 +152,7 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
}, [hoverFolderId, isDragging, expandedFolders, setExpanded])
|
||||
|
||||
useEffect(() => {
|
||||
siblingsCacheRef.current.clear()
|
||||
siblingsCacheRef.current?.clear()
|
||||
}, [workspaceId])
|
||||
|
||||
const calculateDropPosition = useCallback(
|
||||
@@ -276,7 +276,7 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
(folderId: string | null): SiblingItem[] => {
|
||||
const cacheKey = folderId ?? 'root'
|
||||
if (!isDraggingRef.current) {
|
||||
const cached = siblingsCacheRef.current.get(cacheKey)
|
||||
const cached = siblingsCacheRef.current?.get(cacheKey)
|
||||
if (cached) return cached
|
||||
}
|
||||
|
||||
@@ -302,7 +302,8 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
].sort(compareSiblingItems)
|
||||
|
||||
if (!isDraggingRef.current) {
|
||||
siblingsCacheRef.current.set(cacheKey, siblings)
|
||||
const cache = (siblingsCacheRef.current ??= new Map())
|
||||
cache.set(cacheKey, siblings)
|
||||
}
|
||||
return siblings
|
||||
},
|
||||
@@ -474,7 +475,7 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
setDropIndicator(null)
|
||||
isDraggingRef.current = false
|
||||
setIsDragging(false)
|
||||
siblingsCacheRef.current.clear()
|
||||
siblingsCacheRef.current?.clear()
|
||||
|
||||
if (!indicator) return
|
||||
|
||||
@@ -614,7 +615,7 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
|
||||
const handleDragStart = useCallback((sourceFolderId: string | null) => {
|
||||
draggedSourceFolderRef.current = sourceFolderId
|
||||
siblingsCacheRef.current.clear()
|
||||
siblingsCacheRef.current?.clear()
|
||||
isDraggingRef.current = true
|
||||
setIsDragging(true)
|
||||
}, [])
|
||||
@@ -626,7 +627,7 @@ export function useDragDrop(options: UseDragDropOptions = {}) {
|
||||
setDropIndicator(null)
|
||||
draggedSourceFolderRef.current = null
|
||||
setHoverFolderId(null)
|
||||
siblingsCacheRef.current.clear()
|
||||
siblingsCacheRef.current?.clear()
|
||||
}, [])
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
+20
-2
@@ -1,12 +1,18 @@
|
||||
'use client'
|
||||
|
||||
import { createContext, useContext, useMemo } from 'react'
|
||||
import { createContext, type RefObject, useContext, useMemo } from 'react'
|
||||
|
||||
interface SidebarListContextValue {
|
||||
/** Whether any drag operation is currently in progress */
|
||||
isAnyDragActive: boolean
|
||||
/** Whether item dragging is disabled (e.g. viewer permissions) */
|
||||
dragDisabled: boolean
|
||||
/**
|
||||
* Live id of the workflow open in the URL, held in a ref so rows read it at
|
||||
* click/delete time without subscribing to `useParams` — which would re-render
|
||||
* every row on each tab navigation and defeat their memoization.
|
||||
*/
|
||||
activeWorkflowIdRef: RefObject<string | undefined>
|
||||
/** Selects a workflow on click (single or shift-range selection) */
|
||||
onWorkflowClick: (workflowId: string, shiftKey: boolean) => void
|
||||
/** Selects a folder on modifier-click (shift-range or cmd/ctrl-toggle selection) */
|
||||
@@ -18,6 +24,7 @@ interface SidebarListContextValue {
|
||||
}
|
||||
|
||||
const noop = () => {}
|
||||
const noopActiveWorkflowIdRef: RefObject<string | undefined> = { current: undefined }
|
||||
|
||||
/**
|
||||
* Context for sharing list-item interaction handlers and drag state across
|
||||
@@ -27,6 +34,7 @@ const noop = () => {}
|
||||
export const SidebarListContext = createContext<SidebarListContextValue>({
|
||||
isAnyDragActive: false,
|
||||
dragDisabled: false,
|
||||
activeWorkflowIdRef: noopActiveWorkflowIdRef,
|
||||
onWorkflowClick: noop,
|
||||
onFolderClick: noop,
|
||||
onItemDragStart: noop,
|
||||
@@ -55,6 +63,7 @@ export function useSidebarListContextValue(
|
||||
const {
|
||||
isAnyDragActive,
|
||||
dragDisabled,
|
||||
activeWorkflowIdRef,
|
||||
onWorkflowClick,
|
||||
onFolderClick,
|
||||
onItemDragStart,
|
||||
@@ -65,11 +74,20 @@ export function useSidebarListContextValue(
|
||||
() => ({
|
||||
isAnyDragActive,
|
||||
dragDisabled,
|
||||
activeWorkflowIdRef,
|
||||
onWorkflowClick,
|
||||
onFolderClick,
|
||||
onItemDragStart,
|
||||
onItemDragEnd,
|
||||
}),
|
||||
[isAnyDragActive, dragDisabled, onWorkflowClick, onFolderClick, onItemDragStart, onItemDragEnd]
|
||||
[
|
||||
isAnyDragActive,
|
||||
dragDisabled,
|
||||
activeWorkflowIdRef,
|
||||
onWorkflowClick,
|
||||
onFolderClick,
|
||||
onItemDragStart,
|
||||
onItemDragEnd,
|
||||
]
|
||||
)
|
||||
}
|
||||
|
||||
+15
-10
@@ -1,4 +1,4 @@
|
||||
import { useCallback } from 'react'
|
||||
import { useCallback, useRef } from 'react'
|
||||
import { useShallow } from 'zustand/react/shallow'
|
||||
import { useFolderStore } from '@/stores/folders/store'
|
||||
|
||||
@@ -40,6 +40,17 @@ export function useWorkflowSelection({
|
||||
}))
|
||||
)
|
||||
|
||||
/**
|
||||
* Read the click-time anchor values through refs so `handleWorkflowClick` keeps a stable
|
||||
* identity across tab navigation. `activeWorkflowId` changes on every route change; without
|
||||
* refs it would churn the shared sidebar list context on each tab switch and defeat the
|
||||
* memoization of the WorkflowItem/FolderItem rows that consume it.
|
||||
*/
|
||||
const workflowIdsRef = useRef(workflowIds)
|
||||
workflowIdsRef.current = workflowIds
|
||||
const activeWorkflowIdRef = useRef(activeWorkflowId)
|
||||
activeWorkflowIdRef.current = activeWorkflowId
|
||||
|
||||
/**
|
||||
* After a workflow selection change, deselect any folder that is an ancestor of a selected
|
||||
* workflow to prevent ancestor-descendant co-selection.
|
||||
@@ -68,8 +79,9 @@ export function useWorkflowSelection({
|
||||
*/
|
||||
const handleWorkflowClick = useCallback(
|
||||
(workflowId: string, shiftKey: boolean) => {
|
||||
const activeWorkflowId = activeWorkflowIdRef.current
|
||||
if (shiftKey && activeWorkflowId && activeWorkflowId !== workflowId) {
|
||||
selectRange(workflowIds, activeWorkflowId, workflowId)
|
||||
selectRange(workflowIdsRef.current, activeWorkflowId, workflowId)
|
||||
deselectConflictingFolders()
|
||||
} else if (shiftKey) {
|
||||
toggleWorkflowSelection(workflowId)
|
||||
@@ -78,14 +90,7 @@ export function useWorkflowSelection({
|
||||
selectOnly(workflowId)
|
||||
}
|
||||
},
|
||||
[
|
||||
workflowIds,
|
||||
activeWorkflowId,
|
||||
selectOnly,
|
||||
selectRange,
|
||||
toggleWorkflowSelection,
|
||||
deselectConflictingFolders,
|
||||
]
|
||||
[selectOnly, selectRange, toggleWorkflowSelection, deselectConflictingFolders]
|
||||
)
|
||||
|
||||
return {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useMemo, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { getFolderById } from '@/lib/folders/tree'
|
||||
import {
|
||||
downloadFile,
|
||||
@@ -19,6 +18,10 @@ import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
|
||||
const logger = createLogger('useExportFolder')
|
||||
|
||||
interface UseExportFolderProps {
|
||||
/**
|
||||
* Active workspace id
|
||||
*/
|
||||
workspaceId: string | undefined
|
||||
/**
|
||||
* The folder ID to export
|
||||
*/
|
||||
@@ -91,8 +94,7 @@ function collectSubfolders(
|
||||
/**
|
||||
* Hook for managing folder export to ZIP.
|
||||
*/
|
||||
export function useExportFolder({ folderId, onSuccess }: UseExportFolderProps) {
|
||||
const { workspaceId } = useParams<{ workspaceId: string }>()
|
||||
export function useExportFolder({ workspaceId, folderId, onSuccess }: UseExportFolderProps) {
|
||||
const { data: workflows = {} } = useWorkflowMap(workspaceId)
|
||||
const { data: folders = {} } = useFolderMap(workspaceId)
|
||||
const [isExporting, setIsExporting] = useState(false)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useParams } from 'next/navigation'
|
||||
import {
|
||||
downloadFile,
|
||||
exportWorkflowsToZip,
|
||||
@@ -17,6 +16,10 @@ import type { WorkflowMetadata } from '@/stores/workflows/registry/types'
|
||||
const logger = createLogger('useExportSelection')
|
||||
|
||||
interface UseExportSelectionProps {
|
||||
/**
|
||||
* Active workspace id
|
||||
*/
|
||||
workspaceId: string | undefined
|
||||
/**
|
||||
* Optional callback after successful export
|
||||
*/
|
||||
@@ -88,11 +91,8 @@ function collectSubfoldersForMultipleFolders(
|
||||
* Handles mixed selection by collecting all workflows from selected folders
|
||||
* and combining with directly selected workflows.
|
||||
*/
|
||||
export function useExportSelection({ onSuccess }: UseExportSelectionProps = {}) {
|
||||
export function useExportSelection({ workspaceId, onSuccess }: UseExportSelectionProps) {
|
||||
const [isExporting, setIsExporting] = useState(false)
|
||||
const params = useParams()
|
||||
const workspaceId = params.workspaceId as string | undefined
|
||||
|
||||
const onSuccessRef = useRef(onSuccess)
|
||||
onSuccessRef.current = onSuccess
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { useCallback, useRef, useState } from 'react'
|
||||
import { createLogger } from '@sim/logger'
|
||||
import { useParams } from 'next/navigation'
|
||||
import { usePostHog } from 'posthog-js/react'
|
||||
import { captureEvent } from '@/lib/posthog/client'
|
||||
import {
|
||||
@@ -16,6 +15,10 @@ import { useFolderStore } from '@/stores/folders/store'
|
||||
const logger = createLogger('useExportWorkflow')
|
||||
|
||||
interface UseExportWorkflowProps {
|
||||
/**
|
||||
* Active workspace id
|
||||
*/
|
||||
workspaceId: string | undefined
|
||||
/**
|
||||
* Optional callback after successful export
|
||||
*/
|
||||
@@ -25,10 +28,8 @@ interface UseExportWorkflowProps {
|
||||
/**
|
||||
* Hook for managing workflow export to JSON or ZIP.
|
||||
*/
|
||||
export function useExportWorkflow({ onSuccess }: UseExportWorkflowProps = {}) {
|
||||
export function useExportWorkflow({ workspaceId, onSuccess }: UseExportWorkflowProps) {
|
||||
const [isExporting, setIsExporting] = useState(false)
|
||||
const params = useParams()
|
||||
const workspaceId = params.workspaceId as string | undefined
|
||||
const posthog = usePostHog()
|
||||
|
||||
const onSuccessRef = useRef(onSuccess)
|
||||
|
||||
Reference in New Issue
Block a user