Skip to content

Commit

Permalink
Merge pull request #94 from SEP4Y-S24/dev
Browse files Browse the repository at this point in the history
Final product
  • Loading branch information
ricardofernandes21 authored May 30, 2024
2 parents bdcc670 + 76cc292 commit 49d97d2
Show file tree
Hide file tree
Showing 13 changed files with 203 additions and 156 deletions.
4 changes: 3 additions & 1 deletion src/features/auth/api/login.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ export const loginWithEmailAndPassword = (data: LoginPropsRequest): Promise<User
return axios.post(`${baseURL}/UserService/users/login`, data, axiosConfigAuth)
.then(response => response.data)
.catch(error => {
throw error.response.data;
error = error.response.data;
console.log("error: " + error)
throw error;
});
};
7 changes: 7 additions & 0 deletions src/features/auth/routes/AuthenticationConstants.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export const enum ERROR_HANDLING {
INVALID_EMAIL_FORMAT = 'Invalid email format. Please insert valid email.',
INVALID_PASSWORD_FORMAT = 'Password needs to be at least 1 character long.',
INVALID_NAME_FORMAT = 'Name needs to be at least 1 character long.',
EMAIL_REQUIRED = 'Email is required',
FAIL_LOADING_POKEMONS = "Error loading pokemons.",
}
85 changes: 51 additions & 34 deletions src/features/auth/routes/Login.tsx
Original file line number Diff line number Diff line change
@@ -1,29 +1,31 @@

import {Form, Link} from 'react-router-dom';
import { Form, Link } from 'react-router-dom';
import { Layout } from '../components/Layout';
import Heading from "../../../components/Elements/Headings/Heading";
import InputField from "../../../components/Form/InputField";
import Button from "../../../components/Elements/Button";
import * as z from 'zod';
import React, {useState} from "react";
import {useLogin} from "../../../lib/auth";
import {LoginPropsRequest} from "../types";
import React, { useState } from "react";
import { useLogin } from "../../../lib/auth";
import { LoginPropsRequest } from "../types";
import SpinnerComponent from "../../spinner/SpinnerComponent";
import {useNavigate} from "react-router";
import { useNavigate } from "react-router";
import PopUp from "../../../components/Elements/PopUp/PopUp";
import {ERROR_HANDLING} from "./AuthenticationConstants";


const schema = z.object({
email: z.string().min(1, 'Email is required').email('Invalid email format. Please insert valid email.'),
password: z.string().min(1, 'Password is required'),
email: z.string().min(1, ERROR_HANDLING.EMAIL_REQUIRED).email(ERROR_HANDLING.INVALID_EMAIL_FORMAT),
password: z.string().min(1, ERROR_HANDLING.INVALID_PASSWORD_FORMAT),
});


export const Login = () => {
const login = useLogin();
const [values, setValues] = useState<LoginPropsRequest>({ email: '', password: '' });
const [errors, setErrors] = useState<{ [key: string]: string }>({});
const navigate = useNavigate();
const [isSubmitting, setIsSubmitting] = useState(false);
const [showPopup, setShowPopup] = useState(false);
const [popupMessage, setPopupMessage] = useState("");

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
const { name, value } = e.target;
Expand All @@ -36,61 +38,76 @@ export const Login = () => {
setIsSubmitting(true);
try {
schema.parse(values);
const request:LoginPropsRequest = {
const request: LoginPropsRequest = {
email: values.email,
password: values.password
}
login.mutate(request, {
onSuccess: () => {
navigate('/');
},
onError: (error) => {
// @ts-ignore
setPopupMessage(error);
setShowPopup(true);
setIsSubmitting(false);
}
} );
});
} catch (error) {
let displayedError:string = '';
setIsSubmitting(false);
if (error instanceof z.ZodError) {
const fieldErrors: { [key: string]: string } = {};
error.errors.forEach(err => {
const path = err.path.join('.');
fieldErrors[path] = err.message;
displayedError = err.message;
});
setErrors(fieldErrors);
setPopupMessage(displayedError);
setShowPopup(true); // Show PopUp with validation errors
}
}
};


