change account name to email

master
alex 2024-02-10 07:12:08 +01:00
parent 816222af20
commit f8fc680f3d
7 changed files with 151 additions and 130 deletions

View File

@ -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": {

View File

@ -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",

View File

@ -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 (
<MyAvailableCheckFormInput
propsFormItem={propsFormItem}
propsInput={propsInput}
hasFeedback={hasFeedback}
showSkeleton={showSkeleton}
formItemName="accountName"
minLength={Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH}
maxLength={Constants.GLOBALS.MAX_ACCOUNT_NAME_LENGTH}
label={t("common.accountName")}
ruleMessageValueRequired={t("common.inputRules.accountNameRequired")}
ruleMessageValueMinLengthRequired={t(
"common.inputRules.accountNameMinLength",
{
minLength: Constants.GLOBALS.MIN_ACCOUNT_NAME_LENGTH,
}
)}
ruleMessageValueNotAvailable={t("common.inputRules.accountNameTaken")}
inputPlaceholder={
thirdPerson
? t("common.accountNamePlaceholderThirdPerson")
: t("common.accountNamePlaceholder")
}
inputType="text"
disableAvailableCheck={disableAccountNameCheck}
fetchUrl="/user/auth/check/accountname"
fetchParameter="accountName"
fetchDelay={Constants.DELAY_ACCOUNT_NAME_CHECK}
/>
);
}
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 (
<MyAvailableCheckFormInput
propsFormItem={propsFormItem}
propsInput={propsInput}
hasFeedback={hasFeedback}
showSkeleton={showSkeleton}
formItemName="email"
minLength={Constants.GLOBALS.MIN_EMAIL_LENGTH}
maxLength={Constants.GLOBALS.MAX_EMAIL_LENGTH}
label={t("common.email")}
ruleMessageValueRequired={t("common.inputRules.emailRequired")}
ruleMessageValueNotAvailable={t("common.inputRules.emailTaken")}
inputPlaceholder={
thirdPerson
? t("common.emailPlaceholderThirdPerson")
: t("common.emailPlaceholder")
}
ruleMessageValueMinLengthRequired={t("common.inputRules.emailMinLength", {
minLength: Constants.GLOBALS.MIN_EMAIL_LENGTH,
})}
inputType="email"
disableAvailableCheck={disableEmailCheck}
fetchUrl="/user/auth/check/email"
fetchParameter="email"
fetchDelay={Constants.DELAY_EMAIL_CHECK}
formItemRules={[
{
type: "email",
message: t("common.inputRules.emailInvalid"),
},
]}
/>
);
}
/*
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,

View File

@ -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 }) {
<Button
key={1}
type="primary"
onClick={() => {
console.log("reactivate account");
setStep(LoginStep._BACK_TO_PASSWORD);
}}
onClick={() => setStep(LoginStep._BACK_TO_PASSWORD)}
>
{t("authentication.login.stateAccountPendingDeletion.button")}
</Button>,
@ -212,6 +210,10 @@ function Login({ notificationApi }) {
);
}
if (step === LoginStep.PENDING_EMAIL_VERIFICATION) {
return <PendingEmailVerification />;
}
return (
<Form
form={form}
@ -227,10 +229,7 @@ function Login({ notificationApi }) {
</Typography.Paragraph>
)}
<MyAccountNameFormInput
disableAccountNameCheck={true}
hasFeedback={false}
/>
<MyEmailFormInput hasFeedback={false} disableEmailCheck />
<div
style={{
@ -272,10 +271,10 @@ function Login({ notificationApi }) {
let validateFields = [];
if (step === LoginStep.ACCOUNT_NAME) {
validateFields = ["accountName"];
validateFields = ["email"];
} else {
validateFields = [
"accountName",
"email",
"password",
"rememberMe",
"recaptcha",
@ -289,7 +288,7 @@ function Login({ notificationApi }) {
setIsRequesting(true);
let body = {
accountName: values.accountName.toLocaleLowerCase(),
email: values.email.toLocaleLowerCase(),
};
if (
@ -335,6 +334,9 @@ function Login({ notificationApi }) {
case Constants.ACCOUNT_STATE.INIT_LOGGING:
setStep(LoginStep.INIT_LOGIN);
break;
case Constants.ACCOUNT_STATE.PENDING_EMAIL_VERIFICATION:
setStep(LoginStep.PENDING_EMAIL_VERIFICATION);
break;
default:
showUnkownErrorNotification(notificationApi, t);
break;
@ -369,10 +371,16 @@ function Login({ notificationApi }) {
);
}
const SignUpStep = {
SIGN_UP: 1,
PENDING_EMAIL_VERIFICATION: 2,
};
function SignUp({ notificationApi }) {
const { t, i18n } = useTranslation();
const navigate = useNavigate();
const [step, setStep] = useState(SignUpStep.SIGN_UP);
const [isRequesting, setIsRequesting] = useState(false);
const recaptchaValueRef = useRef(null);
@ -398,7 +406,7 @@ function SignUp({ notificationApi }) {
method: "POST",
body: {
companyName: values.companyName,
accountName: values.accountName.toLocaleLowerCase(),
email: values.email.toLocaleLowerCase(),
password: EncodeStringToBase64(values.password),
username: values.username,
language: i18n.language,
@ -408,21 +416,22 @@ function SignUp({ notificationApi }) {
notificationApi: notificationApi,
t: t,
})
.then((data) => {
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 <PendingEmailVerification />;
}
return (
<Form
form={form}
@ -438,10 +447,7 @@ function SignUp({ notificationApi }) {
propsFormItem={{ extra: t("common.usernameInfo") }}
/>
<MyAccountNameFormInput
hasFeedback={true}
propsFormItem={{ extra: t("common.accountNameInfo") }}
/>
<MyEmailFormInput hasFeedback={true} />
<MyPasswordFormInput />
@ -489,3 +495,15 @@ function SignUp({ notificationApi }) {
</Form>
);
}
function PendingEmailVerification() {
const { t } = useTranslation();
return (
<Result
status="warning"
title={t("authentication.signUp.pendingEmailVerification.title")}
subTitle={t("authentication.signUp.pendingEmailVerification.description")}
/>
);
}

View File

@ -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({
>
<MyUsernameFormInput thirdPerson />
<MyAccountNameFormInput hasFeedback thirdPerson />
<MyEmailFormInput hasFeedback thirdPerson />
{modalOptions.mode === "add" && (
<>

View File

@ -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 (
<Card title={t("userProfile.yourProfile.cardTitle")}>
@ -345,10 +344,10 @@ function YourProfile({
showSkeleton={requestState === RequestState.INIT}
/>
<MyAccountNameFormInput
<MyEmailFormInput
showSkeleton={requestState === RequestState.INIT}
hasFeedback
disableAccountNameCheck={requestData.account_name === accountName}
disableEmailCheck={requestData.email === email}
/>
</Form>
</Card>
@ -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);
}
});
})

View File

@ -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,