Skip to content

Commit

Permalink
Merge pull request #30 from HFG43/add-experience-details-page
Browse files Browse the repository at this point in the history
Add Experience Details Page
  • Loading branch information
NitBravoA92 authored Nov 7, 2023
2 parents 9d78823 + 05a5957 commit e6ae783
Show file tree
Hide file tree
Showing 13 changed files with 379 additions and 43 deletions.
4 changes: 2 additions & 2 deletions public/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,10 +9,10 @@
name="description"
content="Web site created using create-react-app"
/>
<title>React App</title>
<title>Gourmet Experience</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<main id="root"></main>
</body>
</html>
4 changes: 2 additions & 2 deletions src/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import {
} from 'react-router-dom';
import './App.css';
import './Style/register.css';
import './Style/experienceDetails.css';
import './Style/medias.css';
import { loadUserFromLocalStorage } from './Redux/Slices/usersSlice';
import LoginPage from './Components/LoginPage';
Expand All @@ -17,13 +18,12 @@ function App() {

useEffect(() => {
if (status === 'idle') {
// Set user infromation from localStorage
// Set user information from localStorage
dispatch(loadUserFromLocalStorage());
}
}, [dispatch, status]);

return (
// <div className="Routes" />
<BrowserRouter>
<Routes>
<Route path="/login" element={<LoginPage />} />
Expand Down
11 changes: 0 additions & 11 deletions src/Components/ExperienceCard.js

This file was deleted.

99 changes: 99 additions & 0 deletions src/Components/ExperienceDetails.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
import { useSelector } from 'react-redux';
import { useState } from 'react';
import { Link, useParams } from 'react-router-dom';
import { BiChevronRightCircle, BiChevronLeftCircle, BiGift } from 'react-icons/bi';
import ExperienceIncluded from './ExperienceIncluded';
import getRandomDiscount from '../logic/utils';

function ExperienceDetails() {
const { experienceID } = useParams();

const [discount] = useState(getRandomDiscount({ start: 5, end: 50 }));

const { experiences } = useSelector((state) => state.experiences);

const selectedExperience = experiences.find((exp) => exp.id === parseInt(experienceID, 10));

const experienceSlug = selectedExperience?.name.replace(/\s/g, '-').toLowerCase();

const experienceReservationURL = `/${experienceSlug}/${experienceID}/new-reservation`;

const detailsIncluded = selectedExperience?.details === undefined ? '' : selectedExperience?.details;

return (
<div className="w-100">
<div className="experience-details w-100">
<div className="experience-image w-100">
<img src={selectedExperience?.image} alt={selectedExperience?.name} className="fluid-img" />
<ExperienceIncluded mediaStyles="desktop" detailsIncluded={detailsIncluded} />
</div>
<div className="experience-content w-100">
<h2 className="experience-name text-right">{selectedExperience?.name}</h2>
<p className="experience-desc text-right">
{selectedExperience?.description}
</p>
<ul className="experience-prices w-100">
<li>
<span>Experience Fee</span>
<span>
$
{selectedExperience?.experience_fee}
</span>
</li>
<li>
<span>Add testing fee</span>
<span>
$
{selectedExperience?.add_testing_fee}
</span>
</li>
<li>
<span>Reserve Full Table</span>
<span>
$
{selectedExperience?.reserve_full_table}
</span>
</li>
<li>
<span>Guests</span>
<span>
{selectedExperience?.guests}
{' '}
people
</span>
</li>
</ul>
<ExperienceIncluded mediaStyles="mobile" detailsIncluded={detailsIncluded} />
{
discount && (
<div className="experience-offers">
<span className="bold">
{discount}
% OFF!
</span>
{' '}
<span>as a gift of the day!</span>
{' '}
<BiGift />
</div>
)
}
<div className="reserve-button">
<Link to={experienceReservationURL} className="btn-reserve btn-default d-flex">
<span>Reserve Now</span>
<BiChevronRightCircle />
</Link>
</div>
</div>
</div>

<div className="footer-content">
<Link to="/MainPage" className="btn-default btn-back">
<BiChevronLeftCircle />
</Link>
</div>
</div>
);
}

export default ExperienceDetails;
23 changes: 23 additions & 0 deletions src/Components/ExperienceIncluded.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import PropTypes from 'prop-types';

function ExperienceIncluded({ detailsIncluded, mediaStyles }) {
return (
<div className={`experience-extra-details w-100 ${mediaStyles}`}>
<h3 className="experience-extra-details-title">
¿What does this experience include?
</h3>
<p className="experience-included w-100">
{ !detailsIncluded
? (<span className="no-details"><i>No details included</i></span>)
: (detailsIncluded)}
</p>
</div>
);
}

ExperienceIncluded.propTypes = {
detailsIncluded: PropTypes.string.isRequired,
mediaStyles: PropTypes.string.isRequired,
};

export default ExperienceIncluded;
2 changes: 1 addition & 1 deletion src/Components/MainPage.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ function MainPage() {
<swiper-container slides-per-view="3" navigation="true" class="swiper-container">
{DataExperiences.experiences.map((item) => (
<swiper-slide key={item.id}>
<Link className="cardlink" to={`./experiences/${item.id}`}>
<Link className="cardlink" to={`../experiences/${item.id}`}>
<div className="card">
<h2>{item.name}</h2>
<img src={item.image} alt="ExperiencePicture" />
Expand Down
4 changes: 2 additions & 2 deletions src/Components/ReservationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -46,9 +46,9 @@ const ReservationForm = () => {
<h2 className="title-form title-reserv color">
Book
{' '}
{selectedExperience[0].name}
{selectedExperience[0]?.name}
</h2>
<span className="d-flex reserv-desc span-desc">{selectedExperience[0].description}</span>
<span className="d-flex reserv-desc span-desc">{selectedExperience[0]?.description}</span>
<form className="d-flex-col gap" onSubmit={(ev) => ev.preventDefault()}>
<span id="message-error" />
<input id="city" type="text" placeholder="City" className="form-input reserv-input" required />
Expand Down
36 changes: 23 additions & 13 deletions src/Components/RoutesWrapper.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useEffect } from 'react';
import { useEffect, useState } from 'react';
import { useSelector, useDispatch } from 'react-redux';
import { useNavigate, Routes, Route } from 'react-router-dom';
import '../Style/myReservation.css';
Expand All @@ -7,37 +7,47 @@ import { getUserReservations } from '../Redux/Slices/reservationsSlice';
import MainPage from './MainPage';
import ReservationForm from './ReservationForm';
import MyReservations from './MyReservations';
import ExperienceCard from './ExperienceCard';
import ExperienceDetails from './ExperienceDetails';

function RoutesWrapper() {
const dispatch = useDispatch();
const navigate = useNavigate();
const status = useSelector((state) => state.experiences.status);
const userStore = useSelector((state) => state.users);

const [session, setSession] = useState(null);

if (userStore.loading === 'succeeded' && userStore.status !== 'Authenticated') {
navigate('/');
}

useEffect(() => {
if (status === 'idle') {
dispatch(getExperiencesData());
if (userStore.loading === 'succeeded' && userStore.status === 'Authenticated') {
setSession(true);
}
}, [dispatch, status]);
}, [userStore]);

useEffect(() => {
if (status === 'succeeded') {
dispatch(getUserReservations(userStore.user.id));
if (session) {
if (status === 'idle') {
dispatch(getExperiencesData());
}
}
}, [dispatch, status, userStore.user.id]);
}, [dispatch, session, status]);

useEffect(() => {
if (userStore.status !== 'Authenticated') {
navigate('/');
if (session) {
if (status === 'succeeded') {
dispatch(getUserReservations(userStore.user.id));
}
}
}, [navigate, userStore.status]);
}, [dispatch, status, session, userStore.user.id]);

return (
<Routes>
<Route path="/MainPage" element={<MainPage />} />
<Route path="/experiences/:experienceID" element={<ExperienceCard />} />
<Route path="/:experienceName/:experienceID" element={<ReservationForm />} />
<Route path="/experiences/:experienceID" element={<ExperienceDetails />} />
<Route path="/:experienceName/:experienceID/new-reservation" element={<ReservationForm />} />
<Route path="/:userID/myReservations" element={<MyReservations />} />
</Routes>
);
Expand Down
23 changes: 11 additions & 12 deletions src/Redux/Slices/usersSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ const usersUrl = 'http://localhost:3000/api/v1/users/';

const initialState = {
user: { id: null, name: null, username: null },
loading: false,
loading: null,
status: 'idle',
error: null,
};
Expand All @@ -21,8 +21,10 @@ const setUserState = (state, statusMessage = 'idle') => {
state.status = statusMessage;
state.user = user;
} else {
state.status = statusMessage;
state.status = 'idle';
state.user = { id: null, name: null, username: null };
}
state.loading = 'succeeded';
};

export const validateUser = createAsyncThunk('users/validateUser', async (name) => {
Expand Down Expand Up @@ -54,11 +56,10 @@ const usersSlice = createSlice({
extraReducers(builder) {
builder
.addCase(validateUser.pending, (state) => {
state.loading = true;
state.loading = 'pending';
})
.addCase(validateUser.fulfilled, (state, action) => {
state.loading = false;

state.loading = 'succeeded';
if (action.payload.exist) {
setLocalStorage(action.payload.id, action.payload.name, action.payload.username);
setUserState(state, 'Authenticated');
Expand All @@ -67,26 +68,24 @@ const usersSlice = createSlice({
}
})
.addCase(validateUser.rejected, (state, action) => {
state.loading = false;
state.loading = 'failed';
state.error = action.error.message;
})
// Create the user
.addCase(createUser.pending, (state) => {
state.loading = true;
state.loading = 'pending';
})
.addCase(createUser.fulfilled, (state, action) => {
state.loading = false;

if (typeof action.payload === 'string') {
state.status = 'Failed to sign up';
} else {
state.status = 'created';
setLocalStorage(action.payload.id, action.payload.name, action.payload.username);
setUserState(state, 'created');
setUserState(state, 'Authenticated');
state.loading = 'succeeded';
}
})
.addCase(createUser.rejected, (state, action) => {
state.loading = false;
state.loading = 'failed';
state.error = action.error.message;
});
},
Expand Down
Loading

0 comments on commit e6ae783

Please sign in to comment.