verification

master
alex 2024-02-10 13:52:20 +01:00
parent e21745125d
commit c2147f45f9
9 changed files with 213 additions and 8 deletions

View File

@ -378,5 +378,22 @@
"improveMinLengthRequired": "Bitte geben Sie mindestens {{minLength}} Zeichen ein"
}
}
},
"verification": {
"error": {
"title": "Verifizierung fehlgeschlagen",
"description": "Keine ausstehende Überprüfung gefunden"
},
"content": [
{
"requesting": {
"title": "E-Mail-Verifizierung ausstehend"
},
"Erfolg": {
"title": "E-Mail-Überprüfung erfolgreich",
"button": "Jetzt anmelden"
}
}
]
}
}

View File

@ -381,5 +381,22 @@
"improveMinLengthRequired": "Please enter at least {{minLength}} characters"
}
}
},
"verification": {
"error": {
"title": "Verification failed",
"description": "No pending verification found."
},
"content": [
{
"requesting": {
"title": "Email verification pending"
},
"success": {
"title": "Email verification successful",
"button": "Login now"
}
}
]
}
}

View File

@ -19,7 +19,10 @@ import StoresProvider from "./Contexts/StoresContext";
import { clarity } from "react-microsoft-clarity";
import { useTranslation } from "react-i18next";
import MyAppLogo from "./Components/MyAppLogo";
import { AuthenticationRoutes } from "./Components/AppRoutes";
import {
AuthenticationRoutes,
VerificationRoutes,
} from "./Components/AppRoutes";
export function Loading() {
const { t } = useTranslation();
@ -65,7 +68,16 @@ export function Loading() {
);
}
export default function App() {
export default function PreApp() {
// if the users comes from a verification email (e.g. when sign up or delete account)
if (window.location.pathname.startsWith("/verify/")) {
return <VerificationRoutes />;
}
return <App />;
}
export function App() {
const { _, i18n } = useTranslation();
const { userSession, setUserSession } = UseUserSession();

View File

@ -3,6 +3,7 @@ import { Constants, isDevelopmentEnv } from "../../utils";
import { lazy } from "react";
import { MySupsenseFallback } from "../MySupsenseFallback";
import { AuthenticationMethod } from "../../Pages/Authentication";
import Verification from "../../Pages/Verification";
// Lazy-loaded components
const Authentication = lazy(() => import("../../Pages/Authentication"));
@ -48,6 +49,30 @@ export function AuthenticationRoutes() {
);
}
export function VerificationRoutes() {
return (
<Routes>
<Route
path={`${Constants.ROUTE_PATHS.VERIFY}/:state/:emailVerificationId`}
element={
<MySupsenseFallback>
<Verification />
</MySupsenseFallback>
}
/>
<Route
path="*"
element={
<MySupsenseFallback>
<Verification />
</MySupsenseFallback>
}
/>
</Routes>
);
}
export function AppRoutes({ setUserSession }) {
return (
<Routes>

View File

@ -9,6 +9,7 @@ export default function MyModal({
onCancel,
footer = <MyModalOnlyCloseButtonFooter onCancel={onCancel} />,
title,
closable = true,
}) {
const screenBreakpoint = useBreakpoint();
@ -21,6 +22,7 @@ export default function MyModal({
footer={footer}
centered={screenBreakpoint.xs}
title={title}
closable={closable}
>
{children}
</Modal>

View File

@ -126,6 +126,7 @@ function Login({ notificationApi }) {
const [step, setStep] = useState(LoginStep.ACCOUNT_NAME);
const [isRequesting, setIsRequesting] = useState(false);
const recaptchaRef = useRef(null);
const recaptchaValueRef = useRef(null);
const [form] = Form.useForm();
@ -253,7 +254,7 @@ function Login({ notificationApi }) {
]}
>
<ReCAPTCHA
ref={recaptchaValueRef}
ref={recaptchaRef}
sitekey={process.env.REACT_APP_RECAPTCHA_SITE_KEY}
onChange={(value) => (recaptchaValueRef.current = value)}
/>
@ -346,7 +347,7 @@ function Login({ notificationApi }) {
.catch((errStatus) => {
showErrorNotification(errStatus);
setIsRequesting(false);
recaptchaValueRef.current.reset();
recaptchaRef.current.reset();
});
})
.catch(() => showInputsInvalidNotification(notificationApi, t));

View File

@ -3,7 +3,24 @@ import { useTranslation } from "react-i18next";
import { Link } from "react-router-dom";
import { Constants } from "../../utils";
export default function PageNotFound() {
export function ButtonBackHome({ useHref }) {
const { t } = useTranslation();
return (
<Button
type="primary"
onClick={() => {
if (useHref) {
window.location.href = Constants.DASHBOARD_ADDRESS;
}
}}
>
{t("pageNotFound.buttonBackHome")}
</Button>
);
}
export default function PageNotFound({ useHref = false }) {
const { t } = useTranslation();
return (
@ -12,9 +29,13 @@ export default function PageNotFound() {
title={t("pageNotFound.title")}
subTitle={t("pageNotFound.subTitle")}
extra={
<Link to={Constants.ROUTE_PATHS.OVERVIEW}>
<Button type="primary">{t("pageNotFound.buttonBackHome")}</Button>
</Link>
useHref ? (
<ButtonBackHome useHref={useHref} />
) : (
<Link to={Constants.DASHBOARD_ADDRESS}>
{<ButtonBackHome useHref={useHref} />}
</Link>
)
}
/>
);

View File

@ -0,0 +1,104 @@
import { useParams } from "react-router-dom";
import MyModal from "../../Components/MyModal";
import { Button, Flex, 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 { useTranslation } from "react-i18next";
import { RequestState } from "../../Components/MyRequestStateItem";
export default function Verification() {
const { state, emailVerificationId } = useParams();
const { t } = useTranslation();
const [notificationApi, notificationContextHolder] =
notification.useNotification();
const [isRequesting, setIsRequesting] = useState(RequestState.REQUESTING);
const content = [
{
requesting: {
title: t("verification.content.0.requesting.title"),
},
sucess: {
title: t("verification.content.0.success.title"),
extra: (
<Button
type="primary"
onClick={() => {
window.location.href = `${Constants.DASHBOARD_ADDRESS}${Constants.ROUTE_PATHS.AUTHENTICATION.LOGIN}`;
}}
>
{t("verification.content.0.success.button")}
</Button>
),
},
},
];
const parsedState = isNaN(state) ? 0 : parseInt(state);
let elementContent;
if (
state === undefined ||
emailVerificationId === undefined ||
state > content.length - 1
) {
elementContent = <PageNotFound useHref />;
} else if (isRequesting === RequestState.REQUESTING) {
elementContent = (
<Result
title={content[parsedState].requesting.title}
extra={<Spin size="large" />}
/>
);
} else if (isRequesting === RequestState.SUCCESS) {
elementContent = (
<Result
status="success"
title={content[parsedState].sucess.title}
extra={content[parsedState].sucess.extra}
/>
);
} else if (isRequesting === RequestState.FAILED) {
elementContent = (
<Result
status="error"
title={t("verification.error.title")}
subTitle={t("verification.error.description")}
extra={<ButtonBackHome useHref />}
/>
);
} else {
elementContent = <PageNotFound useHref />;
}
useEffect(() => {
if (emailVerificationId === undefined || state === undefined) {
setIsRequesting(false);
return;
}
myFetch({
url: `/user/verify/${state}/${emailVerificationId}`,
notificationApi: notificationApi,
t: t,
})
.then(() => setIsRequesting(RequestState.SUCCESS))
.catch(() => setIsRequesting(RequestState.FAILED));
}, []);
return (
<MyModal isOpen={true} footer={null} closable={false}>
{notificationContextHolder}
<Flex justify="center">
<MyAppLogo height={100} />
</Flex>
{elementContent}
</MyModal>
);
}

View File

@ -7,22 +7,26 @@ import { v4 as uuidv4 } from "uuid";
*/
//const wssProtocol = window.location.protocol === "https:" ? "wss://" : "ws://";
let dashboardAddress = "";
let apiAddress = "";
// let staticContentAddress = "";
// let wsAddress = "";
if (window.location.hostname === "localhost" && window.location.port === "") {
// for docker container testing on localhost
dashboardAddress = "http://localhost/";
apiAddress = "http://localhost/api/v1";
// staticContentAddress = "http://localhost/api/";
// wsAddress = "ws://localhost/ws";
} else if (window.location.hostname === "localhost") {
// programming on localhost
dashboardAddress = `http://localhost:${window.location.port}/`;
apiAddress = "http://localhost:50128/api/v1";
// staticContentAddress = "http://localhost:50050/";
// wsAddress = "ws://localhost:50050/ws";
} else {
// production
dashboardAddress = `${window.location.protocol}//${window.location.hostname}/`;
apiAddress = `${window.location.protocol}//${window.location.hostname}/api/v1`;
//staticContentAddress = `${window.location.protocol}//${window.location.hostname}/api/`;
// wsAddress = `${wssProtocol}${window.location.hostname}/ws`;
@ -30,6 +34,7 @@ if (window.location.hostname === "localhost" && window.location.port === "") {
export const Constants = {
TEXT_EMPTY_PLACEHOLDER: "-/-",
DASHBOARD_ADDRESS: dashboardAddress,
API_ADDRESS: apiAddress,
//STATIC_CONTENT_ADDRESS: staticContentAddress,
// WS_ADDRESS: wsAddress,
@ -42,6 +47,7 @@ export const Constants = {
LOGIN: "/login",
SIGN_UP: "/signup",
},
VERIFY: "/verify",
OVERVIEW: "/",
STORE: {
// schema: /store/:storeId/:subPage