This commit is contained in:
Leo Giovanetti
2022-07-29 15:23:54 +02:00
committed by GitHub
parent b931cf2d7c
commit 5437005c7e
6 changed files with 92 additions and 29 deletions
+5 -3
View File
@@ -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);
+18 -9
View File
@@ -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 (
<FormProvider {...formMethods}>
<form id="setup-step-1" name="setup-step-1" className="space-y-4" onSubmit={onSubmit} ref={formRef}>
<form id="setup-step-1" name="setup-step-1" className="space-y-4" onSubmit={onSubmit}>
<div>
<Controller
name="username"
@@ -140,7 +149,6 @@ const SetupFormStep1 = (props: { setIsLoading: (val: boolean) => 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"
@@ -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",
+10 -7
View File
@@ -19,11 +19,11 @@ export async function getSession(options: GetSessionParams): Promise<Session | n
return session as Session | null;
}
export function isPasswordValid(password: string) {
let cap = false,
low = false,
num = false,
min = false;
export const isPasswordValid = ((password: string, breakdown?: boolean) => {
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 };
};
+1 -1
View File
@@ -36,7 +36,7 @@ function WizardForm<T extends DefaultStep>(props: { href: string; steps: T[]; co
</div>
<div className="print:p-none px-4 py-5 sm:p-6">{currentStep.content}</div>
{currentStep.enabled !== false && (
<div className="px-4 py-4 print:hidden sm:px-6">
<div className="flex justify-end px-4 py-4 print:hidden sm:px-6">
{step > 1 && (
<Button
color="secondary"
+54 -8
View File
@@ -1,6 +1,6 @@
import { useId } from "@radix-ui/react-id";
import React, { forwardRef, ReactElement, ReactNode, Ref } from "react";
import { AlertCircle, Icon } from "react-feather";
import { Info, Circle, Check, X } from "react-feather";
import { FieldValues, FormProvider, SubmitHandler, useFormContext, UseFormReturn } from "react-hook-form";
import classNames from "@calcom/lib/classNames";
@@ -44,6 +44,7 @@ export function InputLeading(props: JSX.IntrinsicElements["div"]) {
type InputFieldProps = {
label?: ReactNode;
hint?: ReactNode;
hintErrors?: string[];
addOnLeading?: ReactNode;
addOnSuffix?: ReactNode;
addOnFilled?: boolean;
@@ -70,6 +71,7 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
addOnSuffix,
addOnFilled = true,
hint,
hintErrors,
labelSrOnly,
containerClassName,
...passThrough
@@ -81,7 +83,7 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
<Label
htmlFor={id}
{...labelProps}
className={classNames(labelSrOnly && "sr-only", props.error && "text-red-900", "pb-1")}>
className={classNames(labelSrOnly && "sr-only", props.error && "text-red-900", "mb-2")}>
{label}
</Label>
)}
@@ -122,15 +124,59 @@ const InputField = forwardRef<HTMLInputElement, InputFieldProps>(function InputF
) : (
<Input id={id} placeholder={placeholder} className={className} {...passThrough} ref={ref} />
)}
{hint && (
<div className="text-gray flex items-center text-sm text-gray-700">
<AlertCircle className="mr-1 h-3 w-3" />
{hint}
{methods?.formState?.errors[props.name] && methods?.formState?.errors[props.name].message && (
<div className="text-gray mt-2 flex items-center text-sm text-red-700">
<Info className="mr-1 h-3 w-3" />
{methods.formState.errors[props.name].message}
</div>
)}
{methods?.formState?.errors[props.name] && (
<Alert className="mt-1" severity="error" message={methods.formState.errors[props.name].message} />
{hintErrors && (
<div className="text-gray mt-2 flex items-center text-sm text-gray-700">
{methods?.formState?.errors[props.name] !== undefined ? (
<ul className="then ml-2">
{hintErrors.map((errorKey) => {
const error =
methods?.formState?.errors[props.name][errorKey] ||
methods?.formState?.errors[props.name].message;
const submitted = methods?.formState.isSubmitted;
return (
<li
key={errorKey}
className={error !== undefined ? (submitted ? "text-red-700" : "") : "text-green-600"}>
{error !== undefined ? (
submitted ? (
<X size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
) : (
<Circle fill="currentColor" size="5" className="mr-2 inline-block" />
)
) : (
<Check size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
)}
{error?.message || t(`${props.name}_hint_${errorKey}`)}
</li>
);
})}
</ul>
) : (
<ul className="otherwise ml-2">
{hintErrors.map((errorKey) => {
const dirty = methods?.formState.dirtyFields[props.name];
return (
<li key={errorKey} className={!!dirty ? "text-green-600" : ""}>
{!!dirty ? (
<Check size="12" strokeWidth="3" className="-ml-1 mr-2 inline-block" />
) : (
<Circle fill="currentColor" size="5" className="mr-2 inline-block" />
)}
{t(`${props.name}_hint_${errorKey}`)}
</li>
);
})}
</ul>
)}
</div>
)}
{hint && <div className="text-gray mt-2 flex items-center text-sm text-gray-700">{hint}</div>}
</div>
);
});