your profile and change password

master
alex 2024-01-27 13:22:51 +01:00
parent 2fe3558560
commit dc82975575
8 changed files with 538 additions and 143 deletions

View File

@ -245,9 +245,35 @@
} }
}, },
"userProfile": { "userProfile": {
"title": "Ihr Profil", "tabs": {
"language": "Sprache", "yourProfile": "Ihr Profil",
"analytics": "Analytik", "changePassword": "Passwort ändern",
"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." "yourSessions": "Ihre Sitzungen",
"deleteAccount": "Konto löschen"
},
"yourProfile": {
"cardTitle": "Ihr Profil",
"language": "Sprache",
"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."
},
"changePassword": {
"cardTitle": "Passwort ändern",
"currentPassword": "Aktuelles Passwort",
"currentPasswordPlaceholder": "Geben Sie Ihr aktuelles Passwort ein",
"newPassword": "Neues Passwort",
"newPasswordPlaceholder": "Geben Sie Ihr neues Passwort ein",
"confirmNewPassword": "Bestätigen Sie Ihr neues Passwort",
"confirmNewPasswordPlaceholder": "Bestätigen Sie Ihr neues Passwort",
"confirmNewPasswordMismatch": "Das neue Passwort stimmt nicht überein",
"button": "Passwort ändern",
"buttonInfo": "Nachdem Sie Ihr Passwort geändert haben, werden Sie abgemeldet und müssen sich erneut anmelden."
},
"yourSessions": {
"cardTitle": "Ihre Sitzungen"
},
"deleteAccount": {
"cardTitle": "Konto löschen"
}
} }
} }

View File

@ -67,7 +67,7 @@
"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."
} }
} }
}, },
"app": { "app": {
@ -238,7 +238,7 @@
}, },
"tabs": { "tabs": {
"general": "General", "general": "General",
"banner": "Banner", "banner": "Banner",
"colorPalette": "Color palette", "colorPalette": "Color palette",
"portfolio": "Portfolio", "portfolio": "Portfolio",
"ratings": "Ratings", "ratings": "Ratings",
@ -248,9 +248,41 @@
} }
}, },
"userProfile": { "userProfile": {
"title": "Your profile", "tabs": {
"language": "Language", "yourProfile": "Your profile",
"analytics": "Analytics", "changePassword": "Change password",
"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." "yourSessions": "Your sessions",
"deleteAccount": "Delete account"
},
"yourProfile": {
"cardTitle": "Your profile",
"language": "Language",
"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."
},
"changePassword": {
"cardTitle": "Change password",
"currentPassword": "Current password",
"currentPasswordPlaceholder": "Enter your current password",
"newPassword": "New password",
"newPasswordPlaceholder": "Enter your new password",
"confirmNewPassword": "Confirm new password",
"confirmNewPasswordPlaceholder": "Confirm your new password",
"confirmNewPasswordMismatch": "The new password do not match",
"button": "Change password",
"buttonInfo": "After changing your password, you will be logged out and have to log in again.",
"request": {
"400": {
"title": "Password incorrect",
"description": "Please check your password and try again."
}
}
},
"yourSessions": {
"cardTitle": "Your sessions"
},
"deleteAccount": {
"cardTitle": "Delete account"
}
} }
} }

View File

