From c2147f45f9c9f9d01076749df8cd62d8988f7280 Mon Sep 17 00:00:00 2001 From: alex Date: Sat, 10 Feb 2024 13:52:20 +0100 Subject: [PATCH] verification --- public/locales/de/translation.json | 17 +++++ public/locales/en/translation.json | 17 +++++ src/App.js | 16 ++++- src/Components/AppRoutes/index.js | 25 +++++++ src/Components/MyModal/index.js | 2 + src/Pages/Authentication/index.js | 5 +- src/Pages/PageNotFound/index.js | 29 ++++++-- src/Pages/Verification/index.js | 104 +++++++++++++++++++++++++++++ src/utils.js | 6 ++ 9 files changed, 213 insertions(+), 8 deletions(-) create mode 100644 src/Pages/Verification/index.js diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 6a8d083..ab19a4a 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -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" + } + } + ] } } diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 762bfda..c4f2eac 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -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" + } + } + ] } } diff --git a/src/App.js b/src/App.js index f94393f..2b189fa 100644 --- a/src/App.js +++ b/src/App.js @@ -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 ; + } + + return ; +} + +export function App() { const { _, i18n } = useTranslation(); const { userSession, setUserSession } = UseUserSession(); diff --git a/src/Components/AppRoutes/index.js b/src/Components/AppRoutes/index.js index 4d8c165..3306ab5 100644 --- a/src/Components/AppRoutes/index.js +++ b/src/Components/AppRoutes/index.js @@ -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 ( + + + + + } + /> + + + + + } + /> + + ); +} + export function AppRoutes({ setUserSession }) { return ( diff --git a/src/Components/MyModal/index.js b/src/Components/MyModal/index.js index e1f0343..12fab9a 100644 --- a/src/Components/MyModal/index.js +++ b/src/Components/MyModal/index.js @@ -9,6 +9,7 @@ export default function MyModal({ onCancel, footer = , title, + closable = true, }) { const screenBreakpoint = useBreakpoint(); @@ -21,6 +22,7 @@ export default function MyModal({ footer={footer} centered={screenBreakpoint.xs} title={title} + closable={closable} > {children} diff --git a/src/Pages/Authentication/index.js b/src/Pages/Authentication/index.js index 355e455..45e7926 100644 --- a/src/Pages/Authentication/index.js +++ b/src/Pages/Authentication/index.js @@ -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 }) { ]} > (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)); diff --git a/src/Pages/PageNotFound/index.js b/src/Pages/PageNotFound/index.js index 285016b..7866ae7 100644 --- a/src/Pages/PageNotFound/index.js +++ b/src/Pages/PageNotFound/index.js @@ -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 ( + + ); +} + +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={ - - - + useHref ? ( + + ) : ( + + {} + + ) } /> ); diff --git a/src/Pages/Verification/index.js b/src/Pages/Verification/index.js new file mode 100644 index 0000000..95e239d --- /dev/null +++ b/src/Pages/Verification/index.js @@ -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: ( + + ), + }, + }, + ]; + + const parsedState = isNaN(state) ? 0 : parseInt(state); + + let elementContent; + + if ( + state === undefined || + emailVerificationId === undefined || + state > content.length - 1 + ) { + elementContent = ; + } else if (isRequesting === RequestState.REQUESTING) { + elementContent = ( + } + /> + ); + } else if (isRequesting === RequestState.SUCCESS) { + elementContent = ( + + ); + } else if (isRequesting === RequestState.FAILED) { + elementContent = ( + } + /> + ); + } else { + elementContent = ; + } + + 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 ( + + {notificationContextHolder} + + + + + + {elementContent} + + ); +} diff --git a/src/utils.js b/src/utils.js index 2d101dd..0d764ea 100644 --- a/src/utils.js +++ b/src/utils.js @@ -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