From 41417dbb8aad13040e16978a6a2d91638ffbe98a Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 8 Nov 2023 12:51:29 +0000 Subject: [PATCH 1/5] Extract permissions to the page --- .../WorkspaceSchedulePage.tsx | 28 +++++++++++++++---- 1 file changed, 22 insertions(+), 6 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 08d116744e4a6..148030ed42d75 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -23,12 +23,20 @@ import { formValuesToTTLRequest, } from "./formToRequest"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { useQueryClient } from "react-query"; +import { useQuery, useQueryClient } from "react-query"; +import { checkAuthorization } from "api/queries/authCheck"; -const getAutostart = (workspace: TypesGen.Workspace) => - scheduleToAutostart(workspace.autostart_schedule); -const getAutostop = (workspace: TypesGen.Workspace) => - ttlMsToAutostop(workspace.ttl_ms); +const permissionsToCheck = (workspace: TypesGen.Workspace) => + ({ + updateWorkspace: { + object: { + resource_type: "workspace", + resource_id: workspace.id, + owner_id: workspace.owner_id, + }, + action: "update", + }, + }) as const; export const WorkspaceSchedulePage: FC = () => { const params = useParams() as { username: string; workspace: string }; @@ -37,6 +45,9 @@ export const WorkspaceSchedulePage: FC = () => { const workspaceName = params.workspace; const queryClient = useQueryClient(); const workspace = useWorkspaceSettings(); + const { data: permissions } = useQuery( + checkAuthorization({ checks: permissionsToCheck(workspace) }), + ); const [scheduleState, scheduleSend] = useMachine(workspaceSchedule, { context: { workspace }, }); @@ -44,7 +55,6 @@ export const WorkspaceSchedulePage: FC = () => { checkPermissionsError, submitScheduleError, getTemplateError, - permissions, template, } = scheduleState.context; @@ -134,4 +144,10 @@ export const WorkspaceSchedulePage: FC = () => { ); }; +const getAutostart = (workspace: TypesGen.Workspace) => + scheduleToAutostart(workspace.autostart_schedule); + +const getAutostop = (workspace: TypesGen.Workspace) => + ttlMsToAutostop(workspace.ttl_ms); + export default WorkspaceSchedulePage; From d09c4f8f12a5d0ec4ba17d2b3aec5ec95f592d10 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 8 Nov 2023 12:55:35 +0000 Subject: [PATCH 2/5] Move template data and a few errors to the page --- .../WorkspaceSchedulePage/WorkspaceSchedulePage.tsx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 148030ed42d75..e1cdc04a0175a 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -25,6 +25,7 @@ import { import { ErrorAlert } from "components/Alert/ErrorAlert"; import { useQuery, useQueryClient } from "react-query"; import { checkAuthorization } from "api/queries/authCheck"; +import { templateByName } from "api/queries/templates"; const permissionsToCheck = (workspace: TypesGen.Workspace) => ({ @@ -45,18 +46,16 @@ export const WorkspaceSchedulePage: FC = () => { const workspaceName = params.workspace; const queryClient = useQueryClient(); const workspace = useWorkspaceSettings(); - const { data: permissions } = useQuery( + const { data: permissions, error: checkPermissionsError } = useQuery( checkAuthorization({ checks: permissionsToCheck(workspace) }), ); + const { data: template, error: getTemplateError } = useQuery( + templateByName(workspace.organization_id, workspace.template_name), + ); const [scheduleState, scheduleSend] = useMachine(workspaceSchedule, { context: { workspace }, }); - const { - checkPermissionsError, - submitScheduleError, - getTemplateError, - template, - } = scheduleState.context; + const { submitScheduleError } = scheduleState.context; if (!username || !workspaceName) { return ; From 4d9f8b7b10a22b80dc06bad974dc8389a461187b Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 8 Nov 2023 13:08:05 +0000 Subject: [PATCH 3/5] Move submit logic --- .../WorkspaceSchedulePage.tsx | 129 ++++++++++-------- 1 file changed, 72 insertions(+), 57 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index e1cdc04a0175a..676e2a4f8e276 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -1,4 +1,3 @@ -import { useMachine } from "@xstate/react"; import { Alert } from "components/Alert/Alert"; import { ConfirmDialog } from "components/Dialogs/ConfirmDialog/ConfirmDialog"; import { Loader } from "components/Loader/Loader"; @@ -12,20 +11,20 @@ import { ttlMsToAutostop } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePa import { useWorkspaceSettings } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout"; import { FC } from "react"; import { Helmet } from "react-helmet-async"; -import { Navigate, useNavigate, useParams } from "react-router-dom"; +import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; import * as TypesGen from "api/typesGenerated"; import { workspaceByOwnerAndNameKey } from "api/queries/workspaces"; import { WorkspaceScheduleForm } from "./WorkspaceScheduleForm"; -import { workspaceSchedule } from "xServices/workspaceSchedule/workspaceScheduleXService"; import { formValuesToAutostartRequest, formValuesToTTLRequest, } from "./formToRequest"; import { ErrorAlert } from "components/Alert/ErrorAlert"; -import { useQuery, useQueryClient } from "react-query"; +import { useMutation, useQuery, useQueryClient } from "react-query"; import { checkAuthorization } from "api/queries/authCheck"; import { templateByName } from "api/queries/templates"; +import { putWorkspaceAutostart, putWorkspaceAutostop } from "api/api"; const permissionsToCheck = (workspace: TypesGen.Workspace) => ({ @@ -52,18 +51,16 @@ export const WorkspaceSchedulePage: FC = () => { const { data: template, error: getTemplateError } = useQuery( templateByName(workspace.organization_id, workspace.template_name), ); - const [scheduleState, scheduleSend] = useMachine(workspaceSchedule, { - context: { workspace }, + const submitScheduleMutation = useMutation({ + mutationFn: submitSchedule, + onSuccess: async () => { + await queryClient.invalidateQueries( + workspaceByOwnerAndNameKey(params.username, params.workspace), + ); + }, }); - const { submitScheduleError } = scheduleState.context; - - if (!username || !workspaceName) { - return ; - } - - if (scheduleState.matches("done")) { - return ; - } + const error = checkPermissionsError || getTemplateError; + const isLoading = !template || !permissions; return ( <> @@ -77,54 +74,49 @@ export const WorkspaceSchedulePage: FC = () => { > Workspace Schedule - {(scheduleState.hasTag("loading") || !template) && } - {scheduleState.matches("error") && ( - - )} + + {error && } + + {isLoading && } + {permissions && !permissions.updateWorkspace && ( You don't have permissions to update the schedule for this workspace. )} - {template && - workspace && - (scheduleState.matches("presentForm") || - scheduleState.matches("submittingSchedule")) && ( - { - navigate(`/@${username}/${workspaceName}`); - }} - onSubmit={async (values) => { - scheduleSend({ - type: "SUBMIT_SCHEDULE", - autostart: formValuesToAutostartRequest(values), - ttl: formValuesToTTLRequest(values), - autostartChanged: scheduleChanged( - getAutostart(workspace), - values, - ), - autostopChanged: scheduleChanged( - getAutostop(workspace), - values, - ), - }); - - await queryClient.invalidateQueries( - workspaceByOwnerAndNameKey(params.username, params.workspace), - ); - }} - /> - )} + + {template && ( + { + navigate(`/@${username}/${workspaceName}`); + }} + onSubmit={async (values) => { + await submitScheduleMutation.mutateAsync({ + workspace, + autostart: formValuesToAutostartRequest(values), + ttl: formValuesToTTLRequest(values), + autostartChanged: scheduleChanged( + getAutostart(workspace), + values, + ), + autostopChanged: scheduleChanged(getAutostop(workspace), values), + }); + + navigate(`/@${username}/${workspaceName}`); + }} + /> + )} + const getAutostop = (workspace: TypesGen.Workspace) => ttlMsToAutostop(workspace.ttl_ms); +type SubmitScheduleData = { + workspace: TypesGen.Workspace; + autostart: TypesGen.UpdateWorkspaceAutostartRequest; + autostartChanged: boolean; + ttl: TypesGen.UpdateWorkspaceTTLRequest; + autostopChanged: boolean; +}; + +const submitSchedule = async (data: SubmitScheduleData) => { + const { autostartChanged, workspace, autostart, autostopChanged, ttl } = data; + const actions: Promise[] = []; + + if (autostartChanged) { + actions.push(putWorkspaceAutostart(workspace.id, autostart)); + } + + if (autostopChanged) { + actions.push(putWorkspaceAutostop(workspace.id, ttl)); + } + + return Promise.all(actions); +}; + export default WorkspaceSchedulePage; From 5d3236e3192e9981fafb68951e88c68fe318eaab Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 8 Nov 2023 13:14:12 +0000 Subject: [PATCH 4/5] Remove workspace schedule service --- .../WorkspaceSchedulePage.tsx | 24 +- .../workspaceScheduleXService.ts | 233 ------------------ 2 files changed, 17 insertions(+), 240 deletions(-) delete mode 100644 site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 676e2a4f8e276..311852681538c 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -9,7 +9,7 @@ import { } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/schedule"; import { ttlMsToAutostop } from "pages/WorkspaceSettingsPage/WorkspaceSchedulePage/ttl"; import { useWorkspaceSettings } from "pages/WorkspaceSettingsPage/WorkspaceSettingsLayout"; -import { FC } from "react"; +import { FC, useState } from "react"; import { Helmet } from "react-helmet-async"; import { useNavigate, useParams } from "react-router-dom"; import { pageTitle } from "utils/page"; @@ -24,7 +24,11 @@ import { ErrorAlert } from "components/Alert/ErrorAlert"; import { useMutation, useQuery, useQueryClient } from "react-query"; import { checkAuthorization } from "api/queries/authCheck"; import { templateByName } from "api/queries/templates"; -import { putWorkspaceAutostart, putWorkspaceAutostop } from "api/api"; +import { + putWorkspaceAutostart, + putWorkspaceAutostop, + startWorkspace, +} from "api/api"; const permissionsToCheck = (workspace: TypesGen.Workspace) => ({ @@ -62,6 +66,12 @@ export const WorkspaceSchedulePage: FC = () => { const error = checkPermissionsError || getTemplateError; const isLoading = !template || !permissions; + const [isConfirmingApply, setIsConfirmingApply] = useState(false); + const { mutate: updateWorkspace } = useMutation({ + mutationFn: () => + startWorkspace(workspace.id, workspace.template_active_version_id), + }); + return ( <> @@ -111,24 +121,24 @@ export const WorkspaceSchedulePage: FC = () => { ), autostopChanged: scheduleChanged(getAutostop(workspace), values), }); - - navigate(`/@${username}/${workspaceName}`); + setIsConfirmingApply(true); }} /> )} { - scheduleSend("RESTART_WORKSPACE"); + updateWorkspace(); + navigate(`/@${username}/${workspaceName}`); }} onClose={() => { - scheduleSend("APPLY_LATER"); + navigate(`/@${username}/${workspaceName}`); }} /> diff --git a/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts b/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts deleted file mode 100644 index 9d14f790e0249..0000000000000 --- a/site/src/xServices/workspaceSchedule/workspaceScheduleXService.ts +++ /dev/null @@ -1,233 +0,0 @@ -/** - * @fileoverview workspaceSchedule is an xstate machine backing a form to CRUD - * an individual workspace's schedule. - */ -import { assign, createMachine } from "xstate"; -import * as API from "api/api"; -import * as TypesGen from "api/typesGenerated"; - -type Permissions = Record, boolean>; - -export interface WorkspaceScheduleContext { - getWorkspaceError?: unknown; - /** - * Each workspace has their own schedule (start and ttl). For this reason, we - * re-fetch the workspace to ensure we're up-to-date. As a result, this - * machine is partially influenced by workspaceXService. - */ - workspace: TypesGen.Workspace; - template?: TypesGen.Template; - getTemplateError?: unknown; - permissions?: Permissions; - checkPermissionsError?: unknown; - submitScheduleError?: unknown; - autostopChanged?: boolean; - shouldRestartWorkspace?: boolean; -} - -export const checks = { - updateWorkspace: "updateWorkspace", -} as const; - -const permissionsToCheck = (workspace: TypesGen.Workspace) => - ({ - [checks.updateWorkspace]: { - object: { - resource_type: "workspace", - resource_id: workspace.id, - owner_id: workspace.owner_id, - }, - action: "update", - }, - }) as const; - -export type WorkspaceScheduleEvent = - | { - type: "SUBMIT_SCHEDULE"; - autostart: TypesGen.UpdateWorkspaceAutostartRequest; - autostartChanged: boolean; - ttl: TypesGen.UpdateWorkspaceTTLRequest; - autostopChanged: boolean; - } - | { type: "RESTART_WORKSPACE" } - | { type: "APPLY_LATER" }; - -export const workspaceSchedule = - /** @xstate-layout N4IgpgJg5mDOIC5QHcD2AnA1rADgQwGMwBlAgC0gFcAbEgFzzrAGIBxAUQBUB9AdQHkASgGliABQCCAYXYBtAAwBdRKBypYASzobUAOxUgAHogCMAJgB0AVgAcAThsAWeXZMA2O2YDsdq1YA0IACeiGbyFl428o4AzFYmMT4xbl5mAL5pgWhYuIQk5FS0xAxMFjB02rpQvBjY+ETMEHpgFhq6AG6omC3lNTn1YArKSCBqmtp6BsYI7tbybmZ+9mYO8u6OgSEIMY6OFo5eC34mdqkLNhlZtblEpBQQNPSMPWAVbdXXA8xg6OgYFjhqIwAGYYAC2ZVefTqeSGBjGWh0+hG02SNmsXhMVhWdkcbkcNm8Xk2oRcFgW7hi8h8djcVkuIGyMNuBQeRRKLzeVTEPzBGlgmj0sEazVaHS6LQKBEwPPQfIFSNgcJGCImyNA0wAtOEYrjsa4vD5bMkEiSEG44uTFosCa41lY3AymTd8vdHsVnpCuVBZfLBbphT8-ugAUC6KC5RYpTLefz-UqlPD1IjJijEMkrBE3G4bO5cfN5GYzGadnsDgt5rmnFZHL4nZ88ndCk9Sjh0HAwLo6AAxcHMYgAVQAQgBZACSPGIUgAEuwACIDgAyckTKuTaqmiEzqTMdOzJixJi82OLwUQNic5MxrjxusS5nr-UbrPdHIssEoACM+d6m2yWE0ugtG0nTdO+X4-n+jzKqo65IpuMx7CYF7ZjYMSJLihpeBsZ4zO4Jj7HahomPINaEo4j7Mq6zYeqUH7flolRQFBtAikBYqgS09GQS+tCyCYwyweM8Fpggjg1hYu5UrqZiOCsjjmGaB5uARtYkfq7jUjYjqZIyDYsm67KetxjHvCxLBBv8gIguC4EMXQ5kwaMcGphq6YpBEB4JFYuqxN4Jhmq4bj7CkiReNEpykSYlEuuZtFcWQqDIO8ghwAw6B0HOGh4NQqBQMwgjsMQnASIIPACCI4jSCugnOcJrlGOeMT7LYyExMhClybESmJMFrU2OFDo+OYOlXE+Bk0W+sCJclVSpbA6WZdluX5RIYhiIuACa3CLhInDsIITmqiJbnbG4Oq1gpaxOHmMRKWY2kWPYObnehOwxDFAxxW+lnoGwXB8EIoiSDIR0ueqjXbFYdgWKcVZmCN5x+EpBJPa4ngmLE8jtV4GS6boqAQHABjOl9vEtmASb1RDWqo75GmGr4aFuGampYpYTgI-IUS+Nzhy47ppPPoZFOtBAtBUymNOmOEUQ1nidJWMe1IPazdIWDsXMHseXgxFEAtjVR32euUTHQi6ksbqJXgWPI8y1rJix23ESso3sdvRLrKxmDEjvpIL+nUf+8VekxvpxoqlsnZD6K5nYLhHnJNjYmsZoEjbuu+ASJqdbrn3C5Nnpth2Xa9nKUcNdMuxPdjaFOFS2ErGakTNQjDu3qkJF2PnE3B1NEGmVU5kV9LMzhNDSvzBa1IqZ4OFbLSMMWj7OZoXrhIfQH41B6+xkzSlaV4BlWU5XlI8ISReyYbJ5h2A4cnI7hI2ZtmcSRNpOZxP7huxeTIe-efUSthMyyU8PHO+R4LRKQks4HMmMDhfzWNFLeRs-5vkApTNc1MEK6hAe4c6pE6QDTCCzJ+tZY4nD1qkdCqQBYZCAA */ - createMachine( - { - id: "workspaceScheduleState", - predictableActionArguments: true, - tsTypes: {} as import("./workspaceScheduleXService.typegen").Typegen0, - schema: { - context: {} as WorkspaceScheduleContext, - events: {} as WorkspaceScheduleEvent, - services: {} as { - getTemplate: { - data: TypesGen.Template; - }; - }, - }, - initial: "gettingPermissions", - states: { - gettingPermissions: { - entry: "clearGetPermissionsError", - invoke: { - src: "checkPermissions", - id: "checkPermissions", - onDone: [ - { - actions: ["assignPermissions"], - target: "gettingTemplate", - }, - ], - onError: [ - { - actions: "assignGetPermissionsError", - target: "error", - }, - ], - }, - tags: "loading", - }, - gettingTemplate: { - entry: "clearGetTemplateError", - invoke: { - src: "getTemplate", - id: "getTemplate", - onDone: { - target: "presentForm", - actions: ["assignTemplate"], - }, - onError: { - target: "error", - actions: ["assignGetTemplateError"], - }, - }, - tags: "loading", - }, - presentForm: { - on: { - SUBMIT_SCHEDULE: { - target: "submittingSchedule", - actions: "assignAutostopChanged", - }, - }, - }, - submittingSchedule: { - invoke: { - src: "submitSchedule", - id: "submitSchedule", - onDone: [ - { - cond: "autostopChanged", - target: "showingRestartDialog", - }, - { target: "done" }, - ], - onError: { - target: "presentForm", - actions: ["assignSubmissionError"], - }, - }, - tags: "loading", - }, - showingRestartDialog: { - on: { - RESTART_WORKSPACE: { - target: "done", - actions: "restartWorkspace", - }, - APPLY_LATER: "done", - }, - }, - error: { - type: "final", - }, - done: { - type: "final", - }, - }, - }, - { - guards: { - autostopChanged: (context) => Boolean(context.autostopChanged), - }, - actions: { - assignSubmissionError: assign({ - submitScheduleError: (_, event) => event.data, - }), - assignPermissions: assign({ - // Setting event.data as Permissions to be more stricted. So we know - // what permissions we asked for. - permissions: (_, event) => event.data as Permissions, - }), - assignGetPermissionsError: assign({ - checkPermissionsError: (_, event) => event.data, - }), - assignTemplate: assign({ - template: (_, event) => event.data, - }), - assignGetTemplateError: assign({ - getTemplateError: (_, event) => event.data, - }), - clearGetTemplateError: assign({ - getTemplateError: (_) => undefined, - }), - assignAutostopChanged: assign({ - autostopChanged: (_, event) => event.autostopChanged, - }), - clearGetPermissionsError: assign({ - checkPermissionsError: (_) => undefined, - }), - - // action instead of service because we fire and forget so that the - // user can return to the workspace page to see the restart - restartWorkspace: (context) => { - if (context.workspace && context.template) { - return API.startWorkspace( - context.workspace.id, - context.template.active_version_id, - ); - } - }, - }, - - services: { - getTemplate: async (context) => { - if (context.workspace) { - return await API.getTemplate(context.workspace.template_id); - } else { - throw Error("Can't fetch template without workspace."); - } - }, - checkPermissions: async (context) => { - if (context.workspace) { - return await API.checkAuthorization({ - checks: permissionsToCheck(context.workspace), - }); - } else { - throw Error( - "Cannot check permissions without both workspace and user id", - ); - } - }, - submitSchedule: async (context, event) => { - if (!context.workspace?.id) { - // This state is theoretically impossible, but helps TS - throw new Error("Failed to load workspace."); - } - - if (event.autostartChanged) { - await API.putWorkspaceAutostart( - context.workspace.id, - event.autostart, - ); - } - if (event.autostopChanged) { - await API.putWorkspaceAutostop(context.workspace.id, event.ttl); - } - }, - }, - }, - ); From a3a09ae346516be5c692439f1b5d94b4fbe14684 Mon Sep 17 00:00:00 2001 From: BrunoQuaresma Date: Wed, 8 Nov 2023 13:37:11 +0000 Subject: [PATCH 5/5] Fix confirm apply --- .../WorkspaceSchedulePage/WorkspaceSchedulePage.tsx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx index 311852681538c..b54edcf437e61 100644 --- a/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx +++ b/site/src/pages/WorkspaceSettingsPage/WorkspaceSchedulePage/WorkspaceSchedulePage.tsx @@ -111,7 +111,7 @@ export const WorkspaceSchedulePage: FC = () => { navigate(`/@${username}/${workspaceName}`); }} onSubmit={async (values) => { - await submitScheduleMutation.mutateAsync({ + const data = { workspace, autostart: formValuesToAutostartRequest(values), ttl: formValuesToTTLRequest(values), @@ -120,8 +120,13 @@ export const WorkspaceSchedulePage: FC = () => { values, ), autostopChanged: scheduleChanged(getAutostop(workspace), values), - }); - setIsConfirmingApply(true); + }; + + await submitScheduleMutation.mutateAsync(data); + + if (data.autostopChanged) { + setIsConfirmingApply(true); + } }} /> )}