Merge branch 'master' of https://github.com/ONLYOFFICE/CommunityServer-AspNetCore
This commit is contained in:
commit
d046fc738d
@ -44,6 +44,7 @@ namespace ASC.Core.Data
|
||||
{
|
||||
FromDbQuotaToTenantQuota = r => new TenantQuota()
|
||||
{
|
||||
Id = r.Tenant,
|
||||
Name = r.Name,
|
||||
ActiveUsers = r.ActiveUsers != 0 ? r.ActiveUsers : int.MaxValue,
|
||||
AvangateId = r.AvangateId,
|
||||
|
@ -632,11 +632,11 @@ namespace ASC.Core.Data
|
||||
|
||||
if (!string.IsNullOrEmpty(text))
|
||||
{
|
||||
q.Where(
|
||||
u => u.FirstName.Contains(text) |
|
||||
u.LastName.Contains(text) |
|
||||
u.Title.Contains(text) |
|
||||
u.Location.Contains(text) |
|
||||
q = q.Where(
|
||||
u => u.FirstName.Contains(text) ||
|
||||
u.LastName.Contains(text) ||
|
||||
u.Title.Contains(text) ||
|
||||
u.Location.Contains(text) ||
|
||||
u.Email.Contains(text));
|
||||
}
|
||||
|
||||
|
@ -44,7 +44,7 @@ namespace ASC.Core.Tenants
|
||||
};
|
||||
|
||||
[DataMember(Name = "Id", Order = 10)]
|
||||
public int Id { get; private set; }
|
||||
public int Id { get; set; }
|
||||
|
||||
[DataMember(Name = "Name", Order = 20)]
|
||||
public string Name { get; set; }
|
||||
|
@ -7,7 +7,7 @@ import {
|
||||
DropDownItem,
|
||||
toastr
|
||||
} from "asc-web-components";
|
||||
import InviteDialog from './../../dialogs/Invite';
|
||||
import { InviteDialog } from './../../dialogs';
|
||||
import { withTranslation, I18nextProvider } from 'react-i18next';
|
||||
import i18n from '../i18n';
|
||||
import { typeUser, typeGuest, department } from './../../../helpers/customNames';
|
||||
|
@ -0,0 +1,56 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === 'lowercase') return value.toLowerCase();
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/ChangeEmailDialog/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
},
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,178 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import {
|
||||
toastr,
|
||||
ModalDialog,
|
||||
Button,
|
||||
Text,
|
||||
EmailInput,
|
||||
FieldContainer
|
||||
} from "asc-web-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import ModalDialogContainer from '../ModalDialogContainer';
|
||||
import { api } from "asc-web-common";
|
||||
const { sendInstructionsToChangeEmail } = api.people;
|
||||
|
||||
class ChangeEmailDialogComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { user, language } = props;
|
||||
const { email } = user;
|
||||
|
||||
this.state = {
|
||||
isEmailValid: true,
|
||||
isRequestRunning: false,
|
||||
email,
|
||||
hasError: false,
|
||||
errorMessage: '',
|
||||
emailErrors: []
|
||||
};
|
||||
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { user } = this.props;
|
||||
const { email } = user;
|
||||
if (prevProps.user.email !== email) {
|
||||
this.setState({ email });
|
||||
}
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
onValidateEmailInput = result => this.setState({ isEmailValid: result.isValid, emailErrors: result.errors });
|
||||
|
||||
onChangeEmailInput = e => {
|
||||
const { hasError } = this.state;
|
||||
const email = e.target.value;
|
||||
hasError && this.setState({ hasError: false });
|
||||
this.setState({ email });
|
||||
};
|
||||
|
||||
onSendEmailChangeInstructions = () => {
|
||||
const { email } = this.state;
|
||||
const { user } = this.props;
|
||||
const { id } = user;
|
||||
this.setState({ isRequestRunning: true }, () => {
|
||||
sendInstructionsToChangeEmail(id, email)
|
||||
.then((res) => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch((error) => toastr.error(error))
|
||||
.finally(() => {
|
||||
this.setState({ isRequestRunning: false }, () => this.props.onClose());
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
onValidateEmail = () => {
|
||||
const { isEmailValid, email, emailErrors } = this.state;
|
||||
const { t, user } = this.props;
|
||||
if (isEmailValid) {
|
||||
const sameEmailError = email.toLowerCase() === user.email.toLowerCase();
|
||||
if (sameEmailError) {
|
||||
this.setState({ errorMessage: t('SameEmail'), hasError: true });
|
||||
}
|
||||
else {
|
||||
this.setState({ errorMessage: '', hasError: false });
|
||||
this.onSendEmailChangeInstructions();
|
||||
}
|
||||
}
|
||||
else {
|
||||
const translatedErrors = emailErrors.map((errorKey => t(errorKey)));
|
||||
const errorMessage = translatedErrors[0];
|
||||
this.setState({ errorMessage, hasError: true });
|
||||
}
|
||||
};
|
||||
|
||||
onKeyPress = event => {
|
||||
const { isRequestRunning } = this.state;
|
||||
if (event.key === "Enter" && !isRequestRunning) {
|
||||
this.onValidateEmail();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
render() {
|
||||
console.log("ChangeEmailDialog render");
|
||||
const { t, visible, onClose } = this.props;
|
||||
const { isRequestRunning, email, errorMessage, hasError } = this.state;
|
||||
|
||||
return (
|
||||
<ModalDialogContainer>
|
||||
<ModalDialog
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
headerContent={t('EmailChangeTitle')}
|
||||
bodyContent={
|
||||
<>
|
||||
<FieldContainer
|
||||
isVertical
|
||||
labelText={t('EnterEmail')}
|
||||
errorMessage={errorMessage}
|
||||
hasError={hasError}
|
||||
>
|
||||
<EmailInput
|
||||
id="new-email"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
value={email}
|
||||
onChange={this.onChangeEmailInput}
|
||||
onValidateInput={this.onValidateEmailInput}
|
||||
onKeyUp={this.onKeyPress}
|
||||
hasError={hasError}
|
||||
/>
|
||||
</FieldContainer>
|
||||
<Text
|
||||
className='text-dialog'
|
||||
>
|
||||
{t('EmailActivationDescription')}
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
footerContent={
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label={t('SendButton')}
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onValidateEmail}
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ChangeEmailDialogTranslated = withTranslation()(ChangeEmailDialogComponent);
|
||||
|
||||
const ChangeEmailDialog = props => (
|
||||
<ChangeEmailDialogTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
ChangeEmailDialog.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
user: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
language: state.auth.user.cultureName,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {})(withRouter(ChangeEmailDialog));
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"EmailChangeTitle": "Email change",
|
||||
"EnterEmail": "Enter a new email address",
|
||||
"EmailActivationDescription": "The activation instructions will be sent to the entered email",
|
||||
"SendButton": "Send",
|
||||
|
||||
"SameEmail": "Email is same",
|
||||
"LocalDomain": "Local domains are not supported",
|
||||
"IncorrectDomain": "Incorrect domain",
|
||||
"DomainIpAddress": "Domains as ip address are not supported",
|
||||
"PunycodeDomain": "Punycode domains are not supported",
|
||||
"PunycodeLocalPart": "Punycode local part are not supported",
|
||||
"IncorrectLocalPart": "Incorrect local part",
|
||||
"SpacesInLocalPart": "Incorrect, local part contains spaces",
|
||||
"MaxLengthExceeded": "The maximum total length of a user name or other local part is 64 characters.",
|
||||
"IncorrectEmail": "Incorrect email",
|
||||
"ManyEmails": "Too many email parsed",
|
||||
"EmptyEmail": "No one email parsed"
|
||||
}
|
@ -0,0 +1,19 @@
|
||||
{
|
||||
"EmailChangeTitle": "Изменение адреса электронной почты",
|
||||
"EnterEmail": "Введите новый адрес электронной почты",
|
||||
"EmailActivationDescription": "Инструкции по активации будут отправлены на введённый адрес электронной почты",
|
||||
"SendButton": "Отправить",
|
||||
|
||||
"SameEmail": "Email совпадает с текущим",
|
||||
"LocalDomain": "Псевдодомен верхнего уровня не поддерживается",
|
||||
"IncorrectDomain": "Некорректный домен",
|
||||
"DomainIpAddress": "IP адрес в качестве домена не поддерживается",
|
||||
"PunycodeDomain": "Домен содержит запрещенные символы",
|
||||
"PunycodeLocalPart": "Имя почтового ящика содержит запрещенные символы",
|
||||
"IncorrectLocalPart": "Некорректное имя почтового ящика",
|
||||
"SpacesInLocalPart": "Имя почтового ящика содержит пробелы",
|
||||
"MaxLengthExceeded": "Максимальная длина имени почтового ящика - 64 символа.",
|
||||
"IncorrectEmail": "Некорректный email",
|
||||
"ManyEmails": "Поле содержит больше одного email",
|
||||
"EmptyEmail": "Поле email пустое"
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === 'lowercase') return value.toLowerCase();
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/ChangePasswordDialog/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
},
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,100 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import {
|
||||
toastr,
|
||||
ModalDialog,
|
||||
Button,
|
||||
Link,
|
||||
Text
|
||||
} from "asc-web-components";
|
||||
import { withTranslation, Trans } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import { api } from "asc-web-common";
|
||||
const { sendInstructionsToChangePassword } = api.people;
|
||||
|
||||
class ChangePasswordDialogComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { language } = props;
|
||||
|
||||
this.state = {
|
||||
isRequestRunning: false
|
||||
};
|
||||
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
onSendPasswordChangeInstructions = () => {
|
||||
const { email, onClose } = this.props;
|
||||
this.setState({ isRequestRunning: true }, () => {
|
||||
sendInstructionsToChangePassword(email)
|
||||
.then((res) => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch((error) => toastr.error(error))
|
||||
.finally(() => {
|
||||
this.setState({ isRequestRunning: false }, () => onClose());
|
||||
});
|
||||
})
|
||||
|
||||
}
|
||||
|
||||
|
||||
render() {
|
||||
console.log("ChangePasswordDialog render");
|
||||
const { t, visible, email, onClose } = this.props;
|
||||
const { isRequestRunning } = this.state;
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
headerContent={t('PasswordChangeTitle')}
|
||||
bodyContent={
|
||||
<Text fontSize='13px'>
|
||||
<Trans i18nKey="MessageSendPasswordChangeInstructionsOnEmail" i18n={i18n}>
|
||||
Send the password change instructions to the
|
||||
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
|
||||
{{ email }}
|
||||
</Link>
|
||||
email address
|
||||
</Trans>
|
||||
</Text>
|
||||
|
||||
}
|
||||
footerContent={
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label={t('SendButton')}
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onSendPasswordChangeInstructions}
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ChangePasswordDialogTranslated = withTranslation()(ChangePasswordDialogComponent);
|
||||
|
||||
const ChangePasswordDialog = props => (
|
||||
<ChangePasswordDialogTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
ChangePasswordDialog.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
email: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
language: state.auth.user.cultureName,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {})(withRouter(ChangePasswordDialog));
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"PasswordChangeTitle": "Password change",
|
||||
"MessageSendPasswordChangeInstructionsOnEmail": "Send the password change instructions to the <1>{{email}}</1> email address",
|
||||
"SendButton": "Send"
|
||||
}
|
@ -0,0 +1,5 @@
|
||||
{
|
||||
"PasswordChangeTitle": "Изменение пароля",
|
||||
"MessageSendPasswordChangeInstructionsOnEmail": "Отправить инструкции по смене пароля на адрес <1>{{email}}</1>",
|
||||
"SendButton": "Отправить"
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === 'lowercase') return value.toLowerCase();
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/ChangePhoneDialog/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
},
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,87 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import {
|
||||
toastr,
|
||||
ModalDialog,
|
||||
Button,
|
||||
Text
|
||||
} from "asc-web-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import { api } from "asc-web-common";
|
||||
|
||||
class ChangePhoneDialogComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { language } = props;
|
||||
|
||||
this.state = {
|
||||
isRequestRunning: false
|
||||
};
|
||||
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
|
||||
// TODO: add real api request for executing change phone
|
||||
onChangePhone = () => {
|
||||
const { onClose } = this.props;
|
||||
this.setState({ isRequestRunning: true }, () => {
|
||||
toastr.success("Context action: Change phone");
|
||||
this.setState({ isRequestRunning: false }, () => onClose());
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
|
||||
render() {
|
||||
console.log("ChangePhoneDialog render");
|
||||
const { t, visible, onClose } = this.props;
|
||||
const { isRequestRunning } = this.state;
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
headerContent={t('MobilePhoneChangeTitle')}
|
||||
bodyContent={
|
||||
<Text>
|
||||
{t('MessageChangePhone')}
|
||||
</Text>
|
||||
}
|
||||
footerContent={
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label={t('SendButton')}
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onChangePhone}
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const ChangePhoneDialogTranslated = withTranslation()(ChangePhoneDialogComponent);
|
||||
|
||||
const ChangePhoneDialog = props => (
|
||||
<ChangePhoneDialogTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
ChangePhoneDialog.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
language: state.auth.user.cultureName,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {})(withRouter(ChangePhoneDialog));
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"SendButton": "Send",
|
||||
"MobilePhoneChangeTitle": "Change mobile phone",
|
||||
|
||||
"MessageChangePhone": "The instructions on how to change the user mobile number will be sent to the user email address"
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"SendButton": "Отправить",
|
||||
"MobilePhoneChangeTitle": "Изменение номера телефона",
|
||||
|
||||
"MessageChangePhone": "Инструкция по смене номера мобильного телефона будет отправлена на Вашу почту"
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === 'lowercase') return value.toLowerCase();
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/DeleteProfileEverDialog/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
},
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,127 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import {
|
||||
toastr,
|
||||
ModalDialog,
|
||||
Button,
|
||||
Text
|
||||
} from "asc-web-components";
|
||||
import { withTranslation, Trans } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import { api } from "asc-web-common";
|
||||
import { typeUser } from "../../../helpers/customNames";
|
||||
import { fetchPeople } from '../../../store/people/actions';
|
||||
import ModalDialogContainer from '../ModalDialogContainer';
|
||||
const { deleteUser } = api.people;
|
||||
const { Filter } = api;
|
||||
|
||||
class DeleteProfileEverDialogComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { language } = props;
|
||||
|
||||
this.state = {
|
||||
isRequestRunning: false
|
||||
};
|
||||
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
onDeleteProfileEver = () => {
|
||||
const { onClose, filter, fetchPeople, user, t } = this.props;
|
||||
this.setState({ isRequestRunning: true }, () => {
|
||||
deleteUser(user.id)
|
||||
.then((res) => {
|
||||
toastr.success(t('SuccessfullyDeleteUserInfoMessage'));
|
||||
return fetchPeople(filter);
|
||||
})
|
||||
.catch((error) => toastr.error(error))
|
||||
.finally(() => {
|
||||
this.setState({ isRequestRunning: false }, () => onClose());
|
||||
});
|
||||
})
|
||||
};
|
||||
|
||||
onReassignDataClick = () => {
|
||||
const { history, settings, user } = this.props;
|
||||
history.push(`${settings.homepage}/reassign/${user.userName}`)
|
||||
};
|
||||
|
||||
render() {
|
||||
console.log("DeleteProfileEverDialog render");
|
||||
const { t, visible, user, onClose } = this.props;
|
||||
const { isRequestRunning } = this.state;
|
||||
|
||||
return (
|
||||
<ModalDialogContainer>
|
||||
<ModalDialog
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
headerContent={t('Confirmation')}
|
||||
bodyContent={
|
||||
<>
|
||||
<Text>
|
||||
<Trans i18nKey='DeleteUserConfirmation' i18n={i18n}>
|
||||
{{ typeUser }} <strong>{{ user: user.displayName }}</strong> will be deleted.
|
||||
</Trans>
|
||||
</Text>
|
||||
<Text>{t('NotBeUndone')}</Text>
|
||||
<Text color="#c30" fontSize="18px" className='warning-text'>
|
||||
{t('Warning')}
|
||||
</Text>
|
||||
<Text>
|
||||
{t('DeleteUserDataConfirmation')}
|
||||
</Text>
|
||||
</>
|
||||
}
|
||||
footerContent={
|
||||
<>
|
||||
<Button
|
||||
key="OKBtn"
|
||||
label={t('OKButton')}
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onDeleteProfileEver}
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
<Button
|
||||
className='button-dialog'
|
||||
key="ReassignBtn"
|
||||
label={t('ReassignData')}
|
||||
size="medium"
|
||||
onClick={this.onReassignDataClick}
|
||||
isDisabled={isRequestRunning}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
const DeleteProfileEverDialogTranslated = withTranslation()(DeleteProfileEverDialogComponent);
|
||||
|
||||
const DeleteProfileEverDialog = props => (
|
||||
<DeleteProfileEverDialogTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
DeleteProfileEverDialog.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
user: PropTypes.object.isRequired,
|
||||
filter: PropTypes.instanceOf(Filter).isRequired,
|
||||
fetchPeople: PropTypes.func.isRequired,
|
||||
settings: PropTypes.object.isRequired,
|
||||
history: PropTypes.object.isRequired
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
language: state.auth.user.cultureName,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, { fetchPeople })(withRouter(DeleteProfileEverDialog));
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"Confirmation": "Confirmation",
|
||||
"DeleteUserConfirmation": "{{typeUser}} <strong>{{user}}</strong> will be deleted.",
|
||||
"NotBeUndone": "Note: this action cannot be undone.",
|
||||
"DeleteUserDataConfirmation": "User's personal documents that are available to others will be deleted. To avoid this, you must start the data reassign process before deleting.",
|
||||
"OKButton": "OK",
|
||||
"ReassignData": "Reassign data",
|
||||
"Warning": "Warning!",
|
||||
"SuccessfullyDeleteUserInfoMessage": "User has been successfully deleted"
|
||||
}
|
@ -0,0 +1,10 @@
|
||||
{
|
||||
"Confirmation": "Подтверждение",
|
||||
"DeleteUserConfirmation": "{{typeUser}} <strong>{{user}}</strong> будет удален.",
|
||||
"NotBeUndone": "Внимание: это действие необратимо.",
|
||||
"DeleteUserDataConfirmation": "Будут удалены личные документы пользователя, доступные для других. Чтобы избежать этого, нужно перед удалением запустить процесс передачи данных.",
|
||||
"OKButton": "OK",
|
||||
"ReassignData": "Передать данные",
|
||||
"Warning": "Внимание!",
|
||||
"SuccessfullyDeleteUserInfoMessage": "Пользователь успешно удален"
|
||||
}
|
@ -0,0 +1,56 @@
|
||||
import i18n from "i18next";
|
||||
import Backend from "i18next-xhr-backend";
|
||||
import config from "../../../../package.json";
|
||||
|
||||
const newInstance = i18n.createInstance();
|
||||
|
||||
if (process.env.NODE_ENV === "production") {
|
||||
newInstance
|
||||
.use(Backend)
|
||||
.init({
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
format: function (value, format) {
|
||||
if (format === 'lowercase') return value.toLowerCase();
|
||||
return value;
|
||||
}
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/DeleteSelfProfileDialog/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
},
|
||||
};
|
||||
|
||||
newInstance.init({
|
||||
resources: resources,
|
||||
lng: 'en',
|
||||
fallbackLng: "en",
|
||||
debug: true,
|
||||
|
||||
interpolation: {
|
||||
escapeValue: false, // not needed for react as it escapes by default
|
||||
},
|
||||
|
||||
react: {
|
||||
useSuspense: true
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
export default newInstance;
|
@ -0,0 +1,107 @@
|
||||
import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import PropTypes from "prop-types";
|
||||
import { withRouter } from "react-router";
|
||||
import {
|
||||
toastr,
|
||||
ModalDialog,
|
||||
Button,
|
||||
Link,
|
||||
Text
|
||||
} from "asc-web-components";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import ModalDialogContainer from '../ModalDialogContainer';
|
||||
import { api } from "asc-web-common";
|
||||
const { sendInstructionsToDelete } = api.people;
|
||||
|
||||
class DeleteSelfProfileDialogComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { language } = props;
|
||||
|
||||
this.state = {
|
||||
isRequestRunning: false
|
||||
};
|
||||
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
onDeleteSelfProfileInstructions = () => {
|
||||
const { onClose } = this.props;
|
||||
this.setState({ isRequestRunning: true }, () => {
|
||||
sendInstructionsToDelete()
|
||||
.then((res) => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch((error) => toastr.error(error))
|
||||
.finally(() => {
|
||||
this.setState({ isRequestRunning: false }, () => onClose());
|
||||
});
|
||||
})
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("DeleteSelfProfileDialog render");
|
||||
const { t, visible, email, onClose } = this.props;
|
||||
const { isRequestRunning } = this.state;
|
||||
|
||||
return (
|
||||
<ModalDialogContainer>
|
||||
<ModalDialog
|
||||
visible={visible}
|
||||
onClose={onClose}
|
||||
headerContent={t('DeleteProfileTitle')}
|
||||
bodyContent={
|
||||
<Text fontSize='13px'>
|
||||
{t('DeleteProfileInfo')} <Link type="page" href={`mailto:${email}`} isHovered title={email}>
|
||||
{email}
|
||||
</Link>
|
||||
</Text>
|
||||
}
|
||||
footerContent={
|
||||
<>
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label={t('SendButton')}
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onDeleteSelfProfileInstructions}
|
||||
isLoading={isRequestRunning}
|
||||
/>
|
||||
<Button
|
||||
className='button-dialog'
|
||||
key="CloseBtn"
|
||||
label={t('CloseButton')}
|
||||
size="medium"
|
||||
onClick={onClose}
|
||||
isDisabled={isRequestRunning}
|
||||
/>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
const DeleteSelfProfileDialogTranslated = withTranslation()(DeleteSelfProfileDialogComponent);
|
||||
|
||||
const DeleteSelfProfileDialog = props => (
|
||||
<DeleteSelfProfileDialogTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
DeleteSelfProfileDialog.propTypes = {
|
||||
visible: PropTypes.bool.isRequired,
|
||||
onClose: PropTypes.func.isRequired,
|
||||
email: PropTypes.string.isRequired,
|
||||
};
|
||||
|
||||
function mapStateToProps(state) {
|
||||
return {
|
||||
language: state.auth.user.cultureName,
|
||||
};
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, {})(withRouter(DeleteSelfProfileDialog));
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"DeleteProfileTitle": "Delete profile dialog",
|
||||
"DeleteProfileInfo": "Send the profile deletion instructions to the email address",
|
||||
"SendButton": "Send",
|
||||
"CloseButton": "Close"
|
||||
}
|
@ -0,0 +1,6 @@
|
||||
{
|
||||
"DeleteProfileTitle": "Диалог удаления профиля",
|
||||
"DeleteProfileInfo": "Отправить инструкции по удалению профиля на адрес электронной почты",
|
||||
"SendButton": "Отправить",
|
||||
"CloseButton": "Закрыть"
|
||||
}
|
@ -23,7 +23,7 @@ if (process.env.NODE_ENV === "production") {
|
||||
useSuspense: true
|
||||
},
|
||||
backend: {
|
||||
loadPath: `${config.homepage}/locales/Invite/{{lng}}/{{ns}}.json`
|
||||
loadPath: `${config.homepage}/locales/InviteDialog/{{lng}}/{{ns}}.json`
|
||||
}
|
||||
});
|
||||
} else if (process.env.NODE_ENV === "development") {
|
||||
@ -31,6 +31,9 @@ if (process.env.NODE_ENV === "production") {
|
||||
const resources = {
|
||||
en: {
|
||||
translation: require("./locales/en/translation.json")
|
||||
},
|
||||
ru: {
|
||||
translation: require("./locales/ru/translation.json")
|
||||
}
|
||||
};
|
||||
|
@ -11,46 +11,31 @@ import {
|
||||
Textarea,
|
||||
Text
|
||||
} from "asc-web-components";
|
||||
import { withTranslation, I18nextProvider } from "react-i18next";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import i18n from "./i18n";
|
||||
import { typeGuests } from "./../../../helpers/customNames";
|
||||
import styled from "styled-components";
|
||||
import ModalDialogContainer from '../ModalDialogContainer';
|
||||
import copy from "copy-to-clipboard";
|
||||
import { api } from "asc-web-common";
|
||||
const { getShortenedLink } = api.portal;
|
||||
|
||||
const ModalDialogContainer = styled.div`
|
||||
.margin-text {
|
||||
margin: 12px 0;
|
||||
}
|
||||
|
||||
.margin-link {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.margin-textarea {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
`;
|
||||
|
||||
const textAreaName = "link-textarea";
|
||||
|
||||
class PureInviteDialog extends React.Component {
|
||||
class InviteDialogComponent extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { language, userInvitationLink, guestInvitationLink } = this.props;
|
||||
this.state = {
|
||||
isGuest: false,
|
||||
userInvitationLink: this.props.userInvitationLink,
|
||||
guestInvitationLink: this.props.guestInvitationLink,
|
||||
userInvitationLink,
|
||||
guestInvitationLink,
|
||||
isLoading: false,
|
||||
isLinkShort: false,
|
||||
visible: false
|
||||
};
|
||||
|
||||
i18n.changeLanguage(language);
|
||||
}
|
||||
|
||||
onCopyLinkToClipboard = () => {
|
||||
@ -94,7 +79,7 @@ class PureInviteDialog extends React.Component {
|
||||
};
|
||||
|
||||
componentDidMount() {
|
||||
this.onCopyLinkToClipboard();
|
||||
this.onCopyLinkToClipboard();
|
||||
}
|
||||
|
||||
onClickToCloseButton = () =>
|
||||
@ -113,16 +98,16 @@ class PureInviteDialog extends React.Component {
|
||||
headerContent={t("InviteLinkTitle")}
|
||||
bodyContent={
|
||||
<>
|
||||
<Text className="margin-text" as="p">
|
||||
<Text as="p">
|
||||
{t("HelpAnswerLinkInviteSettings")}
|
||||
</Text>
|
||||
<Text className="margin-text" as="p">
|
||||
<Text className="text-dialog" as="p">
|
||||
{t("InviteLinkValidInterval", { count: 7 })}
|
||||
</Text>
|
||||
<div className="flex">
|
||||
<div>
|
||||
<Link
|
||||
className="margin-link"
|
||||
className="link-dialog"
|
||||
type="action"
|
||||
isHovered={true}
|
||||
onClick={this.onCopyLinkToClipboard}
|
||||
@ -147,7 +132,7 @@ class PureInviteDialog extends React.Component {
|
||||
/>
|
||||
</div>
|
||||
<Textarea
|
||||
className="margin-textarea"
|
||||
className="textarea-dialog"
|
||||
isReadOnly={true}
|
||||
isDisabled={this.state.isLoading}
|
||||
name={textAreaName}
|
||||
@ -185,16 +170,15 @@ const mapStateToProps = state => {
|
||||
return {
|
||||
settings: state.auth.settings.hasShortenService,
|
||||
userInvitationLink: state.portal.inviteLinks.userLink,
|
||||
guestInvitationLink: state.portal.inviteLinks.guestLink
|
||||
guestInvitationLink: state.portal.inviteLinks.guestLink,
|
||||
language: state.auth.user.cultureName,
|
||||
};
|
||||
};
|
||||
|
||||
const InviteDialogContainer = withTranslation()(PureInviteDialog);
|
||||
const InviteDialogTranslated = withTranslation()(InviteDialogComponent);
|
||||
|
||||
const InviteDialog = props => (
|
||||
<I18nextProvider i18n={i18n}>
|
||||
<InviteDialogContainer {...props} />
|
||||
</I18nextProvider>
|
||||
<InviteDialogTranslated i18n={i18n} {...props} />
|
||||
);
|
||||
|
||||
InviteDialog.propTypes = {
|
@ -0,0 +1,13 @@
|
||||
{
|
||||
"InviteLinkTitle": "Пригласительная ссылка",
|
||||
"HelpAnswerLinkInviteSettings": "Поделитесь ссылкой, чтобы пригласить коллег на портал.",
|
||||
"InviteLinkValidInterval": "Эта ссылка действительна только в течение {{ count }} дня.",
|
||||
"CopyToClipboard": "Копировать ссылку",
|
||||
"CloseButton": "Закрыть",
|
||||
"LinkCopySuccess": "Ссылка скопирована в буфер обмена",
|
||||
"GetShortenLink": "Получить сокращенную ссылку",
|
||||
"InviteUsersAsCollaborators": "Добавить со статусом {{typeGuests, lowercase}}",
|
||||
"LoadingProcessing": "Загрузка...",
|
||||
|
||||
"InviteLinkValidInterval_plural": "Эта ссылка действительна только в течение {{ count }} дней."
|
||||
}
|
@ -0,0 +1,44 @@
|
||||
import styled from 'styled-components';
|
||||
|
||||
const ModalDialogContainer = styled.div`
|
||||
|
||||
.flex {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
}
|
||||
|
||||
.text-dialog {
|
||||
margin: 16px 0;
|
||||
}
|
||||
|
||||
.input-dialog {
|
||||
margin-top: 16px;
|
||||
}
|
||||
|
||||
.button-dialog {
|
||||
margin-left: 8px;
|
||||
}
|
||||
|
||||
.warning-text {
|
||||
margin: 20px 0;
|
||||
}
|
||||
|
||||
.textarea-dialog {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.link-dialog {
|
||||
margin-right: 12px;
|
||||
}
|
||||
|
||||
.error-label {
|
||||
position: absolute;
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.field-body {
|
||||
position: relative;
|
||||
}
|
||||
`;
|
||||
|
||||
export default ModalDialogContainer;
|
15
products/ASC.People/Client/src/components/dialogs/index.js
Normal file
15
products/ASC.People/Client/src/components/dialogs/index.js
Normal file
@ -0,0 +1,15 @@
|
||||
import ChangeEmailDialog from "./ChangeEmailDialog";
|
||||
import ChangePasswordDialog from "./ChangePasswordDialog";
|
||||
import ChangePhoneDialog from "./ChangePhoneDialog";
|
||||
import DeleteProfileEverDialog from "./DeleteProfileEverDialog";
|
||||
import DeleteSelfProfileDialog from "./DeleteSelfProfileDialog";
|
||||
import InviteDialog from './InviteDialog';
|
||||
|
||||
export {
|
||||
ChangeEmailDialog,
|
||||
ChangePasswordDialog,
|
||||
ChangePhoneDialog,
|
||||
DeleteProfileEverDialog,
|
||||
DeleteSelfProfileDialog,
|
||||
InviteDialog,
|
||||
};
|
@ -11,10 +11,7 @@ import {
|
||||
Link,
|
||||
RowContainer,
|
||||
ModalDialog,
|
||||
Button,
|
||||
Text,
|
||||
Label,
|
||||
TextInput
|
||||
} from "asc-web-components";
|
||||
import UserContent from "./userContent";
|
||||
import {
|
||||
@ -34,8 +31,9 @@ import { isMobileOnly } from "react-device-detect";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import { store, api, constants } from 'asc-web-common';
|
||||
import i18n from '../../i18n';
|
||||
import { ChangeEmailDialog, ChangePasswordDialog, DeleteSelfProfileDialog, DeleteProfileEverDialog } from '../../../../dialogs';
|
||||
const { isAdmin, isMe } = store.auth.selectors;
|
||||
const { resendUserInvites, sendInstructionsToDelete, sendInstructionsToChangePassword, deleteUser } = api.people;
|
||||
const { resendUserInvites } = api.people;
|
||||
const { EmployeeStatus } = constants;
|
||||
|
||||
|
||||
@ -44,13 +42,19 @@ class SectionBodyContent extends React.PureComponent {
|
||||
super(props);
|
||||
|
||||
this.state = {
|
||||
newEmail: null,
|
||||
dialog: {
|
||||
visible: false,
|
||||
header: "",
|
||||
body: "",
|
||||
buttons: []
|
||||
}
|
||||
buttons: [],
|
||||
},
|
||||
dialogsVisible: {
|
||||
changeEmail: false,
|
||||
changePassword: false,
|
||||
deleteSelfProfile: false,
|
||||
deleteProfileEver: false,
|
||||
},
|
||||
isEmailValid: false
|
||||
};
|
||||
}
|
||||
|
||||
@ -67,100 +71,21 @@ class SectionBodyContent extends React.PureComponent {
|
||||
history.push(`${settings.homepage}/edit/${user.userName}`);
|
||||
};
|
||||
|
||||
onChangePasswordClick = email => {
|
||||
toggleChangePasswordDialog = (email) => {
|
||||
const checkedEmail = typeof (email) === 'string' ? email : undefined;
|
||||
this.setState({
|
||||
dialog: {
|
||||
visible: true,
|
||||
header: "Password change",
|
||||
body: (
|
||||
<Text>
|
||||
Send the password change instructions to the{" "}
|
||||
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
|
||||
{email}
|
||||
</Link>{" "}
|
||||
email address
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="OkBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
const { onLoading } = this.props;
|
||||
onLoading(true);
|
||||
sendInstructionsToChangePassword(email)
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
<Text>
|
||||
The password change instructions have been sent to the{" "}
|
||||
<b>{email}</b> email address
|
||||
</Text>
|
||||
)
|
||||
)
|
||||
.catch(error => toastr.error(error))
|
||||
.finally(() => onLoading(false));
|
||||
this.onDialogClose();
|
||||
}}
|
||||
/>,
|
||||
<Button
|
||||
key="CancelBtn"
|
||||
label="Cancel"
|
||||
size="medium"
|
||||
primary={false}
|
||||
onClick={this.onDialogClose}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
]
|
||||
}
|
||||
dialogsVisible: { ...this.state.dialogsVisible, changePassword: !this.state.dialogsVisible.changePassword },
|
||||
user: { email: checkedEmail }
|
||||
});
|
||||
};
|
||||
|
||||
onChangeEmailClick = email => {
|
||||
toggleChangeEmailDialog = (user) => {
|
||||
const checkedUser = user ? user : {};
|
||||
this.setState({
|
||||
dialog: {
|
||||
visible: true,
|
||||
header: "Email change",
|
||||
body: (
|
||||
<>
|
||||
<Label htmlFor="new-email" text="Enter a new email address" />
|
||||
<TextInput
|
||||
id="new-email"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
value={this.state.newEmail}
|
||||
onChange={e => {
|
||||
this.setState({ newEmail: e.target.value });
|
||||
}}
|
||||
/>
|
||||
<Text style={{ marginTop: "16px" }}>
|
||||
The activation instructions will be sent to the entered email
|
||||
</Text>
|
||||
</>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="OkBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
toastr.success(
|
||||
`Context action: Change e-mail from ${email} to ${this.state.newEmail}`
|
||||
);
|
||||
this.onDialogClose();
|
||||
}}
|
||||
/>,
|
||||
<Button
|
||||
key="CancelBtn"
|
||||
label="Cancel"
|
||||
size="medium"
|
||||
primary={false}
|
||||
onClick={this.onDialogClose}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
]
|
||||
dialogsVisible: { ...this.state.dialogsVisible, changeEmail: !this.state.dialogsVisible.changeEmail },
|
||||
user: {
|
||||
email: checkedUser.email,
|
||||
id: checkedUser.id
|
||||
}
|
||||
});
|
||||
};
|
||||
@ -194,116 +119,23 @@ class SectionBodyContent extends React.PureComponent {
|
||||
toastr.success("Context action: Delete personal data");
|
||||
};
|
||||
|
||||
onDeleteProfileEverClick = user => {
|
||||
toggleDeleteProfileEverDialog = user => {
|
||||
const checkedUser = user ? user : {};
|
||||
this.setState({
|
||||
dialog: {
|
||||
visible: true,
|
||||
header: "Confirmation",
|
||||
body: (
|
||||
<>
|
||||
<Text>
|
||||
User <b>{user.displayName}</b> will be deleted.
|
||||
</Text>
|
||||
<Text>Note: this action cannot be undone.</Text>
|
||||
<Text color="#c30" fontSize="18" style={{ margin: "20px 0" }}>
|
||||
Warning!
|
||||
</Text>
|
||||
<Text>
|
||||
User personal documents which are available to others will be
|
||||
deleted. To avoid this, you must start the data reassign process
|
||||
before deleting.
|
||||
</Text>
|
||||
</>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="OkBtn"
|
||||
label="OK"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
const { onLoading, filter, fetchPeople } = this.props;
|
||||
onLoading(true);
|
||||
deleteUser(user.id)
|
||||
.then(() => {
|
||||
toastr.success("User has been removed successfully");
|
||||
return fetchPeople(filter);
|
||||
})
|
||||
.catch(error => toastr.error(error))
|
||||
.finally(() => onLoading(false));
|
||||
this.onDialogClose();
|
||||
}}
|
||||
/>,
|
||||
<Button
|
||||
key="ReassignBtn"
|
||||
label="Reassign data"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
toastr.success("Context action: Reassign profile");
|
||||
this.onDialogClose();
|
||||
}}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>,
|
||||
<Button
|
||||
key="CancelBtn"
|
||||
label="Cancel"
|
||||
size="medium"
|
||||
primary={false}
|
||||
onClick={this.onDialogClose}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
]
|
||||
dialogsVisible: { ...this.state.dialogsVisible, deleteProfileEver: !this.state.dialogsVisible.deleteProfileEver },
|
||||
user: {
|
||||
id: checkedUser.id,
|
||||
displayName: checkedUser.displayName,
|
||||
userName: checkedUser.userName,
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
onDeleteSelfProfileClick = email => {
|
||||
toggleDeleteSelfProfileDialog = email => {
|
||||
const checkedEmail = typeof (email) === 'string' ? email : undefined;
|
||||
this.setState({
|
||||
dialog: {
|
||||
visible: true,
|
||||
header: "Delete profile dialog",
|
||||
body: (
|
||||
<Text>
|
||||
Send the profile deletion instructions to the email address{" "}
|
||||
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
|
||||
{email}
|
||||
</Link>
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="OkBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
const { onLoading } = this.props;
|
||||
onLoading(true);
|
||||
sendInstructionsToDelete()
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
<Text>
|
||||
Instructions to delete your profile has been sent to{" "}
|
||||
<b>{email}</b> email address
|
||||
</Text>
|
||||
)
|
||||
)
|
||||
.catch(error => toastr.error(error))
|
||||
.finally(() => onLoading(false));
|
||||
this.onDialogClose();
|
||||
}}
|
||||
/>,
|
||||
<Button
|
||||
key="CancelBtn"
|
||||
label="Cancel"
|
||||
size="medium"
|
||||
primary={false}
|
||||
onClick={this.onDialogClose}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
]
|
||||
}
|
||||
dialogsVisible: { ...this.state.dialogsVisible, deleteSelfProfile: !this.state.dialogsVisible.deleteSelfProfile },
|
||||
user: { email: checkedEmail }
|
||||
});
|
||||
};
|
||||
|
||||
@ -347,11 +179,11 @@ class SectionBodyContent extends React.PureComponent {
|
||||
onClick: this.onEmailSentClick.bind(this, user.email)
|
||||
},
|
||||
user.mobilePhone &&
|
||||
isMobileOnly && {
|
||||
key: "send-message",
|
||||
label: t("LblSendMessage"),
|
||||
onClick: this.onSendMessageClick.bind(this, user.mobilePhone)
|
||||
},
|
||||
isMobileOnly && {
|
||||
key: "send-message",
|
||||
label: t("LblSendMessage"),
|
||||
onClick: this.onSendMessageClick.bind(this, user.mobilePhone)
|
||||
},
|
||||
{ key: "separator", isSeparator: true },
|
||||
{
|
||||
key: "edit",
|
||||
@ -361,26 +193,26 @@ class SectionBodyContent extends React.PureComponent {
|
||||
{
|
||||
key: "change-password",
|
||||
label: t("PasswordChangeButton"),
|
||||
onClick: this.onChangePasswordClick.bind(this, user.email)
|
||||
onClick: this.toggleChangePasswordDialog.bind(this, user.email)
|
||||
},
|
||||
{
|
||||
key: "change-email",
|
||||
label: t("EmailChangeButton"),
|
||||
onClick: this.onChangeEmailClick.bind(this, user.email)
|
||||
onClick: this.toggleChangeEmailDialog.bind(this, user)
|
||||
},
|
||||
isSelf
|
||||
? viewer.isOwner
|
||||
? {}
|
||||
? viewer.isOwner
|
||||
? {}
|
||||
: {
|
||||
key: "delete-profile",
|
||||
label: t("DeleteSelfProfile"),
|
||||
onClick: this.onDeleteSelfProfileClick.bind(this, user.email)
|
||||
onClick: this.toggleDeleteSelfProfileDialog.bind(this, user.email)
|
||||
}
|
||||
: {
|
||||
key: "disable",
|
||||
label: t("DisableUserButton"),
|
||||
onClick: this.onDisableClick.bind(this, user)
|
||||
}
|
||||
key: "disable",
|
||||
label: t("DisableUserButton"),
|
||||
onClick: this.onDisableClick.bind(this, user)
|
||||
}
|
||||
];
|
||||
case "disabled":
|
||||
return [
|
||||
@ -402,7 +234,7 @@ class SectionBodyContent extends React.PureComponent {
|
||||
{
|
||||
key: "delete-profile",
|
||||
label: t("DeleteSelfProfile"),
|
||||
onClick: this.onDeleteProfileEverClick.bind(this, user)
|
||||
onClick: this.toggleDeleteProfileEverDialog.bind(this, user)
|
||||
}
|
||||
];
|
||||
case "pending":
|
||||
@ -418,21 +250,21 @@ class SectionBodyContent extends React.PureComponent {
|
||||
onClick: this.onInviteAgainClick.bind(this, user)
|
||||
},
|
||||
!isSelf &&
|
||||
(user.status === EmployeeStatus.Active
|
||||
? {
|
||||
key: "disable",
|
||||
label: t("DisableUserButton"),
|
||||
onClick: this.onDisableClick.bind(this, user)
|
||||
}
|
||||
: {
|
||||
key: "enable",
|
||||
label: t("EnableUserButton"),
|
||||
onClick: this.onEnableClick.bind(this, user)
|
||||
}),
|
||||
(user.status === EmployeeStatus.Active
|
||||
? {
|
||||
key: "disable",
|
||||
label: t("DisableUserButton"),
|
||||
onClick: this.onDisableClick.bind(this, user)
|
||||
}
|
||||
: {
|
||||
key: "enable",
|
||||
label: t("EnableUserButton"),
|
||||
onClick: this.onEnableClick.bind(this, user)
|
||||
}),
|
||||
isSelf && {
|
||||
key: "delete-profile",
|
||||
label: t("DeleteSelfProfile"),
|
||||
onClick: this.onDeleteSelfProfileClick.bind(this, user.email)
|
||||
onClick: this.toggleDeleteSelfProfileDialog.bind(this, user.email)
|
||||
}
|
||||
];
|
||||
default:
|
||||
@ -456,9 +288,8 @@ class SectionBodyContent extends React.PureComponent {
|
||||
};
|
||||
|
||||
onDialogClose = () => {
|
||||
this.setState({
|
||||
newEmail: null,
|
||||
dialog: { visible: false }
|
||||
this.setState({
|
||||
dialog: { visible: false }
|
||||
});
|
||||
};
|
||||
|
||||
@ -477,8 +308,8 @@ class SectionBodyContent extends React.PureComponent {
|
||||
|
||||
render() {
|
||||
console.log("Home SectionBodyContent render()");
|
||||
const { users, viewer, selection, history, settings, t } = this.props;
|
||||
const { dialog } = this.state;
|
||||
const { users, viewer, selection, history, settings, t, filter } = this.props;
|
||||
const { dialog, dialogsVisible, user } = this.state;
|
||||
|
||||
return users.length > 0 ? (
|
||||
<>
|
||||
@ -526,23 +357,57 @@ class SectionBodyContent extends React.PureComponent {
|
||||
footerContent={dialog.buttons}
|
||||
onClose={this.onDialogClose}
|
||||
/>
|
||||
|
||||
{dialogsVisible.changeEmail &&
|
||||
<ChangeEmailDialog
|
||||
visible={dialogsVisible.changeEmail}
|
||||
onClose={this.toggleChangeEmailDialog}
|
||||
user={user}
|
||||
/>
|
||||
}
|
||||
{dialogsVisible.changePassword &&
|
||||
<ChangePasswordDialog
|
||||
visible={dialogsVisible.changePassword}
|
||||
onClose={this.toggleChangePasswordDialog}
|
||||
email={user.email}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.deleteSelfProfile &&
|
||||
<DeleteSelfProfileDialog
|
||||
visible={dialogsVisible.deleteSelfProfile}
|
||||
onClose={this.toggleDeleteSelfProfileDialog}
|
||||
email={user.email}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.deleteProfileEver &&
|
||||
<DeleteProfileEverDialog
|
||||
visible={dialogsVisible.deleteProfileEver}
|
||||
onClose={this.toggleDeleteProfileEverDialog}
|
||||
user={user}
|
||||
filter={filter}
|
||||
settings={settings}
|
||||
history={history}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
) : (
|
||||
<EmptyScreenContainer
|
||||
imageSrc="images/empty_screen_filter.png"
|
||||
imageAlt="Empty Screen Filter image"
|
||||
headerText={t("NotFoundTitle")}
|
||||
descriptionText={t("NotFoundDescription")}
|
||||
buttons={
|
||||
<>
|
||||
<Icons.CrossIcon size="small" style={{ marginRight: "4px" }} />
|
||||
<Link type="action" isHovered={true} onClick={this.onResetFilter}>
|
||||
{t("ClearButton")}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
<EmptyScreenContainer
|
||||
imageSrc="images/empty_screen_filter.png"
|
||||
imageAlt="Empty Screen Filter image"
|
||||
headerText={t("NotFoundTitle")}
|
||||
descriptionText={t("NotFoundDescription")}
|
||||
buttons={
|
||||
<>
|
||||
<Icons.CrossIcon size="small" style={{ marginRight: "4px" }} />
|
||||
<Link type="action" isHovered={true} onClick={this.onResetFilter}>
|
||||
{t("ClearButton")}
|
||||
</Link>
|
||||
</>
|
||||
}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -2,14 +2,10 @@ import React from "react";
|
||||
import { connect } from "react-redux";
|
||||
import {
|
||||
Text,
|
||||
Link,
|
||||
IconButton,
|
||||
ContextMenuButton,
|
||||
toastr,
|
||||
utils,
|
||||
TextInput,
|
||||
Button,
|
||||
ModalDialog,
|
||||
AvatarEditor,
|
||||
} from "asc-web-components";
|
||||
import { Headline } from 'asc-web-common';
|
||||
@ -20,22 +16,18 @@ import {
|
||||
} from "../../../../../store/people/selectors";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import {
|
||||
updateUserStatus,
|
||||
fetchPeople
|
||||
updateUserStatus
|
||||
} from "../../../../../store/people/actions";
|
||||
import { fetchProfile, getUserPhoto } from "../../../../../store/profile/actions";
|
||||
import styled from "styled-components";
|
||||
import { store, api, constants } from "asc-web-common";
|
||||
import { DeleteSelfProfileDialog, ChangePasswordDialog, ChangeEmailDialog, DeleteProfileEverDialog } from '../../../../dialogs';
|
||||
const { isAdmin, isMe } = store.auth.selectors;
|
||||
const {
|
||||
resendUserInvites,
|
||||
sendInstructionsToDelete,
|
||||
sendInstructionsToChangePassword,
|
||||
sendInstructionsToChangeEmail,
|
||||
createThumbnailsAvatar,
|
||||
loadAvatar,
|
||||
deleteAvatar,
|
||||
deleteUser
|
||||
deleteAvatar
|
||||
} = api.people;
|
||||
const { EmployeeStatus } = constants;
|
||||
|
||||
@ -83,19 +75,18 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
const newState = {
|
||||
profile: profile,
|
||||
visibleAvatarEditor: false,
|
||||
dialog: {
|
||||
visible: false,
|
||||
header: "",
|
||||
body: "",
|
||||
buttons: [],
|
||||
newEmail: profile.email
|
||||
},
|
||||
avatar: {
|
||||
tmpFile: "",
|
||||
image: null,
|
||||
defaultWidth: 0,
|
||||
defaultHeight: 0
|
||||
}
|
||||
},
|
||||
dialogsVisible: {
|
||||
deleteSelfProfile: false,
|
||||
changePassword: false,
|
||||
changeEmail: false,
|
||||
deleteProfileEver: false
|
||||
},
|
||||
};
|
||||
|
||||
return newState;
|
||||
@ -198,96 +189,9 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
});
|
||||
};
|
||||
|
||||
onEmailChange = event => {
|
||||
const emailRegex = /.+@.+\..+/;
|
||||
const newEmail =
|
||||
(event && event.target.value) || this.state.dialog.newEmail;
|
||||
const hasError = !emailRegex.test(newEmail);
|
||||
toggleChangePasswordDialog = () => this.setState({ dialogsVisible: { ...this.state.dialogsVisible, changePassword: !this.state.dialogsVisible.changePassword } });
|
||||
|
||||
const dialog = {
|
||||
visible: true,
|
||||
header: "Change email",
|
||||
body: (
|
||||
<Text>
|
||||
<span style={{ display: "block", marginBottom: "8px" }}>
|
||||
The activation instructions will be sent to the entered email
|
||||
</span>
|
||||
<TextInput
|
||||
id="new-email"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
value={newEmail}
|
||||
onChange={this.onEmailChange}
|
||||
hasError={hasError}
|
||||
/>
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onSendEmailChangeInstructions}
|
||||
isDisabled={hasError}
|
||||
/>
|
||||
],
|
||||
newEmail: newEmail
|
||||
};
|
||||
this.setState({ dialog: dialog });
|
||||
};
|
||||
|
||||
onSendEmailChangeInstructions = () => {
|
||||
sendInstructionsToChangeEmail(
|
||||
this.state.profile.id,
|
||||
this.state.dialog.newEmail
|
||||
)
|
||||
.then(res => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch(error => toastr.error(error))
|
||||
.finally(this.onDialogClose);
|
||||
};
|
||||
|
||||
onPasswordChange = () => {
|
||||
const dialog = {
|
||||
visible: true,
|
||||
header: "Change password",
|
||||
body: (
|
||||
<Text>
|
||||
Send the password change instructions to the{" "}
|
||||
<a href={`mailto:${this.state.profile.email}`}>
|
||||
{this.state.profile.email}
|
||||
</a>{" "}
|
||||
email address
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onSendPasswordChangeInstructions}
|
||||
/>
|
||||
]
|
||||
};
|
||||
this.setState({ dialog: dialog });
|
||||
};
|
||||
|
||||
onSendPasswordChangeInstructions = () => {
|
||||
sendInstructionsToChangePassword(this.state.profile.email)
|
||||
.then(res => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch(error => toastr.error(error))
|
||||
.finally(this.onDialogClose);
|
||||
};
|
||||
|
||||
onDialogClose = () => {
|
||||
const dialog = { visible: false, newEmail: this.state.profile.email };
|
||||
this.setState({ dialog: dialog });
|
||||
};
|
||||
toggleChangeEmailDialog = () => this.setState({ dialogsVisible: { ...this.state.dialogsVisible, changeEmail: !this.state.dialogsVisible.changeEmail } });
|
||||
|
||||
onEditClick = () => {
|
||||
const { history, settings } = this.props;
|
||||
@ -317,112 +221,14 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
toastr.success("Context action: Delete personal data");
|
||||
};
|
||||
|
||||
onDeleteProfileClick = user => {
|
||||
this.setState({
|
||||
dialog: {
|
||||
visible: true,
|
||||
header: "Confirmation",
|
||||
body: (
|
||||
<>
|
||||
<Text>
|
||||
User <b>{user.displayName}</b> will be deleted.
|
||||
</Text>
|
||||
<Text>Note: this action cannot be undone.</Text>
|
||||
<Text color="#c30" fontSize="18px" style={{ margin: "20px 0" }}>
|
||||
Warning!
|
||||
</Text>
|
||||
<Text>
|
||||
User personal documents which are available to others will be
|
||||
deleted. To avoid this, you must start the data reassign process
|
||||
before deleting.
|
||||
</Text>
|
||||
</>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="OkBtn"
|
||||
label="OK"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
deleteUser(user.id)
|
||||
.then(() => {
|
||||
const { filter, fetchPeople } = this.props;
|
||||
toastr.success("User has been removed successfully");
|
||||
return fetchPeople(filter);
|
||||
})
|
||||
.catch(error => toastr.error(error));
|
||||
this.onDialogClose();
|
||||
}}
|
||||
/>,
|
||||
<Button
|
||||
key="ReassignBtn"
|
||||
label="Reassign data"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
toastr.success("Context action: Reassign profile");
|
||||
this.onDialogClose();
|
||||
}}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>,
|
||||
<Button
|
||||
key="CancelBtn"
|
||||
label="Cancel"
|
||||
size="medium"
|
||||
primary={false}
|
||||
onClick={this.onDialogClose}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
]
|
||||
}
|
||||
});
|
||||
};
|
||||
toggleDeleteProfileEverDialog = () => this.setState({ dialogsVisible: { ...this.state.dialogsVisible, deleteProfileEver: !this.state.dialogsVisible.deleteProfileEver } });
|
||||
|
||||
onDeleteSelfProfileClick = email => {
|
||||
this.setState({
|
||||
dialog: {
|
||||
visible: true,
|
||||
header: "Delete profile dialog",
|
||||
body: (
|
||||
<Text>
|
||||
Send the profile deletion instructions to the email address{" "}
|
||||
<Link type="page" href={`mailto:${email}`} isHovered title={email}>
|
||||
{email}
|
||||
</Link>
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="OkBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={() => {
|
||||
sendInstructionsToDelete()
|
||||
.then(() =>
|
||||
toastr.success(
|
||||
<Text>
|
||||
Instructions to delete your profile has been sent to{" "}
|
||||
<b>{email}</b> email address
|
||||
</Text>
|
||||
)
|
||||
)
|
||||
.catch(error => toastr.error(error));
|
||||
this.onDialogClose();
|
||||
}}
|
||||
/>,
|
||||
<Button
|
||||
key="CancelBtn"
|
||||
label="Cancel"
|
||||
size="medium"
|
||||
primary={false}
|
||||
onClick={this.onDialogClose}
|
||||
style={{ marginLeft: "8px" }}
|
||||
/>
|
||||
]
|
||||
}
|
||||
});
|
||||
|
||||
toggleDeleteSelfProfileDialog = () => {
|
||||
this.setState(
|
||||
{
|
||||
dialogsVisible: { ...this.state.dialogsVisible, deleteSelfProfile: !this.state.dialogsVisible.deleteSelfProfile }
|
||||
});
|
||||
};
|
||||
|
||||
onInviteAgainClick = () => {
|
||||
@ -458,12 +264,12 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
{
|
||||
key: "change-password",
|
||||
label: t("PasswordChangeButton"),
|
||||
onClick: this.onPasswordChange
|
||||
onClick: this.toggleChangePasswordDialog
|
||||
},
|
||||
{
|
||||
key: "change-email",
|
||||
label: t("EmailChangeButton"),
|
||||
onClick: this.onEmailChange
|
||||
onClick: this.toggleChangeEmailDialog
|
||||
},
|
||||
{
|
||||
key: "edit-photo",
|
||||
@ -476,7 +282,7 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
: {
|
||||
key: "delete-profile",
|
||||
label: t("DeleteSelfProfile"),
|
||||
onClick: this.onDeleteSelfProfileClick.bind(this, user.email)
|
||||
onClick: this.toggleDeleteSelfProfileDialog
|
||||
}
|
||||
: {
|
||||
key: "disable",
|
||||
@ -509,7 +315,7 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
{
|
||||
key: "delete-profile",
|
||||
label: t("DeleteSelfProfile"),
|
||||
onClick: this.onDeleteProfileClick.bind(this, user)
|
||||
onClick: this.toggleDeleteProfileEverDialog
|
||||
}
|
||||
];
|
||||
case "pending":
|
||||
@ -544,7 +350,7 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
isMe(user, viewer.userName) && {
|
||||
key: "delete-profile",
|
||||
label: t("DeleteSelfProfile"),
|
||||
onClick: this.onDeleteSelfProfileClick.bind(this, user.email)
|
||||
onClick: this.toggleDeleteSelfProfileDialog
|
||||
}
|
||||
];
|
||||
default:
|
||||
@ -557,9 +363,8 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
};
|
||||
|
||||
render() {
|
||||
const { profile, isAdmin, viewer, t } = this.props;
|
||||
const { dialog, avatar, visibleAvatarEditor } = this.state;
|
||||
|
||||
const { profile, isAdmin, viewer, t, filter, settings, history } = this.props;
|
||||
const { avatar, visibleAvatarEditor, dialogsVisible } = this.state;
|
||||
const contextOptions = () => this.getUserContextOptions(profile, viewer);
|
||||
|
||||
return (
|
||||
@ -588,13 +393,7 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
isDisabled={false}
|
||||
/>
|
||||
)}
|
||||
<ModalDialog
|
||||
visible={dialog.visible}
|
||||
headerContent={dialog.header}
|
||||
bodyContent={dialog.body}
|
||||
footerContent={dialog.buttons}
|
||||
onClose={this.onDialogClose}
|
||||
/>
|
||||
|
||||
<AvatarEditor
|
||||
image={avatar.image}
|
||||
visible={visibleAvatarEditor}
|
||||
@ -607,6 +406,41 @@ class SectionHeaderContent extends React.PureComponent {
|
||||
maxSizeFileError={t("maxSizeFileError")}
|
||||
unknownError={t("unknownError")}
|
||||
/>
|
||||
|
||||
{dialogsVisible.deleteSelfProfile &&
|
||||
<DeleteSelfProfileDialog
|
||||
visible={dialogsVisible.deleteSelfProfile}
|
||||
onClose={this.toggleDeleteSelfProfileDialog}
|
||||
email={this.state.profile.email}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.changePassword &&
|
||||
<ChangePasswordDialog
|
||||
visible={dialogsVisible.changePassword}
|
||||
onClose={this.toggleChangePasswordDialog}
|
||||
email={this.state.profile.email}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.changeEmail &&
|
||||
<ChangeEmailDialog
|
||||
visible={dialogsVisible.changeEmail}
|
||||
onClose={this.toggleChangeEmailDialog}
|
||||
user={this.state.profile}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.deleteProfileEver &&
|
||||
<DeleteProfileEverDialog
|
||||
visible={dialogsVisible.deleteProfileEver}
|
||||
onClose={this.toggleDeleteProfileEverDialog}
|
||||
user={this.state.profile}
|
||||
filter={filter}
|
||||
settings={settings}
|
||||
history={history}
|
||||
/>
|
||||
}
|
||||
</StyledContainer>
|
||||
);
|
||||
}
|
||||
@ -624,5 +458,5 @@ const mapStateToProps = state => {
|
||||
|
||||
export default connect(
|
||||
mapStateToProps,
|
||||
{ updateUserStatus, fetchProfile, fetchPeople }
|
||||
{ updateUserStatus, fetchProfile }
|
||||
)(withRouter(withTranslation()(SectionHeaderContent)));
|
||||
|
@ -49,6 +49,7 @@ class EmailField extends React.Component {
|
||||
autoComplete='email'
|
||||
isDisabled={inputIsDisabled}
|
||||
onValidateInput={onValidateInput}
|
||||
hasError={hasError}
|
||||
/>
|
||||
</FieldContainer>
|
||||
);
|
||||
|
@ -33,7 +33,8 @@ class TextChangeField extends React.Component {
|
||||
buttonTabIndex,
|
||||
|
||||
tooltipContent,
|
||||
helpButtonHeaderContent
|
||||
helpButtonHeaderContent,
|
||||
dataDialog
|
||||
} = this.props;
|
||||
|
||||
return (
|
||||
@ -59,6 +60,7 @@ class TextChangeField extends React.Component {
|
||||
size="medium"
|
||||
style={{ marginLeft: "8px" }}
|
||||
tabIndex={buttonTabIndex}
|
||||
data-dialog={dataDialog}
|
||||
/>
|
||||
</InputContainer>
|
||||
</FieldContainer>
|
||||
|
@ -202,7 +202,7 @@ class CreateUserForm extends React.Component {
|
||||
const errors = {
|
||||
firstName: !profile.firstName.trim(),
|
||||
lastName: !profile.lastName.trim(),
|
||||
email: stateErrors.email,
|
||||
email: stateErrors.email || !profile.email.trim(),
|
||||
password: profile.passwordType === "temp" && !profile.password.trim()
|
||||
};
|
||||
const hasError = errors.firstName || errors.lastName || errors.email || errors.password;
|
||||
@ -315,7 +315,7 @@ class CreateUserForm extends React.Component {
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
onValidateEmailField = (value) => this.setState({errors: { ...this.state.errors, email:!value }});
|
||||
onValidateEmailField = (value) => this.setState({errors: { ...this.state.errors, email:!value.isValid }});
|
||||
|
||||
render() {
|
||||
const { isLoading, errors, profile, selector } = this.state;
|
||||
|
@ -1,7 +1,7 @@
|
||||
import React from 'react'
|
||||
import { withRouter } from 'react-router'
|
||||
import { connect } from 'react-redux'
|
||||
import { Avatar, Button, Textarea, Text, toastr, ModalDialog, TextInput, AvatarEditor, Link } from 'asc-web-components'
|
||||
import { Avatar, Button, Textarea, Text, toastr, AvatarEditor, Link } from 'asc-web-components'
|
||||
import { withTranslation, Trans } from 'react-i18next';
|
||||
import { toEmployeeWrapper, getUserRole, getUserContactsPattern, getUserContacts, mapGroupsToGroupSelectorOptions, mapGroupSelectorOptionsToGroups, filterGroupSelectorOptions } from "../../../../../store/people/selectors";
|
||||
import { updateProfile, getUserPhoto } from '../../../../../store/profile/actions'
|
||||
@ -16,7 +16,14 @@ import InfoFieldContainer from './FormFields/InfoFieldContainer'
|
||||
import { departments, department, position, employedSinceDate, typeGuest, typeUser } from '../../../../../helpers/customNames';
|
||||
import styled from "styled-components";
|
||||
import { api } from "asc-web-common";
|
||||
const {sendInstructionsToChangePassword, sendInstructionsToChangeEmail, createThumbnailsAvatar, loadAvatar, deleteAvatar} = api.people;
|
||||
import { ChangeEmailDialog, ChangePasswordDialog, ChangePhoneDialog } from '../../../../dialogs';
|
||||
const { createThumbnailsAvatar, loadAvatar, deleteAvatar } = api.people;
|
||||
|
||||
const dialogsDataset = {
|
||||
changeEmail: 'changeEmail',
|
||||
changePassword: 'changePassword',
|
||||
changePhone: 'changePhone'
|
||||
};
|
||||
|
||||
const Table = styled.table`
|
||||
width: 100%;
|
||||
@ -45,14 +52,6 @@ class UpdateUserForm extends React.Component {
|
||||
this.onWorkFromDateChange = this.onWorkFromDateChange.bind(this);
|
||||
this.onCancel = this.onCancel.bind(this);
|
||||
|
||||
this.onEmailChange = this.onEmailChange.bind(this);
|
||||
this.onSendEmailChangeInstructions = this.onSendEmailChangeInstructions.bind(this);
|
||||
this.onPasswordChange = this.onPasswordChange.bind(this);
|
||||
this.onSendPasswordChangeInstructions = this.onSendPasswordChangeInstructions.bind(this);
|
||||
this.onPhoneChange = this.onPhoneChange.bind(this);
|
||||
this.onSendPhoneChangeInstructions = this.onSendPhoneChangeInstructions.bind(this);
|
||||
this.onDialogClose = this.onDialogClose.bind(this);
|
||||
|
||||
this.onContactsItemAdd = this.onContactsItemAdd.bind(this);
|
||||
this.onContactsItemTypeChange = this.onContactsItemTypeChange.bind(this);
|
||||
this.onContactsItemTextChange = this.onContactsItemTextChange.bind(this);
|
||||
@ -81,10 +80,10 @@ class UpdateUserForm extends React.Component {
|
||||
mapPropsToState = (props) => {
|
||||
var profile = toEmployeeWrapper(props.profile);
|
||||
var allOptions = mapGroupsToGroupSelectorOptions(props.groups);
|
||||
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
|
||||
var selected = mapGroupsToGroupSelectorOptions(profile.groups);
|
||||
|
||||
getUserPhoto(profile.id).then(userPhotoData => {
|
||||
if(userPhotoData.original){
|
||||
if (userPhotoData.original) {
|
||||
let avatarDefaultSizes = /_(\d*)-(\d*)./g.exec(userPhotoData.original);
|
||||
if (avatarDefaultSizes !== null && avatarDefaultSizes.length > 2) {
|
||||
this.setState({
|
||||
@ -107,13 +106,6 @@ class UpdateUserForm extends React.Component {
|
||||
},
|
||||
profile: profile,
|
||||
visibleAvatarEditor: false,
|
||||
dialog: {
|
||||
visible: false,
|
||||
header: "",
|
||||
body: "",
|
||||
buttons: [],
|
||||
newEmail: profile.email,
|
||||
},
|
||||
selector: {
|
||||
visible: false,
|
||||
allOptions: allOptions,
|
||||
@ -121,10 +113,16 @@ class UpdateUserForm extends React.Component {
|
||||
selected: selected
|
||||
},
|
||||
avatar: {
|
||||
tmpFile:"",
|
||||
tmpFile: "",
|
||||
image: null,
|
||||
defaultWidth: 0,
|
||||
defaultHeight: 0
|
||||
},
|
||||
dialogsVisible: {
|
||||
[dialogsDataset.changePassword]: false,
|
||||
[dialogsDataset.changePhone]: false,
|
||||
[dialogsDataset.changeEmail]: false,
|
||||
currentDialog: ''
|
||||
}
|
||||
};
|
||||
|
||||
@ -144,6 +142,20 @@ class UpdateUserForm extends React.Component {
|
||||
this.setState(stateCopy)
|
||||
}
|
||||
|
||||
toggleDialogsVisible = (e) => {
|
||||
const stateCopy = Object.assign({}, {}, this.state.dialogsVisible);
|
||||
const selectedDialog = e ? e.target.dataset.dialog : e;
|
||||
if (selectedDialog) {
|
||||
stateCopy[selectedDialog] = true;
|
||||
stateCopy.currentDialog = selectedDialog;
|
||||
}
|
||||
else {
|
||||
stateCopy[stateCopy.currentDialog] = false;
|
||||
stateCopy.currentDialog = '';
|
||||
}
|
||||
this.setState({ dialogsVisible: stateCopy });
|
||||
};
|
||||
|
||||
onUserTypeChange(event) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.isVisitor = event.target.value === "true";
|
||||
@ -181,10 +193,10 @@ class UpdateUserForm extends React.Component {
|
||||
}
|
||||
|
||||
handleSubmit() {
|
||||
if(!this.validate())
|
||||
if (!this.validate())
|
||||
return false;
|
||||
|
||||
this.setState({isLoading: true});
|
||||
this.setState({ isLoading: true });
|
||||
|
||||
this.props.updateProfile(this.state.profile)
|
||||
.then((profile) => {
|
||||
@ -193,7 +205,7 @@ class UpdateUserForm extends React.Component {
|
||||
})
|
||||
.catch((error) => {
|
||||
toastr.error(error);
|
||||
this.setState({isLoading: false});
|
||||
this.setState({ isLoading: false });
|
||||
});
|
||||
}
|
||||
|
||||
@ -201,114 +213,6 @@ class UpdateUserForm extends React.Component {
|
||||
this.props.history.goBack();
|
||||
}
|
||||
|
||||
onEmailChange(event) {
|
||||
const emailRegex = /.+@.+\..+/;
|
||||
const newEmail = event.target.value || this.state.dialog.newEmail;
|
||||
const hasError = !emailRegex.test(newEmail);
|
||||
|
||||
const dialog = {
|
||||
visible: true,
|
||||
header: "Change email",
|
||||
body: (
|
||||
<Text>
|
||||
<span style={{display: "block", marginBottom: "8px"}}>The activation instructions will be sent to the entered email</span>
|
||||
<TextInput
|
||||
id="new-email"
|
||||
scale={true}
|
||||
isAutoFocussed={true}
|
||||
value={newEmail}
|
||||
onChange={this.onEmailChange}
|
||||
hasError={hasError}
|
||||
/>
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onSendEmailChangeInstructions}
|
||||
isDisabled={hasError}
|
||||
/>
|
||||
],
|
||||
newEmail: newEmail
|
||||
};
|
||||
this.setState({ dialog: dialog })
|
||||
}
|
||||
|
||||
onSendEmailChangeInstructions() {
|
||||
sendInstructionsToChangeEmail(this.state.profile.id, this.state.dialog.newEmail)
|
||||
.then((res) => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch((error) => toastr.error(error))
|
||||
.finally(this.onDialogClose);
|
||||
}
|
||||
|
||||
onPasswordChange() {
|
||||
const dialog = {
|
||||
visible: true,
|
||||
header: "Change password",
|
||||
body: (
|
||||
<Text>
|
||||
Send the password change instructions to the <a href={`mailto:${this.state.profile.email}`}>{this.state.profile.email}</a> email address
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onSendPasswordChangeInstructions}
|
||||
/>
|
||||
]
|
||||
};
|
||||
this.setState({ dialog: dialog })
|
||||
}
|
||||
|
||||
onSendPasswordChangeInstructions() {
|
||||
sendInstructionsToChangePassword(this.state.profile.email)
|
||||
.then((res) => {
|
||||
toastr.success(res);
|
||||
})
|
||||
.catch((error) => toastr.error(error))
|
||||
.finally(this.onDialogClose);
|
||||
}
|
||||
|
||||
onPhoneChange() {
|
||||
const dialog = {
|
||||
visible: true,
|
||||
header: "Change phone",
|
||||
body: (
|
||||
<Text>
|
||||
The instructions on how to change the user mobile number will be sent to the user email address
|
||||
</Text>
|
||||
),
|
||||
buttons: [
|
||||
<Button
|
||||
key="SendBtn"
|
||||
label="Send"
|
||||
size="medium"
|
||||
primary={true}
|
||||
onClick={this.onSendPhoneChangeInstructions}
|
||||
/>
|
||||
]
|
||||
};
|
||||
this.setState({ dialog: dialog })
|
||||
}
|
||||
|
||||
onSendPhoneChangeInstructions() {
|
||||
toastr.success("Context action: Change phone");
|
||||
this.onDialogClose();
|
||||
}
|
||||
|
||||
onDialogClose() {
|
||||
const dialog = { visible: false, newEmail: this.state.profile.email };
|
||||
this.setState({ dialog: dialog })
|
||||
}
|
||||
|
||||
onContactsItemAdd(item) {
|
||||
var stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.profile.contacts.push({
|
||||
@ -360,7 +264,7 @@ class UpdateUserForm extends React.Component {
|
||||
defaultWidth: avatarDefaultSizes[1],
|
||||
defaultHeight: avatarDefaultSizes[2]
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
this.setState({
|
||||
visibleAvatarEditor: true,
|
||||
@ -376,14 +280,14 @@ class UpdateUserForm extends React.Component {
|
||||
.then((response) => {
|
||||
var img = new Image();
|
||||
img.onload = function () {
|
||||
var stateCopy = Object.assign({}, _this.state);
|
||||
stateCopy.avatar = {
|
||||
tmpFile: response.data,
|
||||
image: response.data,
|
||||
defaultWidth: img.width,
|
||||
defaultHeight: img.height
|
||||
}
|
||||
_this.setState(stateCopy);
|
||||
var stateCopy = Object.assign({}, _this.state);
|
||||
stateCopy.avatar = {
|
||||
tmpFile: response.data,
|
||||
image: response.data,
|
||||
defaultWidth: img.width,
|
||||
defaultHeight: img.height
|
||||
}
|
||||
_this.setState(stateCopy);
|
||||
};
|
||||
img.src = response.data;
|
||||
})
|
||||
@ -391,33 +295,33 @@ class UpdateUserForm extends React.Component {
|
||||
}
|
||||
|
||||
onSaveAvatar(isUpdate, result) {
|
||||
if(isUpdate){
|
||||
if (isUpdate) {
|
||||
createThumbnailsAvatar(this.state.profile.id, {
|
||||
x: Math.round(result.x*this.state.avatar.defaultWidth - result.width/2),
|
||||
y: Math.round(result.y*this.state.avatar.defaultHeight - result.height/2),
|
||||
x: Math.round(result.x * this.state.avatar.defaultWidth - result.width / 2),
|
||||
y: Math.round(result.y * this.state.avatar.defaultHeight - result.height / 2),
|
||||
width: result.width,
|
||||
height: result.height,
|
||||
tmpFile: this.state.avatar.tmpFile
|
||||
})
|
||||
.then((response) => {
|
||||
.then((response) => {
|
||||
let stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.visibleAvatarEditor = false;
|
||||
stateCopy.avatar.tmpFile = '';
|
||||
stateCopy.profile.avatarMax = response.max + '?_='+Math.floor(Math.random() * Math.floor(10000));
|
||||
stateCopy.profile.avatarMax = response.max + '?_=' + Math.floor(Math.random() * Math.floor(10000));
|
||||
toastr.success(this.props.t("ChangesSavedSuccessfully"));
|
||||
this.setState(stateCopy);
|
||||
})
|
||||
.catch((error) => toastr.error(error));
|
||||
}else{
|
||||
})
|
||||
.catch((error) => toastr.error(error));
|
||||
} else {
|
||||
deleteAvatar(this.state.profile.id)
|
||||
.then((response) => {
|
||||
.then((response) => {
|
||||
let stateCopy = Object.assign({}, this.state);
|
||||
stateCopy.visibleAvatarEditor = false;
|
||||
stateCopy.profile.avatarMax = response.big;
|
||||
toastr.success(this.props.t("ChangesSavedSuccessfully"));
|
||||
this.setState(stateCopy);
|
||||
})
|
||||
.catch((error) => toastr.error(error));
|
||||
})
|
||||
.catch((error) => toastr.error(error));
|
||||
}
|
||||
}
|
||||
|
||||
@ -461,17 +365,17 @@ class UpdateUserForm extends React.Component {
|
||||
}
|
||||
|
||||
render() {
|
||||
const { isLoading, errors, profile, dialog, selector } = this.state;
|
||||
const { isLoading, errors, profile, selector, dialogsVisible } = this.state;
|
||||
const { t, i18n } = this.props;
|
||||
|
||||
const pattern = getUserContactsPattern();
|
||||
const contacts = getUserContacts(profile.contacts);
|
||||
const tooltipTypeContent =
|
||||
const tooltipTypeContent =
|
||||
<>
|
||||
<Text
|
||||
style={{paddingBottom: 17}}
|
||||
<Text
|
||||
style={{ paddingBottom: 17 }}
|
||||
fontSize='13px'>
|
||||
{t("ProfileTypePopupHelper")}
|
||||
{t("ProfileTypePopupHelper")}
|
||||
</Text>
|
||||
|
||||
<Text fontSize='12px' as="div">
|
||||
@ -486,8 +390,8 @@ class UpdateUserForm extends React.Component {
|
||||
<Th>
|
||||
<Text isBold fontSize='13px'>
|
||||
{t("Employee")}
|
||||
</Text>
|
||||
</Th>
|
||||
</Text>
|
||||
</Th>
|
||||
<Th>
|
||||
<Text isBold fontSize='13px'>
|
||||
{t("GuestCaption")}
|
||||
@ -528,16 +432,16 @@ class UpdateUserForm extends React.Component {
|
||||
<Td>{t("Calendar")}</Td>
|
||||
<Td>review</Td>
|
||||
<Td>review</Td>
|
||||
</tr>
|
||||
</tr>
|
||||
</tbody>
|
||||
</Table>
|
||||
</Text>
|
||||
<Link
|
||||
color="#316DAA"
|
||||
isHovered={true}
|
||||
href="https://helpcenter.onlyoffice.com/ru/gettingstarted/people.aspx#ManagingAccessRights_block"
|
||||
style={{marginTop: 23}}>
|
||||
{t("TermsOfUsePopupHelperLink")}
|
||||
<Link
|
||||
color="#316DAA"
|
||||
isHovered={true}
|
||||
href="https://helpcenter.onlyoffice.com/ru/gettingstarted/people.aspx#ManagingAccessRights_block"
|
||||
style={{ marginTop: 23 }}>
|
||||
{t("TermsOfUsePopupHelperLink")}
|
||||
</Link>
|
||||
</>;
|
||||
|
||||
@ -561,10 +465,10 @@ class UpdateUserForm extends React.Component {
|
||||
onSave={this.onSaveAvatar}
|
||||
onLoadFile={this.onLoadFileAvatar}
|
||||
headerLabel={t("editAvatar")}
|
||||
chooseFileLabel ={t("chooseFileLabel")}
|
||||
chooseFileLabel={t("chooseFileLabel")}
|
||||
unknownTypeError={t("unknownTypeError")}
|
||||
maxSizeFileError={t("maxSizeFileError")}
|
||||
unknownError ={t("unknownError")}
|
||||
unknownError={t("unknownError")}
|
||||
/>
|
||||
</AvatarContainer>
|
||||
<MainFieldsContainer ref={this.mainFieldsContainerRef}>
|
||||
@ -574,7 +478,7 @@ class UpdateUserForm extends React.Component {
|
||||
inputValue={profile.email}
|
||||
buttonText={t("ChangeButton")}
|
||||
buttonIsDisabled={isLoading}
|
||||
buttonOnClick={this.onEmailChange}
|
||||
buttonOnClick={this.toggleDialogsVisible}
|
||||
buttonTabIndex={1}
|
||||
|
||||
helpButtonHeaderContent={t("Mail")}
|
||||
@ -582,7 +486,7 @@ class UpdateUserForm extends React.Component {
|
||||
<Text fontSize='13px' as="div">
|
||||
<Trans i18nKey="EmailPopupHelper" i18n={i18n}>
|
||||
The main e-mail is needed to restore access to the portal in case of loss of the password and send notifications.
|
||||
<p style={{margin: "1rem 0"/*, height: "0", visibility: "hidden"*/}}>
|
||||
<p style={{ margin: "1rem 0"/*, height: "0", visibility: "hidden"*/ }}>
|
||||
You can create a new mail on the domain as the primary.
|
||||
In this case, you must set a one-time password so that the user can log in to the portal for the first time.
|
||||
</p>
|
||||
@ -590,6 +494,7 @@ class UpdateUserForm extends React.Component {
|
||||
</Trans>
|
||||
</Text>
|
||||
}
|
||||
dataDialog={dialogsDataset.changeEmail}
|
||||
/>
|
||||
<TextChangeField
|
||||
labelText={`${t("Password")}:`}
|
||||
@ -597,8 +502,9 @@ class UpdateUserForm extends React.Component {
|
||||
inputValue={profile.password}
|
||||
buttonText={t("ChangeButton")}
|
||||
buttonIsDisabled={isLoading}
|
||||
buttonOnClick={this.onPasswordChange}
|
||||
buttonOnClick={this.toggleDialogsVisible}
|
||||
buttonTabIndex={2}
|
||||
dataDialog={dialogsDataset.changePassword}
|
||||
/>
|
||||
<TextChangeField
|
||||
labelText={`${t("Phone")}:`}
|
||||
@ -606,8 +512,9 @@ class UpdateUserForm extends React.Component {
|
||||
inputValue={profile.phone}
|
||||
buttonText={t("ChangeButton")}
|
||||
buttonIsDisabled={isLoading}
|
||||
buttonOnClick={this.onPhoneChange}
|
||||
buttonOnClick={this.toggleDialogsVisible}
|
||||
buttonTabIndex={3}
|
||||
dataDialog={dialogsDataset.changePhone}
|
||||
/>
|
||||
<TextField
|
||||
isRequired={true}
|
||||
@ -644,8 +551,8 @@ class UpdateUserForm extends React.Component {
|
||||
radioName="sex"
|
||||
radioValue={profile.sex}
|
||||
radioOptions={[
|
||||
{ value: 'male', label: t("SexMale")},
|
||||
{ value: 'female', label: t("SexFemale")}
|
||||
{ value: 'male', label: t("SexMale") },
|
||||
{ value: 'female', label: t("SexFemale") }
|
||||
]}
|
||||
radioIsDisabled={isLoading}
|
||||
radioOnChange={this.onInputChange}
|
||||
@ -655,8 +562,8 @@ class UpdateUserForm extends React.Component {
|
||||
radioName="isVisitor"
|
||||
radioValue={profile.isVisitor.toString()}
|
||||
radioOptions={[
|
||||
{ value: "true", label: t("CustomTypeGuest", { typeGuest })},
|
||||
{ value: "false", label: t("CustomTypeUser", { typeUser })}
|
||||
{ value: "true", label: t("CustomTypeGuest", { typeGuest }) },
|
||||
{ value: "false", label: t("CustomTypeUser", { typeUser }) }
|
||||
]}
|
||||
radioIsDisabled={isLoading}
|
||||
radioOnChange={this.onUserTypeChange}
|
||||
@ -735,16 +642,33 @@ class UpdateUserForm extends React.Component {
|
||||
/>
|
||||
</InfoFieldContainer>
|
||||
<div>
|
||||
<Button label={t("SaveButton")} onClick={this.handleSubmit} primary isDisabled={isLoading} size="big" tabIndex={11}/>
|
||||
<Button label={t("CancelButton")} onClick={this.onCancel} isDisabled={isLoading} size="big" style={{ marginLeft: "8px" }} tabIndex={12}/>
|
||||
<Button label={t("SaveButton")} onClick={this.handleSubmit} primary isDisabled={isLoading} size="big" tabIndex={11} />
|
||||
<Button label={t("CancelButton")} onClick={this.onCancel} isDisabled={isLoading} size="big" style={{ marginLeft: "8px" }} tabIndex={12} />
|
||||
</div>
|
||||
<ModalDialog
|
||||
visible={dialog.visible}
|
||||
headerContent={dialog.header}
|
||||
bodyContent={dialog.body}
|
||||
footerContent={dialog.buttons}
|
||||
onClose={this.onDialogClose}
|
||||
/>
|
||||
|
||||
{dialogsVisible.changeEmail &&
|
||||
<ChangeEmailDialog
|
||||
visible={dialogsVisible.changeEmail}
|
||||
onClose={this.toggleDialogsVisible}
|
||||
user={profile}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.changePassword &&
|
||||
<ChangePasswordDialog
|
||||
visible={dialogsVisible.changePassword}
|
||||
onClose={this.toggleDialogsVisible}
|
||||
email={profile.email}
|
||||
/>
|
||||
}
|
||||
|
||||
{dialogsVisible.changePhone &&
|
||||
<ChangePhoneDialog
|
||||
visible={dialogsVisible.changePhone}
|
||||
onClose={this.toggleDialogsVisible}
|
||||
user={profile}
|
||||
/>
|
||||
}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -10,7 +10,7 @@
|
||||
]
|
||||
},
|
||||
"dialogs": {
|
||||
"Invite": {
|
||||
"InviteDialog": {
|
||||
"Resource": [
|
||||
"HelpAnswerLinkInviteSettings",
|
||||
"CopyToClipboard",
|
||||
@ -23,6 +23,49 @@
|
||||
"ResourceJS": [
|
||||
"LinkCopySuccess"
|
||||
]
|
||||
},
|
||||
"ChangeEmailDialog":{
|
||||
"Resource": [
|
||||
"EmailChangeTitle",
|
||||
"EnterEmail",
|
||||
"EmailActivationDescription",
|
||||
"SendButton"
|
||||
]
|
||||
},
|
||||
"DeleteSelfProfileDialog":{
|
||||
"Resource": [
|
||||
"DeleteProfileTitle",
|
||||
"DeleteProfileInfo",
|
||||
"CloseButton",
|
||||
"SendButton"
|
||||
]
|
||||
},
|
||||
"ChangePhoneDialog":{
|
||||
"Resource": [
|
||||
"SendButton",
|
||||
"MobilePhoneChangeTitle"
|
||||
]
|
||||
},
|
||||
"DeleteProfileEverDialog":{
|
||||
"Resource": [
|
||||
"Confirmation",
|
||||
"DeleteUserConfirmation",
|
||||
"Warning",
|
||||
"DeleteUserDataConfirmation"
|
||||
],
|
||||
"PeopleResource": [
|
||||
"SuccessfullyDeleteUserInfoMessage"
|
||||
],
|
||||
"UserControlsCommonResource":[
|
||||
"NotBeUndone"
|
||||
]
|
||||
},
|
||||
"ChangePasswordDialog":{
|
||||
"Resource": [
|
||||
"PasswordChangeTitle",
|
||||
"MessageSendPasswordChangeInstructionsOnEmail",
|
||||
"SendButton"
|
||||
]
|
||||
}
|
||||
},
|
||||
"pages": {
|
||||
|
@ -1384,21 +1384,7 @@ namespace ASC.Employee.Core.Controllers
|
||||
PermissionContext.DemandPermissions(new UserSecurityProvider(user.ID), Constants.Action_EditUser);
|
||||
|
||||
if (contacts == null) return;
|
||||
|
||||
if (user.ContactsList == null)
|
||||
{
|
||||
user.ContactsList = new List<string>();
|
||||
}
|
||||
else
|
||||
{
|
||||
user.ContactsList.Clear();
|
||||
}
|
||||
|
||||
foreach (var contact in contacts.Where(c => !string.IsNullOrEmpty(c.Value)))
|
||||
{
|
||||
user.ContactsList.Add(contact.Type);
|
||||
user.ContactsList.Add(contact.Value);
|
||||
}
|
||||
user.Contacts = contacts.Select(r => $"{r.Type}|{r.Value}").Aggregate((a, b) => $"{a}|{b}");
|
||||
}
|
||||
|
||||
private void DeleteContacts(IEnumerable<Contact> contacts, UserInfo user)
|
||||
|
@ -186,7 +186,7 @@ class Confirm extends React.PureComponent {
|
||||
this.state.errorText && this.setState({ errorText: "" });;
|
||||
}
|
||||
|
||||
onValidateEmail = value => this.setState({emailValid: value });
|
||||
onValidateEmail = value => this.setState({emailValid: value.isValid });
|
||||
|
||||
onChangePassword = event => {
|
||||
this.setState({ password: event.target.value });
|
||||
|
@ -2473,7 +2473,7 @@ asap@~2.0.6:
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
"asc-web-common@file:../../packages/asc-web-common":
|
||||
version "1.0.20"
|
||||
version "1.0.24"
|
||||
dependencies:
|
||||
axios "^0.19.0"
|
||||
faker "^4.1.0"
|
||||
@ -2482,7 +2482,7 @@ asap@~2.0.6:
|
||||
react-window-infinite-loader "^1.0.5"
|
||||
|
||||
"asc-web-components@file:../../packages/asc-web-components":
|
||||
version "1.0.260"
|
||||
version "1.0.264"
|
||||
dependencies:
|
||||
email-addresses "^3.1.0"
|
||||
html-to-react "^1.4.2"
|
||||
|
@ -4,7 +4,6 @@ import styled from "styled-components";
|
||||
import { Scrollbar } from "asc-web-components";
|
||||
|
||||
const StyledArticleBody = styled.div`
|
||||
margin: 16px 0;
|
||||
${props => props.displayBorder && `outline: 1px dotted;`}
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
@ -14,13 +13,19 @@ const StyledArticleBody = styled.div`
|
||||
}
|
||||
`;
|
||||
|
||||
const StyledArticleWrapper = styled.div`
|
||||
margin: 16px 0;
|
||||
`;
|
||||
|
||||
const ArticleBody = React.memo(props => {
|
||||
//console.log("PageLayout ArticleBody render");
|
||||
const { children } = props;
|
||||
|
||||
return (
|
||||
<StyledArticleBody>
|
||||
<Scrollbar>{children}</Scrollbar>
|
||||
<Scrollbar>
|
||||
<StyledArticleWrapper>{children}</StyledArticleWrapper>
|
||||
</Scrollbar>
|
||||
</StyledArticleBody>
|
||||
);
|
||||
});
|
||||
|
@ -5,10 +5,13 @@ import { utils, Scrollbar } from "asc-web-components";
|
||||
const { tablet } = utils.device;
|
||||
|
||||
const StyledSectionBody = styled.div`
|
||||
margin: 16px 8px 16px 0;
|
||||
${props => props.displayBorder && `outline: 1px dotted;`}
|
||||
flex-grow: 1;
|
||||
height: 100%;
|
||||
`;
|
||||
|
||||
const StyledSectionWrapper = styled.div`
|
||||
margin: 16px 8px 16px 0;
|
||||
|
||||
@media ${tablet} {
|
||||
margin: 16px 0;
|
||||
@ -32,14 +35,16 @@ const SectionBody = React.memo(props => {
|
||||
<StyledSectionBody>
|
||||
{withScroll ? (
|
||||
<Scrollbar stype="mediumBlack">
|
||||
{children}
|
||||
<StyledSpacer pinned={pinned}/>
|
||||
<StyledSectionWrapper>
|
||||
{children}
|
||||
<StyledSpacer pinned={pinned}/>
|
||||
</StyledSectionWrapper>
|
||||
</Scrollbar>
|
||||
) : (
|
||||
<>
|
||||
<StyledSectionWrapper>
|
||||
{children}
|
||||
<StyledSpacer pinned={pinned}/>
|
||||
</>
|
||||
</StyledSectionWrapper>
|
||||
)}
|
||||
</StyledSectionBody>
|
||||
);
|
||||
|
@ -2841,7 +2841,7 @@ asap@~2.0.3:
|
||||
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
|
||||
|
||||
"asc-web-components@file:../../packages/asc-web-components":
|
||||
version "1.0.260"
|
||||
version "1.0.264"
|
||||
dependencies:
|
||||
email-addresses "^3.1.0"
|
||||
html-to-react "^1.4.2"
|
||||
@ -10010,9 +10010,9 @@ rc-tree@^2.1.3:
|
||||
warning "^4.0.3"
|
||||
|
||||
rc-util@^4.15.3, rc-util@^4.5.1:
|
||||
version "4.17.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.17.0.tgz#1991a3194ac5c88819e57fa924a8ef4b42544a37"
|
||||
integrity sha512-nMtU4tmPhhpRkTWHGkbEYQpuqRMAws/OUS4aqumJkRIOEj6gA6LkmjJZkDLyLgmLpzHM4VAIQhodIYu+Z+yylg==
|
||||
version "4.18.0"
|
||||
resolved "https://registry.yarnpkg.com/rc-util/-/rc-util-4.18.0.tgz#b014712709ea2f777e5c207dca23ecd211cf73dd"
|
||||
integrity sha512-hftkePUmXu2AeaYQRqMdEJ+bkqNRKZEk59pUmqMKD68+69Csc1xeIc74P73leuZEYij23yG4OMnutejVjc8Jdg==
|
||||
dependencies:
|
||||
add-dom-event-listener "^1.1.0"
|
||||
babel-runtime "6.x"
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-components",
|
||||
"version": "1.0.265",
|
||||
"version": "1.0.266",
|
||||
"description": "Ascensio System SIA component library",
|
||||
"license": "AGPL-3.0",
|
||||
"main": "dist/asc-web-components.js",
|
||||
|
@ -5,17 +5,23 @@ Email entry field with advanced capabilities for validation based on settings
|
||||
### Usage
|
||||
|
||||
```js
|
||||
import { EmailInput } from "asc-web-components";
|
||||
import { EmailInput, utils } from "asc-web-components";
|
||||
|
||||
const { EmailSettings } = utils.email;
|
||||
|
||||
const settings = new EmailSettings();
|
||||
|
||||
settings.allowDomainPunycode = true;
|
||||
```
|
||||
|
||||
```jsx
|
||||
<EmailInput
|
||||
name="email"
|
||||
placeholder="email"
|
||||
onValidateInput={isValidEmail =>
|
||||
console.log("isValidEmail = ", isValidEmail);
|
||||
}
|
||||
emailSettings={settings}
|
||||
onValidateInput={result =>
|
||||
console.log("onValidateInput", result.value, result.isValid, result.errors);
|
||||
}
|
||||
/>;
|
||||
```
|
||||
|
||||
@ -23,22 +29,22 @@ import { EmailInput } from "asc-web-components";
|
||||
|
||||
You can apply all properties of the `TextInput` component to the component
|
||||
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------------- | :-----------------------------------: | :------: | :----: | :-------------: | ------------------------------------------------------------------------ |
|
||||
| `className` | `string` | - | - | - | Accepts class |
|
||||
| `customValidateFunc` | `func` | - | - | - | Function for your custom validation input value |
|
||||
| `emailSettings` | `Object`, `Instance of EmailSettings` | - | - | `EmailSettings` | Settings for validating email |
|
||||
| `id` | `string` | - | - | - | Accepts id |
|
||||
| `isValid` | `bool` | - | - | - | Used in your custom validation function for change border-color of input |
|
||||
| `onChange` | `func` | - | - | - | Function for your custom handling changes in input |
|
||||
| `onValidateInput` | `func` | - | - | - | Will be validate our value, return boolean validation result |
|
||||
| `style` | `obj`, `array` | - | - | - | Accepts css style |
|
||||
| Props | Type | Required | Values | Default | Description |
|
||||
| -------------------- | :-----------------------------------: | :------: | :-----------------------------: | :-------------: | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
|
||||
| `className` | `string` | - | - | - | Accepts class |
|
||||
| `customValidate` | `func` | - | - | - | Function for your custom validation input value. Function must return object with following parameters: `value`: string value of input, `isValid`: boolean result of validating, `errors`(optional): array of errors |
|
||||
| `emailSettings` | `Object`, `EmailSettings` | - | - | { allowDomainPunycode: false, allowLocalPartPunycode: false, allowDomainIp: false, allowStrictLocalPart: true, allowSpaces: false, allowName: false, allowLocalDomainName: false } | Settings for validating email |
|
||||
| `hasError` | `bool` | - | - | - | Used in your custom validation |
|
||||
| `id` | `string` | - | - | - | Accepts id |
|
||||
| `onChange` | `func` | - | - | - | Function for your custom handling changes in input |
|
||||
| `onValidateInput` | `func` | - | { isValid: bool, errors: array} | - | Will be validate our value, return object with following parameters: `isValid`: boolean result of validating, `errors`: array of errors |
|
||||
| `style` | `obj`, `array` | - | - | - | Accepts css style |
|
||||
|
||||
### Validate email
|
||||
|
||||
Our validation algorithm based on [An RFC 5322 email address parser](https://www.npmjs.com/package/email-addresses).
|
||||
Our validation algorithm based on [RFC 5322 email address parser](https://www.npmjs.com/package/email-addresses).
|
||||
|
||||
For email validating you should use plain Object or our email utility with following settings:
|
||||
For email validating you should use plain Object or EmailSettings with following settings:
|
||||
|
||||
| Props | Type | Required | Default | Description |
|
||||
| ------------------------ | :----: | :------: | :-----: | ----------------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
@ -74,7 +80,7 @@ const { EmailSettings } = utils.email;
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
emailSettings.getSettings(); /* returned Object with default settings:
|
||||
emailSettings.toObject(); /* returned Object with default settings:
|
||||
{
|
||||
allowDomainPunycode: false,
|
||||
allowLocalPartPunycode: false,
|
||||
@ -87,7 +93,7 @@ emailSettings.getSettings(); /* returned Object with default settings:
|
||||
*/
|
||||
email.allowName = true; // set allowName setting to true
|
||||
|
||||
emailSettings.getSettings(); /* returned Object with NEW settings:
|
||||
emailSettings.toObject(); /* returned Object with NEW settings:
|
||||
{
|
||||
|
||||
allowDomainPunycode: false,
|
||||
@ -104,62 +110,48 @@ emailSettings.getSettings(); /* returned Object with NEW settings:
|
||||
|
||||
### Custom validate email
|
||||
|
||||
You should use custom validation with the `customValidateFunc` prop. Also you can change state of validation with the help of `isValid` prop.
|
||||
`isValid` prop allow you to change border-color of input.
|
||||
You should use custom validation with the `customValidate` prop. This prop contains function for your custom validation input value. Function must return object with following parameters: `value`: string value of input, `isValid`: boolean result of validating, `errors`(optional): array of errors.
|
||||
|
||||
How are applied colors in component:
|
||||
Base colors:
|
||||
|
||||
| Сomponent actions | isValid | border-color |
|
||||
| ----------------- | :-----: | :----------: |
|
||||
| `:focus` | `false` | #c30 |
|
||||
| `:focus` | `true` | #2DA7DB |
|
||||
| `:hover` | `false` | #c30 |
|
||||
| `:hover` | `true` | #D0D5DA |
|
||||
| `default` | `false` | #c30 |
|
||||
| `default` | `true` | #D0D5DA |
|
||||
| `:focus` | `false` | ![#c30](https://placehold.it/15/c30/000000?text=+) #c30 |
|
||||
| `:focus` | `true` | ![#2DA7DB](https://placehold.it/15/2DA7DB/000000?text=+) #2DA7DB |
|
||||
| `:hover` | `false` | ![#c30](https://placehold.it/15/c30/000000?text=+) #c30 |
|
||||
| `:hover` | `true` | ![#D0D5DA](https://placehold.it/15/D0D5DA/000000?text=+) #D0D5DA |
|
||||
| `default` | `false` | ![#c30](https://placehold.it/15/c30/000000?text=+) #c30 |
|
||||
| `default` | `true` | ![#D0D5DA](https://placehold.it/15/D0D5DA/000000?text=+) #D0D5DA |
|
||||
|
||||
```js
|
||||
import React, { useState } from "react";
|
||||
import React from "react";
|
||||
import { EmailInput } from "asc-web-components";
|
||||
|
||||
const [emailValid, setEmailValid] = useState(true);
|
||||
|
||||
const customChangeFunc = (e) => {
|
||||
const onChange = (e) => {
|
||||
// your event handling
|
||||
customValidateFunc(e.target.value);
|
||||
customValidate(e.target.value);
|
||||
}
|
||||
|
||||
const customValidateFunc = (value) => {
|
||||
let validationResult;
|
||||
// your validating function
|
||||
setEmailValid(validationResult);
|
||||
const customValidate = (value) => {
|
||||
const isValid = !!(value && value.length > 0);
|
||||
return {
|
||||
value,
|
||||
isValid: isValid,
|
||||
errors: isValid ? [] : ["incorrect email"]
|
||||
}
|
||||
}
|
||||
|
||||
const onValidateInput = (isValidEmail) => {
|
||||
console.log(`isValidEmail = ${isValidEmail}`);
|
||||
const onValidateInput = (result) => {
|
||||
console.log("onValidateInput", result);
|
||||
}
|
||||
|
||||
return (
|
||||
```
|
||||
|
||||
```jsx
|
||||
<EmailInput
|
||||
isValid={emailValid}
|
||||
onChange={customChangeFunc}
|
||||
customValidateFunc={customValidateFunc}
|
||||
customValidate={customValidate}
|
||||
onChange={onChange}
|
||||
onValidateInput={onValidateInput}
|
||||
|
||||
/>;
|
||||
);
|
||||
```
|
||||
|
||||
#### Email settings RFC 5321
|
||||
|
||||
```js
|
||||
{
|
||||
allowDomainPunycode: true,
|
||||
allowLocalPartPunycode: true,
|
||||
allowDomainIp: true,
|
||||
allowStrictLocalPart: false,
|
||||
allowSpaces: true,
|
||||
allowName: false,
|
||||
allowLocalDomainName: true
|
||||
}
|
||||
```
|
||||
|
@ -53,7 +53,7 @@ storiesOf('Components|Input', module)
|
||||
id={id}
|
||||
name={name}
|
||||
emailSettings={settings}
|
||||
onValidateInput={(isEmailValid) => action('isValidEmail')(isEmailValid)}
|
||||
onValidateInput={(isEmailValid) => action('onValidateInput')(isEmailValid)}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
|
@ -19,6 +19,73 @@ const baseProps = {
|
||||
}
|
||||
|
||||
describe('<EmailInput />', () => {
|
||||
it('Init invalid value test', () => {
|
||||
const email = "zzz";
|
||||
const wrapper = shallow(<EmailInput value={email} />).instance();
|
||||
|
||||
expect(wrapper.state.isValidEmail.isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('Clean valid value test', () => {
|
||||
const email = "zzz";
|
||||
|
||||
const wrapper = shallow(<EmailInput value={email} />).instance();
|
||||
|
||||
let event = { target: { value: "simple@example.com" } };
|
||||
|
||||
wrapper.onChange(event);
|
||||
|
||||
expect(wrapper.state.isValidEmail.isValid).toBe(true);
|
||||
|
||||
event = { target: { value: "" } };
|
||||
|
||||
wrapper.onChange(event);
|
||||
|
||||
expect(wrapper.state.isValidEmail.isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('Change value prop test', () => {
|
||||
const email = "zzz";
|
||||
|
||||
const wrapper = mount(<EmailInput value={email} />);
|
||||
|
||||
expect(wrapper.state().inputValue).toBe(email);
|
||||
|
||||
wrapper.setProps({ value: 'bar' });
|
||||
|
||||
expect(wrapper.state().isValidEmail.isValid).toBe(false);
|
||||
|
||||
expect(wrapper.state().inputValue).toBe("bar");
|
||||
});
|
||||
|
||||
it('Custom validation test', () => {
|
||||
const email = "zzz";
|
||||
|
||||
const customValidate = (value) => {
|
||||
const isValid = !!(value && value.length > 0);
|
||||
return {
|
||||
isValid: isValid,
|
||||
errors: isValid ? [] : ["incorrect email"]
|
||||
}
|
||||
}
|
||||
|
||||
const wrapper = mount(<EmailInput value={email} customValidate={customValidate} />);
|
||||
|
||||
expect(wrapper.state().inputValue).toBe(email);
|
||||
|
||||
expect(wrapper.state().isValidEmail.isValid).toBe(true);
|
||||
|
||||
wrapper.setProps({ value: 'bar' });
|
||||
|
||||
expect(wrapper.state().isValidEmail.isValid).toBe(true);
|
||||
|
||||
expect(wrapper.state().inputValue).toBe("bar");
|
||||
|
||||
wrapper.setProps({ value: '' });
|
||||
|
||||
expect(wrapper.state().isValidEmail.isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('renders without error', () => {
|
||||
const wrapper = mount(<EmailInput {...baseProps} />);
|
||||
|
||||
@ -58,7 +125,7 @@ describe('<EmailInput />', () => {
|
||||
expect(shouldUpdate).toBe(true);
|
||||
expect(wrapper.state('emailSettings')).toBe(emailSettings);
|
||||
});
|
||||
it('isValidEmail is "true" after deleting value', () => {
|
||||
it('isValidEmail is "false" after deleting value', () => {
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} />);
|
||||
|
||||
@ -66,13 +133,13 @@ describe('<EmailInput />', () => {
|
||||
|
||||
wrapper.simulate('change', event);
|
||||
|
||||
expect(wrapper.state('isValidEmail')).toBe(false);
|
||||
expect(wrapper.state().isValidEmail.isValid).toBe(false);
|
||||
|
||||
const emptyValue = { target: { value: "" } };
|
||||
|
||||
wrapper.simulate('change', emptyValue);
|
||||
|
||||
expect(wrapper.state('isValidEmail')).toBe(true);
|
||||
expect(wrapper.state().isValidEmail.isValid).toBe(false);
|
||||
});
|
||||
|
||||
it('not re-render test', () => {
|
||||
@ -86,7 +153,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email: simple@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -98,7 +165,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email: disposable.style.email.with+symbol@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -110,7 +177,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email: user.name+tag+sorting@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -122,7 +189,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with one-letter local-part: x@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -135,7 +202,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email, local domain name with no TLD: admin@mailserver1', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
|
||||
@ -148,7 +215,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email, local domain name with no TLD: admin@mailserver1 (settings: allowLocalDomainName = true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
@ -162,7 +229,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (one-letter domain name): example@s.example', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -175,7 +242,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (space between the quotes): " "@example.org', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -187,7 +254,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (space between the quotes): " "@example.org (settings: allowSpaces = true, allowStrictLocalPart = false)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowSpaces = true;
|
||||
@ -202,7 +269,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (quoted double dot): "john..doe"@example.org)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
|
||||
@ -214,7 +281,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (quoted double dot): "john..doe"@example.org (settings: allowSpaces = true, allowStrictLocalPart = false)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowSpaces = true;
|
||||
@ -229,7 +296,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (bangified host route used for uucp mailers): mailhost!username@example.org', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -241,7 +308,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (bangified host route used for uucp mailers): mailhost!username@example.org (object settings: allowStrictLocalPart = false)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
const emailSettings = {
|
||||
allowStrictLocalPart: false
|
||||
@ -255,7 +322,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email (% escaped mail route to user@example.com via example.org): user%example.com@example.org)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowStrictLocalPart = false;
|
||||
@ -268,7 +335,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in domain: example@джpумлатест.bрфa', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -281,7 +348,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in local part: mañana@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -294,7 +361,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in local part and domain: mañana@mañana.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -306,7 +373,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in local part and domain: mañana@mañana.com (settings: allowDomainPunycode=true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowDomainPunycode = true;
|
||||
@ -319,7 +386,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in local part and domain: mañana@mañana.com (settings: allowLocalPartPunycode=true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalPartPunycode = true;
|
||||
@ -332,7 +399,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in local part and domain: mañana@mañana.com (settings: allowDomainPunycode=true, allowLocalPartPunycode=true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalPartPunycode = true;
|
||||
@ -346,7 +413,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with punycode symbols in local part and domain: mañana@mañana.com (settings: allowDomainPunycode=true, allowLocalPartPunycode=true, allowStrictLocalPart=false)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalPartPunycode = true;
|
||||
@ -361,7 +428,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with IP address in domain: user@[127.0.0.1]', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
|
||||
@ -373,7 +440,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with IP address in domain: user@[127.0.0.1] (settings: allowDomainIp = true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const emailSettings = { allowDomainIp: true };
|
||||
@ -386,7 +453,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with Name (RFC 5322): "Jack Bowman" <jack@fogcreek.com>', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -398,7 +465,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with Name (RFC 5322): "Jack Bowman" <jack@fogcreek.com> (instance of EmailSettings: allowName = true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
@ -412,7 +479,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with Name (RFC 5322): Bob <bob@example.com>', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -424,7 +491,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed valid email with Name (RFC 5322): Bob <bob@example.com> (instance of EmailSettings: allowName = true)', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(true);
|
||||
expect(isValidEmail.isValid).toEqual(true);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
@ -439,7 +506,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (no @ character): Abc.example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -451,7 +518,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (only one @ is allowed outside quotation marks): A@b@c@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -463,7 +530,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (none of the special characters in this local-part are allowed outside quotation marks): a"b(c)d,e:f;g<h>i[j\k]l@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -475,7 +542,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (none of the special characters in this local-part are allowed outside quotation marks): a"b(c)d,e:f;g<h>i[j\k]l@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
@ -487,7 +554,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (quoted strings must be dot separated or the only element making up the local-part): just"not"right@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
@ -502,7 +569,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (spaces, quotes, and backslashes may only exist when within quoted strings and preceded by a backslash): this is"not\allowed@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
@ -517,7 +584,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (even if escaped (preceded by a backslash), spaces, quotes, and backslashes must still be contained by quotes): this\ still\"not\\allowed@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
@ -532,7 +599,7 @@ describe('<EmailInput />', () => {
|
||||
it('passed invalid email (local part is longer than 64 characters): 1234567890123456789012345678901234567890123456789012345678901234+x@example.com', () => {
|
||||
|
||||
const onValidateInput = jest.fn(isValidEmail => {
|
||||
expect(isValidEmail).toEqual(false);
|
||||
expect(isValidEmail.isValid).toEqual(false);
|
||||
});
|
||||
|
||||
const wrapper = mount(<EmailInput {...baseProps} onValidateInput={onValidateInput} />);
|
||||
|
@ -1,139 +1,156 @@
|
||||
import React from 'react'
|
||||
import styled from 'styled-components'
|
||||
import PropTypes from 'prop-types'
|
||||
import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import TextInput from '../text-input'
|
||||
import { EmailSettings, parseAddress, checkAndConvertEmailSettings, isEqualEmailSettings } from '../../utils/email/';
|
||||
import TextInput from "../text-input";
|
||||
import {
|
||||
EmailSettings,
|
||||
parseAddress
|
||||
} from "../../utils/email/";
|
||||
|
||||
const borderColor = {
|
||||
default: '#D0D5DA',
|
||||
isValid: '#2DA7DB',
|
||||
isNotValid: '#c30'
|
||||
};
|
||||
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const SimpleInput = ({ onValidateInput, isValidEmail, emailSettings, ...props }) => <TextInput {...props}></TextInput>;
|
||||
|
||||
SimpleInput.propTypes = {
|
||||
onValidateInput: PropTypes.func,
|
||||
isValidEmail: PropTypes.bool,
|
||||
emailSettings: PropTypes.oneOfType([PropTypes.instanceOf(EmailSettings), PropTypes.objectOf(PropTypes.bool)])
|
||||
}
|
||||
|
||||
const StyledTextInput = styled(SimpleInput)`
|
||||
|
||||
border-color: ${props => (props.isValidEmail ? borderColor.default : borderColor.isNotValid)};
|
||||
|
||||
:hover {
|
||||
border-color: ${props => (props.isValidEmail ? borderColor.default : borderColor.isNotValid)};
|
||||
}
|
||||
|
||||
:focus {
|
||||
border-color: ${props => (props.isValidEmail ? borderColor.isValid : borderColor.isNotValid)};
|
||||
}
|
||||
|
||||
`;
|
||||
/* eslint-disable no-unused-vars */
|
||||
/* eslint-disable react/prop-types */
|
||||
const TextInputWrapper = ({
|
||||
onValidateInput,
|
||||
isValidEmail,
|
||||
emailSettings,
|
||||
...props
|
||||
}) => <TextInput {...props}></TextInput>;
|
||||
/* eslint-enable react/prop-types */
|
||||
/* eslint-enable no-unused-vars */
|
||||
|
||||
class EmailInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { value, emailSettings } = this.props;
|
||||
const validatedSettings = checkAndConvertEmailSettings(emailSettings);
|
||||
const validatedSettings = EmailSettings.parse(emailSettings);
|
||||
const isValidEmail = this.checkEmail(value, validatedSettings);
|
||||
|
||||
this.state = {
|
||||
isValidEmail: true,
|
||||
isValidEmail,
|
||||
emailSettings: validatedSettings,
|
||||
inputValue: value
|
||||
}
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
const { emailSettings } = this.props;
|
||||
if (isEqualEmailSettings(this.state.emailSettings, emailSettings)) return;
|
||||
|
||||
const validatedSettings = checkAndConvertEmailSettings(emailSettings);
|
||||
|
||||
this.setState({ emailSettings: validatedSettings }, function () {
|
||||
this.checkEmail(this.state.inputValue);
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
checkEmail = (value) => {
|
||||
if (!value.length) {
|
||||
!this.state.isValidEmail && this.setState({ isValidEmail: true, inputValue: value });
|
||||
return;
|
||||
}
|
||||
|
||||
const emailObj = parseAddress(value, this.state.emailSettings);
|
||||
const isValidEmail = emailObj.isValid();
|
||||
|
||||
this.props.onValidateInput
|
||||
&& this.props.onValidateInput(isValidEmail);
|
||||
|
||||
this.setState({ isValidEmail, inputValue: value });
|
||||
}
|
||||
|
||||
onChangeAction = (e) => {
|
||||
this.props.onChange && this.props.onChange(e);
|
||||
|
||||
this.props.customValidateFunc ? this.props.customValidateFunc(e) : this.checkEmail(e.target.value);
|
||||
};
|
||||
}
|
||||
|
||||
shouldComponentUpdate(nextProps, nextState) {
|
||||
return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
const { emailSettings, value } = this.props;
|
||||
|
||||
if (!EmailSettings.equals(emailSettings, prevProps.emailSettings)) {
|
||||
const validatedSettings = EmailSettings.parse(emailSettings);
|
||||
|
||||
this.setState({ emailSettings: validatedSettings }, () => {
|
||||
this.validate(this.state.inputValue);
|
||||
});
|
||||
}
|
||||
|
||||
if (value !== prevProps.value) {
|
||||
this.validate(value);
|
||||
}
|
||||
}
|
||||
|
||||
validate = (value) => {
|
||||
const { onValidateInput } = this.props;
|
||||
const isValidEmail = this.checkEmail(value);
|
||||
this.setState({
|
||||
inputValue: value,
|
||||
isValidEmail
|
||||
});
|
||||
onValidateInput && onValidateInput(isValidEmail);
|
||||
}
|
||||
|
||||
checkEmail = (value, emailSettings = this.state.emailSettings) => {
|
||||
const { customValidate } = this.props;
|
||||
if (customValidate) {
|
||||
return customValidate(value);
|
||||
} else {
|
||||
const emailObj = parseAddress(value, emailSettings);
|
||||
const isValidEmail = emailObj.isValid();
|
||||
const parsedErrors = emailObj.parseErrors;
|
||||
const errors = parsedErrors
|
||||
? parsedErrors.map(error => error.errorKey)
|
||||
: [];
|
||||
return {
|
||||
value,
|
||||
isValid: isValidEmail,
|
||||
errors
|
||||
};
|
||||
}
|
||||
};
|
||||
|
||||
onChange = e => {
|
||||
const { onChange, onValidateInput } = this.props;
|
||||
onChange ? onChange(e) : this.setState({ inputValue: e.target.value });
|
||||
|
||||
const isValidEmail = this.checkEmail(e.target.value);
|
||||
this.setState({ isValidEmail });
|
||||
|
||||
onValidateInput && onValidateInput(isValidEmail);
|
||||
};
|
||||
|
||||
render() {
|
||||
//console.log('EmailInput render()');
|
||||
// eslint-disable-next-line no-unused-vars
|
||||
const { onValidateInput, emailSettings, onChange, isValid, value, ...rest } = this.props;
|
||||
const {
|
||||
onValidateInput,
|
||||
hasError
|
||||
} = this.props;
|
||||
|
||||
const { isValidEmail, inputValue } = this.state;
|
||||
const isError =
|
||||
typeof hasError === "boolean"
|
||||
? hasError
|
||||
: Boolean(inputValue && !isValidEmail.isValid);
|
||||
|
||||
return (
|
||||
<StyledTextInput
|
||||
isValidEmail={isValid || isValidEmail}
|
||||
<TextInputWrapper
|
||||
{...this.props}
|
||||
hasError={isError}
|
||||
value={inputValue}
|
||||
onChange={this.onChangeAction}
|
||||
type='text'
|
||||
onChange={this.onChange}
|
||||
type="text"
|
||||
onValidateInput={onValidateInput}
|
||||
{...rest}
|
||||
/>
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
EmailInput.propTypes = {
|
||||
onValidateInput: PropTypes.func,
|
||||
onChange: PropTypes.func,
|
||||
customValidateFunc: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
isValid: PropTypes.bool,
|
||||
emailSettings: PropTypes.oneOfType([PropTypes.instanceOf(EmailSettings), PropTypes.objectOf(PropTypes.bool)]),
|
||||
className: PropTypes.string,
|
||||
customValidate: PropTypes.func,
|
||||
emailSettings: PropTypes.oneOfType([
|
||||
PropTypes.instanceOf(EmailSettings),
|
||||
PropTypes.objectOf(PropTypes.bool)
|
||||
]),
|
||||
hasError: PropTypes.bool,
|
||||
id: PropTypes.string,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array])
|
||||
}
|
||||
onChange: PropTypes.func,
|
||||
onValidateInput: PropTypes.func,
|
||||
style: PropTypes.oneOfType([PropTypes.object, PropTypes.array]),
|
||||
value: PropTypes.string
|
||||
};
|
||||
|
||||
EmailInput.defaultProps = {
|
||||
id: '',
|
||||
name: '',
|
||||
autoComplete: 'email',
|
||||
maxLength: 255,
|
||||
value: '',
|
||||
autoComplete: "email",
|
||||
className: "",
|
||||
hasError: undefined,
|
||||
id: "",
|
||||
isDisabled: false,
|
||||
isReadOnly: false,
|
||||
size: 'base',
|
||||
maxLength: 255,
|
||||
name: "",
|
||||
placeholder: "",
|
||||
scale: false,
|
||||
size: "base",
|
||||
title: "",
|
||||
value: "",
|
||||
withBorder: true,
|
||||
placeholder: '',
|
||||
className: '',
|
||||
title: '',
|
||||
isValid: undefined,
|
||||
|
||||
emailSettings: new EmailSettings()
|
||||
}
|
||||
};
|
||||
|
||||
export default EmailInput;
|
||||
|
@ -113,12 +113,20 @@ class ModalDialog extends React.Component {
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("resize", this.throttledResize);
|
||||
window.addEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("resize", this.throttledResize);
|
||||
window.removeEventListener("keyup", this.onKeyPress);
|
||||
}
|
||||
|
||||
onKeyPress = event => {
|
||||
if (event.key === "Esc" || event.key === "Escape") {
|
||||
this.props.onClose();
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
const {
|
||||
visible,
|
||||
|
@ -7,3 +7,17 @@ export const parseErrorTypes = Object.freeze({
|
||||
EmptyRecipients: 1,
|
||||
IncorrectEmail: 2
|
||||
});
|
||||
|
||||
export const errorKeys = Object.freeze({
|
||||
LocalDomain: "LocalDomain",
|
||||
IncorrectDomain: "IncorrectDomain",
|
||||
DomainIpAddress: "DomainIpAddress",
|
||||
PunycodeDomain: "PunycodeDomain",
|
||||
PunycodeLocalPart: "PunycodeLocalPart",
|
||||
IncorrectLocalPart: "IncorrectLocalPart",
|
||||
SpacesInLocalPart: "SpacesInLocalPart",
|
||||
MaxLengthExceeded: "MaxLengthExceeded",
|
||||
IncorrectEmail: "IncorrectEmail",
|
||||
ManyEmails: "ManyEmails",
|
||||
EmptyEmail: "EmptyEmail"
|
||||
});
|
||||
|
@ -1,6 +1,6 @@
|
||||
import emailAddresses from "email-addresses";
|
||||
import punycode from "punycode";
|
||||
import { parseErrorTypes } from "./../constants";
|
||||
import { parseErrorTypes, errorKeys } from "./../constants";
|
||||
import { EmailSettings } from './emailSettings';
|
||||
|
||||
const getParts = string => {
|
||||
@ -70,7 +70,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Local domains are not supported",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.LocalDomain
|
||||
});
|
||||
}
|
||||
|
||||
@ -79,7 +80,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Incorrect domain",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.IncorrectDomain
|
||||
});
|
||||
}
|
||||
|
||||
@ -91,7 +93,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Domains as ip address are not supported",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.DomainIpAddress
|
||||
});
|
||||
}
|
||||
|
||||
@ -99,7 +102,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Punycode domains are not supported",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.PunycodeDomain
|
||||
});
|
||||
}
|
||||
|
||||
@ -107,7 +111,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Punycode local part are not supported",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.PunycodeLocalPart
|
||||
});
|
||||
}
|
||||
|
||||
@ -119,7 +124,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Incorrect localpart",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.IncorrectLocalPart
|
||||
});
|
||||
}
|
||||
|
||||
@ -131,7 +137,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "Incorrect, localpart contains spaces",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.SpacesInLocalPart
|
||||
});
|
||||
}
|
||||
|
||||
@ -139,7 +146,8 @@ const checkErrors = (parsedAddress, options) => {
|
||||
errors.push({
|
||||
message: "The maximum total length of a user name or other local-part is 64 characters. See RFC2821",
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorItem: parsedAddress
|
||||
errorItem: parsedAddress,
|
||||
errorKey: errorKeys.MaxLengthExceeded
|
||||
});
|
||||
}
|
||||
|
||||
@ -168,7 +176,8 @@ export const parseAddresses = (str, options = new EmailSettings()) => {
|
||||
if (!parsedAddress || (parsedAddress.name && !options.allowName)) {
|
||||
errors.push({
|
||||
message: "Incorrect email",
|
||||
type: parseErrorTypes.IncorrectEmail
|
||||
type: parseErrorTypes.IncorrectEmail,
|
||||
errorKey: errorKeys.IncorrectEmail
|
||||
});
|
||||
} else {
|
||||
const checkOptionErrors = checkErrors(parsedAddress, options)
|
||||
@ -195,13 +204,13 @@ export const parseAddress = (str, options = new EmailSettings()) => {
|
||||
|
||||
if (!parsedEmails.length) {
|
||||
return new Email("", str, [
|
||||
{ message: "No one email parsed", type: parseErrorTypes.EmptyRecipients }
|
||||
{ message: "No one email parsed", type: parseErrorTypes.EmptyRecipients, errorKey: errorKeys.EmptyEmail }
|
||||
]);
|
||||
}
|
||||
|
||||
if (parsedEmails.length > 1) {
|
||||
return new Email("", str, [
|
||||
{ message: "Too many email parsed", type: parseErrorTypes.IncorrectEmail }
|
||||
{ message: "Too many email parsed", type: parseErrorTypes.IncorrectEmail, errorKey: errorKeys.ManyEmails }
|
||||
]);
|
||||
}
|
||||
|
||||
|
@ -9,6 +9,28 @@ export class EmailSettings {
|
||||
this.allowLocalDomainName = false;
|
||||
}
|
||||
|
||||
static equals = (settings1, settings2) => {
|
||||
const instance1 = EmailSettings.parse(settings1);
|
||||
const instance2 = EmailSettings.parse(settings2);
|
||||
const comparedProperties = [
|
||||
'allowDomainPunycode',
|
||||
'allowLocalPartPunycode',
|
||||
'allowDomainIp',
|
||||
'allowStrictLocalPart',
|
||||
'allowSpaces',
|
||||
'allowName',
|
||||
'allowLocalDomainName'
|
||||
];
|
||||
const propLength = comparedProperties.length;
|
||||
for (let i = 0; i < propLength; i++) {
|
||||
const comparedProp = comparedProperties[i]
|
||||
if (instance1[comparedProp] !== instance2[comparedProp]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
get allowDomainPunycode() {
|
||||
return this._allowDomainPunycode;
|
||||
}
|
||||
@ -18,7 +40,7 @@ export class EmailSettings {
|
||||
this._allowDomainPunycode = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowDomainPunycode option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowDomainPunycode option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -31,7 +53,7 @@ export class EmailSettings {
|
||||
this._allowLocalPartPunycode = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowLocalPartPunycode option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowLocalPartPunycode option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,7 +66,7 @@ export class EmailSettings {
|
||||
this._allowDomainIp = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowDomainIp option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowDomainIp option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -57,7 +79,7 @@ export class EmailSettings {
|
||||
this._allowStrictLocalPart = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowStrictLocalPart option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowStrictLocalPart option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -70,7 +92,7 @@ export class EmailSettings {
|
||||
this._allowSpaces = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowSpaces option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowSpaces option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -83,7 +105,7 @@ export class EmailSettings {
|
||||
this._allowName = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowName option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowName option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
@ -96,11 +118,11 @@ export class EmailSettings {
|
||||
this._allowLocalDomainName = value;
|
||||
}
|
||||
else {
|
||||
throw new TypeError (`Invalid value ${value} for allowLocalDomainName option. Use boolean value`);
|
||||
throw new TypeError(`Invalid value ${value} for allowLocalDomainName option. Use boolean value`);
|
||||
}
|
||||
}
|
||||
|
||||
getSettings() {
|
||||
toObject() {
|
||||
return {
|
||||
allowDomainPunycode: this.allowDomainPunycode,
|
||||
allowLocalPartPunycode: this.allowLocalPartPunycode,
|
||||
@ -121,40 +143,22 @@ export class EmailSettings {
|
||||
this.allowName = true;
|
||||
this.allowLocalDomainName = true;
|
||||
}
|
||||
}
|
||||
|
||||
export const checkAndConvertEmailSettings = (settings) => {
|
||||
if (typeof settings === 'object' && !(settings instanceof EmailSettings)) {
|
||||
static parse = (settings) => {
|
||||
if(settings instanceof EmailSettings)
|
||||
return settings;
|
||||
|
||||
if(typeof settings !== 'object')
|
||||
throw new Error("Invalid argument");
|
||||
|
||||
const defaultSettings = new EmailSettings();
|
||||
Object.keys(settings).map((item) => {
|
||||
if (defaultSettings[item] !== null && defaultSettings[item] != settings[item]) {
|
||||
defaultSettings[item] = settings[item];
|
||||
}
|
||||
Object.keys(settings).map((key) => {
|
||||
if (!(key in defaultSettings) || defaultSettings[key] === settings[key])
|
||||
return;
|
||||
|
||||
defaultSettings[key] = settings[key];
|
||||
});
|
||||
|
||||
return defaultSettings;
|
||||
}
|
||||
|
||||
else if (typeof settings === 'object' && settings instanceof EmailSettings) {
|
||||
return settings;
|
||||
}
|
||||
}
|
||||
|
||||
export const isEqualEmailSettings = (settings1, settings2) => {
|
||||
const comparedProperties = [
|
||||
'allowDomainPunycode',
|
||||
'allowLocalPartPunycode',
|
||||
'allowDomainIp',
|
||||
'allowStrictLocalPart',
|
||||
'allowSpaces',
|
||||
'allowName',
|
||||
'allowLocalDomainName'
|
||||
];
|
||||
const propLength = comparedProperties.length;
|
||||
for (let i = 0; i < propLength; i++) {
|
||||
const comparedProp = comparedProperties[i]
|
||||
if (settings1[comparedProp] !== settings2[comparedProp]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
@ -1,203 +1,214 @@
|
||||
import { EmailSettings, isEqualEmailSettings, checkAndConvertEmailSettings } from './index';
|
||||
import { EmailSettings } from "./index";
|
||||
|
||||
const defaultEmailSettingsObj = {
|
||||
allowDomainPunycode: false,
|
||||
allowLocalPartPunycode: false,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: false
|
||||
allowDomainPunycode: false,
|
||||
allowLocalPartPunycode: false,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: false
|
||||
};
|
||||
describe('emailSettings', () => {
|
||||
describe("emailSettings", () => {
|
||||
it("get default settings from instance", () => {
|
||||
const email = new EmailSettings();
|
||||
const settings = email.toObject();
|
||||
expect(settings).toStrictEqual(defaultEmailSettingsObj);
|
||||
});
|
||||
|
||||
it('get default settings from instance', () => {
|
||||
it("change and get settings from instance", () => {
|
||||
const emailSettingsObj = {
|
||||
allowDomainPunycode: false,
|
||||
allowLocalPartPunycode: false,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: true
|
||||
};
|
||||
|
||||
const email = new EmailSettings();
|
||||
const settings = email.getSettings();
|
||||
expect(settings).toStrictEqual(defaultEmailSettingsObj);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalDomainName = true;
|
||||
const settings = emailSettings.toObject();
|
||||
|
||||
it('change and get settings from instance', () => {
|
||||
expect(settings).toStrictEqual(emailSettingsObj);
|
||||
});
|
||||
|
||||
const emailSettingsObj = {
|
||||
allowDomainPunycode: false,
|
||||
allowLocalPartPunycode: false,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: true
|
||||
};
|
||||
it("set and get allowStrictLocalPart setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowStrictLocalPart = false;
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalDomainName = true;
|
||||
const settings = emailSettings.getSettings();
|
||||
expect(emailSettings.allowStrictLocalPart).toBe(false);
|
||||
});
|
||||
|
||||
expect(settings).toStrictEqual(emailSettingsObj);
|
||||
});
|
||||
it("disable settings", () => {
|
||||
const disabledSettings = {
|
||||
allowDomainPunycode: true,
|
||||
allowLocalPartPunycode: true,
|
||||
allowDomainIp: true,
|
||||
allowStrictLocalPart: false,
|
||||
allowSpaces: true,
|
||||
allowName: true,
|
||||
allowLocalDomainName: true
|
||||
};
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.disableAllSettings();
|
||||
const newSettings = emailSettings.toObject();
|
||||
|
||||
it('set and get allowStrictLocalPart setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowStrictLocalPart = false;
|
||||
expect(newSettings).toStrictEqual(disabledSettings);
|
||||
});
|
||||
|
||||
expect(emailSettings.allowStrictLocalPart).toBe(false);
|
||||
});
|
||||
it("set invalid (non-boolean) value for allowLocalDomainName setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
it('disable settings', () => {
|
||||
try {
|
||||
emailSettings.allowLocalDomainName = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
const disabledSettings = {
|
||||
allowDomainPunycode: true,
|
||||
allowLocalPartPunycode: true,
|
||||
allowDomainIp: true,
|
||||
allowStrictLocalPart: false,
|
||||
allowSpaces: true,
|
||||
allowName: true,
|
||||
allowLocalDomainName: true
|
||||
};
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.disableAllSettings();
|
||||
const newSettings = emailSettings.getSettings();
|
||||
it("set invalid (non-boolean) value for allowDomainPunycode setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
expect(newSettings).toStrictEqual(disabledSettings);
|
||||
});
|
||||
try {
|
||||
emailSettings.allowDomainPunycode = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
it("set invalid (non-boolean) value for allowLocalPartPunycode setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
it('set invalid (non-boolean) value for allowLocalDomainName setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
try {
|
||||
emailSettings.allowLocalPartPunycode = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
emailSettings.allowLocalDomainName = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
it("set invalid (non-boolean) value for allowDomainIp setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
it('set invalid (non-boolean) value for allowDomainPunycode setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
try {
|
||||
emailSettings.allowDomainIp = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
emailSettings.allowDomainPunycode = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
it("set invalid (non-boolean) value for allowStrictLocalPart setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
it('set invalid (non-boolean) value for allowLocalPartPunycode setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
try {
|
||||
emailSettings.allowStrictLocalPart = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
emailSettings.allowLocalPartPunycode = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
it("set invalid (non-boolean) value for allowSpaces setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
it('set invalid (non-boolean) value for allowDomainIp setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
try {
|
||||
emailSettings.allowSpaces = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
emailSettings.allowDomainIp = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
it("set invalid (non-boolean) value for allowName setting", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
it('set invalid (non-boolean) value for allowStrictLocalPart setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
try {
|
||||
emailSettings.allowName = "1";
|
||||
} catch (err) {
|
||||
expect(err.name).toBe("TypeError");
|
||||
}
|
||||
});
|
||||
|
||||
try {
|
||||
emailSettings.allowStrictLocalPart = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
// test EmailSettings.equals function
|
||||
|
||||
it('set invalid (non-boolean) value for allowSpaces setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
it("is not equal email settings", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
const emailSettings2 = new EmailSettings();
|
||||
|
||||
try {
|
||||
emailSettings.allowSpaces = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
emailSettings.allowStrictLocalPart = false;
|
||||
const isEqual = EmailSettings.equals(emailSettings, emailSettings2);
|
||||
|
||||
it('set invalid (non-boolean) value for allowName setting', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
expect(isEqual).toBe(false);
|
||||
});
|
||||
|
||||
try {
|
||||
emailSettings.allowName = '1';
|
||||
} catch (err) {
|
||||
expect(err.name).toBe('TypeError');
|
||||
}
|
||||
});
|
||||
it("is equal email settings", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
const emailSettings2 = new EmailSettings();
|
||||
const isEqual = EmailSettings.equals(emailSettings, emailSettings2);
|
||||
|
||||
// test isEqualEmailSettings function
|
||||
expect(isEqual).toBe(true);
|
||||
});
|
||||
|
||||
it('is not equal email settings', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
const emailSettings2 = new EmailSettings();
|
||||
// test checkAndEmailSettings.parse function
|
||||
|
||||
emailSettings.allowStrictLocalPart = false;
|
||||
const isEqual = isEqualEmailSettings(emailSettings, emailSettings2);
|
||||
it("passed instance of default EmailSettings, return same instance", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
const convertedSettings = EmailSettings.parse(emailSettings);
|
||||
|
||||
expect(isEqual).toBe(false);
|
||||
});
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
|
||||
it('is equal email settings', () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
const emailSettings2 = new EmailSettings();
|
||||
const isEqual = isEqualEmailSettings(emailSettings, emailSettings2);
|
||||
it("passed object with default settings, return instance of default EmailSettings", () => {
|
||||
const convertedSettings = EmailSettings.parse(defaultEmailSettingsObj);
|
||||
const emailSettings = new EmailSettings();
|
||||
|
||||
expect(isEqual).toBe(true);
|
||||
});
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
|
||||
// test checkAndConvertEmailSettings function
|
||||
it("passed instance of EmailSettings, return same instance", () => {
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalDomainName = true;
|
||||
const convertedSettings = EmailSettings.parse(emailSettings);
|
||||
|
||||
it('passed instance of default EmailSettings, return same instance', () => {
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
const convertedSettings = checkAndConvertEmailSettings(emailSettings);
|
||||
it("passed object with settings, return instance of EmailSettings", () => {
|
||||
const emailSettingsObj = {
|
||||
allowDomainPunycode: true,
|
||||
allowLocalPartPunycode: true,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: false
|
||||
};
|
||||
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
const convertedSettings = EmailSettings.parse(emailSettingsObj);
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowDomainPunycode = true;
|
||||
emailSettings.allowLocalPartPunycode = true;
|
||||
|
||||
it('passed object with default settings, return instance of default EmailSettings', () => {
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
|
||||
const convertedSettings = checkAndConvertEmailSettings(defaultEmailSettingsObj);
|
||||
const emailSettings = new EmailSettings();
|
||||
it("passed invalid object with settings, return instance of EmailSettings", () => {
|
||||
const emailSettingsObj = {
|
||||
temp: "temp",
|
||||
allowDomainPunycode: true,
|
||||
allowLocalPartPunycode: true,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: false
|
||||
};
|
||||
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
const convertedSettings = EmailSettings.parse(emailSettingsObj);
|
||||
|
||||
it('passed instance of EmailSettings, return same instance', () => {
|
||||
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowLocalDomainName = true;
|
||||
const convertedSettings = checkAndConvertEmailSettings(emailSettings);
|
||||
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
|
||||
it('passed object with settings, return instance of EmailSettings', () => {
|
||||
|
||||
const emailSettingsObj = {
|
||||
allowDomainPunycode: true,
|
||||
allowLocalPartPunycode: true,
|
||||
allowDomainIp: false,
|
||||
allowStrictLocalPart: true,
|
||||
allowSpaces: false,
|
||||
allowName: false,
|
||||
allowLocalDomainName: false
|
||||
};
|
||||
|
||||
const convertedSettings = checkAndConvertEmailSettings(emailSettingsObj);
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowDomainPunycode = true;
|
||||
emailSettings.allowLocalPartPunycode = true;
|
||||
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
const emailSettings = new EmailSettings();
|
||||
emailSettings.allowDomainPunycode = true;
|
||||
emailSettings.allowLocalPartPunycode = true;
|
||||
|
||||
expect(convertedSettings).toStrictEqual(emailSettings);
|
||||
});
|
||||
});
|
||||
|
@ -1,2 +1,2 @@
|
||||
export { parseAddress, Email, isEqualEmail, isValidDomainName, parseAddresses } from './email';
|
||||
export { EmailSettings, checkAndConvertEmailSettings, isEqualEmailSettings } from './emailSettings';
|
||||
export { EmailSettings } from './emailSettings';
|
Loading…
Reference in New Issue
Block a user