diff --git a/.eslintrc.json b/.eslintrc.json index 51f38ce..f782284 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,35 +1,35 @@ { - "env": { - "browser": true, - "es6": true, - "jest": true + "env": { + "browser": true, + "es6": true, + "jest": true + }, + "parser": "@babel/eslint-parser", + "parserOptions": { + "ecmaFeatures": { + "jsx": true }, - "parser": "@babel/eslint-parser", - "parserOptions": { - "ecmaFeatures": { - "jsx": true - }, - "ecmaVersion": 2018, - "sourceType": "module" - }, - "extends": ["plugin:react/recommended", "plugin:react-hooks/recommended"], - "plugins": ["react"], - "rules": { - "react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }], - "react/react-in-jsx-scope": "off", - "import/no-unresolved": "off", - "no-shadow": "off" - }, - "overrides": [ - { - // feel free to replace with your preferred file pattern - eg. 'src/**/*Slice.js' or 'redux/**/*Slice.js' - "files": ["src/**/*Slice.js"], - // avoid state param assignment - "rules": { "no-param-reassign": ["error", { "props": false }] } - } - ], - "ignorePatterns": [ - "dist/", - "build/" - ] - } \ No newline at end of file + "ecmaVersion": 2018, + "sourceType": "module" + }, + "extends": ["airbnb", "plugin:react/recommended", "plugin:react-hooks/recommended"], + "plugins": ["react"], + "rules": { + "react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }], + "react/react-in-jsx-scope": "off", + "import/no-unresolved": "off", + "no-shadow": "off" + }, + "overrides": [ + { + // feel free to replace with your preferred file pattern - eg. 'src/**/*Slice.js' or 'redux/**/*Slice.js' + "files": ["src/**/*Slice.js"], + // avoid state param assignment + "rules": { "no-param-reassign": ["error", { "props": false }] } + } + ], + "ignorePatterns": [ + "dist/", + "build/" + ] +} \ No newline at end of file diff --git a/README.md b/README.md index 58beeac..47b4806 100644 --- a/README.md +++ b/README.md @@ -1,70 +1,308 @@ -# Getting Started with Create React App +
+ CaBooky-logo +

CaBooky

+
+ + + + + +# 📗 Table of Contents +- [📖 About the Project](#about-project) + - [🛠 Built With](#built-with) + - [Tech Stack](#tech-stack) + - [Key Features](#key-features) + - [📜 Kanban Board](#kanban-board) + - [🎥 Rails Backend](#rails-backend) + - [🎞️ CaBooky Preview](#cabooky-preview) + - [🚀 Live Demo](#live-demo) +- [💻 Getting Started with Create React App](#getting-started-with-create-react-app) +- [💻 Getting Started](#getting-started) + - [Setup](#setup) + - [Prerequisites](#prerequisites) + - [Usage](#usage) + - [Run Tests](#run-tests) + - [Deployment](#deployment) +- [👥 Authors](#authors) +- [🔭 Future Features](#future-features) +- [🤝 Contributing](#contributing) +- [⭐️ Show your support](#support) +- [🙏 Acknowledgements](#acknowledgements) +- [📝 License](#license) + + + +# 📖 CaBooky + +**CaBooky** is Car booking service. This application allows the users to subscribe(sign up) to the app so that the user can add new cars and be able to reserve the cars basing on the date and city. This app uses the external RESTful api endpoints for data accessing. + +## 🛠 Built With + +### Tech Stack + +
+ Client + +
+ + + +### Key Features + +- **A user can add new items (cars)** + +- **Current user can reserve the item basing the date and city.** + +- **JavaScript and Stylelint linters are installed for code implementation improvement** + +- **Uses React Redux middleware to work with async functions for API interactions"** + +- **The application has CRUD operations.** + +

(back to top)

+ + + +## Kanban Board + + +We are a Team of Four, so we used the custom Kanban board to divide tasks and keep track of progress. + +- [Kanban Board Initial version](https://github.com/tan12082001/Vehical-Appointment-App-Backend/assets/81354942/f57bf009-046a-4c8a-817d-5b3cb163421c) +- [Kanban Board Final Version]() + +

(back to top)

+ + + +## Supporting Backend Project + + +- [Vehicle-Booking-App-Backend](https://github.com/tan12082001/hello-rails-back-end) + +

(back to top)

+ + + +## CaBooky Preview + + +

(back to top)

+ + + +## 🚀 Live Demo + + + +- [CaBooky Application ]() + +

(back to top)

+ + + +## Getting Started with Create React App This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). -## Available Scripts +
+ More Information + + ### Available Scripts + + In the project directory, you can run: + + ### `npm start` + + Runs the app in the development mode.\ + Open [http://localhost:3000](http://localhost:3000) to view it in your browser. + + The page will reload when you make changes.\ + You may also see any lint errors in the console. + + ### `npm test` + + Launches the test runner in the interactive watch mode.\ + See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + + ### `npm run build` + + Builds the app for production to the `build` folder.\ + It correctly bundles React in production mode and optimizes the build for the best performance. + + The build is minified and the filenames include the hashes.\ + Your app is ready to be deployed! + + See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. -In the project directory, you can run: + ### `npm run eject` -### `npm start` + **Note: this is a one-way operation. Once you `eject`, you can't go back!** -Runs the app in the development mode.\ -Open [http://localhost:3000](http://localhost:3000) to view it in your browser. + If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. -The page will reload when you make changes.\ -You may also see any lint errors in the console. + Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. -### `npm test` + You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. -Launches the test runner in the interactive watch mode.\ -See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. + ## Learn More -### `npm run build` + You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). -Builds the app for production to the `build` folder.\ -It correctly bundles React in production mode and optimizes the build for the best performance. + To learn React, check out the [React documentation](https://reactjs.org/). -The build is minified and the filenames include the hashes.\ -Your app is ready to be deployed! + ### Code Splitting -See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. + This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) -### `npm run eject` + ### Analyzing the Bundle Size -**Note: this is a one-way operation. Once you `eject`, you can't go back!** + This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) -If you aren't satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. + ### Making a Progressive Web App -Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you're on your own. + This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) -You don't have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn't feel obligated to use this feature. However we understand that this tool wouldn't be useful if you couldn't customize it when you are ready for it. + ### Advanced Configuration -## Learn More + This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) -You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). + ### Deployment -To learn React, check out the [React documentation](https://reactjs.org/). + This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) -### Code Splitting + ### `npm run build` fails to minify -This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) + This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) -### Analyzing the Bundle Size +
-This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) -### Making a Progressive Web App +

(back to top)

-This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) + -### Advanced Configuration +## 💻 Getting Started -This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) +To get a local copy up and running, follow these steps. + +### Prerequisites + +In order to run this project you need: + +- [Node.js and npm](https://nodejs.org/) + +### Setup + +Clone this repository to your desired folder: + +```sh + cd my-folder + + git clone git@github.com:tan12082001/Vehicle-Booking-App-Frontend.git + + npm install +``` + +### Usage + +To run the project, if you are using a Code Editor make use of a live server. +If you are working with webpack you can run the local host with the following command. + +```sh + npm start +``` + +### Run Tests + +To run the test suits: + +```sh + npm test +``` ### Deployment -This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) +You can deploy this project using: + +```sh + gh-pages +``` +This Project is deployed using [Render](https://render.com/) + +

(back to top)

+ + + +## 👥 Authors + +👤 **Deborah Fashoro** + +- GitHub: [@githubhandle](https://github.com/Simpleshaikh1) +- LinkedIn: [LinkedIn](https://www.linkedin.com/in/toyyib-abayomi/) + +👤 **Nweneary Uzochukwu Winnie** + +- GitHub: [@githubhandle](https://github.com/wineshuga) +- LinkedIn: [LinkedIn](https://www.linkedin.com/in/wineshuga/) + +👤 **Suleiman Gacheru** + +- GitHub: [@hetrox8](https://github.com/hetrox8) +- Twitter: [@suleimangacheru](https://twitter.com/SuleimanGacheru) + +👤 **Tanmayi Manku** + +- GitHub: [@tan12082001](https://github.com/tan12082001) +- LinkedIn: [LinkedIn](https://www.linkedin.com/in/tanmayi-manku-99195720a/) + +

(back to top)

+ + + +## 🔭 Future Features + +- [ ] **Apply admin authorization, and restrict the Create new car, delete a car actions unless admin.** + +- [ ] **Add the Delete Reservation Feature.** + +

(back to top)

+ + + +## 🤝 Contributing + +Contributions, issues, and feature requests are welcome! +Feel free to check the GitHub page. + +

(back to top)

+ + + +## ⭐️ Show your support + +If you like this project give it a star. +

(back to top)

+ + + +## 🙏 Acknowledgments + +We would like to thank Microverse for providing the required lessons and tutorials for successfully completing the project. + +We would like to thank [Murat Korkmaz](https://www.behance.net/muratk) for the original Wireframe design of the application [Vespa-Responsive-design](https://www.behance.net/gallery/26425031/Vespa-Responsive-Redesign). + +

(back to top)

+ + + +## 📝 License -### `npm run build` fails to minify +This project is [MIT](./LICENSE) licensed. -This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) +

(back to top)

diff --git a/src/App.js b/src/App.js index a21ea62..5e15ab5 100644 --- a/src/App.js +++ b/src/App.js @@ -1,8 +1,21 @@ import './styles/App.css'; -import React from 'react'; +import React, { useEffect } from 'react'; +import { useDispatch, useSelector } from 'react-redux'; import Router from './routes/routes'; +import { fetchCarReservations, fetchCars } from './redux/thunk'; function App() { + const authenticationStatus = useSelector((state) => state.authentication.status); + const isAuthenticated = authenticationStatus === 'succeeded'; + const dispatch = useDispatch(); + + useEffect(() => { + if (isAuthenticated) { + dispatch(fetchCars()); + dispatch(fetchCarReservations()); + } + }, [dispatch, isAuthenticated]); + return ( <> diff --git a/src/assets/CaBooky-logo-1-preview.png b/src/assets/CaBooky-logo-1-preview.png new file mode 100644 index 0000000..d0788e6 Binary files /dev/null and b/src/assets/CaBooky-logo-1-preview.png differ diff --git a/src/assets/reserve_page_background.png b/src/assets/reserve_page_background.png new file mode 100644 index 0000000..ee9003d Binary files /dev/null and b/src/assets/reserve_page_background.png differ diff --git a/src/components/Card/DisplayCartCard.js b/src/components/Card/DisplayCartCard.js index a00a88a..12e489c 100644 --- a/src/components/Card/DisplayCartCard.js +++ b/src/components/Card/DisplayCartCard.js @@ -4,8 +4,10 @@ import styled from '@emotion/styled'; import { FaFacebook, FaTwitter, FaInstagram } from 'react-icons/fa'; import { Link } from 'react-router-dom'; -const DisplayCartCard = ({ imgSrc, name, shortNote }) => ( - +const DisplayCartCard = ({ + id, imgSrc, name, shortNote, +}) => ( + {name} {name} @@ -26,6 +28,7 @@ const DisplayCartCard = ({ imgSrc, name, shortNote }) => ( ); DisplayCartCard.propTypes = { + id: PropTypes.number.isRequired, imgSrc: PropTypes.string.isRequired, name: PropTypes.string.isRequired, shortNote: PropTypes.string.isRequired, diff --git a/src/components/Card/DisplayItemCard.js b/src/components/Card/DisplayItemCard.js index f20a6ac..8bb085b 100644 --- a/src/components/Card/DisplayItemCard.js +++ b/src/components/Card/DisplayItemCard.js @@ -2,72 +2,88 @@ import React, { useState } from 'react'; import PropTypes from 'prop-types'; import styled from '@emotion/styled'; import { FaCog, FaArrowRight, FaSyncAlt } from 'react-icons/fa'; -import colorWheel from '../../components/asset/small_color_wheel.png'; - -const DisplayItemCard = ({ name, imgSrc, amount, description }) => { +import { useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; +import colorWheel from '../asset/small_color_wheel.png'; +import { RESERVE_CARS, USERS_DASHBOARD } from '../../routes/routeConstants'; + +const DisplayItemCard = ({ + id, name, description, pricePerHr, seatingCapacity, imgSrc, +}) => { + const username = useSelector((state) => state.authentication.authenticatedUser.username); + const navigate = useNavigate(); const [rotation, setRotation] = useState(0); + const handleReserveClick = () => { + navigate(`${USERS_DASHBOARD}/${RESERVE_CARS}`, { + state: { id, username, name }, + }); + }; + const handleRotate = () => { setRotation((prevRotation) => (prevRotation + 90) % 360); }; return ( - - - - - - Rotate vehicle - - - - + + + + + + Rotate vehicle + + + + {name} - {description} + {description} - - Finance Fee - {amount} - - - Opton to purchase fee - {amount} - - - Total amount payable - {amount} - - - Duration - {amount} - + + Rent Price per Hour + {pricePerHr} + + + Seating Capacity + {seatingCapacity} + + + Currently available for booking? + Yes + + + Minimum Rental Duration + 4 hrs +
- 5.9% APR Representative + 5.9% APR Representative DISCOVER MORE MODEL - Color Wheel + Color Wheel - - + + - - Reserve - + + Reserve + - + -
+
-)}; + ); +}; DisplayItemCard.propTypes = { + id: PropTypes.number.isRequired, name: PropTypes.string.isRequired, - amount: PropTypes.string.isRequired, description: PropTypes.string.isRequired, + pricePerHr: PropTypes.number.isRequired, + seatingCapacity: PropTypes.number.isRequired, imgSrc: PropTypes.string.isRequired, }; diff --git a/src/components/DeleteCars/DeleteList.js b/src/components/DeleteCars/DeleteList.js index edc11aa..632a396 100644 --- a/src/components/DeleteCars/DeleteList.js +++ b/src/components/DeleteCars/DeleteList.js @@ -1 +1,45 @@ -// delete cars list component +import React from 'react'; +import { useSelector } from 'react-redux'; +import EachCarDelete from './EachCarDelete'; + +const DeleteList = () => { + const cars = useSelector((state) => state.cars.cars); + // const mockData = [ + // { + // "id": 3, + // "name": "Car1", + // "description": "Description for Car1", + // "removed": false + // }, + // { + // "id": 4, + // "name": "Car1111", + // "description": "Description for Car1", + // "removed": false + // } + // ]; + // const cars = mockData; + + return ( + <> +
+

Cars List

+ + + + + + + + + {cars.map((car) => ( + + ))} + +
Car TitleStatus
+
+ + ); +}; + +export default DeleteList; diff --git a/src/components/DeleteCars/EachCarDelete.js b/src/components/DeleteCars/EachCarDelete.js new file mode 100644 index 0000000..b249524 --- /dev/null +++ b/src/components/DeleteCars/EachCarDelete.js @@ -0,0 +1,37 @@ +import PropTypes from 'prop-types'; +import { useDispatch } from 'react-redux'; +import { deleteCar } from '../../redux/thunk'; +import { removeMarkStatus } from '../../redux/cars/carsSlice'; + +const EachCarDelete = ({ car }) => { + const { + name, + id, + removed, + } = car; + const dispatch = useDispatch(); + const handleDelete = () => { + if (removed) { + return; + } + + dispatch(deleteCar(id)); + dispatch(removeMarkStatus(id)); + }; + return ( + + {name} + + + ); +}; + +EachCarDelete.propTypes = { + car: PropTypes.shape({ + name: PropTypes.string.isRequired, + id: PropTypes.number.isRequired, + removed: PropTypes.bool.isRequired, + }).isRequired, +}; + +export default EachCarDelete; diff --git a/src/components/Form/FormComponent2.js b/src/components/Form/FormComponent2.js index 6d1766c..69eb446 100644 --- a/src/components/Form/FormComponent2.js +++ b/src/components/Form/FormComponent2.js @@ -25,17 +25,17 @@ FormComponent2.propTypes = { name: PropTypes.string.isRequired, description: PropTypes.string.isRequired, pricePerHr: PropTypes.string.isRequired, - sitting_capacity: PropTypes.number.isRequired, + seating_capacity: PropTypes.number.isRequired, rental_duration: PropTypes.number.isRequired, - image: PropTypes.string.isRequired, + // image: PropTypes.string.isRequired, }).isRequired, schema: PropTypes.shape({ name: PropTypes.string.isRequired, description: PropTypes.string.isRequired, pricePerHr: PropTypes.string.isRequired, - sitting_capacity: PropTypes.number.isRequired, + seating_capacity: PropTypes.number.isRequired, rental_duration: PropTypes.number.isRequired, - image: PropTypes.string.isRequired, + // image: PropTypes.string.isRequired, }).isRequired, onSubmit: PropTypes.func.isRequired, children: PropTypes.node.isRequired, diff --git a/src/components/Form/FormField.js b/src/components/Form/FormField.js index 1fba652..0a36224 100644 --- a/src/components/Form/FormField.js +++ b/src/components/Form/FormField.js @@ -19,11 +19,16 @@ export const DateField = ({ handleChange(date); }; - console.log('value fromt eh field', field.value); return ( {label} - helpers.setTouched(true)} /> + helpers.setTouched(true)} + /> {meta.touched && meta.error ? ( {meta.error} ) : null} @@ -39,10 +44,9 @@ DateField.propTypes = { }; export const SelectField = ({ - label, lpiSrc, rpiSrc, className, name, id, options, ...props + label, className, name, id, options, ...props }) => { const [field, meta] = useField(name); - console.log('selected city', field.value); return ( @@ -56,8 +60,6 @@ export const SelectField = ({ ))} - {lpiSrc ? : null} - {lpiSrc ? : null} {meta.touched && meta.error ? ( {meta.error} @@ -68,8 +70,6 @@ export const SelectField = ({ SelectField.propTypes = { label: PropTypes.string.isRequired, - lpiSrc: PropTypes.string.isRequired, - rpiSrc: PropTypes.string.isRequired, className: PropTypes.string.isRequired, name: PropTypes.string.isRequired, id: PropTypes.string.isRequired, @@ -77,12 +77,12 @@ SelectField.propTypes = { PropTypes.shape({ value: PropTypes.string.isRequired, label: PropTypes.string.isRequired, - }) + }), ).isRequired, }; export const TextInputField = ({ - label, lpiSrc, rpiSrc, className, name, placeholder, id, ...props + label, className, name, placeholder, id, ...props }) => { const [field, meta] = useField(name); @@ -93,8 +93,6 @@ export const TextInputField = ({ - {lpiSrc ? : null} - {lpiSrc ? : null} {meta.touched && meta.error ? ( {meta.error} @@ -105,8 +103,6 @@ export const TextInputField = ({ TextInputField.propTypes = { label: PropTypes.string.isRequired, - lpiSrc: PropTypes.string.isRequired, - rpiSrc: PropTypes.string.isRequired, className: PropTypes.string.isRequired, name: PropTypes.string.isRequired, placeholder: PropTypes.string.isRequired, @@ -115,8 +111,6 @@ TextInputField.propTypes = { export const TextAreaInputField = ({ label, - lpiSrc, - rpiSrc, className, name, id, @@ -134,8 +128,6 @@ export const TextAreaInputField = ({ className={className || ''} rows={10} /> - {lpiSrc ? : null} - {rpiSrc ? : null} {meta.touched && meta.error ? ( {meta.error} @@ -146,14 +138,14 @@ export const TextAreaInputField = ({ TextAreaInputField.propTypes = { label: PropTypes.string.isRequired, - lpiSrc: PropTypes.string.isRequired, - rpiSrc: PropTypes.string.isRequired, className: PropTypes.string.isRequired, name: PropTypes.string.isRequired, id: PropTypes.string.isRequired, }; -export const FileInputField = ({ id, label, name, className, ...props }) => { +export const FileInputField = ({ + id, label, name, className, ...props +}) => { const [field, meta, helpers] = useField(name); const handleChange = (event) => { @@ -176,7 +168,7 @@ FileInputField.propTypes = { label: PropTypes.string.isRequired, name: PropTypes.string.isRequired, className: PropTypes.string.isRequired, - id: PropTypes.string.isRequired + id: PropTypes.string.isRequired, }; const DateFieldWrapper = styled.div` diff --git a/src/components/Form/HideableTextFormField.js b/src/components/Form/HideableTextFormField.js index 424627e..e9baf7c 100644 --- a/src/components/Form/HideableTextFormField.js +++ b/src/components/Form/HideableTextFormField.js @@ -16,12 +16,9 @@ import { const HideableTextFormField = ({ label, - lpiSrc, - rpiSrc, className, name, id, - // apiKey, ...props }) => { const [field, meta] = useField(name); @@ -60,12 +57,9 @@ const HideableTextFormField = ({ HideableTextFormField.propTypes = { label: PropTypes.string.isRequired, - lpiSrc: PropTypes.string.isRequired, - rpiSrc: PropTypes.string.isRequired, className: PropTypes.string.isRequired, name: PropTypes.string.isRequired, id: PropTypes.string.isRequired, - // apiKey: PropTypes.string, }; const VisibiltyToggleIconsArea = styled.div` diff --git a/src/components/Img/Img.js b/src/components/Img/Img.js index 50fdabf..c27ac09 100644 --- a/src/components/Img/Img.js +++ b/src/components/Img/Img.js @@ -2,9 +2,7 @@ import PropTypes from 'prop-types'; import styled from '@emotion/styled'; import React from 'react'; -const Img = ({ src, alt, className }) => { -return ; -} +const Img = ({ src, alt, className }) => ; const Imgg = styled.img` width: 100%; diff --git a/src/components/Link/Link.js b/src/components/Link/Link.js index ca46a2c..decff9a 100644 --- a/src/components/Link/Link.js +++ b/src/components/Link/Link.js @@ -54,7 +54,6 @@ export const NavigationBoxLink = styled(RouterLink)` `; NavBoxItem.propTypes = { - icon: PropTypes.node.isRequired, path: PropTypes.string.isRequired, children: PropTypes.node.isRequired, }; diff --git a/src/components/MyReservations/MyReservationsList.js b/src/components/MyReservations/MyReservationsList.js index 495b2e4..740a9e9 100644 --- a/src/components/MyReservations/MyReservationsList.js +++ b/src/components/MyReservations/MyReservationsList.js @@ -1,35 +1,37 @@ -import React, {useEffect} from 'react'; -import { useDispatch, useSelector } from 'react-redux'; -import { fetchCarReservations } from '../../redux/thunk'; +import React from 'react'; +import { useSelector } from 'react-redux'; const MyReservationsList = () => { - const dispatch = useDispatch(); - const myReservations = useSelector((state) => state.reservation.reservations); - const status = useSelector((state) => state.reservation.status); - const error = useSelector((state) => state.reservation.error); + const myReservations = useSelector((state) => state.reservation.reservations); + const newReservationName = useSelector((state) => state.reservation.newReservationName); + const status = useSelector((state) => state.reservation.status); + const error = useSelector((state) => state.reservation.error); - useEffect(() => { - dispatch(fetchCarReservations()); - }, [dispatch]); - - if (status === 'loading') { + if (status === 'loading') { return

Loading...

; - } - - if (status === 'failed') { - return

Error: {error}

; - } + } + if (status === 'failed') { return ( -
-

My Reservations

- {myReservations.map((reservation) => ( -
-

{`Car: ${reservation.car.name}, Date: ${reservation.date}, City: ${reservation.city}`}

+

+ Error: + {error} +

+ ); + } + + return ( +
+

My Reservations

+ {myReservations && myReservations.map((reservation) => ( +
+ {`Car: ${reservation.car && reservation.car.name ? reservation.car.name : newReservationName}`} + {`Date: ${reservation.date}`} + {`City: ${reservation.city}`}
- ))} + ))}
- ); + ); }; export default MyReservationsList; diff --git a/src/components/ReserveCars/ReserveCar.js b/src/components/ReserveCars/ReserveCar.js index 3dfcbe9..0269f5a 100644 --- a/src/components/ReserveCars/ReserveCar.js +++ b/src/components/ReserveCars/ReserveCar.js @@ -1,7 +1,8 @@ +import PropTypes from 'prop-types'; import React from 'react'; import ReserveCarFrom from './ReserveCarForm'; -const ReserveCar = () => ( +const ReserveCar = ({ id, username, name }) => (
@@ -19,10 +20,16 @@ const ReserveCar = () => ( the Vehicle. We have services all over the globe which some include test-riding facilites. To book the car select both City and Date, please use the selector below.

- +
); +ReserveCar.propTypes = { + id: PropTypes.number.isRequired, + username: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, +}; + export default ReserveCar; diff --git a/src/components/ReserveCars/ReserveCarForm.js b/src/components/ReserveCars/ReserveCarForm.js index 384dc86..05ecce6 100644 --- a/src/components/ReserveCars/ReserveCarForm.js +++ b/src/components/ReserveCars/ReserveCarForm.js @@ -1,30 +1,34 @@ +import PropTypes from 'prop-types'; import React from 'react'; import { format } from 'date-fns'; -// import { useDispatch } from 'react-redux'; +import { useDispatch, useSelector } from 'react-redux'; +import { useNavigate } from 'react-router-dom'; import FormComponent from '../Form/FormComponent'; import { ReserveCarSchema, reserveCarInitialValues } from '../../models/reserveCar.model'; import { DateField, SelectField, TextInputField } from '../Form/FormField'; - import FormSubmitButton from '../Button/FormSubmitButton'; -// import { reserveCar } from '../../redux/thunk'; +import { postReserveCar } from '../../redux/thunk'; +import { MY_RESERVATIONS, USERS_DASHBOARD } from '../../routes/routeConstants'; +import { getNewReservationName } from '../../redux/reservations/reservationSlice'; -const ReserveCarFrom = () => { -// const dispatch = useDispatch(); -// const { carId } = useParams(); +const ReserveCarFrom = ({ id, username, name }) => { + const dispatch = useDispatch(); + const navigate = useNavigate(); + const status = useSelector((state) => state.reservation.status); const handleSubmit = (values) => { const formattedDate = format(values.selectedDate, 'EEE, dd MMM yyyy'); - // const reservationData = { - // userName: values.userName, - // selectedDate: formattedDate, - // selectedCity: values.selectedCity - // } - // dispatch(reserveCar({carId, data: reservationData})); - console.log('Reservation successful'); - console.log(values); - console.log('Picked date is:', formattedDate); - console.log('Picked City is: ', values.selectedCity); - console.log('User name:', values.userName); + const reservationData = { + my_reservation: { + date: formattedDate, + city: values.selectedCity, + }, + }; + dispatch(postReserveCar({ carId: id, reservationData })); + if (status === 'succeeded') { + dispatch(getNewReservationName(name)); + navigate(`${USERS_DASHBOARD}/${MY_RESERVATIONS}`); + } }; const options = [ @@ -35,18 +39,15 @@ const ReserveCarFrom = () => { { value: 'City E', label: 'City E' }, ]; - const testUsername = 'Fill User Name Boo'; - // const testCarname = 'Fill Car Name Booo' - return ( - - + +
@@ -58,4 +59,10 @@ const ReserveCarFrom = () => { ); }; +ReserveCarFrom.propTypes = { + id: PropTypes.number.isRequired, + username: PropTypes.string.isRequired, + name: PropTypes.string.isRequired, +}; + export default ReserveCarFrom; diff --git a/src/layout/LandingPage/LandingPageLayout.js b/src/layout/LandingPage/LandingPageLayout.js index d9c3599..6794488 100644 --- a/src/layout/LandingPage/LandingPageLayout.js +++ b/src/layout/LandingPage/LandingPageLayout.js @@ -1,20 +1,14 @@ import React from 'react'; import { Outlet } from 'react-router-dom'; +import Header from './header/Header'; -// import Footer from './footer/Footer'; -import Header from './header/Header'; - -const LandingPageLayout = () => { - // eslint-disable-next-line arrow-body-style - return ( - <> -
-
- -
- {/*