From 9975e0056b122af90122f3ffa38d988b9ab8a06e Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Wed, 1 Dec 2021 13:50:20 +0300 Subject: [PATCH 1/3] fix(plugin-user-profile): CB-1399 move change password form to user profile --- .../src/Administration/Administration.tsx | 13 +- .../src/Administration/ContentSlider.tsx | 67 ++++++++ .../core-localization/src/locales/en.ts | 1 + .../core-localization/src/locales/ru.ts | 1 + .../src/PluginBootstrap.ts | 4 +- .../src/PluginBootstrap.ts | 39 +---- .../plugin-authentication/src/UserInfo.tsx | 57 ++++--- .../ChangeUserPasswordDialog.tsx | 157 ------------------ .../ChangeUserPasswordDialogFooter.tsx | 60 ------- .../src/UserMenu/UserMenu.tsx | 49 ++++++ .../plugin-authentication/src/index.ts | 2 +- .../plugin-authentication/src/locales/en.ts | 6 - .../plugin-authentication/src/locales/it.ts | 6 - .../plugin-authentication/src/locales/ru.ts | 6 - .../Authentication/AuthenticationPanel.tsx | 34 ++++ .../Authentication/AuthenticationTab.tsx | 28 ++++ .../ChangePassword/ChangePassword.tsx | 74 +++++++++ .../ChangePassword/useChangePassword.ts | 76 +++++++++ .../src/UserProfileForm/UserForm.tsx | 7 + .../UserInfo/UserInfoPanel.tsx | 5 - .../plugin-user-profile/src/locales/en.ts | 8 + .../plugin-user-profile/src/locales/it.ts | 8 + .../plugin-user-profile/src/locales/ru.ts | 8 + 23 files changed, 410 insertions(+), 306 deletions(-) create mode 100644 webapp/packages/core-administration/src/Administration/ContentSlider.tsx delete mode 100644 webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialog.tsx delete mode 100644 webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialogFooter.tsx create mode 100644 webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx create mode 100644 webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationPanel.tsx create mode 100644 webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationTab.tsx create mode 100644 webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/ChangePassword.tsx create mode 100644 webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/useChangePassword.ts diff --git a/webapp/packages/core-administration/src/Administration/Administration.tsx b/webapp/packages/core-administration/src/Administration/Administration.tsx index c839f6f6c5..f370775733 100644 --- a/webapp/packages/core-administration/src/Administration/Administration.tsx +++ b/webapp/packages/core-administration/src/Administration/Administration.tsx @@ -16,8 +16,8 @@ import { composes, useStyles } from '@cloudbeaver/core-theming'; import { AdministrationItemService, filterOnlyActive } from '../AdministrationItem/AdministrationItemService'; import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute'; +import { ContentSlider } from './ContentSlider'; import { DrawerItem } from './DrawerItem'; -import { ItemContent } from './ItemContent'; const tabsStyles = composes( css` @@ -53,7 +53,7 @@ const administrationStyles = composes( padding-top: 16px; border-right: 2px solid; } - content { + content-container { flex: 1; display: flex; flex-direction: column; @@ -95,13 +95,10 @@ export const Administration = observer(function Administration({ /> ))} - + {children} - - + + ); diff --git a/webapp/packages/core-administration/src/Administration/ContentSlider.tsx b/webapp/packages/core-administration/src/Administration/ContentSlider.tsx new file mode 100644 index 0000000000..98cb10b690 --- /dev/null +++ b/webapp/packages/core-administration/src/Administration/ContentSlider.tsx @@ -0,0 +1,67 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; +import styled, { css } from 'reshadow'; + +import { SlideBox, SlideElement, slideBoxStyles, SlideOverlay, ErrorBoundary } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import { useStyles } from '@cloudbeaver/core-theming'; +import { OptionsPanelService } from '@cloudbeaver/core-ui'; + +import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute'; +import { ItemContent } from './ItemContent'; + +const styles = css` + SlideBox { + flex: 1; + } + SlideElement { + flex: 1; + } + content { + height: 100%; + display: flex; + flex-direction: column; + overflow: auto; + white-space: normal; + } +`; + +interface Props { + activeScreen: IAdministrationItemRoute | null; + configurationWizard: boolean; +} + +export const ContentSlider = observer(function ContentSlider({ activeScreen, configurationWizard }) { + const optionsPanelService = useService(OptionsPanelService); + const OptionsPanel = optionsPanelService.getPanelComponent(); + + return styled(useStyles(styles, slideBoxStyles))( + + + + + + + + + + + + + + + optionsPanelService.close()} /> + + + ); +}); diff --git a/webapp/packages/core-localization/src/locales/en.ts b/webapp/packages/core-localization/src/locales/en.ts index 4e7ee22d01..9bfd3c316f 100644 --- a/webapp/packages/core-localization/src/locales/en.ts +++ b/webapp/packages/core-localization/src/locales/en.ts @@ -58,6 +58,7 @@ export const defaultENLocale = [ ['ui_file', 'File'], ['ui_changes_will_be_lost', 'Your changes will be lost'], ['ui_continue', 'Continue'], + ['ui_authentication', 'Authentication'], ['root_permission_denied', "You don't have permissions"], ['root_permission_no_permission', "You don't have permission for this action"], diff --git a/webapp/packages/core-localization/src/locales/ru.ts b/webapp/packages/core-localization/src/locales/ru.ts index f4c2c88c9a..b7e51f9fd6 100644 --- a/webapp/packages/core-localization/src/locales/ru.ts +++ b/webapp/packages/core-localization/src/locales/ru.ts @@ -56,6 +56,7 @@ export const defaultRULocale = [ ['ui_file', 'Файл'], ['ui_changes_will_be_lost', 'Ваши изменения будут потеряны'], ['ui_continue', 'Продолжить'], + ['ui_authentication', 'Аутентификация'], ['root_permission_denied', 'Отказано в доступе'], ['root_permission_no_permission', 'У вас нет разрешения на это действие'], diff --git a/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts b/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts index 8b15e21dcf..b944805ff2 100644 --- a/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts @@ -9,7 +9,7 @@ import { AdministrationTopAppBarService } from '@cloudbeaver/core-administration'; import { injectable, Bootstrap } from '@cloudbeaver/core-di'; import { ServerConfigurationService } from '@cloudbeaver/plugin-administration'; -import { UserInfo } from '@cloudbeaver/plugin-authentication'; +import { UserMenu } from '@cloudbeaver/plugin-authentication'; import { AuthenticationProviders } from './Administration/ServerConfiguration/AuthenticationProviders'; @@ -23,7 +23,7 @@ export class PluginBootstrap extends Bootstrap { } register(): void { - this.administrationTopAppBarService.placeholder.add(UserInfo, 4); + this.administrationTopAppBarService.placeholder.add(UserMenu, 4); this.serverConfigurationService.configurationContainer.add(AuthenticationProviders, 0); } diff --git a/webapp/packages/plugin-authentication/src/PluginBootstrap.ts b/webapp/packages/plugin-authentication/src/PluginBootstrap.ts index da05abce04..8c0a168cc7 100644 --- a/webapp/packages/plugin-authentication/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-authentication/src/PluginBootstrap.ts @@ -7,49 +7,28 @@ */ import { SettingsMenuService, TopNavService } from '@cloudbeaver/core-app'; -import { AuthInfoService, AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication'; +import { AuthInfoService } from '@cloudbeaver/core-authentication'; import { injectable, Bootstrap } from '@cloudbeaver/core-di'; -import { CommonDialogService } from '@cloudbeaver/core-dialogs'; import { ServerConfigResource } from '@cloudbeaver/core-root'; -import { DATA_CONTEXT_MENU, DATA_CONTEXT_MENU_NESTED, MenuBaseItem, MenuService } from '@cloudbeaver/core-view'; import { AuthenticationService } from './AuthenticationService'; import { AuthDialogService } from './Dialog/AuthDialogService'; -import { UserInfo } from './UserInfo'; -import { ChangeUserPasswordDialog } from './UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialog'; -import { MENU_USER_PROFILE } from './UserMenu/MENU_USER_PROFILE'; +import { UserMenu } from './UserMenu/UserMenu'; @injectable() export class PluginBootstrap extends Bootstrap { constructor( - private serverConfigResource: ServerConfigResource, - private authDialogService: AuthDialogService, - private authenticationService: AuthenticationService, - private authInfoService: AuthInfoService, - private settingsMenuService: SettingsMenuService, - private topNavService: TopNavService, - private menuService: MenuService, - private readonly commonDialogService: CommonDialogService, + private readonly serverConfigResource: ServerConfigResource, + private readonly authDialogService: AuthDialogService, + private readonly authenticationService: AuthenticationService, + private readonly authInfoService: AuthInfoService, + private readonly settingsMenuService: SettingsMenuService, + private readonly topNavService: TopNavService, ) { super(); } register(): void { - this.menuService.addCreator({ - isApplicable: - context => { - const profile = context.get(DATA_CONTEXT_MENU) === MENU_USER_PROFILE - && !context.get(DATA_CONTEXT_MENU_NESTED); - - return profile && !!this.authInfoService.userInfo?.linkedAuthProviders.includes(AUTH_PROVIDER_LOCAL_ID); - }, - getItems: (context, items) => [ - ...items, - new MenuBaseItem('change-password', 'authentication_user_password_change_menu_title', undefined, { - onSelect: () => this.commonDialogService.open(ChangeUserPasswordDialog, null), - }), - ], - }); this.settingsMenuService.addMenuItem( SettingsMenuService.settingsMenuToken, { @@ -71,7 +50,7 @@ export class PluginBootstrap extends Bootstrap { onClick: this.authenticationService.logout.bind(this.authenticationService), } ); - this.topNavService.placeholder.add(UserInfo, 4); + this.topNavService.placeholder.add(UserMenu, 4); } load(): void | Promise { } diff --git a/webapp/packages/plugin-authentication/src/UserInfo.tsx b/webapp/packages/plugin-authentication/src/UserInfo.tsx index fc36a9633f..07faf5d199 100644 --- a/webapp/packages/plugin-authentication/src/UserInfo.tsx +++ b/webapp/packages/plugin-authentication/src/UserInfo.tsx @@ -9,16 +9,10 @@ import { observer } from 'mobx-react-lite'; import styled, { css } from 'reshadow'; -import { topMenuStyles } from '@cloudbeaver/core-app'; -import { AuthInfoService, DATA_CONTEXT_USER } from '@cloudbeaver/core-authentication'; import { IconOrImage } from '@cloudbeaver/core-blocks'; -import { useService } from '@cloudbeaver/core-di'; -import { useStyles } from '@cloudbeaver/core-theming'; -import { ContextMenu } from '@cloudbeaver/core-ui'; -import { useMenu } from '@cloudbeaver/core-view'; - -import { MENU_USER_PROFILE } from './UserMenu/MENU_USER_PROFILE'; -import { userMenuStyles } from './UserMenu/userMenuStyles'; +import { useTranslate } from '@cloudbeaver/core-localization'; +import type { UserInfo as IUserInfo } from '@cloudbeaver/core-sdk'; +import { composes, useStyles } from '@cloudbeaver/core-theming'; const styles = css` user { @@ -37,24 +31,37 @@ const styles = css` } `; -export const UserInfo = observer(function UserInfo() { - const authInfoService = useService(AuthInfoService); - const style = useStyles(styles, userMenuStyles); - const menu = useMenu(MENU_USER_PROFILE); - menu.context.set(DATA_CONTEXT_USER, authInfoService.userInfo); +const detachedStyles = composes( + css` + user { + composes: theme-ripple from global; + } + `, + css` + user { + padding: 0 16px; + cursor: pointer; + } + ` +); - if (!authInfoService.userInfo) { - return null; - } +interface Props { + info: IUserInfo; + detached?: boolean; + tooltip?: string; + onClick?: () => void; +} + +export const UserInfo = observer(function UserInfo({ info, detached, tooltip, onClick }) { + const translate = useTranslate(); + const style = useStyles(styles, detached && detachedStyles); return styled(style)( - - - - - - {authInfoService.userInfo.displayName || authInfoService.userInfo.userId} - - + + + + + {info.displayName || info.userId} + ); }); diff --git a/webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialog.tsx b/webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialog.tsx deleted file mode 100644 index 4858f2b075..0000000000 --- a/webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialog.tsx +++ /dev/null @@ -1,157 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2021 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 } from 'mobx'; -import { observer } from 'mobx-react-lite'; -import { useState, useCallback, useEffect } from 'react'; -import styled, { css } from 'reshadow'; - -import { UsersResource } from '@cloudbeaver/core-authentication'; -import { BASE_CONTAINERS_STYLES, Container, ErrorMessage, Group, InputField, SubmittingForm, useErrorDetails, useFocus } from '@cloudbeaver/core-blocks'; -import { useService } from '@cloudbeaver/core-di'; -import { CommonDialogWrapper, DialogComponent, DialogComponentProps } from '@cloudbeaver/core-dialogs'; -import { NotificationService } from '@cloudbeaver/core-events'; -import { useTranslate } from '@cloudbeaver/core-localization'; -import { GQLErrorCatcher } from '@cloudbeaver/core-sdk'; -import { composes, useStyles } from '@cloudbeaver/core-theming'; - -import { ChangeUserPasswordDialogFooter } from './ChangeUserPasswordDialogFooter'; - -const styles = composes( - css` - ErrorMessage { - composes: theme-background-secondary theme-text-on-secondary from global; - } -`, - css` - ErrorMessage { - flex: 1; - } -`); - -interface IState { - oldPassword: string; - newPassword: string; - repeatedPassword: string; - error: GQLErrorCatcher; - customError: string; - submitting: boolean; -} - -export const ChangeUserPasswordDialog: DialogComponent = observer( - function ChangeUserPasswordDialog({ - payload, - resolveDialog, - rejectDialog, - }: DialogComponentProps) { - const [focusedRef] = useFocus({}); - const usersResource = useService(UsersResource); - const notificationService = useService(NotificationService); - const style = useStyles(styles, BASE_CONTAINERS_STYLES); - const translate = useTranslate(); - const [state] = useState(() => observable({ - oldPassword: '', newPassword: '', repeatedPassword: '', error: new GQLErrorCatcher(), customError: '', submitting: false, - })); - const { details: errorDetails, open: openErrorDetails } = useErrorDetails(state.error.exception); - const formFilled - = state.newPassword.length > 0 && state.repeatedPassword.length > 0 && state.oldPassword.length > 0; - const disabled = state.submitting; - - const change = useCallback(async () => { - state.error.clear(); - - if (state.newPassword !== state.repeatedPassword) { - state.customError = 'authentication_user_passwords_not_match'; - return; - } - - try { - state.submitting = true; - await usersResource.updateLocalPassword(state.oldPassword, state.newPassword); - notificationService.logSuccess({ title: 'authentication_user_password_change_success' }); - resolveDialog(); - } catch (exeption) { - if (!state.error.catch(exeption)) { - state.customError = exeption.message; - } - } finally { - state.submitting = false; - } - }, [state, resolveDialog, notificationService, usersResource]); - - useEffect(() => { - focusedRef.current?.focus(); - }, []); - - return styled(style)( - - {(state.error.responseMessage || state.customError) && ( - - )} - - )} - onReject={rejectDialog} - > - - - - value.trim())} - small - required - > - {translate('authentication_user_current_password')} - - value.trim())} - small - required - > - {translate('authentication_user_new_password')} - - value.trim())} - small - required - > - {translate('authentication_user_password_repeat')} - - - - - - ); - } -); diff --git a/webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialogFooter.tsx b/webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialogFooter.tsx deleted file mode 100644 index 497d4b5209..0000000000 --- a/webapp/packages/plugin-authentication/src/UserMenu/ChangeUserPasswordDialog/ChangeUserPasswordDialogFooter.tsx +++ /dev/null @@ -1,60 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; -import styled, { css } from 'reshadow'; - -import { Button } from '@cloudbeaver/core-blocks'; -import { useTranslate } from '@cloudbeaver/core-localization'; - -const styles = css` - footer-container { - display: flex; - width: min-content; - flex: 1; - align-items: center; - justify-content: flex-end; - } - footer-container > :not(:first-child) { - margin-left: 16px; - } - Button { - flex: 0 0 auto; - } -`; - -export interface Props { - onChange: () => void; - onCancel: () => void; - formFilled: boolean; - submitting: boolean; - className?: string; -} - -export const ChangeUserPasswordDialogFooter = observer(function ChangeUserPasswordDialogFooter({ - onChange, - onCancel, - formFilled, - submitting, - className, - children, -}) { - const translate = useTranslate(); - - return styled(styles)( - - {children} - - - - ); -}); diff --git a/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx b/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx new file mode 100644 index 0000000000..b5dcb5953b --- /dev/null +++ b/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx @@ -0,0 +1,49 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; +import styled from 'reshadow'; + +import { topMenuStyles } from '@cloudbeaver/core-app'; +import { AuthInfoService, DATA_CONTEXT_USER } from '@cloudbeaver/core-authentication'; +import { useService } from '@cloudbeaver/core-di'; +import { useStyles } from '@cloudbeaver/core-theming'; +import { ContextMenu } from '@cloudbeaver/core-ui'; +import { MenuBaseItem, useMenu } from '@cloudbeaver/core-view'; + +import { UserInfo } from '../UserInfo'; +import { MENU_USER_PROFILE } from './MENU_USER_PROFILE'; +import { userMenuStyles } from './userMenuStyles'; + +export const UserMenu = observer(function UserMenu() { + const style = useStyles(userMenuStyles); + const authInfoService = useService(AuthInfoService); + const menu = useMenu(MENU_USER_PROFILE); + + if (!authInfoService.userInfo) { + return null; + } + + const items = menu.getItems().filter(item => !item.hidden); + + if (items.length === 1 && items[0] instanceof MenuBaseItem) { + const item = items[0]; + + if (item.events?.onSelect) { + return ; + } + } + + menu.context.set(DATA_CONTEXT_USER, authInfoService.userInfo); + + return styled(style)( + + + + ); +}); diff --git a/webapp/packages/plugin-authentication/src/index.ts b/webapp/packages/plugin-authentication/src/index.ts index 6021388650..df66f5b51e 100644 --- a/webapp/packages/plugin-authentication/src/index.ts +++ b/webapp/packages/plugin-authentication/src/index.ts @@ -5,4 +5,4 @@ export default manifest; export * from './AuthenticationService'; export * from './Dialog/AuthDialogService'; export * from './UserMenu/MENU_USER_PROFILE'; -export * from './UserInfo'; +export * from './UserMenu/UserMenu'; diff --git a/webapp/packages/plugin-authentication/src/locales/en.ts b/webapp/packages/plugin-authentication/src/locales/en.ts index 6c57594053..aa6ace1219 100644 --- a/webapp/packages/plugin-authentication/src/locales/en.ts +++ b/webapp/packages/plugin-authentication/src/locales/en.ts @@ -14,8 +14,6 @@ export default [ ['authentication_user_name', 'User name'], ['authentication_user_credentials', 'Credentials'], ['authentication_user_meta_parameters', 'Parameters'], - ['authentication_user_current_password', 'Current password'], - ['authentication_user_new_password', 'New password'], ['authentication_user_password', 'User password'], ['authentication_user_password_repeat', 'Repeat password'], ['authentication_user_role', 'User role'], @@ -25,10 +23,6 @@ export default [ ['authentication_user_passwords_not_match', "Passwords don't match"], ['authentication_user_login_already_exists', 'The user with this name already exists'], ['authentication_user_login_cant_be_used', 'Sorry, that name cannot be used'], - ['authentication_user_password_change_dialog_title', 'Change password (local)'], - ['authentication_user_password_change_menu_title', 'Change local password'], - ['authentication_user_password_change_success', 'Password was changed successfully'], - ['authentication_user_password_change_dialog_submit', 'Change'], ['authentication_user_icon_tooltip', 'User'], ['authentication_role_icon_tooltip', 'Role'], ]; diff --git a/webapp/packages/plugin-authentication/src/locales/it.ts b/webapp/packages/plugin-authentication/src/locales/it.ts index d71ad04365..afba380f87 100644 --- a/webapp/packages/plugin-authentication/src/locales/it.ts +++ b/webapp/packages/plugin-authentication/src/locales/it.ts @@ -8,8 +8,6 @@ export default [ ['authentication_user_name', 'Nome utente'], ['authentication_user_credentials', 'Credenziali'], ['authentication_user_meta_parameters', 'Parameters'], - ['authentication_user_current_password', 'Password corrente'], - ['authentication_user_new_password', 'Nuova password'], ['authentication_user_password', 'Password utente'], ['authentication_user_password_repeat', 'Ripeti password'], ['authentication_user_role', 'Ruolo utente'], @@ -19,10 +17,6 @@ export default [ ['authentication_user_passwords_not_match', 'Le Passwords non coincidono'], ['authentication_user_login_already_exists', 'Esiste già un utente con quel nome'], ['authentication_user_login_cant_be_used', 'Quel nome non può essere usato'], - ['authentication_user_password_change_dialog_title', 'Modifica la password (locale)'], - ['authentication_user_password_change_menu_title', 'Modifica la password locale'], - ['authentication_user_password_change_success', 'Password modificata con successo'], - ['authentication_user_password_change_dialog_submit', 'Modifica'], ['authentication_user_icon_tooltip', 'Utente'], ['authentication_role_icon_tooltip', 'Ruolo'], ]; diff --git a/webapp/packages/plugin-authentication/src/locales/ru.ts b/webapp/packages/plugin-authentication/src/locales/ru.ts index 497a054823..739eeef0b5 100644 --- a/webapp/packages/plugin-authentication/src/locales/ru.ts +++ b/webapp/packages/plugin-authentication/src/locales/ru.ts @@ -14,8 +14,6 @@ export default [ ['authentication_user_name', 'Имя'], ['authentication_user_credentials', 'Учетные данные'], ['authentication_user_meta_parameters', 'Параметры'], - ['authentication_user_current_password', 'Текущий пароль'], - ['authentication_user_new_password', 'Новый пароль'], ['authentication_user_password', 'Пароль'], ['authentication_user_password_repeat', 'Повторите пароль'], ['authentication_user_role', 'Роль'], @@ -25,10 +23,6 @@ export default [ ['authentication_user_passwords_not_match', 'Пароли не совпадают'], ['authentication_user_login_already_exists', 'Пользователь с таким именем уже существует'], ['authentication_user_login_cant_be_used', 'Данное имя нельзя использовать'], - ['authentication_user_password_change_dialog_title', 'Изменение пароля (локального)'], - ['authentication_user_password_change_menu_title', 'Изменить локальный пароль'], - ['authentication_user_password_change_success', 'Пароль успешно изменен'], - ['authentication_user_password_change_dialog_submit', 'Изменить'], ['authentication_user_icon_tooltip', 'Пользователь'], ['authentication_role_icon_tooltip', 'Роль'], ]; diff --git a/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationPanel.tsx b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationPanel.tsx new file mode 100644 index 0000000000..48d873063e --- /dev/null +++ b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationPanel.tsx @@ -0,0 +1,34 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; +import styled from 'reshadow'; + +import { BASE_CONTAINERS_STYLES, ColoredContainer, Container, TabPanel } from '@cloudbeaver/core-blocks'; +import { ComponentStyle, useStyles } from '@cloudbeaver/core-theming'; + +import { ChangePassword } from './ChangePassword/ChangePassword'; + +interface Props { + className?: string; + style?: ComponentStyle; +} + +export const AuthenticationPanel = observer(function AuthenticationPanel({ className, style }) { + const styles = useStyles(style, BASE_CONTAINERS_STYLES); + + return styled(styles)( + + + + + + + + ); +}); diff --git a/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationTab.tsx b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationTab.tsx new file mode 100644 index 0000000000..ab63b0bca0 --- /dev/null +++ b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/AuthenticationTab.tsx @@ -0,0 +1,28 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; +import styled from 'reshadow'; + +import { Tab } from '@cloudbeaver/core-blocks'; +import { useTranslate } from '@cloudbeaver/core-localization'; +import { ComponentStyle, useStyles } from '@cloudbeaver/core-theming'; + +interface Props { + className?: string; + style?: ComponentStyle; +} + +export const AuthenticationTab = observer(function AuthenticationTab({ className, style }) { + const translate = useTranslate(); + const styles = useStyles(style); + + return styled(styles)( + {translate('ui_authentication')} + ); +}); diff --git a/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/ChangePassword.tsx b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/ChangePassword.tsx new file mode 100644 index 0000000000..a7481cbff2 --- /dev/null +++ b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/ChangePassword.tsx @@ -0,0 +1,74 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; +import styled from 'reshadow'; + +import { BASE_CONTAINERS_STYLES, Button, Group, GroupItem, GroupTitle, InputField } from '@cloudbeaver/core-blocks'; +import { useTranslate } from '@cloudbeaver/core-localization'; +import { useStyles } from '@cloudbeaver/core-theming'; + +import { useChangePassword } from './useChangePassword'; + +export const ChangePassword = observer(function ChangePassword() { + const translate = useTranslate(); + const state = useChangePassword(); + + const disabled = state.submitting; + + return styled(useStyles(BASE_CONTAINERS_STYLES))( + + {translate('plugin_user_profile_authentication_change_password')} + value.trim())} + small + required + > + {translate('plugin_user_profile_authentication_change_password_current_password')} + + value.trim())} + small + required + > + {translate('plugin_user_profile_authentication_change_password_new_password')} + + value.trim())} + small + required + > + {translate('plugin_user_profile_authentication_change_password_repeat_password')} + + + + + + ); +}); diff --git a/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/useChangePassword.ts b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/useChangePassword.ts new file mode 100644 index 0000000000..713d839891 --- /dev/null +++ b/webapp/packages/plugin-user-profile/src/UserProfileForm/Authentication/ChangePassword/useChangePassword.ts @@ -0,0 +1,76 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { action, computed, observable } from 'mobx'; + +import { UsersResource } from '@cloudbeaver/core-authentication'; +import { useObservableRef } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; +import { NotificationService } from '@cloudbeaver/core-events'; + +interface IConfig { + oldPassword: string; + password: string; + repeatedPassword: string; +} + +interface IState { + config: IConfig; + submitting: boolean; + readonly formFilled: boolean; + changePassword: () => Promise; + resetConfig: () => void; +} + +export function useChangePassword(): IState { + const usersResource = useService(UsersResource); + const notificationService = useService(NotificationService); + + return useObservableRef(() => ({ + config: { + oldPassword: '', + password: '', + repeatedPassword: '', + }, + submitting: false, + get formFilled() { + return this.config.password.length > 0 && this.config.oldPassword.length > 0 + && this.config.repeatedPassword.length > 0; + }, + async changePassword() { + if (this.config.password !== this.config.repeatedPassword) { + this.notificationService.logError({ title: 'plugin_user_profile_authentication_change_password_passwords_not_match' }); + return; + } + + try { + this.submitting = true; + await usersResource.updateLocalPassword(this.config.oldPassword, this.config.password); + notificationService.logSuccess({ title: 'plugin_user_profile_authentication_change_password_success' }); + this.resetConfig(); + } catch (exception) { + notificationService.logException(exception); + } finally { + this.submitting = false; + } + }, + resetConfig() { + this.config.oldPassword = ''; + this.config.password = ''; + this.config.repeatedPassword = ''; + }, + }), + { + config: observable, + submitting: observable.ref, + formFilled: computed, + changePassword: action.bound, + resetConfig: action, + }, + { usersResource, notificationService }); +} diff --git a/webapp/packages/plugin-user-profile/src/UserProfileForm/UserForm.tsx b/webapp/packages/plugin-user-profile/src/UserProfileForm/UserForm.tsx index ac92b1addd..961d1b1a94 100644 --- a/webapp/packages/plugin-user-profile/src/UserProfileForm/UserForm.tsx +++ b/webapp/packages/plugin-user-profile/src/UserProfileForm/UserForm.tsx @@ -9,11 +9,14 @@ import { observer } from 'mobx-react-lite'; import styled, { css } from 'reshadow'; +import { AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication'; import { BASE_CONTAINERS_STYLES, Button, IconOrImage, TabList, TabsState, UNDERLINE_TAB_STYLES } from '@cloudbeaver/core-blocks'; import { useTranslate } from '@cloudbeaver/core-localization'; import type { UserInfo } from '@cloudbeaver/core-sdk'; import { composes, useStyles } from '@cloudbeaver/core-theming'; +import { AuthenticationPanel } from './Authentication/AuthenticationPanel'; +import { AuthenticationTab } from './Authentication/AuthenticationTab'; import type { IUserProfileFormState } from './IUserProfileFormState'; import { UserInfoPanel } from './UserInfo/UserInfoPanel'; import { UserInfoTab } from './UserInfo/UserInfoTab'; @@ -126,6 +129,8 @@ export const UserForm = observer(function UserForm({ const style = [tabsStyles, UNDERLINE_TAB_STYLES]; const styles = useStyles(style, BASE_CONTAINERS_STYLES, topBarStyles, formStyles); + const localProvider = user.linkedAuthProviders.includes(AUTH_PROVIDER_LOCAL_ID); + return styled(styles)( @@ -141,6 +146,7 @@ export const UserForm = observer(function UserForm({ + {localProvider && } {/* */} @@ -158,6 +164,7 @@ export const UserForm = observer(function UserForm({ + {localProvider && } {/* */} diff --git a/webapp/packages/plugin-user-profile/src/UserProfileForm/UserInfo/UserInfoPanel.tsx b/webapp/packages/plugin-user-profile/src/UserProfileForm/UserInfo/UserInfoPanel.tsx index 6ee13b9790..faf97d0b0d 100644 --- a/webapp/packages/plugin-user-profile/src/UserProfileForm/UserInfo/UserInfoPanel.tsx +++ b/webapp/packages/plugin-user-profile/src/UserProfileForm/UserInfo/UserInfoPanel.tsx @@ -45,7 +45,6 @@ export const UserInfoPanel = observer(function UserInfoPanel({ minLength={1} state={user} mod='surface' - disabled readOnly required tiny @@ -59,7 +58,6 @@ export const UserInfoPanel = observer(function UserInfoPanel({ minLength={1} state={user} mod='surface' - disabled readOnly required tiny @@ -68,14 +66,12 @@ export const UserInfoPanel = observer(function UserInfoPanel({ {translate('plugin_user_profile_info_displayName')} - {() => userMetaParameters.data.length > 0 && styled(styles)( (function UserInfoPanel({ - ); diff --git a/webapp/packages/plugin-user-profile/src/locales/en.ts b/webapp/packages/plugin-user-profile/src/locales/en.ts index d424952bac..0317590459 100644 --- a/webapp/packages/plugin-user-profile/src/locales/en.ts +++ b/webapp/packages/plugin-user-profile/src/locales/en.ts @@ -7,4 +7,12 @@ export default [ ['plugin_user_profile_info', 'User Info'], ['plugin_user_profile_auth_tokens', 'Auth Tokens'], ['plugin_user_profile_auth_providers_active', 'Authentication is active'], + + ['plugin_user_profile_authentication_change_password', 'Change password'], + ['plugin_user_profile_authentication_change_password_current_password', 'Current password'], + ['plugin_user_profile_authentication_change_password_new_password', 'New password'], + ['plugin_user_profile_authentication_change_password_repeat_password', 'Repeat password'], + ['plugin_user_profile_authentication_change_password_success', 'Password was changed successfully'], + ['plugin_user_profile_authentication_change_password_submit_label', 'Change'], + ['plugin_user_profile_authentication_change_password_passwords_not_match', "Passwords don't match"], ]; diff --git a/webapp/packages/plugin-user-profile/src/locales/it.ts b/webapp/packages/plugin-user-profile/src/locales/it.ts index d424952bac..b6e2d18a81 100644 --- a/webapp/packages/plugin-user-profile/src/locales/it.ts +++ b/webapp/packages/plugin-user-profile/src/locales/it.ts @@ -7,4 +7,12 @@ export default [ ['plugin_user_profile_info', 'User Info'], ['plugin_user_profile_auth_tokens', 'Auth Tokens'], ['plugin_user_profile_auth_providers_active', 'Authentication is active'], + + ['plugin_user_profile_authentication_change_password', 'Modifica la password'], + ['plugin_user_profile_authentication_change_password_current_password', 'Password corrente'], + ['plugin_user_profile_authentication_change_password_new_password', 'Nuova password'], + ['plugin_user_profile_authentication_change_password_repeat_password', 'Ripeti password'], + ['plugin_user_profile_authentication_change_password_success', 'Password modificata con successo'], + ['plugin_user_profile_authentication_change_password_submit_label', 'Modifica'], + ['plugin_user_profile_authentication_change_password_passwords_not_match', 'Le Passwords non coincidono'], ]; diff --git a/webapp/packages/plugin-user-profile/src/locales/ru.ts b/webapp/packages/plugin-user-profile/src/locales/ru.ts index 085f4b2ef7..e6c3f0dce4 100644 --- a/webapp/packages/plugin-user-profile/src/locales/ru.ts +++ b/webapp/packages/plugin-user-profile/src/locales/ru.ts @@ -7,4 +7,12 @@ export default [ ['plugin_user_profile_info', 'Информация'], ['plugin_user_profile_auth_tokens', 'Токены авторизации'], ['plugin_user_profile_auth_providers_active', 'Авторизация выполнена'], + + ['plugin_user_profile_authentication_change_password', 'Смена пароля'], + ['plugin_user_profile_authentication_change_password_current_password', 'Текущий пароль'], + ['plugin_user_profile_authentication_change_password_new_password', 'Новый пароль'], + ['plugin_user_profile_authentication_change_password_repeat_password', 'Повторите пароль'], + ['plugin_user_profile_authentication_change_password_success', 'Пароль был успешно изменен'], + ['plugin_user_profile_authentication_change_password_submit_label', 'Сменить'], + ['plugin_user_profile_authentication_change_password_passwords_not_match', 'Пароли не совпадают'], ]; From b83db215f2c008ac9130bafe8cdfa5c287885f39 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Wed, 1 Dec 2021 18:20:02 +0300 Subject: [PATCH 2/3] fix(plugin-user-profile): CB-1399 style changes --- .../src/Administration/Administration.tsx | 37 ++++++++-- .../src/Administration/ContentSlider.tsx | 67 ------------------- .../core-app/src/AppScreen/RightArea.tsx | 3 - .../core-blocks/src/Slide/SlideBoxStyles.ts | 1 + .../plugin-authentication/src/UserInfo.tsx | 46 ++++++------- .../src/UserMenu/UserMenu.tsx | 24 +++---- .../src/UserMenu/userMenuStyles.ts | 8 ++- .../src/PluginBootstrap.ts | 39 ----------- .../src/UserProfileService.ts | 4 +- .../packages/plugin-user-profile/src/index.ts | 2 + .../plugin-user-profile/src/manifest.ts | 2 - 11 files changed, 74 insertions(+), 159 deletions(-) delete mode 100644 webapp/packages/core-administration/src/Administration/ContentSlider.tsx delete mode 100644 webapp/packages/plugin-user-profile/src/PluginBootstrap.ts diff --git a/webapp/packages/core-administration/src/Administration/Administration.tsx b/webapp/packages/core-administration/src/Administration/Administration.tsx index f370775733..7617b610ef 100644 --- a/webapp/packages/core-administration/src/Administration/Administration.tsx +++ b/webapp/packages/core-administration/src/Administration/Administration.tsx @@ -10,14 +10,15 @@ import { observer } from 'mobx-react-lite'; import { useLayoutEffect, useRef } from 'react'; import styled, { css } from 'reshadow'; -import { TabsState, TabList, verticalTabStyles } from '@cloudbeaver/core-blocks'; +import { TabsState, TabList, verticalTabStyles, SlideBox, SlideElement, ErrorBoundary, SlideOverlay, slideBoxStyles } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { composes, useStyles } from '@cloudbeaver/core-theming'; +import { OptionsPanelService } from '@cloudbeaver/core-ui'; import { AdministrationItemService, filterOnlyActive } from '../AdministrationItem/AdministrationItemService'; import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute'; -import { ContentSlider } from './ContentSlider'; import { DrawerItem } from './DrawerItem'; +import { ItemContent } from './ItemContent'; const tabsStyles = composes( css` @@ -59,6 +60,15 @@ const administrationStyles = composes( flex-direction: column; overflow: auto; } + SlideBox { + flex: 1; + } + content { + height: 100%; + display: flex; + flex-direction: column; + overflow: auto; + } ` ); @@ -73,6 +83,9 @@ export const Administration = observer(function Administration({ }) { const contentRef = useRef(null); const administrationItemService = useService(AdministrationItemService); + const optionsPanelService = useService(OptionsPanelService); + + const OptionsPanel = optionsPanelService.getPanelComponent(); const items = administrationItemService.getActiveItems(configurationWizard); const hasOnlyActive = items.some(filterOnlyActive(configurationWizard)); @@ -80,7 +93,7 @@ export const Administration = observer(function Administration({ contentRef.current?.scrollTo({ top: 0, left: 0 }); }, [activeScreen?.item]); - return styled(useStyles(verticalTabStyles, administrationStyles, tabsStyles))( + return styled(useStyles(verticalTabStyles, administrationStyles, tabsStyles, slideBoxStyles))( @@ -97,7 +110,23 @@ export const Administration = observer(function Administration({ {children} - + + + + + + + + + + + + + + + optionsPanelService.close()} /> + + diff --git a/webapp/packages/core-administration/src/Administration/ContentSlider.tsx b/webapp/packages/core-administration/src/Administration/ContentSlider.tsx deleted file mode 100644 index 98cb10b690..0000000000 --- a/webapp/packages/core-administration/src/Administration/ContentSlider.tsx +++ /dev/null @@ -1,67 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2021 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 { observer } from 'mobx-react-lite'; -import styled, { css } from 'reshadow'; - -import { SlideBox, SlideElement, slideBoxStyles, SlideOverlay, ErrorBoundary } from '@cloudbeaver/core-blocks'; -import { useService } from '@cloudbeaver/core-di'; -import { useStyles } from '@cloudbeaver/core-theming'; -import { OptionsPanelService } from '@cloudbeaver/core-ui'; - -import type { IAdministrationItemRoute } from '../AdministrationItem/IAdministrationItemRoute'; -import { ItemContent } from './ItemContent'; - -const styles = css` - SlideBox { - flex: 1; - } - SlideElement { - flex: 1; - } - content { - height: 100%; - display: flex; - flex-direction: column; - overflow: auto; - white-space: normal; - } -`; - -interface Props { - activeScreen: IAdministrationItemRoute | null; - configurationWizard: boolean; -} - -export const ContentSlider = observer(function ContentSlider({ activeScreen, configurationWizard }) { - const optionsPanelService = useService(OptionsPanelService); - const OptionsPanel = optionsPanelService.getPanelComponent(); - - return styled(useStyles(styles, slideBoxStyles))( - - - - - - - - - - - - - - - optionsPanelService.close()} /> - - - ); -}); diff --git a/webapp/packages/core-app/src/AppScreen/RightArea.tsx b/webapp/packages/core-app/src/AppScreen/RightArea.tsx index 11e679dac2..c9ebc2e803 100644 --- a/webapp/packages/core-app/src/AppScreen/RightArea.tsx +++ b/webapp/packages/core-app/src/AppScreen/RightArea.tsx @@ -47,9 +47,6 @@ const styles = composes( SlideBox { flex: 1; } - SlideElement { - display: flex; - } ` ); diff --git a/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts b/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts index 7084e21600..ded44bb8b6 100644 --- a/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts +++ b/webapp/packages/core-blocks/src/Slide/SlideBoxStyles.ts @@ -22,6 +22,7 @@ export const slideBoxStyles = composes( height: 100%; display: inline-block; vertical-align:top; + white-space: normal; transition: transform cubic-bezier(0.4, 0.0, 0.2, 1) 0.6s; transform: translateX(calc(-100% + 120px)); diff --git a/webapp/packages/plugin-authentication/src/UserInfo.tsx b/webapp/packages/plugin-authentication/src/UserInfo.tsx index 07faf5d199..4c7482d471 100644 --- a/webapp/packages/plugin-authentication/src/UserInfo.tsx +++ b/webapp/packages/plugin-authentication/src/UserInfo.tsx @@ -10,28 +10,13 @@ import { observer } from 'mobx-react-lite'; import styled, { css } from 'reshadow'; import { IconOrImage } from '@cloudbeaver/core-blocks'; +import { useService } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; import type { UserInfo as IUserInfo } from '@cloudbeaver/core-sdk'; import { composes, useStyles } from '@cloudbeaver/core-theming'; +import { UserProfileService } from '@cloudbeaver/plugin-user-profile'; -const styles = css` - user { - height: 100%; - display: flex; - align-items: center; - } - IconOrImage { - display: block; - width: 24px; - } - user-name { - display: block; - line-height: initial; - margin-left: 8px; - } -`; - -const detachedStyles = composes( +const styles = composes( css` user { composes: theme-ripple from global; @@ -39,25 +24,34 @@ const detachedStyles = composes( `, css` user { + height: 100%; + display: flex; + align-items: center; padding: 0 16px; cursor: pointer; } - ` -); + IconOrImage { + display: block; + width: 24px; + } + user-name { + display: block; + line-height: initial; + margin-left: 8px; + } +`); interface Props { info: IUserInfo; - detached?: boolean; - tooltip?: string; - onClick?: () => void; } -export const UserInfo = observer(function UserInfo({ info, detached, tooltip, onClick }) { +export const UserInfo = observer(function UserInfo({ info }) { const translate = useTranslate(); - const style = useStyles(styles, detached && detachedStyles); + const style = useStyles(styles); + const userProfileService = useService(UserProfileService); return styled(style)( - + diff --git a/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx b/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx index b5dcb5953b..6bcf9f79dd 100644 --- a/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx +++ b/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx @@ -11,10 +11,11 @@ import styled from 'reshadow'; import { topMenuStyles } from '@cloudbeaver/core-app'; import { AuthInfoService, DATA_CONTEXT_USER } from '@cloudbeaver/core-authentication'; +import { Icon } from '@cloudbeaver/core-blocks'; import { useService } from '@cloudbeaver/core-di'; import { useStyles } from '@cloudbeaver/core-theming'; import { ContextMenu } from '@cloudbeaver/core-ui'; -import { MenuBaseItem, useMenu } from '@cloudbeaver/core-view'; +import { useMenu } from '@cloudbeaver/core-view'; import { UserInfo } from '../UserInfo'; import { MENU_USER_PROFILE } from './MENU_USER_PROFILE'; @@ -25,25 +26,18 @@ export const UserMenu = observer(function UserMenu() { const authInfoService = useService(AuthInfoService); const menu = useMenu(MENU_USER_PROFILE); + menu.context.set(DATA_CONTEXT_USER, authInfoService.userInfo); + if (!authInfoService.userInfo) { return null; } - const items = menu.getItems().filter(item => !item.hidden); - - if (items.length === 1 && items[0] instanceof MenuBaseItem) { - const item = items[0]; - - if (item.events?.onSelect) { - return ; - } - } - - menu.context.set(DATA_CONTEXT_USER, authInfoService.userInfo); - return styled(style)( - + <> - + + + + ); }); diff --git a/webapp/packages/plugin-authentication/src/UserMenu/userMenuStyles.ts b/webapp/packages/plugin-authentication/src/UserMenu/userMenuStyles.ts index a94658ac4a..2535327964 100644 --- a/webapp/packages/plugin-authentication/src/UserMenu/userMenuStyles.ts +++ b/webapp/packages/plugin-authentication/src/UserMenu/userMenuStyles.ts @@ -11,11 +11,17 @@ import { css } from 'reshadow'; export const userMenuStyles = css` ContextMenu { opacity: 1; - padding: 0 16px !important; + padding: 0 !important; height: 48px; cursor: pointer; background: none; border: none; outline: none !important; + & Icon { + background: #47a0dd; + width: 16px; + height: 100%; + padding: 0 16px; + } } `; diff --git a/webapp/packages/plugin-user-profile/src/PluginBootstrap.ts b/webapp/packages/plugin-user-profile/src/PluginBootstrap.ts deleted file mode 100644 index 3534f9c154..0000000000 --- a/webapp/packages/plugin-user-profile/src/PluginBootstrap.ts +++ /dev/null @@ -1,39 +0,0 @@ -/* - * CloudBeaver - Cloud Database Manager - * Copyright (C) 2020-2021 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 { Bootstrap, injectable } from '@cloudbeaver/core-di'; -import { DATA_CONTEXT_MENU, DATA_CONTEXT_MENU_NESTED, MenuBaseItem, MenuService } from '@cloudbeaver/core-view'; -import { MENU_USER_PROFILE } from '@cloudbeaver/plugin-authentication'; - -import { UserProfileService } from './UserProfileService'; - -@injectable() -export class PluginBootstrap extends Bootstrap { - constructor( - private menuService: MenuService, - private userProfileService: UserProfileService - ) { - super(); - } - - register(): void { - this.menuService.addCreator({ - isApplicable: - context => context.get(DATA_CONTEXT_MENU) === MENU_USER_PROFILE - && !context.get(DATA_CONTEXT_MENU_NESTED), - getItems: (context, items) => [ - new MenuBaseItem('profile', 'plugin_user_profile_menu', undefined, { - onSelect: () => this.userProfileService.open(), - }), - ...items, - ], - }); - } - - load(): void {} -} diff --git a/webapp/packages/plugin-user-profile/src/UserProfileService.ts b/webapp/packages/plugin-user-profile/src/UserProfileService.ts index 47234fe259..0cac3b2bc7 100644 --- a/webapp/packages/plugin-user-profile/src/UserProfileService.ts +++ b/webapp/packages/plugin-user-profile/src/UserProfileService.ts @@ -47,7 +47,7 @@ export class UserProfileService { } } - async open(): Promise { + open = async (): Promise => { const state = await this.optionsPanelService.open(formGetter); if (state) { @@ -55,7 +55,7 @@ export class UserProfileService { } return state; - } + }; async close(saved?: boolean): Promise { if (saved) { diff --git a/webapp/packages/plugin-user-profile/src/index.ts b/webapp/packages/plugin-user-profile/src/index.ts index 8c3166e301..4dcf4180d4 100644 --- a/webapp/packages/plugin-user-profile/src/index.ts +++ b/webapp/packages/plugin-user-profile/src/index.ts @@ -1,3 +1,5 @@ import { userProfilePlugin } from './manifest'; export default userProfilePlugin; + +export * from './UserProfileService'; diff --git a/webapp/packages/plugin-user-profile/src/manifest.ts b/webapp/packages/plugin-user-profile/src/manifest.ts index 94a9d8346d..6f67ad7b9b 100644 --- a/webapp/packages/plugin-user-profile/src/manifest.ts +++ b/webapp/packages/plugin-user-profile/src/manifest.ts @@ -9,7 +9,6 @@ import type { PluginManifest } from '@cloudbeaver/core-di'; import { LocaleService } from './LocaleService'; -import { PluginBootstrap } from './PluginBootstrap'; import { UserProfileService } from './UserProfileService'; export const userProfilePlugin: PluginManifest = { @@ -18,7 +17,6 @@ export const userProfilePlugin: PluginManifest = { }, providers: [ - PluginBootstrap, LocaleService, UserProfileService, ], From 10464aaf321369aeac417f065c6c31159001dd01 Mon Sep 17 00:00:00 2001 From: Naumov Alexey Date: Wed, 1 Dec 2021 21:46:19 +0300 Subject: [PATCH 3/3] fix(plugin-user-profile): CB-1399 change dependency direction --- .../src/PluginBootstrap.ts | 4 -- .../src/PluginBootstrap.ts | 5 +- .../plugin-authentication/src/index.ts | 2 - .../.gitignore | 17 +++++++ .../package.json | 46 +++++++++++++++++++ .../src/PluginBootstrap.ts | 26 +++++++++++ .../src/index.ts | 3 ++ .../src/manifest.ts | 21 +++++++++ .../tsconfig.json | 8 ++++ .../src/PluginBootstrap.ts | 27 +++++++++++ .../src/UserInfo.tsx | 5 +- .../src/UserMenu/MENU_USER_PROFILE.ts | 0 .../src/UserMenu/UserMenu.tsx | 0 .../src/UserMenu/userMenuStyles.ts | 0 .../src/UserProfileService.ts | 4 +- .../packages/plugin-user-profile/src/index.ts | 3 +- .../plugin-user-profile/src/manifest.ts | 2 + webapp/packages/product-default/src/index.ts | 2 + 18 files changed, 160 insertions(+), 15 deletions(-) create mode 100644 webapp/packages/plugin-user-profile-administration/.gitignore create mode 100644 webapp/packages/plugin-user-profile-administration/package.json create mode 100644 webapp/packages/plugin-user-profile-administration/src/PluginBootstrap.ts create mode 100644 webapp/packages/plugin-user-profile-administration/src/index.ts create mode 100644 webapp/packages/plugin-user-profile-administration/src/manifest.ts create mode 100644 webapp/packages/plugin-user-profile-administration/tsconfig.json create mode 100644 webapp/packages/plugin-user-profile/src/PluginBootstrap.ts rename webapp/packages/{plugin-authentication => plugin-user-profile}/src/UserInfo.tsx (93%) rename webapp/packages/{plugin-authentication => plugin-user-profile}/src/UserMenu/MENU_USER_PROFILE.ts (100%) rename webapp/packages/{plugin-authentication => plugin-user-profile}/src/UserMenu/UserMenu.tsx (100%) rename webapp/packages/{plugin-authentication => plugin-user-profile}/src/UserMenu/userMenuStyles.ts (100%) diff --git a/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts b/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts index b944805ff2..ca535dffbb 100644 --- a/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-authentication-administration/src/PluginBootstrap.ts @@ -6,24 +6,20 @@ * you may not use this file except in compliance with the License. */ -import { AdministrationTopAppBarService } from '@cloudbeaver/core-administration'; import { injectable, Bootstrap } from '@cloudbeaver/core-di'; import { ServerConfigurationService } from '@cloudbeaver/plugin-administration'; -import { UserMenu } from '@cloudbeaver/plugin-authentication'; import { AuthenticationProviders } from './Administration/ServerConfiguration/AuthenticationProviders'; @injectable() export class PluginBootstrap extends Bootstrap { constructor( - private administrationTopAppBarService: AdministrationTopAppBarService, private readonly serverConfigurationService: ServerConfigurationService, ) { super(); } register(): void { - this.administrationTopAppBarService.placeholder.add(UserMenu, 4); this.serverConfigurationService.configurationContainer.add(AuthenticationProviders, 0); } diff --git a/webapp/packages/plugin-authentication/src/PluginBootstrap.ts b/webapp/packages/plugin-authentication/src/PluginBootstrap.ts index 8c0a168cc7..b77d099f44 100644 --- a/webapp/packages/plugin-authentication/src/PluginBootstrap.ts +++ b/webapp/packages/plugin-authentication/src/PluginBootstrap.ts @@ -6,14 +6,13 @@ * you may not use this file except in compliance with the License. */ -import { SettingsMenuService, TopNavService } from '@cloudbeaver/core-app'; +import { SettingsMenuService } from '@cloudbeaver/core-app'; import { AuthInfoService } from '@cloudbeaver/core-authentication'; import { injectable, Bootstrap } from '@cloudbeaver/core-di'; import { ServerConfigResource } from '@cloudbeaver/core-root'; import { AuthenticationService } from './AuthenticationService'; import { AuthDialogService } from './Dialog/AuthDialogService'; -import { UserMenu } from './UserMenu/UserMenu'; @injectable() export class PluginBootstrap extends Bootstrap { @@ -23,7 +22,6 @@ export class PluginBootstrap extends Bootstrap { private readonly authenticationService: AuthenticationService, private readonly authInfoService: AuthInfoService, private readonly settingsMenuService: SettingsMenuService, - private readonly topNavService: TopNavService, ) { super(); } @@ -50,7 +48,6 @@ export class PluginBootstrap extends Bootstrap { onClick: this.authenticationService.logout.bind(this.authenticationService), } ); - this.topNavService.placeholder.add(UserMenu, 4); } load(): void | Promise { } diff --git a/webapp/packages/plugin-authentication/src/index.ts b/webapp/packages/plugin-authentication/src/index.ts index df66f5b51e..7af0d5bcc7 100644 --- a/webapp/packages/plugin-authentication/src/index.ts +++ b/webapp/packages/plugin-authentication/src/index.ts @@ -4,5 +4,3 @@ export default manifest; export * from './AuthenticationService'; export * from './Dialog/AuthDialogService'; -export * from './UserMenu/MENU_USER_PROFILE'; -export * from './UserMenu/UserMenu'; diff --git a/webapp/packages/plugin-user-profile-administration/.gitignore b/webapp/packages/plugin-user-profile-administration/.gitignore new file mode 100644 index 0000000000..15bc16c7c3 --- /dev/null +++ b/webapp/packages/plugin-user-profile-administration/.gitignore @@ -0,0 +1,17 @@ +# dependencies +/node_modules + +# testing +/coverage + +# production +/lib + +# misc +.DS_Store +.env* + +# debug +npm-debug.log* +yarn-debug.log* +yarn-error.log* diff --git a/webapp/packages/plugin-user-profile-administration/package.json b/webapp/packages/plugin-user-profile-administration/package.json new file mode 100644 index 0000000000..d889c291c8 --- /dev/null +++ b/webapp/packages/plugin-user-profile-administration/package.json @@ -0,0 +1,46 @@ +{ + "name": "@cloudbeaver/plugin-user-profile-administration", + "sideEffects": false, + "version": "0.1.0", + "description": "", + "license": "Apache-2.0", + "main": "lib/index.js", + "module": "lib/index.js", + "types": "src/index.ts", + "scripts": { + "prebuild": "rimraf lib/*", + "build": "webpack --config ../../configs/webpack.plugin.config.js", + "postbuild": "tsc", + "lint": "eslint ./src/ --ext .ts,.tsx", + "lint-fix": "eslint ./src/ --ext .ts,.tsx --fix" + }, + "peerDependencies": { + "@cloudbeaver/core-administration": "^0.1.0", + "@cloudbeaver/core-app": "^0.1.0", + "@cloudbeaver/core-authentication": "^0.1.0", + "@cloudbeaver/core-blocks": "^0.1.0", + "@cloudbeaver/core-connections": "^0.1.0", + "@cloudbeaver/core-di": "^0.1.0", + "@cloudbeaver/core-dialogs": "^0.1.0", + "@cloudbeaver/core-events": "^0.1.0", + "@cloudbeaver/core-executor": "^0.1.0", + "@cloudbeaver/core-extensions": "^0.1.0", + "@cloudbeaver/core-localization": "^0.1.0", + "@cloudbeaver/core-notifications": "^0.1.0", + "@cloudbeaver/core-plugin": "^0.1.0", + "@cloudbeaver/core-product": "^0.1.0", + "@cloudbeaver/core-root": "^0.1.0", + "@cloudbeaver/core-routing": "^0.1.0", + "@cloudbeaver/core-sdk": "^0.1.0", + "@cloudbeaver/core-settings": "^0.1.0", + "@cloudbeaver/core-theming": "^0.1.0", + "@cloudbeaver/core-ui": "^0.1.0", + "@cloudbeaver/core-utils": "^0.1.0", + "@cloudbeaver/core-view": "^0.1.0", + "@cloudbeaver/plugin-user-profile": "^0.1.0", + "react": "^16.x.x", + "mobx": "^6.x.x", + "mobx-react-lite": "^3.x.x", + "reshadow": "^0.x.x" + } +} diff --git a/webapp/packages/plugin-user-profile-administration/src/PluginBootstrap.ts b/webapp/packages/plugin-user-profile-administration/src/PluginBootstrap.ts new file mode 100644 index 0000000000..1a21c1e563 --- /dev/null +++ b/webapp/packages/plugin-user-profile-administration/src/PluginBootstrap.ts @@ -0,0 +1,26 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { AdministrationTopAppBarService } from '@cloudbeaver/core-administration'; +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; +import { UserMenu } from '@cloudbeaver/plugin-user-profile'; + +@injectable() +export class PluginBootstrap extends Bootstrap { + constructor( + private readonly administrationTopAppBarService: AdministrationTopAppBarService + ) { + super(); + } + + register(): void { + this.administrationTopAppBarService.placeholder.add(UserMenu, 4); + } + + load(): void { } +} diff --git a/webapp/packages/plugin-user-profile-administration/src/index.ts b/webapp/packages/plugin-user-profile-administration/src/index.ts new file mode 100644 index 0000000000..b9c556470a --- /dev/null +++ b/webapp/packages/plugin-user-profile-administration/src/index.ts @@ -0,0 +1,3 @@ +import { userProfileAdministrationPlugin } from './manifest'; + +export default userProfileAdministrationPlugin; diff --git a/webapp/packages/plugin-user-profile-administration/src/manifest.ts b/webapp/packages/plugin-user-profile-administration/src/manifest.ts new file mode 100644 index 0000000000..01e6b9837e --- /dev/null +++ b/webapp/packages/plugin-user-profile-administration/src/manifest.ts @@ -0,0 +1,21 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 type { PluginManifest } from '@cloudbeaver/core-di'; + +import { PluginBootstrap } from './PluginBootstrap'; + +export const userProfileAdministrationPlugin: PluginManifest = { + info: { + name: 'User profile administration plugin', + }, + + providers: [ + PluginBootstrap, + ], +}; diff --git a/webapp/packages/plugin-user-profile-administration/tsconfig.json b/webapp/packages/plugin-user-profile-administration/tsconfig.json new file mode 100644 index 0000000000..8cd6b539e3 --- /dev/null +++ b/webapp/packages/plugin-user-profile-administration/tsconfig.json @@ -0,0 +1,8 @@ +{ + "extends": "../../tsconfig.json", + "compilerOptions": { + "outDir": "lib" + }, + "include": ["src"], + "exclude": ["**/node_modules", "**/.*/"], +} diff --git a/webapp/packages/plugin-user-profile/src/PluginBootstrap.ts b/webapp/packages/plugin-user-profile/src/PluginBootstrap.ts new file mode 100644 index 0000000000..847d1c8496 --- /dev/null +++ b/webapp/packages/plugin-user-profile/src/PluginBootstrap.ts @@ -0,0 +1,27 @@ +/* + * CloudBeaver - Cloud Database Manager + * Copyright (C) 2020-2021 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 { TopNavService } from '@cloudbeaver/core-app'; +import { Bootstrap, injectable } from '@cloudbeaver/core-di'; + +import { UserMenu } from './UserMenu/UserMenu'; + +@injectable() +export class PluginBootstrap extends Bootstrap { + constructor( + private readonly topNavService: TopNavService + ) { + super(); + } + + register(): void { + this.topNavService.placeholder.add(UserMenu, 4); + } + + load(): void { } +} diff --git a/webapp/packages/plugin-authentication/src/UserInfo.tsx b/webapp/packages/plugin-user-profile/src/UserInfo.tsx similarity index 93% rename from webapp/packages/plugin-authentication/src/UserInfo.tsx rename to webapp/packages/plugin-user-profile/src/UserInfo.tsx index 4c7482d471..3cde6b8181 100644 --- a/webapp/packages/plugin-authentication/src/UserInfo.tsx +++ b/webapp/packages/plugin-user-profile/src/UserInfo.tsx @@ -14,7 +14,8 @@ import { useService } from '@cloudbeaver/core-di'; import { useTranslate } from '@cloudbeaver/core-localization'; import type { UserInfo as IUserInfo } from '@cloudbeaver/core-sdk'; import { composes, useStyles } from '@cloudbeaver/core-theming'; -import { UserProfileService } from '@cloudbeaver/plugin-user-profile'; + +import { UserProfileService } from './UserProfileService'; const styles = composes( css` @@ -51,7 +52,7 @@ export const UserInfo = observer(function UserInfo({ info }) { const userProfileService = useService(UserProfileService); return styled(style)( - + userProfileService.open()}> diff --git a/webapp/packages/plugin-authentication/src/UserMenu/MENU_USER_PROFILE.ts b/webapp/packages/plugin-user-profile/src/UserMenu/MENU_USER_PROFILE.ts similarity index 100% rename from webapp/packages/plugin-authentication/src/UserMenu/MENU_USER_PROFILE.ts rename to webapp/packages/plugin-user-profile/src/UserMenu/MENU_USER_PROFILE.ts diff --git a/webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx b/webapp/packages/plugin-user-profile/src/UserMenu/UserMenu.tsx similarity index 100% rename from webapp/packages/plugin-authentication/src/UserMenu/UserMenu.tsx rename to webapp/packages/plugin-user-profile/src/UserMenu/UserMenu.tsx diff --git a/webapp/packages/plugin-authentication/src/UserMenu/userMenuStyles.ts b/webapp/packages/plugin-user-profile/src/UserMenu/userMenuStyles.ts similarity index 100% rename from webapp/packages/plugin-authentication/src/UserMenu/userMenuStyles.ts rename to webapp/packages/plugin-user-profile/src/UserMenu/userMenuStyles.ts diff --git a/webapp/packages/plugin-user-profile/src/UserProfileService.ts b/webapp/packages/plugin-user-profile/src/UserProfileService.ts index 0cac3b2bc7..47234fe259 100644 --- a/webapp/packages/plugin-user-profile/src/UserProfileService.ts +++ b/webapp/packages/plugin-user-profile/src/UserProfileService.ts @@ -47,7 +47,7 @@ export class UserProfileService { } } - open = async (): Promise => { + async open(): Promise { const state = await this.optionsPanelService.open(formGetter); if (state) { @@ -55,7 +55,7 @@ export class UserProfileService { } return state; - }; + } async close(saved?: boolean): Promise { if (saved) { diff --git a/webapp/packages/plugin-user-profile/src/index.ts b/webapp/packages/plugin-user-profile/src/index.ts index 4dcf4180d4..61f245673e 100644 --- a/webapp/packages/plugin-user-profile/src/index.ts +++ b/webapp/packages/plugin-user-profile/src/index.ts @@ -2,4 +2,5 @@ import { userProfilePlugin } from './manifest'; export default userProfilePlugin; -export * from './UserProfileService'; +export * from './UserMenu/MENU_USER_PROFILE'; +export * from './UserMenu/UserMenu'; diff --git a/webapp/packages/plugin-user-profile/src/manifest.ts b/webapp/packages/plugin-user-profile/src/manifest.ts index 6f67ad7b9b..94a9d8346d 100644 --- a/webapp/packages/plugin-user-profile/src/manifest.ts +++ b/webapp/packages/plugin-user-profile/src/manifest.ts @@ -9,6 +9,7 @@ import type { PluginManifest } from '@cloudbeaver/core-di'; import { LocaleService } from './LocaleService'; +import { PluginBootstrap } from './PluginBootstrap'; import { UserProfileService } from './UserProfileService'; export const userProfilePlugin: PluginManifest = { @@ -17,6 +18,7 @@ export const userProfilePlugin: PluginManifest = { }, providers: [ + PluginBootstrap, LocaleService, UserProfileService, ], diff --git a/webapp/packages/product-default/src/index.ts b/webapp/packages/product-default/src/index.ts index 667dd60a33..8d1d9408cd 100644 --- a/webapp/packages/product-default/src/index.ts +++ b/webapp/packages/product-default/src/index.ts @@ -20,6 +20,7 @@ import sqlEditor from '@cloudbeaver/plugin-sql-editor'; import sqlEditorTab from '@cloudbeaver/plugin-sql-editor-navigation-tab'; import sqlEditorScreen from '@cloudbeaver/plugin-sql-editor-screen'; import userProfile from '@cloudbeaver/plugin-user-profile'; +import userProfileAdministration from '@cloudbeaver/plugin-user-profile-administration'; import { defaultProductManifest } from './manifest'; @@ -39,6 +40,7 @@ const PLUGINS: PluginManifest[] = [ sqlEditorTab, sqlEditorScreen, userProfile, + userProfileAdministration, defaultProductManifest, connectionPlugin, connectionAdministration,