# Conflicts:
#	web/ASC.Web.Components/package.json
This commit is contained in:
Andrey Savihin 2019-10-03 10:20:41 +03:00
commit c313881377
30 changed files with 816 additions and 346 deletions

View File

@ -8,8 +8,9 @@ const ArticleHeaderContent = ({currentModuleName}) => {
}
const mapStateToProps = (state) => {
const currentModule = getCurrentModule(state.auth.modules, state.auth.settings.currentProductId);
return {
currentModuleName: getCurrentModule(state.auth.modules, state.auth.settings.currentProductId).title
currentModuleName: (currentModule && currentModule.title) || ""
}
}

View File

@ -32,6 +32,9 @@ if (process.env.NODE_ENV === "production") {
const resources = {
en: {
translation: require("./locales/en/translation.json")
},
ru: {
translation: require("./locales/ru/translation.json")
}
};

View File

@ -38,9 +38,12 @@ const StyledContainer = styled.div`
}
.header-container {
position: relative;
display: flex;
align-items: center;
max-width: calc(100vw - 32px);
.add-group-button {
margin-left: 8px;
}
@ -225,7 +228,15 @@ const SectionHeaderContent = props => {
) : (
<>
<Text.ContentHeader>Departments</Text.ContentHeader>
{isAdmin && (
<IconButton
className="add-group-button"
size={16}
iconName="PlusIcon"
isFill={false}
onClick={onAddDepartmentsClick}
/>
)}
</>
)}
</div>

View File

@ -8,7 +8,6 @@ import { useTranslation } from 'react-i18next';
import { resendUserInvites } from "../../../../../store/services/api";
import { EmployeeStatus } from "../../../../../helpers/constants";
import { updateUserStatus } from "../../../../../store/people/actions";
import { callbackify } from "util";
import styled from 'styled-components';
const wrapperStyle = {

View File

@ -43,30 +43,22 @@ class PureProfile extends React.Component {
render() {
console.log("Profile render")
const { profile } = this.props;
return (
profile
?
<PageLayout
articleHeaderContent={<ArticleHeaderContent />}
articleMainButtonContent={<ArticleMainButtonContent />}
articleBodyContent={<ArticleBodyContent />}
sectionHeaderContent={
<SectionHeaderContent profile={profile} />
}
sectionBodyContent={
<SectionBodyContent profile={profile} />
}
/>
: <PageLayout
articleHeaderContent={<ArticleHeaderContent />}
articleMainButtonContent={<ArticleMainButtonContent />}
articleBodyContent={<ArticleBodyContent />}
sectionBodyContent={
<Loader className="pageLoader" type="rombs" size={40} />
}
/>
);
const { profile, isVisitor } = this.props;
const articleProps = isVisitor ? {} : {
articleHeaderContent: <ArticleHeaderContent />,
articleMainButtonContent: <ArticleMainButtonContent />,
articleBodyContent: <ArticleBodyContent />
};
const sectionProps = profile ? {
sectionHeaderContent: <SectionHeaderContent profile={profile} />,
sectionBodyContent: <SectionBodyContent profile={profile} />
} : {
sectionBodyContent: <Loader className="pageLoader" type="rombs" size={40} />
};
return <PageLayout {...articleProps} {...sectionProps} />;
};
};
@ -92,6 +84,7 @@ function mapStateToProps(state) {
return {
profile: state.profile.targetUser,
language: state.auth.user.cultureName || state.auth.settings.culture,
isVisitor: state.auth.user.isVisitor,
};
}

View File

@ -2,14 +2,21 @@ import React from "react";
import { connect } from "react-redux";
import PropTypes from "prop-types";
import { PageLayout, Loader } from "asc-web-components";
import { ArticleHeaderContent, ArticleMainButtonContent, ArticleBodyContent } from '../../Article';
import { SectionHeaderContent, CreateUserForm, UpdateUserForm } from './Section';
import { fetchProfile } from '../../../store/profile/actions';
import {
ArticleHeaderContent,
ArticleMainButtonContent,
ArticleBodyContent
} from "../../Article";
import {
SectionHeaderContent,
CreateUserForm,
UpdateUserForm
} from "./Section";
import { fetchProfile } from "../../../store/profile/actions";
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
class ProfileAction extends React.Component {
componentDidMount() {
const { match, fetchProfile } = this.props;
const { userId } = match.params;
@ -30,10 +37,10 @@ class ProfileAction extends React.Component {
}
render() {
console.log("ProfileAction render")
console.log("ProfileAction render");
let loaded = false;
const { profile, match, language } = this.props;
const { profile, isVisitor, match, language } = this.props;
const { userId, type } = match.params;
i18n.changeLanguage(language);
@ -44,22 +51,28 @@ class ProfileAction extends React.Component {
loaded = profile.userName === userId || profile.id === userId;
}
const articleProps = isVisitor
? {}
: {
articleHeaderContent: <ArticleHeaderContent />,
articleMainButtonContent: <ArticleMainButtonContent />,
articleBodyContent: <ArticleBodyContent />
};
const sectionProps = loaded
? {
sectionHeaderContent: <SectionHeaderContent />,
sectionBodyContent: type ? <CreateUserForm /> : <UpdateUserForm />
}
: {
sectionBodyContent: (
<Loader className="pageLoader" type="rombs" size={40} />
)
};
return (
<I18nextProvider i18n={i18n}>
{loaded
? <PageLayout
articleHeaderContent={<ArticleHeaderContent />}
articleMainButtonContent={<ArticleMainButtonContent />}
articleBodyContent={<ArticleBodyContent />}
sectionHeaderContent={<SectionHeaderContent />}
sectionBodyContent={type ? <CreateUserForm /> : <UpdateUserForm />}
/>
: <PageLayout
articleHeaderContent={<ArticleHeaderContent />}
articleMainButtonContent={<ArticleMainButtonContent />}
articleBodyContent={<ArticleBodyContent />}
sectionBodyContent={<Loader className="pageLoader" type="rombs" size={40} />}
/>}
<PageLayout {...articleProps} {...sectionProps} />
</I18nextProvider>
);
}
@ -75,9 +88,13 @@ function mapStateToProps(state) {
return {
profile: state.profile.targetUser,
language: state.auth.user.cultureName || state.auth.settings.culture,
isVisitor: state.auth.user.isVisitor,
};
}
export default connect(mapStateToProps, {
fetchProfile
})(ProfileAction);
export default connect(
mapStateToProps,
{
fetchProfile
}
)(ProfileAction);

View File

@ -1802,14 +1802,15 @@ asap@~2.0.6:
integrity sha1-5QNHYR1+aQlDIIu9r+vLwvuGbUY=
"asc-web-components@file:../../../packages/asc-web-components":
version "1.0.87"
version "1.0.102"
dependencies:
moment "^2.24.0"
prop-types "^15.7.2"
rc-tree "^2.1.2"
react-autosize-textarea "^7.0.0"
react-avatar-edit "^0.8.3"
react-avatar-editor "^11.0.7"
react-custom-scrollbars "^4.2.1"
react-dropzone "^10.1.8"
react-text-mask "^5.4.3"
react-toastify "^5.3.2"
react-virtualized-auto-sizer "^1.0.2"
@ -1897,6 +1898,13 @@ atob@^2.1.1:
resolved "https://registry.yarnpkg.com/atob/-/atob-2.1.2.tgz#6d9517eb9e030d2436666651e86bd9f6f13533c9"
integrity sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==
attr-accept@^1.1.3:
version "1.1.3"
resolved "https://registry.yarnpkg.com/attr-accept/-/attr-accept-1.1.3.tgz#48230c79f93790ef2775fcec4f0db0f5db41ca52"
integrity sha512-iT40nudw8zmCweivz6j58g+RT33I4KbaIvRUhjNmDwO2WmsQUxFEZZYZ5w3vXe5x5MX9D7mfvA/XaLOZYFR9EQ==
dependencies:
core-js "^2.5.0"
autoprefixer@^9.6.1:
version "9.6.1"
resolved "https://registry.yarnpkg.com/autoprefixer/-/autoprefixer-9.6.1.tgz#51967a02d2d2300bb01866c1611ec8348d355a47"
@ -2968,7 +2976,7 @@ core-js@3.1.4:
resolved "https://registry.yarnpkg.com/core-js/-/core-js-3.1.4.tgz#3a2837fc48e582e1ae25907afcd6cf03b0cc7a07"
integrity sha512-YNZN8lt82XIMLnLirj9MhKDFZHalwzzrL9YLt6eb0T5D0EDl4IQ90IGkua8mHbnxNrkj1d8hbdizMc0Qmg1WnQ==
core-js@^2.4.0, core-js@^2.6.4:
core-js@^2.4.0, core-js@^2.5.0, core-js@^2.6.4:
version "2.6.9"
resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.9.tgz#6b4b214620c834152e179323727fc19741b084f2"
integrity sha512-HOpZf6eXmnl7la+cUdMnLvUxKNqLUzJvgIziQ0DiF3JwSImNphIqdGqzj6hIKyX04MmV0poclQ7+wjWvxQyR2A==
@ -4320,6 +4328,13 @@ file-loader@3.0.1:
loader-utils "^1.0.2"
schema-utils "^1.0.0"
file-selector@^0.1.11:
version "0.1.12"
resolved "https://registry.yarnpkg.com/file-selector/-/file-selector-0.1.12.tgz#fe726547be219a787a9dcc640575a04a032b1fd0"
integrity sha512-Kx7RTzxyQipHuiqyZGf+Nz4vY9R1XGxuQl/hLoJwq+J4avk/9wxxgZyHKtbyIPJmbD4A66DWGYfyykWNpcYutQ==
dependencies:
tslib "^1.9.0"
filesize@3.6.1:
version "3.6.1"
resolved "https://registry.yarnpkg.com/filesize/-/filesize-3.6.1.tgz#090bb3ee01b6f801a8a8be99d31710b3422bb317"
@ -6298,11 +6313,6 @@ kleur@^3.0.3:
resolved "https://registry.yarnpkg.com/kleur/-/kleur-3.0.3.tgz#a79c9ecc86ee1ce3fa6206d1216c501f147fc07e"
integrity sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==
konva@2.5.1:
version "2.5.1"
resolved "https://registry.yarnpkg.com/konva/-/konva-2.5.1.tgz#cca611a9522e831e54cf57c508a1aed3f0ceac25"
integrity sha512-YdHEWqmbWPieqIZuLx7JFGm9Ui08hSUaSJ2k2Ml8o5giFgJ0WmxAS0DPXIM+Ty2ADRagOHZfXSJ/skwYqqlwgQ==
last-call-webpack-plugin@^3.0.0:
version "3.0.0"
resolved "https://registry.yarnpkg.com/last-call-webpack-plugin/-/last-call-webpack-plugin-3.0.0.tgz#9742df0e10e3cf46e5c0381c2de90d3a7a2d7555"
@ -8736,12 +8746,12 @@ react-autosize-textarea@^7.0.0:
line-height "^0.3.1"
prop-types "^15.5.6"
react-avatar-edit@^0.8.3:
version "0.8.3"
resolved "https://registry.yarnpkg.com/react-avatar-edit/-/react-avatar-edit-0.8.3.tgz#0ebf21391328fc255429bdfbc782f795827109bf"
integrity sha512-QEedh6DjDCSI7AUsUHHtfhxApCWC5hJAoywxUA5PtUdw03iIjEurgVqPOIt1UBHhU/Zk/9amElRF3oepN9JZSg==
react-avatar-editor@^11.0.7:
version "11.0.7"
resolved "https://registry.yarnpkg.com/react-avatar-editor/-/react-avatar-editor-11.0.7.tgz#021053cfeaa138407b79279ee5a0384f273f0c54"
integrity sha512-GbNYBd1/L1QyuU9VRvOW0hSkW1R0XSneOWZFgqI5phQf6dX+dF/G3/AjiJ0hv3JWh2irMQ7DL0oYDKzwtTnNBQ==
dependencies:
konva "2.5.1"
prop-types "^15.5.8"
react-custom-scrollbars@^4.2.1:
version "4.2.1"
@ -8798,6 +8808,15 @@ react-dom@^16.9.0:
prop-types "^15.6.2"
scheduler "^0.15.0"
react-dropzone@^10.1.8:
version "10.1.9"
resolved "https://registry.yarnpkg.com/react-dropzone/-/react-dropzone-10.1.9.tgz#8093ecd7d2dc4002280eb2dac1d5fa4216c800ee"
integrity sha512-7iqALZ0mzk+4g/AsYxEy3QyWPMTVQYKQVkYUe9zIbH18u+pi7EBDg010KEwfIX6jeTDH2qP0E6/eUnXvBYrovA==
dependencies:
attr-accept "^1.1.3"
file-selector "^0.1.11"
prop-types "^15.7.2"
react-error-overlay@^6.0.1:
version "6.0.1"
resolved "https://registry.yarnpkg.com/react-error-overlay/-/react-error-overlay-6.0.1.tgz#b8d3cf9bb991c02883225c48044cb3ee20413e0f"

View File

@ -21,7 +21,7 @@ const App = () => {
<Switch>
<PublicRoute exact path={["/login","/login/error=:error", "/login/confirmed-email=:confirmedEmail"]} component={Login} />
<Route path="/confirm" component={Confirm} />
<PrivateRoute exact path="/" component={Home} />
<PrivateRoute exact path={["/","/error=:error"]} component={Home} />
<PrivateRoute exact path="/about" component={About} />
<PrivateRoute component={Error404} />
</Switch>

View File

@ -2,18 +2,18 @@ import React, { Suspense, lazy } from "react";
import { connect } from "react-redux";
import { Redirect, Route, Switch } from "react-router-dom";
import { Loader } from "asc-web-components";
import PublicRoute from "../../../helpers/publicRoute";
import ConfirmRoute from "../../../helpers/confirmRoute";
import i18n from "./i18n";
import { I18nextProvider } from "react-i18next";
import ActivateEmailForm from "./sub-components/activateEmail";
const ActivateUserForm = lazy(() => import("./sub-components/activateUser"));
const CreateUserForm = lazy(() => import("./sub-components/createUser"));
const ChangePasswordForm = lazy(() => import("./sub-components/changePassword"));
// const ActivateEmailForm = lazy(() => import("./sub-components/activateEmail"));
const ActivateEmailForm = lazy(() => import("./sub-components/activateEmail"));
const ChangeEmailForm = lazy(() => import("./sub-components/changeEmail"));
const ChangePhoneForm = lazy(() => import("./sub-components/changePhone"));
const ProfileRemoveForm = lazy(() => import("./sub-components/profileRemove"));
const Error404 = lazy(() => import("../Error"));
const Confirm = ({ match, language }) => {
@ -49,12 +49,17 @@ const Confirm = ({ match, language }) => {
path={`${match.path}/PasswordChange`}
component={ChangePasswordForm}
/>
<ConfirmRoute
exact
path={`${match.path}/ProfileRemove`}
component={ProfileRemoveForm}
/>
<Route
exact
path={`${match.path}/PhoneActivation`}
component={ChangePhoneForm}
/>
<Redirect to={{ pathname: "/" }} />
<Route component={Error404} />
</Switch>
</Suspense>
</I18nextProvider >

View File

@ -18,6 +18,11 @@
"ImportContactsOkButton": "OK",
"LoadingProcessing": "Loading...",
"ChangePasswordSuccess": "Password has been successfully changed",
"DeleteProfileBtn": "Delete my account",
"DeleteProfileConfirmation": "Attention! You are about to delete your account.",
"DeleteProfileConfirmationInfo": "By clicking the \"Delete my account\" button you agree with our Privacy policy.",
"DeleteProfileSuccessMessage": "Your account has been successfully deleted.",
"DeleteProfileSuccessMessageInfo": "See our Privacy policy to learn more about deleting your account and data accociated with it.",
"CustomWelcomePageTitle": "{{welcomePageTitle}}"

View File

@ -7,7 +7,7 @@ import { Collapse } from 'reactstrap';
import { connect } from 'react-redux';
import { welcomePageTitle } from './../../../../helpers/customNames';
import { EmployeeActivationStatus } from './../../../../helpers/constants';
import { getConfirmationInfo, activateConfirmUser } from '../../../../store/auth/actions';
import { getConfirmationInfo, activateConfirmUser, logout } from '../../../../store/auth/actions';
import PropTypes from 'prop-types';
const inputWidth = '400px';
@ -74,7 +74,7 @@ class Confirm extends React.PureComponent {
onSubmit = (e) => {
this.setState({ isLoading: true }, function () {
const { history, activateConfirmUser } = this.props;
const { activateConfirmUser, logout } = this.props;
this.setState({ errorText: "" });
@ -111,9 +111,9 @@ class Confirm extends React.PureComponent {
firstname: this.state.firstName,
lastname: this.state.lastName
};
logout();
activateConfirmUser(personalData, loginData, this.state.key, this.state.userId, EmployeeActivationStatus.Activated)
.then(() => history.push('/'))
.then(() => window.location.href = '/')
.catch(e => {
console.error("activate error", e);
this.setState({ errorText: e.message });
@ -321,4 +321,4 @@ function mapStateToProps(state) {
};
}
export default connect(mapStateToProps, { getConfirmationInfo, activateConfirmUser })(withRouter(withTranslation()(ActivateUserForm)));
export default connect(mapStateToProps, { getConfirmationInfo, activateConfirmUser,logout })(withRouter(withTranslation()(ActivateUserForm)));

View File

@ -101,6 +101,7 @@ class Form extends React.PureComponent {
toastr.success(this.props.t("ChangePasswordSuccess"));
history.push("/");
})
//.catch((res) => toastr.error(res.data))
.catch(e => {
toastr.error(this.props.t(`${e.message}`));
this.setState({ isLoading: false });
@ -110,7 +111,8 @@ class Form extends React.PureComponent {
componentDidMount() {
const { getConfirmationInfo, history } = this.props;
getConfirmationInfo(this.state.key).catch(e => {
getConfirmationInfo(this.state.key)
.catch(e => {
toastr.error(this.props.t(`${e.message}`));
history.push("/");
});

View File

@ -6,7 +6,7 @@ import styled from 'styled-components';
import { Collapse } from 'reactstrap';
import { connect } from 'react-redux';
import { welcomePageTitle } from './../../../../helpers/customNames';
import { getConfirmationInfo, createConfirmUser } from '../../../../store/auth/actions';
import { getConfirmationInfo, createConfirmUser, logout } from '../../../../store/auth/actions';
import PropTypes from 'prop-types';
const inputWidth = '400px';
@ -73,7 +73,7 @@ class Confirm extends React.PureComponent {
onSubmit = (e) => {
this.setState({ isLoading: true }, function () {
const { history, createConfirmUser, linkData } = this.props;
const { history, createConfirmUser, logout, linkData } = this.props;
const isVisitor = parseInt(linkData.emplType) === 2;
this.setState({ errorText: "" });
@ -118,8 +118,9 @@ class Confirm extends React.PureComponent {
email: this.state.email
};
const registerData = Object.assign(personalData, { isVisitor: isVisitor })
logout();
createConfirmUser(registerData, loginData, this.state.key)
.then(() => history.push('/'))
.then(() => window.location.href = '/')
.catch(e => {
console.error("confirm error", e);
this.setState({
@ -340,4 +341,4 @@ function mapStateToProps(state) {
};
}
export default connect(mapStateToProps, { getConfirmationInfo, createConfirmUser })(withRouter(withTranslation()(CreateUserForm)));
export default connect(mapStateToProps, { getConfirmationInfo, createConfirmUser, logout })(withRouter(withTranslation()(CreateUserForm)));

View File

@ -0,0 +1,113 @@
import React from 'react';
import { withRouter } from "react-router";
import { Button, PageLayout, Text } from 'asc-web-components';
import styled from 'styled-components';
import { welcomePageTitle } from './../../../../helpers/customNames';
import PropTypes from 'prop-types';
import { withTranslation } from 'react-i18next';
import { deleteUser, updateUserStatus } from './../../../../store/services/api'
import { EmployeeStatus } from './../../../../helpers/constants';
const ProfileRemoveContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
.start-basis {
align-items: flex-start;
}
.confirm-row {
margin: 23px 0 0;
}
.break-word {
word-break: break-word;
}
`;
class ProfileRemove extends React.PureComponent {
constructor() {
super();
this.state = {
isProfileDeleted: false
};
}
onDeleteProfile = (e) => {
this.setState({ isLoading: true }, function () {
const { linkData } = this.props;
updateUserStatus(EmployeeStatus.Disabled, [linkData.uid], linkData.confirmHeader)
.then((res) => {
console.log('success update status', res)
return deleteUser(linkData.uid);
})
.then((res) => {
this.setState({
isLoading: false,
isProfileDeleted: true
});
console.log('success delete', res)
})
.catch((e) => {
this.setState({ isLoading: false });
console.log('error delete', e)
})
});
};
render() {
console.log('profileRemove render');
const { t } = this.props;
const { isProfileDeleted } = this.state;
return (
<ProfileRemoveContainer>
<div className='start-basis'>
<div className='confirm-row full-width break-word'>
<a href='/login'>
<img src="images/dark_general.png" alt="Logo" />
</a>
<Text.Body as='p' fontSize={24} color='#116d9d'>{t('CustomWelcomePageTitle', { welcomePageTitle })}</Text.Body>
</div>
{!isProfileDeleted
? <>
<Text.Body className='confirm-row' as='p' fontSize={18} >{t('DeleteProfileConfirmation')}</Text.Body>
<Text.Body className='confirm-row' as='p' fontSize={16} >{t('DeleteProfileConfirmationInfo')}</Text.Body>
<Button
className='confirm-row'
primary
size='big'
label={t('DeleteProfileBtn')}
tabIndex={1}
isLoading={this.state.isLoading}
onClick={this.onDeleteProfile}
/>
</>
: <>
<Text.Body className='confirm-row' as='p' fontSize={18} >{t('DeleteProfileSuccessMessage')}</Text.Body>
<Text.Body className='confirm-row' as='p' fontSize={16} >{t('DeleteProfileSuccessMessageInfo')}</Text.Body>
</>
}
</div>
</ProfileRemoveContainer>
);
}
}
ProfileRemove.propTypes = {
location: PropTypes.object.isRequired,
};
const ProfileRemoveForm = (props) => (<PageLayout sectionBodyContent={<ProfileRemove {...props} />} />);
export default withRouter(withTranslation()(ProfileRemoveForm));

View File

@ -1,9 +1,9 @@
import React from 'react';
import React, { useEffect } from 'react';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import { withRouter } from "react-router";
import { Container, Col, Row, Collapse } from 'reactstrap';
import { ModuleTile, Loader, PageLayout } from 'asc-web-components';
import { ModuleTile, Loader, PageLayout, toastr } from 'asc-web-components';
import { useTranslation } from 'react-i18next';
import i18n from './i18n';
@ -30,8 +30,14 @@ Tiles.propTypes = {
history: PropTypes.object.isRequired
};
const Body = ({ modules, history, isLoaded }) => {
const Body = ({ modules, match, history, isLoaded }) => {
const { t } = useTranslation('translation', { i18n });
const { params } = match;
useEffect(() => {
params.error && toastr.error(params.error);
}, [params.error]);
return (
!isLoaded
? (

View File

@ -1,217 +1,320 @@
import React, { useState, useEffect, useCallback } from 'react';
import PropTypes from 'prop-types';
import React, { useState, useEffect, useCallback } from "react";
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import { Collapse, Container, Row, Col, Card, CardTitle, CardImg } from 'reactstrap';
import { Button, TextInput, PageLayout, Text } from 'asc-web-components';
import { connect } from 'react-redux';
import { login } from '../../../store/auth/actions';
import styled from 'styled-components';
import { useTranslation } from 'react-i18next';
import i18n from './i18n';
import { welcomePageTitle } from './../../../helpers/customNames';
import {
Collapse,
Container,
Row,
Col,
Card,
CardTitle,
CardImg
} from "reactstrap";
import {
Button,
TextInput,
PageLayout,
Text,
Link,
toastr
} from "asc-web-components";
import { connect } from "react-redux";
import { login } from "../../../store/auth/actions";
import styled from "styled-components";
import { useTranslation } from "react-i18next";
import i18n from "./i18n";
import { welcomePageTitle } from "./../../../helpers/customNames";
import { sendInstructionsToChangePassword } from "../../../store/services/api";
import SubModalDialog from "./sub-components/modal-dialog";
const FormContainer = styled(Container)`
margin-top: 70px;
margin-top: 70px;
.login-row {
margin: 23px 0 0;
.link-style {
float: right;
}
.login-card {
border: none;
.text-body {
margin-bottom: 16px;
}
.card-img {
max-width: 216px;
max-height: 35px;
}
.card-title {
word-wrap: break-word;
margin: 8px 0;
text-align: left;
font-size: 24px;
color: #116d9d;
}
}
.btn-style {
margin-right: 8px;
}
.login-row {
margin: 23px 0 0;
.login-card {
border: none;
.card-img {
max-width: 216px;
max-height: 35px;
}
.card-title {
word-wrap: break-word;
margin: 8px 0;
text-align: left;
font-size: 24px;
color: #116d9d;
}
}
}
`;
const mdOptions = { size: 6, offset: 3 };
const Form = props => {
const { t } = useTranslation('translation', { i18n });
const { login, match, location, history, language } = props;
const { params } = match;
const [identifier, setIdentifier] = useState(params.confirmedEmail || '');
const [identifierValid, setIdentifierValid] = useState(true);
const [password, setPassword] = useState('');
const [passwordValid, setPasswordValid] = useState(true);
const [errorText, setErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const { t } = useTranslation("translation", { i18n });
const { login, match, location, history, language } = props;
const { params } = match;
const [identifier, setIdentifier] = useState(params.confirmedEmail || "");
const [identifierValid, setIdentifierValid] = useState(true);
const [password, setPassword] = useState("");
const [passwordValid, setPasswordValid] = useState(true);
const [errorText, setErrorText] = useState("");
const [isLoading, setIsLoading] = useState(false);
const onSubmit = useCallback((e) => {
//e.preventDefault();
const [openDialog, setOpenDialog] = useState(false);
const [email, setEmail] = useState("");
const [isDisabled, setIsDisabled] = useState(false);
errorText && setErrorText("");
const onClick = () => {
setOpenDialog(true);
setIsDisabled(true);
setEmail(identifier);
};
let hasError = false;
const onDialogClose = () => {
setOpenDialog(false);
setIsDisabled(false);
setIsLoading(false);
setEmail("");
};
if (!identifier.trim()) {
hasError = true;
setIdentifierValid(!hasError);
}
const onSendPasswordInstructions = useCallback(() => {
setIsLoading(true);
sendInstructionsToChangePassword(email)
.then(res => {
res.data.error
? toastr.error(res.data.error.message)
: toastr.success(res.data.response);
})
.catch(error => toastr.error(error.message))
.finally(onDialogClose());
}, [email]);
if (!password.trim()) {
hasError = true;
setPasswordValid(!hasError);
}
const onSubmit = useCallback(() => {
errorText && setErrorText("");
let hasError = false;
if (hasError)
return false;
setIsLoading(true);
const payload = {
userName: identifier,
password: password
};
login(payload)
.then(function () {
console.log("auth success", match, location, history);
setIsLoading(false)
history.push('/');
})
.catch(e => {
console.error("auth error", e);
setErrorText(e.message);
setIsLoading(false)
});
}, [errorText, history, identifier, location, login, match, password]);
const onKeyPress = useCallback((event) => {
if (event.key === "Enter") {
onSubmit();
}
}, [onSubmit]);
useEffect(() => {
i18n.changeLanguage(language);
params.error && setErrorText(params.error);
window.addEventListener('keydown', onKeyPress);
window.addEventListener('keyup', onKeyPress);
// Remove event listeners on cleanup
return () => {
window.removeEventListener('keydown', onKeyPress);
window.removeEventListener('keyup', onKeyPress);
};
}, [onKeyPress, params, language]);
const onChangePassword = event => {
setPassword(event.target.value);
!passwordValid && setPasswordValid(true);
errorText && setErrorText("");
if (!identifier.trim()) {
hasError = true;
setIdentifierValid(!hasError);
}
const onChangeLogin = event => {
setIdentifier(event.target.value);
!identifierValid && setIdentifierValid(true);
errorText && setErrorText("");
if (!password.trim()) {
hasError = true;
setPasswordValid(!hasError);
}
// console.log('Login render');
if (hasError) return false;
return (
<FormContainer>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<Card className="login-card">
<CardImg className="card-img" src="images/dark_general.png" alt="Logo" top />
<CardTitle className="card-title">{t('CustomWelcomePageTitle', { welcomePageTitle })}</CardTitle>
</Card>
</Col>
</Row>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<TextInput
id="login"
name="login"
hasError={!identifierValid}
value={identifier}
placeholder={t('RegistrationEmailWatermark')}
size='huge'
scale={true}
isAutoFocussed={true}
tabIndex={1}
isDisabled={isLoading}
autoComplete="username"
onChange={onChangeLogin}
onKeyDown={onKeyPress} />
</Col>
</Row>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<TextInput
id="password"
name="password"
type="password"
hasError={!passwordValid}
value={password}
placeholder={t('Password')}
size='huge'
scale={true}
tabIndex={2}
isDisabled={isLoading}
autoComplete="current-password"
onChange={onChangePassword}
onKeyDown={onKeyPress} />
</Col>
</Row>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<Button
primary
size='big'
label={isLoading ? t('LoadingProcessing') : t('LoginButton')}
tabIndex={3}
isDisabled={isLoading}
isLoading={isLoading}
onClick={onSubmit} />
</Col>
</Row>
{params.confirmedEmail && <Row className="login-row">
<Col sm="12" md={mdOptions}>
<Text.Body isBold={true} fontSize={16}>{t('MessageEmailConfirmed')} {t('MessageAuthorize')}</Text.Body>
</Col>
</Row>
}
<Collapse isOpen={!!errorText}>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<div className="alert alert-danger">{errorText}</div>
</Col>
</Row>
</Collapse>
</FormContainer>
);
}
setIsLoading(true);
const LoginForm = (props) => (<PageLayout sectionBodyContent={<Form {...props} />} />);
const payload = {
userName: identifier,
password: password
};
login(payload)
.then(function() {
console.log("auth success", match, location, history);
setIsLoading(false);
history.push("/");
})
.catch(e => {
console.error("auth error", e);
setErrorText(e.message);
setIsLoading(false);
});
}, [errorText, history, identifier, location, login, match, password]);
const onKeyPress = useCallback(
event => {
if (event.key === "Enter") {
!isDisabled ? onSubmit() : onSendPasswordInstructions();
}
},
[onSendPasswordInstructions, onSubmit, isDisabled]
);
useEffect(() => {
i18n.changeLanguage(language);
params.error && setErrorText(params.error);
window.addEventListener("keyup", onKeyPress);
// Remove event listeners on cleanup
return () => {
window.removeEventListener("keyup", onKeyPress);
};
}, [onKeyPress, params, language]);
const onChangePassword = event => {
setPassword(event.target.value);
!passwordValid && setPasswordValid(true);
errorText && setErrorText("");
};
const onChangeLogin = event => {
setIdentifier(event.target.value);
!identifierValid && setIdentifierValid(true);
errorText && setErrorText("");
};
const onChangeEmail = event => {
setEmail(event.target.value);
};
// console.log('Login render');
return (
<FormContainer>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<Card className="login-card">
<CardImg
className="card-img"
src="images/dark_general.png"
alt="Logo"
top
/>
<CardTitle className="card-title">
{t("CustomWelcomePageTitle", { welcomePageTitle })}
</CardTitle>
</Card>
</Col>
</Row>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<TextInput
id="login"
name="login"
hasError={!identifierValid}
value={identifier}
placeholder={t("RegistrationEmailWatermark")}
size="huge"
scale={true}
isAutoFocussed={true}
tabIndex={1}
isDisabled={isLoading}
autoComplete="username"
onChange={onChangeLogin}
onKeyDown={onKeyPress}
/>
</Col>
</Row>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<TextInput
id="password"
name="password"
type="password"
hasError={!passwordValid}
value={password}
placeholder={t("Password")}
size="huge"
scale={true}
tabIndex={2}
isDisabled={isLoading}
autoComplete="current-password"
onChange={onChangePassword}
onKeyDown={onKeyPress}
/>
</Col>
</Row>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<Link
fontSize={12}
className="link-style"
type="page"
isHovered={true}
onClick={onClick}
>
{t("ForgotPassword")}
</Link>
</Col>
</Row>
{openDialog ? (
<SubModalDialog
openDialog={openDialog}
isLoading={isLoading}
email={email}
onChangeEmail={onChangeEmail}
onSendPasswordInstructions={onSendPasswordInstructions}
onDialogClose={onDialogClose}
t={t}
/>
) : null}
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<Button
primary
size="big"
label={isLoading ? t("LoadingProcessing") : t("LoginButton")}
tabIndex={3}
isDisabled={isLoading}
isLoading={isLoading}
onClick={onSubmit}
/>
</Col>
</Row>
{params.confirmedEmail && (
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<Text.Body isBold={true} fontSize={16}>
{t("MessageEmailConfirmed")} {t("MessageAuthorize")}
</Text.Body>
</Col>
</Row>
)}
<Collapse isOpen={!!errorText}>
<Row className="login-row">
<Col sm="12" md={mdOptions}>
<div className="alert alert-danger">{errorText}</div>
</Col>
</Row>
</Collapse>
</FormContainer>
);
};
const LoginForm = props => (
<PageLayout sectionBodyContent={<Form {...props} />} />
);
LoginForm.propTypes = {
login: PropTypes.func.isRequired,
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
}
login: PropTypes.func.isRequired,
match: PropTypes.object.isRequired,
location: PropTypes.object.isRequired,
history: PropTypes.object.isRequired
};
LoginForm.defaultProps = {
identifier: "",
password: ""
}
identifier: "",
password: "",
email: ""
};
function mapStateToProps(state) {
return {
language: state.auth.user.cultureName || state.auth.settings.culture,
};
}
return {
language: state.auth.user.cultureName || state.auth.settings.culture
};
}
export default connect(mapStateToProps, { login })(withRouter(LoginForm));
export default connect(
mapStateToProps,
{ login }
)(withRouter(LoginForm));

View File

@ -5,6 +5,11 @@
"RegistrationEmailWatermark": "Your registration email",
"MessageEmailConfirmed": "Your email was activated successfully.",
"MessageAuthorize": "Please authorize yourself.",
"ForgotPassword": "Forgot your password?",
"PasswordRecoveryTitle": "Password recovery",
"MessageSendPasswordRecoveryInstructionsOnEmail": "Please enter the email you used while registering on the portal. The password recovery instructions will be send to that email address.",
"SendButton": "Send",
"CancelButton": "Cancel",
"CustomWelcomePageTitle": "{{welcomePageTitle}}"
}

View File

@ -5,6 +5,11 @@
"RegistrationEmailWatermark": "Регистрационный email",
"MessageEmailConfirmed": "Ваш email успешно активирован.",
"MessageAuthorize": "Пожалуйста авторизуйтесь.",
"ForgotPassword": "Забыли пароль?",
"PasswordRecoveryTitle": "Восстановление пароля",
"MessageSendPasswordRecoveryInstructionsOnEmail": "Пожалуйста, введите адрес электронной почты, указанный при регистрации на портале. Инструкции для восстановления пароля будут отправлены на этот адрес электронной почты.",
"SendButton": "Отправить",
"CancelButton": "Отмена",
"CustomWelcomePageTitle": "{{welcomePageTitle}}"
}

View File

@ -0,0 +1,86 @@
import React from "react";
import PropTypes from "prop-types";
import { Button, TextInput, Text, ModalDialog } from "asc-web-components";
class SubModalDialog extends React.Component {
render() {
const {
openDialog,
isLoading,
email,
onChangeEmail,
onSendPasswordInstructions,
onDialogClose,
t
} = this.props;
return (
<ModalDialog
visible={openDialog}
headerContent={
<Text.Body isBold={false} fontSize={21}>
{t("PasswordRecoveryTitle")}
</Text.Body>
}
bodyContent={[
<Text.Body
key="text-body"
className="text-body"
isBold={false}
fontSize={13}
>
{t("MessageSendPasswordRecoveryInstructionsOnEmail")}
</Text.Body>,
<TextInput
key="e-mail"
id="e-mail"
name="e-mail"
type="text"
size="base"
scale={true}
tabIndex={1}
isDisabled={isLoading}
value={email}
onChange={onChangeEmail}
/>
]}
footerContent={[
<Button
className="btn-style"
key="SendBtn"
label={isLoading ? t("LoadingProcessing") : t("SendButton")}
size="base"
scale={false}
primary={true}
onClick={onSendPasswordInstructions}
isLoading={isLoading}
isDisabled={isLoading}
tabIndex={2}
/>,
<Button
key="CancelBtn"
label={t("CancelButton")}
size="base"
scale={false}
primary={false}
onClick={onDialogClose}
isDisabled={isLoading}
tabIndex={3}
/>
]}
onClose={onDialogClose}
/>
);
}
}
SubModalDialog.propTypes = {
openDialog: PropTypes.bool.isRequired,
isLoading: PropTypes.bool.isRequired,
email: PropTypes.string.isRequired,
onChangeEmail: PropTypes.func.isRequired,
onSendPasswordInstructions: PropTypes.func.isRequired,
onDialogClose: PropTypes.func.isRequired,
t: PropTypes.func.isRequired
};
export default SubModalDialog;

View File

@ -1,12 +1,11 @@
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { AUTH_KEY } from './constants';
import Cookies from 'universal-cookie';
import { Route } from 'react-router-dom';
import { ValidationResult } from './../helpers/constants';
import { decomposeConfirmLink } from './../helpers/converters';
import { PageLayout, Loader } from "asc-web-components";
import { connect } from 'react-redux';
import { checkConfirmLink } from './../store/auth/actions';
import { ValidationResult } from './../helpers/constants';
import decomposeConfirmLink from './../helpers/decomposeConfirmLink';
import { PageLayout, Loader } from "asc-web-components";
import { withRouter } from "react-router";
class ConfirmRoute extends React.Component {
constructor(props) {
@ -20,16 +19,20 @@ class ConfirmRoute extends React.Component {
uid: '',
firstname: '',
lastname: '',
isReady: false,
isLoaded: false,
componentProps: {}
}
}
componentDidMount() {
const { pathname, search } = this.props.location;
const { checkConfirmLink } = this.props;
const decomposedLink = decomposeConfirmLink(pathname, search);
const { location, checkConfirmLink, isAuthenticated, history } = this.props;
const { search } = location;
const decomposedLink = decomposeConfirmLink(location);
let validationResult;
let path = '';
if (!isAuthenticated) {
path = '/login';
}
checkConfirmLink(decomposedLink)
.then((res) => {
validationResult = res.data.response;
@ -38,44 +41,49 @@ class ConfirmRoute extends React.Component {
const confirmHeader = `type=${decomposedLink.type}&${search.slice(1)}`;
const componentProps = Object.assign({}, decomposedLink, { confirmHeader });
this.setState({
isReady: true,
isLoaded: true,
componentProps
});
break;
case ValidationResult.Invalid:
window.location.href = '/login/error=Invalid link'
history.push(`${path}/error=Invalid link`);
break;
case ValidationResult.Expired:
window.location.href = '/login/error=Expired link'
history.push(`${path}/error=Expired link`);
break;
default:
window.location.href = '/login/error=Unknown error'
history.push(`${path}/error=Unknown error`);
break;
}
})
.catch((e) => window.location.href = '/');
.catch((e) => history.push(`${path}/error=${e}`));
}
render() {
const { component: Component, location, path, computedMatch, ...rest } = this.props;
const newProps = Object.assign({}, { location, path, computedMatch }, { linkData: this.state.componentProps });
const { component: Component, ...rest } = this.props;
return (
<Route
{...rest}
render={props =>
!this.state.isReady ? (
!this.state.isLoaded ? (
<PageLayout
sectionBodyContent={
<Loader className="pageLoader" type="rombs" size={40} />
}
/>
) : (
<Component {...newProps} />
)
<Component {...props = { ...props, linkData: this.state.componentProps }} />
)
}
/>
)
}
};
export default connect(null, { checkConfirmLink })(ConfirmRoute);
function mapStateToProps(state) {
return {
isAuthenticated: state.auth.isAuthenticated
};
}
export default connect(mapStateToProps, { checkConfirmLink })(withRouter(ConfirmRoute));

View File

@ -44,3 +44,12 @@ export const ValidationResult = Object.freeze({
Invalid: 1,
Expired: 2
});
/**
* Enum for employee status.
* @readonly
*/
export const EmployeeStatus = Object.freeze({
Active: 1,
Disabled: 2
});

View File

@ -0,0 +1,25 @@
export function getObjectByLocation(location) {
if (!location.search || !location.search.length) return null;
const searchUrl = location.search.substring(1);
const object = JSON.parse(
'{"' +
decodeURIComponent(searchUrl)
.replace(/"/g, '\\"')
.replace(/&/g, '","')
.replace(/=/g, '":"') +
'"}'
);
return object;
}
export function decomposeConfirmLink(location) {
const queryParams = getObjectByLocation(location);
const url = location.pathname;
const posSeparator = url.lastIndexOf('/');
const type = url.slice(posSeparator + 1);
const data = Object.assign({ type }, queryParams);
return data;
}

View File

@ -1,12 +0,0 @@
const decomposeConfirmLink = (url, querySearch) => {
const decodedString = decodeURIComponent(querySearch);
const queryString = decodedString.slice(1).split('&');
const arrayOfQueryString = queryString.map(queryParam => queryParam.split('='));
const queryParams = Object.fromEntries(arrayOfQueryString);
const posSeparator = url.lastIndexOf('/');
const type = url.slice(posSeparator + 1);
const data = Object.assign({ type }, queryParams);
return data;
}
export default decomposeConfirmLink;

View File

@ -22,7 +22,10 @@
"RegistrationEmailWatermark",
"MessageEmailConfirmed",
"MessageAuthorize",
"LoginButton"
"LoginButton",
"ForgotPassword",
"PasswordRecoveryTitle",
"MessageSendPasswordRecoveryInstructionsOnEmail"
]
},
"Confirm": {
@ -41,6 +44,11 @@
"ErrorPasswordNoSpecialSymbols",
"EmailAndPasswordCopiedToClipboard",
"LastName",
"DeleteProfileBtn",
"DeleteProfileSuccessMessage",
"DeleteProfileSuccessMessageInfo",
"DeleteProfileConfirmation",
"DeleteProfileConfirmationInfo",
"ChangePasswordSuccess"
]
}

