From 5437005c7ecb9b7e734f1da94e0674033854f5b0 Mon Sep 17 00:00:00 2001 From: Leo Giovanetti Date: Fri, 29 Jul 2022 10:23:54 -0300 Subject: [PATCH] UI tweaks (#3576) --- apps/web/pages/api/auth/setup.ts | 8 ++- apps/web/pages/auth/setup.tsx | 27 +++++--- apps/web/public/static/locales/en/common.json | 5 +- packages/lib/auth.ts | 17 ++--- packages/ui/v2/WizardForm.tsx | 2 +- packages/ui/v2/form/fields.tsx | 62 ++++++++++++++++--- 6 files changed, 92 insertions(+), 29 deletions(-) diff --git a/apps/web/pages/api/auth/setup.ts b/apps/web/pages/api/auth/setup.ts index 353dfe082a..c3e354c828 100644 --- a/apps/web/pages/api/auth/setup.ts +++ b/apps/web/pages/api/auth/setup.ts @@ -10,8 +10,10 @@ import slugify from "@calcom/lib/slugify"; import prisma from "@calcom/prisma"; const querySchema = z.object({ - username: z.string().min(1), - full_name: z.string(), + username: z + .string() + .refine((val) => val.trim().length >= 1, { message: "Please enter at least one character" }), + full_name: z.string().min(3, "Please enter at least 3 characters"), email_address: z.string().email({ message: "Please enter a valid email" }), password: z.string().refine((val) => isPasswordValid(val.trim()), { message: @@ -30,7 +32,7 @@ async function handler(req: NextApiRequest, res: NextApiResponse) { throw new HttpError({ statusCode: 422, message: parsedQuery.error.message }); } - const username = slugify(parsedQuery.data.username); + const username = slugify(parsedQuery.data.username.trim()); const userEmail = parsedQuery.data.email_address.toLowerCase(); const hashedPassword = await hashPassword(parsedQuery.data.password); diff --git a/apps/web/pages/auth/setup.tsx b/apps/web/pages/auth/setup.tsx index d6e81ce546..591ce37c81 100644 --- a/apps/web/pages/auth/setup.tsx +++ b/apps/web/pages/auth/setup.tsx @@ -1,7 +1,7 @@ import { CheckIcon } from "@heroicons/react/solid"; import { zodResolver } from "@hookform/resolvers/zod"; import { useRouter } from "next/router"; -import { useRef, useState } from "react"; +import { useState } from "react"; import { Controller, FormProvider, useForm } from "react-hook-form"; import * as z from "zod"; @@ -32,11 +32,22 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => { const { t } = useLocale(); const formSchema = z.object({ - username: z.string().min(1, t("at_least_characters", { count: 1 })), + username: z + .string() + .refine((val) => val.trim().length >= 1, { message: t("at_least_characters", { count: 1 }) }), email_address: z.string().email({ message: t("enter_valid_email") }), full_name: z.string().min(3, t("at_least_characters", { count: 3 })), - password: z.string().refine((val) => isPasswordValid(val.trim()), { - message: t("invalid_password_hint"), + password: z.string().superRefine((data, ctx) => { + const result = isPasswordValid(data, true); + Object.keys(result).map((key: string) => { + if (!result[key as keyof typeof result]) { + ctx.addIssue({ + code: z.ZodIssueCode.custom, + path: [key], + message: t(`password_hint_${key}`), + }); + } + }); }), }); @@ -49,8 +60,6 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => { resolver: zodResolver(formSchema), }); - const formRef = useRef(null); - const onError = () => { props.setIsLoading(false); }; @@ -60,7 +69,7 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => { const response = await fetch("/api/auth/setup", { method: "POST", body: JSON.stringify({ - username: data.username, + username: data.username.trim(), full_name: data.full_name, email_address: data.email_address.toLowerCase(), password: data.password, @@ -78,7 +87,7 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => { return ( -
+
void }) => { formMethods.setValue("email_address", e.target.value); await formMethods.trigger("email_address"); }} - defaultValue={router.query.email_address} className="my-0" name="email_address" /> @@ -160,6 +168,7 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => void }) => { formMethods.setValue("password", e.target.value); await formMethods.trigger("password"); }} + hintErrors={["caplow", "min", "num"]} name="password" className="my-0" autoComplete="off" diff --git a/apps/web/public/static/locales/en/common.json b/apps/web/public/static/locales/en/common.json index cfb671eda2..be69266202 100644 --- a/apps/web/public/static/locales/en/common.json +++ b/apps/web/public/static/locales/en/common.json @@ -405,6 +405,9 @@ "new_password_matches_old_password": "New password matches your old password. Please choose a different password.", "forgotten_secret_description": "If you have lost or forgotten this secret, you can change it, but be aware that all integrations using this secret will need to be updated", "current_incorrect_password": "Current password is incorrect", + "password_hint_caplow": "Mix of uppercase & lowercase letters", + "password_hint_min": "Minimum 7 characters long", + "password_hint_num": "Contain at least 1 number", "invalid_password_hint": "The password must be a minimum of 7 characters long containing at least one number and have a mixture of uppercase and lowercase letters", "incorrect_password": "Password is incorrect.", "1_on_1": "1-on-1", @@ -436,7 +439,7 @@ "additional_guests": "+ Additional Guests", "your_name": "Your name", "email_address": "Email address", - "valid_email_address": "Please enter a valid email", + "enter_valid_email": "Please enter a valid email", "location": "Location", "yes": "yes", "no": "no", diff --git a/packages/lib/auth.ts b/packages/lib/auth.ts index f72271c835..0b4e419627 100644 --- a/packages/lib/auth.ts +++ b/packages/lib/auth.ts @@ -19,11 +19,11 @@ export async function getSession(options: GetSessionParams): Promise { + let cap = false, // Has uppercase characters + low = false, // Has lowercase characters + num = false, // At least one number + min = false; // Seven characters if (password.length > 6) min = true; for (let i = 0; i < password.length; i++) { if (!isNaN(parseInt(password[i]))) num = true; @@ -32,5 +32,8 @@ export function isPasswordValid(password: string) { if (password[i] === password[i].toLowerCase()) low = true; } } - return cap && low && num && min; -} + return !!breakdown ? { caplow: cap && low, num, min } : cap && low && num && min; +}) as { + (password: string): boolean; + (password: string, breakdown: boolean): { caplow: boolean; num: boolean; min: boolean }; +}; diff --git a/packages/ui/v2/WizardForm.tsx b/packages/ui/v2/WizardForm.tsx index 5576398973..89563eb1ff 100644 --- a/packages/ui/v2/WizardForm.tsx +++ b/packages/ui/v2/WizardForm.tsx @@ -36,7 +36,7 @@ function WizardForm(props: { href: string; steps: T[]; co
{currentStep.content}
{currentStep.enabled !== false && ( -
+
{step > 1 && (