├── setupTests.js ├── src ├── pages │ ├── Home │ │ ├── index.js │ │ ├── components │ │ │ ├── HeroSection │ │ │ │ ├── index.js │ │ │ │ ├── HeroSection.jsx │ │ │ │ └── style.module.css │ │ │ ├── FeaturedDeals │ │ │ │ ├── index.js │ │ │ │ ├── SkeletonDealCard.jsx │ │ │ │ ├── DealCard.jsx │ │ │ │ ├── style.module.css │ │ │ │ └── FeaturedDeals.jsx │ │ │ ├── TrendingDestinations │ │ │ │ ├── index.js │ │ │ │ ├── TrendingDestinationsWithLoading.jsx │ │ │ │ ├── TrendingDestinationsSection.jsx │ │ │ │ ├── TrendingDestinations.jsx │ │ │ │ └── TrendingDestinationsWithoutLoading.jsx │ │ │ └── RecentlyVisitedHotels │ │ │ │ ├── index.js │ │ │ │ ├── RecentlyVisitedHotelsWithLoading.jsx │ │ │ │ ├── RecentlyVisitedHotelsSection.jsx │ │ │ │ ├── RecentlyVisitedHotels.jsx │ │ │ │ └── RecentlyVisitedHotelsWithoutLoading.jsx │ │ ├── style.module.css │ │ └── Home.jsx │ ├── Hotel │ │ ├── index.js │ │ ├── components │ │ │ ├── AvailableRooms │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── AvailableRooms.jsx │ │ │ ├── HotelAmenities │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── HotelAmenities.jsx │ │ │ ├── HotelDetails │ │ │ │ ├── index.js │ │ │ │ └── HotelDetails.jsx │ │ │ ├── HotelMapLocation │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── HotelMapLocation.jsx │ │ │ ├── HotelReviews │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── HotelReviews.jsx │ │ │ ├── VisualGallery │ │ │ │ ├── index.js │ │ │ │ ├── VisualGallery.jsx │ │ │ │ └── style.module.css │ │ │ └── HotelHeaderAndDescription │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── HotelHeaderAndDescription.jsx │ │ ├── HotelWithLoading.jsx │ │ ├── style.module.css │ │ ├── Hotel.jsx │ │ ├── HotelWithoutLoading.jsx │ │ └── hooks │ │ │ └── useHotelData.js │ ├── Login │ │ ├── index.js │ │ ├── style.module.css │ │ └── Login.jsx │ ├── Checkout │ │ ├── index.js │ │ ├── components │ │ │ ├── CartItems │ │ │ │ ├── index.js │ │ │ │ └── CartItems.jsx │ │ │ ├── CustomTextField │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── CustomTextField.jsx │ │ │ └── FormInformation │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ ├── paymentSchema.js │ │ │ │ └── FormInformation.jsx │ │ └── Checkout.jsx │ ├── SearchPage │ │ ├── index.js │ │ ├── components │ │ │ ├── DateCheck │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── DateCheck.jsx │ │ │ ├── SearchBar │ │ │ │ ├── index.js │ │ │ │ ├── styles.jsx │ │ │ │ └── style.module.css │ │ │ ├── OptionItem │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── OptionItem.jsx │ │ │ ├── SearchFilters │ │ │ │ ├── index.js │ │ │ │ └── style.module.css │ │ │ ├── SearchResult │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── SearchResult.jsx │ │ │ ├── SearchResultItem │ │ │ │ ├── index.js │ │ │ │ └── SearchResultItem.jsx │ │ │ └── SearchItemContainer │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── SearchItemContainer.jsx │ │ ├── style.module.css │ │ └── SearchPage.jsx │ ├── Admin │ │ ├── pages │ │ │ ├── Cities │ │ │ │ ├── index.js │ │ │ │ ├── components │ │ │ │ │ ├── UpdateCityForm │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── UpdateCityForm.jsx │ │ │ │ │ └── CreateCityDialog │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── CreateCityDialog.jsx │ │ │ │ ├── cityConfig.js │ │ │ │ └── Cities.jsx │ │ │ ├── Hotels │ │ │ │ ├── index.js │ │ │ │ ├── components │ │ │ │ │ ├── UpdateHotelForm │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── UpdateHotelForm.jsx │ │ │ │ │ └── CreateHotelDialog │ │ │ │ │ │ ├── index.js │ │ │ │ │ │ └── CreateHotelDialog.jsx │ │ │ │ ├── hotelConfig.js │ │ │ │ └── Hotels.jsx │ │ │ └── Rooms │ │ │ │ ├── index.js │ │ │ │ ├── components │ │ │ │ ├── SearchBar │ │ │ │ │ ├── index.js │ │ │ │ │ └── SearchBar.jsx │ │ │ │ ├── UpdateRoomForm │ │ │ │ │ ├── index.js │ │ │ │ │ └── UpdateRoomForm.jsx │ │ │ │ ├── CreateRoomDialog │ │ │ │ │ ├── index.js │ │ │ │ │ └── CreateRoomDialog.jsx │ │ │ │ └── RoomsDetailedGrid │ │ │ │ │ ├── index.js │ │ │ │ │ └── RoomsDetailedGrid.jsx │ │ │ │ ├── roomConfig.js │ │ │ │ └── Rooms.jsx │ │ ├── components │ │ │ ├── SearchBar │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── SearchBar.jsx │ │ │ ├── CreateButton │ │ │ │ ├── index.js │ │ │ │ └── CreateButton.jsx │ │ │ ├── DetailedGrid │ │ │ │ ├── index.js │ │ │ │ ├── DetailedGridWithLoading.jsx │ │ │ │ └── DetailedGrid.jsx │ │ │ ├── LeftNavigation │ │ │ │ ├── index.js │ │ │ │ └── LeftNavigation.jsx │ │ │ ├── UpdateButton │ │ │ │ ├── index.js │ │ │ │ └── UpdateButton.jsx │ │ │ ├── UpdateEntityForm │ │ │ │ ├── index.js │ │ │ │ └── UpdateEntityForm.jsx │ │ │ └── CreateEntityDialog │ │ │ │ ├── index.js │ │ │ │ └── CreateEntityDialog.jsx │ │ └── hooks │ │ │ └── useDialogState.js │ ├── Confirmation │ │ ├── index.js │ │ ├── components │ │ │ └── ConfirmationTable │ │ │ │ ├── index.js │ │ │ │ ├── style.module.css │ │ │ │ └── ConfirmationTable.jsx │ │ ├── style.module.css │ │ └── Confirmation.jsx │ └── PageNotFound │ │ ├── index.js │ │ ├── style.module.css │ │ └── PageNotFound.jsx ├── components │ ├── Footer │ │ ├── index.js │ │ └── Footer.jsx │ ├── NavBar │ │ ├── index.js │ │ ├── ButtonLink.jsx │ │ ├── NavBar.jsx │ │ ├── DrawerComponent.jsx │ │ └── AppBarComponent.jsx │ ├── StarRating │ │ ├── index.js │ │ └── StarRating.jsx │ ├── CustomButton │ │ ├── index.js │ │ └── CustomButton.jsx │ ├── WithLoading │ │ ├── index.js │ │ └── WithLoading.jsx │ ├── GenericSnackbar │ │ ├── index.js │ │ └── GenericSnackbar.jsx │ └── CircularProgressIndicator │ │ ├── index.js │ │ ├── style.module.css │ │ └── CircularProgressIndicator.jsx ├── assets │ └── images │ │ ├── pageError.jpg │ │ ├── HomeHeroBackground.jpg │ │ └── travel-booking-icon.png ├── Axios │ └── axiosInstance.js ├── constants │ └── colorConstants.js ├── hooks │ ├── useAuthToken.js │ ├── useComponentLoader.js │ ├── useCartContext.js │ ├── useValueFromToken.js │ └── useSnackbar.js ├── services │ ├── authService.js │ ├── bookingServices.js │ ├── searchService.js │ ├── homePageServices.js │ ├── manageRooms.js │ ├── hotelPageServices.js │ ├── manageCities.js │ └── manageHotels.js ├── helpers │ └── helpers.jsx ├── routes │ └── ProtectedRoutes.jsx ├── context │ ├── CheckoutFormContext .jsx │ ├── searchContext.jsx │ ├── authContext.jsx │ └── CartContext.jsx ├── index.css ├── main.jsx ├── __tests__ │ ├── SearchService.test.jsx │ ├── Home.test.jsx │ └── Login.test.jsx └── App.jsx ├── vercel.json ├── .babelrc ├── vite.config.js ├── .gitignore ├── index.html ├── .eslintrc.cjs ├── public └── vite.svg └── package.json /setupTests.js: -------------------------------------------------------------------------------- 1 | import "@testing-library/jest-dom"; 2 | -------------------------------------------------------------------------------- /src/pages/Home/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Home"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Hotel"; 2 | -------------------------------------------------------------------------------- /src/pages/Login/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Login"; 2 | -------------------------------------------------------------------------------- /src/components/Footer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Footer"; 2 | -------------------------------------------------------------------------------- /src/components/NavBar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./NavBar"; 2 | -------------------------------------------------------------------------------- /src/pages/Checkout/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Checkout"; 2 | -------------------------------------------------------------------------------- /src/pages/SearchPage/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchPage"; 2 | -------------------------------------------------------------------------------- /src/components/StarRating/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./StarRating"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Cities/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Cities"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Hotels/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Hotels"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Rooms/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Rooms"; 2 | -------------------------------------------------------------------------------- /src/pages/Confirmation/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./Confirmation"; 2 | -------------------------------------------------------------------------------- /src/pages/PageNotFound/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./PageNotFound"; 2 | -------------------------------------------------------------------------------- /src/components/CustomButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CustomButton"; 2 | -------------------------------------------------------------------------------- /src/components/WithLoading/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./WithLoading"; 2 | -------------------------------------------------------------------------------- /src/components/GenericSnackbar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./GenericSnackbar"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/SearchBar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchBar"; 2 | -------------------------------------------------------------------------------- /vercel.json: -------------------------------------------------------------------------------- 1 | { 2 | "rewrites": [{ "source": "/(.*)", "destination": "/" }] 3 | } 4 | -------------------------------------------------------------------------------- /src/pages/Checkout/components/CartItems/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CartItems"; 2 | -------------------------------------------------------------------------------- /src/pages/Home/components/HeroSection/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HeroSection"; 2 | -------------------------------------------------------------------------------- /src/pages/SearchPage/components/DateCheck/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DateCheck"; 2 | -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchBar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchBar"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/CreateButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CreateButton"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/DetailedGrid/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./DetailedGrid"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/LeftNavigation/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./LeftNavigation"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/UpdateButton/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./UpdateButton"; 2 | -------------------------------------------------------------------------------- /src/pages/Home/components/FeaturedDeals/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FeaturedDeals"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/AvailableRooms/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./AvailableRooms"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelAmenities/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HotelAmenities"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelDetails/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HotelDetails"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelMapLocation/index.js: -------------------------------------------------------------------------------- 1 | export {default} from "./HotelMapLocation"; -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelReviews/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HotelReviews"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/VisualGallery/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./VisualGallery"; 2 | -------------------------------------------------------------------------------- /src/pages/SearchPage/components/OptionItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./OptionItem"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/UpdateEntityForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./UpdateEntityForm"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Rooms/components/SearchBar/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchBar"; 2 | -------------------------------------------------------------------------------- /src/pages/Checkout/components/CustomTextField/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CustomTextField"; 2 | -------------------------------------------------------------------------------- /src/pages/Checkout/components/FormInformation/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./FormInformation"; 2 | -------------------------------------------------------------------------------- /src/pages/Home/components/TrendingDestinations/index.js: -------------------------------------------------------------------------------- 1 | export {default} from "./TrendingDestinations"; -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchFilters/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchFilters"; 2 | -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchResult/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchResult"; 2 | -------------------------------------------------------------------------------- /src/components/CircularProgressIndicator/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CircularProgressIndicator"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/components/CreateEntityDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CreateEntityDialog"; 2 | -------------------------------------------------------------------------------- /src/pages/Home/components/RecentlyVisitedHotels/index.js: -------------------------------------------------------------------------------- 1 | export {default} from "./RecentlyVisitedHotels"; -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchResultItem/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchResultItem"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Cities/components/UpdateCityForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./UpdateCityForm"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Hotels/components/UpdateHotelForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./UpdateHotelForm"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Rooms/components/UpdateRoomForm/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./UpdateRoomForm"; 2 | -------------------------------------------------------------------------------- /src/pages/Confirmation/components/ConfirmationTable/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./ConfirmationTable"; 2 | -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchItemContainer/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./SearchItemContainer"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Cities/components/CreateCityDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CreateCityDialog"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Hotels/components/CreateHotelDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CreateHotelDialog"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Rooms/components/CreateRoomDialog/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./CreateRoomDialog"; 2 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Rooms/components/RoomsDetailedGrid/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./RoomsDetailedGrid"; 2 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelHeaderAndDescription/index.js: -------------------------------------------------------------------------------- 1 | export { default } from "./HotelHeaderAndDescription"; 2 | -------------------------------------------------------------------------------- /src/assets/images/pageError.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rahaf-Mansour/travel-booking-platform/HEAD/src/assets/images/pageError.jpg -------------------------------------------------------------------------------- /.babelrc: -------------------------------------------------------------------------------- 1 | { 2 | "presets": [ 3 | "@babel/preset-env", 4 | ["@babel/preset-react", { "runtime": "automatic" }] 5 | ] 6 | } -------------------------------------------------------------------------------- /src/assets/images/HomeHeroBackground.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rahaf-Mansour/travel-booking-platform/HEAD/src/assets/images/HomeHeroBackground.jpg -------------------------------------------------------------------------------- /src/assets/images/travel-booking-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/Rahaf-Mansour/travel-booking-platform/HEAD/src/assets/images/travel-booking-icon.png -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchItemContainer/style.module.css: -------------------------------------------------------------------------------- 1 | .heroSearchItem { 2 | display: flex; 3 | align-items: center; 4 | gap: 10px; 5 | } -------------------------------------------------------------------------------- /src/pages/Checkout/components/CustomTextField/style.module.css: -------------------------------------------------------------------------------- 1 | .field { 2 | margin-bottom: 20px; 3 | } 4 | 5 | .error { 6 | color: red; 7 | font-size: 14px; 8 | } -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchResult/style.module.css: -------------------------------------------------------------------------------- 1 | .resultList { 2 | flex: 3; 3 | } 4 | 5 | .noHotelsMessage { 6 | color: red; 7 | font-style: italic; 8 | } -------------------------------------------------------------------------------- /vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from "vite"; 2 | import react from "@vitejs/plugin-react"; 3 | 4 | export default defineConfig({ 5 | plugins: [react()], 6 | envDir: '.', 7 | }); 8 | -------------------------------------------------------------------------------- /src/Axios/axiosInstance.js: -------------------------------------------------------------------------------- 1 | import axios from "axios"; 2 | 3 | const axiosInstance = axios.create({ 4 | baseURL: import.meta.env.VITE_API_BASE_URL, 5 | }); 6 | 7 | export default axiosInstance; 8 | -------------------------------------------------------------------------------- /src/constants/colorConstants.js: -------------------------------------------------------------------------------- 1 | const colors = { 2 | primaryColor: "#395591", 3 | secondaryColor: "#4b6cb7", 4 | tertiaryColor: "#b9c0d2", 5 | mainColor: "#6f6c6c", 6 | }; 7 | 8 | export default colors; 9 | -------------------------------------------------------------------------------- /src/pages/Confirmation/components/ConfirmationTable/style.module.css: -------------------------------------------------------------------------------- 1 | .TableHeadRow { 2 | font-weight: bold; 3 | background-color: #d7d3d3a2; 4 | } 5 | 6 | .TableBodyRow:nth-child(odd) { 7 | background-color: #f3f3f3; 8 | } -------------------------------------------------------------------------------- /src/pages/Home/style.module.css: -------------------------------------------------------------------------------- 1 | .container { 2 | margin: 0 auto; 3 | padding: 2rem; 4 | max-width: 1200px; 5 | } 6 | 7 | @media (max-width: 768px) { 8 | .container { 9 | padding: 2rem; 10 | } 11 | } -------------------------------------------------------------------------------- /src/pages/Hotel/HotelWithLoading.jsx: -------------------------------------------------------------------------------- 1 | import WithLoading from "../../components/WithLoading"; 2 | import HotelWithoutLoading from "./HotelWithoutLoading"; 3 | 4 | const HotelWithLoading = WithLoading(HotelWithoutLoading); 5 | export default HotelWithLoading; 6 | -------------------------------------------------------------------------------- /src/components/CircularProgressIndicator/style.module.css: -------------------------------------------------------------------------------- 1 | .centered { 2 | display: flex; 3 | justify-content: center; 4 | align-items: center; 5 | height: 80vh; 6 | } 7 | 8 | @media (max-width: 768px) { 9 | .centered { 10 | height: 90svh; 11 | } 12 | } -------------------------------------------------------------------------------- /src/pages/Admin/components/DetailedGrid/DetailedGridWithLoading.jsx: -------------------------------------------------------------------------------- 1 | import WithLoading from "../../../../components/WithLoading"; 2 | import DetailedGrid from "./DetailedGrid"; 3 | 4 | const DetailedGridWithLoading = WithLoading(DetailedGrid); 5 | export default DetailedGridWithLoading; 6 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelMapLocation/style.module.css: -------------------------------------------------------------------------------- 1 | .mapContainer { 2 | width: 100%; 3 | height: 300px; 4 | margin-top: 2rem; 5 | margin-bottom: 3rem; 6 | } 7 | 8 | .mapContainer h3 { 9 | font-size: 1.2rem; 10 | margin-bottom: 1rem; 11 | color: #333; 12 | } -------------------------------------------------------------------------------- /src/hooks/useAuthToken.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { AuthContext } from "../context/authContext"; 3 | 4 | const useAuthToken = () => { 5 | const { user } = useContext(AuthContext); 6 | const token = user ? user.authentication : null; 7 | return token; 8 | }; 9 | 10 | export default useAuthToken; 11 | -------------------------------------------------------------------------------- /src/hooks/useComponentLoader.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const useComponentLoader = () => { 4 | const [isLoading, setIsLoading] = useState(true); 5 | 6 | const stopLoading = () => { 7 | setIsLoading(false); 8 | }; 9 | 10 | return { isLoading, stopLoading }; 11 | }; 12 | 13 | export default useComponentLoader; 14 | -------------------------------------------------------------------------------- /src/pages/Admin/components/SearchBar/style.module.css: -------------------------------------------------------------------------------- 1 | .searchBar { 2 | display: flex; 3 | justify-content: space-between; 4 | align-items: center; 5 | box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.2); 6 | gap: 2px; 7 | padding: 1px 4px; 8 | border: 1px solid #ccc; 9 | border-radius: 5px; 10 | margin: 0 8px 20px 0; 11 | } -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchFilters/style.module.css: -------------------------------------------------------------------------------- 1 | .filterSide { 2 | border-radius: 10px; 3 | background-color: #fff; 4 | box-shadow: 0 0 10px rgba(0, 0, 0, 0.1); 5 | padding: 1rem; 6 | margin-bottom: 1rem; 7 | } 8 | 9 | @media screen and (max-width: 768px) { 10 | .filterSide { 11 | padding: 1rem; 12 | } 13 | } -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | .env -------------------------------------------------------------------------------- /src/services/authService.js: -------------------------------------------------------------------------------- 1 | import axiosInstance from "../Axios/axiosInstance"; 2 | 3 | export const loginAPI = async (values) => { 4 | try { 5 | const response = await axiosInstance.post(`/auth/authenticate`, values); 6 | return response.data; 7 | } catch (error) { 8 | throw new Error(error.response?.data.message || "Error: Unauthorized User"); 9 | } 10 | }; 11 | -------------------------------------------------------------------------------- /src/components/CircularProgressIndicator/CircularProgressIndicator.jsx: -------------------------------------------------------------------------------- 1 | import { CircularProgress } from "@mui/material"; 2 | import styles from "./style.module.css"; 3 | 4 | const CircularProgressIndicator = () => { 5 | return ( 6 |
7 | 8 |
9 | ); 10 | }; 11 | 12 | export default CircularProgressIndicator; 13 | -------------------------------------------------------------------------------- /src/pages/Home/components/TrendingDestinations/TrendingDestinationsWithLoading.jsx: -------------------------------------------------------------------------------- 1 | import WithLoading from "../../../../components/WithLoading"; 2 | import TrendingDestinationsWithoutLoading from "./TrendingDestinationsWithoutLoading"; 3 | 4 | const TrendingDestinationsWithLoading = WithLoading( 5 | TrendingDestinationsWithoutLoading 6 | ); 7 | export default TrendingDestinationsWithLoading; 8 | -------------------------------------------------------------------------------- /src/pages/Home/components/RecentlyVisitedHotels/RecentlyVisitedHotelsWithLoading.jsx: -------------------------------------------------------------------------------- 1 | import WithLoading from "../../../../components/WithLoading"; 2 | import RecentlyVisitedHotelsWithoutLoading from "./RecentlyVisitedHotelsWithoutLoading"; 3 | 4 | const RecentlyVisitedHotelsWithLoading = WithLoading( 5 | RecentlyVisitedHotelsWithoutLoading 6 | ); 7 | export default RecentlyVisitedHotelsWithLoading; 8 | -------------------------------------------------------------------------------- /src/hooks/useCartContext.js: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { CartContext } from "../context/CartContext"; 3 | 4 | const useCartContext = () => { 5 | const context = useContext(CartContext); 6 | if (context === undefined) { 7 | throw new Error("useCartContext must be used within a CartContextProvider"); 8 | } 9 | return context; 10 | }; 11 | 12 | export default useCartContext; 13 | -------------------------------------------------------------------------------- /src/services/bookingServices.js: -------------------------------------------------------------------------------- 1 | import axiosInstance from "../Axios/axiosInstance"; 2 | 3 | export const postNewBooking = async (values) => { 4 | try { 5 | const response = await axiosInstance.post(`/bookings`, values); 6 | return response.data; 7 | } catch (error) { 8 | throw new Error( 9 | error.response?.data.message || "Error: Can't post the new booking" 10 | ); 11 | } 12 | }; 13 | -------------------------------------------------------------------------------- /src/pages/Admin/hooks/useDialogState.js: -------------------------------------------------------------------------------- 1 | import { useState } from "react"; 2 | 3 | const useDialogState = () => { 4 | const [isDialogOpen, setIsDialogOpen] = useState(false); 5 | 6 | const handleDialogOpen = () => setIsDialogOpen(true); 7 | const handleDialogClose = () => setIsDialogOpen(false); 8 | 9 | return { isDialogOpen, handleDialogOpen, handleDialogClose }; 10 | }; 11 | 12 | export default useDialogState; 13 | -------------------------------------------------------------------------------- /src/pages/SearchPage/components/SearchItemContainer/SearchItemContainer.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | import styles from "./style.module.css"; 3 | 4 | const SearchItemContainer = ({ children }) => { 5 | return
{children}
; 6 | }; 7 | 8 | SearchItemContainer.propTypes = { 9 | children: PropTypes.node.isRequired, 10 | }; 11 | 12 | export default SearchItemContainer; 13 | -------------------------------------------------------------------------------- /src/services/searchService.js: -------------------------------------------------------------------------------- 1 | import axiosInstance from "../Axios/axiosInstance"; 2 | 3 | export const searchAPI = async (values) => { 4 | try { 5 | const response = await axiosInstance.get(`/home/search`, { 6 | params: { 7 | ...values, 8 | }, 9 | }); 10 | return response.data; 11 | } catch (error) { 12 | throw new Error(error.response?.data.message || "Error: Can't search"); 13 | } 14 | }; 15 | -------------------------------------------------------------------------------- /src/pages/Home/components/TrendingDestinations/TrendingDestinationsSection.jsx: -------------------------------------------------------------------------------- 1 | import TrendingDestinations from "./TrendingDestinations"; 2 | 3 | const TrendingDestinationsSection = () => { 4 | return ( 5 | <> 6 |

7 | Trending Destinations 8 |

9 | 10 | 11 | ); 12 | }; 13 | 14 | export default TrendingDestinationsSection; 15 | -------------------------------------------------------------------------------- /src/pages/Home/components/RecentlyVisitedHotels/RecentlyVisitedHotelsSection.jsx: -------------------------------------------------------------------------------- 1 | import RecentlyVisitedHotels from "./RecentlyVisitedHotels"; 2 | 3 | const RecentlyVisitedHotelsSection = () => { 4 | return ( 5 | <> 6 |

7 | Recently Visited Hotels 8 |

9 | 10 | 11 | ); 12 | }; 13 | 14 | export default RecentlyVisitedHotelsSection; 15 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelAmenities/style.module.css: -------------------------------------------------------------------------------- 1 | .hotelAmenities { 2 | margin-bottom: 2rem; 3 | } 4 | 5 | .hotelAmenities h3 { 6 | font-size: 1.2rem; 7 | margin-bottom: 1rem; 8 | color: #333; 9 | } 10 | 11 | .hotelAmenities ul { 12 | list-style: none; 13 | padding: 0; 14 | } 15 | 16 | .hotelAmenities li { 17 | margin-bottom: 0.5rem; 18 | font-size: 1rem; 19 | line-height: 1.5; 20 | display: flex; 21 | align-items: center; 22 | gap: 0.5rem; 23 | } -------------------------------------------------------------------------------- /index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 10 | 11 | Travel Booking Platform 12 | 13 | 14 |
15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /src/pages/Hotel/components/HotelHeaderAndDescription/style.module.css: -------------------------------------------------------------------------------- 1 | .hotelHeaderAndDescriptionContainer { 2 | margin-bottom: 2rem; 3 | } 4 | 5 | .hotelHeaderAndDescriptionContainer p { 6 | font-size: 16px; 7 | margin-bottom: 4px; 8 | } 9 | 10 | .hotelHeader { 11 | display: flex; 12 | justify-content: space-between; 13 | align-items: center; 14 | vertical-align: middle; 15 | margin-bottom: 0.8rem; 16 | } 17 | 18 | .hotelName { 19 | font-size: 1.5rem; 20 | font-weight: 600; 21 | } -------------------------------------------------------------------------------- /src/components/WithLoading/WithLoading.jsx: -------------------------------------------------------------------------------- 1 | import CircularProgressIndicator from "../CircularProgressIndicator"; 2 | import PropTypes from "prop-types"; 3 | 4 | function WithLoading(Component) { 5 | function WithLoadingComponent({ isLoading, ...props }) { 6 | if (!isLoading) return ; 7 | return ; 8 | } 9 | 10 | WithLoadingComponent.propTypes = { 11 | isLoading: PropTypes.bool.isRequired, 12 | }; 13 | return WithLoadingComponent; 14 | } 15 | export default WithLoading; 16 | -------------------------------------------------------------------------------- /src/helpers/helpers.jsx: -------------------------------------------------------------------------------- 1 | import WifiIcon from "@mui/icons-material/Wifi"; 2 | import TvIcon from "@mui/icons-material/Tv"; 3 | import AcUnitIcon from "@mui/icons-material/AcUnit"; 4 | 5 | export const renderAmenityIcon = (amenityName) => { 6 | switch (amenityName.toLowerCase()) { 7 | case "free wi-fi": 8 | return ; 9 | case "tv": 10 | return ; 11 | case "air conditioning": 12 | return ; 13 | default: 14 | return null; 15 | } 16 | }; 17 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Cities/cityConfig.js: -------------------------------------------------------------------------------- 1 | import * as Yup from "yup"; 2 | 3 | export const fields = [ 4 | { name: "name", label: "Name", type: "text" }, 5 | { name: "description", label: "Description", type: "text" }, 6 | ]; 7 | 8 | export const initialValues = fields.reduce((values, field) => { 9 | values[field.name] = ""; 10 | return values; 11 | }, {}); 12 | 13 | export const validationSchema = Yup.object().shape({ 14 | name: Yup.string().required("Name is required"), 15 | description: Yup.string().required("Description is required"), 16 | }); 17 | -------------------------------------------------------------------------------- /src/pages/Home/components/FeaturedDeals/SkeletonDealCard.jsx: -------------------------------------------------------------------------------- 1 | import { Skeleton } from "@mui/material"; 2 | import styles from "./style.module.css"; 3 | 4 | const SkeletonDealCard = () => { 5 | return ( 6 |
7 | 8 |
9 | 10 | 11 | 12 |
13 |
14 | ); 15 | }; 16 | 17 | export default SkeletonDealCard; 18 | -------------------------------------------------------------------------------- /src/pages/Admin/components/UpdateButton/UpdateButton.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Box } from "@mui/material"; 2 | import PropTypes from "prop-types"; 3 | 4 | const UpdateButton = ({ isSubmitting }) => { 5 | return ( 6 | 7 | 15 | 16 | ); 17 | }; 18 | 19 | export default UpdateButton; 20 | 21 | UpdateButton.propTypes = { 22 | isSubmitting: PropTypes.bool, 23 | }; 24 | -------------------------------------------------------------------------------- /src/hooks/useValueFromToken.js: -------------------------------------------------------------------------------- 1 | import { jwtDecode } from "jwt-decode"; 2 | import useAuthToken from "./useAuthToken"; 3 | 4 | const getValueFromToken = (token, thing) => { 5 | try { 6 | if (token) { 7 | const decodedToken = jwtDecode(token); 8 | return decodedToken[thing]; 9 | } 10 | } catch (error) { 11 | console.error("Error decoding token:", error); 12 | } 13 | return null; 14 | }; 15 | 16 | const useValueFromToken = (thing) => { 17 | const token = useAuthToken(); 18 | return getValueFromToken(token, thing); 19 | }; 20 | 21 | export default useValueFromToken; 22 | -------------------------------------------------------------------------------- /src/components/StarRating/StarRating.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | import { Star } from "@mui/icons-material"; 3 | 4 | const StarRating = ({ starsNumber, className }) => { 5 | return ( 6 |
7 | {Array(starsNumber) 8 | .fill() 9 | .map((_, i) => ( 10 | 11 | ))} 12 |
13 | ); 14 | }; 15 | 16 | export default StarRating; 17 | 18 | StarRating.propTypes = { 19 | starsNumber: PropTypes.number.isRequired, 20 | className: PropTypes.string, 21 | }; 22 | -------------------------------------------------------------------------------- /src/routes/ProtectedRoutes.jsx: -------------------------------------------------------------------------------- 1 | import { useContext } from "react"; 2 | import { Navigate, Outlet } from "react-router-dom"; 3 | import { AuthContext } from "../context/authContext"; 4 | import PropTypes from "prop-types"; 5 | 6 | const ProtectedRoutes = ({ allowedRoles }) => { 7 | const { user } = useContext(AuthContext); 8 | 9 | if (!user || !allowedRoles.includes(user.userType)) { 10 | return ; 11 | } 12 | 13 | return ; 14 | }; 15 | 16 | export default ProtectedRoutes; 17 | 18 | ProtectedRoutes.propTypes = { 19 | allowedRoles: PropTypes.arrayOf(PropTypes.string).isRequired, 20 | }; 21 | -------------------------------------------------------------------------------- /src/pages/Admin/pages/Rooms/roomConfig.js: -------------------------------------------------------------------------------- 1 | import * as Yup from "yup"; 2 | 3 | export const fields = [ 4 | { name: "roomNumber", label: "Room Number", type: "number" }, 5 | { name: "cost", label: "Cost", type: "number" }, 6 | { name: "hotelId", label: "Hotel Id", type: "number" }, 7 | ]; 8 | 9 | export const initialValues = fields.reduce((values, field) => { 10 | values[field.name] = 0; 11 | return values; 12 | }, {}); 13 | 14 | export const validationSchema = Yup.object( 15 | fields.reduce((schema, field) => { 16 | schema[field.name] = Yup.string().required(`${field.label} is required`); 17 | return schema; 18 | }, {}) 19 | ); 20 | -------------------------------------------------------------------------------- /src/pages/Home/components/HeroSection/HeroSection.jsx: -------------------------------------------------------------------------------- 1 | import HomeHeroBackground from "../../../../assets/images/HomeHeroBackground.jpg"; 2 | import styles from "./style.module.css"; 3 | 4 | const HeroSection = () => { 5 | return ( 6 |
7 |

8 | Book with us for a happy, 9 |
comfortable accommodation! 10 |

11 | HomeHeroBackground 16 |
17 | ); 18 | }; 19 | 20 | export default HeroSection; 21 | -------------------------------------------------------------------------------- /.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true, jest: true }, 4 | extends: [ 5 | "eslint:recommended", 6 | "plugin:react/recommended", 7 | "plugin:react/jsx-runtime", 8 | "plugin:react-hooks/recommended", 9 | ], 10 | ignorePatterns: ["dist", ".eslintrc.cjs"], 11 | parserOptions: { ecmaVersion: "latest", sourceType: "module" }, 12 | settings: { react: { version: "18.2" } }, 13 | plugins: ["react-refresh"], 14 | rules: { 15 | "react/display-name": "off", 16 | "react-refresh/only-export-components": [ 17 | "warn", 18 | { allowConstantExport: true }, 19 | ], 20 | }, 21 | }; 22 | -------------------------------------------------------------------------------- /src/context/CheckoutFormContext .jsx: -------------------------------------------------------------------------------- 1 | import { createContext, useState } from "react"; 2 | import PropTypes from "prop-types"; 3 | 4 | export const FormContext = createContext(); 5 | 6 | const FormContextProvider = ({ children }) => { 7 | const [formValues, setFormValues] = useState(null); 8 | 9 | const setValues = (values) => { 10 | setFormValues(values); 11 | }; 12 | 13 | return ( 14 | 15 | {children} 16 | 17 | ); 18 | }; 19 | 20 | FormContextProvider.propTypes = { 21 | children: PropTypes.node.isRequired, 22 | }; 23 | 24 | export default FormContextProvider; 25 | -------------------------------------------------------------------------------- /src/pages/Admin/components/CreateButton/CreateButton.jsx: -------------------------------------------------------------------------------- 1 | import { Button } from "@mui/material"; 2 | import AddBoxIcon from "@mui/icons-material/AddBox"; 3 | import PropTypes from "prop-types"; 4 | 5 | const CreateButton = ({ handleDialogOpen }) => { 6 | return ( 7 | 14 | ); 15 | }; 16 | 17 | export default CreateButton; 18 | 19 | CreateButton.propTypes = { 20 | handleDialogOpen: PropTypes.func.isRequired, 21 | }; 22 | -------------------------------------------------------------------------------- /src/components/NavBar/ButtonLink.jsx: -------------------------------------------------------------------------------- 1 | import { Button, Typography } from "@mui/material"; 2 | import { Link } from "react-router-dom"; 3 | import PropTypes from "prop-types"; 4 | 5 | const ButtonLink = ({ to, icon, text }) => ( 6 | 15 | ); 16 | 17 | export default ButtonLink; 18 | 19 | ButtonLink.propTypes = { 20 | to: PropTypes.string.isRequired, 21 | icon: PropTypes.element.isRequired, 22 | text: PropTypes.string, 23 | }; 24 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Source+Sans+Pro:wght@400;600&display=swap'); 2 | @import url("https://fonts.googleapis.com/css2?family=Monoton&family=Quicksand:wght@500;700&display=swap"); 3 | @import url('https://fonts.googleapis.com/css2?family=Poppins:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); 4 | 5 | * { 6 | margin: 0; 7 | padding: 0; 8 | box-sizing: border-box; 9 | font-family: 'Poppins', sans-serif; 10 | 11 | } 12 | 13 | :root { 14 | --primary-color: #395591; 15 | --secondary-color: #4b6cb7; 16 | --tertiary-color: #b9c0d2; 17 | --main-color: #6f6c6c; 18 | } -------------------------------------------------------------------------------- /src/pages/PageNotFound/style.module.css: -------------------------------------------------------------------------------- 1 | .errorContainer { 2 | display: flex; 3 | flex-direction: column; 4 | justify-content: center; 5 | align-items: center; 6 | height: 100vh; 7 | } 8 | 9 | .pageNotFoundImage { 10 | max-width: 100%; 11 | height: auto; 12 | vertical-align: middle; 13 | margin-top: 1rem; 14 | } 15 | 16 | .errorMessage { 17 | color: #000; 18 | font-size: 1.4rem; 19 | text-align: center; 20 | margin: 0 0 3rem; 21 | } 22 | 23 | @media screen and (max-width: 625px) { 24 | .errorMessage { 25 | font-size: 1.2rem; 26 | margin: 0 4rem; 27 | } 28 | } 29 | 30 | @media screen and (max-width: 400px) { 31 | .errorMessage { 32 | margin: 1rem; 33 | } 34 | } -------------------------------------------------------------------------------- /src/components/CustomButton/CustomButton.jsx: -------------------------------------------------------------------------------- 1 | import PropTypes from "prop-types"; 2 | 3 | const CustomButton = ({ 4 | type = "submit", 5 | className, 6 | onClick, 7 | children, 8 | style, 9 | disabled = false, 10 | }) => { 11 | return ( 12 | 21 | ); 22 | }; 23 | 24 | CustomButton.propTypes = { 25 | type: PropTypes.string, 26 | className: PropTypes.string, 27 | onClick: PropTypes.func, 28 | children: PropTypes.node.isRequired, 29 | style: PropTypes.object, 30 | disabled: PropTypes.bool, 31 | }; 32 | 33 | export default CustomButton; 34 | -------------------------------------------------------------------------------- /src/pages/PageNotFound/PageNotFound.jsx: -------------------------------------------------------------------------------- 1 | import pageError from "../../assets/images/pageError.jpg"; 2 | import NavBar from "../../components/NavBar"; 3 | import styles from "./style.module.css"; 4 | import Footer from "../../components/Footer"; 5 | 6 | const PageNotFound = () => { 7 | return ( 8 | <> 9 | 10 |
11 | pageError 17 |

18 | We cannot find the page you are looking for! 19 |

20 |
21 |