DocSpace-buildtools/web/ASC.Web.Common/src/pages/login/index.js

492 lines
12 KiB
JavaScript
Raw Normal View History

import React, { Component, useEffect } from "react";
2019-11-22 11:48:54 +00:00
import PropTypes from "prop-types";
import { withRouter } from "react-router";
import {
2020-07-02 08:44:17 +00:00
Box,
2019-11-22 11:48:54 +00:00
Button,
TextInput,
Text,
Link,
toastr,
Checkbox,
2020-07-10 12:13:08 +00:00
PasswordInput,
FieldContainer,
2019-11-22 11:48:54 +00:00
} from "asc-web-components";
import PageLayout from "../../components/PageLayout";
import styled, { css } from "styled-components";
import { withTranslation } from "react-i18next";
2019-11-22 11:48:54 +00:00
import i18n from "./i18n";
2020-07-23 12:22:09 +00:00
import ForgotPasswordModalDialog from "./sub-components/forgot-password-modal-dialog";
import { sendInstructionsToChangePassword } from "../../api/people";
2020-07-23 12:22:09 +00:00
import Register from "./sub-components/register-container";
2020-12-04 11:21:51 +00:00
import { createPasswordHash, tryRedirectTo } from "../../utils";
2020-11-13 12:58:14 +00:00
import { checkPwd } from "../../desktop";
2021-02-03 12:42:47 +00:00
import { inject, observer } from "mobx-react";
2020-07-22 10:47:56 +00:00
const LoginContainer = styled.div`
display: flex;
flex-direction: column;
align-items: center;
margin: 120px auto 0 auto;
max-width: 960px;
2019-11-22 11:48:54 +00:00
@media (max-width: 768px) {
2020-07-30 10:25:40 +00:00
padding: 0 16px;
2020-07-22 10:47:56 +00:00
max-width: 475px;
}
@media (max-width: 375px) {
2020-07-22 10:47:56 +00:00
margin: 72px auto 0 auto;
max-width: 311px;
}
2019-11-22 11:48:54 +00:00
2020-07-22 10:47:56 +00:00
.greeting-title {
width: 100%;
@media (max-width: 768px) {
text-align: left;
}
@media (max-width: 375px) {
font-size: 23px;
}
}
2020-07-22 10:47:56 +00:00
.auth-form-container {
2020-09-10 11:49:57 +00:00
margin: 32px 213px 0 213px;
width: 311px;
2020-07-22 10:47:56 +00:00
@media (max-width: 768px) {
margin: 32px 0 0 0;
width: 100%;
2020-07-22 10:47:56 +00:00
}
@media (max-width: 375px) {
2020-07-23 13:07:35 +00:00
margin: 32px 0 0 0;
width: 100%;
2020-07-22 10:47:56 +00:00
}
.login-forgot-wrapper {
height: 36px;
padding: 14px 0;
.login-checkbox-wrapper {
display: flex;
2020-07-22 10:47:56 +00:00
.login-checkbox {
float: left;
span {
2020-07-22 10:47:56 +00:00
font-size: 12px;
}
}
}
2020-07-22 10:47:56 +00:00
.login-link {
line-height: 18px;
margin-left: auto;
2020-07-22 10:47:56 +00:00
}
}
2019-11-22 11:48:54 +00:00
.login-button {
margin-bottom: 16px;
}
2019-11-22 11:48:54 +00:00
.login-button-dialog {
margin-right: 8px;
}
2020-07-02 08:44:17 +00:00
.login-bottom-border {
width: 100%;
height: 1px;
background: #eceef1;
}
2020-07-02 08:44:17 +00:00
.login-bottom-text {
margin: 0 8px;
}
2020-07-02 08:44:17 +00:00
}
2019-11-22 11:48:54 +00:00
`;
2020-11-27 15:43:22 +00:00
const LoginFormWrapper = styled.div`
display: grid;
2020-12-04 08:38:15 +00:00
grid-template-rows: ${(props) =>
2020-12-28 07:37:53 +00:00
props.enabledJoin
? props.isDesktop
? css`1fr 10px`
: css`1fr 66px`
: css`1fr`};
2020-11-27 15:43:22 +00:00
width: 100%;
height: calc(100vh-56px);
2020-11-27 15:43:22 +00:00
`;
2019-11-27 11:14:51 +00:00
class Form extends Component {
constructor(props) {
super(props);
this.state = {
identifierValid: true,
identifier: "",
isLoading: false,
isDisabled: false,
passwordValid: true,
password: "",
isChecked: false,
openDialog: false,
email: "",
2020-07-10 11:02:44 +00:00
emailError: false,
2020-07-15 13:18:09 +00:00
errorText: "",
socialButtons: [],
2019-11-27 11:14:51 +00:00
};
}
onChangeLogin = (event) => {
2019-11-27 11:14:51 +00:00
this.setState({ identifier: event.target.value });
!this.state.identifierValid && this.setState({ identifierValid: true });
this.state.errorText && this.setState({ errorText: "" });
};
onChangePassword = (event) => {
2019-11-27 11:14:51 +00:00
this.setState({ password: event.target.value });
!this.state.passwordValid && this.setState({ passwordValid: true });
this.state.errorText && this.setState({ errorText: "" });
2019-11-22 11:48:54 +00:00
};
onChangeEmail = (event) => {
2020-07-23 12:37:35 +00:00
this.setState({ email: event.target.value, emailError: false });
2019-11-22 11:48:54 +00:00
};
2019-11-27 11:14:51 +00:00
onChangeCheckbox = () => this.setState({ isChecked: !this.state.isChecked });
2019-11-22 11:48:54 +00:00
2019-11-27 11:14:51 +00:00
onClick = () => {
this.setState({
openDialog: true,
isDisabled: true,
email: this.state.identifier,
2019-11-27 11:14:51 +00:00
});
};
onKeyPress = (event) => {
2019-11-27 11:14:51 +00:00
if (event.key === "Enter") {
!this.state.isDisabled
? this.onSubmit()
: this.onSendPasswordInstructions();
}
};
onSendPasswordInstructions = () => {
2020-07-10 11:02:44 +00:00
if (!this.state.email.trim()) {
2020-07-14 09:55:09 +00:00
this.setState({ emailError: true });
} else {
2020-07-14 09:55:09 +00:00
this.setState({ isLoading: true });
sendInstructionsToChangePassword(this.state.email)
.then(
(res) => toastr.success(res),
(message) => toastr.error(message)
)
2020-07-14 09:55:09 +00:00
.finally(this.onDialogClose());
2020-07-10 11:02:44 +00:00
}
2019-11-27 11:14:51 +00:00
};
onDialogClose = () => {
this.setState({
openDialog: false,
isDisabled: false,
isLoading: false,
2020-07-10 12:15:38 +00:00
email: "",
emailError: false,
2019-11-27 11:14:51 +00:00
});
};
onSubmit = () => {
const { errorText, identifier, password } = this.state;
2021-02-03 12:42:47 +00:00
const { login, hashSettings, isDesktop, defaultPage } = this.props;
2019-11-27 11:14:51 +00:00
errorText && this.setState({ errorText: "" });
2019-11-22 11:48:54 +00:00
let hasError = false;
const userName = identifier.trim();
if (!userName) {
hasError = true;
2019-11-27 11:14:51 +00:00
this.setState({ identifierValid: !hasError });
2019-11-22 11:48:54 +00:00
}
const pass = password.trim();
if (!pass) {
hasError = true;
2019-11-27 11:14:51 +00:00
this.setState({ passwordValid: !hasError });
2019-11-22 11:48:54 +00:00
}
if (hasError) return false;
2019-11-27 11:14:51 +00:00
this.setState({ isLoading: true });
const hash = createPasswordHash(pass, hashSettings);
2020-09-30 18:20:51 +00:00
2020-11-13 12:58:14 +00:00
isDesktop && checkPwd();
2020-09-30 18:20:51 +00:00
login(userName, hash)
2021-02-03 12:42:47 +00:00
.then(() => tryRedirectTo(defaultPage))
.catch((error) => {
this.setState({
errorText: error,
identifierValid: !error,
passwordValid: !error,
isLoading: false,
});
2019-11-28 12:08:08 +00:00
});
2019-11-22 11:48:54 +00:00
};
2019-11-27 11:14:51 +00:00
componentDidMount() {
2021-02-03 12:42:47 +00:00
const { match, t, organizationName } = this.props;
const { error, confirmedEmail } = match.params;
document.title = `${t("Authorization")} ${organizationName}`; //TODO: implement the setDocumentTitle() utility in ASC.Web.Common
error && this.setState({ errorText: error });
confirmedEmail && this.setState({ identifier: confirmedEmail });
2019-11-27 11:14:51 +00:00
window.addEventListener("keyup", this.onKeyPress);
}
2019-11-22 11:48:54 +00:00
2019-11-27 11:14:51 +00:00
componentWillUnmount() {
window.removeEventListener("keyup", this.onKeyPress);
}
2019-11-22 11:48:54 +00:00
settings = {
minLength: 6,
upperCase: false,
digits: false,
specSymbols: false,
};
2019-11-27 11:14:51 +00:00
render() {
const { greetingTitle, match, t } = this.props;
2019-11-27 11:14:51 +00:00
const {
identifierValid,
identifier,
isLoading,
passwordValid,
password,
isChecked,
openDialog,
email,
2020-07-10 11:02:44 +00:00
emailError,
2020-07-15 13:18:09 +00:00
errorText,
socialButtons,
2019-11-27 11:14:51 +00:00
} = this.state;
const { params } = match;
//console.log("Login render");
return (
2020-07-06 13:06:37 +00:00
<>
2020-07-22 10:47:56 +00:00
<LoginContainer>
<Text
fontSize="32px"
2020-07-31 10:23:53 +00:00
fontWeight={600}
textAlign="center"
className="greeting-title"
>
2020-07-22 10:47:56 +00:00
{greetingTitle}
</Text>
<form className="auth-form-container">
<FieldContainer
isVertical={true}
labelVisible={false}
2020-07-14 09:55:09 +00:00
hasError={!identifierValid}
errorMessage={errorText ? errorText : t("RequiredFieldMessage")} //TODO: Add wrong login server error
2020-07-22 10:47:56 +00:00
>
<TextInput
id="login"
name="login"
hasError={!identifierValid}
value={identifier}
placeholder={t("RegistrationEmailWatermark")}
size="large"
scale={true}
isAutoFocussed={true}
tabIndex={1}
isDisabled={isLoading}
autoComplete="username"
onChange={this.onChangeLogin}
onKeyDown={this.onKeyPress}
2020-07-06 13:06:37 +00:00
/>
2020-07-22 10:47:56 +00:00
</FieldContainer>
<FieldContainer
isVertical={true}
labelVisible={false}
hasError={!passwordValid}
errorMessage={errorText ? "" : t("RequiredFieldMessage")} //TODO: Add wrong password server error
2020-07-22 10:47:56 +00:00
>
<PasswordInput
simpleView={true}
passwordSettings={this.settings}
id="password"
inputName="password"
placeholder={t("Password")}
type="password"
hasError={!passwordValid}
inputValue={password}
size="large"
scale={true}
tabIndex={1}
isDisabled={isLoading}
autoComplete="current-password"
onChange={this.onChangePassword}
onKeyDown={this.onKeyPress}
2020-07-06 13:06:37 +00:00
/>
2020-07-22 10:47:56 +00:00
</FieldContainer>
<div className="login-forgot-wrapper">
<div className="login-checkbox-wrapper">
<Checkbox
className="login-checkbox"
isChecked={isChecked}
onChange={this.onChangeCheckbox}
label={<Text fontSize="13px">{t("Remember")}</Text>}
2020-07-22 10:47:56 +00:00
/>
{/*<HelpButton
2020-07-22 10:47:56 +00:00
className="login-tooltip"
helpButtonHeaderContent={t("CookieSettingsTitle")}
tooltipContent={
<Text fontSize="12px">{t("RememberHelper")}</Text>
2020-07-22 10:47:56 +00:00
}
/>*/}
<Link
fontSize="13px"
color="#316DAA"
className="login-link"
type="page"
isHovered={false}
onClick={this.onClick}
>
{t("ForgotPassword")}
</Link>
2020-07-22 10:47:56 +00:00
</div>
2020-07-06 13:06:37 +00:00
</div>
{openDialog && (
2020-07-23 12:22:09 +00:00
<ForgotPasswordModalDialog
2020-07-22 10:47:56 +00:00
openDialog={openDialog}
isLoading={isLoading}
email={email}
emailError={emailError}
onChangeEmail={this.onChangeEmail}
onSendPasswordInstructions={this.onSendPasswordInstructions}
onDialogClose={this.onDialogClose}
t={t}
/>
)}
2020-07-22 10:47:56 +00:00
<Button
id="button"
className="login-button"
primary
size="large"
scale={true}
label={isLoading ? t("LoadingProcessing") : t("LoginButton")}
tabIndex={1}
isDisabled={isLoading}
2020-07-06 13:06:37 +00:00
isLoading={isLoading}
2020-07-22 10:47:56 +00:00
onClick={this.onSubmit}
2020-07-06 13:06:37 +00:00
/>
2020-07-22 10:47:56 +00:00
{params.confirmedEmail && (
<Text isBold={true} fontSize="16px">
2020-07-22 10:47:56 +00:00
{t("MessageEmailConfirmed")} {t("MessageAuthorize")}
</Text>
)}
{/* TODO: old error indication
<Text fontSize="14px" color="#c30">
2020-07-22 10:47:56 +00:00
{errorText}
</Text> */}
2020-07-15 13:18:09 +00:00
{socialButtons.length ? (
<Box displayProp="flex" alignItems="center">
<div className="login-bottom-border"></div>
<Text className="login-bottom-text" color="#A3A9AE">
{t("Or")}
</Text>
<div className="login-bottom-border"></div>
</Box>
2020-07-22 10:47:56 +00:00
) : null}
</form>
</LoginContainer>
2020-07-06 13:06:37 +00:00
</>
2019-11-27 11:14:51 +00:00
);
}
}
Form.propTypes = {
2019-11-22 11:48:54 +00:00
login: PropTypes.func.isRequired,
match: PropTypes.object.isRequired,
hashSettings: PropTypes.object,
2019-11-27 11:14:51 +00:00
greetingTitle: PropTypes.string.isRequired,
t: PropTypes.func.isRequired,
i18n: PropTypes.object.isRequired,
2020-07-15 13:18:09 +00:00
language: PropTypes.string.isRequired,
socialButtons: PropTypes.array,
organizationName: PropTypes.string,
homepage: PropTypes.string,
2020-12-04 11:21:51 +00:00
defaultPage: PropTypes.string,
2021-02-03 12:42:47 +00:00
isDesktop: PropTypes.bool,
2019-11-22 11:48:54 +00:00
};
2019-11-27 11:14:51 +00:00
Form.defaultProps = {
2019-11-22 11:48:54 +00:00
identifier: "",
password: "",
email: "",
2019-11-22 11:48:54 +00:00
};
const FormWrapper = withTranslation()(Form);
const LoginForm = (props) => {
2020-12-28 07:37:53 +00:00
const { language, enabledJoin, isDesktop } = props;
useEffect(() => {
i18n.changeLanguage(language);
}, [language]);
return (
2020-12-28 07:37:53 +00:00
<LoginFormWrapper enabledJoin={enabledJoin} isDesktop={isDesktop}>
<PageLayout>
<PageLayout.SectionBody>
2020-12-04 08:38:15 +00:00
<FormWrapper i18n={i18n} {...props} />
</PageLayout.SectionBody>
</PageLayout>
<Register />
</LoginFormWrapper>
);
};
2019-11-27 11:14:51 +00:00
LoginForm.propTypes = {
language: PropTypes.string.isRequired,
2020-07-23 06:50:42 +00:00
isLoaded: PropTypes.bool,
2020-12-28 07:39:30 +00:00
enabledJoin: PropTypes.bool,
isDesktop: PropTypes.bool.isRequired,
2019-11-27 11:14:51 +00:00
};
2021-02-03 12:42:47 +00:00
export default inject(({ store }) => {
const { settingsStore, isAuthenticated, isLoaded, language, login } = store;
const {
greetingSettings: greetingTitle,
organizationName,
hashSettings,
enabledJoin,
defaultPage,
isDesktopClient: isDesktop,
} = settingsStore;
2019-11-22 11:48:54 +00:00
return {
2020-12-04 08:38:15 +00:00
isAuthenticated,
isLoaded,
2021-02-03 12:42:47 +00:00
language,
organizationName,
greetingTitle,
hashSettings,
enabledJoin,
defaultPage,
isDesktop,
login,
};
2021-02-03 12:42:47 +00:00
})(withRouter(observer(LoginForm)));