diff --git a/public/locales/de/translation.json b/public/locales/de/translation.json index 3017d40..6a8d083 100644 --- a/public/locales/de/translation.json +++ b/public/locales/de/translation.json @@ -30,12 +30,9 @@ "usernamePlaceholder": "Geben Sie Ihren Anzeigename ein", "usernamePlaceholderThirdPerson": "Geben Sie den Anzeigename ein", "usernameInfo": "Der Anzeigename wird verwendet, um Sie im Dashboard anzuzeigen.", - "accountName": "Benutzername", - "accountNamePlaceholder": "Geben Sie Ihren Benutzernamen ein", - "accountNamePlaceholderThirdPerson": "Geben Sie den Benutzernamen ein", - "accountNameInfo": "Der Benutzername wird verwendet, um sich anzumelden.", "email": "E-Mail", "emailPlaceholder": "Geben Sie Ihre E-Mail ein", + "emailPlaceholderThirdPerson": "Geben Sie die E-Mail ein", "password": "Passwort", "passwordPlaceholder": "Geben Sie Ihr Passwort ein", "noDataFound": "Keine Einträge gefunden", @@ -53,11 +50,10 @@ "inputRules": { "usernameRequired": "Anzeigename ist erforderlich", "usernameMinLength": "Anzeigename muss mindestens {{minLength}} Zeichen lang sein", - "accountNameRequired": "Benutzername ist erforderlich", - "accountNameMinLength": "Benutzername muss mindestens {{minLength}} Zeichen lang sein", - "accountNameTaken": "Benutzername ist bereits vergeben", + "emailMinLength": "E-Mail muss mindestens {{minLength}} Zeichen lang sein", "emailRequired": "E-Mail ist erforderlich", "emailInvalid": "E-Mail ist ungültig", + "emailTaken": "E-Mail wird bereits verwendet", "passwordRequired": "Passwort ist erforderlich", "passwordMinLength": "Passwort muss mindestens {{minLength}} Zeichen lang sein", "calendarMaxFutureBookingDaysRequired": "Maximaler Buchungszeitraum ist erforderlich", @@ -170,7 +166,11 @@ }, "signUp": { "button": "Registrieren", - "alreadyHaveAccount": "Sie haben bereits ein Konto?" + "alreadyHaveAccount": "Sie haben bereits ein Konto?", + "pendingEmailVerification": { + "title": "E-Mail-Verifizierung ausstehend", + "description": "Bitte überprüfen Sie Ihr E-Mail-Postfach und klicken Sie auf den Link in der E-Mail, um Ihre E-Mail-Adresse zu verifizieren." + } }, "request": { "400": { diff --git a/public/locales/en/translation.json b/public/locales/en/translation.json index 7a5ea59..762bfda 100644 --- a/public/locales/en/translation.json +++ b/public/locales/en/translation.json @@ -30,12 +30,9 @@ "usernamePlaceholder": "Enter your username", "usernameInfo": "The display name is used to display you in the dashboard.", "usernamePlaceholderThirdPerson": "Enter the username", - "accountName": "Account name", - "accountNamePlaceholder": "Enter your account name", - "accountNamePlaceholderThirdPerson": "Enter the account name", - "accountNameInfo": "The account name is used to log in.", "email": "Email", "emailPlaceholder": "Enter your email", + "emailPlaceholderThirdPerson": "Enter the email", "password": "Password", "passwordPlaceholder": "Enter your password", "noDataFound": "No data found", @@ -53,11 +50,10 @@ "inputRules": { "usernameRequired": "Please enter your username", "usernameMinLength": "Username must be at least {{minLength}} characters", - "accountNameRequired": "Please enter your account name", - "accountNameMinLength": "Account name must be at least {{minLength}} characters", - "accountNameTaken": "Account name already exists", + "emailMinLength": "Email must be at least {{minLength}} characters", "emailRequired": "Please enter your email", "emailInvalid": "Please enter a valid email", + "emailTaken": "This email is already taken", "passwordRequired": "Please enter your password", "passwordMinLength": "Password must be at least {{minLength}} characters", "calendarMaxFutureBookingDaysRequired": "Please enter the max. future booking days", @@ -171,6 +167,10 @@ "signUp": { "button": "Sign up", "alreadyHaveAccount": "Already have an account?", + "pendingEmailVerification": { + "title": "Email verification pending", + "description": "Please check your email inbox and click on the link in the e-mail to verify your e-mail address." + }, "request": { "400": { "title": "Sign up failed", @@ -332,13 +332,7 @@ "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." - } - } + "buttonInfo": "After changing your password, you will be logged out and have to log in again." }, "yourSessions": { "cardTitle": "Your sessions", diff --git a/src/Components/MyFormInputs/index.js b/src/Components/MyFormInputs/index.js index bbeb4ae..b1a09d4 100644 --- a/src/Components/MyFormInputs/index.js +++ b/src/Components/MyFormInputs/index.js @@ -1,5 +1,5 @@ import { Form, Input, InputNumber, Skeleton } from "antd"; -import { Constants, myFetch } from "../../utils"; +import { Constants, isEmailValid, myFetch } from "../../utils"; import { useRef } from "react"; import { useTranslation } from "react-i18next"; @@ -36,48 +36,6 @@ export function MyUsernameFormInput({ ); } -export function MyAccountNameFormInput({ - propsFormItem, - propsInput, - disableAccountNameCheck, - hasFeedback, - showSkeleton, - thirdPerson, -}) { - const { t } = useTranslation(); - - return ( - - ); -} - export function MyPasswordFormInput({ propsFormItem, propsInput, @@ -110,6 +68,52 @@ export function MyPasswordFormInput({ ); } +export function MyEmailFormInput({ + propsFormItem, + propsInput, + disableEmailCheck, + hasFeedback, + showSkeleton, + thirdPerson, +}) { + const { t } = useTranslation(); + + return ( + + ); +} + +/* export function MyEmailFormInput() { const { t } = useTranslation(); @@ -130,7 +134,7 @@ export function MyEmailFormInput() { ]} /> ); -} +} */ export function MyCalendarMaxFutureBookingDaysFormInput({ formItemName }) { const { t } = useTranslation(); @@ -214,6 +218,7 @@ export function MyAvailableCheckFormInput({ fetchDelay, hasFeedback, showSkeleton, + formItemRules, }) { const delayTimeout = useRef(); @@ -238,9 +243,14 @@ export function MyAvailableCheckFormInput({ inputType={inputType} showSkeleton={showSkeleton} formItemRules={[ + ...formItemRules, () => ({ validator(_, value) { - if (!value || !isValid(value)) { + if ( + !value || + !isValid(value) || + (inputType === "email" && isEmailValid(value) === false) + ) { return Promise.reject(""); } @@ -256,7 +266,7 @@ export function MyAvailableCheckFormInput({ delayTimeout.current = setTimeout(() => { let body = {}; - body[fetchParameter] = value; // like accountName: value + body[fetchParameter] = value; // like email: value myFetch({ url: fetchUrl, diff --git a/src/Pages/Authentication/index.js b/src/Pages/Authentication/index.js index b30cf93..1849614 100644 --- a/src/Pages/Authentication/index.js +++ b/src/Pages/Authentication/index.js @@ -19,8 +19,8 @@ import { } from "../../utils"; import { useEffect, useRef, useState } from "react"; import { - MyAccountNameFormInput, MyCompanyNameFormInput, + MyEmailFormInput, MyPasswordFormInput, MyUsernameFormInput, } from "../../Components/MyFormInputs"; @@ -115,6 +115,7 @@ const LoginStep = { INIT_LOGIN: 4, BANNED: 5, _BACK_TO_PASSWORD: 6, // needed for going back from pending deletion to password step as the useEffect for the account name is triggered and would set the step to account name + PENDING_EMAIL_VERIFICATION: 7, }; // First step: account name -> get state of account by backend @@ -123,13 +124,13 @@ function Login({ notificationApi }) { const { t } = useTranslation(); const navigate = useNavigate(); - const [isRequesting, setIsRequesting] = useState(false); const [step, setStep] = useState(LoginStep.ACCOUNT_NAME); + const [isRequesting, setIsRequesting] = useState(false); const recaptchaValueRef = useRef(null); const [form] = Form.useForm(); - const accountName = Form.useWatch("accountName", form); + const email = Form.useWatch("email", form); const showErrorNotification = (errStatus) => { if (errStatus === 400) { @@ -141,7 +142,7 @@ function Login({ notificationApi }) { }; useEffect(() => { - if (!accountName) return; + if (!email) return; // triggered if step was set to pending deletion and the user wants to reactivate the account if (step === LoginStep._BACK_TO_PASSWORD) { @@ -153,7 +154,7 @@ function Login({ notificationApi }) { if (step === LoginStep.PASSWORD || step === LoginStep.INIT_LOGIN) { setStep(LoginStep.ACCOUNT_NAME); } - }, [accountName]); + }, [email]); if (step === LoginStep.PENDING_DELETION) { return ( @@ -170,10 +171,7 @@ function Login({ notificationApi }) { , @@ -212,6 +210,10 @@ function Login({ notificationApi }) { ); } + if (step === LoginStep.PENDING_EMAIL_VERIFICATION) { + return ; + } + return (
)} - +
{ - setUserSessionToLocalStorage(data.XAuthorization); - window.location.href = "/"; - }) + .then(() => setStep(SignUpStep.PENDING_EMAIL_VERIFICATION)) .catch((errStatus) => { showErrorNotification(errStatus); setIsRequesting(false); }); }) - .catch((err) => { - console.log(err); + .catch(() => { showInputsInvalidNotification(notificationApi, t); + setIsRequesting(false); }); }; + if (step === SignUpStep.PENDING_EMAIL_VERIFICATION) { + return ; + } + return ( - + @@ -489,3 +495,15 @@ function SignUp({ notificationApi }) { ); } + +function PendingEmailVerification() { + const { t } = useTranslation(); + + return ( + + ); +} diff --git a/src/Pages/Store/Employees/index.js b/src/Pages/Store/Employees/index.js index 9001b9a..9a2c083 100644 --- a/src/Pages/Store/Employees/index.js +++ b/src/Pages/Store/Employees/index.js @@ -21,9 +21,9 @@ import { import { useTranslation } from "react-i18next"; import MyTable from "../../../Components/MyTable"; import { - MyAccountNameFormInput, MyCalendarMaxFutureBookingDaysFormInput, MyCalendarMinEarliestBookingTimeFormInput, + MyEmailFormInput, MyPasswordFormInput, MyUsernameFormInput, } from "../../../Components/MyFormInputs"; @@ -56,9 +56,9 @@ export default function StoreEmployees() { const getTableColumns = () => { return [ { - title: t("common.accountName"), - dataIndex: "account_name", - key: "account_name", + title: t("common.email"), + dataIndex: "email", + key: "email", }, { title: t("common.username"), @@ -124,7 +124,7 @@ export default function StoreEmployees() { return requestData.employees.map((employee) => { return { key: employee.user_id, - account_name: employee.account_name, + email: employee.email, username: employee.username, calendarMaxFutureBookingDays: employee.calendar_max_future_booking_days || @@ -227,7 +227,7 @@ function ModalAddEditEmployee({ if (modalOptions.mode === "edit") { form.setFieldsValue({ username: modalOptions.selectedEmployee.username, - accountName: modalOptions.selectedEmployee.account_name, + email: modalOptions.selectedEmployee.email, calendarMaxFutureBookingDays: modalOptions.selectedEmployee.calendarMaxFutureBookingDays || storeSettings.calendar_max_future_booking_days, @@ -283,7 +283,7 @@ function ModalAddEditEmployee({ let body = { storeId: storeId, username: values.username, - accountName: values.accountName, + email: values.email, language: i18n.language, passwordSetOnInitLogging: values.checkboxSetPasswordOnLogging, @@ -343,7 +343,7 @@ function ModalAddEditEmployee({ onSave={() => { // only validate if something has changed const formUsername = form.getFieldValue("username"); - const formAccountName = form.getFieldValue("accountName"); + const formEmail = form.getFieldValue("email"); const formCalendarMaxFutureBookingDays = form.getFieldValue( "calendarMaxFutureBookingDays" ); @@ -353,8 +353,7 @@ function ModalAddEditEmployee({ if ( formUsername === modalOptions.selectedEmployee.username && - formAccountName === - modalOptions.selectedEmployee.account_name && + formEmail === modalOptions.selectedEmployee.email && formCalendarMaxFutureBookingDays === modalOptions.selectedEmployee.maxFutureBookingDays && formCalendarMinEarliestBookingTime === @@ -374,11 +373,9 @@ function ModalAddEditEmployee({ body.username = formUsername; } - if ( - formAccountName !== modalOptions.selectedEmployee.account_name - ) { - validateFields.push("accountName"); - body.accountName = formAccountName; + if (formEmail !== modalOptions.selectedEmployee.email) { + validateFields.push("email"); + body.email = formEmail; } if ( @@ -441,7 +438,7 @@ function ModalAddEditEmployee({ > - + {modalOptions.mode === "add" && ( <> diff --git a/src/Pages/UserProfile/index.js b/src/Pages/UserProfile/index.js index 0d98819..fb20263 100644 --- a/src/Pages/UserProfile/index.js +++ b/src/Pages/UserProfile/index.js @@ -32,7 +32,6 @@ import { } from "../../Components/MyRequestStateItem"; import { LogoutOutlined } from "@ant-design/icons"; import { - MyAccountNameFormInput, MyEmailFormInput, MyFormInput, MyPasswordFormInput, @@ -173,7 +172,7 @@ function YourProfile({ 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 email = Form.useWatch("email", form); const requestState = globalRequestState.yourProfile; @@ -191,7 +190,7 @@ function YourProfile({ language: data.language, analyticsEnabled: data.analytics_enabled, username: data.username, - accountName: data.account_name, + email: data.email, }); }) .catch(() => { @@ -207,7 +206,7 @@ function YourProfile({ language === undefined || analyticsEnabled === undefined || username === undefined || - accountName === undefined + email === undefined ) return; @@ -247,8 +246,8 @@ function YourProfile({ body.username = username; } - if (accountName !== requestData.account_name) { - body.accountName = accountName; + if (email !== requestData.email) { + body.email = email; } if (Object.keys(body).length === 0) { @@ -278,7 +277,7 @@ function YourProfile({ language: language, analytics_enabled: analyticsEnabled, username: username, - account_name: accountName, + email: email, }); sideBarContext.setUsername(username); @@ -299,7 +298,7 @@ function YourProfile({ }); }); }, 500); - }, [language, analyticsEnabled, username, accountName]); + }, [language, analyticsEnabled, username, email]); return ( @@ -345,10 +344,10 @@ function YourProfile({ showSkeleton={requestState === RequestState.INIT} /> - @@ -408,12 +407,7 @@ function ChangePassword({ }); if (err === 400) { - notificationApi["error"]({ - message: t("userProfile.changePassword.request.400.title"), - description: t( - "userProfile.changePassword.request.400.description" - ), - }); + showPasswordIncorrectNotification(notificationApi, t); } }); }) diff --git a/src/utils.js b/src/utils.js index be79aaa..2d101dd 100644 --- a/src/utils.js +++ b/src/utils.js @@ -60,8 +60,11 @@ export const Constants = { GLOBALS: { MIN_USERNAME_LENGTH: 3, MAX_USERNAME_LENGTH: 20, - MIN_ACCOUNT_NAME_LENGTH: 3, - MAX_ACCOUNT_NAME_LENGTH: 20, + //MIN_ACCOUNT_NAME_LENGTH: 3, + //MAX_ACCOUNT_NAME_LENGTH: 20, + MIN_EMAIL_LENGTH: 3, + MAX_EMAIL_LENGTH: 64, + EMAIL_REGEX: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,4}$/, MIN_PASSWORD_LENGTH: 8, MAX_PASSWORD_LENGTH: 64, MIN_STORE_SERVICE_NAME_LENGTH: 3, @@ -82,7 +85,7 @@ export const Constants = { MIN_COMPANY_NAME_LENGTH: 3, MAX_COMPANY_NAME_LENGTH: 64, }, - DELAY_ACCOUNT_NAME_CHECK: 250, + DELAY_EMAIL_CHECK: 250, CLARITY_PROJECT_ID: process.env.REACT_APP_CLARITY_PROJECT_ID, ACCOUNT_DELETED_AFTER_DAYS: 30, ACCOUNT_STATE: { @@ -90,10 +93,15 @@ export const Constants = { PENDING_DELETION: 1, INIT_LOGGING: 2, BANNED: 3, + PENDING_EMAIL_VERIFICATION: 4, }, SUPPORT_EMAIL: process.env.REACT_APP_SUPPORT_EMAIL, }; +export function isEmailValid(email) { + return Constants.GLOBALS.EMAIL_REGEX.test(email); +} + export const AppStyle = { app: { margin: 12,