mirror of
https://github.com/simstudioai/sim.git
synced 2026-09-24 15:45:35 +08:00
fix: deployed state preview persists across workflows
This commit is contained in:
+3
-4
@@ -1,7 +1,7 @@
|
||||
'use client'
|
||||
|
||||
import { useState } from 'react'
|
||||
import { Loader2 } from 'lucide-react'
|
||||
import { useState, useEffect } from 'react'
|
||||
import { Info, Loader2 } from 'lucide-react'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -66,11 +66,10 @@ export function DeploymentInfo({
|
||||
|
||||
// If deployedState is already loaded, use it directly
|
||||
if (deployedState) {
|
||||
logger.info(`Using cached deployed state for workflow: ${workflowId}`)
|
||||
setIsViewingDeployed(true)
|
||||
return
|
||||
} else if (!isLoadingDeployedState) {
|
||||
logger.debug(`[${workflowId}] No deployed state found`)
|
||||
logger.debug(`No deployed state found`)
|
||||
addNotification('error', 'Cannot view deployment: No deployed state available', workflowId)
|
||||
}
|
||||
}
|
||||
|
||||
+39
-5
@@ -8,6 +8,7 @@ import { Label } from '@/components/ui/label'
|
||||
import { cn } from '@/lib/utils'
|
||||
import { createLogger } from '@/lib/logs/console-logger'
|
||||
import { WorkflowPreview } from '@/app/w/components/workflow-preview/workflow-preview'
|
||||
import { useWorkflowRegistry } from '@/stores/workflows/registry/store'
|
||||
|
||||
const logger = createLogger('DeployedWorkflowCard')
|
||||
|
||||
@@ -16,11 +17,23 @@ interface DeployedWorkflowCardProps {
|
||||
blocks: Record<string, any>
|
||||
edges: Array<any>
|
||||
loops: Record<string, any>
|
||||
_metadata?: {
|
||||
workflowId?: string
|
||||
fetchTimestamp?: number
|
||||
requestId?: number
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
deployedWorkflowState: {
|
||||
blocks: Record<string, any>
|
||||
edges: Array<any>
|
||||
loops: Record<string, any>
|
||||
_metadata?: {
|
||||
workflowId?: string
|
||||
fetchTimestamp?: number
|
||||
requestId?: number
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
className?: string
|
||||
}
|
||||
@@ -32,13 +45,24 @@ export function DeployedWorkflowCard({
|
||||
}: DeployedWorkflowCardProps) {
|
||||
const [showingDeployed, setShowingDeployed] = useState(true)
|
||||
const workflowToShow = showingDeployed ? deployedWorkflowState : currentWorkflowState
|
||||
const activeWorkflowId = useWorkflowRegistry((state) => state.activeWorkflowId)
|
||||
|
||||
// Create sanitized workflow state
|
||||
const sanitizedWorkflowState = useMemo(() => {
|
||||
if (!workflowToShow) return null;
|
||||
|
||||
// Verify the workflow ID matches if metadata exists
|
||||
if (workflowToShow._metadata?.workflowId &&
|
||||
workflowToShow._metadata.workflowId !== activeWorkflowId) {
|
||||
logger.warn('Workflow ID mismatch detected in card', {
|
||||
stateWorkflowId: workflowToShow._metadata.workflowId,
|
||||
activeWorkflowId,
|
||||
isDeployed: showingDeployed
|
||||
});
|
||||
}
|
||||
|
||||
// Filter out invalid blocks and make deep clone to avoid reference issues
|
||||
return {
|
||||
const result = {
|
||||
blocks: Object.fromEntries(
|
||||
Object.entries(workflowToShow.blocks || {})
|
||||
.filter(([_, block]) => block && block.type) // Filter out invalid blocks
|
||||
@@ -49,14 +73,22 @@ export function DeployedWorkflowCard({
|
||||
})
|
||||
),
|
||||
edges: workflowToShow.edges ? JSON.parse(JSON.stringify(workflowToShow.edges)) : [],
|
||||
loops: workflowToShow.loops ? JSON.parse(JSON.stringify(workflowToShow.loops)) : {}
|
||||
loops: workflowToShow.loops ? JSON.parse(JSON.stringify(workflowToShow.loops)) : {},
|
||||
_metadata: {
|
||||
...(workflowToShow._metadata || {}),
|
||||
workflowId: activeWorkflowId,
|
||||
viewType: showingDeployed ? 'deployed' : 'current',
|
||||
sanitizedAt: Date.now()
|
||||
}
|
||||
};
|
||||
}, [workflowToShow]);
|
||||
|
||||
return result;
|
||||
}, [workflowToShow, showingDeployed, activeWorkflowId]);
|
||||
|
||||
// Generate a unique key for the workflow preview
|
||||
const previewKey = useMemo(() => {
|
||||
return `${showingDeployed ? 'deployed' : 'current'}-preview`;
|
||||
}, [showingDeployed]);
|
||||
return `${showingDeployed ? 'deployed' : 'current'}-preview-${activeWorkflowId}-${Date.now()}`;
|
||||
}, [showingDeployed, activeWorkflowId]);
|
||||
|
||||
return (
|
||||
<Card className={cn('relative overflow-hidden', className)}>
|
||||
@@ -94,6 +126,8 @@ export function DeployedWorkflowCard({
|
||||
</div>
|
||||
</div>
|
||||
</CardHeader>
|
||||
|
||||
<div className="h-px w-full bg-border shadow-sm"></div>
|
||||
|
||||
<CardContent className='p-0'>
|
||||
{/* Workflow preview with fixed height */}
|
||||
|
||||
+76
-46
@@ -1,6 +1,6 @@
|
||||
'use client'
|
||||
|
||||
import { useState, useMemo, useEffect } from 'react'
|
||||
import { useState, useMemo, useEffect, useRef } from 'react'
|
||||
import {
|
||||
AlertDialog,
|
||||
AlertDialogAction,
|
||||
@@ -36,6 +36,12 @@ interface DeployedWorkflowModalProps {
|
||||
edges: Array<any>
|
||||
loops: Record<string, any>
|
||||
parallels: Record<string, any>
|
||||
_metadata?: {
|
||||
workflowId?: string
|
||||
fetchTimestamp?: number
|
||||
requestId?: number
|
||||
[key: string]: any
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +54,27 @@ export function DeployedWorkflowModal({
|
||||
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<any>(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]);
|
||||
|
||||
// Get current workflow state to compare with deployed state
|
||||
const currentWorkflowState = useWorkflowStore((state) => ({
|
||||
@@ -61,11 +88,6 @@ export function DeployedWorkflowModal({
|
||||
const sanitizedCurrentState = useMemo(() => {
|
||||
if (!currentWorkflowState) return undefined;
|
||||
|
||||
logger.info('Before current state sanitization', {
|
||||
workflowId: activeWorkflowId,
|
||||
blockCount: Object.keys(currentWorkflowState.blocks || {}).length
|
||||
});
|
||||
|
||||
const result = {
|
||||
blocks: Object.fromEntries(
|
||||
Object.entries(currentWorkflowState.blocks || {})
|
||||
@@ -76,14 +98,14 @@ export function DeployedWorkflowModal({
|
||||
})
|
||||
),
|
||||
edges: currentWorkflowState.edges ? [...currentWorkflowState.edges] : [],
|
||||
loops: currentWorkflowState.loops ? {...currentWorkflowState.loops} : {}
|
||||
loops: currentWorkflowState.loops ? {...currentWorkflowState.loops} : {},
|
||||
_metadata: {
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
type: 'current',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
};
|
||||
|
||||
logger.info('After current state sanitization', {
|
||||
workflowId: activeWorkflowId,
|
||||
blockCount: Object.keys(result.blocks).length
|
||||
});
|
||||
|
||||
return result;
|
||||
}, [currentWorkflowState, activeWorkflowId]);
|
||||
|
||||
@@ -91,10 +113,40 @@ export function DeployedWorkflowModal({
|
||||
if (!deployedWorkflowState) return {
|
||||
blocks: {},
|
||||
edges: [],
|
||||
loops: {}
|
||||
loops: {},
|
||||
_metadata: {
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
type: 'deployed-empty',
|
||||
timestamp: Date.now()
|
||||
}
|
||||
};
|
||||
|
||||
return {
|
||||
const stateWorkflowId = deployedWorkflowState?._metadata?.workflowId;
|
||||
const stateMatch = stateWorkflowId === activeWorkflowId;
|
||||
|
||||
// Check if the deployed state belongs to the current workflow
|
||||
// This is a critical safety check to prevent showing the wrong workflow state
|
||||
if (stateWorkflowId && !stateMatch) {
|
||||
logger.error('Attempted to use deployed state from wrong workflow', {
|
||||
stateWorkflowId,
|
||||
activeWorkflowId,
|
||||
});
|
||||
|
||||
// Return empty state to prevent showing wrong workflow data
|
||||
return {
|
||||
blocks: {},
|
||||
edges: [],
|
||||
loops: {},
|
||||
_metadata: {
|
||||
workflowId: activeWorkflowId || undefined,
|
||||
type: 'deployed-empty-mismatch',
|
||||
originalWorkflowId: stateWorkflowId,
|
||||
timestamp: Date.now()
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
const result = {
|
||||
blocks: Object.fromEntries(
|
||||
Object.entries(deployedWorkflowState.blocks || {})
|
||||
.filter(([_, block]) => block && block.type)
|
||||
@@ -104,47 +156,25 @@ export function DeployedWorkflowModal({
|
||||
})
|
||||
),
|
||||
edges: deployedWorkflowState.edges ? [...deployedWorkflowState.edges] : [],
|
||||
loops: deployedWorkflowState.loops ? {...deployedWorkflowState.loops} : {}
|
||||
loops: deployedWorkflowState.loops ? {...deployedWorkflowState.loops} : {},
|
||||
_metadata: {
|
||||
...(deployedWorkflowState._metadata || {}),
|
||||
workflowId: deployedWorkflowState._metadata?.workflowId || activeWorkflowId || undefined,
|
||||
type: 'deployed-sanitized',
|
||||
sanitizedAt: Date.now()
|
||||
}
|
||||
};
|
||||
}, [deployedWorkflowState]);
|
||||
|
||||
return result;
|
||||
}, [deployedWorkflowState, activeWorkflowId]);
|
||||
|
||||
const handleRevert = () => {
|
||||
<<<<<<< HEAD
|
||||
// Revert to the deployed state
|
||||
revertToDeployedState(deployedWorkflowState)
|
||||
setShowRevertDialog(false)
|
||||
onClose()
|
||||
=======
|
||||
if (activeWorkflowId) {
|
||||
logger.info(`Reverting to deployed state for workflow: ${activeWorkflowId}`)
|
||||
revertToDeployedState(sanitizedDeployedState)
|
||||
setShowRevertDialog(false)
|
||||
onClose()
|
||||
}
|
||||
>>>>>>> 9594f7db (fix: good except for subblocks)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen && activeWorkflowId) {
|
||||
logger.info('DeployedWorkflowModal opened', {
|
||||
workflowId: activeWorkflowId,
|
||||
deployedStateBlockCount: Object.keys(deployedWorkflowState?.blocks || {}).length,
|
||||
currentStateBlockCount: Object.keys(currentWorkflowState?.blocks || {}).length,
|
||||
deployedStateChecksum: JSON.stringify(deployedWorkflowState).length,
|
||||
currentStateChecksum: JSON.stringify(currentWorkflowState).length
|
||||
});
|
||||
|
||||
// Log a sample of block IDs to verify they match expected workflow
|
||||
const deployedBlockIds = Object.keys(deployedWorkflowState?.blocks || {}).slice(0, 2);
|
||||
const currentBlockIds = Object.keys(currentWorkflowState?.blocks || {}).slice(0, 2);
|
||||
|
||||
logger.info('State block samples', {
|
||||
deployedBlockIds,
|
||||
currentBlockIds
|
||||
});
|
||||
}
|
||||
}, [isOpen, activeWorkflowId, deployedWorkflowState, currentWorkflowState]);
|
||||
|
||||
return (
|
||||
<Dialog open={isOpen} onOpenChange={onClose}>
|
||||
<DialogContent
|
||||
|
||||
+67
-1
@@ -1,6 +1,10 @@
|
||||
'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'
|
||||
@@ -42,6 +46,68 @@ 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
|
||||
const lastWorkflowIdRef = useRef<string | null>(null)
|
||||
const lastDeployedStateRef = useRef<any>(null)
|
||||
|
||||
// Log when workflow ID changes
|
||||
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 () => {
|
||||
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()
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
// Update parent component when workflow-specific status changes
|
||||
useEffect(() => {
|
||||
@@ -108,7 +174,7 @@ export function DeploymentControls({
|
||||
setNeedsRedeployment={setNeedsRedeployment}
|
||||
deployedState={deployedState}
|
||||
isLoadingDeployedState={isLoadingDeployedState}
|
||||
refetchDeployedState={refetchDeployedState}
|
||||
refetchDeployedState={refetchWithLogging}
|
||||
/>
|
||||
</>
|
||||
)
|
||||
|
||||
@@ -111,6 +111,11 @@ export function ControlBar() {
|
||||
// Add deployedState management
|
||||
const [deployedState, setDeployedState] = useState<any>(null)
|
||||
const [isLoadingDeployedState, setIsLoadingDeployedState] = useState<boolean>(false)
|
||||
|
||||
// Add refs to manage fetch state and prevent race conditions
|
||||
const abortControllerRef = useRef<AbortController | null>(null)
|
||||
const lastFetchedWorkflowIdRef = useRef<string | null>(null)
|
||||
const lastDeployedStateRef = useRef<boolean>(false)
|
||||
|
||||
// Workflow name editing state
|
||||
const [isEditing, setIsEditing] = useState(false)
|
||||
@@ -301,50 +306,68 @@ export function ControlBar() {
|
||||
* @returns Promise that resolves when the deployed state is fetched
|
||||
*/
|
||||
const fetchDeployedState = async (options = { forceRefetch: false }) => {
|
||||
// Cancel any in-flight requests
|
||||
if (abortControllerRef.current) {
|
||||
abortControllerRef.current.abort();
|
||||
}
|
||||
|
||||
// Create new abort controller for this request
|
||||
abortControllerRef.current = new AbortController();
|
||||
const { signal } = abortControllerRef.current;
|
||||
|
||||
const requestId = Date.now();
|
||||
const currentWorkflowId = activeWorkflowId;
|
||||
|
||||
// Skip fetching if we don't have an active workflow ID or it's not deployed
|
||||
// unless we're explicitly forcing a refetch
|
||||
if ((!activeWorkflowId || !isDeployed) && !options.forceRefetch) {
|
||||
if ((!currentWorkflowId || !isDeployed) && !options.forceRefetch) {
|
||||
setDeployedState(null);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
setIsLoadingDeployedState(true);
|
||||
const logMessage = options.forceRefetch
|
||||
? `[CENTRAL] Explicitly refetching deployed state for workflow: ${activeWorkflowId}`
|
||||
: `[CENTRAL] Fetching deployed state for workflow: ${activeWorkflowId} (Control Bar - Single Source of Truth)`;
|
||||
|
||||
logger.info(logMessage);
|
||||
// Pass the abort signal to the fetch call
|
||||
const response = await fetch(
|
||||
`/api/workflows/${currentWorkflowId}/deployed`,
|
||||
{ signal }
|
||||
);
|
||||
|
||||
const response = await fetch(`/api/workflows/${activeWorkflowId}/deployed`);
|
||||
if (!response.ok) {
|
||||
throw new Error(`Failed to fetch deployed state: ${response.status}`);
|
||||
}
|
||||
|
||||
const data = await response.json();
|
||||
|
||||
// Final workflow ID check before updating state
|
||||
if (currentWorkflowId !== activeWorkflowId) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (data.deployedState) {
|
||||
const successMessage = options.forceRefetch
|
||||
? 'Successfully refetched deployed state from DB after redeployment'
|
||||
: 'Successfully fetched deployed state from DB - This is the only place that should fetch deployed state';
|
||||
// Create a single deep clone with metadata
|
||||
const deployedStateWithMetadata = {
|
||||
...JSON.parse(JSON.stringify(data.deployedState)),
|
||||
_metadata: {
|
||||
workflowId: currentWorkflowId,
|
||||
fetchTimestamp: Date.now(),
|
||||
requestId
|
||||
}
|
||||
};
|
||||
|
||||
logger.info(successMessage);
|
||||
|
||||
// Create a deep clone to ensure no reference sharing with current state
|
||||
const deepClonedState = JSON.parse(JSON.stringify(data.deployedState));
|
||||
logger.info('deepClonedState', deepClonedState);
|
||||
setDeployedState(deepClonedState);
|
||||
setDeployedState(deployedStateWithMetadata);
|
||||
} else {
|
||||
const warningMessage = options.forceRefetch
|
||||
? 'No deployed state found in the database after refetch'
|
||||
: 'No deployed state found in the database';
|
||||
|
||||
logger.warn(warningMessage);
|
||||
setDeployedState(null);
|
||||
}
|
||||
} catch (error) {
|
||||
logger.error('Error fetching deployed state:', error);
|
||||
setDeployedState(null);
|
||||
} catch (error: unknown) {
|
||||
// Don't log AbortError as it's expected when cancelling requests
|
||||
if (error instanceof Error && error.name === 'AbortError') {
|
||||
// Silently ignore abort errors
|
||||
} else {
|
||||
logger.error(`Error fetching deployed state:`, { error });
|
||||
setDeployedState(null);
|
||||
}
|
||||
} finally {
|
||||
setIsLoadingDeployedState(false);
|
||||
}
|
||||
@@ -355,7 +378,16 @@ export function ControlBar() {
|
||||
|
||||
// Fetch deployed state when the workflow ID changes or deployment status changes
|
||||
useEffect(() => {
|
||||
fetchDeployedState();
|
||||
// Only fetch if the workflow ID or deployed status has actually changed
|
||||
if (activeWorkflowId !== lastFetchedWorkflowIdRef.current ||
|
||||
isDeployed !== lastDeployedStateRef.current) {
|
||||
|
||||
// Update refs to track what we're fetching for
|
||||
lastFetchedWorkflowIdRef.current = activeWorkflowId;
|
||||
lastDeployedStateRef.current = isDeployed;
|
||||
|
||||
fetchDeployedState();
|
||||
}
|
||||
}, [activeWorkflowId, isDeployed]);
|
||||
|
||||
// Listen for deployment status changes
|
||||
|
||||
@@ -27,11 +27,7 @@ import { WorkflowEdge } from '@/app/w/[id]/components/workflow-edge/workflow-edg
|
||||
// import { LoopLabel } from '@/app/w/[id]/components/workflow-loop/components/loop-label/loop-label'
|
||||
// import { createLoopNode } from '@/app/w/[id]/components/workflow-loop/workflow-loop'
|
||||
import { getBlock } from '@/blocks'
|
||||
<<<<<<< HEAD
|
||||
import type { SubBlockConfig } from '@/blocks/types'
|
||||
=======
|
||||
import { cn } from '@/lib/utils'
|
||||
>>>>>>> 6f129dfc (fix: subblock rerender fixed)
|
||||
|
||||
const logger = createLogger('WorkflowPreview')
|
||||
|
||||
@@ -72,10 +68,6 @@ const edgeTypes: EdgeTypes = {
|
||||
workflowEdge: WorkflowEdge,
|
||||
}
|
||||
|
||||
// The subblocks should be getting passed from the state and not the subBlockStore.
|
||||
// Create optional parameter boolan isPreview to pass in the block state to know how to render
|
||||
// the subblocks
|
||||
|
||||
export function WorkflowPreview({
|
||||
workflowState,
|
||||
showSubBlocks = true,
|
||||
@@ -86,14 +78,6 @@ export function WorkflowPreview({
|
||||
defaultPosition,
|
||||
defaultZoom,
|
||||
}: WorkflowPreviewProps) {
|
||||
// Use effect to log the workflow state once outside of useMemo
|
||||
useEffect(() => {
|
||||
logger.info('WorkflowPreview received new state', {
|
||||
blockCount: Object.keys(workflowState?.blocks || {}).length,
|
||||
withSubBlocks: Object.values(workflowState?.blocks || {}).filter(b => b.subBlocks && Object.keys(b.subBlocks).length > 0).length,
|
||||
});
|
||||
}, [workflowState]);
|
||||
|
||||
// Transform blocks and loops into ReactFlow nodes
|
||||
const nodes: Node[] = useMemo(() => {
|
||||
const nodeArray: Node[] = []
|
||||
@@ -144,7 +128,6 @@ export function WorkflowPreview({
|
||||
subBlockValues: subBlocksClone, // Use the deep clone to avoid reference issues
|
||||
},
|
||||
})
|
||||
<<<<<<< HEAD
|
||||
|
||||
// Add children of this block if it's a loop
|
||||
if (block.type === 'loop') {
|
||||
@@ -178,14 +161,12 @@ export function WorkflowPreview({
|
||||
})
|
||||
})
|
||||
}
|
||||
=======
|
||||
logger.info(`Preview node created: ${blockId}`, {
|
||||
blockType: block.type,
|
||||
hasSubBlocks: block.subBlocks && Object.keys(block.subBlocks).length > 0
|
||||
});
|
||||
>>>>>>> 6f129dfc (fix: subblock rerender fixed)
|
||||
})
|
||||
|
||||
|
||||
return nodeArray
|
||||
}, [JSON.stringify(workflowState.blocks), JSON.stringify(workflowState.loops), showSubBlocks])
|
||||
|
||||
@@ -201,13 +182,12 @@ export function WorkflowPreview({
|
||||
}))
|
||||
}, [JSON.stringify(workflowState.edges)])
|
||||
|
||||
useEffect(() => {
|
||||
logger.info('Rendering workflow state', { workflowState })
|
||||
}, [workflowState])
|
||||
|
||||
return (
|
||||
<ReactFlowProvider>
|
||||
<div style={{ height, width }} className={cn(className, 'preview-mode')}>
|
||||
<div
|
||||
style={{ height, width }}
|
||||
className={cn(className, 'preview-mode')}
|
||||
>
|
||||
<ReactFlow
|
||||
nodes={nodes}
|
||||
edges={edges}
|
||||
|
||||
Reference in New Issue
Block a user