return (
<Layout>

<Form onSubmit={handleSubmit}>
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<Heading text={"Sign in to your account"} type={"heading1"} className={"text-center"}/>
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<div className="flex min-h-full flex-1 flex-col justify-center px-6 py-12 lg:px-8">
<div className="sm:mx-auto sm:w-full sm:max-w-sm">
<Heading text={"Sign in to your account"} type={"heading1"} className={"text-center"} />
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<div>
<InputField type={"email"} id={"email"} labelText={"Email address"} name={"email"} onChange={handleChange}
error={errors.email}/>
<InputField type={"email"} id={"email"} labelText={"Email address"} name={"email"} onChange={handleChange}/>
</div>
<div>
<InputField type={"password"} id={"password"} labelText={"Password"} name={"password"} onChange={handleChange}
error={errors.password}/>
</div>
<InputField type={"password"} id={"password"} labelText={"Password"} name={"password"} onChange={handleChange}/>
</div>

<div className={"pt-5"}>
{isSubmitting ? (
<SpinnerComponent />
) : (
<Button text={"Sign in"} styleType={"info"} className={"w-full justify-center"} type="submit"/>
)}
<SpinnerComponent />
) : (
<Button text={"Sign in"} styleType={"info"} className={"w-full justify-center"} type="submit" />
)}
</div>
<p className="mt-10 text-center text-sm text-gray-500">
Not a member?{' '}
<Link to={'/auth/register'} className={"text-primaryColor"}>Register a new account here!</Link>
</p>
<p className="mt-10 text-center text-sm text-gray-500">
Not a member?{' '}
<Link to={'/auth/register'} className={"text-primaryColor"}>Register a new account here!</Link>
</p>
</div>
</div>
</div>
</Form>

{showPopup && (
<PopUp
title="Error"
textAlert={popupMessage}
type="danger"
buttonCancelText="Close"
onCancel={() => setShowPopup(false)} // Close PopUp on cancel
/>
)}
</Layout>
);
};
};
76 changes: 40 additions & 36 deletions src/features/auth/routes/Register.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,14 @@ import { useRegister } from "../../../lib/auth";
import { useQuery } from "@tanstack/react-query";
import SpinnerComponent from "../../spinner/SpinnerComponent";
import { CreateUserPropsRequest } from "../types";
import {useNavigate} from "react-router";
import { useNavigate } from "react-router";
import PopUp from "../../../components/Elements/PopUp/PopUp";
import {ERROR_HANDLING} from "./AuthenticationConstants";

const schema = z.object({
email: z.string().min(1, 'Email is required').email('Invalid email format. Please insert valid email.'),
password: z.string().min(1, 'Password needs to be at least 1 character long.'),
name: z.string().min(1, 'Name is required.')
email: z.string().min(1, ERROR_HANDLING.INVALID_EMAIL_FORMAT),
password: z.string().min(1, ERROR_HANDLING.INVALID_PASSWORD_FORMAT),
name: z.string().min(1, ERROR_HANDLING.INVALID_NAME_FORMAT),
});

