├── public
├── img
│ ├── hotel1.jpeg
│ ├── hotel2.jpeg
│ ├── hotel3.jpeg
│ ├── hotel4.jpeg
│ └── hotel5.jpeg
└── vite.svg
├── vite.config.js
├── src
├── store.js
├── main.jsx
├── App.jsx
├── components
│ ├── BookingForm.jsx
│ ├── HotelDetails.jsx
│ └── HotelList.jsx
└── assets
│ └── react.svg
├── .gitignore
├── index.html
├── db.json
├── package.json
└── README.md
/public/img/hotel1.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlosazaustre/hotel-reservation-app/HEAD/public/img/hotel1.jpeg
--------------------------------------------------------------------------------
/public/img/hotel2.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlosazaustre/hotel-reservation-app/HEAD/public/img/hotel2.jpeg
--------------------------------------------------------------------------------
/public/img/hotel3.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlosazaustre/hotel-reservation-app/HEAD/public/img/hotel3.jpeg
--------------------------------------------------------------------------------
/public/img/hotel4.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlosazaustre/hotel-reservation-app/HEAD/public/img/hotel4.jpeg
--------------------------------------------------------------------------------
/public/img/hotel5.jpeg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/carlosazaustre/hotel-reservation-app/HEAD/public/img/hotel5.jpeg
--------------------------------------------------------------------------------
/vite.config.js:
--------------------------------------------------------------------------------
1 | import { defineConfig } from 'vite'
2 | import react from '@vitejs/plugin-react'
3 |
4 | // https://vitejs.dev/config/
5 | export default defineConfig({
6 | plugins: [react()],
7 | })
8 |
--------------------------------------------------------------------------------
/src/store.js:
--------------------------------------------------------------------------------
1 | import { create } from "zustand";
2 |
3 | const useStore = create((set) => ({
4 | reservations: [],
5 | addReservation: (hotel, dates) =>
6 | set((state) => ({
7 | reservations: [...state.reservations, { hotel, dates }],
8 | })),
9 | }));
10 |
11 | export default useStore;
12 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Logs
2 | logs
3 | *.log
4 | npm-debug.log*
5 | yarn-debug.log*
6 | yarn-error.log*
7 | pnpm-debug.log*
8 | lerna-debug.log*
9 |
10 | node_modules
11 | dist
12 | dist-ssr
13 | *.local
14 |
15 | # Editor directories and files
16 | .vscode/*
17 | !.vscode/extensions.json
18 | .idea
19 | .DS_Store
20 | *.suo
21 | *.ntvs*
22 | *.njsproj
23 | *.sln
24 | *.sw?
25 |
--------------------------------------------------------------------------------
/index.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 | Vite + React
8 |
9 |
10 |
11 |
12 |
13 |
14 |
--------------------------------------------------------------------------------
/src/main.jsx:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import ReactDOM from "react-dom/client";
3 | import App from "./App";
4 |
5 | import "@fontsource/roboto/300.css";
6 | import "@fontsource/roboto/400.css";
7 | import "@fontsource/roboto/500.css";
8 | import "@fontsource/roboto/700.css";
9 |
10 | ReactDOM.createRoot(document.getElementById("root")).render(
11 |
12 |
13 |
14 | );
15 |
--------------------------------------------------------------------------------
/src/App.jsx:
--------------------------------------------------------------------------------
1 | import { QueryClient, QueryClientProvider } from "@tanstack/react-query";
2 | import { Route, Switch } from "wouter";
3 | import { Toaster } from "react-hot-toast";
4 | import HotelList from "./components/HotelList";
5 | import HotelDetails from "./components/HotelDetails";
6 |
7 | function App() {
8 | return (
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 | );
19 | }
20 |
21 | export default App;
22 |
--------------------------------------------------------------------------------
/db.json:
--------------------------------------------------------------------------------
1 | {
2 | "hotels": [
3 | {
4 | "name": "Hotel Ritz",
5 | "description": "Un hotel de lujo ubicado en el corazón de la ciudad",
6 | "id": 1,
7 | "image": "/img/hotel1.jpeg"
8 | },
9 | {
10 | "name": "Hotel Plaza",
11 | "description": "Un hotel histórico ubicado en la Quinta Avenida",
12 | "id": 2,
13 | "image": "/img/hotel2.jpeg"
14 | },
15 | {
16 | "name": "Hotel Waldorf Astoria",
17 | "description": "Un hotel de cinco estrellas ubicado en Midtown Manhattan",
18 | "id": 3,
19 | "image": "/img/hotel3.jpeg"
20 | },
21 | {
22 | "name": "Hotel St. Regis",
23 | "description": "Un hotel de lujo ubicado en Central Park",
24 | "id": 4,
25 | "image": "/img/hotel4.jpeg"
26 | },
27 | {
28 | "name": "Hotel Peninsula",
29 | "description": "Un hotel de cinco estrellas ubicado en Midtown Manhattan",
30 | "id": 5,
31 | "image": "/img/hotel5.jpeg"
32 | }
33 | ]
34 | }
35 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hotel-reservation-app",
3 | "private": true,
4 | "version": "0.0.0",
5 | "type": "module",
6 | "scripts": {
7 | "dev": "vite",
8 | "build": "vite build",
9 | "preview": "vite preview",
10 | "server": "json-server --watch db.json --port 3001"
11 | },
12 | "dependencies": {
13 | "@emotion/react": "^11.11.1",
14 | "@emotion/styled": "^11.11.0",
15 | "@fontsource/roboto": "^5.0.5",
16 | "@mui/icons-material": "^5.14.0",
17 | "@mui/material": "^5.14.0",
18 | "@tanstack/react-query": "^4.29.19",
19 | "json-server": "^0.17.3",
20 | "react": "^18.2.0",
21 | "react-dom": "^18.2.0",
22 | "react-hook-form": "^7.45.1",
23 | "react-hot-toast": "^2.4.1",
24 | "wouter": "^2.11.0",
25 | "zustand": "^4.3.9"
26 | },
27 | "devDependencies": {
28 | "@types/react": "^18.0.27",
29 | "@types/react-dom": "^18.0.10",
30 | "@vitejs/plugin-react": "^3.1.0",
31 | "vite": "^4.1.0"
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # hotel-reservation-app
2 |
3 | Code from the ReactJS Video Tutorial on [YouTube](https://www.youtube.com/watch?v=KRrzBkxxMbc)
4 | [](https://www.youtube.com/watch?v=KRrzBkxxMbc)
5 |
6 | You will learn:
7 |
8 | - How to set up a React JS project from scratch.
9 | - Integration and use of **React/TanStack Query** to manage queries and mutations.
10 | - Global state with **Zustand**.
11 | - Navigation with **Wouter**.
12 | - Forms with **React Hooks Forms**.
13 | - Notifications with **React Hot Toast**.
14 | - Design and styles with **Material UI**.
15 |
16 | This tutorial is perfect for both beginners looking for a detailed "react js tutorial" and experienced developers wanting to expand their knowledge and learn about new libraries.
17 |
18 | You don't need to download anything beforehand; I'll show you how to do everything from the beginning.
19 |
20 | So, prepare your development environment and join me on this exciting journey through the world of React JS!
21 |
--------------------------------------------------------------------------------
/src/components/BookingForm.jsx:
--------------------------------------------------------------------------------
1 | import toast from "react-hot-toast";
2 | import { useForm } from "react-hook-form";
3 | import Input from "@mui/material/Input";
4 | import Button from "@mui/material/Button";
5 | import useStore from "../store";
6 | import { Typography } from "@mui/material";
7 |
8 | const BookingForm = ({ hotel }) => {
9 | const {
10 | register,
11 | handleSubmit,
12 | formState: { errors },
13 | } = useForm();
14 | const addReservation = useStore((state) => state.addReservation);
15 |
16 | const onSubmit = (data) => {
17 | addReservation(hotel, data);
18 | toast.success("Reservation made!");
19 | };
20 |
21 | return (
22 |
38 | );
39 | };
40 |
41 | export default BookingForm;
42 |
--------------------------------------------------------------------------------
/public/vite.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------
/src/components/HotelDetails.jsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { useRoute } from "wouter";
3 | import BookingForm from "./BookingForm";
4 |
5 | import Card from "@mui/material/Card";
6 | import CardActions from "@mui/material/CardActions";
7 | import CardContent from "@mui/material/CardContent";
8 | import CardMedia from "@mui/material/CardMedia";
9 | import Button from "@mui/material/Button";
10 | import Typography from "@mui/material/Typography";
11 |
12 | const fetchHotel = async (id) => {
13 | const response = await fetch(`http://localhost:3001/hotels/${id}`);
14 | if (!response.ok) {
15 | throw new Error("Network response was not ok");
16 | }
17 | return response.json();
18 | };
19 |
20 | const HotelDetails = () => {
21 | const [match, params] = useRoute("/hotel/:id");
22 | const {
23 | data: hotel,
24 | isLoading,
25 | error,
26 | } = useQuery({
27 | queryKey: ["hotel", params.id],
28 | queryFn: () => fetchHotel(params.id),
29 | });
30 |
31 | if (isLoading) {
32 | return Loading...
;
33 | }
34 |
35 | if (error) {
36 | return Error fetching Hotel! {error.message}
;
37 | }
38 |
39 | return (
40 |
41 |
42 |
43 |
44 | {hotel.name}
45 |
46 |
47 | {hotel.description}
48 |
49 |
50 |
51 |
52 |
53 |
54 | );
55 | };
56 |
57 | export default HotelDetails;
58 |
--------------------------------------------------------------------------------
/src/components/HotelList.jsx:
--------------------------------------------------------------------------------
1 | import { useQuery } from "@tanstack/react-query";
2 | import { Link } from "wouter";
3 |
4 | import Stack from "@mui/material/Stack";
5 | import Card from "@mui/material/Card";
6 | import CardActions from "@mui/material/CardActions";
7 | import CardContent from "@mui/material/CardContent";
8 | import CardMedia from "@mui/material/CardMedia";
9 | import Button from "@mui/material/Button";
10 | import Typography from "@mui/material/Typography";
11 |
12 | const fetchHotels = async () => {
13 | const response = await fetch("http://localhost:3001/hotels");
14 | if (!response.ok) {
15 | throw new Error("Network response was not ok");
16 | }
17 | return response.json();
18 | };
19 |
20 | const HotelList = () => {
21 | const {
22 | data: hotels,
23 | isLoading,
24 | error,
25 | } = useQuery({ queryKey: ["hotels"], queryFn: fetchHotels });
26 |
27 | if (isLoading) {
28 | return Loading...
;
29 | }
30 |
31 | if (error) {
32 | return Error fetching Hotels! {error.message}
;
33 | }
34 |
35 | return (
36 | <>
37 |
38 | Booking App
39 |
40 | ;
41 |
42 | {hotels.map((hotel) => (
43 |
44 |
45 |
50 |
51 |
52 | {hotel.name}
53 |
54 |
55 | {hotel.description}
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 | ))}
64 |
65 | >
66 | );
67 | };
68 |
69 | export default HotelList;
70 |
--------------------------------------------------------------------------------
/src/assets/react.svg:
--------------------------------------------------------------------------------
1 |
--------------------------------------------------------------------------------