delete account

master
alex 2024-01-28 00:13:51 +01:00
parent e99613e767
commit 451f2297fd
6 changed files with 212 additions and 26 deletions

View File

@ -67,6 +67,10 @@
"failedInternetProblem": { "failedInternetProblem": {
"title": "Anfrage fehlgeschlagen", "title": "Anfrage fehlgeschlagen",
"description": "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut." "description": "Bitte überprüfen Sie Ihre Internetverbindung und versuchen Sie es erneut."
},
"passwordIncorrect": {
"title": "Passwort falsch",
"description": "Bitte überprüfen Sie Ihr Passwort und versuchen Sie es erneut."
} }
} }
}, },
@ -214,13 +218,7 @@
"title": "Google Kalender trennen", "title": "Google Kalender trennen",
"description": "Aus Sicherheitsgründen müssen Sie Ihr Passwort eingeben, um die Verbindung zu Ihrem Google Kalender zu trennen.", "description": "Aus Sicherheitsgründen müssen Sie Ihr Passwort eingeben, um die Verbindung zu Ihrem Google Kalender zu trennen.",
"checkboxDeleteCalendars": "Kalender für Öffnungszeiten und Termine löschen (Alle Termine werden gelöscht und können nicht wiederhergestellt werden)", "checkboxDeleteCalendars": "Kalender für Öffnungszeiten und Termine löschen (Alle Termine werden gelöscht und können nicht wiederhergestellt werden)",
"button": "Google Kalender trennen", "button": "Google Kalender trennen"
"request": {
"400": {
"title": "Passwort ungültig",
"description": "Bitte überprüfen Sie Ihr Passwort und versuchen Sie es erneut."
}
}
}, },
"calendarFrameCustomerView": "Terminkalenderansicht der Kunden" "calendarFrameCustomerView": "Terminkalenderansicht der Kunden"
}, },
@ -283,7 +281,30 @@
} }
}, },
"deleteAccount": { "deleteAccount": {
"cardTitle": "Konto löschen" "cardTitle": "Konto löschen",
"info": "Ihr Konto und alle Ihre Daten endgültig löschen.",
"button": "Konto löschen",
"modal": {
"title": "Konto löschen",
"info": "Ihr Konto wird sofort deaktiviert und nach {{days}} Tagen gelöscht. Während dieser Zeit können Sie Ihr Konto wiederherstellen, indem Sie sich erneut anmelden.",
"why": "Warum löschen Sie Ihr Konto?",
"whyRuleRequired": "Bitte wählen Sie einen Grund",
"radioOptions": {
"notNeeded": "Ich benötige es nicht mehr",
"notUseful": "Ich finde es nicht nützlich",
"other": "Sonstiges"
},
"improveLabel": "Wie können wir das Produkt verbessern?",
"improveRequired": "Bitte geben Sie Ihr Feedback ein",
"improveMinLengthRequired": "Bitte geben Sie mindestens {{minLength}} Zeichen ein",
"button": "Konto löschen",
"request": {
"400": {
"title": "Passwort falsch",
"description": "Bitte überprüfen Sie Ihr Passwort und versuchen Sie es erneut."
}
}
}
} }
} }
} }

View File

@ -67,6 +67,10 @@
"failedInternetProblem": { "failedInternetProblem": {
"title": "Request failed", "title": "Request failed",
"description": "The request failed. Please check your internet connection and try again." "description": "The request failed. Please check your internet connection and try again."
},
"passwordIncorrect": {
"title": "Password incorrect",
"description": "Please check your password and try again."
} }
} }
}, },
@ -217,13 +221,7 @@
"title": "Unlink Google Calendar", "title": "Unlink Google Calendar",
"description": "For security reasons, you need to enter your password to unlink your Google Calendar.", "description": "For security reasons, you need to enter your password to unlink your Google Calendar.",
"checkboxDeleteCalendars": "Delete calendar for opening hours and appointments (all appointments are deleted and cannot be restored)", "checkboxDeleteCalendars": "Delete calendar for opening hours and appointments (all appointments are deleted and cannot be restored)",
"button": "Unlink Google Calendar", "button": "Unlink Google Calendar"
"request": {
"400": {
"title": "Password incorrect",
"description": "Please check your password and try again."
}
}
}, },
"calendarFrameCustomerView": "Appointment diary view of customers" "calendarFrameCustomerView": "Appointment diary view of customers"
}, },
@ -292,7 +290,30 @@
} }
}, },
"deleteAccount": { "deleteAccount": {
"cardTitle": "Delete account" "cardTitle": "Delete account",
"info": "Permanently delete your account and all your data.",
"button": "Delete account",
"modal": {
"title": "Delete account",
"info": "Your account will be deactivated immediately and deleted after {{days}} days. During this time, you can restore your account by logging in again.",
"why": "Why are you deleting your account?",
"whyRuleRequired": "Please select a reason",
"radioOptions": {
"notNeeded": "I don't need it anymore",
"notUseful": "I don't find it useful",
"other": "Other"
},
"improveLabel": "How can we improve the product?",
"improveRequired": "Please enter your feedback",
"improveMinLengthRequired": "Please enter at least {{minLength}} characters",
"button": "Delete account",
"request": {
"400": {
"title": "Password incorrect",
"description": "Please check your password and try again."
}
}
}
} }
} }
} }

