chore: Account Security page ui

---

Reviewed-by: schowdhuri
This commit is contained in:
Ahmed Bouhuolia 2022-05-09 13:53:51 +02:00 committed by GitHub
parent b0d51c5efa
commit d8dd987ccf
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
10 changed files with 288 additions and 12 deletions

View File

@ -21,7 +21,7 @@ import { AccountPaymentAccounts } from "@pages/Account/AccountPaymentAccounts";
import { AccountNodeSettings } from "@pages/Account/AccountNodeSettings";
import { AccountBackup } from "@pages/Account/AccountBackup";
import { AccountWallet } from "@pages/Account/AccountWallet";
import { AccountSecurity } from "@pages/Account/AccountSecurity";
import { AccountSecurity } from "@pages/Account/Security";
import { ROUTES } from "@constants/routes";
import { PaymentMethods } from "@pages/Account";
import { AddPaymentMethod } from "@organisms/AddPaymentMethod";

View File

@ -20,10 +20,18 @@ export enum LangKeys {
Header = "app.header",
ConnectingToNetwork = "app.connectingToNetwork",
WelcomeToHaveno = "app.welcomeToHaveno",
Save = "app.save",
AccountTitle = "account.title",
AccountSidebarPaymentAccounts = "account.sidebar.paymentAccounts",
AccountSidebarSecurity = "account.sidebar.security",
AccountSidebarWallet = "account.sidebar.wallet",
AccountSidebarBackup = "account.sidebar.backup",
AccountSidebarNodeSettings = "account.sidebar.nodeSettings",
AccountSecurityTitle = "account.security.title",
AccountSecurityDesc = "account.security.desc",
AccountSecurityFieldPassword = "account.security.field.password",
AccountSecurityFieldRepeatPassword = "account.security.field.repeatPassword",
AccountSecurityFieldCurrentPassword = "account.security.field.currentPassword",
AccountSecurityFieldPasswordFormatMsg = "account.security.field.password.format.message",
AccountSecurityFieldRepeatPasswordMatchMsg = "account.security.field.repeatPassword.match.message",
}

View File

@ -23,12 +23,23 @@ const LangPackEN: { [key in LangKeys]: string } = {
[LangKeys.Header]: "Haveno",
[LangKeys.WelcomeToHaveno]:
"Welcome to Haveno. The worlds first Monero based decentralised exchange.",
[LangKeys.Save]: "Save",
[LangKeys.AccountTitle]: "Account",
[LangKeys.AccountSidebarPaymentAccounts]: "Payment Accounts",
[LangKeys.AccountSidebarSecurity]: "Security",
[LangKeys.AccountSidebarWallet]: "Wallet",
[LangKeys.AccountSidebarBackup]: "Backup",
[LangKeys.AccountSidebarNodeSettings]: "Settings",
[LangKeys.AccountSecurityTitle]: "Account Security",
[LangKeys.AccountSecurityDesc]:
"Haveno does not store any of your data, this happens solely locally on your device. Its not possible to restore your password when lost. Please make sure you store a copy of it on a safe place.",
[LangKeys.AccountSecurityFieldPassword]: "Password",
[LangKeys.AccountSecurityFieldRepeatPassword]: "Repeat new password",
[LangKeys.AccountSecurityFieldCurrentPassword]: "Current password",
[LangKeys.AccountSecurityFieldPasswordFormatMsg]:
"contain atleast {minChars} characters, one uppercase, one lowercase and one number.",
[LangKeys.AccountSecurityFieldRepeatPasswordMatchMsg]:
"Password confirmation doesn't match Password.",
};
export default LangPackEN;

View File