type RegisterValues = {
Expand All @@ -32,20 +34,18 @@ const SLIDE_COUNT = 6
const SLIDES = Array.from(Array(SLIDE_COUNT).keys())

export const Register = () => {
// for register form
const [values, setValues] = useState<RegisterValues>({ email: '', password: '', name: '', avatarId: '1' });
const [errors, setErrors] = useState<{ [key: string]: string }>({});
const [isSubmitting, setIsSubmitting] = useState(false);
// eslint-disable-next-line @typescript-eslint/no-unused-vars
const [pokemonError, setPokemonError] = useState<string>('');
const [showPopup, setShowPopup] = useState(false); // State to manage PopUp visibility
const [popupMessage, setPopupMessage] = useState(""); // State to manage PopUp message
const [emailsList, setEmailsList] = useState<string[]>([]);
const navigate = useNavigate();

// for pokemon image slider
const [pokemonList, setPokemonList] = useState<Pokemon[]>([]);
const handlePokemonSelect = (pokemonId: string) => {
handleChange({ target: { name: 'avatarId', value: pokemonId } } as React.ChangeEvent<HTMLInputElement>)
};
const navigate = useNavigate();

const { isLoading, error, data } = useQuery({
queryKey: ['pokemonList'],
Expand All @@ -56,13 +56,6 @@ export const Register = () => {
if (data) {
setPokemonList(data);
}
const emails: string[] = [
"[email protected]",
"[email protected]"
];

setEmailsList(emails);

}, [data]);

const handleChange = (e: React.ChangeEvent<HTMLInputElement>) => {
Expand All @@ -79,7 +72,11 @@ export const Register = () => {
schema.parse(values);

if (emailsList.includes(values.email)) {
setErrors({ ...errors, email: 'Email is already taken' });
// @ts-ignore
setErrors({ ...errors, email: register.error });
// @ts-ignore
setPopupMessage(register.error);
setShowPopup(true); // Show PopUp with error message
setIsSubmitting(false);
return;
}
Expand All @@ -95,21 +92,28 @@ export const Register = () => {
onSuccess: () => {
navigate('/');
},
onError: () => {
onError: (error) => {
// @ts-ignore
setPopupMessage(error);
setShowPopup(true); // Show PopUp with error message
setIsSubmitting(false);
}
} );
});

setEmailsList([...emailsList, values.email]);
} catch (error) {
let displayedError:string = '';
setIsSubmitting(false);
if (error instanceof z.ZodError) {
const fieldErrors: { [key: string]: string } = {};
error.errors.forEach(err => {
const path = err.path.join('.');
fieldErrors[path] = err.message;
displayedError = err.message;
});
setErrors(fieldErrors);
setPopupMessage(displayedError);
setShowPopup(true); // Show PopUp with validation errors
}
}
};
Expand All @@ -123,34 +127,24 @@ export const Register = () => {
</div>
<div className="mt-10 sm:mx-auto sm:w-full sm:max-w-sm">
<div>
<InputField type={"text"} id={"name"} labelText={"Name"} name={"name"}
onChange={handleChange}
error={errors.name} />
<InputField type={"text"} id={"name"} labelText={"Name"} name={"name"} onChange={handleChange} />
</div>
<div>
<InputField type={"email"} id={"email"} labelText={"Email address"} name={"email"}
onChange={handleChange}
error={errors.email} />
<InputField type={"email"} id={"email"} labelText={"Email address"} name={"email"} onChange={handleChange}/>
</div>
<div>
<InputField type={"password"} id={"password"} labelText={"Password"} name={"password"}
onChange={handleChange}
error={errors.password} />
<InputField type={"password"} id={"password"} labelText={"Password"} name={"password"} onChange={handleChange}/>
</div>
<label
className="block my-2 text-base font-normal text-dark"> Choose your avatar:
</label>
<label className="block my-2 text-base font-normal text-dark"> Choose your avatar:</label>
{isLoading && <SpinnerComponent />}
{error && <p className={"text-red-500"}>{"Error loading pokemons"}</p>}
{data && <EmblaCarousel data={pokemonList} slides={SLIDES} options={OPTIONS}
onSelect={handlePokemonSelect} />}
{error && <p className={"text-red-500"}>{ERROR_HANDLING.FAIL_LOADING_POKEMONS}</p>}
{data && <EmblaCarousel data={pokemonList} slides={SLIDES} options={OPTIONS} onSelect={handlePokemonSelect} />}

<div className={"pt-5"}>
{isSubmitting ? (
<SpinnerComponent />
) : (
<Button text={"Register"} styleType={"info"} className={"w-full justify-center"}
type="submit" />
<Button text={"Register"} styleType={"info"} className={"w-full justify-center"} type="submit" />
)}
</div>
<p className="mt-10 text-center text-sm text-gray-500">
Expand All @@ -160,6 +154,16 @@ export const Register = () => {
</div>
</div>
</Form>

{showPopup && (
<PopUp
title="Error"
textAlert={popupMessage}
type="danger"
buttonCancelText="Close"
onCancel={() => setShowPopup(false)}
/>
)}
</Layout>
);
};
2 changes: 1 addition & 1 deletion src/features/calendar/components/task/TaskForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -178,7 +178,7 @@ export const TaskForm: React.FC<TaskFormProps> = ({ selectedTask , mode}) => {
</div>
</LocalizationProvider>
</div>
<Heading text={"Select category.ts"} type={"heading3"} className={"mt-2"}/>
<Heading text={"Select category"} type={"heading3"} className={"mt-2"}/>
<div>
{defaultCategories.map((category) => (
<CategoryTag
Expand Down
Loading

0 comments on commit 49d97d2

Please sign in to comment.