View File

@ -108,6 +108,7 @@ export function MyModalCloseConfirmButtonFooter({
onCancel, onCancel,
onConfirm, onConfirm,
isConfirmButtonLoading, isConfirmButtonLoading,
danger = false,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@ -115,6 +116,7 @@ export function MyModalCloseConfirmButtonFooter({
<> <>
<Button onClick={onCancel}>{t("common.button.close")}</Button> <Button onClick={onCancel}>{t("common.button.close")}</Button>
<Button <Button
danger={danger}
onClick={onConfirm} onClick={onConfirm}
type="primary" type="primary"
loading={isConfirmButtonLoading} loading={isConfirmButtonLoading}

View File

@ -19,6 +19,7 @@ import {
getUserSessionFromLocalStorage, getUserSessionFromLocalStorage,
myFetch, myFetch,
showInputsInvalidNotification, showInputsInvalidNotification,
showPasswordIncorrectNotification,
} from "../../../utils"; } from "../../../utils";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import MyCenteredContainer from "../../../Components/MyContainer"; import MyCenteredContainer from "../../../Components/MyContainer";
@ -406,14 +407,7 @@ function CardPersonalCalendarSettings({ settings }) {
setIsRequesting(false); setIsRequesting(false);
if (errStatus === 400) { if (errStatus === 400) {
notificationApi["error"]({ showPasswordIncorrectNotification(notificationApi, t);
message: t(
"calendar.unlinkGoogleCalendar.request.400.title"
),
description: t(
"calendar.unlinkGoogleCalendar.request.400.description"
),
});
} }
}); });
}) })

View File