@ -23,12 +23,23 @@ const LangPackES: { [key in LangKeys]: string } = {
[LangKeys.Header]: "Haveno",
[LangKeys.WelcomeToHaveno]:
"Bienvenido a Haveno. El primer intercambio descentralizado basado en Monero del mundo.",
[LangKeys.Save]: "Guardar",
[LangKeys.AccountTitle]: "Cuenta",
[LangKeys.AccountSidebarPaymentAccounts]: "Cuentas de pago",
[LangKeys.AccountSidebarSecurity]: "Seguridad",
[LangKeys.AccountSidebarWallet]: "Cartera",
[LangKeys.AccountSidebarBackup]: "Respaldo",
[LangKeys.AccountSidebarNodeSettings]: "Ajustes",
[LangKeys.AccountSecurityTitle]: "Seguridad de la cuenta",
[LangKeys.AccountSecurityDesc]:
"Haveno no almacena ninguno de sus datos, esto ocurre únicamente localmente en su dispositivo. No es posible restaurar su contraseña cuando se pierde. Asegúrese de guardar una copia en un lugar seguro.",
[LangKeys.AccountSecurityFieldPassword]: "Clave",
[LangKeys.AccountSecurityFieldRepeatPassword]: "Repita la nueva contraseña",
[LangKeys.AccountSecurityFieldCurrentPassword]: "Contraseña actual",
[LangKeys.AccountSecurityFieldPasswordFormatMsg]:
"contener al menos {minChars} caracteres, una mayúscula, una minúscula y un número.",
[LangKeys.AccountSecurityFieldRepeatPasswordMatchMsg]:
"La confirmación de la contraseña no coincide con la contraseña.",
};
export default LangPackES;

View File

@ -0,0 +1,58 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import { LangKeys } from "@constants/lang";
import { Stack, Box, createStyles, Group } from "@mantine/core";
import { AccountLayout } from "@templates/AccountLayout";
import { AccountSecurityForm } from "./AccountSecurityForm";
import { Heading, BodyText } from "@atoms/Typography";
import { WIDTH } from "./_constants";
function AccountSecurityHeader() {
return (
<Group spacing="sm">
<Heading stringId={LangKeys.AccountSecurityTitle} order={3}>
Account Security
</Heading>
<BodyText stringId={LangKeys.AccountSecurityDesc} size="md">
Haveno does not store any of your data, this happens solely locally on
your device. Its not possible to restore your password when lost.
Please make sure you store a copy of it on a safe place.
</BodyText>
</Group>
);
}
export function AccountSecurity() {
const { classes } = useStyles();
return (
<AccountLayout>
<Box className={classes.content}>
<Stack spacing="lg">
<AccountSecurityHeader />
<AccountSecurityForm />
</Stack>
</Box>
</AccountLayout>
);
}
const useStyles = createStyles(() => ({
content: {
maxWidth: WIDTH,
},
}));

View File

@ -0,0 +1,86 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
import { FormattedMessage } from "react-intl";
import { Stack, Box, Group } from "@mantine/core";
import { useForm, joiResolver } from "@mantine/form";
import { TextInput } from "@atoms/TextInput";
import { LangKeys } from "@constants/lang";
import { useAccountSecurityFormSchema } from "./_hooks";
import { Button } from "@atoms/Buttons";
export function AccountSecurityForm() {
const accountSecurityFormSchema = useAccountSecurityFormSchema();
const form = useForm({
initialValues: {
currentPassword: "",
password: "",
confirmPassword: "",
},
schema: joiResolver(accountSecurityFormSchema),
});
return (
<Box>
<form onSubmit={form.onSubmit((values) => console.log(values))}>
<Stack spacing="lg">
<TextInput
id={"password"}
type={"password"}
required
label={
<FormattedMessage
id={LangKeys.AccountSecurityFieldPassword}
defaultMessage={"Password"}
/>
}
{...form.getInputProps("password")}
/>
<TextInput
id={"confirmPassword"}
required
type={"password"}
label={
<FormattedMessage
id={LangKeys.AccountSecurityFieldRepeatPassword}
defaultMessage={"Repeat new password"}
/>
}
{...form.getInputProps("confirmPassword")}
/>
<TextInput
id={"currentPassword"}
type={"password"}
required
label={
<FormattedMessage
id={LangKeys.AccountSecurityFieldCurrentPassword}
defaultMessage={"Current password"}
/>
}
{...form.getInputProps("currentPassword")}
/>
<Group position="right" mt="md">
<Button size="md" type={"submit"}>
<FormattedMessage id={LangKeys.Save} defaultMessage={"Save"} />
</Button>
</Group>
</Stack>
</form>
</Box>
);
}

View File

