From 4aae26e99424ec83369179dff452870a3306f0e0 Mon Sep 17 00:00:00 2001 From: Loi Phan Date: Mon, 20 Nov 2023 17:52:51 +0700 Subject: [PATCH] Add user and rooms to global state --- hotel-management/src/App.tsx | 62 +++++++-- .../src/contexts/UserRoomAvailableContext.ts | 130 ++++++++++++++++++ .../src/hooks/rooms/useCreateRoom.ts | 10 +- .../src/hooks/rooms/useUpdateRoom.ts | 10 +- .../src/hooks/useUserRoomAvailable.ts | 16 +++ .../src/hooks/users/useCreateUser.ts | 10 +- .../src/hooks/users/useUpdateUser.ts | 10 +- hotel-management/src/pages/Dashboard.tsx | 6 + hotel-management/src/pages/User/UserRow.tsx | 7 +- hotel-management/src/pages/User/UserTable.tsx | 4 +- hotel-management/src/services/roomServices.ts | 52 +++++-- hotel-management/src/services/userServices.ts | 42 +++++- hotel-management/src/types/common.ts | 7 +- hotel-management/src/types/supabase.ts | 5 +- hotel-management/src/types/users.ts | 1 + 15 files changed, 329 insertions(+), 43 deletions(-) create mode 100644 hotel-management/src/contexts/UserRoomAvailableContext.ts create mode 100644 hotel-management/src/hooks/useUserRoomAvailable.ts diff --git a/hotel-management/src/App.tsx b/hotel-management/src/App.tsx index d0c2887..cb03a8b 100644 --- a/hotel-management/src/App.tsx +++ b/hotel-management/src/App.tsx @@ -13,30 +13,66 @@ import NotFound from './pages/NotFound'; // Constants import * as PATH from './constants/path'; import Toast from './components/Toast'; +import { + UserRoomAvailableContext, + initialState, + reducer, +} from '@context/UserRoomAvailableContext'; +import { useEffect, useReducer } from 'react'; +import { getUserNotBooked } from '@service/userServices'; +import { getRoomsAvailable } from '@service/roomServices'; const queryClient = new QueryClient({ defaultOptions: { queries: { staleTime: 60 * 1000, - } - } + }, + }, }); function App() { + const [{ usersAvailable, roomsAvailable }, dispatch] = useReducer( + reducer, + initialState + ); + + // Init list user and room available + useEffect(() => { + const load = async () => { + const tempUser = await getUserNotBooked(); + + if (tempUser) { + dispatch({ type: 'initUser', payload: tempUser }); + } + + const tempRoom = await getRoomsAvailable(); + + if (tempRoom) { + dispatch({ type: 'initRoom', payload: tempRoom }); + } + }; + + load(); + }, []); + return ( - - - }> - } /> - } /> - } /> - } /> - - } /> - - + + + + }> + } /> + } /> + } /> + } /> + + } /> + + + diff --git a/hotel-management/src/contexts/UserRoomAvailableContext.ts b/hotel-management/src/contexts/UserRoomAvailableContext.ts new file mode 100644 index 0000000..40d1a9e --- /dev/null +++ b/hotel-management/src/contexts/UserRoomAvailableContext.ts @@ -0,0 +1,130 @@ +import { IDataState } from '@type/common'; +import { Dispatch, createContext } from 'react'; + +interface IUserRoomState { + usersAvailable: IDataState[]; + roomsAvailable: IDataState[]; +} + +type TAction = + | 'initRoom' + | 'initUser' + | 'addRoom' + | 'removeRoom' + | 'addUser' + | 'removeUser' + | 'updateUserName' + | 'updateRoomName'; + +interface IAction { + type: TAction; + payload: IDataState[]; +} + +interface IUserRoomAvailableContext { + roomsAvailable?: IDataState[]; + usersAvailable?: IDataState[]; + dispatch?: Dispatch; +} + +const UserRoomAvailableContext = createContext({}); + +const initialState: IUserRoomState = { + usersAvailable: [], + roomsAvailable: [], +}; + +const reducer = (state: IUserRoomState, action: IAction) => { + switch (action.type) { + case 'initRoom': + return { + ...state, + roomsAvailable: action.payload, + }; + case 'initUser': + return { + ...state, + usersAvailable: action.payload, + }; + case 'addUser': { + const tempArr = state.usersAvailable; + const itemExist = state.usersAvailable.find( + (item) => item.id === action.payload[0].id + ); + + if (!itemExist) { + tempArr.push(action.payload[0]); + } + + return { + ...state, + usersAvailable: tempArr, + }; + } + case 'updateUserName': { + const tempArr = state.usersAvailable; + + if (action.payload[0].name) { + const indexItemUpdate = tempArr.findIndex( + (item) => item.id === action.payload[0].id + ); + + tempArr[indexItemUpdate].name = action.payload[0].name; + } + + return { + ...state, + usersAvailable: tempArr, + }; + } + case 'removeUser': + return { + ...state, + usersAvailable: state.usersAvailable.filter( + (item) => item.id !== action.payload[0].id + ), + }; + case 'addRoom': { + const tempArr = state.roomsAvailable; + const itemExist = state.roomsAvailable.find( + (item) => item.id === action.payload[0].id + ); + + if (!itemExist) { + tempArr.push(action.payload[0]); + } + + return { + ...state, + roomsAvailable: tempArr, + }; + } + case 'updateRoomName': { + const tempArr = state.roomsAvailable; + + if (action.payload[0].name) { + const indexItemUpdate = tempArr.findIndex( + (item) => item.id === action.payload[0].id + ); + + tempArr[indexItemUpdate].name = action.payload[0].name; + } + + return { + ...state, + roomsAvailable: tempArr, + }; + } + case 'removeRoom': + return { + ...state, + roomsAvailable: state.roomsAvailable.filter( + (item) => item.id !== action.payload[0].id + ), + }; + default: + throw new Error('Action unknown'); + } +}; + +export { UserRoomAvailableContext, reducer, initialState }; diff --git a/hotel-management/src/hooks/rooms/useCreateRoom.ts b/hotel-management/src/hooks/rooms/useCreateRoom.ts index 570992f..7578bd6 100644 --- a/hotel-management/src/hooks/rooms/useCreateRoom.ts +++ b/hotel-management/src/hooks/rooms/useCreateRoom.ts @@ -6,6 +6,7 @@ import { createRoom as createRoomFn } from '@service/roomServices'; // Constants import { ADD_SUCCESS } from '@constant/messages'; +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; /** * Create room on database @@ -13,12 +14,19 @@ import { ADD_SUCCESS } from '@constant/messages'; */ const useCreateRoom = () => { const queryClient = useQueryClient(); + const { dispatch } = useUserRoomAvailable(); const { mutate: createRoom, isPending: isCreating } = useMutation({ mutationFn: createRoomFn, - onSuccess: () => { + onSuccess: (room) => { toast.success(ADD_SUCCESS); queryClient.invalidateQueries({ queryKey: ['rooms'] }); + + // Add room available to global state + dispatch!({ + type: 'addRoom', + payload: [{ id: room.id, name: room.name }], + }); }, onError: (err) => toast.error(err.message), }); diff --git a/hotel-management/src/hooks/rooms/useUpdateRoom.ts b/hotel-management/src/hooks/rooms/useUpdateRoom.ts index ab6bdb4..7b1bf15 100644 --- a/hotel-management/src/hooks/rooms/useUpdateRoom.ts +++ b/hotel-management/src/hooks/rooms/useUpdateRoom.ts @@ -6,6 +6,7 @@ import { updateRoom as updateRoomFn } from '@service/roomServices'; // Constants import { UPDATE_SUCCESS } from '@constant/messages'; +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; /** * Update room from database @@ -13,12 +14,19 @@ import { UPDATE_SUCCESS } from '@constant/messages'; */ const useUpdateRoom = () => { const queryClient = useQueryClient(); + const { dispatch } = useUserRoomAvailable(); const { mutate: updateRoom, isPending: isUpdating } = useMutation({ mutationFn: updateRoomFn, - onSuccess: () => { + onSuccess: (room) => { toast.success(UPDATE_SUCCESS); queryClient.invalidateQueries({ queryKey: ['rooms'] }); + + // Update name room in global state + dispatch!({ + type: 'updateRoomName', + payload: [{ id: room.id, name: room.name }], + }); }, onError: (err) => toast.error(err.message), }); diff --git a/hotel-management/src/hooks/useUserRoomAvailable.ts b/hotel-management/src/hooks/useUserRoomAvailable.ts new file mode 100644 index 0000000..18ae1e3 --- /dev/null +++ b/hotel-management/src/hooks/useUserRoomAvailable.ts @@ -0,0 +1,16 @@ +import { UserRoomAvailableContext } from '@context/UserRoomAvailableContext'; +import { useContext } from 'react'; + +const useUserRoomAvailable = () => { + const context = useContext(UserRoomAvailableContext); + + if (context === undefined) { + throw new Error( + 'UserRoomAvailableContext was used outside of UserRoomAvailableProvider' + ); + } + + return context; +}; + +export { useUserRoomAvailable }; diff --git a/hotel-management/src/hooks/users/useCreateUser.ts b/hotel-management/src/hooks/users/useCreateUser.ts index 95e3b35..7d7c8e3 100644 --- a/hotel-management/src/hooks/users/useCreateUser.ts +++ b/hotel-management/src/hooks/users/useCreateUser.ts @@ -6,6 +6,7 @@ import { createUser as createUserFn } from '@service/userServices'; // Constants import { ADD_SUCCESS } from '@constant/messages'; +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; /** * Create user in database @@ -13,12 +14,19 @@ import { ADD_SUCCESS } from '@constant/messages'; */ const useCreateUser = () => { const queryClient = useQueryClient(); + const { dispatch } = useUserRoomAvailable(); const { mutate: createUser, isPending: isCreating } = useMutation({ mutationFn: createUserFn, - onSuccess: () => { + onSuccess: (user) => { toast.success(ADD_SUCCESS); queryClient.invalidateQueries({ queryKey: ['users'] }); + + // Add user to global statement + dispatch!({ + type: 'addUser', + payload: [{ id: user.id, name: user.name }], + }); }, onError: (err) => toast.error(err.message), }); diff --git a/hotel-management/src/hooks/users/useUpdateUser.ts b/hotel-management/src/hooks/users/useUpdateUser.ts index a06b5aa..6603c8d 100644 --- a/hotel-management/src/hooks/users/useUpdateUser.ts +++ b/hotel-management/src/hooks/users/useUpdateUser.ts @@ -6,6 +6,7 @@ import { updateUser as updateUserFn } from '@service/userServices'; // Messages import { UPDATE_SUCCESS } from '@constant/messages'; +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; /** * Update user from database @@ -13,12 +14,19 @@ import { UPDATE_SUCCESS } from '@constant/messages'; */ const useUpdateUser = () => { const queryClient = useQueryClient(); + const { dispatch } = useUserRoomAvailable(); const { mutate: updateUser, isPending: isUpdating } = useMutation({ mutationFn: updateUserFn, - onSuccess: () => { + onSuccess: (user) => { toast.success(UPDATE_SUCCESS); queryClient.invalidateQueries({ queryKey: ['users'] }); + + // Update name in global state + dispatch!({ + type: 'updateUserName', + payload: [{ id: user.id, name: user.name }], + }); }, onError: (err) => toast.error(err.message), }); diff --git a/hotel-management/src/pages/Dashboard.tsx b/hotel-management/src/pages/Dashboard.tsx index 46e053e..94d877b 100644 --- a/hotel-management/src/pages/Dashboard.tsx +++ b/hotel-management/src/pages/Dashboard.tsx @@ -1,3 +1,4 @@ +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; import styled from 'styled-components'; const StyledDashboard = styled.main` @@ -5,6 +6,11 @@ const StyledDashboard = styled.main` `; const Dashboard = () => { + const { roomsAvailable, usersAvailable } = useUserRoomAvailable(); + + console.log('Rooms Available: ', roomsAvailable); + console.log('Users available: ', usersAvailable); + return (