@ -3,13 +3,18 @@ import { Constants, myFetch } from "../../utils";
import { useRef } from "react"; import { useRef } from "react";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
export function MyUsernameFormInput({ propsFormItem, propsInput }) { export function MyUsernameFormInput({
propsFormItem,
propsInput,
showSkeleton,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<MyFormInput <MyFormInput
propsFormItem={propsFormItem} propsFormItem={propsFormItem}
propsInput={propsInput} propsInput={propsInput}
showSkeleton={showSkeleton}
formItemName="username" formItemName="username"
minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH} minLength={Constants.GLOBALS.MIN_USERNAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH} maxLength={Constants.GLOBALS.MAX_USERNAME_LENGTH}
@ -31,6 +36,7 @@ export function MyAccountNameFormInput({
propsInput, propsInput,
disableAccountNameCheck, disableAccountNameCheck,
hasFeedback, hasFeedback,
showSkeleton,
}) { }) {
const { t } = useTranslation(); const { t } = useTranslation();
@ -39,6 +45,7 @@ export function MyAccountNameFormInput({
propsFormItem={propsFormItem} propsFormItem={propsFormItem}
propsInput={propsInput} propsInput={propsInput}
hasFeedback={hasFeedback} hasFeedback={hasFeedback}
showSkeleton={showSkeleton}
formItemName="accountName" formItemName="accountName"
minLength={Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH} minLength={Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_ACCOUNT_NAME_LENGTH} maxLength={Constants.GLOBALS.MAX_ACCOUNT_NAME_LENGTH}
@ -61,17 +68,24 @@ export function MyAccountNameFormInput({
); );
} }
export function MyPasswordFormInput({ propsFormItem, propsInput }) { export function MyPasswordFormInput({
propsFormItem,
propsInput,
formItemName,
label,
inputPlaceholder,
formItemRules,
}) {
const { t } = useTranslation(); const { t } = useTranslation();
return ( return (
<MyFormInput <MyFormInput
propsFormItem={propsFormItem} propsFormItem={propsFormItem}
propsInput={propsInput} propsInput={propsInput}
formItemName="password" formItemName={formItemName || "password"}
minLength={Constants.GLOBALS.MIN_PASSWORD_LENGTH} minLength={Constants.GLOBALS.MIN_PASSWORD_LENGTH}
maxLength={Constants.GLOBALS.MAX_PASSWORD_LENGTH} maxLength={Constants.GLOBALS.MAX_PASSWORD_LENGTH}
label={t("common.password")} label={label || t("common.password")}
ruleMessageValueRequired={t("common.inputRules.passwordRequired")} ruleMessageValueRequired={t("common.inputRules.passwordRequired")}
ruleMessageValueMinLengthRequired={t( ruleMessageValueMinLengthRequired={t(
"common.inputRules.passwordMinLength", "common.inputRules.passwordMinLength",
@ -79,8 +93,9 @@ export function MyPasswordFormInput({ propsFormItem, propsInput }) {
minLength: Constants.GLOBALS.MIN_PASSWORD_LENGTH, minLength: Constants.GLOBALS.MIN_PASSWORD_LENGTH,
} }
)} )}
inputPlaceholder={t("common.passwordPlaceholder")} inputPlaceholder={inputPlaceholder || t("common.passwordPlaceholder")}
inputType="password" inputType="password"
formItemRules={formItemRules}
/> />
); );
} }
@ -144,6 +159,7 @@ export function MyAvailableCheckFormInput({
fetchParameter, fetchParameter,
fetchDelay, fetchDelay,
hasFeedback, hasFeedback,
showSkeleton,
}) { }) {
const delayTimeout = useRef(); const delayTimeout = useRef();
@ -166,6 +182,7 @@ export function MyAvailableCheckFormInput({
ruleMessageValueMinLengthRequired={ruleMessageValueMinLengthRequired} ruleMessageValueMinLengthRequired={ruleMessageValueMinLengthRequired}
inputPlaceholder={inputPlaceholder} inputPlaceholder={inputPlaceholder}
inputType={inputType} inputType={inputType}
showSkeleton={showSkeleton}
formItemRules={[ formItemRules={[
() => ({ () => ({
validator(_, value) { validator(_, value) {
@ -238,7 +255,12 @@ export function MyFormInput({
} }
if (formItemRules) { if (formItemRules) {
myFormItemRules.push(...formItemRules); // check if formItemRules is an array
if (Array.isArray(formItemRules)) {
myFormItemRules.push(...formItemRules);
} else {
myFormItemRules.push(formItemRules);
}
} }
if (inputType === "number") { if (inputType === "number") {

View File

@ -108,11 +108,7 @@ export default function StoreEmployees() {
t: t, t: t,
}) })
.then(() => fetchEmployees()) .then(() => fetchEmployees())
.catch((errStatus) => { .catch(() => setIsRequesting(false));
console.log(errStatus);
setIsRequesting(false);
});
}} }}
> >
<Link to="#">{t("common.button.delete")}</Link> <Link to="#">{t("common.button.delete")}</Link>
@ -152,9 +148,7 @@ export default function StoreEmployees() {
setIsRequesting(false); setIsRequesting(false);
setRequestData(data); setRequestData(data);
}) })
.catch((errStatus) => { .catch(() => {});
console.log(errStatus);
});
}; };
useEffect(() => fetchEmployees(), []); useEffect(() => fetchEmployees(), []);

View File

@ -44,9 +44,7 @@ export default function StoreSettings() {
setIsRequesting(false); setIsRequesting(false);
}) })
.catch((errStatus) => { .catch(() => {});
console.log(errStatus);
});
}, []); }, []);
useEffect(() => { useEffect(() => {
@ -83,10 +81,7 @@ export default function StoreSettings() {
t: t, t: t,
}) })
.then(() => setRequestState(RequestState.SUCCESS)) .then(() => setRequestState(RequestState.SUCCESS))
.catch((errStatus) => { .catch(() => setRequestState(RequestState.FAILED));
console.log(errStatus);
setRequestState(RequestState.FAILED);
});
}, 500); }, 500);
}, [companyName, phoneNumber, email, address]); }, [companyName, phoneNumber, email, address]);