@ -14,12 +14,7 @@
// limitations under the License.
// =============================================================================
import { AccountLayout } from "@templates/AccountLayout";
export const WIDTH = 475;
export function AccountSecurity() {
return (
<AccountLayout>
<h1>Account Security</h1>
</AccountLayout>
);
}
// The minimum characters that should password field contain.
export const MIN_PASSWORD_CHARS = 8;

View File

@ -0,0 +1,57 @@
import { LangKeys } from "@constants/lang";
import Joi from "joi";
import { useIntl } from "react-intl";
import { MIN_PASSWORD_CHARS } from "./_constants";
const getPasswordRegex = () => {
return RegExp(
`^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.{${MIN_PASSWORD_CHARS},})`,
"i"
);
};
export const useAccountSecurityFormSchema = () => {
const { formatMessage } = useIntl();
return Joi.object({
password: Joi.string()
.required()
.regex(
getPasswordRegex(),
formatMessage(
{
id: LangKeys.AccountSecurityFieldPasswordFormatMsg,
defaultMessage: `contain atleast ${MIN_PASSWORD_CHARS} characters, one uppercase, one lowercase and one number`,
},
{
minChars: MIN_PASSWORD_CHARS,
}
)
)
.label(
formatMessage({
id: LangKeys.AccountSecurityFieldPassword,
defaultMessage: "Password",
})
),
confirmPassword: Joi.string()
.valid(Joi.ref("password"))
.required()
.options({
messages: {
"any.only": formatMessage({
id: LangKeys.AccountSecurityFieldRepeatPasswordMatchMsg,
defaultMessage: "Password confirmation doesn't match Password.",
}),
},
}),
currentPassword: Joi.string()
.required()
.label(
formatMessage({
id: LangKeys.AccountSecurityFieldCurrentPassword,
defaultMessage: "Current password",
})
),
});
};

View File

@ -0,0 +1,17 @@
// =============================================================================
// Copyright 2022 Haveno
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
// =============================================================================
export * from "./AccountSecurity";

View File

