diff --git a/.eslintrc.json b/.eslintrc.json
index 641c61e..7d401e6 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -22,7 +22,8 @@
"react/jsx-filename-extension": ["warn", { "extensions": [".js", ".jsx"] }],
"react/react-in-jsx-scope": "off",
"import/no-unresolved": "off",
- "no-shadow": "off"
+ "no-shadow": "off",
+ "quotes": "off"
},
"overrides": [
{
diff --git a/package-lock.json b/package-lock.json
index e18a84c..a66e55b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,7 @@
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.0",
"jwt-decode": "^4.0.0",
+ "prop-types": "^15.8.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-redux": "^8.1.3",
diff --git a/package.json b/package.json
index 439b0fb..24263e7 100644
--- a/package.json
+++ b/package.json
@@ -8,6 +8,7 @@
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.0",
+ "prop-types": "^15.8.1",
"jwt-decode": "^4.0.0",
"react": "^18.2.0",
"react-dom": "^18.2.0",
diff --git a/src/App.css b/src/App.css
index 8c7466b..c3d4cf3 100644
--- a/src/App.css
+++ b/src/App.css
@@ -50,3 +50,22 @@ section.signup > form {
.form-switch-link {
color: var(--color-white);
}
+
+main {
+ display: flex;
+}
+
+.mobmenu {
+ position: fixed;
+ left: 1rem;
+ top: 1rem;
+ width: 30px;
+ z-index: 5;
+ background: none;
+ border: none;
+ cursor: pointer;
+}
+
+.mobmenu > img {
+ width: 100%;
+}
diff --git a/src/App.js b/src/App.js
index 39130d9..4d8e474 100644
--- a/src/App.js
+++ b/src/App.js
@@ -1,9 +1,11 @@
-import './App.css';
-import { Route, Routes } from 'react-router-dom';
-import { useDispatch, useSelector } from 'react-redux';
-import { useEffect } from 'react';
-import { isLogIn } from './redux/login/loginSlice';
-import Login from './components/login/login';
+import "./App.css";
+import { Route, Routes } from "react-router-dom";
+import { useDispatch, useSelector } from "react-redux";
+import { useEffect } from "react";
+import { isLogIn } from "./redux/login/loginSlice";
+import Login from "./components/login/login";
+import Cars from "./components/car/Cars";
+import NavMenu from "./components/nav/NavMenu";
function App() {
const loginData = useSelector((state) => state.login);
@@ -13,7 +15,7 @@ function App() {
}, [dispatch]);
// Checking if the authentication token key exists
- const authKey = localStorage.getItem('authKey');
+ const authKey = localStorage.getItem("authKey");
// If authKey does not exist display login page
if (!authKey) {
return ;
@@ -26,9 +28,10 @@ function App() {
return (
- Main Page
+
} />
+ } />
);
diff --git a/src/CSS/Car.module.css b/src/CSS/Car.module.css
new file mode 100644
index 0000000..d72453e
--- /dev/null
+++ b/src/CSS/Car.module.css
@@ -0,0 +1,44 @@
+.car {
+ display: flex;
+ flex-direction: column;
+ gap: 35px;
+ align-items: center;
+ text-align: center;
+ width: 350px;
+ height: 450px;
+}
+
+.carImg {
+ width: 100%;
+}
+
+.carDetailsBox {
+ display: flex;
+ flex-direction: column;
+ gap: 25px;
+}
+
+.carName {
+ text-transform: uppercase;
+ font-weight: 700;
+ font-size: 1.3rem;
+}
+
+.carDescription {
+ color: #c7c9c8;
+ text-transform: capitalize;
+}
+
+.carSocialBox {
+ width: 50%;
+ display: flex;
+ justify-content: space-evenly;
+}
+
+.carSocialBox > img {
+ width: 30px;
+ padding: 5px;
+ border-radius: 50%;
+ border: 1px solid #c7c9c8;
+ cursor: pointer;
+}
diff --git a/src/CSS/Cars.module.css b/src/CSS/Cars.module.css
new file mode 100644
index 0000000..2b97e92
--- /dev/null
+++ b/src/CSS/Cars.module.css
@@ -0,0 +1,46 @@
+.carsSection {
+ width: 100%;
+ min-height: 100vh;
+ position: relative;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+ justify-content: space-around;
+ overflow-x: hidden;
+ overflow-y: unset;
+}
+
+.titleBox {
+ display: flex;
+ flex-direction: column;
+ gap: 11px;
+}
+
+.titleBox > * {
+ text-transform: uppercase;
+ text-align: center;
+}
+
+.titleBox > h2 {
+ letter-spacing: 11px;
+}
+
+.titleBox > p {
+ color: #c7c9c8;
+ font-size: 0.9rem;
+}
+
+.cars {
+ width: 70%;
+ display: flex;
+ gap: 25px;
+ transition: all 1s;
+}
+
+.dblock {
+ display: block;
+}
+
+.dnone {
+ display: none;
+}
diff --git a/src/CSS/NavMenu.module.css b/src/CSS/NavMenu.module.css
new file mode 100644
index 0000000..8944685
--- /dev/null
+++ b/src/CSS/NavMenu.module.css
@@ -0,0 +1,29 @@
+.sidebarMenu {
+ width: 250px;
+ min-height: 100vh;
+ background-color: #fff;
+ display: flex;
+ flex-direction: column;
+ align-items: center;
+}
+
+.logo {
+ width: 75%;
+}
+
+.navigation {
+ margin-top: 10rem;
+}
+
+.navlist {
+ display: flex;
+ flex-direction: column;
+ list-style-type: none;
+ gap: 2rem;
+ font-weight: 700;
+}
+
+a {
+ text-decoration: none;
+ color: rgb(0, 17, 17);
+}
diff --git a/src/CSS/Slider.module.css b/src/CSS/Slider.module.css
new file mode 100644
index 0000000..432e9ce
--- /dev/null
+++ b/src/CSS/Slider.module.css
@@ -0,0 +1,34 @@
+.prevnextbtns {
+ display: flex;
+ justify-content: space-between;
+ position: absolute;
+ width: 100%;
+}
+
+.prevnextbtns > button {
+ padding: 15px 35px;
+ background-color: var(--color-bg-2);
+ border: none;
+ color: #fff;
+ font-size: 1rem;
+ cursor: pointer;
+}
+
+.prevnextbtns > button > img {
+ width: 15px;
+}
+
+.prevnextbtns > button:nth-child(1) {
+ border-top-right-radius: 16px;
+ border-bottom-right-radius: 16px;
+ background-color: #c7c9c8;
+}
+
+.prevnextbtns > button:nth-child(1) > img {
+ transform: rotateZ(180deg);
+}
+
+.prevnextbtns > button:nth-child(2) {
+ border-top-left-radius: 16px;
+ border-bottom-left-radius: 16px;
+}
diff --git a/src/assets/arrow.png b/src/assets/arrow.png
new file mode 100644
index 0000000..2f1e2cf
Binary files /dev/null and b/src/assets/arrow.png differ
diff --git a/src/assets/car-1.png b/src/assets/car-1.png
new file mode 100644
index 0000000..c9fb2c7
Binary files /dev/null and b/src/assets/car-1.png differ
diff --git a/src/assets/fb.png b/src/assets/fb.png
new file mode 100644
index 0000000..fe4d148
Binary files /dev/null and b/src/assets/fb.png differ
diff --git a/src/assets/insta.png b/src/assets/insta.png
new file mode 100644
index 0000000..a421848
Binary files /dev/null and b/src/assets/insta.png differ
diff --git a/src/assets/logo.jpg b/src/assets/logo.jpg
new file mode 100644
index 0000000..e408fbf
Binary files /dev/null and b/src/assets/logo.jpg differ
diff --git a/src/assets/menu.png b/src/assets/menu.png
new file mode 100644
index 0000000..c8f355e
Binary files /dev/null and b/src/assets/menu.png differ
diff --git a/src/assets/twitter.png b/src/assets/twitter.png
new file mode 100644
index 0000000..de5dd37
Binary files /dev/null and b/src/assets/twitter.png differ
diff --git a/src/components/Slider.js b/src/components/Slider.js
new file mode 100644
index 0000000..170caea
--- /dev/null
+++ b/src/components/Slider.js
@@ -0,0 +1,38 @@
+import React from "react";
+// import { useDispatch, useSelector } from "react-redux";
+// import { nextCar } from "../redux/cars/carsSlice";
+import styles from "../CSS/Slider.module.css";
+import arrow from "../assets/arrow.png";
+
+const Slider = () => {
+ // const sliderIndex = useSelector((state) => state.cars.value);
+ // const dispatch = useDispatch();
+ const slider = document.querySelector("#slider");
+ const slideLeft = () => {
+ const leftMargin = +window.getComputedStyle(slider).marginLeft.slice(0, -2);
+ if (leftMargin < (slider.childElementCount + 1) * -350) return;
+ document.querySelector("#slider").style.marginLeft = `${
+ leftMargin - 350
+ }px`;
+ };
+
+ const slideRight = () => {
+ const leftMargin = +window.getComputedStyle(slider).marginLeft.slice(0, -2);
+ if (leftMargin >= 0) return;
+ document.querySelector("#slider").style.marginLeft = `${
+ leftMargin + 350
+ }px`;
+ };
+ return (
+
+
+
+
+ );
+};
+
+export default Slider;
diff --git a/src/components/car/Car.js b/src/components/car/Car.js
new file mode 100644
index 0000000..8f6a162
--- /dev/null
+++ b/src/components/car/Car.js
@@ -0,0 +1,38 @@
+import React from "react";
+import PropTypes from "prop-types";
+import styles from "../../CSS/Car.module.css";
+import carImg from "../../assets/car-1.png";
+import fb from "../../assets/fb.png";
+import twitter from "../../assets/twitter.png";
+import insta from "../../assets/insta.png";
+
+const Car = ({ name, image, description }) => (
+
+
+
+
+
+
{name}
+
{description}
+
+
+
+);
+
+Car.propTypes = {
+ name: PropTypes.string,
+ image: PropTypes.string,
+ description: PropTypes.string,
+};
+
+Car.defaultProps = {
+ name: "",
+ image: "",
+ description: "",
+};
+
+export default Car;
diff --git a/src/components/car/Cars.js b/src/components/car/Cars.js
new file mode 100644
index 0000000..757e0f8
--- /dev/null
+++ b/src/components/car/Cars.js
@@ -0,0 +1,46 @@
+import React, { useEffect } from "react";
+import { useDispatch, useSelector } from "react-redux";
+import { getCars } from "../../redux/cars/carsSlice";
+import Car from "./Car";
+import styles from "../../CSS/Cars.module.css";
+import Slider from "../Slider";
+
+const Cars = () => {
+ const { cars, isLoading } = useSelector((state) => state.cars);
+ const dispatch = useDispatch();
+ useEffect(() => {
+ dispatch(getCars());
+ }, [dispatch, cars.length]);
+
+ if (isLoading) {
+ return (
+
+
Loading...
+
+ );
+ }
+
+ return (
+
+
+
Latest Models
+
+
Please select a car model
+
+
+ {cars.map((car) => (
+
+
+
+ ))}
+
+
+
+ );
+};
+
+export default Cars;
diff --git a/src/components/nav/NavMenu.js b/src/components/nav/NavMenu.js
new file mode 100644
index 0000000..e862e7f
--- /dev/null
+++ b/src/components/nav/NavMenu.js
@@ -0,0 +1,47 @@
+import React, { useState } from "react";
+import { NavLink } from "react-router-dom";
+import styles from "../../CSS/NavMenu.module.css";
+import menu from "../../assets/menu.png";
+import logo from "../../assets/logo.jpg";
+
+const NavMenu = () => {
+ const [isOpen, setIsOpen] = useState(true);
+
+ const toggleMenu = () => {
+ setIsOpen(!isOpen);
+ };
+
+ return (
+
+
+ {isOpen && (
+
+
+
+
+ )}
+
+ );
+};
+
+export default NavMenu;
diff --git a/src/redux/cars/carsSlice.js b/src/redux/cars/carsSlice.js
new file mode 100644
index 0000000..90cd22f
--- /dev/null
+++ b/src/redux/cars/carsSlice.js
@@ -0,0 +1,63 @@
+import { createAsyncThunk, createSlice } from "@reduxjs/toolkit";
+import axios from "axios";
+import { getLocalStorageAuth } from "../../utility/helper";
+import { API_URL } from "../../utility/globalVariable";
+
+const initialState = {
+ cars: [],
+ isLoading: false,
+ error: null,
+ value: null,
+ length: null,
+};
+
+export const getCars = createAsyncThunk("cars/getCars", async (_, thunkAPI) => {
+ try {
+ const authToken = getLocalStorageAuth();
+ const response = await axios(`${API_URL}/users/1/cars`, {
+ headers: {
+ Authorization: `Bearer ${authToken}`, // Include the Authorization header with the token
+ },
+ });
+ return response.data;
+ } catch (error) {
+ return thunkAPI.rejectWithValue("Error fetching cars");
+ }
+});
+
+const carsSlice = createSlice({
+ name: "cars",
+ initialState,
+ reducers: {
+ nextCar(state, action) {
+ state.value = action.payload > state.length ? 6 : action.payload;
+ },
+ prevCar(state, action) {
+ state.value = action.payload < 6 ? state.length : action.payload;
+ },
+ dotCar() {},
+ },
+ extraReducers: {
+ [getCars.pending]: (state) => {
+ state.isLoading = true;
+ },
+ [getCars.fulfilled]: (state, action) => {
+ const data = action.payload;
+ const newdata = data.map((car) => ({
+ id: car.id,
+ name: car.name,
+ description: car.description,
+ }));
+ state.isLoading = false;
+ state.cars = newdata;
+ state.length = newdata.length;
+ state.value = newdata.length - (newdata.length - 1);
+ },
+ [getCars.rejected]: (state) => {
+ state.isLoading = false;
+ },
+ },
+});
+
+export const { nextCar, prevCar, dotCar } = carsSlice.actions;
+export default carsSlice.reducer;
diff --git a/src/redux/login/loginSlice.js b/src/redux/login/loginSlice.js
index a7a8362..b569f33 100644
--- a/src/redux/login/loginSlice.js
+++ b/src/redux/login/loginSlice.js
@@ -1,12 +1,12 @@
-import axios from 'axios';
-import { createSlice, createAsyncThunk } from '@reduxjs/toolkit';
+import axios from "axios";
+import { createSlice, createAsyncThunk } from "@reduxjs/toolkit";
import {
getUserIdFromToken,
getUserNameFromToken,
getLocalStorageAuth,
setLocalStorageAuth,
-} from '../../utility/helper';
-import { API_URL } from '../../utility/globalVariable';
+} from "../../utility/helper";
+import { API_URL } from "../../utility/globalVariable";
const initialState = {
username: null,
@@ -18,7 +18,7 @@ const initialState = {
};
export const isLogIn = createAsyncThunk(
- 'login/isLogIn',
+ "login/isLogIn",
async (_, thunkAPI) => {
try {
const Authorization = getLocalStorageAuth();
@@ -28,20 +28,20 @@ export const isLogIn = createAsyncThunk(
{ username },
{
headers: {
- 'Content-Type': 'multipart/form-data',
+ "Content-Type": "multipart/form-data",
},
},
);
- if (res.status !== 200) throw new Error('Error');
+ if (res.status !== 200) throw new Error("Error");
return res;
} catch (err) {
- return thunkAPI.rejectWithValue('Something went wrong');
+ return thunkAPI.rejectWithValue("Something went wrong");
}
},
);
export const logIn = createAsyncThunk(
- 'login/logIn',
+ "login/logIn",
async (username, thunkAPI) => {
try {
const res = await axios.post(
@@ -49,21 +49,20 @@ export const logIn = createAsyncThunk(
{ username },
{
headers: {
- 'Content-Type': 'multipart/form-data',
+ "Content-Type": "multipart/form-data",
},
},
);
- console.log(res);
- if (res.status !== 200) throw new Error('Error');
+ if (res.status !== 200) throw new Error("Error");
return res;
} catch (err) {
- return thunkAPI.rejectWithValue('Something went wrong');
+ return thunkAPI.rejectWithValue("Something went wrong");
}
},
);
export const loginSlice = createSlice({
- name: 'loginSlice',
+ name: "loginSlice",
initialState,
extraReducers: (builder) => {
builder.addCase(isLogIn.fulfilled, (state, action) => {
@@ -78,7 +77,7 @@ export const loginSlice = createSlice({
});
builder.addCase(isLogIn.rejected, (state) => {
state.isLoading = false;
- state.isError = 'Error Logging In';
+ state.isError = "Error Logging In";
});
builder.addCase(logIn.fulfilled, (state, action) => {
state.isLoading = false;
diff --git a/src/redux/store.js b/src/redux/store.js
index 4a65990..f066ed7 100644
--- a/src/redux/store.js
+++ b/src/redux/store.js
@@ -1,11 +1,13 @@
import { configureStore } from '@reduxjs/toolkit';
import loginReducer from './login/loginSlice';
import signupReducer from './signup/signupSlice';
+import carsReducer from './cars/carsSlice';
const store = configureStore({
reducer: {
login: loginReducer,
signup: signupReducer,
+ cars: carsReducer,
},
});