378 lines
11 KiB
JavaScript
378 lines
11 KiB
JavaScript
import { Button, Flex, Form, Result, Space, Typography } from "antd";
|
|
import { useRef, useState } from "react";
|
|
import { useTranslation } from "react-i18next";
|
|
import { useNavigate } from "react-router-dom";
|
|
import {
|
|
Constants,
|
|
EncodeStringToBase64,
|
|
myFetch,
|
|
setUserSessionToLocalStorage,
|
|
showInputsInvalidNotification,
|
|
showUnkownErrorNotification,
|
|
} from "../../utils";
|
|
import {
|
|
MyLanguageSwitch,
|
|
MyRecaptcha,
|
|
PendingEmailVerification,
|
|
PrivacyPolicyCheckbox,
|
|
RememberMeCheckbox,
|
|
} from ".";
|
|
import {
|
|
MyEmailFormInput,
|
|
MyPasswordFormInput,
|
|
} from "../../Components/MyFormInputs";
|
|
|
|
const LoginStep = {
|
|
LOGIN: 1,
|
|
PENDING_DELETION: 2,
|
|
INIT_LOGIN: 3,
|
|
BANNED: 4,
|
|
_BACK_TO_PASSWORD: 5, // needed for going back from pending deletion to password step as the useEffect for the account name is triggered and would set the step to account name
|
|
PENDING_EMAIL_VERIFICATION: 6,
|
|
INIT_PAYMENT: 7,
|
|
};
|
|
|
|
// First step: account name -> get state of account by backend
|
|
// Second step: if account is active -> password -> login otherwise show the state of the account (e. g. deleted, banned)
|
|
export function Login({ notificationApi }) {
|
|
const { t } = useTranslation();
|
|
const navigate = useNavigate();
|
|
|
|
const [step, setStep] = useState(LoginStep.LOGIN);
|
|
const [isRequesting, setIsRequesting] = useState(false);
|
|
const recaptchaRef = useRef(null);
|
|
const recaptchaValueRef = useRef(null);
|
|
|
|
const [form] = Form.useForm();
|
|
|
|
// const email = Form.useWatch("email", form);
|
|
|
|
const showErrorNotification = (errStatus) => {
|
|
if (errStatus === 400) {
|
|
notificationApi["error"]({
|
|
message: t("authentication.login.request.400.title"),
|
|
description: t("authentication.login.request.400.description"),
|
|
});
|
|
}
|
|
};
|
|
/*
|
|
useEffect(() => {
|
|
if (!email) return;
|
|
|
|
// triggered if step was set to pending deletion and the user wants to reactivate the account
|
|
if (step === LoginStep._BACK_TO_PASSWORD) {
|
|
setStep(LoginStep.LOGIN);
|
|
return;
|
|
}
|
|
|
|
// reset step if account name changed for handling the state of the account
|
|
if (step === LoginStep.LOGIN || step === LoginStep.INIT_LOGIN) {
|
|
setStep(LoginStep.ACCOUNT_NAME);
|
|
}
|
|
}, [email]); */
|
|
|
|
if (step === LoginStep.PENDING_DELETION) {
|
|
return (
|
|
<Result
|
|
status="warning"
|
|
title={t("authentication.login.stateAccountPendingDeletion.title")}
|
|
subTitle={t(
|
|
"authentication.login.stateAccountPendingDeletion.description"
|
|
)}
|
|
extra={[
|
|
<Button key={0} onClick={() => setStep(LoginStep.LOGIN)}>
|
|
{t("common.button.cancel")}
|
|
</Button>,
|
|
<Button
|
|
key={1}
|
|
type="primary"
|
|
onClick={() => setStep(LoginStep._BACK_TO_PASSWORD)}
|
|
>
|
|
{t("authentication.login.stateAccountPendingDeletion.button")}
|
|
</Button>,
|
|
]}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (step === LoginStep.BANNED) {
|
|
return (
|
|
<Result
|
|
status="error"
|
|
title={t("authentication.login.stateAccountBanned.title")}
|
|
subTitle={
|
|
<Space direction="vertical">
|
|
<Typography.Text type="secondary">
|
|
{t("authentication.login.stateAccountBanned.infoSupport")}
|
|
</Typography.Text>
|
|
|
|
<Button type="link" href={`mailto:${Constants.SUPPORT_EMAIL}`}>
|
|
{Constants.SUPPORT_EMAIL}
|
|
</Button>
|
|
</Space>
|
|
}
|
|
extra={[
|
|
<Button
|
|
key={0}
|
|
onClick={() => {
|
|
setStep(LoginStep.LOGIN);
|
|
}}
|
|
>
|
|
{t("authentication.login.stateAccountBanned.backButton")}
|
|
</Button>,
|
|
]}
|
|
/>
|
|
);
|
|
}
|
|
|
|
if (step === LoginStep.PENDING_EMAIL_VERIFICATION) {
|
|
return <PendingEmailVerification />;
|
|
}
|
|
|
|
if (step === LoginStep.INIT_PAYMENT) {
|
|
return (
|
|
<Result
|
|
status="warning"
|
|
title={t("authentication.login.stateInitPayment.title")}
|
|
subTitle={t("authentication.login.stateInitPayment.description")}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Form
|
|
form={form}
|
|
layout="vertical"
|
|
requiredMark={false}
|
|
initialValues={{
|
|
rememberMe: false,
|
|
}}
|
|
>
|
|
{step === LoginStep.INIT_LOGIN && (
|
|
<Typography.Paragraph type="secondary">
|
|
{t("authentication.login.stateInitLogin.info")}
|
|
</Typography.Paragraph>
|
|
)}
|
|
|
|
<MyEmailFormInput hasFeedback={false} disableEmailCheck />
|
|
|
|
<MyPasswordFormInput />
|
|
|
|
<Flex justify="space-between">
|
|
<RememberMeCheckbox />
|
|
<Button
|
|
type="link"
|
|
onClick={() =>
|
|
navigate(Constants.ROUTE_PATHS.AUTHENTICATION.FORGOT_PASSWORD)
|
|
}
|
|
>
|
|
{t("authentication.login.forgotPassword")}
|
|
</Button>
|
|
</Flex>
|
|
|
|
<MyRecaptcha
|
|
recaptchaRef={recaptchaRef}
|
|
recaptchaValueRef={recaptchaValueRef}
|
|
/>
|
|
|
|
<PrivacyPolicyCheckbox />
|
|
|
|
<Button
|
|
type="primary"
|
|
size="large"
|
|
block
|
|
loading={isRequesting}
|
|
htmlType="submit"
|
|
onClick={() => {
|
|
let validateFields = [];
|
|
|
|
if (step === LoginStep.ACCOUNT_NAME) {
|
|
validateFields = ["email"];
|
|
} else {
|
|
validateFields = [
|
|
"email",
|
|
"password",
|
|
"rememberMe",
|
|
"recaptcha",
|
|
"privacyPolicy",
|
|
];
|
|
}
|
|
|
|
form
|
|
.validateFields(validateFields)
|
|
.then((values) => {
|
|
setIsRequesting(true);
|
|
|
|
myFetch({
|
|
url: `/user/auth/login`,
|
|
method: "POST",
|
|
body: {
|
|
email: values.email.toLocaleLowerCase(),
|
|
password: EncodeStringToBase64(values.password),
|
|
recaptcha: recaptchaValueRef.current,
|
|
},
|
|
notificationApi: notificationApi,
|
|
t: t,
|
|
})
|
|
.then((data) => {
|
|
setIsRequesting(false);
|
|
|
|
if (data.state === undefined) {
|
|
if (
|
|
step === LoginStep.LOGIN ||
|
|
step === LoginStep.INIT_LOGIN
|
|
) {
|
|
setUserSessionToLocalStorage(data.XAuthorization);
|
|
window.location.href = "/";
|
|
}
|
|
return;
|
|
}
|
|
|
|
switch (data.state) {
|
|
case Constants.ACCOUNT_STATE.PENDING_DELETION:
|
|
setStep(LoginStep.PENDING_DELETION);
|
|
break;
|
|
case Constants.ACCOUNT_STATE.BANNED:
|
|
setStep(LoginStep.BANNED);
|
|
break;
|
|
case Constants.ACCOUNT_STATE.INIT_LOGGING:
|
|
setStep(LoginStep.INIT_LOGIN);
|
|
break;
|
|
case Constants.ACCOUNT_STATE.PENDING_EMAIL_VERIFICATION:
|
|
setStep(LoginStep.PENDING_EMAIL_VERIFICATION);
|
|
break;
|
|
case Constants.ACCOUNT_STATE.INIT_PAYMENT:
|
|
setStep(LoginStep.INIT_PAYMENT);
|
|
break;
|
|
default:
|
|
showUnkownErrorNotification(notificationApi, t);
|
|
break;
|
|
}
|
|
})
|
|
.catch((errStatus) => {
|
|
showErrorNotification(errStatus);
|
|
setIsRequesting(false);
|
|
recaptchaRef.current.reset();
|
|
});
|
|
})
|
|
.catch(() => showInputsInvalidNotification(notificationApi, t));
|
|
}}
|
|
>
|
|
{t("authentication.login.button")}
|
|
</Button>
|
|
|
|
<Flex justify="center" style={{ paddingTop: 12 }}>
|
|
<Typography.Text>
|
|
{t("authentication.login.dontHaveAccount")}{" "}
|
|
<Button
|
|
type="link"
|
|
style={{ padding: 0 }}
|
|
onClick={() =>
|
|
navigate(Constants.ROUTE_PATHS.AUTHENTICATION.SIGN_UP)
|
|
}
|
|
>
|
|
{t("authentication.signUpLink")}
|
|
</Button>
|
|
</Typography.Text>
|
|
</Flex>
|
|
|
|
<MyLanguageSwitch />
|
|
</Form>
|
|
);
|
|
}
|
|
|
|
const FORGOT_PASSWORD_STEP = {
|
|
ENTER_EMAIL: 1,
|
|
RESET_LINK_SENT: 2,
|
|
};
|
|
|
|
export function ForgotPassword({ notificationApi }) {
|
|
const { t } = useTranslation();
|
|
const [form] = Form.useForm();
|
|
const navigate = useNavigate();
|
|
|
|
const [step, setStep] = useState(FORGOT_PASSWORD_STEP.ENTER_EMAIL);
|
|
const [isRequesting, setIsRequesting] = useState(false);
|
|
const recaptchaValueRef = useRef(null);
|
|
|
|
if (step === FORGOT_PASSWORD_STEP.RESET_LINK_SENT) {
|
|
return (
|
|
<Result
|
|
status="warning"
|
|
title={t("authentication.forgotPassword.resetLinkSent.title")}
|
|
subTitle={t("authentication.forgotPassword.resetLinkSent.description")}
|
|
/>
|
|
);
|
|
}
|
|
|
|
return (
|
|
<Form
|
|
form={form}
|
|
layout="vertical"
|
|
requiredMark={false}
|
|
initialValues={{
|
|
rememberMe: false,
|
|
}}
|
|
>
|
|
<Typography.Title level={3} style={{ textAlign: "center" }}>
|
|
{t("authentication.forgotPassword.title")}
|
|
</Typography.Title>
|
|
|
|
<Typography.Paragraph
|
|
type="secondary"
|
|
style={{
|
|
textAlign: "center",
|
|
}}
|
|
>
|
|
{t("authentication.forgotPassword.info")}
|
|
</Typography.Paragraph>
|
|
|
|
<MyEmailFormInput hasFeedback={true} disableEmailCheck />
|
|
|
|
<MyRecaptcha recaptchaValueRef={recaptchaValueRef} />
|
|
|
|
<Button
|
|
type="primary"
|
|
size="large"
|
|
block
|
|
loading={isRequesting}
|
|
onClick={() => {
|
|
form
|
|
.validateFields()
|
|
.then((values) => {
|
|
setIsRequesting(true);
|
|
|
|
myFetch({
|
|
url: `/user/auth/forgot-password`,
|
|
method: "POST",
|
|
body: {
|
|
email: values.email.toLocaleLowerCase(),
|
|
recaptcha: recaptchaValueRef.current,
|
|
},
|
|
notificationApi: notificationApi,
|
|
t: t,
|
|
})
|
|
.then(() => setStep(FORGOT_PASSWORD_STEP.RESET_LINK_SENT))
|
|
.catch(() => {
|
|
setIsRequesting(false);
|
|
showUnkownErrorNotification(notificationApi, t);
|
|
});
|
|
})
|
|
.catch(() => showInputsInvalidNotification(notificationApi, t));
|
|
}}
|
|
>
|
|
{t("authentication.forgotPassword.button")}
|
|
</Button>
|
|
|
|
<Flex justify="center">
|
|
<Button
|
|
type="link"
|
|
onClick={() => navigate(Constants.ROUTE_PATHS.AUTHENTICATION.LOGIN)}
|
|
>
|
|
{t("authentication.forgotPassword.backToLogin")}
|
|
</Button>
|
|
</Flex>
|
|
</Form>
|
|
);
|
|
}
|