@ -1074,7 +1074,7 @@
pirates "^4.0.5"
source-map-support "^0.5.16"
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
"@babel/runtime@^7.0.0", "@babel/runtime@^7.10.2", "@babel/runtime@^7.12.5", "@babel/runtime@^7.13.10", "@babel/runtime@^7.14.8", "@babel/runtime@^7.15.4", "@babel/runtime@^7.17.8", "@babel/runtime@^7.3.1", "@babel/runtime@^7.5.0", "@babel/runtime@^7.5.5", "@babel/runtime@^7.6.2", "@babel/runtime@^7.7.2", "@babel/runtime@^7.7.6", "@babel/runtime@^7.8.4", "@babel/runtime@^7.8.7":
version "7.17.9"
resolved "https://registry.yarnpkg.com/@babel/runtime/-/runtime-7.17.9.tgz#d19fbf802d01a8cb6cf053a64e472d42c434ba72"
integrity sha512-lSiBBvodq29uShpWGNbgFdKYNiFDo5/HIYsaCEY9ff4sb10x9jizo2+pRrSyF4jKZCXqgzuqBOQKbUm90gQwJg==
@ -3178,9 +3178,9 @@
resolved "https://registry.yarnpkg.com/@types/json-schema/-/json-schema-7.0.11.tgz#d421b6c527a3037f7c84433fd2c4229e016863d3"
integrity sha512-wOuvG1SN4Us4rez+tylwwwCV1psiNVOkJeM3AUWUNWg/jDQY2+HE/444y5gc+jBmRqASOm2Oeh5c1axHobwRKQ==
"@types/lodash@^4.14.182":
"@types/lodash@^4.14.175", "@types/lodash@^4.14.182":
version "4.14.182"
resolved "https://registry.yarnpkg.com/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
resolved "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.182.tgz#05301a4d5e62963227eaafe0ce04dd77c54ea5c2"
integrity sha512-/THyiqyQAP9AfARo4pF+aCGcyiQ94tX/Is2I7HofNRqoYLgN1PBoOWu2/zTA5zMxzP5EFutMtWtGAFRKUe961Q==
"@types/mdast@^3.0.0":
@ -9198,6 +9198,11 @@ locate-path@^6.0.0:
dependencies:
p-locate "^5.0.0"
lodash-es@^4.17.21:
version "4.17.21"
resolved "https://registry.npmjs.org/lodash-es/-/lodash-es-4.17.21.tgz#43e626c46e6591b7750beb2b50117390c609e3ee"
integrity sha512-mKnC+QJ9pWVzv+C4/U3rRsHapFfHvQFoFB92e52xeyGMcX6/OlIl78je1u8vePzYZSkkogMPJ2yjxxsb89cxyw==
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
@ -9778,6 +9783,11 @@ nano-time@1.0.0:
dependencies:
big-integer "^1.6.16"
nanoclone@^0.2.1:
version "0.2.1"
resolved "https://registry.npmjs.org/nanoclone/-/nanoclone-0.2.1.tgz#dd4090f8f1a110d26bb32c49ed2f5b9235209ed4"
integrity sha512-wynEP02LmIbLpcYw8uBKpcfF6dmg2vcpKqxeH5UcoKEYdExslsdUA4ugFauuaeYdTB76ez6gJW8XAZ6CgkXYxA==
nanoid@^3.1.23:
version "3.3.3"
resolved "https://registry.yarnpkg.com/nanoid/-/nanoid-3.3.3.tgz#fd8e8b7aa761fe807dba2d1b98fb7241bb724a25"
@ -10785,6 +10795,11 @@ proper-lockfile@4.1.2:
retry "^0.12.0"
signal-exit "^3.0.2"
property-expr@^2.0.4:
version "2.0.5"
resolved "https://registry.npmjs.org/property-expr/-/property-expr-2.0.5.tgz#278bdb15308ae16af3e3b9640024524f4dc02cb4"
integrity sha512-IJUkICM5dP5znhCckHSv30Q4b5/JA5enCtkRHYaOVOAocnH/1BQEYTC5NMfT3AVl/iXKdr3aqQbQn9DxyWknwA==
property-information@^5.0.0, property-information@^5.3.0:
version "5.6.0"
resolved "https://registry.yarnpkg.com/property-information/-/property-information-5.6.0.tgz#61675545fb23002f245c6540ec46077d4da3ed69"
@ -12677,6 +12692,11 @@ toidentifier@1.0.1:
resolved "https://registry.yarnpkg.com/toidentifier/-/toidentifier-1.0.1.tgz#3be34321a88a820ed1bd80dfaa33e479fbb8dd35"
integrity sha512-o5sSPKEkg/DIQNmH43V0/uerLrpzVedkUh8tGNvaeXpfpuwjKenlSox/2O/BTlZUtEe+JG7s5YhEz608PlAHRA==
toposort@^2.0.2:
version "2.0.2"
resolved "https://registry.npmjs.org/toposort/-/toposort-2.0.2.tgz#ae21768175d1559d48bef35420b2f4962f09c330"
integrity sha1-riF2gXXRVZ1IvvNUILL0li8JwzA=
tr46@~0.0.3:
version "0.0.3"
resolved "https://registry.yarnpkg.com/tr46/-/tr46-0.0.3.tgz#8184fd347dac9cdc185992f3a6622e14b9d9ab6a"
@ -13746,6 +13766,19 @@ yocto-queue@^0.1.0:
resolved "https://registry.yarnpkg.com/yocto-queue/-/yocto-queue-0.1.0.tgz#0294eb3dee05028d31ee1a5fa2c556a6aaf10a1b"
integrity sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==
yup@^0.32.11:
version "0.32.11"
resolved "https://registry.npmjs.org/yup/-/yup-0.32.11.tgz#d67fb83eefa4698607982e63f7ca4c5ed3cf18c5"
integrity sha512-Z2Fe1bn+eLstG8DRR6FTavGD+MeAwyfmouhHsIUgaADz8jvFKbO/fXc2trJKZg+5EBjh4gGm3iU/t3onKlXHIg==
dependencies:
"@babel/runtime" "^7.15.4"
"@types/lodash" "^4.14.175"
lodash "^4.17.21"
lodash-es "^4.17.21"
nanoclone "^0.2.1"
property-expr "^2.0.4"
toposort "^2.0.2"
zwitch@^1.0.0:
version "1.0.5"
resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-1.0.5.tgz#d11d7381ffed16b742f6af7b3f223d5cd9fe9920"