diff --git a/.eslintrc.cjs b/.eslintrc.cjs index 3e212e1..d5a5d48 100644 --- a/.eslintrc.cjs +++ b/.eslintrc.cjs @@ -2,20 +2,21 @@ module.exports = { root: true, env: { browser: true, es2020: true }, extends: [ - 'eslint:recommended', - 'plugin:react/recommended', - 'plugin:react/jsx-runtime', - 'plugin:react-hooks/recommended', + "eslint:recommended", + "plugin:react/recommended", + "plugin:react/jsx-runtime", + "plugin:react-hooks/recommended", ], - ignorePatterns: ['dist', '.eslintrc.cjs'], - parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, - settings: { react: { version: '18.2' } }, - plugins: ['react-refresh'], + ignorePatterns: ["dist", ".eslintrc.cjs"], + parserOptions: { ecmaVersion: "latest", sourceType: "module" }, + settings: { react: { version: "18.2" } }, + plugins: ["react-refresh"], rules: { - 'react/jsx-no-target-blank': 'off', - 'react-refresh/only-export-components': [ - 'warn', + "react/prop-types": "off", + "react/jsx-no-target-blank": "off", + "react-refresh/only-export-components": [ + "warn", { allowConstantExport: true }, ], }, -} +}; diff --git a/package-lock.json b/package-lock.json index b8b9273..54432a1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,7 +14,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^9.1.0", - "styled-components": "^6.1.8" + "react-router-dom": "^6.22.3", + "styled-components": "^6.1.8", + "uid": "^2.0.2" }, "devDependencies": { "@types/react": "^18.2.66", @@ -915,6 +917,14 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@lukeed/csprng": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@lukeed/csprng/-/csprng-1.1.0.tgz", + "integrity": "sha512-Z7C/xXCiGWsg0KuKsHTKJxbWhpI3Vs5GwLfOean7MGyVFGqdRgBbAjOCh6u4bbjPc/8MJ2pZmK/0DLdCbivLDA==", + "engines": { + "node": ">=8" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -973,6 +983,14 @@ } } }, + "node_modules/@remix-run/router": { + "version": "1.15.3", + "resolved": "https://registry.npmjs.org/@remix-run/router/-/router-1.15.3.tgz", + "integrity": "sha512-Oy8rmScVrVxWZVOpEF57ovlnhpZ8CCPlnIIumVcV9nFdiSIrus99+Lw78ekXyGvVDlIsFJbSfmSovJUhCWYV3w==", + "engines": { + "node": ">=14.0.0" + } + }, "node_modules/@rollup/rollup-android-arm-eabi": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/@rollup/rollup-android-arm-eabi/-/rollup-android-arm-eabi-4.13.0.tgz", @@ -3729,6 +3747,36 @@ "node": ">=0.10.0" } }, + "node_modules/react-router": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router/-/react-router-6.22.3.tgz", + "integrity": "sha512-dr2eb3Mj5zK2YISHK++foM9w4eBnO23eKnZEDs7c880P6oKbrjz/Svg9+nxqtHQK+oMW4OtjZca0RqPglXxguQ==", + "dependencies": { + "@remix-run/router": "1.15.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8" + } + }, + "node_modules/react-router-dom": { + "version": "6.22.3", + "resolved": "https://registry.npmjs.org/react-router-dom/-/react-router-dom-6.22.3.tgz", + "integrity": "sha512-7ZILI7HjcE+p31oQvwbokjk6OA/bnFxrhJ19n82Ex9Ph8fNAq+Hm/7KchpMGlTgWhUxRHMMCut+vEtNpWpowKw==", + "dependencies": { + "@remix-run/router": "1.15.3", + "react-router": "6.22.3" + }, + "engines": { + "node": ">=14.0.0" + }, + "peerDependencies": { + "react": ">=16.8", + "react-dom": ">=16.8" + } + }, "node_modules/redux": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/redux/-/redux-5.0.1.tgz", @@ -4329,6 +4377,17 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/uid": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/uid/-/uid-2.0.2.tgz", + "integrity": "sha512-u3xV3X7uzvi5b1MncmZo3i2Aw222Zk1keqLA1YkHldREkAhAqi65wuPfe7lHx8H/Wzy+8CE7S7uS3jekIM5s8g==", + "dependencies": { + "@lukeed/csprng": "^1.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/unbox-primitive": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/unbox-primitive/-/unbox-primitive-1.0.2.tgz", diff --git a/package.json b/package.json index 640cb18..fca9547 100644 --- a/package.json +++ b/package.json @@ -16,7 +16,9 @@ "react": "^18.2.0", "react-dom": "^18.2.0", "react-redux": "^9.1.0", - "styled-components": "^6.1.8" + "react-router-dom": "^6.22.3", + "styled-components": "^6.1.8", + "uid": "^2.0.2" }, "devDependencies": { "@types/react": "^18.2.66", diff --git a/src/App.css b/src/App.css index b9d355d..e69de29 100644 --- a/src/App.css +++ b/src/App.css @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/App.jsx b/src/App.jsx index 6a9eeb6..2d32a20 100644 --- a/src/App.jsx +++ b/src/App.jsx @@ -1,42 +1,18 @@ -import { useEffect, useState } from "react"; -import reactLogo from "./assets/react.svg"; -import viteLogo from "/vite.svg"; -import "./App.css"; -import { useDispatch } from "react-redux"; -import { fetchCars } from "./redux/cars/operations"; -// import { selectCars } from "./redux/cars/carsSelectors"; +import { lazy } from "react"; +import { Route, Routes } from "react-router-dom"; +import { AppLayout } from "./components/AppLayout/AppLayout"; -function App() { - const [count, setCount] = useState(0); - const dispatch = useDispatch(); - // const cars = useSelector(selectCars); - useEffect(() => { - dispatch(fetchCars()); - }, [dispatch]); +const HomePage = lazy(() => import("./pages/HomePage")); +const NotFoundPage = lazy(() => import("./pages/NotFoundPage")); +function App() { return ( - <> -
- - Vite logo - - - React logo - -
-

