├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── _redirects ├── favicon.ico ├── index.html └── manifest.json └── src ├── App.css ├── App.js ├── App.test.js ├── Contentful.js ├── components ├── Banner.js ├── FeaturedRooms.js ├── Hero.js ├── Loading.js ├── Navbar.js ├── Room.js ├── RoomsContainer.js ├── RoomsFilter.js ├── RoomsList.js ├── Services.js ├── StyledHero.js └── Title.js ├── context.js ├── data.js ├── images ├── defaultBcg.jpeg ├── details-1.jpeg ├── details-2.jpeg ├── details-3.jpeg ├── details-4.jpeg ├── gif │ ├── loading-arrow.gif │ └── loading-gear.gif ├── logo.svg ├── room-1.jpeg ├── room-10.jpeg ├── room-11.jpeg ├── room-12.jpeg ├── room-2.jpeg ├── room-3.jpeg ├── room-4.jpeg ├── room-5.jpeg ├── room-6.jpeg ├── room-7.jpeg ├── room-8.jpeg └── room-9.jpeg ├── index.css ├── index.js ├── logo.svg ├── pages ├── Error.js ├── Home.js ├── Rooms.js └── SingleRoom.js └── serviceWorker.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | .env.development 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 2 | 3 | ## Available Scripts 4 | 5 | In the project directory, you can run: 6 | 7 | ### `npm start` 8 | 9 | Runs the app in the development mode.
10 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 11 | 12 | The page will reload if you make edits.
13 | You will also see any lint errors in the console. 14 | 15 | ### `npm test` 16 | 17 | Launches the test runner in the interactive watch mode.
18 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 19 | 20 | ### `npm run build` 21 | 22 | Builds the app for production to the `build` folder.
23 | It correctly bundles React in production mode and optimizes the build for the best performance. 24 | 25 | The build is minified and the filenames include the hashes.
26 | Your app is ready to be deployed! 27 | 28 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 29 | 30 | ### `npm run eject` 31 | 32 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 33 | 34 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 35 | 36 | Instead, it will copy all the configuration files and the transitive dependencies (Webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 37 | 38 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 39 | 40 | ## Learn More 41 | 42 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 43 | 44 | To learn React, check out the [React documentation](https://reactjs.org/). 45 | 46 | ### Code Splitting 47 | 48 | This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting 49 | 50 | ### Analyzing the Bundle Size 51 | 52 | This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size 53 | 54 | ### Making a Progressive Web App 55 | 56 | This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app 57 | 58 | ### Advanced Configuration 59 | 60 | This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration 61 | 62 | ### Deployment 63 | 64 | This section has moved here: https://facebook.github.io/create-react-app/docs/deployment 65 | 66 | ### `npm run build` fails to minify 67 | 68 | This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify 69 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "resort", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "contentful": "^7.5.0", 7 | "react": "^16.8.6", 8 | "react-dom": "^16.8.6", 9 | "react-icons": "^3.6.1", 10 | "react-router-dom": "^5.0.0", 11 | "react-scripts": "3.0.0", 12 | "styled-components": "^4.2.0" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/_redirects: -------------------------------------------------------------------------------- 1 | /* /index.html 200 2 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 22 | Beach Resort 23 | 24 | 25 | 26 |
27 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": ".", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /src/App.css: -------------------------------------------------------------------------------- 1 | * { 2 | margin: 0; 3 | padding: 0; 4 | box-sizing: border-box; 5 | } 6 | :root { 7 | --primaryColor: #af9a7d; 8 | --mainWhite: #fff; 9 | --offWhite: #f7f7f7; 10 | --mainBlack: #222; 11 | --mainGrey: #ececec; 12 | --darkGrey: #cfcfcf; 13 | --mainTransition: all 0.3s linear; 14 | --mainSpacing: 3px; 15 | --lightShadow: 2px 5px 3px 0px rgba(0, 0, 0, 0.5); 16 | --darkShadow: 4px 10px 5px 0px rgba(0, 0, 0, 0.5); 17 | } 18 | /* globals */ 19 | body { 20 | padding-top: 66px; 21 | color: var(--mainBlack); 22 | background: var(--mainWhite); 23 | font-family: Verdana, Geneva, Tahoma, sans-serif; 24 | line-height: 1.4; 25 | } 26 | h1 { 27 | font-size: 3em; 28 | line-height: 1; 29 | margin-bottom: 0.5em; 30 | } 31 | h2 { 32 | font-size: 2em; 33 | margin-bottom: 0.75em; 34 | } 35 | h3 { 36 | font-size: 1.5em; 37 | line-height: 1; 38 | margin-bottom: 1em; 39 | } 40 | h4 { 41 | font-size: 1.2em; 42 | line-height: 1.25; 43 | margin-bottom: 1.25em; 44 | } 45 | h5 { 46 | font-size: 1em; 47 | font-weight: bold; 48 | margin-bottom: 1.5em; 49 | } 50 | h6 { 51 | font-size: 1em; 52 | font-weight: bold; 53 | margin-bottom: 1.5em; 54 | } 55 | 56 | .btn-primary { 57 | display: inline-block; 58 | text-decoration: none; 59 | letter-spacing: var(--mainSpacing); 60 | color: var(--mainBlack); 61 | background: var(--primaryColor); 62 | padding: 0.4rem 0.9rem; 63 | border: 3px solid var(--primaryColor); 64 | transition: var(--mainTransition); 65 | text-transform: uppercase; 66 | cursor: pointer; 67 | } 68 | .btn-primary:hover { 69 | background: transparent; 70 | color: var(--primaryColor); 71 | } 72 | .loading { 73 | text-transform: capitalize; 74 | text-align: center; 75 | margin-top: 3rem; 76 | } 77 | .error { 78 | text-align: center; 79 | text-transform: uppercase; 80 | margin: 2rem 0; 81 | } 82 | .empty-search { 83 | text-align: center; 84 | text-transform: capitalize; 85 | margin: 2rem 0; 86 | padding: 1rem; 87 | letter-spacing: var(--mainSpacing); 88 | } 89 | /* end of globals */ 90 | /* Navbar */ 91 | .navbar { 92 | position: fixed; 93 | top: 0; 94 | left: 0; 95 | width: 100%; 96 | padding: 0.75rem 2rem; 97 | background: var(--offWhite); 98 | z-index: 1; 99 | } 100 | .nav-header { 101 | display: flex; 102 | justify-content: space-between; 103 | } 104 | .nav-btn { 105 | background: transparent; 106 | border: none; 107 | cursor: pointer; 108 | outline: none; 109 | } 110 | .nav-icon { 111 | font-size: 1.5rem; 112 | color: var(--primaryColor); 113 | } 114 | .nav-links { 115 | height: 0; 116 | overflow: hidden; 117 | transition: var(--mainTransition); 118 | list-style-type: none; 119 | } 120 | .nav-links a { 121 | display: block; 122 | text-decoration: none; 123 | padding: 1rem 0; 124 | color: var(--mainBlack); 125 | transition: var(--mainTransition); 126 | text-align: center; 127 | font-size: 1rem; 128 | font-weight: 600; 129 | letter-spacing: var(--mainSpacing); 130 | } 131 | .nav-links a:hover { 132 | color: var(--primaryColor); 133 | } 134 | 135 | .show-nav { 136 | height: 100px; 137 | } 138 | @media screen and (min-width: 768px) { 139 | .nav-btn { 140 | display: none; 141 | } 142 | .nav-center { 143 | max-width: 1170px; 144 | margin: 0 auto; 145 | display: flex; 146 | } 147 | .nav-links { 148 | height: auto; 149 | display: flex; 150 | margin-left: 4rem; 151 | } 152 | .nav-links a { 153 | margin: 0 1rem; 154 | padding: 0.5rem 0; 155 | } 156 | } 157 | /* end of navbar */ 158 | /* Hero */ 159 | .defaultHero, 160 | .roomsHero { 161 | min-height: calc(100vh - 66px); 162 | background: url("./images/defaultBcg.jpeg") center/cover no-repeat; 163 | display: flex; 164 | align-items: center; 165 | justify-content: center; 166 | } 167 | .roomsHero { 168 | background-image: url("./images/room-2.jpeg"); 169 | min-height: 60vh; 170 | } 171 | /* End of Hero */ 172 | /* Banner */ 173 | .banner { 174 | display: inline-block; 175 | background: rgba(0, 0, 0, 0.5); 176 | color: var(--mainWhite); 177 | padding: 2rem 1rem; 178 | text-align: center; 179 | text-transform: capitalize; 180 | letter-spacing: var(--mainSpacing); 181 | } 182 | .banner h1 { 183 | font-size: 2.5rem; 184 | } 185 | .banner div { 186 | width: 10rem; 187 | height: 5px; 188 | background: var(--primaryColor); 189 | margin: 1.7rem auto; 190 | } 191 | .banner p { 192 | font-size: 1.2rem; 193 | margin-bottom: 2rem; 194 | } 195 | @media screen and (min-width: 576px) { 196 | .banner { 197 | padding: 2rem 3rem; 198 | } 199 | .banner h1 { 200 | font-size: 3rem; 201 | } 202 | } 203 | @media screen and (min-width: 992px) { 204 | .banner { 205 | padding: 2rem 6rem; 206 | } 207 | .banner h1 { 208 | font-size: 4rem; 209 | } 210 | } 211 | /* End of Banner */ 212 | /* Title */ 213 | .section-title { 214 | text-align: center; 215 | margin-bottom: 4rem; 216 | } 217 | .section-title h4 { 218 | font-size: 2rem; 219 | letter-spacing: var(--mainSpacing); 220 | text-transform: capitalize; 221 | margin-bottom: 1rem; 222 | } 223 | .section-title div { 224 | width: 5rem; 225 | height: 5px; 226 | margin: 0 auto; 227 | background: var(--primaryColor); 228 | } 229 | /* end of Title */ 230 | 231 | /* services */ 232 | .services { 233 | padding: 5rem 0; 234 | } 235 | .services { 236 | background: var(--darkGrey); 237 | text-align: center; 238 | } 239 | .services-center { 240 | width: 90vw; 241 | margin: 0 auto; 242 | display: grid; 243 | grid-template-columns: repeat(auto-fit, minmax(255px, 1fr)); 244 | grid-row-gap: 2rem; 245 | grid-column-gap: 50px; 246 | } 247 | .service span { 248 | display: inline-block; 249 | color: var(--primaryColor); 250 | font-size: 2.5rem; 251 | margin-bottom: 1.5rem; 252 | } 253 | .services h6 { 254 | letter-spacing: var(--mainSpacing); 255 | } 256 | .services p { 257 | width: 80%; 258 | margin: 0 auto; 259 | } 260 | @media screen and (min-width: 992px) { 261 | .services-center { 262 | width: 95vw; 263 | max-width: 1170px; 264 | } 265 | } 266 | 267 | @media screen and (min-width: 1200px) { 268 | .services p { 269 | width: 100%; 270 | } 271 | } 272 | /*end of services */ 273 | /* featured rooms */ 274 | 275 | .featured-rooms { 276 | padding: 5rem 0; 277 | } 278 | .featured-rooms-center { 279 | width: 80vw; 280 | margin: 0 auto; 281 | display: grid; 282 | grid-template-columns: repeat(auto-fit, minmax(270px, 1fr)); 283 | grid-row-gap: 2rem; 284 | grid-column-gap: 40px; 285 | } 286 | @media screen and (min-width: 776px) { 287 | .featured-rooms-center { 288 | width: 90vw; 289 | grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); 290 | } 291 | } 292 | @media screen and (min-width: 992px) { 293 | .featured-rooms-center { 294 | width: 95vw; 295 | max-width: 1170px; 296 | } 297 | } 298 | /* end pf featured rooms */ 299 | /* room */ 300 | .room { 301 | box-shadow: var(--lightShadow); 302 | transition: var(--mainTransition); 303 | } 304 | .room:hover { 305 | box-shadow: var(--darkShadow); 306 | } 307 | 308 | .img-container { 309 | position: relative; 310 | } 311 | .img-container img { 312 | width: 100%; 313 | display: block; 314 | transition: var(--mainTransition); 315 | } 316 | .price-top { 317 | position: absolute; 318 | top: 0; 319 | left: 0; 320 | background: rgba(0, 0, 0, 0.8); 321 | color: var(--mainWhite); 322 | padding: 0.3rem 0.6rem 0.5rem; 323 | border-bottom-right-radius: 1rem; 324 | font-size: 0.5rem; 325 | text-align: center; 326 | transition: var(--mainTransition); 327 | } 328 | .price-top h6 { 329 | margin-bottom: 0; 330 | font-size: 0.9rem; 331 | font-weight: 300; 332 | letter-spacing: var(--mainSpacing); 333 | } 334 | .room-link { 335 | position: absolute; 336 | top: 50%; 337 | left: 50%; 338 | transform: scale(0); 339 | transition: all 0.3s linear; 340 | } 341 | .img-container:hover { 342 | background: rgba(0, 0, 0, 0.8); 343 | } 344 | .img-container:hover img { 345 | opacity: 0.3; 346 | } 347 | .img-container:hover .price-top { 348 | opacity: 0; 349 | } 350 | .img-container:hover .room-link { 351 | transform: translate(-50%, -50%) scale(1); 352 | } 353 | .room-info { 354 | background: var(--darkGrey); 355 | text-transform: capitalize; 356 | padding: 0.5rem 0; 357 | text-align: center; 358 | font-weight: 700; 359 | letter-spacing: var(--mainSpacing); 360 | } 361 | /* end of room */ 362 | /* single room*/ 363 | 364 | .single-room { 365 | padding: 5rem 0 0 0; 366 | } 367 | .single-room-images { 368 | width: 80vw; 369 | margin: 0 auto; 370 | display: grid; 371 | grid-template-columns: repeat(auto-fit, minmax(350px, 1fr)); 372 | grid-row-gap: 2rem; 373 | grid-column-gap: 50px; 374 | } 375 | .single-room-images img { 376 | width: 100%; 377 | display: block; 378 | } 379 | .single-room-info { 380 | width: 80vw; 381 | display: grid; 382 | grid-template-columns: 1fr; 383 | margin: 2rem auto; 384 | } 385 | .desc, 386 | .info { 387 | margin: 1rem 0; 388 | } 389 | .desc h3 { 390 | text-transform: capitalize; 391 | letter-spacing: var(--mainSpacing); 392 | } 393 | .desc p { 394 | line-height: 1.5; 395 | } 396 | .info h3, 397 | .info h6 { 398 | text-transform: capitalize; 399 | letter-spacing: var(--mainSpacing); 400 | } 401 | 402 | .info h6 { 403 | font-weight: 300; 404 | } 405 | .room-extras { 406 | width: 80vw; 407 | margin: 0 auto 3rem auto; 408 | } 409 | .room-extras h6 { 410 | text-transform: capitalize; 411 | letter-spacing: var(--mainSpacing); 412 | } 413 | .extras { 414 | list-style-type: none; 415 | display: grid; 416 | grid-template-columns: repeat(auto-fit, minmax(330px, 1fr)); 417 | grid-column-gap: 2rem; 418 | grid-row-gap: 1rem; 419 | } 420 | @media screen and (min-width: 992px) { 421 | .single-room-images, 422 | .single-room-info, 423 | .room-extras { 424 | width: 95vw; 425 | max-width: 1170px; 426 | } 427 | .single-room-info { 428 | display: grid; 429 | grid-template-columns: 1fr 1fr; 430 | grid-column-gap: 2rem; 431 | } 432 | .info { 433 | padding-left: 3rem; 434 | } 435 | } 436 | /* end of single room*/ 437 | /* roomlist */ 438 | .roomslist { 439 | padding: 5rem 0; 440 | } 441 | .roomslist-center { 442 | width: 80vw; 443 | margin: 0 auto; 444 | display: grid; 445 | grid-template-columns: repeat(auto-fill, minmax(270px, 1fr)); 446 | grid-row-gap: 2rem; 447 | grid-column-gap: 30px; 448 | } 449 | 450 | @media screen and (min-width: 776px) { 451 | .roomslist-center { 452 | width: 90vw; 453 | } 454 | } 455 | @media screen and (min-width: 992px) { 456 | .roomslist-center { 457 | width: 95vw; 458 | max-width: 1170px; 459 | } 460 | } 461 | /* end of roomlist */ 462 | /* rooms fitler*/ 463 | .filter-container { 464 | padding: 5rem 0; 465 | } 466 | .filter-form { 467 | width: 60vw; 468 | margin: 0 auto; 469 | display: grid; 470 | grid-template-columns: repeat(auto-fit, minmax(202px, 1fr)); 471 | grid-row-gap: 2rem; 472 | grid-column-gap: 40px; 473 | } 474 | .form-group { 475 | text-transform: capitalize; 476 | } 477 | .form-group label { 478 | display: block; 479 | letter-spacing: var(--mainSpacing); 480 | margin-bottom: 0.5rem; 481 | } 482 | .form-control { 483 | width: 100%; 484 | background: transparent; 485 | font-size: 1rem; 486 | } 487 | .size-inputs { 488 | display: flex; 489 | } 490 | .size-input { 491 | width: 40%; 492 | padding: 0.2rem; 493 | border: 1px solid var(--mainBlack); 494 | border-radius: 0.3rem; 495 | margin-right: 0.3rem; 496 | } 497 | .single-extra label { 498 | display: inline-block; 499 | font-size: 0.8rem; 500 | margin-left: 0.5rem; 501 | } 502 | @media screen and (min-width: 776px) { 503 | .filter-form { 504 | width: 70vw; 505 | } 506 | } 507 | @media screen and (min-width: 992px) { 508 | .filter-form { 509 | width: 95vw; 510 | max-width: 1170px; 511 | } 512 | } 513 | /* end of rooms fitler*/ 514 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import "./App.css"; 3 | 4 | import Home from "./pages/Home"; 5 | import Rooms from "./pages/Rooms"; 6 | import SingleRoom from "./pages/SingleRoom"; 7 | import Error from "./pages/Error"; 8 | 9 | import Navbar from "./components/Navbar"; 10 | 11 | import { Switch, Route } from "react-router-dom"; 12 | 13 | function App() { 14 | return ( 15 | <> 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | ); 25 | } 26 | 27 | export default App; 28 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import App from './App'; 4 | 5 | it('renders without crashing', () => { 6 | const div = document.createElement('div'); 7 | ReactDOM.render(, div); 8 | ReactDOM.unmountComponentAtNode(div); 9 | }); 10 | -------------------------------------------------------------------------------- /src/Contentful.js: -------------------------------------------------------------------------------- 1 | const contentful = require("contentful"); 2 | 3 | export default contentful.createClient({ 4 | space: process.env.REACT_APP_API_SPACE, 5 | accessToken: process.env.REACT_APP_ACCESS_TOKEN 6 | }); 7 | -------------------------------------------------------------------------------- /src/components/Banner.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | const Banner = ({ children, title, subtitle }) => { 3 | return ( 4 |
5 |

