Skip to content
This repository has been archived by the owner on Feb 9, 2024. It is now read-only.

BAKI TUNCER/LONDON-10/CFY HOTEL #618

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
22,469 changes: 8,803 additions & 13,666 deletions package-lock.json

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
18 changes: 15 additions & 3 deletions src/App.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,27 @@
import React from "react";

import Bookings from "./Bookings";
import "./App.css";
import Heading from "./components/Heading";
import TouristInfoCards from "./components/TouristInfoCards";
import Footer from "./components/Footer";
import Restaurant from "./components/Restaurant";

const App = () => {
const addresses = [
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This variable makes it seem like the list contains only addresses. However it has an email and a phone number. It might be better to rename it to something like details.

"123 Fake Street, London, E1 4UD",
"[email protected]",
"0123 456789",
];

return (
<div className="App">
<header className="App-header">CYF Hotel</header>
<Heading />
<TouristInfoCards />
<Bookings />
<Restaurant />
<Footer addresses={addresses} />
</div>
);
};

export default App;
export default App;
55 changes: 44 additions & 11 deletions src/Bookings.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,52 @@
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 "./components/SearchResults.js";
import fakeBookings from "./data/fakeBookings.json";
const Bookings = () => {
const search = searchVal => {
console.info("TO DO!", searchVal);
const [bookings, setBookings] = useState([]);
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState(null);

useEffect(() => {
console.log("Page loaded");
setIsLoading(true);
setError(null);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Really nice use of error handling here on page load. However, you shouldnt need this since you already have the default set to null.

fetch("https://cyf-react.glitch.me")
.then((response) => {
if (!response.ok) {
throw new Error("Failed to fetch data");
}
return response.json();
})
.then((data) => {
setBookings(data);
setIsLoading(false);
})
.catch((error) => {
setError(error.message);
setIsLoading(false);
});
}, []);

const search = (searchVal) => {
const filteredBookings = bookings.filter(
(booking) =>
booking.firstName.toLowerCase().includes(searchVal.toLowerCase()) ||
booking.surname.toLowerCase().includes(searchVal.toLowerCase())
);
setBookings(filteredBookings);
};

return (
<div className="App-content">
<div className="container">
<Search search={search} />
{/* <SearchResults results={FakeBookings} /> */}
</div>
<div className="container">
<Search search={search} />
{isLoading ? (
<p>Loading...</p>
) : error ? (
<p>Error: {error}</p>
) : (
<SearchResults bookings={bookings} />
)}
</div>
);
};
Expand Down
17 changes: 0 additions & 17 deletions src/Restaurant.js

This file was deleted.

25 changes: 21 additions & 4 deletions src/Search.js
Original file line number Diff line number Diff line change
@@ -1,23 +1,40 @@
import React from "react";
import React, { useState } from "react";

const Search = ({ search }) => {
const [searchInput, setSearchInput] = useState("");

const handleSearchInput = (event) => {
setSearchInput(event.target.value);
console.log(event.target.value);
};

const handleFormSubmit = (event) => {
event.preventDefault();
search(searchInput);
console.log(searchInput);
};

const Search = () => {
return (
<div className="search">
<div className="page-header">
<h4 className="text-left">Search Bookings</h4>
</div>
<div className="row search-wrapper">
<div className="col">
<form className="form-group search-box">
<form className="form-group search-box" onSubmit={handleFormSubmit}>
<label htmlFor="customerName">Customer name</label>
<div className="search-row">
<input
type="text"
id="customerName"
className="form-control"
placeholder="Customer name"
value={searchInput}
onChange={handleSearchInput}
/>
<button className="btn btn-primary">Search</button>
<button type="submit" className="btn btn-primary">
Search
</button>
</div>
</form>
</div>
Expand Down
33 changes: 33 additions & 0 deletions src/components/CustomerProfile.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import React, { useEffect, useState } from "react";

const CustomerProfile = ({ customerId }) => {
const [customerProfile, setCustomerProfile] = useState(null);

useEffect(() => {
if (customerId) {
fetch(`https://cyf-react.glitch.me/customers/${customerId}`)
.then((response) => response.json())
.then((data) => {
setCustomerProfile(data);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Would be good to add a catch here aswell and some error handling similar to the other api requests. Then you can use that to display an error message.

});
}
}, [customerId]);

if (!customerProfile) {
return null;
}

return (
<div className="customer-profile">
<h2>Customer Profile</h2>
<ul>
<li>ID: {customerProfile.id}</li>
<li>Email: {customerProfile.email}</li>
<li>VIP: {customerProfile.vip ? "Yes" : "No"}</li>
<li>Phone: {customerProfile.phoneNumber}</li>
</ul>
</div>
);
};

export default CustomerProfile;
15 changes: 15 additions & 0 deletions src/components/Footer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import React from "react";

const Footer = ({addresses})=> {
return (
<footer>
<ul>
{addresses.map((address, index) => (
<li key={index}>{address}</li>
))}
</ul>
</footer>
);
}

export default Footer;
14 changes: 14 additions & 0 deletions src/components/Heading.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import React from "react";

const Heading = () => {
return (
<header className="App-header">
<img
src="https://image.flaticon.com/icons/svg/139/139899.svg"
alt="Hotel Logo"
/>
</header>
);
}

export default Heading;
38 changes: 38 additions & 0 deletions src/components/Restaurant.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
import React, { useState } from "react";

const Order = ({ orderType }) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is great separation making them all into smaller components however it still might be best to split them into separate files to keep them smaller and easier to read.

const [orders, setOrders] = useState(0);

const orderOne = () => {
setOrders(orders + 1);
};

return (
<li>
{orderType}: {orders} <RestaurantButton onClick={orderOne} />
</li>
);
};

const RestaurantButton = ({ onClick }) => {
return (
<button className="btn btn-primary" onClick={onClick}>
Add
</button>
);
};

const Restaurant = () => {
return (
<div>
<h3>Restaurant Orders</h3>
<ul>
<Order orderType="Pizzas" />
<Order orderType="Salads" />
<Order orderType="Chocolate cake" />
</ul>
</div>
);
};

export default Restaurant;
7 changes: 7 additions & 0 deletions src/components/SearchButton.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
import React from "react";

const SearchButton = () => {
return <button className="btn btn-primary">Search</button>;
}

export default SearchButton;
100 changes: 100 additions & 0 deletions src/components/SearchResults.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import React, { useEffect, useState } from "react";

const SearchResults = ({ bookings }) => {
const [selectedCustomerId, setSelectedCustomerId] = useState(null);

const handleShowProfile = (customerId) => {
setSelectedCustomerId(customerId);
};

return (
<div>
<table className="table table-striped">
<thead>
<tr>
<th>Title</th>
<th>First Name</th>
<th>Surname</th>
<th>Email</th>
<th>Room ID</th>
<th>Check-in Date</th>
<th>Check-out Date</th>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For task 6 to add the difference of nights You installed moment library which is great. The docs provide some guidance on calculating the difference here https://momentjs.com/docs/#/displaying/difference/

<th>Actions</th>
</tr>
</thead>
<tbody>
{bookings.map((booking) => (
<tr key={booking.id}>
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The map works perfectly here and it seems to function as expected. It just seems to be missing lesson 2 task 15. You can achieve it by adding a onClick here and a function to change its class

<td>{booking.title}</td>
<td>{booking.firstName}</td>
<td>{booking.surname}</td>
<td>{booking.email}</td>
<td>{booking.roomId}</td>
<td>{booking.checkInDate}</td>
<td>{booking.checkOutDate}</td>
<td>
<button
className="btn btn-primary"
onClick={() => handleShowProfile(booking.id)}
>
Show profile
</button>
</td>
</tr>
))}
</tbody>
</table>
{selectedCustomerId && (
<CustomerProfile customerId={selectedCustomerId} />
)}
</div>
);
};

const CustomerProfile = ({ customerId }) => {
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

You used the Id really well here to allow for searching. Only suggestion would be to move this into a separate file so it becomes its own reusable component if it needs to be used elsewhere. Similar to the other components in the app.

const [customerData, setCustomerData] = useState(null);
const [isLoading, setIsLoading] = useState(true);

useEffect(() => {
setIsLoading(true);
fetch(`https://cyf-react.glitch.me/customers/${customerId}`)
.then((response) => {
if (!response.ok) {
throw new Error("Failed to fetch customer data");
}
return response.json();
})
.then((data) => {
setCustomerData(data);
setIsLoading(false);
})
.catch((error) => {
console.error(error);
setIsLoading(false);
});
}, [customerId]);

return (
<div>
{isLoading ? (
<p>Loading customer profile...</p>
) : (
<div>
<h2>Customer Profile</h2>
{customerData ? (
<ul>
<li>ID: {customerData.id}</li>
<li>Email: {customerData.email}</li>
<li>VIP: {customerData.vip ? "Yes" : "No"}</li>
<li>Phone: {customerData.phoneNumber}</li>
</ul>
) : (
<p>Unable to fetch customer profile.</p>
)}
</div>
)}
</div>
);
};

export default SearchResults;
Loading