save user settings

master
alex 2024-01-26 16:03:40 +01:00
parent c958e0cb2f
commit c2b17ddae3
9 changed files with 156 additions and 57 deletions

6
package-lock.json generated
View File

@ -22,6 +22,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.0.1", "react-i18next": "^13.0.1",
"react-microsoft-clarity": "^1.2.0",
"react-qr-scanner": "^1.0.0-alpha.11", "react-qr-scanner": "^1.0.0-alpha.11",
"react-router-dom": "^6.10.0", "react-router-dom": "^6.10.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",
@ -18687,6 +18688,11 @@
"resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz",
"integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==" "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w=="
}, },
"node_modules/react-microsoft-clarity": {
"version": "1.2.0",
"resolved": "https://registry.npmjs.org/react-microsoft-clarity/-/react-microsoft-clarity-1.2.0.tgz",
"integrity": "sha512-a1bsJR1uN1daWq3cBc7NheEGPXrotMRE0oFeRio7kXvHawzQfqu5iX9BoYFF9zRUL0dn+Mz57h1fNlcv3pqXEg=="
},
"node_modules/react-qr-scanner": { "node_modules/react-qr-scanner": {
"version": "1.0.0-alpha.11", "version": "1.0.0-alpha.11",
"resolved": "https://registry.npmjs.org/react-qr-scanner/-/react-qr-scanner-1.0.0-alpha.11.tgz", "resolved": "https://registry.npmjs.org/react-qr-scanner/-/react-qr-scanner-1.0.0-alpha.11.tgz",

View File

@ -17,6 +17,7 @@
"react": "^18.2.0", "react": "^18.2.0",
"react-dom": "^18.2.0", "react-dom": "^18.2.0",
"react-i18next": "^13.0.1", "react-i18next": "^13.0.1",
"react-microsoft-clarity": "^1.2.0",
"react-qr-scanner": "^1.0.0-alpha.11", "react-qr-scanner": "^1.0.0-alpha.11",
"react-router-dom": "^6.10.0", "react-router-dom": "^6.10.0",
"react-scripts": "5.0.1", "react-scripts": "5.0.1",

View File

@ -29,19 +29,4 @@
<noscript>You need to enable JavaScript to run this app.</noscript> <noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div> <div id="root"></div>
</body> </body>
<script type="text/javascript">
(function (c, l, a, r, i, t, y) {
c[a] =
c[a] ||
function () {
(c[a].q = c[a].q || []).push(arguments);
};
t = l.createElement(r);
t.async = 1;
t.src = "https://www.clarity.ms/tag/" + i;
y = l.getElementsByTagName(r)[0];
y.parentNode.insertBefore(t, y);
})(window, document, "clarity", "script", "kr0pale8uy");
</script>
</html> </html>

View File

@ -229,6 +229,8 @@
}, },
"userProfile": { "userProfile": {
"title": "Ihr Profil", "title": "Ihr Profil",
"language": "Sprache" "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."
} }
} }

View File

@ -232,6 +232,8 @@
}, },
"userProfile": { "userProfile": {
"title": "Your profile", "title": "Your profile",
"language": "Language" "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."
} }
} }

View File

