change email
parent
5b62d4ff3f
commit
d6016c3d4d
|
@ -33,6 +33,7 @@
|
|||
"email": "E-Mail",
|
||||
"emailPlaceholder": "Geben Sie Ihre E-Mail ein",
|
||||
"emailPlaceholderThirdPerson": "Geben Sie die E-Mail ein",
|
||||
"emailPlaceholderNew": "Geben Sie Ihre neue E-Mail ein",
|
||||
"password": "Passwort",
|
||||
"passwordPlaceholder": "Geben Sie Ihr Passwort ein",
|
||||
"noDataFound": "Keine Einträge gefunden",
|
||||
|
@ -313,6 +314,16 @@
|
|||
"analytics": "Analytik",
|
||||
"analyticsDescription": "Dazu gehören Informationen über die Nutzung unseres Dashboards, wie die besuchten Seiten, die Verweildauer auf den Seiten und die allgemeine Interaktion mit den Seiten. Dies hilft uns, unser Dashboard zu verbessern und es benutzerfreundlicher zu gestalten."
|
||||
},
|
||||
"changeEmailModal": {
|
||||
"title": "E-Mail-Adresse ändern",
|
||||
"info": "Nachdem Sie Ihre neue E-Mail-Adresse angegeben haben, erhalten Sie eine E-Mail mit einem Bestätigungslink. Bitte klicken Sie auf diesen Link, um Ihre neue E-Mail-Adresse zu bestätigen. Ihre alte E-Mail-Adresse bleibt so lange aktiv, bis die neue E-Mail-Adresse verifiziert ist. Nach der Verifizierung werden alle Ihre Sitzungen abgemeldet und Sie müssen sich erneut anmelden.",
|
||||
"request": {
|
||||
"200": {
|
||||
"title": "Anfrage zur E-Mail-Änderung gesendet",
|
||||
"description": "Wir haben einen Bestätigungslink an Ihre neue E-Mail-Adresse gesendet. Bitte klicken Sie auf den Link, um Ihre neue E-Mail-Adresse zu bestätigen."
|
||||
}
|
||||
}
|
||||
},
|
||||
"changePassword": {
|
||||
"cardTitle": "Passwort ändern",
|
||||
"currentPassword": "Aktuelles Passwort",
|
||||
|
@ -378,6 +389,7 @@
|
|||
"title": "Verifizierung fehlgeschlagen",
|
||||
"description": "Keine ausstehende Überprüfung gefunden"
|
||||
},
|
||||
"buttonToLogin": "Jetzt anmelden",
|
||||
"content": [
|
||||
{
|
||||
"requesting": {
|
||||
|
@ -387,6 +399,18 @@
|
|||
"title": "E-Mail-Überprüfung erfolgreich",
|
||||
"button": "Jetzt anmelden"
|
||||
}
|
||||
},
|
||||
{
|
||||
"init": {
|
||||
"title": "Bestätigen Sie die E-Mail Änderung",
|
||||
"subTitle": "Bestätigen Sie Ihre neue E-Mail Adresse, indem Sie Ihr Passwort eingeben"
|
||||
},
|
||||
"requesting": {
|
||||
"title": "Bestätigung der E-Mail Änderung"
|
||||
},
|
||||
"success": {
|
||||
"title": "E-Mail-Änderung erfolgreich"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -33,6 +33,7 @@
|
|||
"email": "Email",
|
||||
"emailPlaceholder": "Enter your email",
|
||||
"emailPlaceholderThirdPerson": "Enter the email",
|
||||
"emailPlaceholderNew": "Enter your new email",
|
||||
"password": "Password",
|
||||
"passwordPlaceholder": "Enter your password",
|
||||
"noDataFound": "No data found",
|
||||
|
@ -316,6 +317,16 @@
|
|||
"analytics": "Analytics",
|
||||
"analyticsDescription": "This includes information about the use of our dashboard, such as the visited pages, the length of stay on the pages and the general interaction with the pages. This helps us to improve our dashboard and make it more user-friendly."
|
||||
},
|
||||
"changeEmailModal": {
|
||||
"title": "Change email",
|
||||
"info": "After submitting your new email, you will receive an email with a verification link. Please click on the link to verify your new email address. Your old email address will remain active until the new email address is verified. After verification all your sessions will be logged out and you have to log in again.",
|
||||
"request": {
|
||||
"200": {
|
||||
"title": "Email change request sent",
|
||||
"description": "We have sent an verification link to your new email address. Please click on the link to verify your new email address."
|
||||
}
|
||||
}
|
||||
},
|
||||
"changePassword": {
|
||||
"cardTitle": "Change password",
|
||||
"currentPassword": "Current password",
|
||||
|
@ -381,6 +392,7 @@
|
|||
"title": "Verification failed",
|
||||
"description": "No pending verification found."
|
||||
},
|
||||
"buttonToLogin": "Login now",
|
||||
"content": [
|
||||
{
|
||||
"requesting": {
|
||||
|
@ -390,6 +402,18 @@
|
|||
"title": "Email verification successful",
|
||||
"button": "Login now"
|
||||
}
|
||||
},
|
||||
{
|
||||
"init": {
|
||||
"title": "Confirm the email change",
|
||||
"subTitle": "Confirm your new email address by entering your password"
|
||||
},
|
||||
"requesting": {
|
||||
"title": "Validating email change"
|
||||
},
|
||||
"success": {
|
||||
"title": "Email change successful"
|
||||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
import { Form, Input, InputNumber, Skeleton } from "antd";
|
||||
import { Button, Form, Input, InputNumber, Skeleton, Space } from "antd";
|
||||
import { Constants, isEmailValid, myFetch } from "../../utils";
|
||||
import { useRef } from "react";
|
||||
import { createElement, useRef } from "react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { useAppContext } from "../../Contexts/AppContext";
|
||||
import { EditOutlined } from "@ant-design/icons";
|
||||
|
||||
export function MyUsernameFormInput({
|
||||
propsFormItem,
|
||||
|
@ -76,6 +77,7 @@ export function MyEmailFormInput({
|
|||
hasFeedback,
|
||||
showSkeleton,
|
||||
thirdPerson,
|
||||
newEmail,
|
||||
}) {
|
||||
const { t } = useTranslation();
|
||||
|
||||
|
@ -94,6 +96,8 @@ export function MyEmailFormInput({
|
|||
inputPlaceholder={
|
||||
thirdPerson
|
||||
? t("common.emailPlaceholderThirdPerson")
|
||||
: newEmail
|
||||
? t("common.emailPlaceholderNew")
|
||||
: t("common.emailPlaceholder")
|
||||
}
|
||||
ruleMessageValueMinLengthRequired={t("common.inputRules.emailMinLength", {
|
||||
|
@ -307,6 +311,7 @@ export function MyFormInput({
|
|||
inputType,
|
||||
inputNotRequired,
|
||||
showSkeleton,
|
||||
InputFatherElement,
|
||||
}) {
|
||||
const commonProps = {
|
||||
...propsInput,
|
||||
|
@ -359,6 +364,8 @@ export function MyFormInput({
|
|||
),
|
||||
};
|
||||
|
||||
const inputComponent = inputComponents[inputType] || inputComponents.default;
|
||||
|
||||
return (
|
||||
<Form.Item
|
||||
{...propsFormItem}
|
||||
|
@ -367,7 +374,11 @@ export function MyFormInput({
|
|||
required
|
||||
rules={myFormItemRules}
|
||||
>
|
||||
{inputComponents[inputType] || inputComponents.default}
|
||||
{InputFatherElement ? (
|
||||
createElement(InputFatherElement, { children: inputComponent })
|
||||
) : (
|
||||
<>{inputComponent}</>
|
||||
)}
|
||||
</Form.Item>
|
||||
);
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ import {
|
|||
Card,
|
||||
Col,
|
||||
Form,
|
||||
Input,
|
||||
Popconfirm,
|
||||
Radio,
|
||||
Row,
|
||||
|
@ -30,7 +31,7 @@ import {
|
|||
RequestState,
|
||||
RequestStateItem,
|
||||
} from "../../Components/MyRequestStateItem";
|
||||
import { LogoutOutlined } from "@ant-design/icons";
|
||||
import { EditOutlined, LogoutOutlined } from "@ant-design/icons";
|
||||
import {
|
||||
MyEmailFormInput,
|
||||
MyFormInput,
|
||||
|
@ -205,8 +206,7 @@ function YourProfile({
|
|||
if (
|
||||
language === undefined ||
|
||||
analyticsEnabled === undefined ||
|
||||
username === undefined ||
|
||||
email === undefined
|
||||
username === undefined
|
||||
)
|
||||
return;
|
||||
|
||||
|
@ -246,10 +246,6 @@ function YourProfile({
|
|||
body.username = username;
|
||||
}
|
||||
|
||||
if (email !== requestData.email) {
|
||||
body.email = email;
|
||||
}
|
||||
|
||||
if (Object.keys(body).length === 0) {
|
||||
setGlobalRequestState({
|
||||
...globalRequestState,
|
||||
|
@ -277,7 +273,6 @@ function YourProfile({
|
|||
language: language,
|
||||
analytics_enabled: analyticsEnabled,
|
||||
username: username,
|
||||
email: email,
|
||||
});
|
||||
|
||||
sideBarContext.setUsername(username);
|
||||
|
@ -298,7 +293,7 @@ function YourProfile({
|
|||
});
|
||||
});
|
||||
}, 500);
|
||||
}, [language, analyticsEnabled, username, email]);
|
||||
}, [language, analyticsEnabled, username]);
|
||||
|
||||
return (
|
||||
<Card title={t("userProfile.yourProfile.cardTitle")}>
|
||||
|
@ -344,16 +339,107 @@ function YourProfile({
|
|||
showSkeleton={requestState === RequestState.INIT}
|
||||
/>
|
||||
|
||||
<MyEmailFormInput
|
||||
<MyFormInput
|
||||
formItemName="email"
|
||||
label={t("common.email")}
|
||||
propsInput={{
|
||||
disabled: true,
|
||||
value: email,
|
||||
}}
|
||||
showSkeleton={requestState === RequestState.INIT}
|
||||
hasFeedback
|
||||
disableEmailCheck={requestData.email === email}
|
||||
InputFatherElement={(props) => (
|
||||
<Space.Compact block>
|
||||
{props.children}
|
||||
<YourProfileChangeEmailModal />
|
||||
</Space.Compact>
|
||||
)}
|
||||
/>
|
||||
</Form>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function YourProfileChangeEmailModal() {
|
||||
const { t } = useTranslation();
|
||||
|
||||
const [form] = Form.useForm();
|
||||
const [notificationApi, notificationContextHolder] =
|
||||
notification.useNotification();
|
||||
const [isModalOpen, setIsModalOpen] = useState(false);
|
||||
|
||||
const [isRequesting, setIsRequesting] = useState(false);
|
||||
|
||||
const handleModalClose = () => setIsModalOpen(false);
|
||||
|
||||
useEffect(() => {
|
||||
if (!isModalOpen) return;
|
||||
|
||||
form.resetFields();
|
||||
}, [isModalOpen]);
|
||||
|
||||
return (
|
||||
<>
|
||||
{notificationContextHolder}
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<EditOutlined />}
|
||||
onClick={() => setIsModalOpen(true)}
|
||||
/>
|
||||
|
||||
<MyModal
|
||||
title={t("userProfile.changeEmailModal.title")}
|
||||
isOpen={isModalOpen}
|
||||
onCancel={handleModalClose}
|
||||
footer={
|
||||
<MyModalCloseConfirmButtonFooter
|
||||
onCancel={handleModalClose}
|
||||
isConfirmButtonLoading={isRequesting}
|
||||
onConfirm={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
setIsRequesting(true);
|
||||
|
||||
myFetch({
|
||||
url: "/user/profile/email",
|
||||
method: "POST",
|
||||
body: {
|
||||
email: values.email,
|
||||
},
|
||||
})
|
||||
.then(() => {
|
||||
setIsRequesting(false);
|
||||
setIsModalOpen(false);
|
||||
|
||||
notificationApi["info"]({
|
||||
message: t(
|
||||
"userProfile.changeEmailModal.request.200.title"
|
||||
),
|
||||
description: t(
|
||||
"userProfile.changeEmailModal.request.200.description"
|
||||
),
|
||||
});
|
||||
})
|
||||
.catch(() => setIsRequesting(false));
|
||||
})
|
||||
.catch(() => setIsRequesting(false));
|
||||
}}
|
||||
/>
|
||||
}
|
||||
>
|
||||
<Form form={form} layout="vertical" requiredMark={false}>
|
||||
<Typography.Paragraph type="secondary">
|
||||
{t("userProfile.changeEmailModal.info")}
|
||||
</Typography.Paragraph>
|
||||
|
||||
<MyEmailFormInput hasFeedback newEmail />
|
||||
</Form>
|
||||
</MyModal>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
function ChangePassword({
|
||||
notificationApi,
|
||||
globalRequestState,
|
||||
|
|
|
@ -1,39 +1,106 @@
|
|||
import { useParams } from "react-router-dom";
|
||||
import MyModal from "../../Components/MyModal";
|
||||
import { Button, ConfigProvider, Flex, Result, Spin, notification } from "antd";
|
||||
import {
|
||||
Button,
|
||||
ConfigProvider,
|
||||
Flex,
|
||||
Form,
|
||||
Result,
|
||||
Spin,
|
||||
notification,
|
||||
} from "antd";
|
||||
import { useEffect, useState } from "react";
|
||||
import MyAppLogo from "../../Components/MyAppLogo";
|
||||
import PageNotFound, { ButtonBackHome } from "../PageNotFound";
|
||||
import { Constants, myFetch } from "../../utils";
|
||||
import {
|
||||
Constants,
|
||||
EncodeStringToBase64,
|
||||
myFetch,
|
||||
showInputsInvalidNotification,
|
||||
showPasswordIncorrectNotification,
|
||||
} from "../../utils";
|
||||
import { useTranslation } from "react-i18next";
|
||||
import { RequestState } from "../../Components/MyRequestStateItem";
|
||||
import { MyPasswordFormInput } from "../../Components/MyFormInputs";
|
||||
|
||||
export default function Verification() {
|
||||
const { state, emailVerificationId } = useParams();
|
||||
const { t } = useTranslation();
|
||||
const [notificationApi, notificationContextHolder] =
|
||||
notification.useNotification();
|
||||
const [form] = Form.useForm();
|
||||
|
||||
const [isRequesting, setIsRequesting] = useState(RequestState.REQUESTING);
|
||||
const [isRequesting, setIsRequesting] = useState(RequestState.INIT);
|
||||
|
||||
const ButtonToLogin = () => {
|
||||
return (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
window.location.href = `${Constants.DASHBOARD_ADDRESS}${Constants.ROUTE_PATHS.AUTHENTICATION.LOGIN}`;
|
||||
}}
|
||||
>
|
||||
{t("verification.buttonToLogin")}
|
||||
</Button>
|
||||
);
|
||||
};
|
||||
|
||||
const content = [
|
||||
{
|
||||
requesting: {
|
||||
title: t("verification.content.0.requesting.title"),
|
||||
extra: <Spin size="large" />,
|
||||
},
|
||||
sucess: {
|
||||
success: {
|
||||
title: t("verification.content.0.success.title"),
|
||||
extra: <ButtonToLogin />,
|
||||
},
|
||||
},
|
||||
{
|
||||
init: {
|
||||
title: t("verification.content.1.init.title"),
|
||||
subTitle: t("verification.content.1.init.subTitle"),
|
||||
extra: (
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => {
|
||||
window.location.href = `${Constants.DASHBOARD_ADDRESS}${Constants.ROUTE_PATHS.AUTHENTICATION.LOGIN}`;
|
||||
}}
|
||||
>
|
||||
{t("verification.content.0.success.button")}
|
||||
</Button>
|
||||
<Form form={form} layout="vertical" requiredMark={false}>
|
||||
<MyPasswordFormInput />
|
||||
|
||||
<Button
|
||||
type="primary"
|
||||
htmlType="submit"
|
||||
block
|
||||
onClick={() => {
|
||||
form
|
||||
.validateFields()
|
||||
.then((values) => {
|
||||
setIsRequesting(RequestState.REQUESTING);
|
||||
|
||||
sendVerifyRequest({
|
||||
password: EncodeStringToBase64(values.password),
|
||||
})
|
||||
.then(() => setIsRequesting(RequestState.SUCCESS))
|
||||
.catch(() => {
|
||||
setIsRequesting(RequestState.INIT);
|
||||
showPasswordIncorrectNotification(notificationApi, t);
|
||||
});
|
||||
})
|
||||
.catch(() =>
|
||||
showInputsInvalidNotification(notificationApi, t)
|
||||
);
|
||||
}}
|
||||
>
|
||||
{t("common.button.confirm")}
|
||||
</Button>
|
||||
</Form>
|
||||
),
|
||||
},
|
||||
requesting: {
|
||||
title: t("verification.content.1.requesting.title"),
|
||||
extra: <Spin size="large" />,
|
||||
},
|
||||
success: {
|
||||
title: t("verification.content.1.success.title"),
|
||||
extra: <ButtonToLogin />,
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
|
@ -47,19 +114,31 @@ export default function Verification() {
|
|||
state > content.length - 1
|
||||
) {
|
||||
elementContent = <PageNotFound useHref />;
|
||||
} else if (isRequesting === RequestState.INIT) {
|
||||
elementContent = (
|
||||
<Result
|
||||
status="info"
|
||||
title={content[parsedState].init?.title}
|
||||
subTitle={content[parsedState].init?.subTitle}
|
||||
extra={content[parsedState].init?.extra}
|
||||
/>
|
||||
);
|
||||
} else if (isRequesting === RequestState.REQUESTING) {
|
||||
elementContent = (
|
||||
<Result
|
||||
title={content[parsedState].requesting.title}
|
||||
extra={<Spin size="large" />}
|
||||
status="info"
|
||||
title={content[parsedState].requesting?.title}
|
||||
subTitle={content[parsedState].requesting?.subTitle}
|
||||
extra={content[parsedState].requesting?.extra}
|
||||
/>
|
||||
);
|
||||
} else if (isRequesting === RequestState.SUCCESS) {
|
||||
elementContent = (
|
||||
<Result
|
||||
status="success"
|
||||
title={content[parsedState].sucess.title}
|
||||
extra={content[parsedState].sucess.extra}
|
||||
title={content[parsedState].success?.title}
|
||||
subTitle={content[parsedState].success?.subTitle}
|
||||
extra={content[parsedState].success?.extra}
|
||||
/>
|
||||
);
|
||||
} else if (isRequesting === RequestState.FAILED) {
|
||||
|
@ -75,18 +154,29 @@ export default function Verification() {
|
|||
elementContent = <PageNotFound useHref />;
|
||||
}
|
||||
|
||||
const sendVerifyRequest = (body) => {
|
||||
return myFetch({
|
||||
method: "POST",
|
||||
url: `/user/verify/${state}/${emailVerificationId}`,
|
||||
notificationApi: notificationApi,
|
||||
body: body,
|
||||
t: t,
|
||||
});
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
if (emailVerificationId === undefined || state === undefined) {
|
||||
setIsRequesting(false);
|
||||
setIsRequesting(RequestState.FAILED);
|
||||
return;
|
||||
}
|
||||
|
||||
myFetch({
|
||||
url: `/user/verify/${state}/${emailVerificationId}`,
|
||||
notificationApi: notificationApi,
|
||||
t: t,
|
||||
})
|
||||
.then(() => setIsRequesting(RequestState.SUCCESS))
|
||||
sendVerifyRequest()
|
||||
.then((res) => {
|
||||
// if an action by the user is required, do not change the state (e.g. for providing a new password)
|
||||
if (res.status !== undefined && res.status === "actionRequired") return;
|
||||
|
||||
setIsRequesting(RequestState.SUCCESS);
|
||||
})
|
||||
.catch(() => setIsRequesting(RequestState.FAILED));
|
||||
}, []);
|
||||
|
||||
|
|
Loading…
Reference in New Issue