diff --git a/webapp/packages/core-administration/package.json b/webapp/packages/core-administration/package.json index b91285872c..adf258a365 100644 --- a/webapp/packages/core-administration/package.json +++ b/webapp/packages/core-administration/package.json @@ -20,6 +20,7 @@ "@cloudbeaver/core-root": "^0.1.0", "@cloudbeaver/core-routing": "^0.1.0", "@cloudbeaver/core-theming": "^0.1.0", + "@cloudbeaver/core-executor": "^0.1.0", "react": "^16.x.x", "mobx": "^5.x.x", "mobx-react": "^6.x.x", diff --git a/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts b/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts index 2e7b9b2ea2..735c10e856 100644 --- a/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts +++ b/webapp/packages/core-administration/src/AdministrationItem/IAdministrationItem.ts @@ -54,7 +54,7 @@ export interface IAdministrationItemSubItem { export interface IConfigurationWizardItemOptions { description: string; - onValidate?: () => boolean; + onValidate?: () => Promise | boolean; isDisabled?: () => boolean; isHidden?: () => boolean; isDone?: () => boolean; diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts index a0292993c1..38daf0e41b 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts +++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/ConfigurationWizardService.ts @@ -55,15 +55,6 @@ export class ConfigurationWizardService { }); } - @computed get canNext() { - if (this.currentStep?.configurationWizardOptions?.isDone - && !this.currentStep?.configurationWizardOptions?.isDone()) { - return false; - } - - return true; - } - @computed get nextStep() { return this.steps.find((item, index) => { if (index <= this.currentStepIndex) { @@ -112,27 +103,37 @@ export class ConfigurationWizardService { const step = this.getStep(name); if (!step) { - return; + return false; + } + + if (step.configurationWizardOptions?.onValidate) { + const isValid = await step.configurationWizardOptions.onValidate(); + if (!isValid) { + return false; + } } if (step.configurationWizardOptions?.onFinish) { await step.configurationWizardOptions.onFinish(); } + return true; } - next() { - if (!this.currentStep || !this.canNext) { + async next() { + if (!this.currentStep) { return; } - this.finishStep(this.currentStep.name); + if (!await this.finishStep(this.currentStep.name)) { + return; + } if (this.currentStepIndex + 1 < this.steps.length) { if (this.nextStep) { this.administrationScreenService.navigateToItem(this.nextStep.name); } } else { - this.finish(); + await this.finish(); } } diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationForm.tsx b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationForm.tsx index c36c7b4c90..d39f7d5086 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationForm.tsx +++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationForm.tsx @@ -7,15 +7,19 @@ */ import { observer } from 'mobx-react'; +import { useEffect } from 'react'; import styled, { css } from 'reshadow'; import { SubmittingForm, InputGroup, InputField, useFocus, Switch } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; import { ServerConfigInput } from '@cloudbeaver/core-sdk'; import { useStyles } from '@cloudbeaver/core-theming'; +import { ServerConfigurationService } from './ServerConfigurationService'; + export const formStyles = css` SubmittingForm { flex: 1; @@ -51,9 +55,20 @@ export const ServerConfigurationForm = observer(function ServerConfigurationForm onChange, onSave, }: Props) { + const service = useService(ServerConfigurationService); const translate = useTranslate(); const [focusedRef] = useFocus({ focusFirstChild: true }); + useEffect(() => { + const validate = () => { + focusedRef.current?.checkValidity(); + focusedRef.current?.reportValidity(); + }; + service.validationTask.addHandler(validate); + + return () => service.validationTask.removeHandler(validate); + }, [service]); + return styled(useStyles(formStyles, boxStyles))( diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationService.ts b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationService.ts index 7b3870a25f..ed5bf7bd55 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationService.ts +++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/ServerConfiguration/ServerConfigurationService.ts @@ -8,6 +8,7 @@ import { injectable } from '@cloudbeaver/core-di'; import { NotificationService } from '@cloudbeaver/core-events'; +import { IExecutor, Executor, IContextProvider } from '@cloudbeaver/core-executor'; import { GraphQLService } from '@cloudbeaver/core-sdk'; import { AdministrationScreenService } from '../../../AdministrationScreenService'; @@ -17,6 +18,7 @@ import { IServerConfigurationPageState } from './IServerConfigurationPageState'; export class ServerConfigurationService { readonly state: IServerConfigurationPageState; + readonly validationTask: IExecutor; constructor( private administrationScreenService: AdministrationScreenService, @@ -42,18 +44,35 @@ export class ServerConfigurationService { showUtilityObjects: false, }, })); + this.validationTask = new Executor(); } isDone() { return this.isFormFilled(); } - validate() { - return this.isFormFilled(); + async validate() { + const context = await this.validationTask.execute(true); + const state = await context.getContext(this.validationStatusContext); + + return state.getState(); + } + + validationStatusContext = (context: IContextProvider) => { + let state = this.isFormFilled(); + + const invalidate = () => { + state = false; + }; + const getState = () => state; + + return { + getState, + invalidate, + }; } private isFormFilled() { - return !!( this.state?.serverConfig.serverName && this.state.serverConfig.adminName diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/WizardStepper.tsx b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/WizardStepper.tsx index c7675faeea..fc42b83bbb 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/WizardStepper.tsx +++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/WizardStepper.tsx @@ -79,7 +79,6 @@ export const WizardStepper = observer(function WizardStepper() { type="button" mod={['unelevated']} onClick={() => service.next()} - disabled={!service.canNext} > {translate(service.currentStepIndex === service.steps.length - 1 ? 'ui_stepper_finish' : 'ui_stepper_next')}