Skip to content

Commit fb2714a

Browse files
committed
use reactquery cache in deploy modal
1 parent cde43b7 commit fb2714a

File tree

4 files changed

+164
-133
lines changed

4 files changed

+164
-133
lines changed

apps/sim/app/workspace/[workspaceId]/logs/components/log-details/components/execution-snapshot/execution-snapshot.tsx

Lines changed: 44 additions & 92 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
'use client'
22

3-
import { useEffect, useState } from 'react'
4-
import { createLogger } from '@sim/logger'
3+
import { useEffect, useMemo, useState } from 'react'
54
import { AlertCircle, Loader2 } from 'lucide-react'
65
import { Modal, ModalBody, ModalContent, ModalHeader } from '@/components/emcn'
76
import { redactApiKeys } from '@/lib/core/security/redaction'
@@ -10,10 +9,9 @@ import {
109
BlockDetailsSidebar,
1110
WorkflowPreview,
1211
} from '@/app/workspace/[workspaceId]/w/components/preview'
12+
import { useExecutionSnapshot } from '@/hooks/queries/logs'
1313
import type { WorkflowState } from '@/stores/workflows/workflow/types'
1414

15-
const logger = createLogger('ExecutionSnapshot')
16-
1715
interface TraceSpan {
1816
blockId?: string
1917
input?: unknown
@@ -45,25 +43,6 @@ function isMigratedWorkflowState(state: WorkflowState): state is MigratedWorkflo
4543
return (state as MigratedWorkflowState)._migrated === true
4644
}
4745

