Skip to content

Commit

Permalink
Merge pull request #27 from ITurres/feature/finish-final-touches-left…
Browse files Browse the repository at this point in the history
…-overs

Issue #25 🎫: Finish final touches left overs
  • Loading branch information
ITurres authored Apr 15, 2024
2 parents 899615c + c96276b commit fcac8fb
Show file tree
Hide file tree
Showing 26 changed files with 588 additions and 247 deletions.
25 changes: 13 additions & 12 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
<a name="readme-top"></a>

<div align="center">
<img src="public/tesla-red-logo.png" alt="" width="170" height="auto" />
<h2><b>Tesla | Book a Ride App</b></h2>
<img src="public/tesla-red-logo.png" alt="" width="150" height="auto" />
<h2><b>Tesla | Booking App</b></h2>
</div>

---
Expand All @@ -17,7 +17,7 @@
- [Tech Stack](#tech-stack)
- [⚙️ Setting Up Environment Variables](#env-setup)
- [🖥️ Backend Integration](#backend-integration)
- [Key Features](#key-features)
- [🔑 Key Features](#key-features)
- [🚀 Live Demo](#live-demo)
- [💻 Getting Started](#getting-started)
- [Setup](#setup)
Expand All @@ -35,15 +35,15 @@

<!-- ! PROJECT DESCRIPTION -->

# 📖 Tesla | Book a Ride App <a name="about-project"></a>
# 📖 Tesla | Booking App <a name="about-project"></a>

**Tesla | Book a Ride App** Team (4): is a project developed by Front-end [Arturo Emanuel Guerra Iturres](https://www.linkedin.com/in/arturoemanuelguerraiturres/), [Mahammad Mostafa](https://www.linkedin.com/in/mahammad-mostafa/), and Back-end [Demes Ameneshoa](https://www.linkedin.com/in/demesameneshoa/) and [Fatema Nazari](https://www.linkedin.com/in/fatemanazari/).
**Tesla | Booking App** is a project developed by Front-end [Arturo Emanuel Guerra Iturres](https://www.linkedin.com/in/arturoemanuelguerraiturres/), [Mahammad Mostafa](https://www.linkedin.com/in/mahammad-mostafa/), and Back-end [Demes Ameneshoa](https://www.linkedin.com/in/demesameneshoa/) and [Fatema Nazari](https://www.linkedin.com/in/fatemanazari/).

> Book your ideal Tesla model effortlessly with our new
> Tesla vehicle booking app! Enjoy daily rates on our five available models,
> available in cities worldwide. Experience the best service in the industry.
- **Back-end repository:** [Tesla | Book a Ride App - Back-end](https://github.com/mahammad-mostafa/tesla-booking-back-end).
- **Back-end repository:** [Tesla | Booking App - Back-end](https://github.com/mahammad-mostafa/tesla-booking-back-end).

---

Expand Down Expand Up @@ -108,7 +108,7 @@
</li>
<li>
<img src="https://skillicons.dev/icons?i=sass"/>
<a href="https://sass-lang.com/">SASS</a>
<a href="https://sass-lang.com/">SCSS</a>
</li>
<li>
<img src="https://skillicons.dev/icons?i=html"/>
Expand Down Expand Up @@ -182,7 +182,7 @@ Feel free to reach out if you encounter any issues or need further assistance wi

<!-- ! Features -->

### Key Features <a name="key-features"></a>
### 🔑 Key Features <a name="key-features"></a>

- Register with name, email, and password.
- Log in with email and password.
Expand Down Expand Up @@ -311,9 +311,9 @@ You may also see any lint errors in the console.

## 🔭 Future Features <a name="future-features"></a>

- [ ] **Test the app with Jest and React Testing Library.**
- [ ] **Address all the inner issues left at the final-touches issue.**
- [ ] **Dark mode.**
- [ ] **Test the app with Jest and React Testing Library**.
- [x] **Address all the inner issues left at the final-touches issue**.
- [ ] **Dark mode**.
- [ ] **Option to Delete reservations**.

<p align="right">(<a href="#readme-top">back to top</a>)</p>
Expand Down Expand Up @@ -346,7 +346,8 @@ Give a ⭐ if you liked this project!

## 🙏 Acknowledgments <a name="acknowledgements"></a>

- We the team would like to thank each other for the hard work and dedication to this project.
- We the team would like to thank each other for the hard work and dedication to this project. We would also like to thank the Code Reviewers and Project Managers for their support and guidance throughout the project.

- Original [design](https://www.behance.net/gallery/26425031/Vespa-Responsive-Redesign) idea by [Murat Korkmaz](https://www.behance.net/muratk).

<p align="right">(<a href="#readme-top">back to top</a>)</p>
Expand Down
8 changes: 3 additions & 5 deletions public/index.html
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
<!DOCTYPE html>
<html lang="en">

<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="" />
<meta name="theme-color" content="#e82127" />
<meta
name="description"
content="Book your ideal Tesla model effortlessly with our new
Expand All @@ -20,13 +19,12 @@
rel="apple-touch-icon"
href="%PUBLIC_URL%/tesla-apple-touch-icon.png"
/>
<!-- ? needed? <link rel="manifest" href="%PUBLIC_URL%/manifest.json" /> -->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<title>Tesla</title>
</head>

<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
</body>

</html>
</html>
25 changes: 14 additions & 11 deletions public/manifest.json
Original file line number Diff line number Diff line change
@@ -1,25 +1,28 @@
{
"short_name": "Tesla Booking",
"name": "Tesla Booking App",
"start_url": ".",
"display": "standalone",
"theme_color": "#e82127",
"background_color": "#e6e6e6",
"icons": [
{
"src": "favicon.ico",
"src": "tesla-red-logo-favicon.png",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
"type": "image/x-icon",
"purpose": "any maskable"
},
{
"src": "logo192.png",
"src": "tesla-apple-touch-icon.png",
"type": "image/png",
"sizes": "192x192"
"sizes": "192x192",
"purpose": "any maskable"
},
{
"src": "logo512.png",
"src": "tesla-apple-touch-icon.png",
"type": "image/png",
"sizes": "512x512"
"sizes": "512x512",
"purpose": "any maskable"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
]
}
41 changes: 39 additions & 2 deletions src/app/App.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,41 @@ import AddVehiclePage from '../pages/AddVehiclePage';
import VehicleDetail from '../features/vehicles/VehicleDetail';
import ReservationList from '../features/reservations/ReservationsList';
import PageNotFound from '../components/PageNotFound';
import DeleteVehicleList from '../features/vehicles/DeleteVehicleList';
import ListVehiclesToDelete from '../features/vehicles/ListVehiclesToDelete';

import deleteCookie from '../helpers/deleteCookie';
import getCookie from '../helpers/getCookie';

function getAndDeleteCookie() {
const userTokenCookieName = 'tesla-booking-user-token';
if (getCookie(userTokenCookieName)) {
deleteCookie(userTokenCookieName);
}
}

// * Check if the browser supports the PerformanceNavigationTiming API.
function isNavigationTimingSupported() {
return 'performance' in window && 'getEntriesByType' in performance;
}

// ! When user reloads the page, the user is logged out and the token (cookie) is deleted.
// ! Ideally, the user should stay logged in when the page is reloaded.
// ! This is a momentary solution to delete the token when the page is reloaded, so when
// ! the main page vehicle list is loaded, does not contain the user's vehicles.
document.addEventListener('DOMContentLoaded', () => {
if (isNavigationTimingSupported()) {
if (performance.getEntriesByType('navigation')[0]?.type === 'reload') {
getAndDeleteCookie();
}
} else {
// eslint-disable-next-line no-lonely-if
if (performance.navigation.type === 1) {
// * Fallback ^^^ for non-supporting browsers.
// TODO:Note: 'navigation' is deprecated and will be removed in the future.
getAndDeleteCookie();
}
}
});

function App() {
const userLogged = useSelector((state) => state.users.logged);
Expand All @@ -24,7 +58,10 @@ function App() {
<Route path="/reservations" element={<ReservationList />} />
<Route path="/reservations/new" element={<ReservationPage />} />
<Route path="/vehicles/new" element={<AddVehiclePage />} />
<Route path="/vehicles/delete" element={<DeleteVehicleList />} />
<Route
path="/vehicles/delete"
element={<ListVehiclesToDelete />}
/>
</>
)}
</Route>
Expand Down
50 changes: 50 additions & 0 deletions src/components/DataStatusDialog.jsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import PropTypes from 'prop-types';

// * 'out-of-normal-flow' class is used to style the dialog when it is not centered
// * this will place the dialog with an absolute position on the screen.

import '../styles/components/DataStatusDialog.scss';

function DataStatusDialog({
isCentered,
paragraph,
strongText,
status,
reFetchFunction,
}) {
return (
<div
className={`data-status-component ${
isCentered ? '' : 'out-of-normal-flow'
}`}
>
<p>
{paragraph}
&nbsp;
<strong>{strongText}</strong>
</p>
<h3>{status}</h3>
<button type="button" className="btn" onClick={reFetchFunction}>
Refresh
</button>
</div>
);
}

DataStatusDialog.propTypes = {
isCentered: PropTypes.bool,
paragraph: PropTypes.string,
strongText: PropTypes.string,
status: PropTypes.string,
reFetchFunction: PropTypes.func.isRequired,
};

DataStatusDialog.defaultProps = {
isCentered: false,
paragraph:
'The project database is currently hosted on Render.com and is in a dormant state. Kindly allow a few moments for the database to initialize and become active. ',
strongText:
'We appreciate your patience. Once the database is active, you will be able to see the updated list of vehicles, log in, register, and more.',
status: 'Loading...',
};
export default DataStatusDialog;
33 changes: 26 additions & 7 deletions src/components/SidePanel.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,19 @@ import { useEffect, useRef } from 'react';
import { useLocation } from 'react-router-dom';
import {
FaBars,
FaFacebook, FaInstagram, FaXTwitter, FaXmark, FaYoutube,
FaFacebook,
FaInstagram,
FaXTwitter,
FaXmark,
FaYoutube,
} from 'react-icons/fa6';

import NavigationLinks from './NavigationLinks';
import getRandomId from '../helpers/getRandomId';
import Logo from '../app/assets/images/tesla-red-word-logo.png';
import '../styles/components/SidePanel.scss';

const SidePanel = () => {
function SidePanel() {
const panel = useRef(null);
const location = useLocation();
const socials = [
Expand All @@ -18,6 +23,8 @@ const SidePanel = () => {
{ id: getRandomId(), icon: <FaYoutube /> },
{ id: getRandomId(), icon: <FaXTwitter /> },
];
const iconSize = 22;

const toggleMenu = (opened) => {
if (opened) {
panel.current.classList.add('panel_visible');
Expand All @@ -27,22 +34,34 @@ const SidePanel = () => {
setTimeout(() => panel.current.classList.remove('panel_visible'), 500);
}
};

useEffect(() => {
if (panel.current.classList.contains('panel_visible')) {
toggleMenu(false);
}
}, [location]);

return (
<menu>
<button type="button" aria-label="menu" className="panel_button" onClick={() => toggleMenu(true)}>
<FaBars />
<button
type="button"
aria-label="menu"
className="panel_button"
onClick={() => toggleMenu(true)}
>
<FaBars className="bars_icon_svg" size={iconSize} />
</button>
<div className="panel_menu" ref={panel}>
<div>
<span>
<img src={Logo} alt="Tesla Booking" />
<button type="button" aria-label="Close" onClick={() => toggleMenu(false)}>
<FaXmark />
<button
type="button"
aria-label="Close"
className="mobile_close_button"
onClick={() => toggleMenu(false)}
>
<FaXmark className="x_icon_svg" size={iconSize} />
</button>
</span>
<NavigationLinks />
Expand All @@ -57,6 +76,6 @@ const SidePanel = () => {
</div>
</menu>
);
};
}

export default SidePanel;
2 changes: 1 addition & 1 deletion src/features/reservations/ReservationForm.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ const ReservationForm = () => {
const locationRef = useRef(null);
const vehicleRef = useRef(null);

const vehicles = useSelector((state) => state.vehicles.vehicles);
const vehicles = useSelector((state) => state.vehicles.vehiclesList);
const userName = useSelector((state) => state.users.userName) || 'Guest';
const vehicle = useSelector((state) => state.reservations.vehicle);

Expand Down
2 changes: 1 addition & 1 deletion src/features/reservations/ReservationItem.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ const ReservationItem = ({
ReservationItem.propTypes = {
item: PropTypes.shape({
carModelName: PropTypes.string.isRequired,
rentalPrice: PropTypes.number.isRequired,
rentalPrice: PropTypes.string.isRequired,
image: PropTypes.string.isRequired,
date: PropTypes.string.isRequired,
location: PropTypes.string.isRequired,
Expand Down
4 changes: 3 additions & 1 deletion src/features/reservations/ReservationsList.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,9 @@ const ReservationList = () => {
const dispatch = useDispatch();
const error = useSelector((state) => state.reservations.error);
const loading = useSelector((state) => state.reservations.loading);
const reservations = useSelector((state) => state.reservations.reservations);
const reservations = useSelector(
(state) => state.reservations.reservationsList,
);

const toggleLoader = (open) => {
if (open && !loader.current.classList.contains('reservations_visible')) {
Expand Down
8 changes: 4 additions & 4 deletions src/features/reservations/reservationsSlice.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ const reservationSlice = createSlice({
name: 'reservations',
initialState: {
vehicle: null,
reservations: [],
reservationsList: [],
loading: false,
error: null,
},
Expand All @@ -16,7 +16,7 @@ const reservationSlice = createSlice({
// ? the API will return a single object, not an array.
// ? thats why we need to create a new array with the new reservation
// ? to be iterable in 'ReservationsList' component.
state.reservations = [payload];
state.reservationsList = [payload];
},
selectVehicle: (state, { payload }) => {
state.vehicle = payload;
Expand All @@ -31,7 +31,7 @@ const reservationSlice = createSlice({
state.loading = false;
// ? When is fulfilled, we are getting a single object, not an array.
// ? thats why we need to create a new array with the new reservation.
state.reservations = [action.payload];
state.reservationsList = [action.payload];
})
.addCase(postReservation.rejected, (state, action) => {
state.loading = false;
Expand All @@ -44,7 +44,7 @@ const reservationSlice = createSlice({
.addCase(fetchReservations.fulfilled, (state, action) => {
state.loading = false;
state.error = null;
state.reservations = action.payload;
state.reservationsList = action.payload;
})
.addCase(fetchReservations.rejected, (state, action) => {
state.loading = false;
Expand Down
Loading

0 comments on commit fcac8fb

Please sign in to comment.