From f09a4bc60d070af78ad74a4619672ccb0bbc54a4 Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Thu, 30 Nov 2023 11:19:18 +0700 Subject: [PATCH 1/2] Create hooks and implement login pages --- hotel-management/src/App.tsx | 27 ++++++-- .../src/hooks/authentication/useAccount.ts | 20 ++++++ .../src/hooks/authentication/useLogin.ts | 32 +++++++++ .../src/hooks/authentication/useLogout.ts | 22 ++++++ .../src/pages/Login/LoginForm.tsx | 69 +++++++++++++++++++ hotel-management/src/pages/Login/index.tsx | 32 +++++++++ hotel-management/src/pages/Login/styled.ts | 43 ++++++++++++ .../src/services/authenticationService.ts | 43 ++++++++++++ hotel-management/src/services/roomServices.ts | 26 ++----- hotel-management/tsconfig.json | 3 +- 10 files changed, 290 insertions(+), 27 deletions(-) create mode 100644 hotel-management/src/hooks/authentication/useAccount.ts create mode 100644 hotel-management/src/hooks/authentication/useLogin.ts create mode 100644 hotel-management/src/hooks/authentication/useLogout.ts create mode 100644 hotel-management/src/pages/Login/LoginForm.tsx create mode 100644 hotel-management/src/pages/Login/index.tsx create mode 100644 hotel-management/src/pages/Login/styled.ts create mode 100644 hotel-management/src/services/authenticationService.ts diff --git a/hotel-management/src/App.tsx b/hotel-management/src/App.tsx index c0785e7..e6edaa6 100644 --- a/hotel-management/src/App.tsx +++ b/hotel-management/src/App.tsx @@ -7,12 +7,15 @@ import { useEffect, useMemo, useReducer } from 'react'; // Components import AppLayout from './components/AppLayout'; import Toast from './components/Toast'; +import RouteProtected from '@component/RouteProtected'; +import RootLayout from '@component/RootLayout'; // Pages import User from './pages/User'; import Booking from './pages/Booking'; import Room from './pages/Room'; import NotFound from './pages/NotFound'; +import Login from '@page/Login'; // Constants import * as PATH from './constants/path'; @@ -75,13 +78,25 @@ function App() { - }> - } /> - } /> - } /> - } /> + }> + + + + } + > + } + /> + } /> + } /> + } /> + + } /> + } /> - } /> diff --git a/hotel-management/src/hooks/authentication/useAccount.ts b/hotel-management/src/hooks/authentication/useAccount.ts new file mode 100644 index 0000000..34c5c86 --- /dev/null +++ b/hotel-management/src/hooks/authentication/useAccount.ts @@ -0,0 +1,20 @@ +import { useQuery } from '@tanstack/react-query'; + +// Services +import { getCurrentAccount } from '@service/authenticationService'; + +const useAccount = () => { + let isAuthenticated: boolean = false; + const { data: account, isPending } = useQuery({ + queryKey: ['account'], + queryFn: getCurrentAccount, + }); + + if (account) { + isAuthenticated = account.role === 'authenticated'; + } + + return { isPending, account, isAuthenticated }; +}; + +export { useAccount }; diff --git a/hotel-management/src/hooks/authentication/useLogin.ts b/hotel-management/src/hooks/authentication/useLogin.ts new file mode 100644 index 0000000..b2c854a --- /dev/null +++ b/hotel-management/src/hooks/authentication/useLogin.ts @@ -0,0 +1,32 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useNavigate, useSearchParams } from 'react-router-dom'; +import toast from 'react-hot-toast'; + +// Services +import { login as loginFn } from '@service/authenticationService'; + +// Types +import { ILogin } from '@type/common'; + +const useLogin = () => { + const queryClient = useQueryClient(); + const navigate = useNavigate(); + const [searchParams] = useSearchParams(); + const redirectTo = searchParams.get('redirectTo') || null; + + const { mutate: login, isPending } = useMutation({ + mutationFn: ({ email, password }: ILogin) => loginFn({ email, password }), + onSuccess: (account) => { + queryClient.setQueryData(['account'], account.user); + redirectTo ? navigate(redirectTo) : navigate('/'); + }, + onError: (err) => { + console.error(err.message); + toast.error('Email or password not correct!'); + }, + }); + + return { login, isPending }; +}; + +export { useLogin }; diff --git a/hotel-management/src/hooks/authentication/useLogout.ts b/hotel-management/src/hooks/authentication/useLogout.ts new file mode 100644 index 0000000..66cc623 --- /dev/null +++ b/hotel-management/src/hooks/authentication/useLogout.ts @@ -0,0 +1,22 @@ +import { useMutation, useQueryClient } from '@tanstack/react-query'; +import { useNavigate } from 'react-router-dom'; + +// Services +import { logout as logoutFn } from '@service/authenticationService'; + +const useLogout = () => { + const navigate = useNavigate(); + const queryClient = useQueryClient(); + + const { mutate: logout, isPending } = useMutation({ + mutationFn: logoutFn, + onSuccess: () => { + queryClient.removeQueries(); + navigate('/login', { replace: true }); + }, + }); + + return { logout, isPending }; +}; + +export { useLogout }; diff --git a/hotel-management/src/pages/Login/LoginForm.tsx b/hotel-management/src/pages/Login/LoginForm.tsx new file mode 100644 index 0000000..9d0f6a2 --- /dev/null +++ b/hotel-management/src/pages/Login/LoginForm.tsx @@ -0,0 +1,69 @@ +import { useForm } from 'react-hook-form'; + +// Components +import Form from '@component/Form'; + +// Styled +import { FieldInput, StyledLoginForm } from './styled'; +import Button from '@commonStyle/Button'; + +// Constants +import { REQUIRED_FIELD_ERROR } from '@constant/formValidateMessage'; + +// Types +import { ILogin } from '@type/common'; + +// Hooks +import { useLogin } from '@hook/authentication/useLogin'; + +const LoginForm = () => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm(); + const { login, isPending } = useLogin(); + + const onSubmit = (data: ILogin) => { + login(data); + }; + + return ( + +
+ + + + + + + + +
+
+ ); +}; + +export default LoginForm; diff --git a/hotel-management/src/pages/Login/index.tsx b/hotel-management/src/pages/Login/index.tsx new file mode 100644 index 0000000..c443ab2 --- /dev/null +++ b/hotel-management/src/pages/Login/index.tsx @@ -0,0 +1,32 @@ +import { Navigate } from 'react-router-dom'; + +// Styled +import { LoginLayout, StyledLogo, TitleStyled } from './styled'; + +// Assets +import logo from '@assets/images/logo.png'; + +// Components +import LoginForm from './LoginForm'; + +// Hooks +import { useAccount } from '@hook/authentication/useAccount'; + +const Login = () => { + const { account } = useAccount(); + + if (account) { + return ; + } + + return ( + + + Log In + + + + ); +}; + +export default Login; diff --git a/hotel-management/src/pages/Login/styled.ts b/hotel-management/src/pages/Login/styled.ts new file mode 100644 index 0000000..9298f3b --- /dev/null +++ b/hotel-management/src/pages/Login/styled.ts @@ -0,0 +1,43 @@ +import styled from 'styled-components'; + +// Styled +import CommonInput from '@commonStyle/CommonInput'; + +const LoginLayout = styled.main` + height: 100vh; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + background-color: var(--background-login); +`; + +const StyledLogo = styled.img` + width: 250px; + height: 250px; +`; + +const TitleStyled = styled.h1` + font-size: var(--fs-md); +`; + +const StyledLoginForm = styled.div` + background-color: var(--form-color); + + border-radius: var(--radius-sm); + + display: flex; + flex-direction: column; + gap: 10px; + + margin-top: 20px; + padding: 30px 50px; +`; + +const FieldInput = styled.input` + ${CommonInput} + + width: 300px; +` + +export { LoginLayout, StyledLogo, TitleStyled, StyledLoginForm, FieldInput }; diff --git a/hotel-management/src/services/authenticationService.ts b/hotel-management/src/services/authenticationService.ts new file mode 100644 index 0000000..5a0e5f1 --- /dev/null +++ b/hotel-management/src/services/authenticationService.ts @@ -0,0 +1,43 @@ +import { ILogin } from '@type/common'; + +// Services +import supabase from './supabaseService'; + +const login = async ({ email, password }: ILogin) => { + const { data, error } = await supabase.auth.signInWithPassword({ + email, + password, + }); + + if (error) { + throw new Error(error.message); + } + + return data; +}; + +const getCurrentAccount = async () => { + const { data } = await supabase.auth.getSession(); + + if (!data.session) { + return null; + } + + const { data: accountData, error } = await supabase.auth.getUser(); + + if (error) { + throw new Error(error.message); + } + + return accountData.user; +}; + +const logout = async () => { + const { error } = await supabase.auth.signOut(); + + if (error) { + throw new Error(error.message); + } +}; + +export { login, logout, getCurrentAccount }; diff --git a/hotel-management/src/services/roomServices.ts b/hotel-management/src/services/roomServices.ts index e660376..91c76c4 100644 --- a/hotel-management/src/services/roomServices.ts +++ b/hotel-management/src/services/roomServices.ts @@ -1,6 +1,5 @@ // Types import { IRoom } from '@type/rooms'; -import { IDataState } from '@type/common'; // Services import supabase from './supabaseService'; @@ -127,28 +126,17 @@ const getRoomById = async (idRoom: string): Promise => { return data; }; + /** - * - * @returns + * Update status of room + * @param id The id of room need to be change status + * @param status The status of room */ -const getRoomsAvailable = async (): Promise => { - const { data, error } = await supabase - .from(ROOMS_TABLE) - .select('id, name, status'); - - if (error) { - console.error(error.message); - throw new Error(ERROR_FETCHING_ROOM); - } - - return data; -}; - const updateRoomStatus = async ( id: number, status: boolean -): Promise => { - const { error, status: statusConnection } = await supabase +): Promise => { + const { error} = await supabase .from(ROOMS_TABLE) .update({ status }) .eq('id', id); @@ -158,7 +146,6 @@ const updateRoomStatus = async ( throw new Error(ERROR_UPDATE_ROOM); } - return statusConnection; }; export { @@ -166,7 +153,6 @@ export { updateRoom, createRoom, deleteRoom, - getRoomsAvailable, getRoomById, updateRoomStatus, }; diff --git a/hotel-management/tsconfig.json b/hotel-management/tsconfig.json index ba79ba5..284f24e 100644 --- a/hotel-management/tsconfig.json +++ b/hotel-management/tsconfig.json @@ -32,7 +32,8 @@ "@component/*": ["./src/components/*"], "@commonStyle/*": ["./src/commons/styles/*"], "@type/*": ["./src/types/*"], - "@page/*": ["./src/pages/*"] + "@page/*": ["./src/pages/*"], + "@assets/*": ["./src/assets/*"], } }, "include": ["src", "test"], From b5589bba0fbbfaa5c3bcfdf51b4e0961f01c872e Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Thu, 30 Nov 2023 11:21:37 +0700 Subject: [PATCH 2/2] Format code style --- hotel-management/src/hooks/authentication/useLogin.ts | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/hotel-management/src/hooks/authentication/useLogin.ts b/hotel-management/src/hooks/authentication/useLogin.ts index b2c854a..5529169 100644 --- a/hotel-management/src/hooks/authentication/useLogin.ts +++ b/hotel-management/src/hooks/authentication/useLogin.ts @@ -18,7 +18,9 @@ const useLogin = () => { mutationFn: ({ email, password }: ILogin) => loginFn({ email, password }), onSuccess: (account) => { queryClient.setQueryData(['account'], account.user); - redirectTo ? navigate(redirectTo) : navigate('/'); + redirectTo + ? navigate(redirectTo) + : navigate('/'); }, onError: (err) => { console.error(err.message);