@ -3,20 +3,24 @@ import {
Card, Card,
Form, Form,
Popconfirm, Popconfirm,
Radio,
Select, Select,
Skeleton, Skeleton,
Space, Space,
Switch, Switch,
Tabs, Tabs,
Tag, Tag,
Typography,
notification, notification,
} from "antd"; } from "antd";
import { import {
Constants,
EncodeStringToBase64, EncodeStringToBase64,
FormatDatetime, FormatDatetime,
handleLogout, handleLogout,
myFetch, myFetch,
showInputsInvalidNotification, showInputsInvalidNotification,
showPasswordIncorrectNotification,
} from "../../utils"; } from "../../utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
@ -27,12 +31,16 @@ import {
import { LogoutOutlined } from "@ant-design/icons"; import { LogoutOutlined } from "@ant-design/icons";
import { import {
MyAccountNameFormInput, MyAccountNameFormInput,
MyFormInput,
MyPasswordFormInput, MyPasswordFormInput,
MyUsernameFormInput, MyUsernameFormInput,
} from "../../Components/MyFormInputs"; } from "../../Components/MyFormInputs";
import { MySupsenseFallback } from "../../Components/MySupsenseFallback"; import { MySupsenseFallback } from "../../Components/MySupsenseFallback";
import { useSideBarContext } from "../../Contexts/SideBarContext"; import { useSideBarContext } from "../../Contexts/SideBarContext";
import MyTable from "../../Components/MyTable"; import MyTable from "../../Components/MyTable";
import MyModal, {
MyModalCloseConfirmButtonFooter,
} from "../../Components/MyModal";
const globalRequestStatePreview = { const globalRequestStatePreview = {
global: RequestState.INIT, global: RequestState.INIT,
@ -586,5 +594,134 @@ function YourSessions() {
} }
function DeleteAccount() { function DeleteAccount() {
return <div>Delete account, Password and feedback</div>; const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
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 (
<>
{notificationContextHolder}
<Space direction="vertical">
<Typography.Text>{t("userProfile.deleteAccount.info")}</Typography.Text>
<Button type="primary" danger onClick={() => setIsModalOpen(true)}>
{t("userProfile.deleteAccount.button")}
</Button>
</Space>
<MyModal
title={t("userProfile.deleteAccount.modal.title")}
isOpen={isModalOpen}
onCancel={handleModalClose}
footer={
<MyModalCloseConfirmButtonFooter
danger
isConfirmButtonLoading={isRequesting}
onConfirm={() => {
form
.validateFields()
.then((values) => {
setIsRequesting(true);
myFetch({
url: "/user/profile/delete",
method: "DELETE",
body: {
feedback: values.improve,
reason: values["deleteReason"],
password: EncodeStringToBase64(values.password),
},
})
.then(() => {
setIsRequesting(false);
window.location.reload();
})
.catch((errStatus) => {
setIsRequesting(false);
if (errStatus === 400) {
showPasswordIncorrectNotification(notificationApi, t);
}
});
})
.catch(() => setIsRequesting(false));
}}
onCancel={handleModalClose}
/>
}
>
<Form form={form} layout="vertical" requiredMark={false}>
<Typography.Paragraph type="secondary">
{t("userProfile.deleteAccount.modal.info", {
days: Constants.ACCOUNT_DELETED_AFTER_DAYS,
})}
</Typography.Paragraph>
<Form.Item
name="deleteReason"
label={t("userProfile.deleteAccount.modal.why")}
required
rules={[
{
required: true,
message: t("userProfile.deleteAccount.modal.whyRuleRequired"),
},
]}
>
<Radio.Group>
<Space direction="vertical">
<Radio value="notNeeded">
{t("userProfile.deleteAccount.modal.radioOptions.notNeeded")}
</Radio>
<Radio value="notUseful">
{t("userProfile.deleteAccount.modal.radioOptions.notUseful")}
</Radio>
<Radio value="other">
{t("userProfile.deleteAccount.modal.radioOptions.other")}
</Radio>
</Space>
</Radio.Group>
</Form.Item>
<MyFormInput
formItemName="improve"
minLength={Constants.GLOBALS.MIN_FEEDBACK_LENGTH}
maxLength={Constants.GLOBALS.MAX_FEEDBACK_LENGTH}
inputType="textarea"
propsInput={{
showCount: true,
}}
label={t("userProfile.deleteAccount.modal.improveLabel")}
ruleMessageValueRequired={t(
"userProfile.deleteAccount.modal.improveRequired"
)}
ruleMessageValueMinLengthRequired={t(
"userProfile.deleteAccount.modal.improveMinLengthRequired",
{
minLength: Constants.GLOBALS.MIN_FEEDBACK_LENGTH,
}
)}
/>
<MyPasswordFormInput />
</Form>
</MyModal>
</>
);
} }

View File

@ -68,10 +68,13 @@ export const Constants = {
MAX_CALENDAR_FUTURE_BOOKING_DAYS: 365, MAX_CALENDAR_FUTURE_BOOKING_DAYS: 365,
MIN_CALENDAR_EARLIEST_BOOKING_TIME: 0, MIN_CALENDAR_EARLIEST_BOOKING_TIME: 0,
MAX_CALENDAR_EARLIEST_BOOKING_TIME: 60 * 24, // 24 hours in minutes MAX_CALENDAR_EARLIEST_BOOKING_TIME: 60 * 24, // 24 hours in minutes
MIN_FEEDBACK_LENGTH: 10,
MAX_FEEDBACK_LENGTH: 1024,
}, },
DELAY_ACCOUNT_NAME_CHECK: 250, DELAY_ACCOUNT_NAME_CHECK: 250,
CLARITY_PROJECT_ID: "kr0pale8uy", CLARITY_PROJECT_ID: "kr0pale8uy",
KK_JOBS_URL: "https://kk-innovation.eu/jobs/", KK_JOBS_URL: "https://kk-innovation.eu/jobs/",
ACCOUNT_DELETED_AFTER_DAYS: 30,
}; };
export const AppStyle = { export const AppStyle = {
@ -247,8 +250,16 @@ export function showInputsInvalidNotification(notificationApi, t) {
}); });
} }
export function showPasswordIncorrectNotification(notificationApi, t) {
notificationApi["warning"]({
message: t("common.request.passwordIncorrect.title"),
description: t("common.request.passwordIncorrect.description"),
});
}
export function handleLogout({ setUserSession }) { export function handleLogout({ setUserSession }) {
setUserSession(); if (setUserSession !== undefined) setUserSession();
window.location.href = "/"; window.location.href = "/";
} }