This page currently still develop. Please try again later!

diff --git a/hotel-management/src/pages/User/UserRow.tsx b/hotel-management/src/pages/User/UserRow.tsx index 484ec8a..0ca4aca 100644 --- a/hotel-management/src/pages/User/UserRow.tsx +++ b/hotel-management/src/pages/User/UserRow.tsx @@ -14,13 +14,18 @@ interface IUserRow { } const UserRow = ({ user }: IUserRow) => { - const { id, name, phone } = user; + const { id, name, phone, isBooked } = user; return (
{id}
{name}
{phone}
+
{ + isBooked + ? 'Yes' + : 'No' + }
diff --git a/hotel-management/src/pages/User/UserTable.tsx b/hotel-management/src/pages/User/UserTable.tsx index 912bbc0..f3179a8 100644 --- a/hotel-management/src/pages/User/UserTable.tsx +++ b/hotel-management/src/pages/User/UserTable.tsx @@ -24,7 +24,7 @@ import Spinner from '@commonStyle/Spinner'; import { useUsers } from '@hook/users/useUsers'; const UserTable = () => { - const columnName = ['Id', 'Name', 'Phone']; + const columnName = ['Id', 'Name', 'Phone', 'Is Booked']; const { isLoading, users } = useUsers(); const renderUserRow = useCallback( @@ -51,7 +51,7 @@ const UserTable = () => { {users && users.length ? ( - +
data={users} render={renderUserRow} />
diff --git a/hotel-management/src/services/roomServices.ts b/hotel-management/src/services/roomServices.ts index f40dc0e..fa96d94 100644 --- a/hotel-management/src/services/roomServices.ts +++ b/hotel-management/src/services/roomServices.ts @@ -3,6 +3,7 @@ import { IRoom } from '@type/rooms'; // Services import supabase from './supabaseService'; +import { IDataState } from '@type/common'; // Constants const ROOMS_TABLE = 'rooms'; @@ -34,47 +35,70 @@ const getAllRooms = async ( return data; }; - /** * Update room into database * @param room Room object need to be updated */ -const updateRoom = async (room: IRoom): Promise => { - const { error } = await supabase.from(ROOMS_TABLE).update(room).eq("id", room.id); +const updateRoom = async (room: IRoom): Promise => { + const { data, error } = await supabase + .from(ROOMS_TABLE) + .update(room) + .eq('id', room.id) + .select() + .single(); - if(error) { + if (error) { console.error(error.message); throw new Error(ERROR_UPDATE_ROOM); } + + return data; }; /** * Add room to database * @param room The room object need to be add */ -const createRoom = async (room: IRoom): Promise => { - // Set default status - room.status = false; +const createRoom = async (room: IRoom): Promise => { + const { data, error } = await supabase + .from(ROOMS_TABLE) + .insert(room) + .select() + .single(); - const { error } = await supabase.from(ROOMS_TABLE).insert([room]); - - if(error) { + if (error) { console.error(error.message); throw new Error(ERROR_CREATE_ROOM); } + + return data; }; /** * Delete room in database * @param idRoom The id of room need to delete */ -const deleteRoom = async(idRoom: number) => { +const deleteRoom = async (idRoom: number) => { const { error } = await supabase.from(ROOMS_TABLE).delete().eq('id', idRoom); - if(error) { + if (error) { console.error(error.message); throw new Error(ERROR_DELETE_ROOM); } -} +}; -export { getAllRooms, updateRoom, createRoom, deleteRoom }; +const getRoomsAvailable = async (): Promise => { + const { data, error } = await supabase + .from(ROOMS_TABLE) + .select('id, name') + .eq('status', false); + + if (error) { + console.error(error.message); + throw new Error(ERROR_FETCHING); + } + + return data; +}; + +export { getAllRooms, updateRoom, createRoom, deleteRoom, getRoomsAvailable }; diff --git a/hotel-management/src/services/userServices.ts b/hotel-management/src/services/userServices.ts index a2903ff..6578517 100644 --- a/hotel-management/src/services/userServices.ts +++ b/hotel-management/src/services/userServices.ts @@ -3,6 +3,7 @@ import { IUser } from '@type/users'; // Services import supabase from './supabaseService'; +import { IDataState } from '@type/common'; const USERS_TABLE = 'users'; const ERROR_FETCHING = "Users can't be loaded!"; @@ -13,26 +14,39 @@ const ERROR_UPDATE_USER = "Can't update user!"; * Create user to the database * @param user The user object need to be created */ -const createUser = async (user: IUser): Promise => { - const { error } = await supabase.from(USERS_TABLE).insert([user]); +const createUser = async (user: IUser): Promise => { + const { data, error } = await supabase + .from(USERS_TABLE) + .insert(user) + .select() + .single(); - if(error) { + if (error) { console.error(error.message); throw new Error(ERROR_CREATE_USER); } + + return data; }; /** * Update the user to the database * @param user The user object need to be updated */ -const updateUser = async (user: IUser): Promise => { - const { error } = await supabase.from(USERS_TABLE).update(user).eq("id", user.id); +const updateUser = async (user: IUser): Promise => { + const { data, error } = await supabase + .from(USERS_TABLE) + .update(user) + .eq('id', user.id) + .select() + .single(); - if(error) { + if (error) { console.error(error.message); throw new Error(ERROR_UPDATE_USER); } + + return data; }; /** @@ -61,4 +75,18 @@ const getAllUsers = async ( return data; }; -export { updateUser, createUser, getAllUsers }; +const getUserNotBooked = async (): Promise => { + const { data, error } = await supabase + .from(USERS_TABLE) + .select('id, name') + .eq('isBooked', false); + + if (error) { + console.error(error.message); + throw new Error(ERROR_FETCHING); + } + + return data; +}; + +export { updateUser, createUser, getAllUsers, getUserNotBooked }; diff --git a/hotel-management/src/types/common.ts b/hotel-management/src/types/common.ts index 67e9609..825c8f3 100644 --- a/hotel-management/src/types/common.ts +++ b/hotel-management/src/types/common.ts @@ -1,3 +1,8 @@ type Nullable = T | null; -export type { Nullable }; +interface IDataState { + id: number; + name: string; +} + +export type { Nullable, IDataState }; diff --git a/hotel-management/src/types/supabase.ts b/hotel-management/src/types/supabase.ts index b5203da..f19cbb3 100644 --- a/hotel-management/src/types/supabase.ts +++ b/hotel-management/src/types/supabase.ts @@ -65,7 +65,7 @@ export interface Database { id?: number name: string price: number - status: boolean + status?: boolean } Update: { id?: number @@ -78,16 +78,19 @@ export interface Database { users: { Row: { id: number + isBooked: boolean name: string phone: string } Insert: { id?: number + isBooked?: boolean name: string phone: string } Update: { id?: number + isBooked?: boolean name?: string phone?: string } diff --git a/hotel-management/src/types/users.ts b/hotel-management/src/types/users.ts index 1725420..0f9517b 100644 --- a/hotel-management/src/types/users.ts +++ b/hotel-management/src/types/users.ts @@ -2,6 +2,7 @@ interface IUser { id: number; name: string; phone: string; + isBooked: boolean; } export type { IUser };