{title}

6 |
7 |

{subtitle}

8 | {children} 9 |
10 | ); 11 | }; 12 | 13 | export default Banner; 14 | -------------------------------------------------------------------------------- /src/components/FeaturedRooms.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import Title from "./Title"; 3 | import { RoomContext } from "../context"; 4 | import Room from "./Room"; 5 | import Loading from "./Loading"; 6 | export default class FeaturedRooms extends Component { 7 | static contextType = RoomContext; 8 | 9 | render() { 10 | let { loading, featuredRooms: rooms } = this.context; 11 | 12 | rooms = rooms.map(room => { 13 | return ; 14 | }); 15 | return ( 16 |
17 | 18 | <div className="featured-rooms-center"> 19 | {loading ? <Loading /> : rooms} 20 | </div> 21 | </section> 22 | ); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/components/Hero.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Hero = ({ children, hero }) => { 4 | return <header className={hero}>{children}</header>; 5 | }; 6 | 7 | export default Hero; 8 | 9 | Hero.defaultProps = { 10 | hero: "defaultHero" 11 | }; 12 | -------------------------------------------------------------------------------- /src/components/Loading.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import loadingGif from "../images/gif/loading-arrow.gif"; 3 | const Loading = () => { 4 | return ( 5 | <div className="loading"> 6 | <h4>rooms data loading....</h4> 7 | <img src={loadingGif} alt="" /> 8 | </div> 9 | ); 10 | }; 11 | 12 | export default Loading; 13 | -------------------------------------------------------------------------------- /src/components/Navbar.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { Link } from "react-router-dom"; 3 | import { FaAlignRight } from "react-icons/fa"; 4 | import logo from "../images/logo.svg"; 5 | export default class Navbar extends Component { 6 | state = { 7 | isOpen: false 8 | }; 9 | handleToggle = () => { 10 | this.setState({ isOpen: !this.state.isOpen }); 11 | }; 12 | render() { 13 | return ( 14 | <nav className="navbar"> 15 | <div className="nav-center"> 16 | <div className="nav-header"> 17 | <Link to="/"> 18 | <img src={logo} alt="Beach Resort" /> 19 | </Link> 20 | <button 21 | type="button" 22 | className="nav-btn" 23 | onClick={this.handleToggle} 24 | > 25 | <FaAlignRight className="nav-icon" /> 26 | </button> 27 | </div> 28 | <ul 29 | className={this.state.isOpen ? "nav-links show-nav" : "nav-links"} 30 | > 31 | <li> 32 | <Link to="/">Home</Link> 33 | </li> 34 | <li> 35 | <Link to="/rooms">Rooms</Link> 36 | </li> 37 | </ul> 38 | </div> 39 | </nav> 40 | ); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /src/components/Room.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import defaultImg from "../images/room-1.jpeg"; 4 | import PropTypes from "prop-types"; 5 | import { memo } from "react"; 6 | const Room = memo(({ room }) => { 7 | const { name, slug, images, price } = room; 8 | // console.log(name); 9 | return ( 10 | <article className="room"> 11 | <div className="img-container"> 12 | <img src={images[0] || defaultImg} alt="single room" /> 13 | <div className="price-top"> 14 | <h6>${price}</h6> 15 | <p>per night</p> 16 | </div> 17 | <Link to={`/rooms/${slug}`} className="btn-primary room-link"> 18 | features 19 | </Link> 20 | </div> 21 | <p className="room-info">{name}</p> 22 | </article> 23 | ); 24 | }); 25 | 26 | Room.propTypes = { 27 | room: PropTypes.shape({ 28 | name: PropTypes.string.isRequired, 29 | slug: PropTypes.string.isRequired, 30 | images: PropTypes.arrayOf(PropTypes.string).isRequired, 31 | price: PropTypes.number.isRequired 32 | }) 33 | }; 34 | export default Room; 35 | -------------------------------------------------------------------------------- /src/components/RoomsContainer.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { withRoomConsumer } from "../context"; 3 | import Loading from "./Loading"; 4 | import RoomsFilter from "./RoomsFilter"; 5 | import RoomsList from "./RoomsList"; 6 | 7 | function RoomContainer({ context }) { 8 | const { loading, sortedRooms, rooms } = context; 9 | if (loading) { 10 | return <Loading />; 11 | } 12 | return ( 13 | <> 14 | <RoomsFilter rooms={rooms} /> 15 | <RoomsList rooms={sortedRooms} /> 16 | </> 17 | ); 18 | } 19 | 20 | export default withRoomConsumer(RoomContainer); 21 | 22 | // import React from "react"; 23 | // import { RoomConsumer } from "../context"; 24 | // import Loading from "./Loading"; 25 | // import RoomsFilter from "./RoomsFilter"; 26 | // import RoomsList from "./RoomsList"; 27 | // export default function RoomContainer() { 28 | // return ( 29 | // <RoomConsumer> 30 | // {value => { 31 | // const { loading, setRoom, sortedRooms,rooms } = value; 32 | // if (loading) { 33 | // return <Loading />; 34 | // } 35 | // return ( 36 | // <> 37 | // <RoomsFilter rooms={rooms} /> 38 | // <RoomsList rooms={sortedRooms} setRoom={setRoom} /> 39 | // </> 40 | // ); 41 | // }} 42 | // </RoomConsumer> 43 | // ); 44 | // } 45 | -------------------------------------------------------------------------------- /src/components/RoomsFilter.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { useContext } from "react"; 3 | import { RoomContext } from "../context"; 4 | import Title from "./Title"; 5 | // get all unique values 6 | const getUnique = (items, value) => { 7 | return [...new Set(items.map(item => item[value]))]; 8 | }; 9 | 10 | const RoomsFilter = ({ rooms }) => { 11 | // react hooks 12 | const context = useContext(RoomContext); 13 | const { 14 | handleChange, 15 | type, 16 | capacity, 17 | price, 18 | minPrice, 19 | maxPrice, 20 | minSize, 21 | maxSize, 22 | breakfast, 23 | pets 24 | } = context; 25 | 26 | // get unique types 27 | let types = getUnique(rooms, "type"); 28 | // add all 29 | types = ["all", ...types]; 30 | // map to jsx 31 | types = types.map((item, index) => ( 32 | <option key={index} value={item}> 33 | {item} 34 | </option> 35 | )); 36 | // get unique capacity 37 | let people = getUnique(rooms, "capacity"); 38 | people = people.map((item, index) => ( 39 | <option key={index} value={item}> 40 | {item} 41 | </option> 42 | )); 43 | return ( 44 | <section className="filter-container"> 45 | <Title title="search rooms" /> 46 | <form className="filter-form"> 47 | {/* select type */} 48 | <div className="form-group"> 49 | <label htmlFor="type">room type</label> 50 | <select 51 | name="type" 52 | id="type" 53 | onChange={handleChange} 54 | className="form-control" 55 | value={type} 56 | > 57 | {types} 58 | </select> 59 | </div> 60 | {/* end of select type */} 61 | {/* guests */} 62 | <div className="form-group"> 63 | <label htmlFor="capacity">Guests</label> 64 | <select 65 | name="capacity" 66 | id="capacity" 67 | onChange={handleChange} 68 | className="form-control" 69 | value={capacity} 70 | > 71 | {people} 72 | </select> 73 | </div> 74 | {/* end of guests */} 75 | {/* room price */} 76 | <div className="form-group"> 77 | <label htmlFor="price">room price ${price}</label> 78 | <input 79 | type="range" 80 | name="price" 81 | min={minPrice} 82 | max={maxPrice} 83 | id="price" 84 | value={price} 85 | onChange={handleChange} 86 | className="form-control" 87 | /> 88 | </div> 89 | {/* end of room price*/} 90 | {/* size */} 91 | <div className="form-group"> 92 | <label htmlFor="price">room size </label> 93 | <div className="size-inputs"> 94 | <input 95 | type="number" 96 | name="minSize" 97 | value={minSize} 98 | onChange={handleChange} 99 | className="size-input" 100 | /> 101 | <input 102 | type="number" 103 | name="maxSize" 104 | value={maxSize} 105 | onChange={handleChange} 106 | className="size-input" 107 | /> 108 | </div> 109 | </div> 110 | {/* end of select type */} 111 | {/* extras */} 112 | <div className="form-group"> 113 | <div className="single-extra"> 114 | <input 115 | type="checkbox" 116 | name="breakfast" 117 | id="breakfast" 118 | checked={breakfast} 119 | onChange={handleChange} 120 | /> 121 | <label htmlFor="breakfast">breakfast</label> 122 | </div> 123 | <div className="single-extra"> 124 | <input 125 | type="checkbox" 126 | name="pets" 127 | checked={pets} 128 | onChange={handleChange} 129 | /> 130 | <label htmlFor="breakfast">pets</label> 131 | </div> 132 | </div> 133 | {/* end of extras type */} 134 | </form> 135 | </section> 136 | ); 137 | }; 138 | 139 | export default RoomsFilter; 140 | -------------------------------------------------------------------------------- /src/components/RoomsList.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Room from "./Room"; 3 | const RoomsList = ({ rooms }) => { 4 | if (rooms.length === 0) { 5 | return ( 6 | <div className="empty-search"> 7 | <h3>unfortunately no rooms matched your search parameters</h3> 8 | </div> 9 | ); 10 | } 11 | return ( 12 | <section className="roomslist"> 13 | <div className="roomslist-center"> 14 | {rooms.map(item => { 15 | return <Room key={item.id} room={item} />; 16 | })} 17 | </div> 18 | </section> 19 | ); 20 | }; 21 | 22 | export default RoomsList; 23 | -------------------------------------------------------------------------------- /src/components/Services.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import { FaCocktail, FaHiking, FaShuttleVan, FaBeer } from "react-icons/fa"; 3 | import Title from "./Title"; 4 | export default class Services extends Component { 5 | state = { 6 | services: [ 7 | { 8 | icon: <FaCocktail />, 9 | title: "Free Cocktails", 10 | info: 11 | "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Alias molestias eius libero?" 12 | }, 13 | { 14 | icon: <FaHiking />, 15 | title: "Endless Hiking", 16 | info: 17 | "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Alias molestias eius libero?" 18 | }, 19 | { 20 | icon: <FaShuttleVan />, 21 | title: "Free Shuttle", 22 | info: 23 | "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Alias molestias eius libero?" 24 | }, 25 | { 26 | icon: <FaBeer />, 27 | title: "Strongest Beer", 28 | info: 29 | "Lorem ipsum, dolor sit amet consectetur adipisicing elit. Alias molestias eius libero?" 30 | } 31 | ] 32 | }; 33 | render() { 34 | return ( 35 | <section className="services"> 36 | <Title title="services" /> 37 | <div className="services-center"> 38 | {this.state.services.map(item => { 39 | return ( 40 | <article key={`item-${item.title}`} className="service"> 41 | <span>{item.icon}</span> 42 | <h6>{item.title}</h6> 43 | <p>{item.info}</p> 44 | </article> 45 | ); 46 | })} 47 | </div> 48 | </section> 49 | ); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/components/StyledHero.js: -------------------------------------------------------------------------------- 1 | import styled from "styled-components"; 2 | import defaultImg from "../images/room-1.jpeg"; 3 | const StyledHero = styled.header` 4 | min-height: 60vh; 5 | /* background: url(${defaultImg}); */ 6 | background: url(${props => (props.img ? props.img : defaultImg)}); 7 | display: flex; 8 | align-items: center; 9 | justify-content: center; 10 | `; 11 | 12 | export default StyledHero; 13 | -------------------------------------------------------------------------------- /src/components/Title.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | 3 | const Title = ({ title }) => { 4 | return ( 5 | <div className="section-title"> 6 | <h4>{title}</h4> 7 | <div /> 8 | </div> 9 | ); 10 | }; 11 | 12 | export default Title; 13 | -------------------------------------------------------------------------------- /src/context.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import items from "./data"; 3 | import Client from "./Contentful"; 4 | 5 | const RoomContext = React.createContext(); 6 | 7 | export default class RoomProvider extends Component { 8 | state = { 9 | rooms: [], 10 | sortedRooms: [], 11 | featuredRooms: [], 12 | loading: true, 13 | // 14 | type: "all", 15 | capacity: 1, 16 | price: 0, 17 | minPrice: 0, 18 | maxPrice: 0, 19 | minSize: 0, 20 | maxSize: 0, 21 | breakfast: false, 22 | pets: false 23 | }; 24 | 25 | // getData = async () => { 26 | // try { 27 | // let response = await Client.getEntries({ 28 | // content_type: "beachResortRoom" 29 | // }); 30 | // let rooms = this.formatData(response.items); 31 | 32 | // let featuredRooms = rooms.filter(room => room.featured === true); 33 | // // 34 | // let maxPrice = Math.max(...rooms.map(item => item.price)); 35 | // let maxSize = Math.max(...rooms.map(item => item.size)); 36 | // this.setState({ 37 | // rooms, 38 | // featuredRooms, 39 | // sortedRooms: rooms, 40 | // loading: false, 41 | // // 42 | // price: maxPrice, 43 | // maxPrice, 44 | // maxSize 45 | // }); 46 | // } catch (error) { 47 | // console.log(error); 48 | // } 49 | // }; 50 | 51 | componentDidMount() { 52 | // this.getData(); 53 | let rooms = this.formatData(items); 54 | let featuredRooms = rooms.filter(room => room.featured === true); 55 | // 56 | let maxPrice = Math.max(...rooms.map(item => item.price)); 57 | let maxSize = Math.max(...rooms.map(item => item.size)); 58 | this.setState({ 59 | rooms, 60 | featuredRooms, 61 | sortedRooms: rooms, 62 | loading: false, 63 | // 64 | price: maxPrice, 65 | maxPrice, 66 | maxSize 67 | }); 68 | } 69 | 70 | formatData(items) { 71 | let tempItems = items.map(item => { 72 | let id = item.sys.id; 73 | let images = item.fields.images.map(image => image.fields.file.url); 74 | 75 | let room = { ...item.fields, images, id }; 76 | return room; 77 | }); 78 | return tempItems; 79 | } 80 | getRoom = slug => { 81 | let tempRooms = [...this.state.rooms]; 82 | const room = tempRooms.find(room => room.slug === slug); 83 | return room; 84 | }; 85 | handleChange = event => { 86 | const target = event.target; 87 | const value = target.type === "checkbox" ? target.checked : target.value; 88 | const name = target.name; 89 | console.log(name, value); 90 | 91 | this.setState( 92 | { 93 | [name]: value 94 | }, 95 | this.filterRooms 96 | ); 97 | }; 98 | filterRooms = () => { 99 | let { 100 | rooms, 101 | type, 102 | capacity, 103 | price, 104 | minSize, 105 | maxSize, 106 | breakfast, 107 | pets 108 | } = this.state; 109 | 110 | let tempRooms = [...rooms]; 111 | // transform values 112 | // get capacity 113 | capacity = parseInt(capacity); 114 | price = parseInt(price); 115 | // filter by type 116 | if (type !== "all") { 117 | tempRooms = tempRooms.filter(room => room.type === type); 118 | } 119 | // filter by capacity 120 | if (capacity !== 1) { 121 | tempRooms = tempRooms.filter(room => room.capacity >= capacity); 122 | } 123 | // filter by price 124 | tempRooms = tempRooms.filter(room => room.price <= price); 125 | //filter by size 126 | tempRooms = tempRooms.filter( 127 | room => room.size >= minSize && room.size <= maxSize 128 | ); 129 | //filter by breakfast 130 | if (breakfast) { 131 | tempRooms = tempRooms.filter(room => room.breakfast === true); 132 | } 133 | //filter by pets 134 | if (pets) { 135 | tempRooms = tempRooms.filter(room => room.pets === true); 136 | } 137 | this.setState({ 138 | sortedRooms: tempRooms 139 | }); 140 | }; 141 | render() { 142 | return ( 143 | <RoomContext.Provider 144 | value={{ 145 | ...this.state, 146 | getRoom: this.getRoom, 147 | handleChange: this.handleChange 148 | }} 149 | > 150 | {this.props.children} 151 | </RoomContext.Provider> 152 | ); 153 | } 154 | } 155 | const RoomConsumer = RoomContext.Consumer; 156 | 157 | export { RoomProvider, RoomConsumer, RoomContext }; 158 | 159 | export function withRoomConsumer(Component) { 160 | return function ConsumerWrapper(props) { 161 | return ( 162 | <RoomConsumer> 163 | {value => <Component {...props} context={value} />} 164 | </RoomConsumer> 165 | ); 166 | }; 167 | } 168 | -------------------------------------------------------------------------------- /src/data.js: -------------------------------------------------------------------------------- 1 | import room1 from "./images/details-1.jpeg"; 2 | import room2 from "./images/details-2.jpeg"; 3 | import room3 from "./images/details-3.jpeg"; 4 | import room4 from "./images/details-4.jpeg"; 5 | import img1 from "./images/room-1.jpeg"; 6 | import img2 from "./images/room-2.jpeg"; 7 | import img3 from "./images/room-3.jpeg"; 8 | import img4 from "./images/room-4.jpeg"; 9 | import img5 from "./images/room-5.jpeg"; 10 | import img6 from "./images/room-6.jpeg"; 11 | import img7 from "./images/room-7.jpeg"; 12 | import img8 from "./images/room-8.jpeg"; 13 | import img9 from "./images/room-9.jpeg"; 14 | import img10 from "./images/room-10.jpeg"; 15 | import img11 from "./images/room-11.jpeg"; 16 | import img12 from "./images/room-12.jpeg"; 17 | 18 | export default [ 19 | { 20 | sys: { 21 | id: "1" 22 | }, 23 | fields: { 24 | name: "single economy", 25 | slug: "single-economy", 26 | type: "single", 27 | price: 100, 28 | size: 200, 29 | capacity: 1, 30 | pets: false, 31 | breakfast: false, 32 | featured: false, 33 | description: 34 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 35 | extras: [ 36 | "Plush pillows and breathable bed linens", 37 | "Soft, oversized bath towels", 38 | "Full-sized, pH-balanced toiletries", 39 | "Complimentary refreshments", 40 | "Adequate safety/security", 41 | "Internet", 42 | "Comfortable beds" 43 | ], 44 | images: [ 45 | { 46 | fields: { 47 | file: { 48 | url: img1 49 | } 50 | } 51 | }, 52 | { 53 | fields: { 54 | file: { 55 | url: room2 56 | } 57 | } 58 | }, 59 | { 60 | fields: { 61 | file: { 62 | url: room3 63 | } 64 | } 65 | }, 66 | { 67 | fields: { 68 | file: { 69 | url: room4 70 | } 71 | } 72 | } 73 | ] 74 | } 75 | }, 76 | { 77 | sys: { 78 | id: "2" 79 | }, 80 | fields: { 81 | name: "single basic", 82 | slug: "single-basic", 83 | type: "single", 84 | price: 150, 85 | size: 250, 86 | capacity: 1, 87 | pets: false, 88 | breakfast: false, 89 | featured: false, 90 | description: 91 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 92 | extras: [ 93 | "Plush pillows and breathable bed linens", 94 | "Soft, oversized bath towels", 95 | "Full-sized, pH-balanced toiletries", 96 | "Complimentary refreshments", 97 | "Adequate safety/security", 98 | "Internet", 99 | "Comfortable beds" 100 | ], 101 | images: [ 102 | { 103 | fields: { 104 | file: { 105 | url: img2 106 | } 107 | } 108 | }, 109 | { 110 | fields: { 111 | file: { 112 | url: room2 113 | } 114 | } 115 | }, 116 | { 117 | fields: { 118 | file: { 119 | url: room3 120 | } 121 | } 122 | }, 123 | { 124 | fields: { 125 | file: { 126 | url: room4 127 | } 128 | } 129 | } 130 | ] 131 | } 132 | }, 133 | { 134 | sys: { 135 | id: "3" 136 | }, 137 | fields: { 138 | name: "single standard", 139 | slug: "single-standard", 140 | type: "single", 141 | price: 250, 142 | size: 300, 143 | capacity: 1, 144 | pets: true, 145 | breakfast: false, 146 | featured: false, 147 | description: 148 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 149 | extras: [ 150 | "Plush pillows and breathable bed linens", 151 | "Soft, oversized bath towels", 152 | "Full-sized, pH-balanced toiletries", 153 | "Complimentary refreshments", 154 | "Adequate safety/security", 155 | "Internet", 156 | "Comfortable beds" 157 | ], 158 | images: [ 159 | { 160 | fields: { 161 | file: { 162 | url: img3 163 | } 164 | } 165 | }, 166 | { 167 | fields: { 168 | file: { 169 | url: room2 170 | } 171 | } 172 | }, 173 | { 174 | fields: { 175 | file: { 176 | url: room3 177 | } 178 | } 179 | }, 180 | { 181 | fields: { 182 | file: { 183 | url: room4 184 | } 185 | } 186 | } 187 | ] 188 | } 189 | }, 190 | { 191 | sys: { 192 | id: "4" 193 | }, 194 | fields: { 195 | name: "single deluxe", 196 | slug: "single-deluxe", 197 | type: "single", 198 | price: 300, 199 | size: 400, 200 | capacity: 1, 201 | pets: true, 202 | breakfast: true, 203 | featured: false, 204 | description: 205 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 206 | extras: [ 207 | "Plush pillows and breathable bed linens", 208 | "Soft, oversized bath towels", 209 | "Full-sized, pH-balanced toiletries", 210 | "Complimentary refreshments", 211 | "Adequate safety/security", 212 | "Internet", 213 | "Comfortable beds" 214 | ], 215 | images: [ 216 | { 217 | fields: { 218 | file: { 219 | url: img4 220 | } 221 | } 222 | }, 223 | { 224 | fields: { 225 | file: { 226 | url: room2 227 | } 228 | } 229 | }, 230 | { 231 | fields: { 232 | file: { 233 | url: room3 234 | } 235 | } 236 | }, 237 | { 238 | fields: { 239 | file: { 240 | url: room4 241 | } 242 | } 243 | } 244 | ] 245 | } 246 | }, 247 | { 248 | sys: { 249 | id: "5" 250 | }, 251 | fields: { 252 | name: "double economy", 253 | slug: "double-economy", 254 | type: "double", 255 | price: 200, 256 | size: 300, 257 | capacity: 2, 258 | pets: false, 259 | breakfast: false, 260 | featured: false, 261 | description: 262 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 263 | extras: [ 264 | "Plush pillows and breathable bed linens", 265 | "Soft, oversized bath towels", 266 | "Full-sized, pH-balanced toiletries", 267 | "Complimentary refreshments", 268 | "Adequate safety/security", 269 | "Internet", 270 | "Comfortable beds" 271 | ], 272 | images: [ 273 | { 274 | fields: { 275 | file: { 276 | url: img5 277 | } 278 | } 279 | }, 280 | { 281 | fields: { 282 | file: { 283 | url: room2 284 | } 285 | } 286 | }, 287 | { 288 | fields: { 289 | file: { 290 | url: room3 291 | } 292 | } 293 | }, 294 | { 295 | fields: { 296 | file: { 297 | url: room4 298 | } 299 | } 300 | } 301 | ] 302 | } 303 | }, 304 | { 305 | sys: { 306 | id: "6" 307 | }, 308 | fields: { 309 | name: "double basic", 310 | slug: "double-basic", 311 | type: "double", 312 | price: 250, 313 | size: 350, 314 | capacity: 2, 315 | pets: false, 316 | breakfast: false, 317 | featured: false, 318 | description: 319 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 320 | extras: [ 321 | "Plush pillows and breathable bed linens", 322 | "Soft, oversized bath towels", 323 | "Full-sized, pH-balanced toiletries", 324 | "Complimentary refreshments", 325 | "Adequate safety/security", 326 | "Internet", 327 | "Comfortable beds" 328 | ], 329 | images: [ 330 | { 331 | fields: { 332 | file: { 333 | url: img6 334 | } 335 | } 336 | }, 337 | { 338 | fields: { 339 | file: { 340 | url: room2 341 | } 342 | } 343 | }, 344 | { 345 | fields: { 346 | file: { 347 | url: room3 348 | } 349 | } 350 | }, 351 | { 352 | fields: { 353 | file: { 354 | url: room4 355 | } 356 | } 357 | } 358 | ] 359 | } 360 | }, 361 | { 362 | sys: { 363 | id: "7" 364 | }, 365 | fields: { 366 | name: "double standard", 367 | slug: "double-standard", 368 | type: "double", 369 | price: 300, 370 | size: 400, 371 | capacity: 2, 372 | pets: true, 373 | breakfast: false, 374 | featured: false, 375 | description: 376 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 377 | extras: [ 378 | "Plush pillows and breathable bed linens", 379 | "Soft, oversized bath towels", 380 | "Full-sized, pH-balanced toiletries", 381 | "Complimentary refreshments", 382 | "Adequate safety/security", 383 | "Internet", 384 | "Comfortable beds" 385 | ], 386 | images: [ 387 | { 388 | fields: { 389 | file: { 390 | url: img7 391 | } 392 | } 393 | }, 394 | { 395 | fields: { 396 | file: { 397 | url: room2 398 | } 399 | } 400 | }, 401 | { 402 | fields: { 403 | file: { 404 | url: room3 405 | } 406 | } 407 | }, 408 | { 409 | fields: { 410 | file: { 411 | url: room4 412 | } 413 | } 414 | } 415 | ] 416 | } 417 | }, 418 | { 419 | sys: { 420 | id: "8" 421 | }, 422 | fields: { 423 | name: "double deluxe", 424 | slug: "double-deluxe", 425 | type: "double", 426 | price: 400, 427 | size: 500, 428 | capacity: 2, 429 | pets: true, 430 | breakfast: true, 431 | featured: true, 432 | description: 433 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 434 | extras: [ 435 | "Plush pillows and breathable bed linens", 436 | "Soft, oversized bath towels", 437 | "Full-sized, pH-balanced toiletries", 438 | "Complimentary refreshments", 439 | "Adequate safety/security", 440 | "Internet", 441 | "Comfortable beds" 442 | ], 443 | images: [ 444 | { 445 | fields: { 446 | file: { 447 | url: img8 448 | } 449 | } 450 | }, 451 | { 452 | fields: { 453 | file: { 454 | url: room2 455 | } 456 | } 457 | }, 458 | { 459 | fields: { 460 | file: { 461 | url: room3 462 | } 463 | } 464 | }, 465 | { 466 | fields: { 467 | file: { 468 | url: room4 469 | } 470 | } 471 | } 472 | ] 473 | } 474 | }, 475 | { 476 | sys: { 477 | id: "9" 478 | }, 479 | fields: { 480 | name: "family economy", 481 | slug: "family-economy", 482 | type: "family", 483 | price: 300, 484 | size: 500, 485 | capacity: 3, 486 | pets: false, 487 | breakfast: false, 488 | featured: false, 489 | description: 490 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 491 | extras: [ 492 | "Plush pillows and breathable bed linens", 493 | "Soft, oversized bath towels", 494 | "Full-sized, pH-balanced toiletries", 495 | "Complimentary refreshments", 496 | "Adequate safety/security", 497 | "Internet", 498 | "Comfortable beds" 499 | ], 500 | images: [ 501 | { 502 | fields: { 503 | file: { 504 | url: img9 505 | } 506 | } 507 | }, 508 | { 509 | fields: { 510 | file: { 511 | url: room2 512 | } 513 | } 514 | }, 515 | { 516 | fields: { 517 | file: { 518 | url: room3 519 | } 520 | } 521 | }, 522 | { 523 | fields: { 524 | file: { 525 | url: room4 526 | } 527 | } 528 | } 529 | ] 530 | } 531 | }, 532 | { 533 | sys: { 534 | id: "10" 535 | }, 536 | fields: { 537 | name: "family basic", 538 | slug: "family-basic", 539 | type: "family", 540 | price: 350, 541 | size: 550, 542 | capacity: 4, 543 | pets: false, 544 | breakfast: false, 545 | featured: false, 546 | description: 547 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 548 | extras: [ 549 | "Plush pillows and breathable bed linens", 550 | "Soft, oversized bath towels", 551 | "Full-sized, pH-balanced toiletries", 552 | "Complimentary refreshments", 553 | "Adequate safety/security", 554 | "Internet", 555 | "Comfortable beds" 556 | ], 557 | images: [ 558 | { 559 | fields: { 560 | file: { 561 | url: img10 562 | } 563 | } 564 | }, 565 | { 566 | fields: { 567 | file: { 568 | url: room2 569 | } 570 | } 571 | }, 572 | { 573 | fields: { 574 | file: { 575 | url: room3 576 | } 577 | } 578 | }, 579 | { 580 | fields: { 581 | file: { 582 | url: room4 583 | } 584 | } 585 | } 586 | ] 587 | } 588 | }, 589 | { 590 | sys: { 591 | id: "11" 592 | }, 593 | fields: { 594 | name: "family standard", 595 | slug: "family-standard", 596 | type: "family", 597 | price: 400, 598 | size: 600, 599 | capacity: 5, 600 | pets: true, 601 | breakfast: false, 602 | featured: false, 603 | description: 604 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 605 | extras: [ 606 | "Plush pillows and breathable bed linens", 607 | "Soft, oversized bath towels", 608 | "Full-sized, pH-balanced toiletries", 609 | "Complimentary refreshments", 610 | "Adequate safety/security", 611 | "Internet", 612 | "Comfortable beds" 613 | ], 614 | images: [ 615 | { 616 | fields: { 617 | file: { 618 | url: img11 619 | } 620 | } 621 | }, 622 | { 623 | fields: { 624 | file: { 625 | url: room2 626 | } 627 | } 628 | }, 629 | { 630 | fields: { 631 | file: { 632 | url: room3 633 | } 634 | } 635 | }, 636 | { 637 | fields: { 638 | file: { 639 | url: room4 640 | } 641 | } 642 | } 643 | ] 644 | } 645 | }, 646 | { 647 | sys: { 648 | id: "12" 649 | }, 650 | fields: { 651 | name: "family deluxe", 652 | slug: "family-deluxe", 653 | type: "family", 654 | price: 500, 655 | size: 700, 656 | capacity: 6, 657 | pets: true, 658 | breakfast: true, 659 | featured: true, 660 | description: 661 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 662 | extras: [ 663 | "Plush pillows and breathable bed linens", 664 | "Soft, oversized bath towels", 665 | "Full-sized, pH-balanced toiletries", 666 | "Complimentary refreshments", 667 | "Adequate safety/security", 668 | "Internet", 669 | "Comfortable beds" 670 | ], 671 | images: [ 672 | { 673 | fields: { 674 | file: { 675 | url: img12 676 | } 677 | } 678 | }, 679 | { 680 | fields: { 681 | file: { 682 | url: room2 683 | } 684 | } 685 | }, 686 | { 687 | fields: { 688 | file: { 689 | url: room3 690 | } 691 | } 692 | }, 693 | { 694 | fields: { 695 | file: { 696 | url: room4 697 | } 698 | } 699 | } 700 | ] 701 | } 702 | }, 703 | { 704 | sys: { 705 | id: "13" 706 | }, 707 | fields: { 708 | name: "presidential", 709 | slug: "presidential-room", 710 | type: "presidential", 711 | price: 600, 712 | size: 1000, 713 | capacity: 10, 714 | pets: true, 715 | breakfast: true, 716 | featured: true, 717 | description: 718 | "Street art edison bulb gluten-free, tofu try-hard lumbersexual brooklyn tattooed pickled chambray. Actually humblebrag next level, deep v art party wolf tofu direct trade readymade sustainable hell of banjo. Organic authentic subway tile cliche palo santo, street art XOXO dreamcatcher retro sriracha portland air plant kitsch stumptown. Austin small batch squid gastropub. Pabst pug tumblr gochujang offal retro cloud bread bushwick semiotics before they sold out sartorial literally mlkshk. Vaporware hashtag vice, sartorial before they sold out pok pok health goth trust fund cray.", 719 | extras: [ 720 | "Plush pillows and breathable bed linens", 721 | "Soft, oversized bath towels", 722 | "Full-sized, pH-balanced toiletries", 723 | "Complimentary refreshments", 724 | "Adequate safety/security", 725 | "Internet", 726 | "Comfortable beds" 727 | ], 728 | images: [ 729 | { 730 | fields: { 731 | file: { 732 | url: room1 733 | } 734 | } 735 | }, 736 | { 737 | fields: { 738 | file: { 739 | url: room2 740 | } 741 | } 742 | }, 743 | { 744 | fields: { 745 | file: { 746 | url: room3 747 | } 748 | } 749 | }, 750 | { 751 | fields: { 752 | file: { 753 | url: room4 754 | } 755 | } 756 | } 757 | ] 758 | } 759 | } 760 | ]; 761 | -------------------------------------------------------------------------------- /src/images/defaultBcg.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/defaultBcg.jpeg -------------------------------------------------------------------------------- /src/images/details-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/details-1.jpeg -------------------------------------------------------------------------------- /src/images/details-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/details-2.jpeg -------------------------------------------------------------------------------- /src/images/details-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/details-3.jpeg -------------------------------------------------------------------------------- /src/images/details-4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/details-4.jpeg -------------------------------------------------------------------------------- /src/images/gif/loading-arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/gif/loading-arrow.gif -------------------------------------------------------------------------------- /src/images/gif/loading-gear.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/gif/loading-gear.gif -------------------------------------------------------------------------------- /src/images/logo.svg: -------------------------------------------------------------------------------- 1 | <svg width="250" height="37" viewBox="0 0 250 37" fill="none" xmlns="http://www.w3.org/2000/svg"> 2 | <rect x="1" y="1" width="248" height="35" stroke="#222222" stroke-width="2"/> 3 | <g filter="url(#filter0_d)"> 4 | <path d="M23.2807 25.24C23.8567 25.24 24.3287 25.172 24.6967 25.036C25.0727 24.892 25.3727 24.704 25.5967 24.472C25.8207 24.24 25.9767 23.976 26.0647 23.68C26.1527 23.376 26.1967 23.064 26.1967 22.744C26.1967 22.392 26.1447 22.076 26.0407 21.796C25.9367 21.516 25.7687 21.28 25.5367 21.088C25.3047 20.888 25.0007 20.736 24.6247 20.632C24.2567 20.528 23.8007 20.476 23.2567 20.476H20.1967V25.24H23.2807ZM20.1967 13.324V18.028H22.5607C23.0647 18.028 23.5127 17.988 23.9047 17.908C24.2967 17.82 24.6247 17.684 24.8887 17.5C25.1527 17.316 25.3527 17.076 25.4887 16.78C25.6327 16.476 25.7047 16.112 25.7047 15.688C25.7047 15.256 25.6447 14.888 25.5247 14.584C25.4127 14.28 25.2367 14.036 24.9967 13.852C24.7567 13.668 24.4527 13.536 24.0847 13.456C23.7167 13.368 23.2807 13.324 22.7767 13.324H20.1967ZM22.7767 10.588C23.9447 10.588 24.9407 10.7 25.7647 10.924C26.5967 11.14 27.2727 11.452 27.7927 11.86C28.3207 12.26 28.7047 12.748 28.9447 13.324C29.1847 13.9 29.3047 14.544 29.3047 15.256C29.3047 15.656 29.2447 16.04 29.1247 16.408C29.0127 16.776 28.8327 17.124 28.5847 17.452C28.3447 17.772 28.0327 18.064 27.6487 18.328C27.2647 18.592 26.8087 18.816 26.2807 19C27.4647 19.28 28.3407 19.736 28.9087 20.368C29.4767 21 29.7607 21.82 29.7607 22.828C29.7607 23.564 29.6167 24.248 29.3287 24.88C29.0487 25.512 28.6367 26.06 28.0927 26.524C27.5487 26.988 26.8767 27.352 26.0767 27.616C25.2847 27.872 24.3727 28 23.3407 28H16.5967V10.588H22.7767ZM46.9387 20.356C46.9387 20.012 46.8907 19.684 46.7947 19.372C46.7067 19.06 46.5627 18.784 46.3627 18.544C46.1627 18.304 45.9107 18.116 45.6067 17.98C45.3027 17.836 44.9387 17.764 44.5147 17.764C43.7227 17.764 43.1027 17.988 42.6547 18.436C42.2067 18.884 41.9147 19.524 41.7787 20.356H46.9387ZM41.7307 22.324C41.8347 23.484 42.1627 24.332 42.7147 24.868C43.2747 25.404 44.0027 25.672 44.8987 25.672C45.3547 25.672 45.7467 25.62 46.0747 25.516C46.4107 25.404 46.7027 25.284 46.9507 25.156C47.2067 25.02 47.4347 24.9 47.6347 24.796C47.8427 24.684 48.0467 24.628 48.2467 24.628C48.5027 24.628 48.7027 24.724 48.8467 24.916L49.8067 26.116C49.4547 26.524 49.0667 26.864 48.6427 27.136C48.2187 27.4 47.7787 27.612 47.3227 27.772C46.8667 27.924 46.4067 28.028 45.9427 28.084C45.4787 28.148 45.0307 28.18 44.5987 28.18C43.7347 28.18 42.9267 28.04 42.1747 27.76C41.4307 27.472 40.7787 27.052 40.2187 26.5C39.6667 25.94 39.2307 25.248 38.9107 24.424C38.5907 23.6 38.4307 22.644 38.4307 21.556C38.4307 20.716 38.5667 19.924 38.8387 19.18C39.1187 18.436 39.5187 17.788 40.0387 17.236C40.5587 16.684 41.1907 16.248 41.9347 15.928C42.6787 15.6 43.5187 15.436 44.4547 15.436C45.2467 15.436 45.9747 15.564 46.6387 15.82C47.3107 16.068 47.8867 16.432 48.3667 16.912C48.8547 17.392 49.2307 17.984 49.4947 18.688C49.7667 19.384 49.9027 20.18 49.9027 21.076C49.9027 21.324 49.8907 21.528 49.8667 21.688C49.8427 21.848 49.8027 21.976 49.7467 22.072C49.6907 22.168 49.6147 22.236 49.5187 22.276C49.4227 22.308 49.2987 22.324 49.1467 22.324H41.7307ZM65.6258 22.828C64.8178 22.868 64.1458 22.94 63.6098 23.044C63.0738 23.14 62.6458 23.268 62.3258 23.428C62.0138 23.588 61.7898 23.772 61.6538 23.98C61.5258 24.18 61.4618 24.4 61.4618 24.64C61.4618 25.12 61.5938 25.46 61.8578 25.66C62.1298 25.86 62.5058 25.96 62.9858 25.96C63.5378 25.96 64.0138 25.864 64.4138 25.672C64.8218 25.472 65.2258 25.164 65.6258 24.748V22.828ZM58.8818 17.344C59.6098 16.688 60.4138 16.2 61.2938 15.88C62.1738 15.552 63.1258 15.388 64.1498 15.388C64.8858 15.388 65.5418 15.508 66.1178 15.748C66.7018 15.988 67.1938 16.324 67.5938 16.756C68.0018 17.18 68.3138 17.688 68.5298 18.28C68.7458 18.872 68.8538 19.52 68.8538 20.224V28H67.3418C67.0298 28 66.7898 27.956 66.6218 27.868C66.4618 27.78 66.3298 27.596 66.2258 27.316L65.9618 26.524C65.6498 26.796 65.3458 27.036 65.0498 27.244C64.7618 27.452 64.4618 27.628 64.1498 27.772C63.8378 27.908 63.5018 28.012 63.1418 28.084C62.7898 28.156 62.3938 28.192 61.9538 28.192C61.4098 28.192 60.9138 28.12 60.4658 27.976C60.0178 27.832 59.6298 27.62 59.3018 27.34C58.9818 27.052 58.7338 26.696 58.5578 26.272C58.3818 25.848 58.2938 25.36 58.2938 24.808C58.2938 24.352 58.4098 23.896 58.6418 23.44C58.8738 22.984 59.2698 22.572 59.8298 22.204C60.3978 21.828 61.1498 21.52 62.0858 21.28C63.0298 21.032 64.2098 20.892 65.6258 20.86V20.224C65.6258 19.448 65.4618 18.88 65.1338 18.52C64.8058 18.152 64.3338 17.968 63.7178 17.968C63.2618 17.968 62.8818 18.024 62.5778 18.136C62.2738 18.24 62.0058 18.356 61.7738 18.484C61.5498 18.604 61.3378 18.72 61.1378 18.832C60.9378 18.936 60.7058 18.988 60.4418 18.988C60.2098 18.988 60.0138 18.932 59.8538 18.82C59.7018 18.7 59.5738 18.56 59.4698 18.4L58.8818 17.344ZM87.6151 18.388C87.5191 18.516 87.4231 18.616 87.3271 18.688C87.2311 18.752 87.0951 18.784 86.9191 18.784C86.7591 18.784 86.6031 18.74 86.4511 18.652C86.3071 18.556 86.1391 18.456 85.9471 18.352C85.7551 18.24 85.5271 18.14 85.2631 18.052C84.9991 17.956 84.6711 17.908 84.2791 17.908C83.7831 17.908 83.3511 18 82.9831 18.184C82.6231 18.36 82.3231 18.616 82.0831 18.952C81.8431 19.288 81.6631 19.696 81.5431 20.176C81.4311 20.656 81.3751 21.196 81.3751 21.796C81.3751 23.06 81.6271 24.028 82.1311 24.7C82.6431 25.372 83.3391 25.708 84.2191 25.708C84.5231 25.708 84.7831 25.684 84.9991 25.636C85.2231 25.58 85.4151 25.512 85.5751 25.432C85.7431 25.352 85.8871 25.264 86.0071 25.168C86.1271 25.072 86.2391 24.984 86.3431 24.904C86.4551 24.824 86.5631 24.76 86.6671 24.712C86.7791 24.656 86.9031 24.628 87.0391 24.628C87.2951 24.628 87.4951 24.724 87.6391 24.916L88.5871 26.116C88.2351 26.524 87.8591 26.864 87.4591 27.136C87.0591 27.4 86.6431 27.612 86.2111 27.772C85.7871 27.924 85.3551 28.028 84.9151 28.084C84.4751 28.148 84.0391 28.18 83.6071 28.18C82.8471 28.18 82.1271 28.04 81.4471 27.76C80.7671 27.472 80.1711 27.056 79.6591 26.512C79.1471 25.968 78.7391 25.3 78.4351 24.508C78.1391 23.716 77.9911 22.812 77.9911 21.796C77.9911 20.892 78.1231 20.052 78.3871 19.276C78.6511 18.5 79.0391 17.828 79.5511 17.26C80.0631 16.692 80.6951 16.248 81.4471 15.928C82.2071 15.6 83.0831 15.436 84.0751 15.436C85.0191 15.436 85.8471 15.588 86.5591 15.892C87.2711 16.196 87.9111 16.636 88.4791 17.212L87.6151 18.388ZM100.776 16.84C101.008 16.632 101.248 16.444 101.496 16.276C101.752 16.1 102.02 15.952 102.3 15.832C102.588 15.704 102.892 15.608 103.212 15.544C103.54 15.472 103.892 15.436 104.268 15.436C104.94 15.436 105.536 15.552 106.056 15.784C106.576 16.016 107.012 16.34 107.364 16.756C107.724 17.172 107.992 17.668 108.168 18.244C108.352 18.82 108.444 19.452 108.444 20.14V28H105.144V20.14C105.144 19.452 104.984 18.92 104.664 18.544C104.344 18.16 103.872 17.968 103.248 17.968C102.784 17.968 102.348 18.068 101.94 18.268C101.532 18.468 101.144 18.748 100.776 19.108V28H97.4641V10.108H100.776V16.84Z" fill="#222222"/> 5 | <path d="M137.081 18.832C137.665 18.832 138.169 18.76 138.593 18.616C139.017 18.464 139.365 18.26 139.637 18.004C139.909 17.74 140.109 17.432 140.237 17.08C140.365 16.728 140.429 16.344 140.429 15.928C140.429 15.104 140.153 14.464 139.601 14.008C139.057 13.552 138.225 13.324 137.105 13.324H135.233V18.832H137.081ZM145.517 28H142.265C141.657 28 141.217 27.768 140.945 27.304L137.633 21.964C137.497 21.756 137.345 21.604 137.177 21.508C137.017 21.412 136.777 21.364 136.457 21.364H135.233V28H131.633V10.588H137.105C138.321 10.588 139.361 10.716 140.225 10.972C141.089 11.22 141.797 11.568 142.349 12.016C142.901 12.464 143.305 13 143.561 13.624C143.817 14.24 143.945 14.92 143.945 15.664C143.945 16.24 143.861 16.788 143.693 17.308C143.533 17.82 143.297 18.288 142.985 18.712C142.673 19.136 142.289 19.512 141.833 19.84C141.377 20.168 140.857 20.432 140.273 20.632C140.505 20.76 140.721 20.916 140.921 21.1C141.121 21.276 141.301 21.488 141.461 21.736L145.517 28ZM160.99 20.356C160.99 20.012 160.942 19.684 160.846 19.372C160.758 19.06 160.614 18.784 160.414 18.544C160.214 18.304 159.962 18.116 159.658 17.98C159.354 17.836 158.99 17.764 158.566 17.764C157.774 17.764 157.154 17.988 156.706 18.436C156.258 18.884 155.966 19.524 155.83 20.356H160.99ZM155.782 22.324C155.886 23.484 156.214 24.332 156.766 24.868C157.326 25.404 158.054 25.672 158.95 25.672C159.406 25.672 159.798 25.62 160.126 25.516C160.462 25.404 160.754 25.284 161.002 25.156C161.258 25.02 161.486 24.9 161.686 24.796C161.894 24.684 162.098 24.628 162.298 24.628C162.554 24.628 162.754 24.724 162.898 24.916L163.858 26.116C163.506 26.524 163.118 26.864 162.694 27.136C162.27 27.4 161.83 27.612 161.374 27.772C160.918 27.924 160.458 28.028 159.994 28.084C159.53 28.148 159.082 28.18 158.65 28.18C157.786 28.18 156.978 28.04 156.226 27.76C155.482 27.472 154.83 27.052 154.27 26.5C153.718 25.94 153.282 25.248 152.962 24.424C152.642 23.6 152.482 22.644 152.482 21.556C152.482 20.716 152.618 19.924 152.89 19.18C153.17 18.436 153.57 17.788 154.09 17.236C154.61 16.684 155.242 16.248 155.986 15.928C156.73 15.6 157.57 15.436 158.506 15.436C159.298 15.436 160.026 15.564 160.69 15.82C161.362 16.068 161.938 16.432 162.418 16.912C162.906 17.392 163.282 17.984 163.546 18.688C163.818 19.384 163.954 20.18 163.954 21.076C163.954 21.324 163.942 21.528 163.918 21.688C163.894 21.848 163.854 21.976 163.798 22.072C163.742 22.168 163.666 22.236 163.57 22.276C163.474 22.308 163.35 22.324 163.198 22.324H155.782ZM180.901 18.172C180.813 18.308 180.721 18.408 180.625 18.472C180.537 18.528 180.413 18.556 180.253 18.556C180.093 18.556 179.929 18.516 179.761 18.436C179.593 18.356 179.405 18.272 179.197 18.184C178.989 18.088 178.749 18 178.477 17.92C178.213 17.84 177.905 17.8 177.553 17.8C177.017 17.8 176.601 17.912 176.305 18.136C176.009 18.352 175.861 18.64 175.861 19C175.861 19.248 175.945 19.456 176.113 19.624C176.281 19.792 176.501 19.94 176.773 20.068C177.053 20.188 177.369 20.304 177.721 20.416C178.073 20.52 178.433 20.636 178.801 20.764C179.177 20.892 179.541 21.04 179.893 21.208C180.245 21.376 180.557 21.588 180.829 21.844C181.109 22.092 181.333 22.396 181.501 22.756C181.669 23.108 181.753 23.536 181.753 24.04C181.753 24.64 181.641 25.196 181.417 25.708C181.201 26.212 180.881 26.648 180.457 27.016C180.033 27.384 179.505 27.672 178.873 27.88C178.241 28.088 177.513 28.192 176.689 28.192C176.265 28.192 175.845 28.152 175.429 28.072C175.013 28 174.617 27.896 174.241 27.76C173.865 27.616 173.513 27.452 173.185 27.268C172.857 27.084 172.573 26.884 172.333 26.668L173.101 25.432C173.189 25.288 173.297 25.176 173.425 25.096C173.561 25.016 173.733 24.976 173.941 24.976C174.141 24.976 174.325 25.028 174.493 25.132C174.661 25.228 174.849 25.336 175.057 25.456C175.265 25.568 175.509 25.676 175.789 25.78C176.077 25.876 176.433 25.924 176.857 25.924C177.177 25.924 177.453 25.888 177.685 25.816C177.917 25.744 178.105 25.648 178.249 25.528C178.393 25.4 178.497 25.26 178.561 25.108C178.633 24.948 178.669 24.784 178.669 24.616C178.669 24.344 178.581 24.124 178.405 23.956C178.237 23.78 178.013 23.628 177.733 23.5C177.461 23.372 177.145 23.256 176.785 23.152C176.425 23.048 176.057 22.932 175.681 22.804C175.313 22.676 174.949 22.524 174.589 22.348C174.237 22.172 173.921 21.952 173.641 21.688C173.369 21.416 173.145 21.084 172.969 20.692C172.801 20.3 172.717 19.824 172.717 19.264C172.717 18.752 172.817 18.268 173.017 17.812C173.217 17.348 173.517 16.94 173.917 16.588C174.317 16.236 174.813 15.956 175.405 15.748C176.005 15.54 176.697 15.436 177.481 15.436C178.361 15.436 179.161 15.58 179.881 15.868C180.601 16.156 181.193 16.532 181.657 16.996L180.901 18.172ZM196.607 15.436C197.535 15.436 198.379 15.584 199.139 15.88C199.899 16.176 200.551 16.6 201.095 17.152C201.639 17.696 202.059 18.36 202.355 19.144C202.651 19.928 202.799 20.812 202.799 21.796C202.799 22.78 202.651 23.668 202.355 24.46C202.059 25.244 201.639 25.912 201.095 26.464C200.551 27.016 199.899 27.44 199.139 27.736C198.379 28.032 197.535 28.18 196.607 28.18C195.671 28.18 194.819 28.032 194.051 27.736C193.291 27.44 192.639 27.016 192.095 26.464C191.551 25.912 191.127 25.244 190.823 24.46C190.527 23.668 190.379 22.78 190.379 21.796C190.379 20.812 190.527 19.928 190.823 19.144C191.127 18.36 191.551 17.696 192.095 17.152C192.639 16.6 193.291 16.176 194.051 15.88C194.819 15.584 195.671 15.436 196.607 15.436ZM196.607 25.696C197.551 25.696 198.251 25.372 198.707 24.724C199.163 24.068 199.391 23.096 199.391 21.808C199.391 20.528 199.163 19.564 198.707 18.916C198.251 18.26 197.551 17.932 196.607 17.932C195.639 17.932 194.927 18.26 194.471 18.916C194.015 19.564 193.787 20.528 193.787 21.808C193.787 23.096 194.015 24.068 194.471 24.724C194.927 25.372 195.639 25.696 196.607 25.696ZM215.186 17.668C215.578 16.964 216.03 16.408 216.542 16C217.054 15.592 217.654 15.388 218.342 15.388C218.902 15.388 219.358 15.52 219.71 15.784L219.494 18.232C219.454 18.392 219.39 18.504 219.302 18.568C219.222 18.624 219.11 18.652 218.966 18.652C218.838 18.652 218.654 18.636 218.414 18.604C218.174 18.564 217.95 18.544 217.742 18.544C217.438 18.544 217.166 18.588 216.926 18.676C216.694 18.764 216.486 18.888 216.302 19.048C216.118 19.208 215.95 19.404 215.798 19.636C215.654 19.868 215.518 20.132 215.39 20.428V28H212.078V15.628H214.034C214.37 15.628 214.602 15.688 214.73 15.808C214.858 15.928 214.95 16.136 215.006 16.432L215.186 17.668ZM232.597 28.192C232.037 28.192 231.541 28.112 231.109 27.952C230.677 27.784 230.313 27.548 230.017 27.244C229.721 26.94 229.497 26.572 229.345 26.14C229.193 25.708 229.117 25.22 229.117 24.676V18.004H227.953C227.777 18.004 227.625 17.948 227.497 17.836C227.377 17.724 227.317 17.556 227.317 17.332V16.036L229.297 15.676L229.969 12.472C230.057 12.112 230.301 11.932 230.701 11.932H232.429V15.7H235.621V18.004H232.429V24.46C232.429 24.804 232.509 25.076 232.669 25.276C232.837 25.476 233.073 25.576 233.377 25.576C233.537 25.576 233.673 25.56 233.785 25.528C233.897 25.488 233.993 25.448 234.073 25.408C234.153 25.36 234.225 25.32 234.289 25.288C234.361 25.248 234.433 25.228 234.505 25.228C234.601 25.228 234.681 25.252 234.745 25.3C234.809 25.34 234.873 25.412 234.937 25.516L235.933 27.1C235.477 27.46 234.961 27.732 234.385 27.916C233.809 28.1 233.213 28.192 232.597 28.192Z" fill="#AF9A7D"/> 6 | </g> 7 | <defs> 8 | <filter id="filter0_d" x="12.5967" y="10.108" width="227.336" height="26.084" filterUnits="userSpaceOnUse" color-interpolation-filters="sRGB"> 9 | <feFlood flood-opacity="0" result="BackgroundImageFix"/> 10 | <feColorMatrix in="SourceAlpha" type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 127 0"/> 11 | <feOffset dy="4"/> 12 | <feGaussianBlur stdDeviation="2"/> 13 | <feColorMatrix type="matrix" values="0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0.25 0"/> 14 | <feBlend mode="normal" in2="BackgroundImageFix" result="effect1_dropShadow"/> 15 | <feBlend mode="normal" in="SourceGraphic" in2="effect1_dropShadow" result="shape"/> 16 | </filter> 17 | </defs> 18 | </svg> 19 | -------------------------------------------------------------------------------- /src/images/room-1.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-1.jpeg -------------------------------------------------------------------------------- /src/images/room-10.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-10.jpeg -------------------------------------------------------------------------------- /src/images/room-11.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-11.jpeg -------------------------------------------------------------------------------- /src/images/room-12.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-12.jpeg -------------------------------------------------------------------------------- /src/images/room-2.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-2.jpeg -------------------------------------------------------------------------------- /src/images/room-3.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-3.jpeg -------------------------------------------------------------------------------- /src/images/room-4.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-4.jpeg -------------------------------------------------------------------------------- /src/images/room-5.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-5.jpeg -------------------------------------------------------------------------------- /src/images/room-6.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-6.jpeg -------------------------------------------------------------------------------- /src/images/room-7.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-7.jpeg -------------------------------------------------------------------------------- /src/images/room-8.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-8.jpeg -------------------------------------------------------------------------------- /src/images/room-9.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-beach-resort-project/9c06f54c8548579846d47abf9afac379ad9998cc/src/images/room-9.jpeg -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | padding: 0; 4 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 5 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 6 | sans-serif; 7 | -webkit-font-smoothing: antialiased; 8 | -moz-osx-font-smoothing: grayscale; 9 | } 10 | 11 | code { 12 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 13 | monospace; 14 | } 15 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import ReactDOM from "react-dom"; 3 | // import './index.css'; 4 | import App from "./App"; 5 | import * as serviceWorker from "./serviceWorker"; 6 | import { RoomProvider } from "./context"; 7 | import { BrowserRouter } from "react-router-dom"; 8 | 9 | ReactDOM.render( 10 | <RoomProvider> 11 | <BrowserRouter> 12 | <App /> 13 | </BrowserRouter> 14 | </RoomProvider>, 15 | document.getElementById("root") 16 | ); 17 | 18 | // If you want your app to work offline and load faster, you can change 19 | // unregister() to register() below. Note this comes with some pitfalls. 20 | // Learn more about service workers: https://bit.ly/CRA-PWA 21 | serviceWorker.unregister(); 22 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 841.9 595.3"> 2 | <g fill="#61DAFB"> 3 | <path d="M666.3 296.5c0-32.5-40.7-63.3-103.1-82.4 14.4-63.6 8-114.2-20.2-130.4-6.5-3.8-14.1-5.6-22.4-5.6v22.3c4.6 0 8.3.9 11.4 2.6 13.6 7.8 19.5 37.5 14.9 75.7-1.1 9.4-2.9 19.3-5.1 29.4-19.6-4.8-41-8.5-63.5-10.9-13.5-18.5-27.5-35.3-41.6-50 32.6-30.3 63.2-46.9 84-46.9V78c-27.5 0-63.5 19.6-99.9 53.6-36.4-33.8-72.4-53.2-99.9-53.2v22.3c20.7 0 51.4 16.5 84 46.6-14 14.7-28 31.4-41.3 49.9-22.6 2.4-44 6.1-63.6 11-2.3-10-4-19.7-5.2-29-4.7-38.2 1.1-67.9 14.6-75.8 3-1.8 6.9-2.6 11.5-2.6V78.5c-8.4 0-16 1.8-22.6 5.6-28.1 16.2-34.4 66.7-19.9 130.1-62.2 19.2-102.7 49.9-102.7 82.3 0 32.5 40.7 63.3 103.1 82.4-14.4 63.6-8 114.2 20.2 130.4 6.5 3.8 14.1 5.6 22.5 5.6 27.5 0 63.5-19.6 99.9-53.6 36.4 33.8 72.4 53.2 99.9 53.2 8.4 0 16-1.8 22.6-5.6 28.1-16.2 34.4-66.7 19.9-130.1 62-19.1 102.5-49.9 102.5-82.3zm-130.2-66.7c-3.7 12.9-8.3 26.2-13.5 39.5-4.1-8-8.4-16-13.1-24-4.6-8-9.5-15.8-14.4-23.4 14.2 2.1 27.9 4.7 41 7.9zm-45.8 106.5c-7.8 13.5-15.8 26.3-24.1 38.2-14.9 1.3-30 2-45.2 2-15.1 0-30.2-.7-45-1.9-8.3-11.9-16.4-24.6-24.2-38-7.6-13.1-14.5-26.4-20.8-39.8 6.2-13.4 13.2-26.8 20.7-39.9 7.8-13.5 15.8-26.3 24.1-38.2 14.9-1.3 30-2 45.2-2 15.1 0 30.2.7 45 1.9 8.3 11.9 16.4 24.6 24.2 38 7.6 13.1 14.5 26.4 20.8 39.8-6.3 13.4-13.2 26.8-20.7 39.9zm32.3-13c5.4 13.4 10 26.8 13.8 39.8-13.1 3.2-26.9 5.9-41.2 8 4.9-7.7 9.8-15.6 14.4-23.7 4.6-8 8.9-16.1 13-24.1zM421.2 430c-9.3-9.6-18.6-20.3-27.8-32 9 .4 18.2.7 27.5.7 9.4 0 18.7-.2 27.8-.7-9 11.7-18.3 22.4-27.5 32zm-74.4-58.9c-14.2-2.1-27.9-4.7-41-7.9 3.7-12.9 8.3-26.2 13.5-39.5 4.1 8 8.4 16 13.1 24 4.7 8 9.5 15.8 14.4 23.4zM420.7 163c9.3 9.6 18.6 20.3 27.8 32-9-.4-18.2-.7-27.5-.7-9.4 0-18.7.2-27.8.7 9-11.7 18.3-22.4 27.5-32zm-74 58.9c-4.9 7.7-9.8 15.6-14.4 23.7-4.6 8-8.9 16-13 24-5.4-13.4-10-26.8-13.8-39.8 13.1-3.1 26.9-5.8 41.2-7.9zm-90.5 125.2c-35.4-15.1-58.3-34.9-58.3-50.6 0-15.7 22.9-35.6 58.3-50.6 8.6-3.7 18-7 27.7-10.1 5.7 19.6 13.2 40 22.5 60.9-9.2 20.8-16.6 41.1-22.2 60.6-9.9-3.1-19.3-6.5-28-10.2zM310 490c-13.6-7.8-19.5-37.5-14.9-75.7 1.1-9.4 2.9-19.3 5.1-29.4 19.6 4.8 41 8.5 63.5 10.9 13.5 18.5 27.5 35.3 41.6 50-32.6 30.3-63.2 46.9-84 46.9-4.5-.1-8.3-1-11.3-2.7zm237.2-76.2c4.7 38.2-1.1 67.9-14.6 75.8-3 1.8-6.9 2.6-11.5 2.6-20.7 0-51.4-16.5-84-46.6 14-14.7 28-31.4 41.3-49.9 22.6-2.4 44-6.1 63.6-11 2.3 10.1 4.1 19.8 5.2 29.1zm38.5-66.7c-8.6 3.7-18 7-27.7 10.1-5.7-19.6-13.2-40-22.5-60.9 9.2-20.8 16.6-41.1 22.2-60.6 9.9 3.1 19.3 6.5 28.1 10.2 35.4 15.1 58.3 34.9 58.3 50.6-.1 15.7-23 35.6-58.4 50.6zM320.8 78.4z"/> 4 | <circle cx="420.9" cy="296.5" r="45.7"/> 5 | <path d="M520.5 78.1z"/> 6 | </g> 7 | </svg> 8 | -------------------------------------------------------------------------------- /src/pages/Error.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Hero from "../components/Hero"; 3 | import Banner from "../components/Banner"; 4 | import { Link } from "react-router-dom"; 5 | const Error = () => { 6 | return ( 7 | <Hero> 8 | <Banner title="404" subtitle="page not found"> 9 | <Link to="/" className="btn-primary"> 10 | return home 11 | </Link> 12 | </Banner> 13 | </Hero> 14 | ); 15 | }; 16 | 17 | export default Error; 18 | -------------------------------------------------------------------------------- /src/pages/Home.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import { Link } from "react-router-dom"; 3 | import Hero from "../components/Hero"; 4 | import Banner from "../components/Banner"; 5 | import Services from "../components/Services"; 6 | import FeaturedRooms from "../components/FeaturedRooms"; 7 | const home = () => { 8 | return ( 9 | <> 10 | <Hero> 11 | <Banner 12 | title="luxurious rooms" 13 | subtitle="deluxe rooms starting at $299" 14 | > 15 | <Link to="/rooms" className="btn-primary"> 16 | our rooms 17 | </Link> 18 | </Banner> 19 | </Hero> 20 | <Services /> 21 | <FeaturedRooms /> 22 | </> 23 | ); 24 | }; 25 | 26 | export default home; 27 | -------------------------------------------------------------------------------- /src/pages/Rooms.js: -------------------------------------------------------------------------------- 1 | import React from "react"; 2 | import Hero from "../components/Hero"; 3 | import Banner from "../components/Banner"; 4 | import { Link } from "react-router-dom"; 5 | import RoomsContainer from "../components/RoomsContainer"; 6 | const Rooms = () => { 7 | return ( 8 | <> 9 | <Hero hero="roomsHero"> 10 | <Banner title="our rooms"> 11 | <Link to="/" className="btn-primary"> 12 | return home 13 | </Link> 14 | </Banner> 15 | </Hero> 16 | <RoomsContainer /> 17 | </> 18 | ); 19 | }; 20 | 21 | export default Rooms; 22 | -------------------------------------------------------------------------------- /src/pages/SingleRoom.js: -------------------------------------------------------------------------------- 1 | import React, { Component } from "react"; 2 | import defaultBcg from "../images/room-1.jpeg"; 3 | import Hero from "../components/Hero"; 4 | import Banner from "../components/Banner"; 5 | import { Link } from "react-router-dom"; 6 | import { RoomContext } from "../context"; 7 | 8 | import StyledHero from "../components/StyledHero"; 9 | export default class SingleRoom extends Component { 10 | constructor(props) { 11 | super(props); 12 | console.log(this.props); 13 | this.state = { 14 | slug: this.props.match.params.slug, 15 | defaultBcg: defaultBcg 16 | }; 17 | } 18 | static contextType = RoomContext; 19 | 20 | // componentDidMount() { 21 | // console.log(this.props); 22 | // } 23 | render() { 24 | const { getRoom } = this.context; 25 | const room = getRoom(this.state.slug); 26 | 27 | if (!room) { 28 | return ( 29 | <div className="error"> 30 | <h3> no such room could be found...</h3> 31 | <Link to="/rooms" className="btn-primary"> 32 | back to rooms 33 | </Link> 34 | </div> 35 | ); 36 | } 37 | const { 38 | name, 39 | description, 40 | capacity, 41 | size, 42 | price, 43 | extras, 44 | breakfast, 45 | pets, 46 | images 47 | } = room; 48 | const [main, ...defaultImages] = images; 49 | console.log(defaultImages); 50 | 51 | return ( 52 | <> 53 | <StyledHero img={images[0] || this.state.defaultBcg}> 54 | <Banner title={`${name} room`}> 55 | <Link to="/rooms" className="btn-primary"> 56 | back to rooms 57 | </Link> 58 | </Banner> 59 | </StyledHero> 60 | <section className="single-room"> 61 | <div className="single-room-images"> 62 | {defaultImages.map((item, index) => ( 63 | <img key={index} src={item} alt={name} /> 64 | ))} 65 | </div> 66 | <div className="single-room-info"> 67 | <article className="desc"> 68 | <h3>details</h3> 69 | <p>{description}</p> 70 | </article> 71 | <article className="info"> 72 | <h3>info</h3> 73 | <h6>price : ${price}</h6> 74 | <h6>size : {size} SQFT</h6> 75 | <h6> 76 | max capacity : 77 | {capacity > 1 ? `${capacity} people` : `${capacity} person`} 78 | </h6> 79 | <h6>{pets ? "pets allowed" : "no pets allowed"}</h6> 80 | <h6>{breakfast && "free breakfast included"}</h6> 81 | </article> 82 | </div> 83 | </section> 84 | <section className="room-extras"> 85 | <h6>extras </h6> 86 | <ul className="extras"> 87 | {extras.map((item, index) => ( 88 | <li key={index}>- {item}</li> 89 | ))} 90 | </ul> 91 | </section> 92 | </> 93 | ); 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/serviceWorker.js: -------------------------------------------------------------------------------- 1 | // This optional code is used to register a service worker. 2 | // register() is not called by default. 3 | 4 | // This lets the app load faster on subsequent visits in production, and gives 5 | // it offline capabilities. However, it also means that developers (and users) 6 | // will only see deployed updates on subsequent visits to a page, after all the 7 | // existing tabs open on the page have been closed, since previously cached 8 | // resources are updated in the background. 9 | 10 | // To learn more about the benefits of this model and instructions on how to 11 | // opt-in, read https://bit.ly/CRA-PWA 12 | 13 | const isLocalhost = Boolean( 14 | window.location.hostname === 'localhost' || 15 | // [::1] is the IPv6 localhost address. 16 | window.location.hostname === '[::1]' || 17 | // 127.0.0.1/8 is considered localhost for IPv4. 18 | window.location.hostname.match( 19 | /^127(?:\.(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)){3}$/ 20 | ) 21 | ); 22 | 23 | export function register(config) { 24 | if (process.env.NODE_ENV === 'production' && 'serviceWorker' in navigator) { 25 | // The URL constructor is available in all browsers that support SW. 26 | const publicUrl = new URL(process.env.PUBLIC_URL, window.location.href); 27 | if (publicUrl.origin !== window.location.origin) { 28 | // Our service worker won't work if PUBLIC_URL is on a different origin 29 | // from what our page is served on. This might happen if a CDN is used to 30 | // serve assets; see https://github.com/facebook/create-react-app/issues/2374 31 | return; 32 | } 33 | 34 | window.addEventListener('load', () => { 35 | const swUrl = `${process.env.PUBLIC_URL}/service-worker.js`; 36 | 37 | if (isLocalhost) { 38 | // This is running on localhost. Let's check if a service worker still exists or not. 39 | checkValidServiceWorker(swUrl, config); 40 | 41 | // Add some additional logging to localhost, pointing developers to the 42 | // service worker/PWA documentation. 43 | navigator.serviceWorker.ready.then(() => { 44 | console.log( 45 | 'This web app is being served cache-first by a service ' + 46 | 'worker. To learn more, visit https://bit.ly/CRA-PWA' 47 | ); 48 | }); 49 | } else { 50 | // Is not localhost. Just register service worker 51 | registerValidSW(swUrl, config); 52 | } 53 | }); 54 | } 55 | } 56 | 57 | function registerValidSW(swUrl, config) { 58 | navigator.serviceWorker 59 | .register(swUrl) 60 | .then(registration => { 61 | registration.onupdatefound = () => { 62 | const installingWorker = registration.installing; 63 | if (installingWorker == null) { 64 | return; 65 | } 66 | installingWorker.onstatechange = () => { 67 | if (installingWorker.state === 'installed') { 68 | if (navigator.serviceWorker.controller) { 69 | // At this point, the updated precached content has been fetched, 70 | // but the previous service worker will still serve the older 71 | // content until all client tabs are closed. 72 | console.log( 73 | 'New content is available and will be used when all ' + 74 | 'tabs for this page are closed. See https://bit.ly/CRA-PWA.' 75 | ); 76 | 77 | // Execute callback 78 | if (config && config.onUpdate) { 79 | config.onUpdate(registration); 80 | } 81 | } else { 82 | // At this point, everything has been precached. 83 | // It's the perfect time to display a 84 | // "Content is cached for offline use." message. 85 | console.log('Content is cached for offline use.'); 86 | 87 | // Execute callback 88 | if (config && config.onSuccess) { 89 | config.onSuccess(registration); 90 | } 91 | } 92 | } 93 | }; 94 | }; 95 | }) 96 | .catch(error => { 97 | console.error('Error during service worker registration:', error); 98 | }); 99 | } 100 | 101 | function checkValidServiceWorker(swUrl, config) { 102 | // Check if the service worker can be found. If it can't reload the page. 103 | fetch(swUrl) 104 | .then(response => { 105 | // Ensure service worker exists, and that we really are getting a JS file. 106 | const contentType = response.headers.get('content-type'); 107 | if ( 108 | response.status === 404 || 109 | (contentType != null && contentType.indexOf('javascript') === -1) 110 | ) { 111 | // No service worker found. Probably a different app. Reload the page. 112 | navigator.serviceWorker.ready.then(registration => { 113 | registration.unregister().then(() => { 114 | window.location.reload(); 115 | }); 116 | }); 117 | } else { 118 | // Service worker found. Proceed as normal. 119 | registerValidSW(swUrl, config); 120 | } 121 | }) 122 | .catch(() => { 123 | console.log( 124 | 'No internet connection found. App is running in offline mode.' 125 | ); 126 | }); 127 | } 128 | 129 | export function unregister() { 130 | if ('serviceWorker' in navigator) { 131 | navigator.serviceWorker.ready.then(registration => { 132 | registration.unregister(); 133 | }); 134 | } 135 | } 136 | --------------------------------------------------------------------------------