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..53074210e 100644 --- a/src/App.css +++ b/src/App.css @@ -4,13 +4,14 @@ .App-logo { animation: App-logo-spin infinite 20s linear; - height: 80px; + height: 40px; } .App-header { background-color: #222; height: 50px; padding: 20px; + margin-bottom: 15px; color: white; text-align: left; font-family: Arial, Helvetica, sans-serif; @@ -52,6 +53,14 @@ tr { text-align: center; } +.un-list { + list-style-type: none; +} + .card { width: 18rem; } + +.highlighted { +background-color: gray; +} diff --git a/src/App.js b/src/App.js index 953c98560..10e0f2b6f 100644 --- a/src/App.js +++ b/src/App.js @@ -2,12 +2,26 @@ import React from "react"; import Bookings from "./Bookings"; import "./App.css"; +import Heading from "./Heading"; +import TouristInfoCard from "./TouristInfoCards"; +import Footer from "./Footer"; +import Restaurant from "./Restaurant"; + const App = () => { return (
-
CYF Hotel
+ + + +
); }; diff --git a/src/Bookings.js b/src/Bookings.js index e0d911b13..88197bcd1 100644 --- a/src/Bookings.js +++ b/src/Bookings.js @@ -1,21 +1,70 @@ -import React from "react"; +import React, { useEffect, useState } 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 [allData, setAllData] = useState([]); + const [isLoading, setIsLoading] = useState(true); + const [errorMessage, setErrorMessage] = useState(null); + + const search = (searchVal) => { + if (searchVal !== "") { + searchVal = searchVal.toLowerCase(); + let filteredResults = bookings.filter((booking) => { + return ( + booking.firstName.toLowerCase().includes(searchVal) || + booking.surname.toLowerCase().includes(searchVal) + ); + }); + setBookings(filteredResults); + } else { + setBookings(allData); + } }; + function doingFetchForTable() { + fetch("https://cyf-react.glitch.me") + .then((response) => { + if (!response.ok) { + throw new Error("Error while data fetching("); + } + return response.json(); + }) + .then((data) => { + setAllData(data); + setBookings(data); + setIsLoading(false); + setErrorMessage(null); + }) + .catch((errorMessage) => { + setIsLoading(false); + setErrorMessage(errorMessage.message); + }); + } + + useEffect(() => { + doingFetchForTable(); + }, []); + + const SearchContainer = () => { + if (isLoading) { + return

Please wait while the information is loading...

; + } - return ( -
-
- - {/* */} + if (errorMessage) { + return

{errorMessage}

; + } + + return ( +
+
+ + +
-
- ); + ); + }; + return ; }; export default Bookings; diff --git a/src/CustomerProfile.js b/src/CustomerProfile.js new file mode 100644 index 000000000..d0fe572f4 --- /dev/null +++ b/src/CustomerProfile.js @@ -0,0 +1,61 @@ +import { useState, useEffect } from "react"; + +const CustomerProfile = ({ id }) => { + const [allCustomerData, setAllCustomerData] = useState(); + const [isLoading, setIsLoading] = useState(true); + const [errorMessage, setErrorMessage] = useState(null); + + function doFetchForCustomerProf() { + fetch(`https://cyf-react.glitch.me/customers/${id}`) + .then((response) => { + if (!response.ok) { + throw new Error("Failed to fetch customer data."); + } + return response.json(); + }) + .then((data) => { + setAllCustomerData(data); + setIsLoading(false); + setErrorMessage(null); + }) + .catch((error) => { + setIsLoading(false); + setErrorMessage(error.message); + }); + } + useEffect(() => { + if (id) { + doFetchForCustomerProf(); + } + }, [id]); + + if (isLoading) { + return

Please wait while the customer information is loading...

; + } + + if (errorMessage) { + return

{errorMessage}

; + } + + return ( +
+

Customer Profile Card

+

+ Customer ID: {allCustomerData.id} +

+

+ Customer Name: {allCustomerData.title}{" "} + {allCustomerData.firstName} {allCustomerData.surname} +

+

+ Customer email: {allCustomerData.email} +

+

+ Customer Phone Number: {allCustomerData.phoneNumber} +

+

{allCustomerData.vip ? "vip" : "not vip"}

+
+ ); +}; + +export default CustomerProfile; diff --git a/src/Footer.js b/src/Footer.js new file mode 100644 index 000000000..1c645b7a6 --- /dev/null +++ b/src/Footer.js @@ -0,0 +1,17 @@ +import React from "react"; + +const Footer = (props) => { + return ( +
+
    + {props.contacts.map((item) => ( +
  • + {item} +
  • + ))} +
+
+ ); +}; + +export default Footer; diff --git a/src/Glasgow.png b/src/Glasgow.png new file mode 100644 index 000000000..615c9af34 Binary files /dev/null and b/src/Glasgow.png differ diff --git a/src/Heading.js b/src/Heading.js new file mode 100644 index 000000000..95cb9b1b0 --- /dev/null +++ b/src/Heading.js @@ -0,0 +1,13 @@ +import React from "react"; +import logo from "./Logo.png"; + +const Heading = () => { + return ( +
+ Logo + CYF Hotel +
+ ); +}; + +export default Heading; diff --git a/src/Logo.png b/src/Logo.png new file mode 100644 index 000000000..639114080 Binary files /dev/null and b/src/Logo.png differ diff --git a/src/London.png b/src/London.png new file mode 100644 index 000000000..6a3be1707 Binary files /dev/null and b/src/London.png differ diff --git a/src/Manchester.png b/src/Manchester.png new file mode 100644 index 000000000..4d0a67c7c Binary files /dev/null and b/src/Manchester.png differ diff --git a/src/Order.js b/src/Order.js new file mode 100644 index 000000000..dfe720f7e --- /dev/null +++ b/src/Order.js @@ -0,0 +1,17 @@ +import { useState } from "react"; +import React from "react"; +import RestaurantButton from "./RestaurantButton"; + +const Order = (props) => { + const [orders, setOrders] = useState(0); + const orderOne = () => { + setOrders((orders) => orders + 1); + }; + return ( +
  • + {props.orderType}: {orders} +
  • + ); +}; + +export default Order; diff --git a/src/Restaurant.js b/src/Restaurant.js index ecb2b43a2..df599cbbb 100644 --- a/src/Restaurant.js +++ b/src/Restaurant.js @@ -1,14 +1,16 @@ +import { useState } from "react"; import React from "react"; +import RestaurantButton from "./RestaurantButton"; +import Order from "./Order"; const Restaurant = () => { - const pizzas = 0; return (

    Restaurant Orders

      -
    • - Pizzas: {pizzas} -
    • + + +
    ); diff --git a/src/RestaurantButton.js b/src/RestaurantButton.js new file mode 100644 index 000000000..9225c4e1d --- /dev/null +++ b/src/RestaurantButton.js @@ -0,0 +1,9 @@ +import React from "react"; +const RestaurantButton = (props) => { + return ( + + ); +}; +export default RestaurantButton; diff --git a/src/Search.js b/src/Search.js index 7bd5871c0..efb213e17 100644 --- a/src/Search.js +++ b/src/Search.js @@ -1,6 +1,18 @@ -import React from "react"; +import React, { useState } from "react"; +import SearchButton from "./SearchButton"; + +const Search = (props) => { + const [searchInput, setSearchInput] = useState(""); + + const handleSearchInput = (event) => { + setSearchInput(event.target.value); + }; + + const handleSubmit = (event) => { + event.preventDefault(); + props.search(searchInput); + }; -const Search = () => { return (
    @@ -8,7 +20,7 @@ const Search = () => {
    -
    +
    { id="customerName" className="form-control" placeholder="Customer name" + value={searchInput} + onChange={handleSearchInput} /> - +
    diff --git a/src/SearchButton.js b/src/SearchButton.js new file mode 100644 index 000000000..47b261ce7 --- /dev/null +++ b/src/SearchButton.js @@ -0,0 +1,6 @@ +import React from "react"; +const SearchButton = () => { + return ; +}; + +export default SearchButton; diff --git a/src/SearchResults.js b/src/SearchResults.js new file mode 100644 index 000000000..239204cc1 --- /dev/null +++ b/src/SearchResults.js @@ -0,0 +1,41 @@ +import React, { useState } from "react"; +import moment from "moment"; +import CustomerProfile from "./CustomerProfile"; +import SearchResultsRow from "./SearchResultsRow"; + +const SearchResults = (props) => { + const [selectedID, setSelectedId] = useState(null); + + function handleClickOnShowProfile(id) { + setSelectedId(id); + } + + return ( + <> + + + + + + + + + + + + + + + + {props.results.map((data) => { + return ; + })} + ; + +
    TitleFirst NameSurnameEmailRoom idCheck in DateCheck out dateNumber of nightsShow profile
    + + + ); +}; + +export default SearchResults; diff --git a/src/SearchResultsRow.js b/src/SearchResultsRow.js new file mode 100644 index 000000000..03e771a8d --- /dev/null +++ b/src/SearchResultsRow.js @@ -0,0 +1,52 @@ +import React, { useState } from "react"; +import moment from "moment"; + +const SearchResultsRow = (props) => { + const [isActive, setIsActive] = useState(false); + function highlightSelectedRow() { + if (isActive === true) { + setIsActive(false); + } else { + setIsActive(true); + } + } + const CountNumberOfNights = (checkInDate, checkOutDate) => { + let a = moment(checkInDate); + let b = moment(checkOutDate); + return b.diff(a, "days"); + }; + + return ( + highlightSelectedRow()} + className={isActive === true ? "highlighted" : ""} + > + {props.booking.title} + {props.booking.firstName} + {props.booking.surname} + {props.booking.email} + {props.booking.id} + {props.booking.checkInDate} + {props.booking.checkOutDate} + + {CountNumberOfNights( + props.booking.checkInDate, + props.booking.checkOutDate + )} + + + + + + ); +}; + +export default SearchResultsRow; diff --git a/src/TouristInfoCards.js b/src/TouristInfoCards.js new file mode 100644 index 000000000..79f49b760 --- /dev/null +++ b/src/TouristInfoCards.js @@ -0,0 +1,66 @@ +import React from "react"; +import theImage1 from "./Glasgow.png"; +import theImage2 from "./Manchester.png"; +import theImage3 from "./London.png"; +const TouristInfoCard = () => { + return ( +
    +
    + Glasgow +
    +
    Glasgow
    +

    + Glasgow is a port city on the River Clyde in Scotland's western + Lowlands. It's famed for its Victorian and art nouveau architecture, + a rich legacy of the city's 18th–20th-century prosperity due to + trade and shipbuilding. Today it's a national cultural hub, home to + institutions including the Scottish Opera, Scottish Ballet and + National Theatre of Scotland, as well as acclaimed museums and a + thriving music scene. +

    + + More information + +
    +
    +
    + Manchester +
    +
    Manchester
    +

    + Manchester is a major city in the northwest of England with a rich + industrial heritage. The Castlefield conservation area’s + 18th-century canal system recalls the city’s days as a textile + powerhouse, and visitors can trace this history at the interactive + Museum of Science & Industry. The revitalised Salford Quays + dockyards now house the Daniel Libeskind-designed Imperial War + Museum North and the Lowry cultural centre. +

    + + More information + +
    +
    +
    + London +
    +
    London
    +

    + London, the capital of England and the United Kingdom, is a + 21st-century city with history stretching back to Roman times. At + its centre stand the imposing Houses of Parliament, the iconic ‘Big + Ben’ clock tower and Westminster Abbey, site of British monarch + coronations. Across the Thames River, the London Eye observation + wheel provides panoramic views of the South Bank cultural complex, + and the entire city. +

    + + More information + +
    +
    +
    + ); +}; + +export default TouristInfoCard; diff --git a/src/index.css b/src/index.css index 4607bb217..4f6a3d1c5 100644 --- a/src/index.css +++ b/src/index.css @@ -24,3 +24,24 @@ body { .search-row input { margin-right: 10px; } + +.customer-card { + background-color: #f7f7f7; + border: 1px solid #ccc; + border-radius: 6px; + padding: 20px; + margin: 20px; + box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); + text-align: center; + justify-content: center; +} + +.customer-card h2 { + font-size: 20px; + margin-bottom: 8px; +} + +.customer-card p { + font-size: 16px; + margin: 6px 0; +}