diff --git a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/Finish/FinishPage.tsx b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/Finish/FinishPage.tsx index c19974014d..0e851d08ae 100644 --- a/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/Finish/FinishPage.tsx +++ b/webapp/packages/core-administration/src/AdministrationScreen/ConfigurationWizard/Pages/Finish/FinishPage.tsx @@ -41,6 +41,11 @@ const styles = composes( border: solid 1px; padding: 16px 24px } + + p { + line-height: 2; + white-space: pre; + } ` ); @@ -52,8 +57,8 @@ export const FinishPage = observer(function FinishPage() { -

All done

-

Go to fun

+

{translate('administration_configuration_wizard_finish_title')}

+

{translate('administration_configuration_wizard_finish_message')}

} + {onAdd && } diff --git a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx index 6883c5ad8c..d4251ee281 100644 --- a/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx +++ b/webapp/packages/core-blocks/src/PropertiesTable/PropertyItem.tsx @@ -134,10 +134,10 @@ export const PropertyItem = observer(function PropertyItem({ const handleRemove = useCallback(() => onRemove(property.id), [property]); useLayoutEffect(() => { - if (keyInputRef.current && isKeyEditable) { + if (keyInputRef.current && isKeyEditable && property.new) { keyInputRef.current.focus(); } - }, []); + }, [property]); return styled(useStyles(styles))( diff --git a/webapp/packages/core-bootstrap/src/AppBootstrap.ts b/webapp/packages/core-bootstrap/src/AppBootstrap.ts index 1ac408b8ed..17546199db 100644 --- a/webapp/packages/core-bootstrap/src/AppBootstrap.ts +++ b/webapp/packages/core-bootstrap/src/AppBootstrap.ts @@ -12,12 +12,10 @@ import { NavigationTreeContextMenuService, LogViewerMenuService, ConnectionSchemaManagerService, - AppScreenService, } from '@cloudbeaver/core-app'; import { injectable } from '@cloudbeaver/core-di'; import { SessionExpireService } from '@cloudbeaver/core-dialogs'; import { ExceptionsCatcherService } from '@cloudbeaver/core-events'; -import { RouterService } from '@cloudbeaver/core-routing'; import { ThemeService } from '@cloudbeaver/core-theming'; /** diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverProperties.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverProperties.tsx index 9049b630e9..004588e5bb 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverProperties.tsx +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverProperties.tsx @@ -39,7 +39,7 @@ export const DriverProperties = observer(function DriverProperties({ state, loadProperties, }: DriverPropertiesProps) { - const controller = useController(DriverPropertiesController, driverId); + const controller = useController(DriverPropertiesController, driverId, state); useMemo(() => { if (loadProperties) { @@ -54,7 +54,7 @@ export const DriverProperties = observer(function DriverProperties({ )} diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverPropertiesController.ts b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverPropertiesController.ts index 2d962886c0..e467d10628 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverPropertiesController.ts +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionForm/DriverProperties/DriverPropertiesController.ts @@ -32,20 +32,24 @@ export class DriverPropertiesController implements IInitializableController { loaded = false; + private state!: Record; + constructor( private driverPropertiesService: DriverPropertiesService, private notificationService: NotificationService ) { } - init(driverId: string) { + init(driverId: string, state: Record) { this.driverId = driverId; + this.state = state; } - onAddProperty = () => { + addProperty = (key?: string, value?: string) => { this.driverProperties.unshift({ id: uuid(), - key: 'property', - defaultValue: '', + key: key ?? 'property', + defaultValue: value ?? '', + new: !key, }); } @@ -64,6 +68,14 @@ export class DriverPropertiesController implements IInitializableController { description: property.description, validValues: property.validValues, }))); + + for (const key of Object.keys(this.state)) { + if (this.driverProperties.some(property => property.key === key)) { + continue; + } + + this.addProperty(key, this.state[key]); + } this.loaded = true; } catch (exception) { this.notificationService.logException(exception, 'Can\'t load driver properties'); diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsAdministration.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsAdministration.tsx index 782baa80b4..66b59ab3da 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsAdministration.tsx +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsAdministration.tsx @@ -12,6 +12,7 @@ import styled, { css, use } from 'reshadow'; import { AdministrationTools, AdministrationItemContentProps } from '@cloudbeaver/core-administration'; import { Loader, IconButton } from '@cloudbeaver/core-blocks'; import { useController } from '@cloudbeaver/core-di'; +import { Translate } from '@cloudbeaver/core-localization'; import { useStyles, composes } from '@cloudbeaver/core-theming'; import { ConnectionsAdministrationController } from './ConnectionsAdministrationController'; @@ -24,11 +25,16 @@ const styles = composes( composes: theme-background-surface theme-text-on-surface from global; } - layout-grid-cell { + layout-grid-cell, message-box { composes: theme-border-color-background from global; } `, css` + message-box { + padding: 16px 24px; + border-bottom: solid 1px; + } + layout-grid { width: 100%; flex: 1; @@ -59,6 +65,11 @@ const styles = composes( padding: 0 12px; padding-right: 24px; } + + p { + line-height: 2; + white-space: pre; + } ` ); @@ -73,6 +84,11 @@ export const ConnectionsAdministration = observer(function ConnectionsAdministra + {configurationWizard && ( + +

+
+ )} diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx index 3c83bf1a29..2f946cd0a4 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionEdit.tsx @@ -26,18 +26,13 @@ const styles = composes( `, css` box { + box-sizing: border-box; padding: 24px; min-height: 320px; max-height: 500px; display: flex; flex-direction: column; } - - ErrorMessage { - position: sticky; - bottom: 0; - padding: 8px 24px; - } ` ); diff --git a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionsTable.tsx b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionsTable.tsx index 927d0ea02a..b857fdde6f 100644 --- a/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionsTable.tsx +++ b/webapp/packages/core-connections/src/Administration/Connections/ConnectionsTable/ConnectionsTable.tsx @@ -25,6 +25,9 @@ const styles = composes( } `, css` + Table { + width: 100%; + } TableColumnHeader { border-top: solid 1px; } diff --git a/webapp/packages/core-connections/src/locales/en.ts b/webapp/packages/core-connections/src/locales/en.ts index e6a3e0f8b0..108fa27a00 100644 --- a/webapp/packages/core-connections/src/locales/en.ts +++ b/webapp/packages/core-connections/src/locales/en.ts @@ -5,6 +5,7 @@ export default [ ['connections_administration_new_connection', 'New connection'], ['connections_administration_configuration_wizard_step_title', 'Database connections'], ['connections_administration_configuration_wizard_step_description', 'Database connections'], + ['connections_administration_configuration_wizard_message', 'Here you can create the database connections manually or using a database server search feature.\nYou can skip this step and configure connections later.'], ['connections_connection_create_search_database', 'Search Database'], ['connections_connection_edit_authentication', 'Authentication'], ['connections_connection_edit_access', 'Access'], diff --git a/webapp/packages/core-theming/src/styles/_table.scss b/webapp/packages/core-theming/src/styles/_table.scss index c4edd9db1c..6ee29790cf 100644 --- a/webapp/packages/core-theming/src/styles/_table.scss +++ b/webapp/packages/core-theming/src/styles/_table.scss @@ -12,10 +12,15 @@ min-width: 100%; text-align: left; border-collapse: collapse; + table-layout: fixed; &[use|size="big"] { > thead > tr > th { height: 54px; + + &[use|min] { + width: 46px; + } } > tbody > tr > td { @@ -53,6 +58,7 @@ } th { + box-sizing: border-box; white-space: nowrap; padding: 16px; height: 36px; @@ -64,7 +70,7 @@ text-decoration: none !important; &[use|min] { - width: 1%; + width: 28px; } &:last-child { @@ -74,6 +80,7 @@ td { position: relative; + box-sizing: border-box; height: 28px; padding: 0 16px; transition: padding ease-in-out 0.24s; diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/CreateUser.tsx b/webapp/packages/plugin-authentication/src/Administration/Users/CreateUser.tsx index 0ff36b77d6..9b00d72650 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/CreateUser.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Users/CreateUser.tsx @@ -28,13 +28,13 @@ const styles = composes( css` title-bar { composes: theme-typography--headline6 from global; - padding: 0 16px; + box-sizing: border-box; + padding: 16px 24px; border-top: solid 1px; align-items: center; - height: 48px; display: flex; font-weight: 400; - flex: 1; + flex: auto 0 0; } ` ); @@ -47,7 +47,7 @@ export function CreateUser({ return styled(useStyles(styles))( - + ); } diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserForm.tsx b/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserForm.tsx index 9c5861ee45..210f58da14 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserForm.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserForm.tsx @@ -21,6 +21,7 @@ import { } from '@cloudbeaver/core-blocks'; import { useController } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; +import { AdminUserInfo } from '@cloudbeaver/core-sdk'; import { useStyles, composes } from '@cloudbeaver/core-theming'; import { GrantedConnections } from './GrantedConnections'; @@ -53,10 +54,6 @@ const styles = composes( } `, css` - box { - padding: 24px; - } - SubmittingForm { flex: 1; display: flex; @@ -128,16 +125,18 @@ const styles = composes( ); type Props = { - userId: string; + user: AdminUserInfo; + editing?: boolean; onCancel: () => void; } export const UserForm = observer(function UserForm({ - userId, + user, + editing = false, onCancel, }: Props) { const translate = useTranslate(); - const controller = useController(UserFormController, userId, onCancel); + const controller = useController(UserFormController, user, editing, onCancel); const [focusedRef] = useFocus({ focusFirstChild: true }); const handleLoginChange = useCallback( @@ -182,7 +181,7 @@ export const UserForm = observer(function UserForm({ mod={['unelevated']} onClick={controller.save} > - {translate(controller.isNew ? 'ui_processing_create' : 'ui_processing_save')} + {translate(!editing ? 'ui_processing_create' : 'ui_processing_save')} @@ -199,7 +198,7 @@ export const UserForm = observer(function UserForm({ name='login' value={controller.credentials.login} onChange={handleLoginChange} - disabled={!controller.isNew || controller.isSaving} + disabled={editing || controller.isSaving} mod='surface' required > diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserFormController.ts b/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserFormController.ts index feba6eb8b2..6edbc7a5f9 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserFormController.ts +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UserForm/UserFormController.ts @@ -31,10 +31,6 @@ export class UserFormController implements IInitializableController, IDestructib roles: new Map(), }; - @computed get isNew() { - return this.usersResource.isNew(this.userId); - } - @computed get connections() { return Array.from(this.connectionsResource.data.values()) .filter(connection => !this.connectionsResource.isNew(connection.id)); @@ -44,16 +40,14 @@ export class UserFormController implements IInitializableController, IDestructib return Array.from(this.rolesManagerService.roles.data.values()); } - @computed get user(): AdminUserInfo { - return this.usersResource.get(this.userId)!; - } + user!: AdminUserInfo; readonly error = new GQLErrorCatcher(); private isDistructed = false; - private userId!: string; private connectionAccessChanged = false; private connectionAccessLoaded = false; private collapse!: () => void; + private editing!: boolean; constructor( private notificationService: NotificationService, @@ -64,8 +58,9 @@ export class UserFormController implements IInitializableController, IDestructib private dbDriverResource: DBDriverResource ) { } - init(id: string, collapse: () => void) { - this.userId = id; + init(user: AdminUserInfo, editing: boolean, collapse: () => void) { + this.user = user; + this.editing = editing; this.collapse = collapse; this.loadRoles(); } @@ -84,10 +79,9 @@ export class UserFormController implements IInitializableController, IDestructib this.isSaving = true; try { - if (this.isNew) { + if (!this.editing) { await this.usersResource.create({ userId: this.credentials.login, - newId: this.userId, credentials: { password: this.credentials.password }, roles: this.getGrantedRoles(), grantedConnections: this.getGrantedConnections(), @@ -103,12 +97,9 @@ export class UserFormController implements IInitializableController, IDestructib await this.usersResource.refresh(this.user.userId); this.notificationService.logInfo({ title: 'authentication_administration_user_updated' }); } - - this.connectionAccessLoaded = false; - await this.loadConnectionsAccess(); } catch (exception) { if (!this.error.catch(exception) || this.isDistructed) { - if (this.isNew) { + if (!this.editing) { this.notificationService.logException(exception, 'Error creating new user'); } else { this.notificationService.logException(exception, 'Error saving user'); @@ -134,12 +125,14 @@ export class UserFormController implements IInitializableController, IDestructib this.isLoading = true; try { - this.grantedConnections = await this.usersResource.loadConnections(this.userId); + if (this.editing) { + this.grantedConnections = await this.usersResource.loadConnections(this.user.userId); - this.selectedConnections.clear(); - for (const connection of this.grantedConnections) { - if (connection.subjectType !== AdminSubjectType.Role) { - this.selectedConnections.set(connection.connectionId, true); + this.selectedConnections.clear(); + for (const connection of this.grantedConnections) { + if (connection.subjectType !== AdminSubjectType.Role) { + this.selectedConnections.set(connection.connectionId, true); + } } } this.connectionAccessLoaded = true; @@ -161,12 +154,12 @@ export class UserFormController implements IInitializableController, IDestructib return; } - if (!this.credentials.password && this.isNew) { + if (!this.credentials.password && !this.editing) { this.notificationService.logError({ title: 'authentication_user_password_not_set' }); return; } - if (!this.credentials.password && this.isNew) { + if (!this.credentials.password && !this.editing) { this.notificationService.logError({ title: 'authentication_user_password_not_set' }); return; } @@ -190,7 +183,7 @@ export class UserFormController implements IInitializableController, IDestructib if (!this.user.grantedRoles.includes(roleId)) { await this.usersResource.grantRole(this.user.userId, roleId, true); } - } else if (!this.isNew) { + } else { await this.usersResource.revokeRole(this.user.userId, roleId, true); } } @@ -215,8 +208,10 @@ export class UserFormController implements IInitializableController, IDestructib if (!this.connectionAccessChanged) { return; } - await this.usersResource.setConnections(this.userId, this.getGrantedConnections()); + await this.usersResource.setConnections(this.user.userId, this.getGrantedConnections()); this.connectionAccessChanged = false; + this.connectionAccessLoaded = false; + await this.loadConnectionsAccess(); } private async loadRoles() { @@ -232,11 +227,7 @@ export class UserFormController implements IInitializableController, IDestructib private async loadUser() { try { - await this.usersResource.load(this.userId); - - if (!this.isNew) { - this.credentials.login = this.user.userId; - } + this.credentials.login = this.user.userId; this.credentials.roles = new Map(this.user.grantedRoles.map(roleId => ([roleId, true]))); } catch (exception) { this.notificationService.logException(exception, 'Can\'t load user'); diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UsersAdministrationController.ts b/webapp/packages/plugin-authentication/src/Administration/Users/UsersAdministrationController.ts index 8df7843c18..351e00765a 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/UsersAdministrationController.ts +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UsersAdministrationController.ts @@ -13,7 +13,7 @@ import { injectable } from '@cloudbeaver/core-di'; import { CommonDialogService, ConfirmationDialog } from '@cloudbeaver/core-dialogs'; import { NotificationService } from '@cloudbeaver/core-events'; import { ErrorDetailsDialog } from '@cloudbeaver/core-notifications'; -import { GQLErrorCatcher, resourceKeyList } from '@cloudbeaver/core-sdk'; +import { GQLErrorCatcher, resourceKeyList, AdminUserInfo } from '@cloudbeaver/core-sdk'; import { UsersAdministrationNavigationService } from './UsersAdministrationNavigationService'; @@ -26,10 +26,9 @@ export class UsersAdministrationController { @computed get users() { return Array.from(this.usersResource.data.values()) - .filter(user => !this.usersResource.isNew(user.userId)) .sort((a, b) => { if (this.usersResource.isNew(a.userId) === this.usersResource.isNew(b.userId)) { - return 0; + return a.userId.localeCompare(b.userId); } if (this.usersResource.isNew(a.userId)) { return -1; @@ -38,10 +37,7 @@ export class UsersAdministrationController { }); } - @computed get creatingUser() { - return Array.from(this.usersResource.data.values()) - .find(user => this.usersResource.isNew(user.userId)); - } + @observable creatingUser: AdminUserInfo | null = null; get isLoading() { return this.usersResource.isLoading() || this.isDeleting; @@ -55,7 +51,13 @@ export class UsersAdministrationController { ) { } create = () => { - this.usersResource.addNew(); + this.creatingUser = { + userId: '', + grantedRoles: [], + grantedConnections: [], + configurationParameters: {}, + metaParameters: {}, + } as AdminUserInfo; this.usersAdministrationNavigationService.navToAdd(); } @@ -64,7 +66,7 @@ export class UsersAdministrationController { return; } - this.usersResource.delete(this.creatingUser.userId); + this.creatingUser = null; this.usersAdministrationNavigationService.navToRoot(); } diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/User.tsx b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/User.tsx index 760f123893..2f0a814f10 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/User.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/User.tsx @@ -7,14 +7,11 @@ */ import { observer } from 'mobx-react'; -import styled, { css, use } from 'reshadow'; +import styled, { css } from 'reshadow'; -import { UsersResource } from '@cloudbeaver/core-authentication'; import { TableItem, TableColumnValue, TableItemSelect, TableItemExpand } from '@cloudbeaver/core-blocks'; -import { useService } from '@cloudbeaver/core-di'; -import { useTranslate } from '@cloudbeaver/core-localization'; import { AdminUserInfo } from '@cloudbeaver/core-sdk'; import { useStyles } from '@cloudbeaver/core-theming'; @@ -31,9 +28,6 @@ type Props = { } export const User = observer(function User({ user }: Props) { - const translate = useTranslate(); - const usersResource = useService(UsersResource); - const isNew = usersResource.isNew(user.userId); return styled(useStyles(styles))( @@ -43,9 +37,9 @@ export const User = observer(function User({ user }: Props) { - {isNew ? translate('authentication_administration_user_connections_user_new') : user.userId} + {user.userId} {user.grantedRoles.join(', ')} - {isNew && {translate('ui_tag_new')}} + ); }); diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEdit.tsx b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEdit.tsx index ab38706268..3146c39f2c 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEdit.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEdit.tsx @@ -8,12 +8,34 @@ import { observer } from 'mobx-react'; import { useContext, useCallback } from 'react'; +import styled, { css } from 'reshadow'; import { TableContext, } from '@cloudbeaver/core-blocks'; +import { useController } from '@cloudbeaver/core-di'; +import { composes, useStyles } from '@cloudbeaver/core-theming'; import { UserForm } from '../UserForm/UserForm'; +import { UserEditController } from './UserEditController'; + +const styles = composes( + css` + box { + composes: theme-background-secondary theme-text-on-secondary from global; + } + `, + css` + box { + box-sizing: border-box; + padding: 24px; + min-height: 320px; + max-height: 500px; + display: flex; + flex-direction: column; + } + ` +); type Props = { item: string; @@ -22,8 +44,15 @@ type Props = { export const UserEdit = observer(function UserEdit({ item, }: Props) { + const controller = useController(UserEditController, item); const tableContext = useContext(TableContext); const collapse = useCallback(() => tableContext?.setItemExpand(item, false), [tableContext]); - return ; + return styled(useStyles(styles))( + + {controller.user && ( + + )} + + ); }); diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEditController.ts b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEditController.ts new file mode 100644 index 0000000000..6210dbd87c --- /dev/null +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UserEditController.ts @@ -0,0 +1,55 @@ +/* + * cloudbeaver - Cloud Database Manager + * Copyright (C) 2020 DBeaver Corp and others + * + * Licensed under the Apache License, Version 2.0. + * you may not use this file except in compliance with the License. + */ + +import { observable, computed } from 'mobx'; + +import { UsersResource } from '@cloudbeaver/core-authentication'; +import { + injectable, IInitializableController, IDestructibleController +} from '@cloudbeaver/core-di'; +import { NotificationService } from '@cloudbeaver/core-events'; +import { GQLErrorCatcher, AdminUserInfo } from '@cloudbeaver/core-sdk'; + +@injectable() +export class UserEditController +implements IInitializableController, IDestructibleController { + @observable isLoading = true; + @observable user: AdminUserInfo | null = null; + + @computed get isDisabled() { + return this.isLoading; + } + + userId!: string; + + readonly error = new GQLErrorCatcher(); + + constructor( + private notificationService: NotificationService, + private usersResource: UsersResource, + ) { } + + init(id: string) { + this.userId = id; + this.loadUser(); + } + + destruct(): void { } + + private async loadUser() { + this.isLoading = true; + try { + // we create a copy to protect the current value from mutation + this.user = JSON.parse(JSON.stringify(await this.usersResource.load(this.userId))); + } catch (exception) { + this.notificationService.logException(exception, `Can't load user ${this.userId}`); + } finally { + this.isLoading = false; + } + } +} diff --git a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UsersTable.tsx b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UsersTable.tsx index 1cd3d78b9f..727e2b2972 100644 --- a/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UsersTable.tsx +++ b/webapp/packages/plugin-authentication/src/Administration/Users/UsersTable/UsersTable.tsx @@ -19,6 +19,9 @@ import { useStyles } from '@cloudbeaver/core-theming'; import { User } from './User'; const styles = css` + Table { + width: 100%; + } TableColumnHeader { border-top: solid 1px; } diff --git a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx index e5b4b2714a..98978f1a40 100644 --- a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx +++ b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverProperties.tsx @@ -41,7 +41,7 @@ export const DriverProperties = observer(function DriverProperties({ state, loadProperties, }: DriverPropertiesProps) { - const controller = useController(DriverPropertiesController, driver); + const controller = useController(DriverPropertiesController, driver, state); useEffect(() => { if (loadProperties) { @@ -56,7 +56,7 @@ export const DriverProperties = observer(function DriverProperties({ )} diff --git a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts index 9a1d574942..85a3ad9fc8 100644 --- a/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts +++ b/webapp/packages/plugin-connection-custom/src/CustomConnection/ConnectionFormDialog/DriverProperties/DriverPropertiesController.ts @@ -34,21 +34,24 @@ export class DriverPropertiesController implements IInitializableController { @observable driverProperties = observable([]) private loaded = false; + private state!: Record; constructor( private driverPropertiesService: DriverPropertiesService, private notificationService: NotificationService ) { } - init(driver: DBDriver) { + init(driver: DBDriver, state: Record) { this.driver = driver; + this.state = state; } - onAddProperty = () => { + addProperty = (key?: string, value?: string) => { this.driverProperties.unshift({ id: uuid(), - key: 'property', - defaultValue: '', + key: key ?? 'property', + defaultValue: value ?? '', + new: !key, }); } @@ -67,6 +70,14 @@ export class DriverPropertiesController implements IInitializableController { description: property.description, validValues: property.validValues, }))); + + for (const key of Object.keys(this.state)) { + if (this.driverProperties.some(property => property.key === key)) { + continue; + } + + this.addProperty(key, this.state[key]); + } this.loaded = true; } catch (exception) { this.notificationService.logException(exception, 'Can\'t load driver properties'); diff --git a/webapp/packages/plugin-data-spreadsheet/src/AgGridTable/AgGridTableController.ts b/webapp/packages/plugin-data-spreadsheet/src/AgGridTable/AgGridTableController.ts index 744166d363..af98a39f38 100644 --- a/webapp/packages/plugin-data-spreadsheet/src/AgGridTable/AgGridTableController.ts +++ b/webapp/packages/plugin-data-spreadsheet/src/AgGridTable/AgGridTableController.ts @@ -18,7 +18,6 @@ import { ColDef, ValueGetterParams, GridOptions, - CellEditingStoppedEvent, CellClassParams, SortChangedEvent, RowNode, @@ -74,7 +73,6 @@ export class AgGridTableController implements IInitializableController, IDestruc onGridReady: this.handleGridReady.bind(this), onBodyScroll: this.handleBodyScroll.bind(this), - onCellEditingStopped: this.handleCellEditingStopped.bind(this), onSortChanged: this.handleSortChanged.bind(this), }; @@ -163,11 +161,6 @@ export class AgGridTableController implements IInitializableController, IDestruc this.gridModel.onCellEditingStopped(rowIndex, colId, value, editing); } - private handleCellEditingStopped(event: CellEditingStoppedEvent) { - // TODO: probably no more needed because editCellValue executes on every change in editor - // this.gridModel.onCellEditingStopped(event.rowIndex, event.column.getColId(), event.value, false); - } - private isCellEdited(rowIndex: number, column: string) { return this.gridModel.isCellEdited(rowIndex, column); }