diff --git a/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx b/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx index 578916b1..80cfa883 100644 --- a/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx +++ b/skyvern-frontend/src/routes/workflows/editor/Workspace.tsx @@ -198,6 +198,34 @@ function Workspace({ splitLeft: useRef(null), }; + const handleOnSave = async () => { + const errors = getWorkflowErrors(nodes); + if (errors.length > 0) { + toast({ + title: "Encountered error while trying to save workflow:", + description: ( +
+ {errors.map((error) => ( +

{error}

+ ))} +
+ ), + variant: "destructive", + }); + return; + } + + await saveWorkflow.mutateAsync(); + + workflowChangesStore.setSaidOkToCodeCacheDeletion(false); + + queryClient.invalidateQueries({ + queryKey: ["cache-key-values", workflowPermanentId, cacheKey], + }); + + setCacheKeyValueFilter(""); + }; + useEffect(() => { const handleKeyDown = (event: KeyboardEvent) => { if (event.key === "Escape") { @@ -802,6 +830,39 @@ function Workspace({ + {/* confirm code cache deletion dialog */} + { + !open && workflowChangesStore.setShowConfirmCodeCacheDeletion(false); + !open && workflowChangesStore.setSaidOkToCodeCacheDeletion(false); + }} + > + + + Are you sure? + + Saving will delete cached code, and Skyvern will re-generate it in + the next run. Proceed? + + + + + + + + + + + {/* cache key value delete dialog */} { - const errors = getWorkflowErrors(nodes); - if (errors.length > 0) { - toast({ - title: "Can not save workflow because of errors:", - description: ( -
- {errors.map((error) => ( -

{error}

- ))} -
- ), - variant: "destructive", - }); - return; - } - await saveWorkflow.mutateAsync(); - - queryClient.invalidateQueries({ - queryKey: ["cache-key-values", workflowPermanentId, cacheKey], - }); - - setCacheKeyValueFilter(""); - }} + onSave={async () => await handleOnSave()} onRun={() => { closeWorkflowPanel(); }} diff --git a/skyvern-frontend/src/store/WorkflowHasChangesStore.ts b/skyvern-frontend/src/store/WorkflowHasChangesStore.ts index 3b332bf9..89fa0017 100644 --- a/skyvern-frontend/src/store/WorkflowHasChangesStore.ts +++ b/skyvern-frontend/src/store/WorkflowHasChangesStore.ts @@ -28,15 +28,21 @@ type WorkflowHasChangesStore = { getSaveData: () => SaveData | null; hasChanges: boolean; saveIsPending: boolean; + saidOkToCodeCacheDeletion: boolean; + showConfirmCodeCacheDeletion: boolean; setGetSaveData: (getSaveData: () => SaveData) => void; setHasChanges: (hasChanges: boolean) => void; setSaveIsPending: (isPending: boolean) => void; + setSaidOkToCodeCacheDeletion: (saidOkToCodeCacheDeletion: boolean) => void; + setShowConfirmCodeCacheDeletion: (show: boolean) => void; }; const useWorkflowHasChangesStore = create((set) => { return { hasChanges: false, saveIsPending: false, + saidOkToCodeCacheDeletion: false, + showConfirmCodeCacheDeletion: false, getSaveData: () => null, setGetSaveData: (getSaveData: () => SaveData) => { set({ getSaveData }); @@ -47,14 +53,25 @@ const useWorkflowHasChangesStore = create((set) => { setSaveIsPending: (isPending: boolean) => { set({ saveIsPending: isPending }); }, + setSaidOkToCodeCacheDeletion: (saidOkToCodeCacheDeletion: boolean) => { + set({ saidOkToCodeCacheDeletion }); + }, + setShowConfirmCodeCacheDeletion: (show: boolean) => { + set({ showConfirmCodeCacheDeletion: show }); + }, }; }); const useWorkflowSave = () => { const credentialGetter = useCredentialGetter(); const queryClient = useQueryClient(); - const { getSaveData, setHasChanges, setSaveIsPending } = - useWorkflowHasChangesStore(); + const { + getSaveData, + saidOkToCodeCacheDeletion, + setHasChanges, + setSaveIsPending, + setShowConfirmCodeCacheDeletion, + } = useWorkflowHasChangesStore(); const saveWorkflowMutation = useMutation({ mutationFn: async () => { @@ -136,6 +153,11 @@ const useWorkflowSave = () => { headers: { "Content-Type": "text/plain", }, + params: { + delete_code_cache_is_ok: saidOkToCodeCacheDeletion + ? "true" + : "false", + }, }, ); }, @@ -169,6 +191,14 @@ const useWorkflowSave = () => { onError: (error: AxiosError) => { const detail = (error.response?.data as { detail?: string })?.detail; + if ( + detail && + detail.startsWith("No confirmation for code cache deletion") + ) { + setShowConfirmCodeCacheDeletion(true); + return; + } + toast({ title: "Error", description: detail ? detail : error.message,