mirror of
https://github.com/dbeaver/cloudbeaver.git
synced 2026-09-24 16:04:36 +08:00
Merge pull request #560 from dbeaver/fix/profile
fix(plugin-user-profile): CB-1399 move change password form to user p…
This commit is contained in:
@@ -10,9 +10,10 @@ 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';
|
||||
@@ -53,12 +54,21 @@ const administrationStyles = composes(
|
||||
padding-top: 16px;
|
||||
border-right: 2px solid;
|
||||
}
|
||||
content {
|
||||
content-container {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
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<Props>(function Administration({
|
||||
}) {
|
||||
const contentRef = useRef<HTMLDivElement>(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<Props>(function Administration({
|
||||
contentRef.current?.scrollTo({ top: 0, left: 0 });
|
||||
}, [activeScreen?.item]);
|
||||
|
||||
return styled(useStyles(verticalTabStyles, administrationStyles, tabsStyles))(
|
||||
return styled(useStyles(verticalTabStyles, administrationStyles, tabsStyles, slideBoxStyles))(
|
||||
<container>
|
||||
<TabsState currentTabId={activeScreen?.item} orientation='vertical'>
|
||||
<TabList aria-label="Administration items">
|
||||
@@ -95,13 +108,26 @@ export const Administration = observer<Props>(function Administration({
|
||||
/>
|
||||
))}
|
||||
</TabList>
|
||||
<content ref={contentRef} as='div'>
|
||||
<content-container ref={contentRef} as='div'>
|
||||
{children}
|
||||
<ItemContent
|
||||
activeScreen={activeScreen}
|
||||
configurationWizard={configurationWizard}
|
||||
/>
|
||||
</content>
|
||||
<SlideBox open={optionsPanelService.active}>
|
||||
<SlideElement>
|
||||
<ErrorBoundary remount>
|
||||
<content>
|
||||
<OptionsPanel />
|
||||
</content>
|
||||
</ErrorBoundary>
|
||||
</SlideElement>
|
||||
<SlideElement>
|
||||
<ErrorBoundary remount>
|
||||
<content>
|
||||
<ItemContent activeScreen={activeScreen} configurationWizard={configurationWizard} />
|
||||
</content>
|
||||
</ErrorBoundary>
|
||||
<SlideOverlay onClick={() => optionsPanelService.close()} />
|
||||
</SlideElement>
|
||||
</SlideBox>
|
||||
</content-container>
|
||||
</TabsState>
|
||||
</container>
|
||||
);
|
||||
|
||||
@@ -47,9 +47,6 @@ const styles = composes(
|
||||
SlideBox {
|
||||
flex: 1;
|
||||
}
|
||||
SlideElement {
|
||||
display: flex;
|
||||
}
|
||||
`
|
||||
);
|
||||
|
||||
|
||||
@@ -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));
|
||||
|
||||
|
||||
@@ -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"],
|
||||
|
||||
@@ -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', 'У вас нет разрешения на это действие'],
|
||||
|
||||
@@ -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 { UserInfo } 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(UserInfo, 4);
|
||||
this.serverConfigurationService.configurationContainer.add(AuthenticationProviders, 0);
|
||||
}
|
||||
|
||||
|
||||
@@ -6,50 +6,27 @@
|
||||
* you may not use this file except in compliance with the License.
|
||||
*/
|
||||
|
||||
import { SettingsMenuService, TopNavService } from '@cloudbeaver/core-app';
|
||||
import { AuthInfoService, AUTH_PROVIDER_LOCAL_ID } from '@cloudbeaver/core-authentication';
|
||||
import { SettingsMenuService } from '@cloudbeaver/core-app';
|
||||
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';
|
||||
|
||||
@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,
|
||||
) {
|
||||
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 +48,6 @@ export class PluginBootstrap extends Bootstrap {
|
||||
onClick: this.authenticationService.logout.bind(this.authenticationService),
|
||||
}
|
||||
);
|
||||
this.topNavService.placeholder.add(UserInfo, 4);
|
||||
}
|
||||
|
||||
load(): void | Promise<void> { }
|
||||
|
||||
@@ -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 { 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';
|
||||
|
||||
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;
|
||||
}
|
||||
`;
|
||||
|
||||
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);
|
||||
|
||||
if (!authInfoService.userInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return styled(style)(
|
||||
<ContextMenu menu={menu} style={[topMenuStyles]} rtl modal>
|
||||
<user>
|
||||
<user-icon>
|
||||
<IconOrImage icon='user' viewBox='0 0 28 28' />
|
||||
</user-icon>
|
||||
<user-name>{authInfoService.userInfo.displayName || authInfoService.userInfo.userId}</user-name>
|
||||
</user>
|
||||
</ContextMenu>
|
||||
);
|
||||
});
|
||||
-157
@@ -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<null, null> = observer(
|
||||
function ChangeUserPasswordDialog({
|
||||
payload,
|
||||
resolveDialog,
|
||||
rejectDialog,
|
||||
}: DialogComponentProps<null, null>) {
|
||||
const [focusedRef] = useFocus<HTMLInputElement>({});
|
||||
const usersResource = useService(UsersResource);
|
||||
const notificationService = useService(NotificationService);
|
||||
const style = useStyles(styles, BASE_CONTAINERS_STYLES);
|
||||
const translate = useTranslate();
|
||||
const [state] = useState<IState>(() => 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)(
|
||||
<CommonDialogWrapper
|
||||
size='medium'
|
||||
title="authentication_user_password_change_dialog_title"
|
||||
footer={(
|
||||
<ChangeUserPasswordDialogFooter
|
||||
submitting={state.submitting}
|
||||
formFilled={formFilled}
|
||||
onCancel={rejectDialog}
|
||||
onChange={change}
|
||||
>
|
||||
{(state.error.responseMessage || state.customError) && (
|
||||
<ErrorMessage
|
||||
text={state.error.responseMessage || translate(state.customError)}
|
||||
hasDetails={errorDetails?.hasDetails}
|
||||
onShowDetails={openErrorDetails}
|
||||
/>
|
||||
)}
|
||||
</ChangeUserPasswordDialogFooter>
|
||||
)}
|
||||
onReject={rejectDialog}
|
||||
>
|
||||
<SubmittingForm onSubmit={change}>
|
||||
<Container>
|
||||
<Group small gap center>
|
||||
<InputField
|
||||
ref={focusedRef}
|
||||
type='password'
|
||||
name='oldPassword'
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_current_password')}
|
||||
</InputField>
|
||||
<InputField
|
||||
type='password'
|
||||
name='newPassword'
|
||||
autoComplete='new-password'
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_new_password')}
|
||||
</InputField>
|
||||
<InputField
|
||||
type='password'
|
||||
name='repeatedPassword'
|
||||
state={state}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('authentication_user_password_repeat')}
|
||||
</InputField>
|
||||
</Group>
|
||||
</Container>
|
||||
</SubmittingForm>
|
||||
</CommonDialogWrapper>
|
||||
);
|
||||
}
|
||||
);
|
||||
-60
@@ -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<Props>(function ChangeUserPasswordDialogFooter({
|
||||
onChange,
|
||||
onCancel,
|
||||
formFilled,
|
||||
submitting,
|
||||
className,
|
||||
children,
|
||||
}) {
|
||||
const translate = useTranslate();
|
||||
|
||||
return styled(styles)(
|
||||
<footer-container className={className}>
|
||||
{children}
|
||||
<Button disabled={submitting} type="button" mod={['outlined']} onClick={onCancel}>
|
||||
{translate('ui_processing_cancel')}
|
||||
</Button>
|
||||
<Button disabled={submitting || !formFilled} loading={submitting} type="button" mod={['unelevated']} onClick={onChange}>
|
||||
{translate('authentication_user_password_change_dialog_submit')}
|
||||
</Button>
|
||||
</footer-container>
|
||||
);
|
||||
});
|
||||
@@ -4,5 +4,3 @@ export default manifest;
|
||||
|
||||
export * from './AuthenticationService';
|
||||
export * from './Dialog/AuthDialogService';
|
||||
export * from './UserMenu/MENU_USER_PROFILE';
|
||||
export * from './UserInfo';
|
||||
|
||||
@@ -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'],
|
||||
];
|
||||
|
||||
@@ -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'],
|
||||
];
|
||||
|
||||
@@ -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', 'Роль'],
|
||||
];
|
||||
|
||||
@@ -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*
|
||||
@@ -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"
|
||||
}
|
||||
}
|
||||
@@ -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 { }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
import { userProfileAdministrationPlugin } from './manifest';
|
||||
|
||||
export default userProfileAdministrationPlugin;
|
||||
@@ -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,
|
||||
],
|
||||
};
|
||||
@@ -0,0 +1,8 @@
|
||||
{
|
||||
"extends": "../../tsconfig.json",
|
||||
"compilerOptions": {
|
||||
"outDir": "lib"
|
||||
},
|
||||
"include": ["src"],
|
||||
"exclude": ["**/node_modules", "**/.*/"],
|
||||
}
|
||||
@@ -6,34 +6,22 @@
|
||||
* 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 { 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';
|
||||
import { UserMenu } from './UserMenu/UserMenu';
|
||||
|
||||
@injectable()
|
||||
export class PluginBootstrap extends Bootstrap {
|
||||
constructor(
|
||||
private menuService: MenuService,
|
||||
private userProfileService: UserProfileService
|
||||
private readonly topNavService: TopNavService
|
||||
) {
|
||||
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,
|
||||
],
|
||||
});
|
||||
this.topNavService.placeholder.add(UserMenu, 4);
|
||||
}
|
||||
|
||||
load(): void {}
|
||||
load(): void { }
|
||||
}
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
/*
|
||||
* 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 { 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 './UserProfileService';
|
||||
|
||||
const styles = composes(
|
||||
css`
|
||||
user {
|
||||
composes: theme-ripple from global;
|
||||
}
|
||||
`,
|
||||
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;
|
||||
}
|
||||
|
||||
export const UserInfo = observer<Props>(function UserInfo({ info }) {
|
||||
const translate = useTranslate();
|
||||
const style = useStyles(styles);
|
||||
const userProfileService = useService(UserProfileService);
|
||||
|
||||
return styled(style)(
|
||||
<user title={translate('plugin_user_profile_menu')} onClick={() => userProfileService.open()}>
|
||||
<user-icon>
|
||||
<IconOrImage icon='user' viewBox='0 0 28 28' />
|
||||
</user-icon>
|
||||
<user-name>{info.displayName || info.userId}</user-name>
|
||||
</user>
|
||||
);
|
||||
});
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* 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 { Icon } 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 { 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);
|
||||
|
||||
menu.context.set(DATA_CONTEXT_USER, authInfoService.userInfo);
|
||||
|
||||
if (!authInfoService.userInfo) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return styled(style)(
|
||||
<>
|
||||
<UserInfo info={authInfoService.userInfo} />
|
||||
<ContextMenu menu={menu} style={[topMenuStyles]} rtl modal>
|
||||
<Icon name="angle" viewBox="0 0 15 8" />
|
||||
</ContextMenu>
|
||||
</>
|
||||
);
|
||||
});
|
||||
+7
-1
@@ -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;
|
||||
}
|
||||
}
|
||||
`;
|
||||
+34
@@ -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<Props>(function AuthenticationPanel({ className, style }) {
|
||||
const styles = useStyles(style, BASE_CONTAINERS_STYLES);
|
||||
|
||||
return styled(styles)(
|
||||
<TabPanel tabId='authentication' className={className}>
|
||||
<ColoredContainer wrap overflow parent gap>
|
||||
<Container medium gap>
|
||||
<ChangePassword />
|
||||
</Container>
|
||||
</ColoredContainer>
|
||||
</TabPanel>
|
||||
);
|
||||
});
|
||||
+28
@@ -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<Props>(function AuthenticationTab({ className, style }) {
|
||||
const translate = useTranslate();
|
||||
const styles = useStyles(style);
|
||||
|
||||
return styled(styles)(
|
||||
<Tab tabId='authentication' style={style} className={className}>{translate('ui_authentication')}</Tab>
|
||||
);
|
||||
});
|
||||
+74
@@ -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))(
|
||||
<Group form gap>
|
||||
<GroupTitle>{translate('plugin_user_profile_authentication_change_password')}</GroupTitle>
|
||||
<InputField
|
||||
type='password'
|
||||
name='oldPassword'
|
||||
state={state.config}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('plugin_user_profile_authentication_change_password_current_password')}
|
||||
</InputField>
|
||||
<InputField
|
||||
type='password'
|
||||
name='password'
|
||||
autoComplete='new-password'
|
||||
state={state.config}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('plugin_user_profile_authentication_change_password_new_password')}
|
||||
</InputField>
|
||||
<InputField
|
||||
type='password'
|
||||
name='repeatedPassword'
|
||||
state={state.config}
|
||||
disabled={disabled}
|
||||
mapValue={(value => value.trim())}
|
||||
small
|
||||
required
|
||||
>
|
||||
{translate('plugin_user_profile_authentication_change_password_repeat_password')}
|
||||
</InputField>
|
||||
<GroupItem>
|
||||
<Button
|
||||
disabled={disabled || !state.formFilled}
|
||||
loading={state.submitting}
|
||||
type="button"
|
||||
mod={['unelevated']}
|
||||
onClick={state.changePassword}
|
||||
>
|
||||
{translate('plugin_user_profile_authentication_change_password_submit_label')}
|
||||
</Button>
|
||||
</GroupItem>
|
||||
</Group>
|
||||
);
|
||||
});
|
||||
+76
@@ -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<void>;
|
||||
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 });
|
||||
}
|
||||
@@ -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<Props>(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)(
|
||||
<flex-box>
|
||||
<TabsState>
|
||||
@@ -141,6 +146,7 @@ export const UserForm = observer<Props>(function UserForm({
|
||||
</status-message>
|
||||
<TabList style={style} disabled={state.info.disabled}>
|
||||
<UserInfoTab style={style} />
|
||||
{localProvider && <AuthenticationTab style={style} />}
|
||||
{/* <UserAuthProvidersTab style={style} /> */}
|
||||
</TabList>
|
||||
</top-bar-tabs>
|
||||
@@ -158,6 +164,7 @@ export const UserForm = observer<Props>(function UserForm({
|
||||
</top-bar>
|
||||
<content-box>
|
||||
<UserInfoPanel user={user} style={style} />
|
||||
{localProvider && <AuthenticationPanel style={style} />}
|
||||
{/* <UserAuthProviderPanel user={user} style={style} /> */}
|
||||
</content-box>
|
||||
</TabsState>
|
||||
|
||||
@@ -45,7 +45,6 @@ export const UserInfoPanel = observer<Props>(function UserInfoPanel({
|
||||
minLength={1}
|
||||
state={user}
|
||||
mod='surface'
|
||||
disabled
|
||||
readOnly
|
||||
required
|
||||
tiny
|
||||
@@ -59,7 +58,6 @@ export const UserInfoPanel = observer<Props>(function UserInfoPanel({
|
||||
minLength={1}
|
||||
state={user}
|
||||
mod='surface'
|
||||
disabled
|
||||
readOnly
|
||||
required
|
||||
tiny
|
||||
@@ -68,14 +66,12 @@ export const UserInfoPanel = observer<Props>(function UserInfoPanel({
|
||||
{translate('plugin_user_profile_info_displayName')}
|
||||
</InputField>
|
||||
</Container>
|
||||
|
||||
<Loader state={userMetaParameters} inline>
|
||||
{() => userMetaParameters.data.length > 0 && styled(styles)(
|
||||
<Container wrap gap>
|
||||
<ObjectPropertyInfoForm
|
||||
state={user.metaParameters}
|
||||
properties={userMetaParameters.data}
|
||||
disabled
|
||||
readOnly
|
||||
tiny
|
||||
fill
|
||||
@@ -89,7 +85,6 @@ export const UserInfoPanel = observer<Props>(function UserInfoPanel({
|
||||
<AuthTokenList user={user} />
|
||||
</Group>
|
||||
</Container>
|
||||
|
||||
</ColoredContainer>
|
||||
</TabPanel>
|
||||
);
|
||||
|
||||
@@ -1,3 +1,6 @@
|
||||
import { userProfilePlugin } from './manifest';
|
||||
|
||||
export default userProfilePlugin;
|
||||
|
||||
export * from './UserMenu/MENU_USER_PROFILE';
|
||||
export * from './UserMenu/UserMenu';
|
||||
|
||||
@@ -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"],
|
||||
];
|
||||
|
||||
@@ -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'],
|
||||
];
|
||||
|
||||
@@ -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', 'Пароли не совпадают'],
|
||||
];
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user