@ -2,7 +2,7 @@ import "antd/dist/reset.css";
import "./App.css"; import "./App.css";
import Login from "./Pages/Login"; import Login from "./Pages/Login";
import { Layout, Spin } from "antd"; import { Layout, Spin } from "antd";
import { UseUserSession, myFetch } from "./utils"; import { Constants, UseUserSession, myFetch } from "./utils";
import DashboardLayout from "./Components/DashboardLayout"; import DashboardLayout from "./Components/DashboardLayout";
import SideBarProvider from "./Contexts/SideBarContext"; import SideBarProvider from "./Contexts/SideBarContext";
import { AppProvider } from "./Contexts/AppContext"; import { AppProvider } from "./Contexts/AppContext";
@ -11,6 +11,7 @@ import { UsersProvider } from "./Contexts/UsersContext";
import HeaderProvider from "./Contexts/HeaderContext"; import HeaderProvider from "./Contexts/HeaderContext";
import { useEffect, useState } from "react"; import { useEffect, useState } from "react";
import StoresProvider from "./Contexts/StoresContext"; import StoresProvider from "./Contexts/StoresContext";
import { clarity } from "react-microsoft-clarity";
export default function App() { export default function App() {
/*const [notificationApi, notificationContextHolder] = /*const [notificationApi, notificationContextHolder] =
@ -30,6 +31,10 @@ export default function App() {
}) })
.then((data) => { .then((data) => {
setAppUserData(data); setAppUserData(data);
if (data.user.analytics_enabled) {
clarity.init(Constants.CLARITY_PROJECT_ID);
}
}) })
.catch(() => { .catch(() => {
setUserSession(); setUserSession();

View File

@ -19,7 +19,7 @@ export default function StoreSettings() {
const [isRequesting, setIsRequesting] = useState(true); const [isRequesting, setIsRequesting] = useState(true);
const [requestState, setRequestState] = useState(RequestState.INIT); const [requestState, setRequestState] = useState(RequestState.INIT);
const [storeData, setStoreData] = useState({}); //const [storeData, setStoreData] = useState({});
const delayTimeout = useRef(); const delayTimeout = useRef();
const companyName = Form.useWatch("companyName", form); const companyName = Form.useWatch("companyName", form);
@ -35,7 +35,7 @@ export default function StoreSettings() {
t: t, t: t,
}) })
.then((data) => { .then((data) => {
setStoreData(data.store); // setStoreData(data.store);
form.setFieldsValue({ form.setFieldsValue({
companyName: data.store.name, companyName: data.store.name,

View File

@ -1,14 +1,86 @@
import { Button, Card, Select, Typography, notification } from "antd"; import {
Button,
Card,
Checkbox,
Form,
Select,
Skeleton,
Space,
Switch,
Typography,
notification,
} from "antd";
import { myFetch } from "../../utils"; import { myFetch } from "../../utils";
import { useTranslation } from "react-i18next"; import { useTranslation } from "react-i18next";
import { useState } from "react"; import { useEffect, useRef, useState } from "react";
import {
RequestState,
RequestStateItem,
} from "../../Components/MyRequestStateItem";
export default function UserProfile({ setUserSession }) { export default function UserProfile({ setUserSession }) {
const { t, i18n } = useTranslation(); const { t, i18n } = useTranslation();
const [notificationApi, notificationContextHolder] = const [notificationApi, notificationContextHolder] =
notification.useNotification(); notification.useNotification();
const [form] = Form.useForm();
const [requestState, setRequestState] = useState(RequestState.INIT);
const [isRequestingLogout, setIsRequestingLogout] = useState(false); const [isRequestingLogout, setIsRequestingLogout] = useState(false);
const delayTimeout = useRef();
const language = Form.useWatch("language", form);
const analytics = Form.useWatch("analytics", form);
useEffect(() => {
myFetch({
url: "/user/profile/settings",
method: "GET",
notificationApi: notificationApi,
t: t,
})
.then((data) => {
form.setFieldsValue({
language: data.language,
analytics: data.analytics_enabled,
});
})
.catch((errStatus) => {
console.log(errStatus);
});
}, []);
useEffect(() => {
if (language === undefined || analytics === undefined) return;
if (RequestState.INIT === requestState) {
setRequestState(RequestState.NOTHING);
return;
}
setRequestState(RequestState.REQUESTING);
if (delayTimeout.current) {
clearTimeout(delayTimeout.current);
}
delayTimeout.current = setTimeout(() => {
myFetch({
url: "/user/profile/settings",
method: "POST",
body: {
language: language,
analyticsEnabled: analytics,
},
notificationApi: notificationApi,
t: t,
})
.then(() => setRequestState(RequestState.SUCCESS))
.catch((errStatus) => {
console.log(errStatus);
setRequestState(RequestState.FAILED);
});
}, 500);
}, [language, analytics]);
return ( return (
<> <>
@ -17,46 +89,71 @@ export default function UserProfile({ setUserSession }) {
<Card <Card
title={t("userProfile.title")} title={t("userProfile.title")}
extra={ extra={
<Button <Space>
type="primary" <RequestStateItem
loading={isRequestingLogout} state={requestState}
onClick={() => { setRequestState={setRequestState}
setIsRequestingLogout(true); />
myFetch({ <Button
url: "/user/auth/logout", type="primary"
method: "DELETE", loading={isRequestingLogout}
notificationApi: notificationApi, onClick={() => {
t: t, setIsRequestingLogout(true);
})
.then(() => { myFetch({
setUserSession(); url: "/user/auth/logout",
window.location.href = "/"; method: "DELETE",
notificationApi: notificationApi,
t: t,
}) })
.catch(() => setIsRequestingLogout(false)); .then(() => {
}} setUserSession();
> window.location.href = "/";
{t("common.button.logout")} })
</Button> .catch(() => setIsRequestingLogout(false));
}}
>
{t("common.button.logout")}
</Button>
</Space>
} }
> >
<Typography.Paragraph>{t("userProfile.language")}</Typography.Paragraph> <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>
<Select <Form.Item
style={{ width: "100%" }} name="analytics"
defaultValue={i18n.language} valuePropName="checked"
options={[ label={t("userProfile.analytics")}
{ extra={t("userProfile.analyticsDescription")}
value: "en", >
label: "English", {requestState === RequestState.INIT ? (
}, <Skeleton.Button shape="round" active />
{ ) : (
value: "de", <Switch>{t("userProfile.analytics")}</Switch>
label: "Deutsch", )}
}, </Form.Item>
]} </Form>
onChange={(e) => i18n.changeLanguage(e)}
/>
</Card> </Card>
</> </>
); );

View File

@ -70,6 +70,7 @@ export const Constants = {
MAX_CALENDAR_EARLIEST_BOOKING_TIME: 60 * 24, // 24 hours in minutes MAX_CALENDAR_EARLIEST_BOOKING_TIME: 60 * 24, // 24 hours in minutes
}, },
DELAY_ACCOUNT_NAME_CHECK: 250, DELAY_ACCOUNT_NAME_CHECK: 250,
CLARITY_PROJECT_ID: "kr0pale8uy",
}; };
export const AppStyle = { export const AppStyle = {