From c5f3c331f051be0ca7b8780fbd8951d64eb91103 Mon Sep 17 00:00:00 2001 From: waleed Date: Mon, 10 Nov 2025 15:50:36 -0800 Subject: [PATCH] feat(workflow-block): added redeploy action to workflow header for workflow block --- .../hooks/use-child-deployment.ts | 50 +++++++++++++++---- .../hooks/use-child-workflow.ts | 4 ++ .../workflow-block/workflow-block.tsx | 42 +++++++++++----- 3 files changed, 72 insertions(+), 24 deletions(-) diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-deployment.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-deployment.ts index dfbc512829..bdd0240708 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-deployment.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-deployment.ts @@ -8,6 +8,8 @@ export interface UseChildDeploymentReturn { activeVersion: number | null /** Whether the child workflow has an active deployment */ isDeployed: boolean | null + /** Whether the child workflow needs redeployment due to changes */ + needsRedeploy: boolean /** Whether the deployment information is currently being fetched */ isLoading: boolean /** Function to manually refetch deployment status */ @@ -23,6 +25,7 @@ export interface UseChildDeploymentReturn { export function useChildDeployment(childWorkflowId: string | undefined): UseChildDeploymentReturn { const [activeVersion, setActiveVersion] = useState(null) const [isDeployed, setIsDeployed] = useState(null) + const [needsRedeploy, setNeedsRedeploy] = useState(false) const [isLoading, setIsLoading] = useState(false) const [refetchTrigger, setRefetchTrigger] = useState(0) @@ -31,37 +34,60 @@ export function useChildDeployment(childWorkflowId: string | undefined): UseChil try { setIsLoading(true) - const res = await fetch(`/api/workflows/${wfId}/deployments`, { - cache: 'no-store', - headers: { 'Cache-Control': 'no-cache' }, - }) - if (!res.ok) { + // Fetch both deployment versions and workflow metadata in parallel + const [deploymentsRes, workflowRes] = await Promise.all([ + fetch(`/api/workflows/${wfId}/deployments`, { + cache: 'no-store', + headers: { 'Cache-Control': 'no-cache' }, + }), + fetch(`/api/workflows/${wfId}`, { + cache: 'no-store', + headers: { 'Cache-Control': 'no-cache' }, + }), + ]) + + if (!deploymentsRes.ok || !workflowRes.ok) { if (!cancelled) { setActiveVersion(null) setIsDeployed(null) + setNeedsRedeploy(false) } return } - const json = await res.json() - const versions = Array.isArray(json?.data?.versions) - ? json.data.versions - : Array.isArray(json?.versions) - ? json.versions + const deploymentsJson = await deploymentsRes.json() + const workflowJson = await workflowRes.json() + + const versions = Array.isArray(deploymentsJson?.data?.versions) + ? deploymentsJson.data.versions + : Array.isArray(deploymentsJson?.versions) + ? deploymentsJson.versions : [] const active = versions.find((v: any) => v.isActive) + const workflowUpdatedAt = workflowJson?.data?.updatedAt || workflowJson?.updatedAt if (!cancelled) { const v = active ? Number(active.version) : null + const deployed = v != null setActiveVersion(v) - setIsDeployed(v != null) // true if deployed, false if undeployed + setIsDeployed(deployed) + + // Check if workflow has been updated since deployment + if (deployed && active?.createdAt && workflowUpdatedAt) { + const deploymentTime = new Date(active.createdAt).getTime() + const updateTime = new Date(workflowUpdatedAt).getTime() + setNeedsRedeploy(updateTime > deploymentTime) + } else { + setNeedsRedeploy(false) + } } } catch { if (!cancelled) { setActiveVersion(null) setIsDeployed(null) + setNeedsRedeploy(false) } } finally { if (!cancelled) setIsLoading(false) @@ -78,6 +104,7 @@ export function useChildDeployment(childWorkflowId: string | undefined): UseChil } else { setActiveVersion(null) setIsDeployed(null) + setNeedsRedeploy(false) } }, [childWorkflowId, refetchTrigger, fetchActiveVersion]) @@ -88,6 +115,7 @@ export function useChildDeployment(childWorkflowId: string | undefined): UseChil return { activeVersion, isDeployed, + needsRedeploy, isLoading, refetch, } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-workflow.ts b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-workflow.ts index f3f61ae1f9..bd62a9a916 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-workflow.ts +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/hooks/use-child-workflow.ts @@ -12,6 +12,8 @@ export interface UseChildWorkflowReturn { childActiveVersion: number | null /** Whether the child workflow is deployed */ childIsDeployed: boolean | null + /** Whether the child workflow needs redeployment due to changes */ + childNeedsRedeploy: boolean /** Whether the child version information is loading */ isLoadingChildVersion: boolean /** Function to manually refetch deployment status */ @@ -54,6 +56,7 @@ export function useChildWorkflow( const { activeVersion: childActiveVersion, isDeployed: childIsDeployed, + needsRedeploy: childNeedsRedeploy, isLoading: isLoadingChildVersion, refetch: refetchDeployment, } = useChildDeployment(isWorkflowSelector ? childWorkflowId : undefined) @@ -62,6 +65,7 @@ export function useChildWorkflow( childWorkflowId, childActiveVersion, childIsDeployed, + childNeedsRedeploy, isLoadingChildVersion, refetchDeployment, } diff --git a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx index fb279cb53f..66001e35c7 100644 --- a/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx +++ b/apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/workflow-block/workflow-block.tsx @@ -214,12 +214,8 @@ export const WorkflowBlock = memo(function WorkflowBlock({ disableSchedule, } = useScheduleInfo(id, type, currentWorkflowId) - const { childWorkflowId, childIsDeployed, refetchDeployment } = useChildWorkflow( - id, - type, - data.isPreview ?? false, - data.subBlockValues - ) + const { childWorkflowId, childIsDeployed, childNeedsRedeploy, refetchDeployment } = + useChildWorkflow(id, type, data.isPreview ?? false, data.subBlockValues) const [isDeploying, setIsDeploying] = useState(false) const setDeploymentStatus = useWorkflowRegistry((state) => state.setDeploymentStatus) @@ -656,24 +652,44 @@ export const WorkflowBlock = memo(function WorkflowBlock({ { e.stopPropagation() - if (!childIsDeployed && childWorkflowId && !isDeploying) { + if ( + (!childIsDeployed || childNeedsRedeploy) && + childWorkflowId && + !isDeploying + ) { deployWorkflow(childWorkflowId) } }} > - {isDeploying ? 'Deploying...' : childIsDeployed ? 'deployed' : 'undeployed'} + {isDeploying + ? 'Deploying...' + : !childIsDeployed + ? 'undeployed' + : childNeedsRedeploy + ? 'redeploy' + : 'deployed'} - {!childIsDeployed && ( + {(!childIsDeployed || childNeedsRedeploy) && ( - Click to deploy + + {!childIsDeployed ? 'Click to deploy' : 'Click to redeploy'} + )}