From 107c6c3995e44cc1ea136c8034f9e42ecab04cb1 Mon Sep 17 00:00:00 2001 From: Manuel Date: Tue, 22 Aug 2023 21:45:10 +0200 Subject: [PATCH] =?UTF-8?q?=E2=9C=A8=20Add=20password=20meter=20to=20onboa?= =?UTF-8?q?rding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- public/locales/en/manage/users/create.json | 9 +-- public/locales/en/password-requirements.json | 7 ++ .../Manage/User/Create/security-step.tsx | 80 ++----------------- .../Onboarding/onboarding-steps.tsx | 6 +- .../Onboarding/step-create-account.tsx | 12 ++- .../Onboarding/step-docker-import.tsx | 20 ----- .../Password/password-requirement.tsx | 24 ++++++ .../Password/password-requirements.tsx | 39 +++++++++ src/pages/manage/users/create.tsx | 2 +- src/pages/onboard.tsx | 2 +- 10 files changed, 90 insertions(+), 111 deletions(-) create mode 100644 public/locales/en/password-requirements.json delete mode 100644 src/components/Onboarding/step-docker-import.tsx create mode 100644 src/components/Password/password-requirement.tsx create mode 100644 src/components/Password/password-requirements.tsx diff --git a/public/locales/en/manage/users/create.json b/public/locales/en/manage/users/create.json index c8c34de42..dcf8d4fe6 100644 --- a/public/locales/en/manage/users/create.json +++ b/public/locales/en/manage/users/create.json @@ -15,14 +15,7 @@ "title": "Second step", "text": "Password", "password": { - "label": "Password", - "requirements": { - "number": "Includes number", - "lowercase": "Includes lowercase letter", - "uppercase": "Includes uppercase letter", - "special": "Includes special character", - "length": "Includes at least {{count}} characters" - } + "label": "Password" } }, "finish": { diff --git a/public/locales/en/password-requirements.json b/public/locales/en/password-requirements.json new file mode 100644 index 000000000..605007553 --- /dev/null +++ b/public/locales/en/password-requirements.json @@ -0,0 +1,7 @@ +{ + "number": "Includes number", + "lowercase": "Includes lowercase letter", + "uppercase": "Includes uppercase letter", + "special": "Includes special character", + "length": "Includes at least {{count}} characters" +} \ No newline at end of file diff --git a/src/components/Manage/User/Create/security-step.tsx b/src/components/Manage/User/Create/security-step.tsx index 1cbcf573a..5e76e5ac8 100644 --- a/src/components/Manage/User/Create/security-step.tsx +++ b/src/components/Manage/User/Create/security-step.tsx @@ -1,48 +1,13 @@ -import { - Box, - Button, - Card, - Flex, - Group, - PasswordInput, - Popover, - Progress, - Text, -} from '@mantine/core'; +import { Button, Card, Flex, Group, PasswordInput, Popover } from '@mantine/core'; import { useForm } from '@mantine/form'; -import { - IconArrowLeft, - IconArrowRight, - IconCheck, - IconDice, - IconKey, - IconX, -} from '@tabler/icons-react'; +import { IconArrowLeft, IconArrowRight, IconDice, IconKey } from '@tabler/icons-react'; import { useTranslation } from 'next-i18next'; import { useState } from 'react'; import { z } from 'zod'; +import { PasswordRequirements } from '~/components/Password/password-requirements'; import { api } from '~/utils/api'; import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; -import { minPasswordLength, passwordSchema } from '~/validations/user'; - -const requirements = [ - { re: /[0-9]/, label: 'number' }, - { re: /[a-z]/, label: 'lowercase' }, - { re: /[A-Z]/, label: 'uppercase' }, - { re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: 'special' }, -]; - -function getStrength(password: string) { - let multiplier = password.length >= minPasswordLength ? 0 : 1; - - requirements.forEach((requirement) => { - if (!requirement.re.test(password)) { - multiplier += 1; - } - }); - - return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 10); -} +import { passwordSchema } from '~/validations/user'; interface CreateAccountSecurityStepProps { defaultPassword: string; @@ -70,16 +35,6 @@ export const CreateAccountSecurityStep = ({ const { mutateAsync, isLoading } = api.password.generate.useMutation(); const [popoverOpened, setPopoverOpened] = useState(false); - const checks = requirements.map((requirement, index) => ( - - )); - - const strength = getStrength(form.values.password); - const color = strength === 100 ? 'teal' : strength > 50 ? 'yellow' : 'red'; return ( @@ -122,12 +77,7 @@ export const CreateAccountSecurityStep = ({ - - = minPasswordLength} - /> - {checks} + @@ -153,26 +103,6 @@ export const CreateAccountSecurityStep = ({ ); }; -const PasswordRequirement = ({ meets, label }: { meets: boolean; label: string }) => { - const { t } = useTranslation('manage/users/create'); - - return ( - - {meets ? : }{' '} - - {t(`steps.security.password.requirements.${label}`, { - count: minPasswordLength, - })} - - - ); -}; - export const createAccountSecurityStepValidationSchema = z.object({ password: passwordSchema, }); diff --git a/src/components/Onboarding/onboarding-steps.tsx b/src/components/Onboarding/onboarding-steps.tsx index 036f4fb31..0e9f8c245 100644 --- a/src/components/Onboarding/onboarding-steps.tsx +++ b/src/components/Onboarding/onboarding-steps.tsx @@ -2,14 +2,13 @@ import { Stack, Stepper } from '@mantine/core'; import { useState } from 'react'; import { StepCreateAccount } from './step-create-account'; -import { StepDockerImport } from './step-docker-import'; import { StepDocumentation } from './step-documentation'; import { StepOnboardingFinished } from './step-onboarding-finished'; import { StepUpdatePathMappings } from './step-update-path-mappings'; export const OnboardingSteps = ({ isUpdate }: { isUpdate: boolean }) => { const [currentStep, setCurrentStep] = useState(0); - const nextStep = () => setCurrentStep((current) => (current < 4 ? current + 1 : current)); + const nextStep = () => setCurrentStep((current) => (current < 3 ? current + 1 : current)); const prevStep = () => setCurrentStep((current) => (current > 0 ? current - 1 : current)); return ( @@ -31,9 +30,6 @@ export const OnboardingSteps = ({ isUpdate }: { isUpdate: boolean }) => { - - - diff --git a/src/components/Onboarding/step-create-account.tsx b/src/components/Onboarding/step-create-account.tsx index de7a8b3d8..e2d7dd10d 100644 --- a/src/components/Onboarding/step-create-account.tsx +++ b/src/components/Onboarding/step-create-account.tsx @@ -1,4 +1,4 @@ -import { Button, Group, PasswordInput, Stack, TextInput, Title } from '@mantine/core'; +import { Button, Card, Group, PasswordInput, Stack, TextInput, Title } from '@mantine/core'; import { useForm } from '@mantine/form'; import { IconArrowLeft, IconArrowRight } from '@tabler/icons-react'; import { signIn } from 'next-auth/react'; @@ -8,6 +8,7 @@ import { api } from '~/utils/api'; import { useI18nZodResolver } from '~/utils/i18n-zod-resolver'; import { signUpFormSchema } from '~/validations/user'; +import { PasswordRequirements } from '../Password/password-requirements'; import { OnboardingStepWrapper } from './common-wrapper'; export const StepCreateAccount = ({ @@ -22,6 +23,11 @@ export const StepCreateAccount = ({ const { i18nZodResolver } = useI18nZodResolver(); const form = useForm>({ + initialValues: { + password: '', + username: '', + passwordConfirmation: '', + }, validate: i18nZodResolver(signUpFormSchema), validateInputOnBlur: true, }); @@ -70,6 +76,10 @@ export const StepCreateAccount = ({ {...form.getInputProps('password')} /> + + + + void }) => { - return ( - - - Automatic container import - - - - - - - ); -}; diff --git a/src/components/Password/password-requirement.tsx b/src/components/Password/password-requirement.tsx new file mode 100644 index 000000000..720fdad55 --- /dev/null +++ b/src/components/Password/password-requirement.tsx @@ -0,0 +1,24 @@ +import { Box, Text } from "@mantine/core"; +import { IconCheck, IconX } from "@tabler/icons-react"; +import { useTranslation } from "react-i18next"; +import { minPasswordLength } from "~/validations/user"; + +export const PasswordRequirement = ({ meets, label }: { meets: boolean; label: string }) => { + const { t } = useTranslation('password-requirements'); + + return ( + + {meets ? : }{' '} + + {t(`${label}`, { + count: minPasswordLength, + })} + + + ); + }; \ No newline at end of file diff --git a/src/components/Password/password-requirements.tsx b/src/components/Password/password-requirements.tsx new file mode 100644 index 000000000..2a25a54fb --- /dev/null +++ b/src/components/Password/password-requirements.tsx @@ -0,0 +1,39 @@ +import { Progress } from '@mantine/core'; +import { minPasswordLength } from '~/validations/user'; + +import { PasswordRequirement } from './password-requirement'; + +const requirements = [ + { re: /[0-9]/, label: 'number' }, + { re: /[a-z]/, label: 'lowercase' }, + { re: /[A-Z]/, label: 'uppercase' }, + { re: /[$&+,:;=?@#|'<>.^*()%!-]/, label: 'special' }, +]; + +function getStrength(password: string) { + let multiplier = password.length >= minPasswordLength ? 0 : 1; + + requirements.forEach((requirement) => { + if (!requirement.re.test(password)) { + multiplier += 1; + } + }); + + return Math.max(100 - (100 / (requirements.length + 1)) * multiplier, 10); +} + +export const PasswordRequirements = ({ value }: { value: string }) => { + const checks = requirements.map((requirement, index) => ( + + )); + + const strength = getStrength(value); + const color = strength === 100 ? 'teal' : strength > 50 ? 'yellow' : 'red'; + return ( + <> + + = minPasswordLength} /> + {checks} + + ); +}; diff --git a/src/pages/manage/users/create.tsx b/src/pages/manage/users/create.tsx index 3f200e431..421290cc8 100644 --- a/src/pages/manage/users/create.tsx +++ b/src/pages/manage/users/create.tsx @@ -139,7 +139,7 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => { } const translations = await getServerSideTranslations( - manageNamespaces, + [...manageNamespaces, 'password-requirements'], ctx.locale, ctx.req, ctx.res diff --git a/src/pages/onboard.tsx b/src/pages/onboard.tsx index c93ceb93b..d17b980ae 100644 --- a/src/pages/onboard.tsx +++ b/src/pages/onboard.tsx @@ -77,7 +77,7 @@ export const getServerSideProps: GetServerSideProps = async (ctx) => { const configs = files.map((file) => getConfig(file)); const configSchemaVersions = configs.map((config) => config.schemaVersion); - const translations = await getServerSideTranslations([], ctx.locale, ctx.req, ctx.res); + const translations = await getServerSideTranslations(['password-requirements'], ctx.locale, ctx.req, ctx.res); return { props: {