From 44c8f0556a15d36af0a3b53980bc302fb3b39fce Mon Sep 17 00:00:00 2001 From: Adam Gough Date: Thu, 22 May 2025 10:40:23 -0700 Subject: [PATCH] fix: added greptile changes --- .../components/deployed-workflow-card.tsx | 8 +-- .../components/deployed-workflow-modal.tsx | 34 +++------- .../deployment-controls.tsx | 63 +++---------------- .../components/control-bar/control-bar.tsx | 16 +++-- .../sub-block/components/short-input.tsx | 11 +++- .../sub-block/components/webhook/webhook.tsx | 6 +- .../sub-block/hooks/use-sub-block-value.ts | 2 +- .../workflow-preview/workflow-preview.tsx | 23 ++++++- 8 files changed, 62 insertions(+), 101 deletions(-) diff --git a/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-card.tsx b/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-card.tsx index fe26e855a6..025e60e069 100644 --- a/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-card.tsx +++ b/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-card.tsx @@ -68,12 +68,12 @@ export function DeployedWorkflowCard({ .filter(([_, block]) => block && block.type) // Filter out invalid blocks .map(([id, block]) => { // Deep clone the block to avoid any reference sharing - const clonedBlock = JSON.parse(JSON.stringify(block)); + const clonedBlock = structuredClone(block); return [id, clonedBlock]; }) ), - edges: workflowToShow.edges ? JSON.parse(JSON.stringify(workflowToShow.edges)) : [], - loops: workflowToShow.loops ? JSON.parse(JSON.stringify(workflowToShow.loops)) : {}, + edges: workflowToShow.edges ? structuredClone(workflowToShow.edges) : [], + loops: workflowToShow.loops ? structuredClone(workflowToShow.loops) : {}, _metadata: { ...(workflowToShow._metadata || {}), workflowId: activeWorkflowId, @@ -87,7 +87,7 @@ export function DeployedWorkflowCard({ // Generate a unique key for the workflow preview const previewKey = useMemo(() => { - return `${showingDeployed ? 'deployed' : 'current'}-preview-${activeWorkflowId}-${Date.now()}`; + return `${showingDeployed ? 'deployed' : 'current'}-preview-${activeWorkflowId}}`; }, [showingDeployed, activeWorkflowId]); return ( diff --git a/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-modal.tsx b/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-modal.tsx index 1772fd30bb..24759228f9 100644 --- a/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-modal.tsx +++ b/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/components/deployed-workflow-modal.tsx @@ -53,30 +53,17 @@ export function DeployedWorkflowModal({ deployedWorkflowState, }: DeployedWorkflowModalProps) { const [showRevertDialog, setShowRevertDialog] = useState(false) - const [isLoading, setIsLoading] = useState(false) const { revertToDeployedState } = useWorkflowStore() const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId) // Add instance ID to track component lifecycle - const instanceId = useRef(Date.now()); const modalOpenCount = useRef(0); - // Keep track of the original deployed state when modal opens - const initialDeployedStateRef = useRef(null); - useEffect(() => { if (isOpen) { modalOpenCount.current += 1; - - // Store the initial deployed state when modal first opens - if (!initialDeployedStateRef.current) { - initialDeployedStateRef.current = deployedWorkflowState; - } - } else if (initialDeployedStateRef.current) { - // Reset the initial state reference when modal closes - initialDeployedStateRef.current = null; } - }, [isOpen, deployedWorkflowState, activeWorkflowId]); + }, [isOpen]); // Get current workflow state to compare with deployed state const currentWorkflowState = useWorkflowStore((state) => ({ @@ -96,7 +83,7 @@ export function DeployedWorkflowModal({ .filter(([_, block]) => block && block.type) .map(([id, block]) => { // Deep clone the block to avoid any reference sharing - return [id, JSON.parse(JSON.stringify(block))]; + return [id, structuredClone(block)]; }) ), edges: currentWorkflowState.edges ? [...currentWorkflowState.edges] : [], @@ -154,7 +141,7 @@ export function DeployedWorkflowModal({ .filter(([_, block]) => block && block.type) .map(([id, block]) => { // Deep clone the block to avoid any reference sharing - return [id, JSON.parse(JSON.stringify(block))]; + return [id, structuredClone(block)]; }) ), edges: deployedWorkflowState.edges ? [...deployedWorkflowState.edges] : [], @@ -189,17 +176,10 @@ export function DeployedWorkflowModal({ Deployed Workflow - - {isLoading ? ( -
-
-
- ) : ( - - )} +
{needsRedeployment && ( diff --git a/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/deployment-controls.tsx b/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/deployment-controls.tsx index af23f15ee7..6f7babd045 100644 --- a/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/deployment-controls.tsx +++ b/apps/sim/app/w/[id]/components/control-bar/components/deployment-controls/deployment-controls.tsx @@ -1,25 +1,19 @@ 'use client' -<<<<<<< HEAD -import { useEffect, useState } from 'react' -======= import { useState, useEffect, useRef } from 'react' ->>>>>>> 2d314bcc (fix: deployed state preview persists across workflows) import { Loader2, Rocket } from 'lucide-react' import { Button } from '@/components/ui/button' import { Tooltip, TooltipContent, TooltipTrigger } from '@/components/ui/tooltip' -import { createLogger } from '@/lib/logs/console-logger' import { cn } from '@/lib/utils' import { useWorkflowRegistry } from '@/stores/workflows/registry/store' +import { WorkflowState } from '@/stores/workflows/workflow/types' import { DeployModal } from '../deploy-modal/deploy-modal' -const _logger = createLogger('DeploymentControls') - interface DeploymentControlsProps { activeWorkflowId: string | null needsRedeployment: boolean setNeedsRedeployment: (value: boolean) => void - deployedState: any + deployedState: WorkflowState | null isLoadingDeployedState: boolean refetchDeployedState: () => Promise } @@ -47,65 +41,24 @@ export function DeploymentControls({ const [isDeploying, _setIsDeploying] = useState(false) const [isModalOpen, setIsModalOpen] = useState(false) - // Add a ref to track the last seen workflow ID and deployed state + // Track the last seen workflow ID const lastWorkflowIdRef = useRef(null) - const lastDeployedStateRef = useRef(null) - // Log when workflow ID changes + // Update last seen workflow ID useEffect(() => { if (activeWorkflowId !== lastWorkflowIdRef.current) { - logger.info('Workflow ID changed in DeploymentControls', { - previousId: lastWorkflowIdRef.current, - currentId: activeWorkflowId, - timestamp: Date.now() - }); lastWorkflowIdRef.current = activeWorkflowId; } }, [activeWorkflowId]); - // Log when deployed state changes - useEffect(() => { - if (deployedState && deployedState !== lastDeployedStateRef.current) { - const blockIds = Object.keys(deployedState?.blocks || {}); - logger.info('Deployed state changed in DeploymentControls', { - workflowId: activeWorkflowId, - blockCount: blockIds.length, - blockIds: JSON.stringify(blockIds.slice(0, 3)), - isLoadingState: isLoadingDeployedState, - timestamp: Date.now(), - stateHash: JSON.stringify(deployedState).length - }); - lastDeployedStateRef.current = deployedState; - } - }, [deployedState, activeWorkflowId, isLoadingDeployedState]); - - // Add wrapper around refetchDeployedState to track timing - const refetchWithLogging = async () => { + // Refetch deployed state wrapper + const refetchWithErrorHandling = async () => { if (!activeWorkflowId) return; - const fetchId = Date.now(); - logger.info('Starting deployedState refetch', { - workflowId: activeWorkflowId, - fetchId, - timestamp: Date.now() - }); - try { await refetchDeployedState(); - - logger.info('Completed deployedState refetch', { - workflowId: activeWorkflowId, - fetchId, - timestamp: Date.now(), - duration: Date.now() - fetchId - }); } catch (error) { - logger.error('Error in deployedState refetch', { - workflowId: activeWorkflowId, - fetchId, - error, - timestamp: Date.now() - }); + // Silent error handling } }; @@ -174,7 +127,7 @@ export function DeploymentControls({ setNeedsRedeployment={setNeedsRedeployment} deployedState={deployedState} isLoadingDeployedState={isLoadingDeployedState} - refetchDeployedState={refetchWithLogging} + refetchDeployedState={refetchWithErrorHandling} /> ) diff --git a/apps/sim/app/w/[id]/components/control-bar/control-bar.tsx b/apps/sim/app/w/[id]/components/control-bar/control-bar.tsx index 313c089e20..ff5b9b0dbb 100644 --- a/apps/sim/app/w/[id]/components/control-bar/control-bar.tsx +++ b/apps/sim/app/w/[id]/components/control-bar/control-bar.tsx @@ -218,11 +218,10 @@ export function ControlBar() { // Function to check if redeployment is needed const checkForChanges = async () => { // Skip if we're already showing needsRedeployment - if (needsRedeployment) return - + // Reset the pending changes counter - pendingChanges = 0 - lastCheckTime = Date.now() + pendingChanges = 0; + lastCheckTime = Date.now(); try { // Get the deployed state from the API @@ -235,6 +234,10 @@ export function ControlBar() { setNeedsRedeployment(true) // Also update the store state so other components can access this flag useWorkflowStore.getState().setNeedsRedeploymentFlag(true) + } else { + // Add this else branch to handle the case when changes are reverted + setNeedsRedeployment(false) + useWorkflowStore.getState().setNeedsRedeploymentFlag(false) } } } catch (error) { @@ -397,11 +400,6 @@ export function ControlBar() { if (isDeployed) { setNeedsRedeployment(false) useWorkflowStore.getState().setNeedsRedeploymentFlag(false) - - // When a workflow is newly deployed, we need to fetch the deployed state - if (activeWorkflowId && deployedState === null) { - // We'll fetch the deployed state in the other useEffect - } } else { // If workflow is undeployed, clear the deployed state setDeployedState(null) diff --git a/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/short-input.tsx b/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/short-input.tsx index 513346410a..3a837677ec 100644 --- a/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/short-input.tsx +++ b/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/short-input.tsx @@ -37,12 +37,21 @@ export function ShortInput({ const [isFocused, setIsFocused] = useState(false) const [showEnvVars, setShowEnvVars] = useState(false) const [showTags, setShowTags] = useState(false) + const validatePropValue = (value: any): string => { + if (value === undefined || value === null) return ''; + if (typeof value === 'string') return value; + try { + return String(value); + } catch { + return ''; + } + } const [storeValue, setStoreValue] = useSubBlockValue( blockId, subBlockId, false, // No workflow update needed isPreview, - propValue + validatePropValue(propValue) ) const [searchTerm, setSearchTerm] = useState('') const [cursorPosition, setCursorPosition] = useState(0) diff --git a/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/webhook/webhook.tsx b/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/webhook/webhook.tsx index 929bfae674..b501b5cfb9 100644 --- a/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/webhook/webhook.tsx +++ b/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/components/webhook/webhook.tsx @@ -288,7 +288,11 @@ interface WebhookConfigProps { subBlockId?: string isConnecting: boolean isPreview?: boolean - value?: any + value?: { + webhookProvider?: string + webhookPath?: string + providerConfig?: ProviderConfig + } } export function WebhookConfig({ diff --git a/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/hooks/use-sub-block-value.ts b/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/hooks/use-sub-block-value.ts index a00af9305d..d578a62eaa 100644 --- a/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/hooks/use-sub-block-value.ts +++ b/apps/sim/app/w/[id]/components/workflow-block/components/sub-block/hooks/use-sub-block-value.ts @@ -237,7 +237,7 @@ export function useSubBlockValue( // Otherwise use the store value or initial value valueRef.current = storeValue !== undefined ? storeValue : initialValue; } - }, [storeValue, initialValue]) + }, [storeValue, initialValue, isPreview]) // Update the ref if the store value changes // This ensures we're always working with the latest value diff --git a/apps/sim/app/w/components/workflow-preview/workflow-preview.tsx b/apps/sim/app/w/components/workflow-preview/workflow-preview.tsx index ce7c6b1dec..ee9209ca43 100644 --- a/apps/sim/app/w/components/workflow-preview/workflow-preview.tsx +++ b/apps/sim/app/w/components/workflow-preview/workflow-preview.tsx @@ -1,6 +1,6 @@ 'use client' -import { useMemo, useEffect } from 'react' +import { useMemo } from 'react' import { cloneDeep } from 'lodash' import ReactFlow, { Background, @@ -78,6 +78,22 @@ export function WorkflowPreview({ defaultPosition, defaultZoom, }: WorkflowPreviewProps) { + // Track structure changes efficiently + const blocksStructure = useMemo(() => ({ + count: Object.keys(workflowState.blocks || {}).length, + ids: Object.keys(workflowState.blocks || {}).join(',') + }), [workflowState.blocks]); + + const loopsStructure = useMemo(() => ({ + count: Object.keys(workflowState.loops || {}).length, + ids: Object.keys(workflowState.loops || {}).join(',') + }), [workflowState.loops]); + + const edgesStructure = useMemo(() => ({ + count: workflowState.edges.length, + ids: workflowState.edges.map(e => e.id).join(',') + }), [workflowState.edges]); + // Transform blocks and loops into ReactFlow nodes const nodes: Node[] = useMemo(() => { const nodeArray: Node[] = [] @@ -167,8 +183,9 @@ export function WorkflowPreview({ }); }) + return nodeArray - }, [JSON.stringify(workflowState.blocks), JSON.stringify(workflowState.loops), showSubBlocks]) + }, [blocksStructure, loopsStructure, showSubBlocks, workflowState.blocks, workflowState.loops]) // Transform edges const edges: Edge[] = useMemo(() => { @@ -180,7 +197,7 @@ export function WorkflowPreview({ targetHandle: edge.targetHandle, type: 'workflowEdge', })) - }, [JSON.stringify(workflowState.edges)]) + }, [edgesStructure, workflowState.edges]) return (