diff --git a/webapp/.eslintrc.js b/webapp/.eslintrc.js index 950e756e75..dc892fd578 100644 --- a/webapp/.eslintrc.js +++ b/webapp/.eslintrc.js @@ -89,7 +89,7 @@ module.exports = { "@typescript-eslint/keyword-spacing": ["error"], "@typescript-eslint/no-duplicate-imports": ["error"], "@typescript-eslint/object-curly-spacing": ["error", 'always'], - "@typescript-eslint/quotes": ["error", "single"], + "@typescript-eslint/quotes": ["error", "single", { "avoidEscape": true }], "@typescript-eslint/space-before-function-paren": ["error" , {"anonymous": "always", "named": "never", "asyncArrow": "always"}], "@typescript-eslint/space-infix-ops": ["error", { "int32Hint": false }], "@typescript-eslint/no-confusing-non-null-assertion": ["warn"], diff --git a/webapp/packages/core-blocks/src/Slide/SlideBox.tsx b/webapp/packages/core-blocks/src/Slide/SlideBox.tsx index a1800e8d46..b3d0f9aa9a 100644 --- a/webapp/packages/core-blocks/src/Slide/SlideBox.tsx +++ b/webapp/packages/core-blocks/src/Slide/SlideBox.tsx @@ -19,17 +19,22 @@ export const SlideBox: React.FC = function SlideBox({ children, className useEffect(() => { const div = divRef.current; - if (div) { - function handleScroll() { - if (div) { - div.scrollLeft = 0; - div.scrollTop = 0; - } + function handleScroll() { + if (div) { + div.scrollLeft = 0; + div.scrollTop = 0; } - div.addEventListener('scroll', handleScroll); - - return () => { div.removeEventListener('scroll', handleScroll); }; } + + if (div) { + div.addEventListener('scroll', handleScroll); + } + + return () => { + if (div) { + div.removeEventListener('scroll', handleScroll); + } + }; }, []); return ( diff --git a/webapp/packages/core-connections/src/DBDriverResource.ts b/webapp/packages/core-connections/src/DBDriverResource.ts index e6e4ec67b1..b6a8278bb5 100644 --- a/webapp/packages/core-connections/src/DBDriverResource.ts +++ b/webapp/packages/core-connections/src/DBDriverResource.ts @@ -24,7 +24,7 @@ export type DBDriver = DatabaseDriverFragment; @injectable() export class DBDriverResource extends CachedMapResource { constructor( - private graphQLService: GraphQLService, + private readonly graphQLService: GraphQLService, permissionsResource: PermissionsResource, ) { super(); @@ -64,6 +64,10 @@ export class DBDriverResource extends CachedMapResource driver.id === driverId)) { + throw new Error('Driver is not found'); + } + this.updateDriver(...drivers); }); diff --git a/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx b/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx index 7f4df303f4..e0b03423c7 100644 --- a/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx +++ b/webapp/packages/plugin-authentication/src/Dialog/AuthDialog.tsx @@ -126,14 +126,14 @@ export const AuthDialog: DialogComponent = observer(function if (state.configure) { return ( - - {translate('authentication_provider_disabled')} - {authenticationService.configureAuthProvider && ( - navToSettings()}> - - - )} - + + {translate('authentication_provider_disabled')} + {authenticationService.configureAuthProvider && ( + { navToSettings(); }}> + + + )} + ); } @@ -147,7 +147,7 @@ export const AuthDialog: DialogComponent = observer(function } return styled(useStyles(styles, UNDERLINE_TAB_STYLES))( - state.setTabId(tabData.tabId)}> + { state.setTabId(tabData.tabId); }}> = observer(function tabId={provider.id} title={provider.description || provider.label} disabled={state.authenticating} - onClick={() => state.setActiveProvider(provider)} + onClick={() => { state.setActiveProvider(provider); }} > {provider.label} @@ -190,7 +190,7 @@ export const AuthDialog: DialogComponent = observer(function tabId={FEDERATED_AUTH} title={translate('authentication_auth_federated')} disabled={state.authenticating} - onClick={() => state.setActiveProvider(null)} + onClick={() => { state.setActiveProvider(null); }} > {translate('authentication_auth_federated')} @@ -201,8 +201,8 @@ export const AuthDialog: DialogComponent = observer(function {() => federate ? ( - - ) + + ) : renderForm(state.activeProvider)} diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx index a2a3cc1907..363202383f 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx +++ b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionForm.tsx @@ -10,7 +10,7 @@ import { observer } from 'mobx-react-lite'; import { useEffect } from 'react'; import styled, { css } from 'reshadow'; -import { TabsState, TabList, UNDERLINE_TAB_STYLES, TabPanelList, Placeholder, useObjectRef, useExecutor, BASE_CONTAINERS_STYLES, IconOrImage, Loader } from '@cloudbeaver/core-blocks'; +import { TabsState, TabList, UNDERLINE_TAB_STYLES, TabPanelList, Placeholder, useObjectRef, useExecutor, BASE_CONTAINERS_STYLES, IconOrImage, Loader, ErrorMessage, useErrorDetails } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; import type { ConnectionConfig } from '@cloudbeaver/core-sdk'; @@ -131,6 +131,7 @@ export const ConnectionForm = observer(function ConnectionForm({ const style = [tabsStyles, UNDERLINE_TAB_STYLES]; const styles = useStyles(style, BASE_CONTAINERS_STYLES, topBarStyles, formStyles); const service = useService(ConnectionFormService); + const error = useErrorDetails(state.initError); useExecutor({ executor: state.submittingTask, @@ -149,6 +150,16 @@ export const ConnectionForm = observer(function ConnectionForm({ state.loadConnectionInfo(); }, []); + if (state.initError) { + return styled(styles)( + + ); + } + if (!state.configured) { return styled(styles)( diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionFormState.ts b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionFormState.ts index ae8c9cc445..1314037fef 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionFormState.ts +++ b/webapp/packages/plugin-connections/src/ConnectionForm/ConnectionFormState.ts @@ -29,6 +29,7 @@ export class ConnectionFormState implements IConnectionFormState { statusMessage: string | null; configured: boolean; + initError: Error | null; get loading(): boolean { return this.loadConnectionTask.executing || this.submittingTask.executing; @@ -75,14 +76,16 @@ export class ConnectionFormState implements IConnectionFormState { readonly submittingTask: IExecutor; private stateInfo: IFormStateInfo | null; - private loadConnectionTask: IExecutor; - private formStateTask: IExecutor; + private readonly loadConnectionTask: IExecutor; + private readonly formStateTask: IExecutor; private _availableDrivers: string[]; constructor( service: ConnectionFormService, resource: IConnectionsResource ) { + this.initError = null; + makeObservable(this, { mode: observable, type: observable, @@ -94,6 +97,7 @@ export class ConnectionFormState implements IConnectionFormState { configured: observable, readonly: computed, stateInfo: observable, + initError: observable.ref, }); this.resource = resource; @@ -141,9 +145,15 @@ export class ConnectionFormState implements IConnectionFormState { } async loadConnectionInfo(): Promise { - await this.loadConnectionTask.execute(this); + try { + await this.loadConnectionTask.execute(this); + this.initError = null; - return this.info; + return this.info; + } catch (exception) { + this.initError = exception; + throw exception; + } } async load(): Promise { diff --git a/webapp/packages/plugin-connections/src/ConnectionForm/IConnectionFormProps.ts b/webapp/packages/plugin-connections/src/ConnectionForm/IConnectionFormProps.ts index e876a2df43..b238bf5f32 100644 --- a/webapp/packages/plugin-connections/src/ConnectionForm/IConnectionFormProps.ts +++ b/webapp/packages/plugin-connections/src/ConnectionForm/IConnectionFormProps.ts @@ -23,6 +23,7 @@ export interface IConnectionFormState { partsState: MetadataMap; + readonly initError: Error | null; readonly statusMessage: string | null; readonly disabled: boolean; readonly loading: boolean;