export account

master
alex 2024-02-03 22:36:37 +01:00
parent 5e0af4aa88
commit eb74f42aa1
4 changed files with 296 additions and 131 deletions

View File

@ -33,6 +33,8 @@
"accountNamePlaceholder": "Geben Sie Ihren Benutzernamen ein", "accountNamePlaceholder": "Geben Sie Ihren Benutzernamen ein",
"accountNamePlaceholderThirdPerson": "Geben Sie den Benutzernamen ein", "accountNamePlaceholderThirdPerson": "Geben Sie den Benutzernamen ein",
"accountNameInfo": "Der Benutzername wird verwendet, um sich anzumelden.", "accountNameInfo": "Der Benutzername wird verwendet, um sich anzumelden.",
"email": "E-Mail",
"emailPlaceholder": "Geben Sie Ihre E-Mail ein",
"password": "Passwort", "password": "Passwort",
"passwordPlaceholder": "Geben Sie Ihr Passwort ein", "passwordPlaceholder": "Geben Sie Ihr Passwort ein",
"noDataFound": "Keine Einträge gefunden", "noDataFound": "Keine Einträge gefunden",
@ -53,6 +55,8 @@
"accountNameRequired": "Benutzername ist erforderlich", "accountNameRequired": "Benutzername ist erforderlich",
"accountNameMinLength": "Benutzername muss mindestens {{minLength}} Zeichen lang sein", "accountNameMinLength": "Benutzername muss mindestens {{minLength}} Zeichen lang sein",
"accountNameTaken": "Benutzername ist bereits vergeben", "accountNameTaken": "Benutzername ist bereits vergeben",
"emailRequired": "E-Mail ist erforderlich",
"emailInvalid": "E-Mail ist ungültig",
"passwordRequired": "Passwort ist erforderlich", "passwordRequired": "Passwort ist erforderlich",
"passwordMinLength": "Passwort muss mindestens {{minLength}} Zeichen lang sein", "passwordMinLength": "Passwort muss mindestens {{minLength}} Zeichen lang sein",
"calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich", "calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich",
@ -285,7 +289,7 @@
"yourProfile": "Ihr Profil", "yourProfile": "Ihr Profil",
"changePassword": "Passwort ändern", "changePassword": "Passwort ändern",
"yourSessions": "Ihre Sitzungen", "yourSessions": "Ihre Sitzungen",
"deleteAccount": "Konto löschen" "yourAccount": "Ihr Konto"
}, },
"yourProfile": { "yourProfile": {
"cardTitle": "Ihr Profil", "cardTitle": "Ihr Profil",
@ -318,6 +322,21 @@
"description": "Möchten Sie diese Sitzung wirklich abmelden?" "description": "Möchten Sie diese Sitzung wirklich abmelden?"
} }
}, },
"accountExport": {
"cardTitle": "Konto exportieren",
"info": "Exportieren Sie Ihr Konto und alle Daten, die im Laufe der Nutzung des Dashboards entstanden sind.",
"button": "Konto export anfordern",
"modal": {
"title": "Konto export anfordern",
"info": "Dies kann einige Zeit in Anspruch nehmen. Wir senden Ihnen eine E-Mail mit dem Download-Link, sobald der Export fertig ist.",
"request": {
"400": {
"title": "Passwort falsch",
"description": "Bitte überprüfen Sie Ihr Passwort und versuchen Sie es erneut."
}
}
}
},
"deleteAccount": { "deleteAccount": {
"cardTitle": "Konto löschen", "cardTitle": "Konto löschen",
"info": "Ihr Konto und alle Ihre Daten endgültig löschen.", "info": "Ihr Konto und alle Ihre Daten endgültig löschen.",
@ -335,7 +354,6 @@
"improveLabel": "Wie können wir das Produkt verbessern?", "improveLabel": "Wie können wir das Produkt verbessern?",
"improveRequired": "Bitte geben Sie Ihr Feedback ein", "improveRequired": "Bitte geben Sie Ihr Feedback ein",
"improveMinLengthRequired": "Bitte geben Sie mindestens {{minLength}} Zeichen ein", "improveMinLengthRequired": "Bitte geben Sie mindestens {{minLength}} Zeichen ein",
"button": "Konto löschen",
"request": { "request": {
"400": { "400": {
"title": "Passwort falsch", "title": "Passwort falsch",

View File

@ -33,6 +33,8 @@
"accountNamePlaceholder": "Enter your account name", "accountNamePlaceholder": "Enter your account name",
"accountNamePlaceholderThirdPerson": "Enter the account name", "accountNamePlaceholderThirdPerson": "Enter the account name",
"accountNameInfo": "The account name is used to log in.", "accountNameInfo": "The account name is used to log in.",
"email": "Email",
"emailPlaceholder": "Enter your email",
"password": "Password", "password": "Password",
"passwordPlaceholder": "Enter your password", "passwordPlaceholder": "Enter your password",
"noDataFound": "No data found", "noDataFound": "No data found",
@ -53,6 +55,8 @@
"accountNameRequired": "Please enter your account name", "accountNameRequired": "Please enter your account name",
"accountNameMinLength": "Account name must be at least {{minLength}} characters", "accountNameMinLength": "Account name must be at least {{minLength}} characters",
"accountNameTaken": "Account name already exists", "accountNameTaken": "Account name already exists",
"emailRequired": "Please enter your email",
"emailInvalid": "Please enter a valid email",
"passwordRequired": "Please enter your password", "passwordRequired": "Please enter your password",
"passwordMinLength": "Password must be at least {{minLength}} characters", "passwordMinLength": "Password must be at least {{minLength}} characters",
"calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days", "calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days",
@ -288,7 +292,7 @@
"yourProfile": "Your profile", "yourProfile": "Your profile",
"changePassword": "Change password", "changePassword": "Change password",
"yourSessions": "Your sessions", "yourSessions": "Your sessions",
"deleteAccount": "Delete account" "yourAccount": "Your account"
}, },
"yourProfile": { "yourProfile": {
"cardTitle": "Your profile", "cardTitle": "Your profile",
@ -327,6 +331,21 @@
"description": "Are you sure you want to delete this session?" "description": "Are you sure you want to delete this session?"
} }
}, },
"accountExport": {
"cardTitle": "Account export",
"info": "Export your account and all your data to a ZIP file.",
"button": "Export account",
"modal": {
"title": "Request account export",
"info": "Export your account and all data created during the use of the dashboard.",
"request": {
"400": {
"title": "Password incorrect",
"description": "Please check your password and try again."
}
}
}
},
"deleteAccount": { "deleteAccount": {
"cardTitle": "Delete account", "cardTitle": "Delete account",
"info": "Permanently delete your account and all your data.", "info": "Permanently delete your account and all your data.",
@ -344,7 +363,6 @@
"improveLabel": "How can we improve the product?", "improveLabel": "How can we improve the product?",
"improveRequired": "Please enter your feedback", "improveRequired": "Please enter your feedback",
"improveMinLengthRequired": "Please enter at least {{minLength}} characters", "improveMinLengthRequired": "Please enter at least {{minLength}} characters",
"button": "Delete account",
"request": { "request": {
"400": { "400": {
"title": "Password incorrect", "title": "Password incorrect",

View File

@ -110,6 +110,28 @@ export function MyPasswordFormInput({
); );
} }
export function MyEmailFormInput() {
const { t } = useTranslation();
return (
<MyFormInput
formItemName="email"
minLength={0}
maxLength={64}
label={t("common.email")}
ruleMessageValueRequired={t("common.inputRules.emailRequired")}
inputPlaceholder={t("common.emailPlaceholder")}
inputType="email"
formItemRules={[
{
type: "email",
message: t("common.inputRules.emailInvalid"),
},
]}
/>
);
}
export function MyCalendarMaxFutureBookingDaysFormInput({ formItemName }) { export function MyCalendarMaxFutureBookingDaysFormInput({ formItemName }) {
const { t } = useTranslation(); const { t } = useTranslation();

View File

@ -1,9 +1,11 @@
import { import {
Button, Button,
Card, Card,
Col,
Form, Form,
Popconfirm, Popconfirm,
Radio, Radio,
Row,
Select, Select,
Skeleton, Skeleton,
Space, Space,
@ -31,6 +33,7 @@ import {
import { LogoutOutlined } from "@ant-design/icons"; import { LogoutOutlined } from "@ant-design/icons";
import { import {
MyAccountNameFormInput, MyAccountNameFormInput,
MyEmailFormInput,
MyFormInput, MyFormInput,
MyPasswordFormInput, MyPasswordFormInput,
MyUsernameFormInput, MyUsernameFormInput,
@ -64,7 +67,6 @@ export default function UserProfile({ setUserSession }) {
const tabItems = [ const tabItems = [
{ {
label: t("userProfile.tabs.yourProfile"), label: t("userProfile.tabs.yourProfile"),
cardTitle: t("userProfile.yourProfile.cardTitle"),
children: ( children: (
<YourProfile <YourProfile
notificationApi={notificationApi} notificationApi={notificationApi}
@ -75,7 +77,6 @@ export default function UserProfile({ setUserSession }) {
}, },
{ {
label: t("userProfile.tabs.changePassword"), label: t("userProfile.tabs.changePassword"),
cardTitle: t("userProfile.changePassword.cardTitle"),
children: ( children: (
<ChangePassword <ChangePassword
notificationApi={notificationApi} notificationApi={notificationApi}
@ -87,24 +88,20 @@ export default function UserProfile({ setUserSession }) {
}, },
{ {
label: t("userProfile.tabs.yourSessions"), label: t("userProfile.tabs.yourSessions"),
cardTitle: t("userProfile.yourSessions.cardTitle"),
children: <YourSessions />, children: <YourSessions />,
}, },
{ {
label: t("userProfile.tabs.deleteAccount"), label: t("userProfile.tabs.yourAccount"),
cardTitle: t("userProfile.deleteAccount.cardTitle"), children: <YourAccount />,
children: <DeleteAccount />,
}, },
].map((item, index) => { ].map((item, index) => {
return { return {
key: index, key: index,
...item, ...item,
children: ( children: (
<Card title={item.cardTitle}>
<MySupsenseFallback spinnerCentered={false}> <MySupsenseFallback spinnerCentered={false}>
{item.children} {item.children}
</MySupsenseFallback> </MySupsenseFallback>
</Card>
), ),
}; };
}); });
@ -305,8 +302,12 @@ function YourProfile({
}, [language, analyticsEnabled, username, accountName]); }, [language, analyticsEnabled, username, accountName]);
return ( return (
<Card title={t("userProfile.yourProfile.cardTitle")}>
<Form form={form} layout="vertical" requiredMark={false}> <Form form={form} layout="vertical" requiredMark={false}>
<Form.Item name="language" label={t("userProfile.yourProfile.language")}> <Form.Item
name="language"
label={t("userProfile.yourProfile.language")}
>
{requestState === RequestState.INIT ? ( {requestState === RequestState.INIT ? (
<Skeleton.Input active block></Skeleton.Input> <Skeleton.Input active block></Skeleton.Input>
) : ( ) : (
@ -340,7 +341,9 @@ function YourProfile({
)} )}
</Form.Item> </Form.Item>
<MyUsernameFormInput showSkeleton={requestState === RequestState.INIT} /> <MyUsernameFormInput
showSkeleton={requestState === RequestState.INIT}
/>
<MyAccountNameFormInput <MyAccountNameFormInput
showSkeleton={requestState === RequestState.INIT} showSkeleton={requestState === RequestState.INIT}
@ -348,6 +351,7 @@ function YourProfile({
disableAccountNameCheck={requestData.account_name === accountName} disableAccountNameCheck={requestData.account_name === accountName}
/> />
</Form> </Form>
</Card>
); );
} }
@ -417,7 +421,13 @@ function ChangePassword({
}; };
return ( return (
<Form form={form} layout="vertical" autoComplete="off" requiredMark={false}> <Card title={t("userProfile.changePassword.cardTitle")}>
<Form
form={form}
layout="vertical"
autoComplete="off"
requiredMark={false}
>
<MyPasswordFormInput <MyPasswordFormInput
propsInput={{ propsInput={{
id: "currentPassword", id: "currentPassword",
@ -480,6 +490,7 @@ function ChangePassword({
</Space> </Space>
</Form.Item> </Form.Item>
</Form> </Form>
</Card>
); );
} }
@ -583,6 +594,7 @@ function YourSessions() {
useEffect(() => requestSessions(), []); useEffect(() => requestSessions(), []);
return ( return (
<Card title={t("userProfile.yourSessions.cardTitle")}>
<MyTable <MyTable
props={{ props={{
loading: isRequesting, loading: isRequesting,
@ -590,21 +602,40 @@ function YourSessions() {
dataSource: getTableItems(), dataSource: getTableItems(),
}} }}
/> />
</Card>
); );
} }
function DeleteAccount() { function YourAccount() {
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] = const [notificationApi, notificationContextHolder] =
notification.useNotification(); notification.useNotification();
return (
<>
{notificationContextHolder}
<Row gutter={[16, 16]}>
<Col span={24}>
<YourAccountExportDataCard notificationApi={notificationApi} />
</Col>
<Col span={24}>
<YourAccountDeleteAccountCard notificationApi={notificationApi} />
</Col>
</Row>
</>
);
}
function YourAccountExportDataCard({ notificationApi }) {
const { t } = useTranslation();
const [form] = Form.useForm(); const [form] = Form.useForm();
const [isModalOpen, setIsModalOpen] = useState(false); const [isModalOpen, setIsModalOpen] = useState(false);
const [isRequesting, setIsRequesting] = useState(false); const [isRequesting, setIsRequesting] = useState(false);
const handleModalClose = () => { const handleModalClose = () => setIsModalOpen(false);
setIsModalOpen(false);
};
useEffect(() => { useEffect(() => {
if (!isModalOpen) return; if (!isModalOpen) return;
@ -613,9 +644,85 @@ function DeleteAccount() {
}, [isModalOpen]); }, [isModalOpen]);
return ( return (
<> <Card title={t("userProfile.accountExport.cardTitle")}>
{notificationContextHolder} <Space direction="vertical">
<Typography.Text>{t("userProfile.accountExport.info")}</Typography.Text>
<Button type="default" onClick={() => setIsModalOpen(true)}>
{t("userProfile.accountExport.button")}
</Button>
</Space>
<MyModal
title={t("userProfile.accountExport.modal.title")}
isOpen={isModalOpen}
onCancel={handleModalClose}
footer={
<MyModalCloseConfirmButtonFooter
isConfirmButtonLoading={isRequesting}
onConfirm={() => {
form
.validateFields()
.then((values) => {
setIsRequesting(true);
myFetch({
url: "/user/profile/export",
method: "POST",
body: {
email: values.email,
password: EncodeStringToBase64(values.password),
},
})
.then(() => {
setIsRequesting(false);
setIsModalOpen(false);
})
.catch((errStatus) => {
setIsRequesting(false);
if (errStatus === 400) {
showPasswordIncorrectNotification(notificationApi, t);
}
});
})
.catch(() => showInputsInvalidNotification(notificationApi, t));
}}
onCancel={handleModalClose}
/>
}
>
<Form form={form} layout="vertical" requiredMark={false}>
<Typography.Paragraph type="secondary">
{t("userProfile.accountExport.modal.info")}
</Typography.Paragraph>
<MyEmailFormInput />
<MyPasswordFormInput />
</Form>
</MyModal>
</Card>
);
}
function YourAccountDeleteAccountCard({ notificationApi }) {
const { t } = useTranslation();
const [form] = Form.useForm();
const [isModalOpen, setIsModalOpen] = useState(false);
const [isRequesting, setIsRequesting] = useState(false);
const handleModalClose = () => setIsModalOpen(false);
/*useEffect(() => {
if (!isModalOpen) return;
form.resetFields();
}, [isModalOpen]); */
return (
<Card title={t("userProfile.deleteAccount.cardTitle")}>
<Space direction="vertical"> <Space direction="vertical">
<Typography.Text>{t("userProfile.deleteAccount.info")}</Typography.Text> <Typography.Text>{t("userProfile.deleteAccount.info")}</Typography.Text>
@ -660,7 +767,7 @@ function DeleteAccount() {
} }
}); });
}) })
.catch(() => setIsRequesting(false)); .catch(() => showInputsInvalidNotification(notificationApi, t));
}} }}
onCancel={handleModalClose} onCancel={handleModalClose}
/> />
@ -722,6 +829,6 @@ function DeleteAccount() {
<MyPasswordFormInput /> <MyPasswordFormInput />
</Form> </Form>
</MyModal> </MyModal>
</> </Card>
); );
} }