View File

@ -11,12 +11,6 @@ const General = lazy(() => import("./General"));
const Banner = lazy(() => import("./Banner")); const Banner = lazy(() => import("./Banner"));
const ColorPalette = lazy(() => import("./ColorPalette")); const ColorPalette = lazy(() => import("./ColorPalette"));
function SuspenseFallback({ children }) {
return (
<MySupsenseFallback spinnerCentered={false}>{children}</MySupsenseFallback>
);
}
export default function Website() { export default function Website() {
const { storeId } = useParams(); const { storeId } = useParams();
const { t } = useTranslation(); const { t } = useTranslation();
@ -45,7 +39,11 @@ export default function Website() {
return { return {
key: index, key: index,
...item, ...item,
children: <SuspenseFallback>{item.children}</SuspenseFallback>, children: (
<MySupsenseFallback spinnerCentered={false}>
{item.children}
</MySupsenseFallback>
),
}; };
}); });
@ -86,7 +84,6 @@ export default function Website() {
<Card> <Card>
<Tabs <Tabs
type="card"
defaultActiveKey={0} defaultActiveKey={0}
items={tabItems} items={tabItems}
activeKey={activeTab} activeKey={activeTab}

View File

@ -6,9 +6,15 @@ import {
Skeleton, Skeleton,
Space, Space,
Switch, Switch,
Tabs,
notification, notification,
} from "antd"; } from "antd";
import { myFetch } from "../../utils"; import {
EncodeStringToBase64,
handleLogout,
myFetch,
showInputsInvalidNotification,
} from "../../utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useEffect, useRef, useState } from "react"; import { useEffect, useRef, useState } from "react";
import { import {
@ -16,82 +22,100 @@ import {
RequestStateItem, RequestStateItem,
} from "../../Components/MyRequestStateItem"; } from "../../Components/MyRequestStateItem";
import { LogoutOutlined } from "@ant-design/icons"; import { LogoutOutlined } from "@ant-design/icons";
import {
MyAccountNameFormInput,
MyPasswordFormInput,
MyUsernameFormInput,
} from "../../Components/MyFormInputs";
import { MySupsenseFallback } from "../../Components/MySupsenseFallback";
import { useSideBarContext } from "../../Contexts/SideBarContext";
const globalRequestStatePreview = {
global: RequestState.INIT,
yourProfile: RequestState.INIT,
changePassword: RequestState.INIT,
yourSessions: RequestState.INIT,
deleteAccount: RequestState.INIT,
};
export default function UserProfile({ setUserSession }) { export default function UserProfile({ setUserSession }) {
const { t, i18n } = useTranslation(); const { t } = useTranslation();
const [notificationApi, notificationContextHolder] = const [notificationApi, notificationContextHolder] =
notification.useNotification(); notification.useNotification();
const [form] = Form.useForm();
const [requestState, setRequestState] = useState(RequestState.INIT); const [globalRequestState, setGlobalRequestState] = useState(
globalRequestStatePreview
);
const [isRequestingLogout, setIsRequestingLogout] = useState(false); const [isRequestingLogout, setIsRequestingLogout] = useState(false);
const delayTimeout = useRef(); const [activeTab, setActiveTab] = useState(0);
const language = Form.useWatch("language", form); const tabItems = [
const analytics = Form.useWatch("analytics", form); {
label: t("userProfile.tabs.yourProfile"),
useEffect(() => { cardTitle: t("userProfile.yourProfile.cardTitle"),
myFetch({ children: (
url: "/user/profile/settings", <YourProfile
method: "GET", notificationApi={notificationApi}
notificationApi: notificationApi, globalRequestState={globalRequestState}
t: t, setGlobalRequestState={setGlobalRequestState}
}) />
.then((data) => { ),
form.setFieldsValue({ },
language: data.language, {
analytics: data.analytics_enabled, label: t("userProfile.tabs.changePassword"),
}); cardTitle: t("userProfile.changePassword.cardTitle"),
}) children: (
.catch((errStatus) => { <ChangePassword
console.log(errStatus); notificationApi={notificationApi}
}); globalRequestState={globalRequestState}
}, []); setGlobalRequestState={setGlobalRequestState}
setUserSession={setUserSession}
useEffect(() => { />
if (language === undefined || analytics === undefined) return; ),
},
if (RequestState.INIT === requestState) { {
setRequestState(RequestState.NOTHING); label: t("userProfile.tabs.yourSessions"),
return; cardTitle: t("userProfile.yourSessions.cardTitle"),
} children: <YourSessions />,
},
setRequestState(RequestState.REQUESTING); {
label: t("userProfile.tabs.deleteAccount"),
if (delayTimeout.current) { cardTitle: t("userProfile.deleteAccount.cardTitle"),
clearTimeout(delayTimeout.current); children: <DeleteAccount />,
} },
].map((item, index) => {
delayTimeout.current = setTimeout(() => { return {
myFetch({ key: index,
url: "/user/profile/settings", ...item,
method: "POST", children: (
body: { <Card title={item.cardTitle}>
language: language, <MySupsenseFallback spinnerCentered={false}>
analyticsEnabled: analytics, {item.children}
}, </MySupsenseFallback>
notificationApi: notificationApi, </Card>
t: t, ),
}) };
.then(() => setRequestState(RequestState.SUCCESS)) });
.catch((errStatus) => {
console.log(errStatus);
setRequestState(RequestState.FAILED);
});
}, 500);
}, [language, analytics]);
return ( return (
<> <>
{notificationContextHolder} {notificationContextHolder}
<Card <Tabs
title={t("userProfile.title")} defaultActiveKey={0}
extra={ items={tabItems}
activeKey={activeTab}
onChange={(key) => setActiveTab(key)}
tabBarExtraContent={
<Space> <Space>
<RequestStateItem <RequestStateItem
state={requestState} state={globalRequestState.global}
setRequestState={setRequestState} setRequestState={(state) =>
setGlobalRequestState({
...globalRequestState,
global: state,
})
}
/> />
<Button <Button
@ -107,10 +131,11 @@ export default function UserProfile({ setUserSession }) {
notificationApi: notificationApi, notificationApi: notificationApi,
t: t, t: t,
}) })
.then(() => { .then(() =>
setUserSession(); handleLogout({
window.location.href = "/"; setUserSession: setUserSession,
}) })
)
.catch(() => setIsRequestingLogout(false)); .catch(() => setIsRequestingLogout(false));
}} }}
> >
@ -118,43 +143,342 @@ export default function UserProfile({ setUserSession }) {
</Button> </Button>
</Space> </Space>
} }
> />
<Form layout="horizontal" form={form}>
<Form.Item name="language" label={t("userProfile.language")}>
{requestState === RequestState.INIT ? (
<Skeleton.Input active block></Skeleton.Input>
) : (
<Select
style={{ width: "100%" }}
options={[
{
value: "en",
label: "English",
},
{
value: "de",
label: "Deutsch",
},
]}
onChange={(e) => i18n.changeLanguage(e)}
/>
)}
</Form.Item>
<Form.Item
name="analytics"
valuePropName="checked"
label={t("userProfile.analytics")}
extra={t("userProfile.analyticsDescription")}
>
{requestState === RequestState.INIT ? (
<Skeleton.Button shape="round" active />
) : (
<Switch>{t("userProfile.analytics")}</Switch>
)}
</Form.Item>
</Form>
</Card>
</> </>
); );
} }
function YourProfile({
notificationApi,
globalRequestState,
setGlobalRequestState,
}) {
const { t, i18n } = useTranslation();
const [form] = Form.useForm();
const sideBarContext = useSideBarContext();
const delayTimeout = useRef();
const [requestData, setRequestData] = useState({});
const language = Form.useWatch("language", form);
const analyticsEnabled = Form.useWatch("analyticsEnabled", form);
const username = Form.useWatch("username", form);
const accountName = Form.useWatch("accountName", form);
const requestState = globalRequestState.yourProfile;
useEffect(() => {
myFetch({
url: "/user/profile/settings",
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((data) => {
setRequestData(data);
form.setFieldsValue({
language: data.language,
analyticsEnabled: data.analytics_enabled,
username: data.username,
accountName: data.account_name,
});
})
.catch(() => {
setGlobalRequestState({
...globalRequestState,
global: RequestState.FAILED,
});
});
}, []);
useEffect(() => {
if (
language === undefined ||
analyticsEnabled === undefined ||
username === undefined ||
accountName === undefined
)
return;
if (RequestState.INIT === requestState) {
setGlobalRequestState({
...globalRequestState,
global: RequestState.NOTHING,
yourProfile: RequestState.NOTHING,
});
return;
}
setGlobalRequestState({
...globalRequestState,
global: RequestState.REQUESTING,
});
if (delayTimeout.current) {
clearTimeout(delayTimeout.current);
}
delayTimeout.current = setTimeout(() => {
form
.validateFields()
.then(() => {
let body = {};
if (language !== requestData.language) {
body.language = language;
}
if (analyticsEnabled !== requestData.analytics_enabled) {
body.analyticsEnabled = analyticsEnabled;
}
if (username !== requestData.username) {
body.username = username;
}
if (accountName !== requestData.account_name) {
body.accountName = accountName;
}
if (Object.keys(body).length === 0) {
setGlobalRequestState({
...globalRequestState,
global: RequestState.NOTHING,
});
return;
}
myFetch({
url: "/user/profile/settings",
method: "POST",
body: body,
notificationApi: notificationApi,
t: t,
})
.then(() => {
setGlobalRequestState({
...globalRequestState,
global: RequestState.SUCCESS,
});
// Update requestData to the new values
setRequestData({
...requestData,
language: language,
analytics_enabled: analyticsEnabled,
username: username,
account_name: accountName,
});
sideBarContext.setUsername(username);
})
.catch(() => {
setGlobalRequestState({
...globalRequestState,
global: RequestState.FAILED,
});
});
})
.catch(() => {
showInputsInvalidNotification(notificationApi, t);
setGlobalRequestState({
...globalRequestState,
global: RequestState.NOTHING,
});
});
}, 500);
}, [language, analyticsEnabled, username, accountName]);
return (
<Form form={form} layout="vertical" requiredMark={false}>
<Form.Item name="language" label={t("userProfile.yourProfile.language")}>
{requestState === RequestState.INIT ? (
<Skeleton.Input active block></Skeleton.Input>
) : (
<Select
style={{ width: "100%" }}
options={[
{
value: "en",
label: "English",
},
{
value: "de",
label: "Deutsch",
},
]}
onChange={(e) => i18n.changeLanguage(e)}
/>
)}
</Form.Item>
<Form.Item
name="analyticsEnabled"
valuePropName="checked"
label={t("userProfile.yourProfile.analytics")}
extra={t("userProfile.yourProfile.analyticsDescription")}
>
{requestState === RequestState.INIT ? (
<Skeleton.Button shape="round" active />
) : (
<Switch>{t("userProfile.yourProfile.analytics")}</Switch>
)}
</Form.Item>
<MyUsernameFormInput showSkeleton={requestState === RequestState.INIT} />
<MyAccountNameFormInput
showSkeleton={requestState === RequestState.INIT}
hasFeedback
disableAccountNameCheck={requestData.account_name === accountName}
/>
</Form>
);
}
function ChangePassword({
notificationApi,
globalRequestState,
setGlobalRequestState,
setUserSession,
}) {
const { t } = useTranslation();
const [form] = Form.useForm();
const handlePasswordChange = () => {
form
.validateFields()
.then((values) => {
console.log(values);
setGlobalRequestState({
...globalRequestState,
global: RequestState.REQUESTING,
changePassword: RequestState.REQUESTING,
});
myFetch({
url: "/user/profile/password",
method: "POST",
body: {
currentPassword: EncodeStringToBase64(values.currentPassword),
newPassword: EncodeStringToBase64(values.newPassword),
},
notificationApi: notificationApi,
t: t,
})
.then(() => {
form.resetFields();
setGlobalRequestState({
...globalRequestState,
global: RequestState.SUCCESS,
changePassword: RequestState.NOTHING,
});
handleLogout({
setUserSession: setUserSession,
});
})
.catch((err) => {
setGlobalRequestState({
...globalRequestState,
global: RequestState.FAILED,
changePassword: RequestState.NOTHING,
});
if (err === 400) {
notificationApi["error"]({
message: t("userProfile.changePassword.request.400.title"),
description: t(
"userProfile.changePassword.request.400.description"
),
});
}
});
})
.catch(() => showInputsInvalidNotification(notificationApi, t));
};
return (
<Form form={form} layout="vertical" autoComplete="off" requiredMark={false}>
<MyPasswordFormInput
propsInput={{
id: "currentPassword",
}}
formItemName="currentPassword"
label={t("userProfile.changePassword.currentPassword")}
inputPlaceholder={t(
"userProfile.changePassword.currentPasswordPlaceholder"
)}
/>
<MyPasswordFormInput
propsInput={{
id: "newPassword",
}}
formItemName="newPassword"
label={t("userProfile.changePassword.newPassword")}
inputPlaceholder={t(
"userProfile.changePassword.newPasswordPlaceholder"
)}
/>
<MyPasswordFormInput
propsInput={{
id: "confirmNewPassword",
}}
formItemName="confirmNewPassword"
label={t("userProfile.changePassword.confirmNewPassword")}
inputPlaceholder={t(
"userProfile.changePassword.confirmNewPasswordPlaceholder"
)}
propsFormItem={{
dependencies: ["newPassword"],
}}
formItemRules={({ getFieldValue }) => ({
validator(_, value) {
if (!value || getFieldValue("newPassword") === value) {
return Promise.resolve();
}
return Promise.reject(
new Error(
t("userProfile.changePassword.confirmNewPasswordMismatch")
)
);
},
})}
/>
<Form.Item extra={t("userProfile.changePassword.buttonInfo")}>
<Space>
<Button
type="primary"
onClick={handlePasswordChange}
loading={
globalRequestState.changePassword === RequestState.REQUESTING
}
>
{t("userProfile.changePassword.button")}
</Button>
</Space>
</Form.Item>
</Form>
);
}
function YourSessions() {
useEffect(() => {
console.log("your sessions");
}, []);
return <h1>Test</h1>;
}
function DeleteAccount() {
return <div>Delete account, Password and feedback</div>;
}

View File

@ -246,3 +246,8 @@ export function showInputsInvalidNotification(notificationApi, t) {
description: t("common.request.inputsInvalid.description"), description: t("common.request.inputsInvalid.description"),
}); });
} }
export function handleLogout({ setUserSession }) {
setUserSession();
window.location.href = "/";
}