diff --git a/README.md b/README.md index 5711ee7f8..8da2c4afe 100644 --- a/README.md +++ b/README.md @@ -127,7 +127,7 @@ A hotel booking application in React. Homework for the [CodeYourFuture React mod #### 15. Highlight booking row when clicked **Instructions:** Within the `` component or its child components, add an `onClick` handler to each row in the table (hint: on the `` element). When clicked, the row is "selected" and highlighted with a different colour. When clicked again, the row is unselected and the coloured highlighting is removed. - +. **Hint:** Use a new state variable for each row to record if the row is selected or not, and use this value to set a class to the `className` prop of the row. **Test:** Verify that each row of your table can be highlighted (on and off) independently when being clicked. @@ -178,7 +178,7 @@ A hotel booking application in React. Homework for the [CodeYourFuture React mod #### 21. Display a customer profile - step 2 -**Instructions:** When a "Show profile" button is clicked in the table, fetch the corresponding customer profile from `https://cyf-react.glitch.me/customers/` in the `` component. A customer profile should show the customer ID, their email, if they are VIP and their phone number in a list. +**Instructions:** When a "Show profile" button is clicked in the table, fetch the corresponding customer profile from ` in the ```https://cyf-react.glitch.me/customers/ component. A customer profile should show the customer ID, their email, if they are VIP and their phone number in a list. **Hint:** You need to use `useEffect` and the correct dependency array. You'll need to fetch customers data from the API every time a "Show profile" button is clicked and render it accordingly. diff --git a/package-lock.json b/package-lock.json index 8197e6f22..94217b93a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -8,6 +8,7 @@ "name": "react-hotel", "version": "0.1.0", "dependencies": { + "moment": "^2.29.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1" @@ -11681,6 +11682,14 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/moment": { + "version": "2.29.4", + "resolved": "https://registry.npmjs.org/moment/-/moment-2.29.4.tgz", + "integrity": "sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w==", + "engines": { + "node": "*" + } + }, "node_modules/mri": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/mri/-/mri-1.2.0.tgz", diff --git a/package.json b/package.json index e3e1562a7..f81c28013 100644 --- a/package.json +++ b/package.json @@ -3,6 +3,7 @@ "version": "0.1.0", "private": true, "dependencies": { + "moment": "^2.29.4", "react": "^18.2.0", "react-dom": "^18.2.0", "react-scripts": "^5.0.1" diff --git a/src/App.css b/src/App.css index 05fe2d52e..2ca0c5c08 100644 --- a/src/App.css +++ b/src/App.css @@ -1,40 +1,19 @@ -.App { - text-align: left; -} - -.App-logo { - animation: App-logo-spin infinite 20s linear; - height: 80px; +body { + background-color: aliceblue; } - .App-header { - background-color: #222; - height: 50px; - padding: 20px; + background-color: black; + height: 60px; color: white; text-align: left; font-family: Arial, Helvetica, sans-serif; - font-size: 1em; - font-weight: bold; + margin-bottom: 2rem; } .App-title { - font-size: 1.5em; + font-size: 10px; } -.App-content { - padding-top: 20px; - font-size: large; -} - -@keyframes App-logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} .search { padding: 5px 0 20px 0; } @@ -53,5 +32,112 @@ tr { } .card { + display: flex; + flex-direction: row; + row-gap: 50px; + justify-content: space-around; + background-color: aliceblue; +} + +.logo { + height: 70px; + width: 100px; +} + +header { + display: flex; + flex-direction: row; +} + +h2 { + font-weight: bold; + font-size: 30px; + text-align: flex-start; + color: plum; +} +.card-img-top { + height: 25rem; + width: 28rem; +} + +.card-title { + color: white; + background-color: black; + font-size: 1rem; +} + +.card-text { + display: flex; + flex-direction: column; + justify-content: space-between; + font-size: 1rem; + background-color: beige; +} +.card-body { width: 18rem; } + +.btn-primary { + background-color: brown; +} + +.welcome { + display: flex; + flex-direction: column; + font-size: 2rem; +} + +.welcome-img { + display: flex; + max-width: auto; + height: 25rem; + border: 2px solid brown; + margin: 0rem 4rem 1rem 4rem; +} + +.order { + display: flex; + flex-direction: row; + gap: 5rem; +} + +.welcome-text { + text-align: center; + color: blueviolet; +} + +.table { + background-color: black; +} + +th { + color: brown; +} + +td { + color: beige; +} +.selected { + background-color: green; +} + +.profile { + background-color: burlywood; +} + +.customer { + border: 1px solid gray; + margin-bottom: 30px; +} +@media only screen and (max-width: 600px) { + .card { + flex-direction: column; + } + .welcome-img { + margin: 0; + } + .table { + width: 40%; + font-size: 10px; + } +} diff --git a/src/App.js b/src/App.js index 953c98560..a84c7411e 100644 --- a/src/App.js +++ b/src/App.js @@ -1,13 +1,24 @@ import React from "react"; - +import Heading from "./Heading"; +import TouristInfoCard from "./TouristInfoCards"; import Bookings from "./Bookings"; +import Restaurant from "./Restaurant"; import "./App.css"; +import Footer from "./Footer"; const App = () => { + const address = [ + "123 Fake Street, London, E1 4UD", + "hello@fakehotel.com", + "0123 456789", + ]; return (
-
CYF Hotel
+ + + +
); }; diff --git a/src/Bookings.js b/src/Bookings.js index e0d911b13..7d271e6c7 100644 --- a/src/Bookings.js +++ b/src/Bookings.js @@ -1,18 +1,41 @@ -import React from "react"; +import React, { useState, useEffect } from "react"; import Search from "./Search.js"; -// import SearchResults from "./SearchResults.js"; -// import FakeBookings from "./data/fakeBookings.json"; +import SearchResults from "./SearchResults.js"; const Bookings = () => { - const search = searchVal => { - console.info("TO DO!", searchVal); + const [bookings, setBookings] = useState([]); + const [loading, setLoading] = useState(true); + + useEffect(() => { + fetch(`https://cyf-react.glitch.me/`) + .then((response) => { + if (!response.ok) { + throw Error("Something went wrong"); + } + return response.json(); + }) + .then((data) => { + console.log(data); + setBookings(data); + setLoading(false); + }); + }, []); + + const search = (searchVal) => { + const filteredBookings = bookings.filter( + (booking) => + booking.firstName.toLowerCase().includes(searchVal.toLowerCase()) || + booking.surname.toLowerCase().includes(searchVal.toLowerCase()) + ); + setBookings(filteredBookings); }; return (
- {/* */} +
{loading ?

Loading...

: null}
+
); diff --git a/src/CustomerProfile.js b/src/CustomerProfile.js new file mode 100644 index 000000000..acdb5b953 --- /dev/null +++ b/src/CustomerProfile.js @@ -0,0 +1,17 @@ +import React from "react"; + +const CustomerProfile = (props) => { + return ( +
+
    + {props.id &&
  • Customer_ID : {props.id}
  • } + {props.firstName &&
  • Name : {props.firstName}
  • } + {props.surname &&
  • surname : {props.surname}
  • } + {props.vip &&
  • email : {props.email}
  • } + {props.phoneNumber &&
  • phone number : {props.phoneNumber}
  • } +
+
+ ); +}; + +export default CustomerProfile; diff --git a/src/Footer.js b/src/Footer.js new file mode 100644 index 000000000..01985bd00 --- /dev/null +++ b/src/Footer.js @@ -0,0 +1,17 @@ +import react from "react"; + +const Footer = ({ props }) => { + return ( +
+
+
    + {props.map((prop) => { + return
  • {prop}
  • ; + })} +
+
+
+ ); +}; + +export default Footer; \ No newline at end of file diff --git a/src/Heading.js b/src/Heading.js new file mode 100644 index 000000000..4a10b289d --- /dev/null +++ b/src/Heading.js @@ -0,0 +1,10 @@ +import React from "react"; +const Heading = () => { + const name = "CYF HOTEL" + return
+
hotel-logo
+

{name}

+
+} + +export default Heading; \ No newline at end of file diff --git a/src/Order.js b/src/Order.js new file mode 100644 index 000000000..6ff13a6df --- /dev/null +++ b/src/Order.js @@ -0,0 +1,17 @@ +import React, { useState } from "react"; +import Button from "./RestaurentButton"; + +const Order = ({ orderType }) => { + const [order, setOrder] = useState(0); + function orderOne() { + setOrder(order + 1); + } + return ( +
  • + {orderType}: {order} +
  • + ); +}; + +export default Order; diff --git a/src/Restaurant.js b/src/Restaurant.js index ecb2b43a2..1a9baae2b 100644 --- a/src/Restaurant.js +++ b/src/Restaurant.js @@ -1,14 +1,14 @@ import React from "react"; +import Order from "./Order"; const Restaurant = () => { - const pizzas = 0; return (

    Restaurant Orders

    -
      -
    • - Pizzas: {pizzas} -
    • +
        + + +
    ); diff --git a/src/RestaurentButton.js b/src/RestaurentButton.js new file mode 100644 index 000000000..75090accf --- /dev/null +++ b/src/RestaurentButton.js @@ -0,0 +1,11 @@ +import React from "react"; + +const Button = ({ orderOne }) => { + return ( + + ); +}; + +export default Button; diff --git a/src/Search.js b/src/Search.js index 7bd5871c0..4691377fa 100644 --- a/src/Search.js +++ b/src/Search.js @@ -1,6 +1,16 @@ -import React from "react"; +import React, { useState } from "react"; +import SearchButton from "./SearchButton"; -const Search = () => { +const Search = (props) => { + const [searchInput, setSearchInput] = useState(""); + + function handleSearchInput(e) { + setSearchInput(e.target.value); + } + function handleSubmit(e) { + e.preventDefault(); + props.search(searchInput); + } return (
    @@ -8,16 +18,18 @@ const Search = () => {
    -
    +
    - +
    diff --git a/src/SearchButton.js b/src/SearchButton.js new file mode 100644 index 000000000..8c6e70950 --- /dev/null +++ b/src/SearchButton.js @@ -0,0 +1,7 @@ +import React from "react"; + + const SearchButton = () => { + return + } + + export default SearchButton; \ No newline at end of file diff --git a/src/SearchResults.js b/src/SearchResults.js new file mode 100644 index 000000000..2e4875bc8 --- /dev/null +++ b/src/SearchResults.js @@ -0,0 +1,91 @@ +import React, { useState, useEffect } from "react"; +import moment from "moment"; +import CustomerProfile from "./CustomerProfile"; + +function SearchResults(props) { + const [rowSelected, rowSetSelected] = useState([]); + const [selectProfile, setSelectProfile] = useState(""); + const [profile, setProfile] = useState([]); + + useEffect(() => { + fetch(`https://cyf-react.glitch.me/customers/${selectProfile}`) + .then((res) => res.json()) + .then((data) => setProfile(data)); + }, [selectProfile]); + + function showProfile(selectProfile) { + setSelectProfile(selectProfile); + } + + function handleClick(index) { + if (rowSelected.includes(index)) { + rowSetSelected((selected) => { + return selected.filter((item) => item !== index); + }); + } else { + rowSetSelected([...rowSelected, index]); + } + } + return ( +
    + + + + + + + + + + + + + + + + + {props.results.map((result, index) => { + return ( + handleClick(index)} + > + + + + + + + + + + + + + ); + })} + +
    IdTitleFirst NameSurnameEmailRoom idCheck in dateCheck out dateStay nightsCustomer's Profile
    {result.id}{result.title}{result.firstName}{result.surname}{result.email}{result.roomId}{result.checkInDate}{result.checkOutDate} + {moment(result.checkOutDate).diff( + moment(result.checkInDate), + "days" + )} + + +
    + +
    + ); +} + +export default SearchResults; diff --git a/src/TouristInfoCards.js b/src/TouristInfoCards.js new file mode 100644 index 000000000..989fd11f7 --- /dev/null +++ b/src/TouristInfoCards.js @@ -0,0 +1,87 @@ +import React from "react"; + +const TouristInfoCard = () => { + return ( +
    + hotel-img +

    Explore The World With Us!!!

    +
    +
    + glasgow-pic +
    +
    Glasgow
    +

    + You’re guaranteed to find accommodation in Glasgow which suits + your taste and budget. Whether you’re looking for something + uniquely Glaswegian, a trendy hotel, a vibrant hostel or a + comfortable city centre apartment, you can be assured of a warm + welcome. +

    + + MORE INFO + +
    +
    +
    + glasgow-pic +
    +
    Manchester
    +

    + Manchester city centre is jam-packed with unique and eclectic + restaurants, bars, shops, museums, galleries, hotels and places to + stay whilst the surrounding Greater Manchester boroughs offer a + patch-work of visitor experiences including quaint market towns, + traditional pubs and beautiful green spaces and waterways to be + explored on foot or bike. The city region is easily navigated, + with great transport links both in and around Greater Manchester.. +

    + + MORE INFO + +
    +
    +
    + glasgow-pic +
    +
    London
    +

    + Welcome to London! Discover the best of London with Visit London, + the official guide to England’s exciting capital. Find things to + do in London, from iconic sightseeing spots and fun-filled days + out to top restaurants, theatre and unmissable London events. If + you’re not able to visit just yet, plan ahead to make the most of + your next visit. +

    + + MORE INFO + +
    +
    +
    +
    + ); +}; + +export default TouristInfoCard;