diff --git a/hotel-management/src/App.tsx b/hotel-management/src/App.tsx index d0c2887..96ae698 100644 --- a/hotel-management/src/App.tsx +++ b/hotel-management/src/App.tsx @@ -13,30 +13,73 @@ 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, useMemo, useReducer } from 'react'; + +// Services +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(); + }, []); + + const store = useMemo(() => { + return { roomsAvailable, usersAvailable, dispatch }; + }, [roomsAvailable, usersAvailable]); + return ( - - - }> - } /> - } /> - } /> - } /> - - } /> - - + + + + }> + } + /> + } /> + } /> + } /> + + } /> + + + diff --git a/hotel-management/src/contexts/UserRoomAvailableContext.ts b/hotel-management/src/contexts/UserRoomAvailableContext.ts new file mode 100644 index 0000000..01c91ce --- /dev/null +++ b/hotel-management/src/contexts/UserRoomAvailableContext.ts @@ -0,0 +1,139 @@ +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..045cd10 100644 --- a/hotel-management/src/hooks/rooms/useCreateRoom.ts +++ b/hotel-management/src/hooks/rooms/useCreateRoom.ts @@ -7,18 +7,28 @@ import { createRoom as createRoomFn } from '@service/roomServices'; // Constants import { ADD_SUCCESS } from '@constant/messages'; +// Hooks +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; + /** * Create room on database * @returns The boolean of isCreating and create room function */ 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..6b6d6e6 100644 --- a/hotel-management/src/hooks/rooms/useUpdateRoom.ts +++ b/hotel-management/src/hooks/rooms/useUpdateRoom.ts @@ -7,18 +7,28 @@ import { updateRoom as updateRoomFn } from '@service/roomServices'; // Constants import { UPDATE_SUCCESS } from '@constant/messages'; +// Hooks +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; + /** * Update room from database * @returns The status of updating and updateRoom function */ 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..e362b9d --- /dev/null +++ b/hotel-management/src/hooks/useUserRoomAvailable.ts @@ -0,0 +1,18 @@ +import { useContext } from 'react'; + +// Contexts +import { UserRoomAvailableContext } from '@context/UserRoomAvailableContext'; + +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..7f89db0 100644 --- a/hotel-management/src/hooks/users/useCreateUser.ts +++ b/hotel-management/src/hooks/users/useCreateUser.ts @@ -7,18 +7,28 @@ import { createUser as createUserFn } from '@service/userServices'; // Constants import { ADD_SUCCESS } from '@constant/messages'; +// Hooks +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; + /** * Create user in database * @returns The status of creating users and createUser function */ 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..3c4a029 100644 --- a/hotel-management/src/hooks/users/useUpdateUser.ts +++ b/hotel-management/src/hooks/users/useUpdateUser.ts @@ -7,18 +7,28 @@ import { updateUser as updateUserFn } from '@service/userServices'; // Messages import { UPDATE_SUCCESS } from '@constant/messages'; +// Hooks +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; + /** * Update user from database * @returns The status of updating user and updateUser function */ 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..592fd32 100644 --- a/hotel-management/src/pages/Dashboard.tsx +++ b/hotel-management/src/pages/Dashboard.tsx @@ -1,10 +1,18 @@ import styled from 'styled-components'; +// Hooks +import { useUserRoomAvailable } from '@hook/useUserRoomAvailable'; + const StyledDashboard = styled.main` padding: 20px; `; 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 9c7eea4..ed0a843 100644 --- a/hotel-management/src/services/roomServices.ts +++ b/hotel-management/src/services/roomServices.ts @@ -1,5 +1,6 @@ // Types import { IRoom } from '@type/rooms'; +import { IDataState } from '@type/common'; // Services import supabase from './supabaseService'; @@ -38,32 +39,39 @@ const getAllRooms = async ( * Update room into database * @param room Room object need to be updated */ -const updateRoom = async (room: IRoom): Promise => { - const { error } = await supabase +const updateRoom = async (room: IRoom): Promise => { + const { data, error } = await supabase .from(ROOMS_TABLE) .update(room) - .eq('id', room.id); + .eq('id', room.id) + .select() + .single(); 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 { error } = await supabase.from(ROOMS_TABLE).insert([room]); +const createRoom = async (room: IRoom): Promise => { + const { data, error } = await supabase + .from(ROOMS_TABLE) + .insert(room) + .select() + .single(); if (error) { console.error(error.message); throw new Error(ERROR_CREATE_ROOM); } + + return data; }; /** @@ -79,4 +87,18 @@ const deleteRoom = async (idRoom: number) => { } }; -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 a5aab9a..62d2fc9 100644 --- a/hotel-management/src/services/userServices.ts +++ b/hotel-management/src/services/userServices.ts @@ -1,5 +1,6 @@ // Types import { IUser } from '@type/users'; +import { IDataState } from '@type/common'; // Services import supabase from './supabaseService'; @@ -13,29 +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) { 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 +const updateUser = async (user: IUser): Promise => { + const { data, error } = await supabase .from(USERS_TABLE) .update(user) - .eq('id', user.id); + .eq('id', user.id) + .select() + .single(); if (error) { console.error(error.message); throw new Error(ERROR_UPDATE_USER); } + + return data; }; /** @@ -64,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 };