diff --git a/gui/src/base-components/MultiStepsProvider/index.vue b/gui/src/base-components/MultiStepsProvider/index.vue index f3a8b72..a55d5c2 100644 --- a/gui/src/base-components/MultiStepsProvider/index.vue +++ b/gui/src/base-components/MultiStepsProvider/index.vue @@ -58,7 +58,7 @@ ? 'url(#successGradient)' : 'none' " - :icon="isStageConnected(stage + 1) ? 'CheckCircle2' : 'Circle'" + :icon="hasError ? 'XCircle' : isStageConnected(stage + 1) ? 'CheckCircle2' : 'Circle'" class="w-8 h-8" :class="[ { 'text-danger': hasError }, @@ -196,46 +196,54 @@ const isPrevButtonDisabled = computed(() => { const prevButtonTooltip = computed(() => (isPrevButtonDisabled.value ? currentStage.value.prevButtonTooltip : "")) const toPreviousPage = async ({ rawNavigation }: { rawNavigation?: boolean } = {}) => { - const { componentV$, ...data } = currentStageRef.value || {} - const isComponentValid = !componentV$ || (await componentV$.value.$validate()) - const isTitleValid = !props.v$ || (await props.v$.$validate()) - if (isTitleValid && isComponentValid) { - clientErrorMessages.value = [] - if (rawNavigation) { - currentStageName.value = currentStage.value.previous + const { componentV$, ...data } = currentStageRef.value || {}; + const isComponentValid = !componentV$ || (await componentV$.value.$validate()); + const isTitleValid = !props.v$ || (await props.v$.$validate()); + try { + if (!isTitleValid || !isComponentValid) { + clientErrorMessages.value = componentV$.value.$errors.map( + (error: ErrorObject) => error.$propertyPath + ": " + error.$message + ); } else { - const toPrev = () => (currentStageName.value = currentStage.value.previous) - // emits data handler if navigation requires it, here your api call may be implemented - await currentStage.value.onPrevPageClick?.(toPrev, data) + clientErrorMessages.value = []; } - } else { - // TODO: refactor messages and reinvent way of displaying - clientErrorMessages.value = componentV$.value.$errors.map( - (error: ErrorObject) => error.$propertyPath + ": " + error.$message - ) - } -} + } catch (error) { + } finally { + if (rawNavigation) { + currentStageName.value = currentStage.value.previous; + } else { + const toPrev = () => (currentStageName.value = currentStage.value.previous); + await currentStage.value.onPrevPageClick?.(toPrev, data); + } + } +}; + -// Navigates to next page only if current stage fields are valid +// Navigates to next page regardless if current stage fields are valid const toNextPage = async ({ rawNavigation }: { rawNavigation?: boolean } = {}) => { - const { componentV$, ...data } = currentStageRef.value || {} - const isComponentValid = !componentV$ || (await componentV$.value.$validate()) - const isTitleValid = !props.v$ || (await props.v$.$validate()) - if (isTitleValid && isComponentValid) { - clientErrorMessages.value = [] + const { componentV$, ...data } = currentStageRef.value || {}; + const isComponentValid = !componentV$ || (await componentV$.value.$validate()); + const isTitleValid = !props.v$ || (await props.v$.$validate()); + + try { + if (!isTitleValid || !isComponentValid) { + clientErrorMessages.value = componentV$.value.$errors.map( + (error: ErrorObject) => error.$propertyPath + ": " + error.$message + ); + } else { + clientErrorMessages.value = []; + } + } catch (error) { + console.error('Validation error:', error); + } finally { if (rawNavigation) { - currentStageName.value = currentStage.value.next + currentStageName.value = currentStage.value.next; } else { - const toNext = () => (currentStageName.value = currentStage.value.next) - // emits data handler if navigation requires it, here your api call may be implemented - await currentStage.value.onNextPageClick?.(toNext, data) + const toNext = () => (currentStageName.value = currentStage.value.next); + await currentStage.value.onNextPageClick?.(toNext, data); } - } else { - clientErrorMessages.value = componentV$.value.$errors.map( - (error: ErrorObject) => error.$propertyPath + ": " + error.$message - ) } -} +}; const onSaveClick = async () => { const { componentV$, ...data } = currentStageRef.value || {} diff --git a/gui/src/components/Application/index.vue b/gui/src/components/Application/index.vue index cf846bb..e28abf5 100644 --- a/gui/src/components/Application/index.vue +++ b/gui/src/components/Application/index.vue @@ -164,13 +164,15 @@ const updateStagesData = () => { environmentVariables: applicationData.environmentVariables } - return await Promise.all([getComponentList(), applicationStore.validateApplication(stepPayload)]) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - next() - }) - .catch(handleError) + try { + await Promise.all([getComponentList(), applicationStore.validateApplication(stepPayload)]); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + next(); + } }, payload: { content: applicationData.content, @@ -190,26 +192,28 @@ const updateStagesData = () => { onNextPageClick: async (next: () => void, { resources }: { resources: Array }) => { applicationData.resources = resources - await applicationStore - .validateApplication({ title: applicationData.title, resources: applicationData.resources }) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - next() - }) - .catch(handleError) + try { + await applicationStore.validateApplication({ title: applicationData.title, resources: applicationData.resources }); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + next(); + } }, onPrevPageClick: async (prev: () => void, { resources }: { resources: Array }) => { applicationData.resources = resources - await applicationStore - .validateApplication({ title: applicationData.title, resources: applicationData.resources }) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - prev() - }) - .catch(handleError) + try { + await applicationStore.validateApplication({ title: applicationData.title, resources: applicationData.resources }); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + prev(); + } }, payload: { appResources: applicationData.resources @@ -243,20 +247,21 @@ const updateStagesData = () => { applicationData.metrics = metrics applicationData.sloViolations = sloViolations - await applicationStore - .validateApplication({ + try { + await applicationStore.validateApplication({ title: applicationData.title, templates: applicationData.templates, parameters: applicationData.parameters, metrics: applicationData.metrics, - sloViolations: applicationData.sloViolations - }) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - next() - }) - .catch(handleError) + sloViolations: applicationData.sloViolations, + }); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + next(); // Always navigate to the next step + } }, onPrevPageClick: async ( prev: () => void, @@ -277,20 +282,21 @@ const updateStagesData = () => { applicationData.metrics = metrics applicationData.sloViolations = sloViolations - await applicationStore - .validateApplication({ + try { + await applicationStore.validateApplication({ title: applicationData.title, templates: applicationData.templates, parameters: applicationData.parameters, metrics: applicationData.metrics, - sloViolations: applicationData.sloViolations - }) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - prev() - }) - .catch(handleError) + sloViolations: applicationData.sloViolations, + }); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + prev(); + } }, payload: { componentList: componentList.value, @@ -315,17 +321,18 @@ const updateStagesData = () => { ) => { applicationData.utilityFunctions = utilityFunctions - await applicationStore - .validateApplication({ + try { + await applicationStore.validateApplication({ title: applicationData.title, - utilityFunctions: applicationData.utilityFunctions - }) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - next() - }) - .catch(handleError) + utilityFunctions: applicationData.utilityFunctions, + }); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + next(); + } }, onPrevPageClick: async ( prev: () => void, @@ -333,17 +340,18 @@ const updateStagesData = () => { ) => { applicationData.utilityFunctions = utilityFunctions - await applicationStore - .validateApplication({ + try { + await applicationStore.validateApplication({ title: applicationData.title, - utilityFunctions: applicationData.utilityFunctions - }) - .then(() => { - pathsWithError.value = [] - responseErrorMessages.value = [] - prev() - }) - .catch(handleError) + utilityFunctions: applicationData.utilityFunctions, + }); + pathsWithError.value = []; + responseErrorMessages.value = []; + } catch (error) { + handleError(error); + } finally { + prev(); + } }, payload: { metrics: applicationData.metrics.map((metric) => metric.name), diff --git a/gui/src/containers/Applications/ApplicationCreation/index.vue b/gui/src/containers/Applications/ApplicationCreation/index.vue index 5ad64f1..b8b0903 100644 --- a/gui/src/containers/Applications/ApplicationCreation/index.vue +++ b/gui/src/containers/Applications/ApplicationCreation/index.vue @@ -15,15 +15,25 @@ const uiStore = useUIStore() const router = useRouter() const createApplication = async (applicationData: IApplication): Promise => { - return await applicationStore.validateApplication(applicationData).then(async () => { + try { + if (!applicationData.title) { + throw new Error("Title is required"); + } return await applicationStore.createApplication(applicationData).then((application) => { uiStore.setSnackbarMessage({ message: `Successfully created application ${application.title}`, type: SNACKBAR_MESSAGE_TYPES.SUCCESS - }) - router.push({ name: "applications-overview" }) - return application - }) - }) -} + }); + router.push({ name: "applications-overview" }); + return application; + }); + } catch (error) { + uiStore.setSnackbarMessage({ + message: `Failed to create application: ${error.message}`, + type: SNACKBAR_MESSAGE_TYPES.ERROR + }); + throw error; + } +}; +