Skip to content

Commit

Permalink
FE: add frontend validations
Browse files Browse the repository at this point in the history
  • Loading branch information
Mgrdich committed May 13, 2024
1 parent 184d0c4 commit 5bc2616
Show file tree
Hide file tree
Showing 13 changed files with 212 additions and 133 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public boolean isValid(String value, ConstraintValidatorContext context) {
errorMessages.add("Username must start with a letter.");
}

if (!value.matches(".{8,30}")) {
if (!value.matches(".{4,30}")) {
errorMessages.add("Username must be between 8 and 30 characters long.");
}

Expand Down
1 change: 0 additions & 1 deletion docker-compose.yml
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,6 @@ services:
context: ./frontend/
dockerfile: Dockerfile
volumes:
- '/app/node_modules'
- './frontend/:/app'
ports:
- "4040:80" # Assuming you want to use port 8080 locally
Expand Down
2 changes: 2 additions & 0 deletions frontend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,13 @@
"preview": "vite preview"
},
"dependencies": {
"@hookform/resolvers": "^3.3.4",
"@tanstack/react-query": "^5.35.1",
"@tanstack/react-query-devtools": "^5.35.1",
"clsx": "^2.1.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-hook-form": "^7.51.4",
"react-router-dom": "^6.23.0",
"tailwind-merge": "^2.3.0",
"zod": "^3.23.8"
Expand Down
23 changes: 23 additions & 0 deletions frontend/pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

15 changes: 13 additions & 2 deletions frontend/src/pages/Auth/ForgotPassword.tsx
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import InputWithLabel from "ui/InputWithLabel.tsx";
import LinkText from "ui/LinkText.tsx";
import FormSubmitButton from "./components/FormSubmitButton.tsx";
import { useForm } from "react-hook-form";
import ErrorLabel from "ui/ErrorLabel.tsx";
import FormSubmitButton from "ui/FormSubmitButton.tsx";

type ForgotPasswordForm = {
username: string;
};

function ForgotPassword() {
const {
register,
formState: { errors },
} = useForm<ForgotPasswordForm>();
return (
<div className="bg-neutral-50 min-h-screen flex flex-col items-center justify-center dark:bg-neutral-900">
<h1 className="mt-0 mb-16 text-5xl text-white font-bold tracking-tight md:text-5xl xl:text-5xl self-center">
Expand All @@ -11,7 +21,8 @@ function ForgotPassword() {
<div className="block p-6 rounded-lg shadow-lg bg-white max-w-sm">
<form>
<div className="form-group mb-6">
<InputWithLabel label="Email" name="email" type="text" placeholder="Enter Email" />
<InputWithLabel label="username" type="text" placeholder="Enter username" {...register("username")} />
<ErrorLabel error={errors.username} />
</div>
<FormSubmitButton>Submit</FormSubmitButton>
<div className="text-gray-800 mt-6 text-center">
Expand Down
63 changes: 35 additions & 28 deletions frontend/src/pages/Auth/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,17 +1,38 @@
import InputWithLabel from "ui/InputWithLabel.tsx";
import LinkText from "ui/LinkText.tsx";
import { useState } from "react";
import useLogin from "hooks/useLogin.ts";
import { useNavigate } from "react-router-dom";
import Button from "ui/Button.tsx";
import { SubmitHandler, useForm } from "react-hook-form";
import ErrorLabel from "ui/ErrorLabel.tsx";
import FormSubmitButton from "ui/FormSubmitButton.tsx";
import { z, ZodType } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

function Login() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const { mutateAsync, isPending } = useLogin();
type LoginForm = {
username: string;
password: string;
};

export const LoginSchema: ZodType<LoginForm> = z.object({
username: z.string().min(4).max(30),
password: z
.string()
.min(8)
.max(30)
.regex(/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/),
});

export default function Login() {
const {
register,
handleSubmit,
formState: { errors, isLoading, isValid },
} = useForm<LoginForm>({ resolver: zodResolver(LoginSchema) });

const { mutateAsync } = useLogin();
const navigate = useNavigate();

const onSubmit = async () => {
const onSubmit: SubmitHandler<LoginForm> = async ({ username, password }) => {
await mutateAsync({ username, password });
navigate("/conversation");
};
Expand All @@ -22,37 +43,23 @@ function Login() {
Welcome Back :)
</h1>
<div className="block p-6 rounded-lg shadow-lg bg-white max-w-sm">
<div>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group mb-6">
<InputWithLabel
label="Username"
name="username"
type="text"
placeholder="Enter Username"
onInput={(e) => setUsername(e.currentTarget.value)}
/>
<InputWithLabel label="Username" type="text" placeholder="Enter Username" {...register("username")} />
<ErrorLabel error={errors.username} />
</div>
<div className="form-group mb-6">
<InputWithLabel
label="Password"
type="password"
name="password"
placeholder="Enter Password"
onInput={(e) => setPassword(e.currentTarget.value)}
/>
<InputWithLabel label="Password" type="password" placeholder="Enter Password" {...register("password")} />
<ErrorLabel error={errors.password} />
</div>
<div className="flex justify-between items-center mb-6" />
<Button disabled={isPending} className="w-full" onClick={onSubmit}>
Sign In
</Button>
<FormSubmitButton disabled={isLoading || !isValid}>Sign In</FormSubmitButton>
<div className="text-gray-800 mt-6 text-center">
Not a member?
<LinkText to="/register">Register</LinkText>
</div>
</div>
</form>
</div>
</div>
);
}

export default Login;
102 changes: 52 additions & 50 deletions frontend/src/pages/Auth/Register.tsx
Original file line number Diff line number Diff line change
@@ -1,23 +1,51 @@
import InputWithLabel from "ui/InputWithLabel.tsx";
import LinkText from "ui/LinkText.tsx";
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import Button from "ui/Button.tsx";
import useRegister from "hooks/useRegister.ts";
import Checkbox from "ui/Checkbox.tsx";
import { Roles } from "models/User.ts";
import { SubmitHandler, useForm } from "react-hook-form";
import ErrorLabel from "ui/ErrorLabel.tsx";
import FormSubmitButton from "ui/FormSubmitButton.tsx";
import { z, ZodType } from "zod";
import { zodResolver } from "@hookform/resolvers/zod";

type RegisterForm = {
username: string;
password: string;
confirmPassword: string;
firstName: string;
lastName: string;
premium: boolean;
};

const passwordRegex = /^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*?&])[A-Za-z\d@$!%*?&]{8,}$/;

export const RegisterSchema: ZodType<RegisterForm> = z
.object({
username: z.string().min(4).max(30),
password: z.string().min(8).max(30).regex(passwordRegex),
confirmPassword: z.string().min(8).max(30).regex(passwordRegex),
firstName: z.string().min(3).max(30),
lastName: z.string().min(3).max(30),
premium: z.boolean(),
})
.refine((data) => data.password === data.confirmPassword, {
message: "Passwords do not match",
path: ["confirmPassword"], // path of error
});

function Register() {
const [username, setUsername] = useState("");
const [password, setPassword] = useState("");
const [passwordCheck, setCheckPassword] = useState("");
const [firstName, setFirstName] = useState("");
const [lastName, setLastName] = useState("");
const [premium, setPremium] = useState(false);
const { mutateAsync, isPending } = useRegister();
const { mutateAsync } = useRegister();
const {
register,
handleSubmit,
formState: { errors, isLoading, isValid },
} = useForm<RegisterForm>({ resolver: zodResolver(RegisterSchema) });

const navigate = useNavigate();

const onSubmit = async () => {
const onSubmit: SubmitHandler<RegisterForm> = async ({ username, password, firstName, lastName, premium }) => {
await mutateAsync({
username,
password,
Expand All @@ -34,64 +62,38 @@ function Register() {
Register Now
</h1>
<div className="block p-6 rounded-lg shadow-lg bg-white max-w-sm">
<form>
<form onSubmit={handleSubmit(onSubmit)}>
<div className="form-group mb-6">
<InputWithLabel
label="Username"
name="username"
type="text"
placeholder="Enter Username"
value={username}
onInput={(e) => setUsername(e.currentTarget.value)}
/>
<InputWithLabel label="Username" type="text" placeholder="Enter Username" {...register("username")} />
<ErrorLabel error={errors.username} />
</div>
<div className="form-group mb-6">
<InputWithLabel
label="Password"
type="password"
name="password"
placeholder="Enter Password"
value={password}
onInput={(e) => setPassword(e.currentTarget.value)}
/>
<InputWithLabel label="Password" type="password" placeholder="Enter Password" {...register("password")} />
<ErrorLabel error={errors.password} />
</div>
<div className="form-group mb-6">
<InputWithLabel
label="Repeat Password"
type="password"
name="repeat_password"
placeholder="Enter Password"
value={passwordCheck}
onInput={(e) => setCheckPassword(e.currentTarget.value)}
{...register("confirmPassword")}
/>
<ErrorLabel error={errors.confirmPassword} />
</div>
<div className="form-group mb-6">
<InputWithLabel
label="FirstName"
name="firstname"
type="text"
placeholder="Enter Firstname"
value={firstName}
onInput={(e) => setFirstName(e.currentTarget.value)}
/>
<InputWithLabel label="FirstName" type="text" placeholder="Enter Firstname" {...register("firstName")} />
<ErrorLabel error={errors.firstName} />
</div>
<div className="form-group mb-6">
<InputWithLabel
label="LastName"
name="lastname"
type="text"
value={lastName}
placeholder="Enter Lastname"
onInput={(e) => setLastName(e.currentTarget.value)}
/>
<InputWithLabel label="LastName" type="text" placeholder="Enter Lastname" {...register("lastName")} />
<ErrorLabel error={errors.lastName} />
</div>
<div>
Premium
<Checkbox onClick={() => setPremium((value) => !value)} checked={premium} />
<Checkbox {...register("premium")} />
<ErrorLabel error={errors.premium} />
</div>
<Button onClick={onSubmit} disabled={isPending} className="w-full">
Register
</Button>
<FormSubmitButton disabled={isLoading || !isValid}>Register</FormSubmitButton>
<div className="text-gray-800 mt-6 text-center">
Already have an account
<LinkText to="/login">Log In</LinkText>
Expand Down
Loading

0 comments on commit 5bc2616

Please sign in to comment.