View File

@ -83,4 +83,24 @@ export function checkConfirmLink(data) {
return IS_FAKE
? fakeApi.checkConfirmLink()
: axios.post(`${API_URL}/authentication/confirm.json`, data);
}
export function deleteUser(userId) {
return IS_FAKE
? fakeApi.deleteUser(userId)
: axios.delete(`${API_URL}/people/${userId}.json`);
}
export function updateUserStatus(status, userIds, key) {
return IS_FAKE
? fakeApi.updateUserStatus(status, userIds)
: axios.put(`${API_URL}/people/status/${status}`, { userIds }, {
headers: { confirm: key }
});
}
export function sendInstructionsToChangePassword(email) {
return IS_FAKE
? fakeApi.sendInstructionsToChangePassword()
: axios.post(`${API_URL}/people/password.json`, { email });
}

View File

@ -160,3 +160,15 @@ export function updateUser(data) {
export function checkConfirmLink(data) {
return fakeResponse(data);
}
export function deleteUser(data) {
return fakeResponse(data);
}
export function updateUserStatus(data) {
return fakeResponse(data);
}
export function sendInstructionsToChangePassword() {
return fakeResponse("Instruction has been sent successfully");
}

View File

@ -1,6 +1,6 @@
{
"name": "asc-web-components",
"version": "1.0.114",
"version": "1.0.116",
"description": "Ascensio System SIA component library",
"license": "AGPL-3.0",
"main": "dist/asc-web-components.js",

View File

@ -1,15 +1,21 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { action } from '@storybook/addon-actions';
import { withKnobs, boolean, color, select, date } from '@storybook/addon-knobs/react';
import withReadme from 'storybook-readme/with-readme';
import Readme from './README.md';
import DatePicker from '.';
import Section from '../../../.storybook/decorators/section';
import React from "react";
import { storiesOf } from "@storybook/react";
import { action } from "@storybook/addon-actions";
import {
withKnobs,
boolean,
color,
select,
date
} from "@storybook/addon-knobs/react";
import withReadme from "storybook-readme/with-readme";
import Readme from "./README.md";
import DatePicker from ".";
import Section from "../../../.storybook/decorators/section";
function myDateKnob(name, defaultValue) {
const stringTimestamp = date(name, defaultValue)
return new Date(stringTimestamp)
const stringTimestamp = date(name, defaultValue);
return new Date(stringTimestamp);
}
const locales = [
@ -40,25 +46,32 @@ const locales = [
"vi"
];
storiesOf('Components|DatePicker', module)
const displayType = ["dropdown", "aside"];
storiesOf("Components|DatePicker", module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('base', () => (
.add("base", () => (
<Section>
<DatePicker
onChange={date => {
action('Selected date')(date);
action("Selected date")(date);
}}
selectedDate={myDateKnob('selectedDate', new Date())}
minDate={myDateKnob('minDate', new Date("1970/01/01"))}
maxDate={myDateKnob('maxDate', new Date(new Date().getFullYear() + 1 + "/01/01"))}
selectedDate={myDateKnob("selectedDate", new Date())}
minDate={myDateKnob("minDate", new Date("1970/01/01"))}
maxDate={myDateKnob(
"maxDate",
new Date(new Date().getFullYear() + 1 + "/01/01")
)}
isDisabled={boolean("isDisabled", false)}
isReadOnly={boolean("isReadOnly", false)}
hasError={boolean("hasError", false)}
//hasWarning={boolean("hasWarning", false)}
isOpen={boolean('isOpen', false)}
themeColor={color('themeColor', '#ED7309')}
locale={select('locale', locales, "en")}
isOpen={boolean("isOpen", false)}
themeColor={color("themeColor", "#ED7309")}
locale={select("locale", locales, "en")}
displayType={select("displayType", displayType, "dropdown")}
calendarSize={select("calendarSize", ["base", "big"], "base")}
/>
</Section>
));

View File

@ -7,6 +7,7 @@ import Calendar from "../calendar";
import moment from "moment";
import { handleAnyClick } from "../../utils/event";
import isEmpty from "lodash/isEmpty";
import Aside from "../layout/sub-components/aside";
const DateInputStyle = styled.div`
max-width: 110px;
@ -232,18 +233,35 @@ class DatePicker extends Component {
}
}
render() {
renderBody = () => {
const {
isDisabled,
isReadOnly,
//hasWarning,
minDate,
maxDate,
locale,
themeColor
themeColor,
calendarSize
} = this.props;
const { selectedDate } = this.state;
const { value, isOpen, mask, selectedDate, hasError } = this.state;
return (
<Calendar
locale={locale}
themeColor={themeColor}
minDate={minDate}
maxDate={maxDate}
isDisabled={isDisabled}
openToDate={selectedDate}
selectedDate={selectedDate}
onChange={this.onChange}
size={calendarSize}
/>
);
};
render() {
const { isDisabled, isReadOnly, displayType } = this.props;
const { value, isOpen, mask, hasError } = this.state;
return (
<DateInputStyle ref={this.ref}>
@ -253,7 +271,6 @@ class DatePicker extends Component {
isReadOnly={isReadOnly}
hasError={hasError}
onFocus={this.onClick.bind(this, true)}
//hasWarning={hasWarning}
iconName={"CalendarIcon"}
onIconClick={this.onClick.bind(this, !isOpen)}
value={value}
@ -264,22 +281,15 @@ class DatePicker extends Component {
//showMask={true}
/>
{isOpen ? (
<DropDownStyle>
<DropDown opened={isOpen}>
{
<Calendar
locale={locale}
themeColor={themeColor}
minDate={minDate}
maxDate={maxDate}
isDisabled={isDisabled}
openToDate={selectedDate}
selectedDate={selectedDate}
onChange={this.onChange}
/>
}
</DropDown>
</DropDownStyle>
displayType === "dropdown" ? (
<DropDownStyle>
<DropDown opened={isOpen}>{this.renderBody()}</DropDown>
</DropDownStyle>
) : (
<Aside visible={isOpen} scale={false}>
{this.renderBody()}
</Aside>
)
) : null}
</DateInputStyle>
);
@ -298,13 +308,16 @@ DatePicker.propTypes = {
isReadOnly: PropTypes.bool,
hasError: PropTypes.bool,
hasWarning: PropTypes.bool,
isOpen: PropTypes.bool
isOpen: PropTypes.bool,
displayType: PropTypes.oneOf(["dropdown", "aside"]),
calendarSize: PropTypes.oneOf(["base", "big"])
};
DatePicker.defaultProps = {
minDate: new Date("1970/01/01"),
maxDate: new Date(new Date().getFullYear() + 1, 1, 1),
selectedDate: moment(new Date()).toDate()
selectedDate: moment(new Date()).toDate(),
displayType: "dropdown"
};
export default DatePicker;

View File

@ -447,7 +447,7 @@ namespace ASC.Web.Studio.Core.Notify
public void SendMsgProfileDeletion(int tenantId, UserInfo user)
{
var confirmationUrl = CommonLinkUtility.GetConfirmationUrl(tenantId, user.Email, ConfirmType.ProfileRemove);
var confirmationUrl = CommonLinkUtility.GetConfirmationUrl(tenantId, user.Email, ConfirmType.ProfileRemove, null, SecurityContext.CurrentAccount.ID);
static string greenButtonText() => CoreContext.Configuration.Personal ? WebstudioNotifyPatternResource.ButtonConfirmTermination : WebstudioNotifyPatternResource.ButtonRemoveProfile;