Vite + React

-
- -

- Edit src/App.jsx and save to test HMR -

-
-

- Click on the Vite and React logos to learn more -

- + + }> + } /> + + } /> + ); } diff --git a/src/assets/sprite.svg b/src/assets/sprite.svg new file mode 100644 index 0000000..cbbf64e --- /dev/null +++ b/src/assets/sprite.svg @@ -0,0 +1,98 @@ + diff --git a/src/components/AppLayout/AppLayout.jsx b/src/components/AppLayout/AppLayout.jsx new file mode 100644 index 0000000..22b015a --- /dev/null +++ b/src/components/AppLayout/AppLayout.jsx @@ -0,0 +1,23 @@ +import { Outlet } from "react-router-dom"; +import { Suspense, useEffect } from "react"; +import { GlobalStyle } from "../GlobalStyle"; +import { useDispatch } from "react-redux"; +import { fetchCars } from "../../redux/cars/operations"; + +export const AppLayout = () => { + const dispatch = useDispatch(); + useEffect(() => { + dispatch(fetchCars()); + }, [dispatch]); + + return ( + <> +
+ Loader}> + + +
+ + + ); +}; diff --git a/src/components/AppLayout/AppLayout.styled.js b/src/components/AppLayout/AppLayout.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Car/Car.jsx b/src/components/Car/Car.jsx new file mode 100644 index 0000000..05f9d61 --- /dev/null +++ b/src/components/Car/Car.jsx @@ -0,0 +1,30 @@ +import { uid } from "uid"; +import { Icon } from "../Icon/Icon"; + +export const Car = ({ car }) => { + return ( +
  • + car image +
    +
    +

    {car.name}

    +

    €{car.price}.00

    + +
    +
    +

    + {car.rating} {car.reviews.length || 0} Reviews +

    +

    {car.location}

    +
    +

    {car.description}

    +
    + {/* {Object.entries(car.details).map(([key, value]) => { + + })} */} +
    + +
    +
  • + ); +}; diff --git a/src/components/Car/Car.styled.js b/src/components/Car/Car.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/CarModal/CarModal.jsx b/src/components/CarModal/CarModal.jsx new file mode 100644 index 0000000..e69de29 diff --git a/src/components/CarModal/CarModal.styled.js b/src/components/CarModal/CarModal.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/CarsList/CarsList.jsx b/src/components/CarsList/CarsList.jsx new file mode 100644 index 0000000..889f869 --- /dev/null +++ b/src/components/CarsList/CarsList.jsx @@ -0,0 +1,17 @@ +import { useSelector } from "react-redux"; +import { selectCars } from "../../redux/cars/carsSelectors"; +import { Car } from "../Car/Car"; + +export const CarsList = () => { + const cars = useSelector(selectCars); + // console.log(cars); + return ( +
    + +
    + ); +}; diff --git a/src/components/CarsList/CarsList.styled.js b/src/components/CarsList/CarsList.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/FilterButton/FilterButton.jsx b/src/components/FilterButton/FilterButton.jsx new file mode 100644 index 0000000..cd9e851 --- /dev/null +++ b/src/components/FilterButton/FilterButton.jsx @@ -0,0 +1,14 @@ +import { Icon } from "../Icon/Icon"; + +export const FilterButton = ({ id, iconType, text }) => { + return ( + + ); +}; diff --git a/src/components/FilterButton/FilterButton.styled.js b/src/components/FilterButton/FilterButton.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/Filters/Filters.jsx b/src/components/Filters/Filters.jsx new file mode 100644 index 0000000..6f8b099 --- /dev/null +++ b/src/components/Filters/Filters.jsx @@ -0,0 +1,48 @@ +import { FilterButton } from "../FilterButton/FilterButton"; + +export const Filters = () => { + return ( +
    +
    +

    Location

    + +
    +

    Filters

    +
    +

    Vehicle equipment

    +
    +
    + + + + + +
    +

    Vehicle type

    +
    +
    + + + +
    +
    + +
    + ); +}; diff --git a/src/components/Filters/Filters.styled.js b/src/components/Filters/Filters.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/components/GlobalStyle.jsx b/src/components/GlobalStyle.jsx new file mode 100644 index 0000000..0b03fab --- /dev/null +++ b/src/components/GlobalStyle.jsx @@ -0,0 +1,88 @@ +import "modern-normalize"; +import { createGlobalStyle } from "styled-components"; + +export const GlobalStyle = createGlobalStyle` + body { + margin: 0; + -webkit-font-smoothing: antialiased; + -moz-osx-font-smoothing: grayscale; + font-family: "Inter", sans-serif; + } + + +main { + min-height: calc(100vh - 688px - 54px); +} + +h1, +h2, +h3, +h4, +p, +ul, +ol, +li { + list-style: none; + margin-top: 0; + margin-bottom: 0; + padding: 0; +} + +button { + margin: 0; + padding: 0; +} + +p { + font-size: 14px; + line-height: 21px; + font-weight: 400; +} + +img { + display: block; +} + +.list { + list-style-type: none; +} + +.link { + text-decoration: none; +} + +a { + text-decoration: none; +} + +img { + display: block; + max-width: 100%; +} + +button { + cursor: pointer; + font-family: "Roboto", sans-serif; + font-size: 14px; + line-height: 21px; + font-weight: 400; +} + +.is-hidden { + display: none; +} + +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + border: 0; + padding: 0; + + white-space: nowrap; + clip-path: inset(100%); + clip: rect(0 0 0 0); +} + +`; diff --git a/src/components/Icon/Icon.jsx b/src/components/Icon/Icon.jsx new file mode 100644 index 0000000..b9a07ae --- /dev/null +++ b/src/components/Icon/Icon.jsx @@ -0,0 +1,11 @@ +import sprite from "../../assets/sprite.svg"; + +export const Icon = ({ styles, width, height, iconId }) => { + return ( + <> + + + + + ); +}; diff --git a/src/components/Icon/Icon.styled.js b/src/components/Icon/Icon.styled.js new file mode 100644 index 0000000..e69de29 diff --git a/src/index.css b/src/index.css index 6119ad9..c798e28 100644 --- a/src/index.css +++ b/src/index.css @@ -1,4 +1,4 @@ -:root { +/* :root { font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; line-height: 1.5; font-weight: 400; @@ -65,4 +65,4 @@ button:focus-visible { button { background-color: #f9f9f9; } -} +} */ diff --git a/src/main.jsx b/src/main.jsx index 5e9bd71..7c00c99 100644 --- a/src/main.jsx +++ b/src/main.jsx @@ -4,11 +4,18 @@ import App from "./App.jsx"; import "./index.css"; import { store } from "./redux/store.js"; import { Provider } from "react-redux"; +import { BrowserRouter } from "react-router-dom"; +import { ThemeProvider } from "styled-components"; +import { theme } from "./theme.js"; ReactDOM.createRoot(document.getElementById("root")).render( - - - + + + + + + + ); diff --git a/src/pages/HomePage.jsx b/src/pages/HomePage.jsx new file mode 100644 index 0000000..078f5be --- /dev/null +++ b/src/pages/HomePage.jsx @@ -0,0 +1,11 @@ +import { CarsList } from "../components/CarsList/CarsList"; +import { Filters } from "../components/Filters/Filters"; + +export default function HomePage() { + return ( +
    + + +
    + ); +} diff --git a/src/pages/NotFoundPage.jsx b/src/pages/NotFoundPage.jsx new file mode 100644 index 0000000..e7fc050 --- /dev/null +++ b/src/pages/NotFoundPage.jsx @@ -0,0 +1,9 @@ +export default function NotFoundPage() { + return ( +
    +
    +

    NotFoundPage

    +
    +
    + ); +} diff --git a/src/theme.js b/src/theme.js new file mode 100644 index 0000000..95cdb39 --- /dev/null +++ b/src/theme.js @@ -0,0 +1,17 @@ +export const theme = { + colors: { + dark: "#101828", + grey: "#475467", + red: "#E44848", + yellow: "#FFC531", + mainWhite: "#F2F4F7", + white: "#F7F7F7", + }, + radius: { + sm: "4px", + md: "8px", + lg: "16px", + }, +}; + +// ${p => p.theme.colors.white}