48-
interface ExecutionSnapshotData {
49-
executionId: string
50-
workflowId: string
51-
workflowState: WorkflowState | MigratedWorkflowState
52-
executionMetadata: {
53-
trigger: string
54-
startedAt: string
55-
endedAt?: string
56-
totalDurationMs?: number
57-
58-
cost: {
59-
total: number | null
60-
input: number | null
61-
output: number | null
62-
}
63-
totalTokens: number | null
64-
}
65-
}
66-
6746
interface ExecutionSnapshotProps {
6847
executionId: string
6948
traceSpans?: TraceSpan[]
@@ -85,83 +64,55 @@ export function ExecutionSnapshot({
8564
isOpen = false,
8665
onClose = () => {},
8766
}: ExecutionSnapshotProps) {
88-
const [data, setData] = useState<ExecutionSnapshotData | null>(null)
89-
const [blockExecutions, setBlockExecutions] = useState<Record<string, BlockExecutionData>>({})
90-
const [loading, setLoading] = useState(true)
91-
const [error, setError] = useState<string | null>(null)
67+
const { data, isLoading, error } = useExecutionSnapshot(executionId)
9268
const [pinnedBlockId, setPinnedBlockId] = useState<string | null>(null)
9369

94-
useEffect(() => {
95-
if (traceSpans && Array.isArray(traceSpans)) {
96-
const blockExecutionMap: Record<string, BlockExecutionData> = {}
97-
98-
const collectBlockSpans = (spans: TraceSpan[]): TraceSpan[] => {
99-
const blockSpans: TraceSpan[] = []
100-
101-
for (const span of spans) {
102-
if (span.blockId) {
103-
blockSpans.push(span)
104-
}
105-
if (span.children && Array.isArray(span.children)) {
106-
blockSpans.push(...collectBlockSpans(span.children))
107-
}
108-
}
70+
// Process traceSpans to create blockExecutions map
71+
const blockExecutions = useMemo(() => {
72+
if (!traceSpans || !Array.isArray(traceSpans)) return {}
10973

110-
return blockSpans
111-
}
74+
const blockExecutionMap: Record<string, BlockExecutionData> = {}
11275

113-
const allBlockSpans = collectBlockSpans(traceSpans)
76+
const collectBlockSpans = (spans: TraceSpan[]): TraceSpan[] => {
77+
const blockSpans: TraceSpan[] = []
11478

115-
for (const span of allBlockSpans) {
116-
if (span.blockId && !blockExecutionMap[span.blockId]) {
117-
blockExecutionMap[span.blockId] = {
118-
input: redactApiKeys(span.input || {}),
119-
output: redactApiKeys(span.output || {}),
120-
status: span.status || 'unknown',
121-
durationMs: span.duration || 0,
122-
}
79+
for (const span of spans) {
80+
if (span.blockId) {
81+
blockSpans.push(span)
82+
}
83+
if (span.children && Array.isArray(span.children)) {
84+
blockSpans.push(...collectBlockSpans(span.children))
12385
}
12486
}
12587

126-
setBlockExecutions(blockExecutionMap)
88+
return blockSpans
12789
}
128-
}, [traceSpans])
129-
130-
useEffect(() => {
131-
const abortController = new AbortController()
13290

133-
const fetchData = async () => {
134-
try {
135-
setLoading(true)
136-
setError(null)
91+
const allBlockSpans = collectBlockSpans(traceSpans)
13792

138-
const response = await fetch(`/api/logs/execution/${executionId}`, {
139-
signal: abortController.signal,
140-
})
141-
if (!response.ok) {
142-
throw new Error(`Failed to fetch execution snapshot data: ${response.statusText}`)
93+
for (const span of allBlockSpans) {
94+
if (span.blockId && !blockExecutionMap[span.blockId]) {
95+
blockExecutionMap[span.blockId] = {
96+
input: redactApiKeys(span.input || {}),
97+
output: redactApiKeys(span.output || {}),
98+
status: span.status || 'unknown',
99+
durationMs: span.duration || 0,
143100
}
144-
145-
const result = await response.json()
146-
setData(result)
147-
logger.debug(`Loaded execution snapshot data for execution: ${executionId}`)
148-
} catch (err) {
149-
if (err instanceof Error && err.name === 'AbortError') return
150-
const errorMessage = err instanceof Error ? err.message : 'Unknown error'
151-
logger.error('Failed to fetch execution snapshot data:', err)
152-
setError(errorMessage)
153-
} finally {
154-
setLoading(false)
155101
}
156102
}
157103

158-
fetchData()
104+
return blockExecutionMap
105+
}, [traceSpans])
159106

160-
return () => abortController.abort()
107+
// Reset pinned block when executionId changes
108+
useEffect(() => {
109+
setPinnedBlockId(null)
161110
}, [executionId])
162111

112+
const workflowState = data?.workflowState as WorkflowState | undefined
113+
163114
const renderContent = () => {
164-
if (loading) {
115+
if (isLoading) {
165116
return (
166117
<div
167118
className={cn('flex items-center justify-center', className)}
@@ -183,24 +134,27 @@ export function ExecutionSnapshot({
183134
>
184135
<div className='flex items-center gap-[8px] text-[var(--text-error)]'>
185136
<AlertCircle className='h-[16px] w-[16px]' />
186-
<span className='text-[13px]'>Failed to load execution snapshot: {error}</span>
137+
<span className='text-[13px]'>Failed to load execution snapshot: {error.message}</span>
187138
</div>
188139
</div>
189140
)
190141
}
191142

192-
if (!data) {
143+
if (!data || !workflowState) {
193144
return (
194145
<div
195146
className={cn('flex items-center justify-center', className)}
196147
style={{ height, width }}
197148
>
198-
<div className='text-[13px] text-[var(--text-secondary)]'>No data available</div>
149+
<div className='flex items-center gap-[8px] text-[var(--text-secondary)]'>
150+
<Loader2 className='h-[16px] w-[16px] animate-spin' />
151+
<span className='text-[13px]'>Loading execution snapshot...</span>
152+
</div>
199153
</div>
200154
)
201155
}
202156

203-
if (isMigratedWorkflowState(data.workflowState)) {
157+
if (isMigratedWorkflowState(workflowState)) {
204158
return (
205159
<div
206160
className={cn('flex flex-col items-center justify-center gap-[16px] p-[32px]', className)}
@@ -214,9 +168,7 @@ export function ExecutionSnapshot({
214168
This log was migrated from the old logging system. The workflow state at execution time
215169
is not available.
216170
</div>
217-
<div className='text-[12px] text-[var(--text-tertiary)]'>
218-
Note: {data.workflowState._note}
219-
</div>
171+
<div className='text-[12px] text-[var(--text-tertiary)]'>Note: {workflowState._note}</div>
220172
</div>
221173
)
222174
}
@@ -231,7 +183,7 @@ export function ExecutionSnapshot({
231183
>
232184
<div className='h-full flex-1'>
233185
<WorkflowPreview
234-
workflowState={data.workflowState}
186+
workflowState={workflowState}
235187
showSubBlocks={true}
236188
isPannable={true}
237189
defaultPosition={{ x: 0, y: 0 }}
@@ -244,12 +196,12 @@ export function ExecutionSnapshot({
244196
executedBlocks={blockExecutions}
245197
/>
246198
</div>
247-
{pinnedBlockId && data.workflowState.blocks[pinnedBlockId] && (
199+
{pinnedBlockId && workflowState.blocks[pinnedBlockId] && (
248200
<BlockDetailsSidebar
249-
block={data.workflowState.blocks[pinnedBlockId]}
201+
block={workflowState.blocks[pinnedBlockId]}
250202
executionData={blockExecutions[pinnedBlockId]}
251203
allBlockExecutions={blockExecutions}
252-
workflowBlocks={data.workflowState.blocks}
204+
workflowBlocks={workflowState.blocks}
253205
isExecutionMode
254206
/>
255207
)}

apps/sim/app/workspace/[workspaceId]/w/[workflowId]/components/panel/components/deploy/components/deploy-modal/components/general/general.tsx

Lines changed: 14 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
'use client'
22

3-
import { useCallback, useEffect, useMemo, useRef, useState } from 'react'
3+
import { useCallback, useEffect, useMemo, useState } from 'react'
44
import { createLogger } from '@sim/logger'
55
import { Maximize2 } from 'lucide-react'
66
import {
@@ -19,6 +19,7 @@ import {
1919
BlockDetailsSidebar,
2020
WorkflowPreview,
2121
} from '@/app/workspace/[workspaceId]/w/components/preview'
22+
import { useDeploymentVersionState, useRevertToVersion } from '@/hooks/queries/workflows'
2223
import type { WorkflowState } from '@/stores/workflows/workflow/types'
2324
import { Versions } from './components'
2425

@@ -59,45 +60,23 @@ export function GeneralDeploy({
5960
const [versionToLoad, setVersionToLoad] = useState<number | null>(null)
6061
const [versionToPromote, setVersionToPromote] = useState<number | null>(null)
6162

62-
const versionCacheRef = useRef<Map<number, WorkflowState>>(new Map())
63-
const [, forceUpdate] = useState({})
64-
6563
const selectedVersionInfo = versions.find((v) => v.version === selectedVersion)
6664
const versionToPromoteInfo = versions.find((v) => v.version === versionToPromote)
6765
const versionToLoadInfo = versions.find((v) => v.version === versionToLoad)
6866

69-
const cachedSelectedState =
70-
selectedVersion !== null ? versionCacheRef.current.get(selectedVersion) : null
71-
72-
const fetchSelectedVersionState = useCallback(
73-
async (version: number) => {
74-
if (!workflowId) return
75-
if (versionCacheRef.current.has(version)) return
67+
// React Query for fetching selected version state (with caching)
68+
const { data: selectedVersionState } = useDeploymentVersionState(workflowId, selectedVersion)
7669

77-
try {
78-
const res = await fetch(`/api/workflows/${workflowId}/deployments/${version}`)
79-
if (res.ok) {
80-
const data = await res.json()
81-
if (data.deployedState) {
82-
versionCacheRef.current.set(version, data.deployedState)
83-
forceUpdate({})
84-
}
85-
}
86-
} catch (error) {
87-
logger.error('Error fetching version state:', error)
88-
}
89-
},
90-
[workflowId]
91-
)
70+
// React Query mutation for reverting to a version
71+
const revertMutation = useRevertToVersion()
9272

9373
useEffect(() => {
9474
if (selectedVersion !== null) {
95-
fetchSelectedVersionState(selectedVersion)
9675
setPreviewMode('selected')
9776
} else {
9877
setPreviewMode('active')
9978
}
100-
}, [selectedVersion, fetchSelectedVersionState])
79+
}, [selectedVersion])
10180

10281
const handleSelectVersion = useCallback((version: number | null) => {
10382
setSelectedVersion(version)
@@ -122,14 +101,7 @@ export function GeneralDeploy({
122101
setVersionToLoad(null)
123102

124103
try {
125-
const response = await fetch(`/api/workflows/${workflowId}/deployments/${version}/revert`, {
126-
method: 'POST',
127-
})
128-
129-
if (!response.ok) {
130-
throw new Error('Failed to load deployment')
131-
}
132-
104+
await revertMutation.mutateAsync({ workflowId, version })
133105
onLoadDeploymentComplete()
134106
} catch (error) {
135107
logger.error('Failed to load deployment:', error)
@@ -152,11 +124,11 @@ export function GeneralDeploy({
152124
}
153125

154126
const workflowToShow = useMemo(() => {
155-
if (previewMode === 'selected' && cachedSelectedState) {
156-
return cachedSelectedState
127+
if (previewMode === 'selected' && selectedVersionState) {
128+
return selectedVersionState
157129
}
158130
return deployedState
159-
}, [previewMode, cachedSelectedState, deployedState])
131+
}, [previewMode, selectedVersionState, deployedState])
160132

161133
const showToggle = selectedVersion !== null && deployedState
162134

@@ -241,8 +213,9 @@ export function GeneralDeploy({
241213
<Button
242214
type='button'
243215
variant='default'
216+
size='sm'
244217
onClick={() => setShowExpandedPreview(true)}
245-
className='absolute top-[8px] right-[8px] z-10 h-[28px] w-[28px] p-0'
218+
className='absolute top-[8px] right-[8px] z-10'
246219
>
247220
<Maximize2 className='h-[14px] w-[14px]' />
248221
</Button>
@@ -351,7 +324,7 @@ export function GeneralDeploy({
351324
showSubBlocks={true}
352325
isPannable={true}
353326
defaultPosition={{ x: 0, y: 0 }}
354-
defaultZoom={0.8}
327+
defaultZoom={0.6}
355328
onNodeClick={(blockId) => {
356329
setExpandedSelectedBlockId(
357330
expandedSelectedBlockId === blockId ? null : blockId

0 commit comments

Comments
 (0)