Skip to content

Commit 43f7f3b

Browse files
committed
feat(description): refactor to tanstack query and remove useEffect
1 parent d4af644 commit 43f7f3b

File tree

6 files changed

+130
-111
lines changed

6 files changed

+130
-111
lines changed

apps/sim/app/api/workflows/[id]/deployments/[version]/route.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -136,7 +136,7 @@ export async function PATCH(
136136
return createSuccessResponse({ name: updated.name, description: updated.description })
137137
} catch (error: any) {
138138
logger.error(
139-
`[${requestId}] Error renaming deployment version ${version} for workflow ${id}`,
139+
`[${requestId}] Error updating deployment version ${version} for workflow ${id}`,
140140
error
141141
)
142142
return createErrorResponse(error.message || 'Failed to update deployment version', 500)

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

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

3-
import { useCallback, useEffect, useRef, useState } from 'react'
4-
import { createLogger } from '@sim/logger'
3+
import { useCallback, useState } from 'react'
54
import {
65
Button,
76
Modal,
@@ -11,8 +10,7 @@ import {
1110
ModalHeader,
1211
Textarea,
1312
} from '@/components/emcn'
14-
15-
const logger = createLogger('VersionDescriptionModal')
13+
import { useUpdateDeploymentVersion } from '@/hooks/queries/deployments'
1614

1715
interface VersionDescriptionModalProps {
1816
open: boolean
@@ -21,7 +19,6 @@ interface VersionDescriptionModalProps {
2119
version: number
2220
versionName: string
2321
currentDescription: string | null | undefined
24-
onSave: () => Promise<void>
2522
}
2623

2724
export function VersionDescriptionModal({
@@ -31,69 +28,46 @@ export function VersionDescriptionModal({
3128
version,
3229
versionName,
3330
currentDescription,
34-
onSave,
3531
}: VersionDescriptionModalProps) {
36-
const [description, setDescription] = useState('')
37-
const [isSaving, setIsSaving] = useState(false)
38-
const [error, setError] = useState<string | null>(null)
32+
// Initialize state from props - component remounts via key prop when version changes
33+
const initialDescription = currentDescription || ''
34+
const [description, setDescription] = useState(initialDescription)
3935
const [showUnsavedChangesAlert, setShowUnsavedChangesAlert] = useState(false)
4036

41-
const initialDescriptionRef = useRef('')
42-
43-
useEffect(() => {
44-
if (open) {
45-
const initialDescription = currentDescription || ''
46-
setDescription(initialDescription)
47-
initialDescriptionRef.current = initialDescription
48-
setError(null)
49-
}
50-
}, [open, currentDescription])
37+
const updateMutation = useUpdateDeploymentVersion()
5138

52-
const hasChanges = description.trim() !== initialDescriptionRef.current.trim()
39+
const hasChanges = description.trim() !== initialDescription.trim()
5340

5441
const handleCloseAttempt = useCallback(() => {
55-
if (hasChanges && !isSaving) {
42+
if (hasChanges && !updateMutation.isPending) {
5643
setShowUnsavedChangesAlert(true)
5744
} else {
5845
onOpenChange(false)
5946
}
60-
}, [hasChanges, isSaving, onOpenChange])
47+
}, [hasChanges, updateMutation.isPending, onOpenChange])
6148

6249
const handleDiscardChanges = useCallback(() => {
6350
setShowUnsavedChangesAlert(false)
64-
setDescription(initialDescriptionRef.current)
51+
setDescription(initialDescription)
6552
onOpenChange(false)
66-
}, [onOpenChange])
53+
}, [initialDescription, onOpenChange])
6754

6855
const handleSave = useCallback(async () => {
6956
if (!workflowId) return
7057

71-
setIsSaving(true)
72-
setError(null)
73-
try {
74-
const res = await fetch(`/api/workflows/${workflowId}/deployments/${version}`, {
75-
method: 'PATCH',
76-
headers: { 'Content-Type': 'application/json' },
77-
body: JSON.stringify({ description: description.trim() || null }),
78-
})
79-
80-
if (res.ok) {
81-
await onSave()
82-
onOpenChange(false)
83-
} else {
84-
const data = await res.json().catch(() => ({}))
85-
const message = data.error || 'Failed to save description'
86-
setError(message)
87-
logger.error('Failed to save description:', message)
58+
updateMutation.mutate(
59+
{
60+
workflowId,
61+
version,
62+
description: description.trim() || null,
63+
},
64+
{
65+
onSuccess: () => {
66+
onOpenChange(false)
67+
},
8868
}
89-
} catch (err) {
90-
const message = err instanceof Error ? err.message : 'An unexpected error occurred'
91-
setError(message)
92-
logger.error('Error saving description:', err)
93-
} finally {
94-
setIsSaving(false)
95-
}
96-
}, [workflowId, version, description, onSave, onOpenChange])
69+
)
70+
}, [workflowId, version, description, updateMutation, onOpenChange])
9771

9872
return (
9973
<>
@@ -104,7 +78,7 @@ export function VersionDescriptionModal({
10478
</ModalHeader>
10579
<ModalBody className='space-y-[12px]'>
10680
<p className='text-[12px] text-[var(--text-secondary)]'>
107-
{currentDescription ? 'Edit' : 'Add'} a description for{' '}
81+
{currentDescription ? 'Edit the' : 'Add a'} description for{' '}
10882
<span className='font-medium text-[var(--text-primary)]'>{versionName}</span>
10983
</p>
11084
<Textarea
@@ -115,16 +89,30 @@ export function VersionDescriptionModal({
11589
maxLength={500}
11690
/>
11791
<div className='flex items-center justify-between'>
118-
{error ? <p className='text-[12px] text-[var(--text-error)]'>{error}</p> : <div />}
92+
{updateMutation.error ? (
93+
<p className='text-[12px] text-[var(--text-error)]'>
94+
{updateMutation.error.message}
95+
</p>
96+
) : (
97+
<div />
98+
)}
11999
<p className='text-[11px] text-[var(--text-tertiary)]'>{description.length}/500</p>
120100
</div>
121101
</ModalBody>
122102
<ModalFooter>
123-
<Button variant='default' onClick={handleCloseAttempt} disabled={isSaving}>
103+
<Button
104+
variant='default'
105+
onClick={handleCloseAttempt}
106+
disabled={updateMutation.isPending}
107+
>
124108
Cancel
125109
</Button>
126-
<Button variant='tertiary' onClick={handleSave} disabled={isSaving || !hasChanges}>
127-
{isSaving ? 'Saving...' : 'Save'}
110+
<Button
111+
variant='tertiary'
112+
onClick={handleSave}
113+
disabled={updateMutation.isPending || !hasChanges}
114+
>
115+
{updateMutation.isPending ? 'Saving...' : 'Save'}
128116
</Button>
129117
</ModalFooter>
130118
</ModalContent>

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

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

33
import { useEffect, useRef, useState } from 'react'
4-
import { createLogger } from '@sim/logger'
54
import clsx from 'clsx'
65
import { FileText, MoreVertical, Pencil, RotateCcw, SendToBack } from 'lucide-react'
76
import {
@@ -15,16 +14,13 @@ import {
1514
import { Skeleton } from '@/components/ui'
1615
import { formatDateTime } from '@/lib/core/utils/formatting'
1716
import type { WorkflowDeploymentVersionResponse } from '@/lib/workflows/persistence/utils'
17+
import { useUpdateDeploymentVersion } from '@/hooks/queries/deployments'
1818
import { VersionDescriptionModal } from './version-description-modal'
1919

20-
const logger = createLogger('Versions')
21-
22-
/** Shared styling constants aligned with terminal component */
2320
const HEADER_TEXT_CLASS = 'font-medium text-[var(--text-tertiary)] text-[12px]'
2421
const ROW_TEXT_CLASS = 'font-medium text-[var(--text-primary)] text-[12px]'
2522
const COLUMN_BASE_CLASS = 'flex-shrink-0'
2623

27-
/** Column width configuration */
2824
const COLUMN_WIDTHS = {
2925
VERSION: 'w-[180px]',
3026
DEPLOYED_BY: 'w-[140px]',
@@ -40,20 +36,6 @@ interface VersionsProps {
4036
onSelectVersion: (version: number | null) => void
4137
onPromoteToLive: (version: number) => void
4238
onLoadDeployment: (version: number) => void
43-
fetchVersions: () => Promise<void>
44-
}
45-
46-
/**
47-
* Formats a timestamp into a readable string.
48-
* @param value - The date string or Date object to format
49-
* @returns Formatted string like "Jan 28, 2026, 10:43 AM"
50-
*/
51-
const formatTimestamp = (value: string | Date): string => {
52-
const date = value instanceof Date ? value : new Date(value)
53-
if (Number.isNaN(date.getTime())) {
54-
return '-'
55-
}
56-
return formatDateTime(date)
5739
}
5840

5941
/**
@@ -68,15 +50,15 @@ export function Versions({
6850
onSelectVersion,
6951
onPromoteToLive,
7052
onLoadDeployment,
71-
fetchVersions,
7253
}: VersionsProps) {
7354
const [editingVersion, setEditingVersion] = useState<number | null>(null)
7455
const [editValue, setEditValue] = useState('')
75-
const [isRenaming, setIsRenaming] = useState(false)
7656
const [openDropdown, setOpenDropdown] = useState<number | null>(null)
7757
const [descriptionModalVersion, setDescriptionModalVersion] = useState<number | null>(null)
7858
const inputRef = useRef<HTMLInputElement>(null)
7959

60+
const renameMutation = useUpdateDeploymentVersion()
61+
8062
useEffect(() => {
8163
if (editingVersion !== null && inputRef.current) {
8264
inputRef.current.focus()
@@ -90,7 +72,7 @@ export function Versions({
9072
setEditValue(currentName || `v${version}`)
9173
}
9274

93-
const handleSaveRename = async (version: number) => {
75+
const handleSaveRename = (version: number) => {
9476
if (!workflowId || !editValue.trim()) {
9577
setEditingVersion(null)
9678
return
@@ -104,25 +86,21 @@ export function Versions({
10486
return
10587
}
10688

107-
setIsRenaming(true)
108-
try {
109-
const res = await fetch(`/api/workflows/${workflowId}/deployments/${version}`, {
110-
method: 'PATCH',
111-
headers: { 'Content-Type': 'application/json' },
112-
body: JSON.stringify({ name: editValue.trim() }),
113-
})
114-
115-
if (res.ok) {
116-
await fetchVersions()
117-
setEditingVersion(null)
118-
} else {
119-
logger.error('Failed to rename version')
89+
renameMutation.mutate(
90+
{
91+
workflowId,
92+
version,
93+
name: editValue.trim(),
94+
},
95+
{
96+
onSuccess: () => {
97+
setEditingVersion(null)
98+
},
99+
onError: () => {
100+
// Keep editing state open on error so user can retry
101+
},
120102
}
121-
} catch (error) {
122-
logger.error('Error renaming version:', error)
123-
} finally {
124-
setIsRenaming(false)
125-
}
103+
)
126104
}
127105

128106
const handleCancelRename = () => {
@@ -270,7 +248,7 @@ export function Versions({
270248
'text-[var(--text-primary)] focus:outline-none focus:ring-0'
271249
)}
272250
maxLength={100}
273-
disabled={isRenaming}
251+
disabled={renameMutation.isPending}
274252
autoComplete='off'
275253
autoCorrect='off'
276254
autoCapitalize='off'
@@ -302,7 +280,7 @@ export function Versions({
302280
<span
303281
className={clsx('block truncate text-[var(--text-tertiary)]', ROW_TEXT_CLASS)}
304282
>
305-
{formatTimestamp(v.createdAt)}
283+
{formatDateTime(new Date(v.createdAt))}
306284
</span>
307285
</div>
308286

@@ -374,6 +352,7 @@ export function Versions({
374352

375353
{workflowId && descriptionModalVersionData && (
376354
<VersionDescriptionModal
355+
key={descriptionModalVersionData.version}
377356
open={descriptionModalVersion !== null}
378357
onOpenChange={(open) => !open && setDescriptionModalVersion(null)}
379358
workflowId={workflowId}
@@ -382,7 +361,6 @@ export function Versions({
382361
descriptionModalVersionData.name || `v${descriptionModalVersionData.version}`
383362
}
384363
currentDescription={descriptionModalVersionData.description}
385-
onSave={fetchVersions}
386364
/>
387365
)}
388366
</div>

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

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -32,7 +32,6 @@ interface GeneralDeployProps {
3232
versionsLoading: boolean
3333
onPromoteToLive: (version: number) => Promise<void>
3434
onLoadDeploymentComplete: () => void
35-
fetchVersions: () => Promise<void>
3635
}
3736

3837
type PreviewMode = 'active' | 'selected'
@@ -48,7 +47,6 @@ export function GeneralDeploy({
4847
versionsLoading,
4948
onPromoteToLive,
5049
onLoadDeploymentComplete,
51-
fetchVersions,
5250
}: GeneralDeployProps) {
5351
const [selectedVersion, setSelectedVersion] = useState<number | null>(null)
5452
const [previewMode, setPreviewMode] = useState<PreviewMode>('active')
@@ -229,7 +227,6 @@ export function GeneralDeploy({
229227
onSelectVersion={handleSelectVersion}
230228
onPromoteToLive={handlePromoteToLive}
231229
onLoadDeployment={handleLoadDeployment}
232-
fetchVersions={fetchVersions}
233230
/>
234231
</div>
235232
</div>

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

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -135,11 +135,9 @@ export function DeployModal({
135135
refetch: refetchDeploymentInfo,
136136
} = useDeploymentInfo(workflowId, { enabled: open && isDeployed })
137137

138-
const {
139-
data: versionsData,
140-
isLoading: versionsLoading,
141-
refetch: refetchVersions,
142-
} = useDeploymentVersions(workflowId, { enabled: open })
138+
const { data: versionsData, isLoading: versionsLoading } = useDeploymentVersions(workflowId, {
139+
enabled: open,
140+
})
143141

144142
const {
145143
isLoading: isLoadingChat,
@@ -450,10 +448,6 @@ export function DeployModal({
450448
deleteTrigger?.click()
451449
}, [])
452450

453-
const handleFetchVersions = useCallback(async () => {
454-
await refetchVersions()
455-
}, [refetchVersions])
456-
457451
const isSubmitting = deployMutation.isPending
458452
const isUndeploying = undeployMutation.isPending
459453

@@ -512,7 +506,6 @@ export function DeployModal({
512506
versionsLoading={versionsLoading}
513507
onPromoteToLive={handlePromoteToLive}
514508
onLoadDeploymentComplete={handleCloseModal}
515-
fetchVersions={handleFetchVersions}
516509
/>
517510
</ModalTabsContent>
518511

0 commit comments

Comments
 (0)