diff --git a/.eslintrc.js b/.eslintrc.js index cefb7cc..6bea878 100644 --- a/.eslintrc.js +++ b/.eslintrc.js @@ -1,26 +1,26 @@ -module.exports = { - parser: '@typescript-eslint/parser', - extends: ['@mate-academy/eslint-config-react-typescript'], - plugins: ['@typescript-eslint', 'react-hooks', 'prettier'], - parserOptions: { - ecmaVersion: 2018, - sourceType: 'module', - ecmaFeatures: { - jsx: true, - }, - }, - env: { - browser: true, - node: true, - es6: true, - jest: true, - }, - rules: { - 'linebreak-style': 'off', - }, - settings: { - react: { - version: 'detect', - }, - }, -}; +module.exports = { + parser: '@typescript-eslint/parser', + extends: ['@mate-academy/eslint-config-react-typescript'], + plugins: ['@typescript-eslint', 'react-hooks', 'prettier'], + parserOptions: { + ecmaVersion: 2018, + sourceType: 'module', + ecmaFeatures: { + jsx: true, + }, + }, + env: { + browser: true, + node: true, + es6: true, + jest: true, + }, + rules: { + 'linebreak-style': 'off', + }, + settings: { + react: { + version: 'detect', + }, + }, +}; diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index 43bf5e1..05e0512 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -1,45 +1,45 @@ -name: Checking with lint - -on: - pull_request: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14.x] - - steps: - - name: Checkout repository ๐Ÿ“‚ - uses: actions/checkout@v3 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - - name: Cache dependencies ๐Ÿ“ฆ - uses: actions/cache@v3 - with: - path: | - **/node_modules - key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} - - - name: Install dependencies ๐Ÿ“š - run: npm install - - - name: Run the lint ๐Ÿงช - run: npm run lint - - - name: Build ๐Ÿ”ง - run: npm run build - - env: - user_name: 'github-actions[bot]' - user_email: 'github-actions[bot]@users.noreply.github.com' - github_token: ${{ secrets.ACTIONS_DEPLOY_ACCESS_TOKEN }} - repository: ${{ github.repository }} +name: Checking with lint + +on: + pull_request: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [14.x] + + steps: + - name: Checkout repository ๐Ÿ“‚ + uses: actions/checkout@v3 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache dependencies ๐Ÿ“ฆ + uses: actions/cache@v3 + with: + path: | + **/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + + - name: Install dependencies ๐Ÿ“š + run: npm install + + - name: Run the lint ๐Ÿงช + run: npm run lint + + - name: Build ๐Ÿ”ง + run: npm run build + + env: + user_name: 'github-actions[bot]' + user_email: 'github-actions[bot]@users.noreply.github.com' + github_token: ${{ secrets.ACTIONS_DEPLOY_ACCESS_TOKEN }} + repository: ${{ github.repository }} diff --git a/.github/workflows/deploy.yml b/.github/workflows/deploy.yml index 8689578..9a74794 100644 --- a/.github/workflows/deploy.yml +++ b/.github/workflows/deploy.yml @@ -1,51 +1,51 @@ -name: Auto deploy - -on: - push: - branches: [ master ] - -jobs: - build: - - runs-on: ubuntu-latest - - strategy: - matrix: - node-version: [14.x] - - steps: - - name: Checkout repository ๐Ÿ“‚ - uses: actions/checkout@v3 - - - name: Use Node.js ${{ matrix.node-version }} - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node-version }} - - - name: Cache dependencies ๐Ÿ“ฆ - uses: actions/cache@v3 - with: - path: | - **/node_modules - key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} - - - name: Install dependencies ๐Ÿ“š - run: npm install - - - name: Run the lint ๐Ÿงช - run: npm run lint - - - name: Build ๐Ÿ”ง - run: npm run build - - - name: Deploy ๐Ÿš€ - run: | - git config --global user.name $user_name - git config --global user.email $user_email - git remote set-url origin https://${github_token}@github.com/${repository} - npm run deploy - env: - user_name: 'github-actions[bot]' - user_email: 'github-actions[bot]@users.noreply.github.com' - github_token: ${{ secrets.ACTIONS_DEPLOY_ACCESS_TOKEN }} - repository: ${{ github.repository }} +name: Auto deploy + +on: + push: + branches: [ master ] + +jobs: + build: + + runs-on: ubuntu-latest + + strategy: + matrix: + node-version: [14.x] + + steps: + - name: Checkout repository ๐Ÿ“‚ + uses: actions/checkout@v3 + + - name: Use Node.js ${{ matrix.node-version }} + uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node-version }} + + - name: Cache dependencies ๐Ÿ“ฆ + uses: actions/cache@v3 + with: + path: | + **/node_modules + key: ${{ runner.os }}-${{ hashFiles('**/package-lock.json') }} + + - name: Install dependencies ๐Ÿ“š + run: npm install + + - name: Run the lint ๐Ÿงช + run: npm run lint + + - name: Build ๐Ÿ”ง + run: npm run build + + - name: Deploy ๐Ÿš€ + run: | + git config --global user.name $user_name + git config --global user.email $user_email + git remote set-url origin https://${github_token}@github.com/${repository} + npm run deploy + env: + user_name: 'github-actions[bot]' + user_email: 'github-actions[bot]@users.noreply.github.com' + github_token: ${{ secrets.ACTIONS_DEPLOY_ACCESS_TOKEN }} + repository: ${{ github.repository }} diff --git a/.prettierrc b/.prettierrc index e73e0b5..e84c7e8 100644 --- a/.prettierrc +++ b/.prettierrc @@ -1,8 +1,8 @@ -{ - "printWidth": 80, - "tabWidth": 2, - "singleQuote": true, - "semi": true, - "trailingComma": "all", - "bracketSpacing": true -} +{ + "printWidth": 80, + "tabWidth": 2, + "singleQuote": true, + "semi": true, + "trailingComma": "all", + "bracketSpacing": true +} diff --git a/package-lock.json b/package-lock.json index 01856f3..f5a937a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -12054,9 +12054,9 @@ } }, "typescript": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.1.6.tgz", - "integrity": "sha512-zaWCozRZ6DLEWAWFrVDz1H6FVXzUSfTy5FUMWsQlU8Ym5JP9eO4xkTIROFCQvhQf61z6O/G6ugw3SgAnvvm+HA==", + "version": "5.3.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.3.3.tgz", + "integrity": "sha512-pXWcraxM0uxAS+tN0AG/BF2TyqmHO014Z070UsJ+pFvYuRSq8KH8DmWpnbXe0pEPDHXZV3FcAbJkijJ5oNEnWw==", "dev": true }, "unbox-primitive": { diff --git a/public/banner-accessories.png b/public/banner-accessories.png deleted file mode 100644 index 28b5c4c..0000000 Binary files a/public/banner-accessories.png and /dev/null differ diff --git a/public/banner-phones.png b/public/banner-phones.png deleted file mode 100644 index 09b0654..0000000 Binary files a/public/banner-phones.png and /dev/null differ diff --git a/public/banner-tablets.png b/public/banner-tablets.png deleted file mode 100644 index 45e303b..0000000 Binary files a/public/banner-tablets.png and /dev/null differ diff --git a/public/banners/banner-accessories.webp b/public/banners/banner-accessories.webp new file mode 100644 index 0000000..d9e69fe Binary files /dev/null and b/public/banners/banner-accessories.webp differ diff --git a/public/banners/banner-phones.webp b/public/banners/banner-phones.webp new file mode 100644 index 0000000..6498893 Binary files /dev/null and b/public/banners/banner-phones.webp differ diff --git a/public/banners/banner-tablets.webp b/public/banners/banner-tablets.webp new file mode 100644 index 0000000..05ca339 Binary files /dev/null and b/public/banners/banner-tablets.webp differ diff --git a/public/icons/Buttons/Icons/ArrowLeft.svg b/public/icons/Buttons/Icons/ArrowLeft.svg index 5a64cef..ef8650b 100644 --- a/public/icons/Buttons/Icons/ArrowLeft.svg +++ b/public/icons/Buttons/Icons/ArrowLeft.svg @@ -1,3 +1,3 @@ - - - + + + diff --git a/public/icons/Buttons/Icons/ArrowRight.svg b/public/icons/Buttons/Icons/ArrowRight.svg index e0ec5e5..c609788 100644 --- a/public/icons/Buttons/Icons/ArrowRight.svg +++ b/public/icons/Buttons/Icons/ArrowRight.svg @@ -1,3 +1,3 @@ - - - + + + diff --git a/public/index.html b/public/index.html index 7311733..d2de9a0 100644 --- a/public/index.html +++ b/public/index.html @@ -1,19 +1,19 @@ - - - - - - - - - - - Nice Gadgets - - - -
- - + + + + + + + + + + + Nice Gadgets + + + +
+ + diff --git a/src/Root.tsx b/src/Root.tsx index 0e8c4fc..335c982 100644 --- a/src/Root.tsx +++ b/src/Root.tsx @@ -6,15 +6,22 @@ import { } from 'react-router-dom'; import { App } from './App'; import { HomePage } from './modules/HomePage'; -import { PhonesPage } from './modules/PhonesPage'; +import { ProductsPage } from './modules/PhonesPage'; import { ProductDetailsPage } from './modules/ProductDetailsPage'; import { CartPage } from './modules/CartPage'; import { NotFoundPage } from './modules/NotFoundPage'; -import { TabletsPage } from './modules/TabletsPage'; import { PageInProgress } from './modules/PageInProgress'; -import { AccessoriesPage } from './modules/AccessoriesPage'; import { FavoritesPage } from './modules/FavoritesPage'; -import { getProductDetail } from './api/service'; +import { + getProductDetail, + getProductsWithSearchParams as loadPhones, + getProductAmount as loadPhonesAmount, +} from './api/service'; +import { EndPoints } from './types/Enums'; + +const MOBILE_TITLE = 'Mobile phones'; +// const TABLETS_TITLE = 'Tablet'; +// const ACCESSOTIES_TITLE = 'Accessories'; export const Root = () => ( @@ -24,14 +31,44 @@ export const Root = () => ( } /> } /> - } /> + + )} + /> } + element={( + + )} /> - } /> - } /> + {/* } + /> */} + {/* } + /> */} } /> } /> } /> diff --git a/src/modules/AccessoriesPage/AccessoriesPage.tsx b/src/modules/AccessoriesPage/AccessoriesPage.tsx index f3d43d0..1e0692a 100644 --- a/src/modules/AccessoriesPage/AccessoriesPage.tsx +++ b/src/modules/AccessoriesPage/AccessoriesPage.tsx @@ -1,7 +1,7 @@ -import './AccessoriesPage.module.scss'; - -import React from 'react'; - -export const AccessoriesPage: React.FC = () => { - return

Accessories Page

; -}; +import './AccessoriesPage.module.scss'; + +import React from 'react'; + +export const AccessoriesPage: React.FC = () => { + return

Accessories Page

; +}; diff --git a/src/modules/AccessoriesPage/index.ts b/src/modules/AccessoriesPage/index.ts index 486474a..0cc97ca 100644 --- a/src/modules/AccessoriesPage/index.ts +++ b/src/modules/AccessoriesPage/index.ts @@ -1 +1 @@ -export * from './AccessoriesPage'; +export * from './AccessoriesPage'; diff --git a/src/modules/CartPage/CartPage.module.scss b/src/modules/CartPage/CartPage.module.scss index 2d9354b..6bb528d 100644 --- a/src/modules/CartPage/CartPage.module.scss +++ b/src/modules/CartPage/CartPage.module.scss @@ -107,11 +107,21 @@ color: $color__white; @include buttons-typography; background-color: $color__accent; + cursor: pointer; + transition: all 0.3s ease; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.40); + } &__DARK { border-radius: 0; color: $color__dark-theme__white; background-color: $color__dark-theme__accent; + + &:hover { + background-color: $color__dark-theme__accent__hover; + } } } diff --git a/src/modules/CartPage/components/CartItem/index.ts b/src/modules/CartPage/components/CartItem/index.ts index 37a0553..7f07517 100644 --- a/src/modules/CartPage/components/CartItem/index.ts +++ b/src/modules/CartPage/components/CartItem/index.ts @@ -1 +1 @@ -export * from './CartItem'; +export * from './CartItem'; diff --git a/src/modules/FavoritesPage/HomeIcon.tsx b/src/modules/FavoritesPage/HomeIcon.tsx index 9a89871..be6c45d 100644 --- a/src/modules/FavoritesPage/HomeIcon.tsx +++ b/src/modules/FavoritesPage/HomeIcon.tsx @@ -1,46 +1,46 @@ -import React, { FC, SVGProps } from 'react'; - -interface IconProps extends SVGProps { - color: string; -} - -export const HomeIcon: FC = ({ color, ...props }) => { - return ( - - - - - ); -}; +import React, { FC, SVGProps } from 'react'; + +interface IconProps extends SVGProps { + color: string; +} + +export const HomeIcon: FC = ({ color, ...props }) => { + return ( + + + + + ); +}; diff --git a/src/modules/HomePage/HomePage.module.scss b/src/modules/HomePage/HomePage.module.scss index 4d35aab..69a1a50 100644 --- a/src/modules/HomePage/HomePage.module.scss +++ b/src/modules/HomePage/HomePage.module.scss @@ -1,19 +1,19 @@ -@import '../../styles/utils/mixins/mixin-media'; -@import '../../styles/utils/mixins/mixin-typography'; - -.title { - margin-bottom: 24px; - @include h1-typography; - - @include onTablet { - margin-bottom: 32px; - } - - @include onDesktop { - margin-bottom: 56px; - } - - &__DARK { - @include typography-dark; - } -} +@import '../../styles/utils/mixins/mixin-media'; +@import '../../styles/utils/mixins/mixin-typography'; + +.title { + margin-bottom: 24px; + @include h1-typography; + + @include onTablet { + margin-bottom: 32px; + } + + @include onDesktop { + margin-bottom: 56px; + } + + &__DARK { + @include typography-dark; + } +} diff --git a/src/modules/HomePage/HomePage.tsx b/src/modules/HomePage/HomePage.tsx index 298b15f..6b2d645 100644 --- a/src/modules/HomePage/HomePage.tsx +++ b/src/modules/HomePage/HomePage.tsx @@ -1,52 +1,63 @@ -import React, { useEffect, useState } from 'react'; -import cn from 'classnames'; - -import styles from './HomePage.module.scss'; - -import { ShopByCategory } from './componets/ShopByCategory'; -import { MainTitle } from './componets/MainTitle'; -import { MainSlider } from './componets/MainSlider'; -import { ProductSlider } from '../shared/ProductSlider'; -import { useAppSelector } from '../../store/hooks'; -import { getNewestProducts, getProductsWithDiscount } from '../../api/service'; -import { Product } from '../../types/Product'; - -export const HomePage: React.FC = () => { - const [newPhones, setNewPhones] = useState([]); - const [phonesWithDiscount, setPhonesWithDiscount] = useState([]); - const { isDarkTheme } = useAppSelector((state) => state.theme); - - useEffect(() => { - getNewestProducts().then(setNewPhones); - - getProductsWithDiscount().then(setPhonesWithDiscount); - }, []); - - return ( - <> - - -

- Welcome to Nice Gadgets store! -

- - - - - - - - - - ); -}; +import React, { useEffect, useState } from 'react'; +import cn from 'classnames'; + +import styles from './HomePage.module.scss'; + +import { ShopByCategory } from './componets/ShopByCategory'; +import { MainTitle } from './componets/MainTitle'; +import { MainSlider } from './componets/MainSlider'; +import { ProductSlider } from '../shared/ProductSlider'; +import { useAppSelector } from '../../store/hooks'; +import { getNewestProducts, getProductsWithDiscount } from '../../api/service'; +import { Product } from '../../types/Product'; +import { Loader } from '../shared/Loader'; + +export const HomePage: React.FC = () => { + const [newPhones, setNewPhones] = useState([]); + const [phonesWithDiscount, setPhonesWithDiscount] = useState([]); + const { isDarkTheme } = useAppSelector((state) => state.theme); + + useEffect(() => { + getNewestProducts().then(setNewPhones); + + getProductsWithDiscount().then(setPhonesWithDiscount); + }, []); + + return ( + <> + + +

+ Welcome to Nice Gadgets store! +

+ + + + {!newPhones.length + ? ( + + ) : ( + + )} + + + + {!phonesWithDiscount.length ? ( + + ) : ( + + )} + + + ); +}; diff --git a/src/modules/HomePage/componets/MainSlider/MainSlider.module.scss b/src/modules/HomePage/componets/MainSlider/MainSlider.module.scss index beceb1b..c0afdba 100644 --- a/src/modules/HomePage/componets/MainSlider/MainSlider.module.scss +++ b/src/modules/HomePage/componets/MainSlider/MainSlider.module.scss @@ -1,44 +1,44 @@ -@import '../../../../styles/blocks/grid'; -@import './slick.scss'; - -.container { - @extend .grid; -} - -.slider { - position: relative; - width: 100%; - height: 432px; - border: none; - box-sizing: border-box; - - @include onMobile { - grid-column: 1 / -1; - } - - @include onTablet { - grid-column: 2 / 12; - } - - @include onDesktop { - grid-column: 2 / 24; - } -} - -.sliderPhoto { - position: relative; - overflow: hidden; - width: 100%; - height: 320px; - - object-fit: cover; - object-position: center center; - - @include onTablet { - height: 400px; - } - - @include onDesktop { - height: 400px; - } -} +@import '../../../../styles/blocks/grid'; +@import './slick.scss'; + +.container { + @extend .grid; +} + +.slider { + position: relative; + width: 100%; + height: 432px; + border: none; + box-sizing: border-box; + + @include onMobile { + grid-column: 1 / -1; + } + + @include onTablet { + grid-column: 2 / 12; + } + + @include onDesktop { + grid-column: 2 / 24; + } +} + +.sliderPhoto { + position: relative; + overflow: hidden; + width: 100%; + height: 320px; + + object-fit: cover; + object-position: center center; + + @include onTablet { + height: 400px; + } + + @include onDesktop { + height: 400px; + } +} diff --git a/src/modules/HomePage/componets/MainSlider/MainSlider.tsx b/src/modules/HomePage/componets/MainSlider/MainSlider.tsx index d98d3d6..30787a2 100644 --- a/src/modules/HomePage/componets/MainSlider/MainSlider.tsx +++ b/src/modules/HomePage/componets/MainSlider/MainSlider.tsx @@ -1,74 +1,74 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { createRef, useEffect } from 'react'; -import Slider from 'react-slick'; -import cn from 'classnames'; - -import './slick.scss'; -import './slick-theme.scss'; - -import { bannersData } from '../../../../static/banners/bannersData'; -import style from './MainSlider.module.scss'; -import { useAppSelector } from '../../../../store/hooks'; -import { SampleNextArrow } from './SampleNextArrow'; -import { SamplePrevArrow } from './SamplePrevArrow'; - -export const MainSlider: React.FC = () => { - const ref = createRef(); - - const { isDarkTheme } = useAppSelector((state) => state.theme); - // const isDarkTheme = true; - const settings = { - dots: true, - infinite: true, - speed: 500, - slidesToShow: 1, - slidesToScroll: 1, - autoplay: true, - autoplaySpeed: 5000, - pauseOnHover: true, - swipeToSlide: true, - nextArrow: , - prevArrow: , - adaptiveHeight: true, - appendDots: (dots: any) => ( -
-
    - {dots} -
-
- ), - }; - - useEffect(() => { - if (ref.current && isDarkTheme) { - ref.current.className = 'slick-dots slick-dots__dark'; - } - - if (ref.current && !isDarkTheme) { - ref.current.className = 'slick-dots'; - } - }, [ref, isDarkTheme]); - - return ( -
- - {bannersData.map(banner => ( -
- {banner.title} -
- ))} -
-
- ); -}; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React, { createRef, useEffect } from 'react'; +import Slider from 'react-slick'; +import cn from 'classnames'; + +import './slick.scss'; +import './slick-theme.scss'; + +import { bannersData } from '../../../../static/banners/bannersData'; +import style from './MainSlider.module.scss'; +import { useAppSelector } from '../../../../store/hooks'; +import { SampleNextArrow } from './SampleNextArrow'; +import { SamplePrevArrow } from './SamplePrevArrow'; + +export const MainSlider: React.FC = () => { + const ref = createRef(); + + const { isDarkTheme } = useAppSelector((state) => state.theme); + // const isDarkTheme = true; + const settings = { + dots: true, + infinite: true, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1, + autoplay: true, + autoplaySpeed: 5000, + pauseOnHover: true, + swipeToSlide: true, + nextArrow: , + prevArrow: , + adaptiveHeight: true, + appendDots: (dots: any) => ( +
+
    + {dots} +
+
+ ), + }; + + useEffect(() => { + if (ref.current && isDarkTheme) { + ref.current.className = 'slick-dots slick-dots__dark'; + } + + if (ref.current && !isDarkTheme) { + ref.current.className = 'slick-dots'; + } + }, [ref, isDarkTheme]); + + return ( +
+ + {bannersData.map(banner => ( +
+ {banner.title} +
+ ))} +
+
+ ); +}; diff --git a/src/modules/HomePage/componets/MainSlider/SampleNextArrow.tsx b/src/modules/HomePage/componets/MainSlider/SampleNextArrow.tsx index f15f784..e46c63c 100644 --- a/src/modules/HomePage/componets/MainSlider/SampleNextArrow.tsx +++ b/src/modules/HomePage/componets/MainSlider/SampleNextArrow.tsx @@ -1,35 +1,35 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useState } from 'react'; -import { - ReactComponent as NextIcon, -} from '../../../../static/icons/arrow-right_icon.svg'; -import { useAppSelector } from '../../../../store/hooks'; - -export const SampleNextArrow: React.FC = (props: any) => { - const [isHover, setIsHover] = useState(false); - - const { className, style, onClick } = props; - const { isDarkTheme } = useAppSelector((state) => state.theme); - - return ( - <> - - - ); -}; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React, { useState } from 'react'; +import { + ReactComponent as NextIcon, +} from '../../../../static/icons/arrow-right_icon.svg'; +import { useAppSelector } from '../../../../store/hooks'; + +export const SampleNextArrow: React.FC = (props: any) => { + const [isHover, setIsHover] = useState(false); + + const { className, style, onClick } = props; + const { isDarkTheme } = useAppSelector((state) => state.theme); + + return ( + <> + + + ); +}; diff --git a/src/modules/HomePage/componets/MainSlider/SamplePrevArrow.tsx b/src/modules/HomePage/componets/MainSlider/SamplePrevArrow.tsx index fd27a23..b2e4ddf 100644 --- a/src/modules/HomePage/componets/MainSlider/SamplePrevArrow.tsx +++ b/src/modules/HomePage/componets/MainSlider/SamplePrevArrow.tsx @@ -1,35 +1,35 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useState } from 'react'; -import { - ReactComponent as PrevIcon, -} from '../../../../static/icons/arrow-left_icon.svg'; -import { useAppSelector } from '../../../../store/hooks'; - -export const SamplePrevArrow: React.FC = (props: any) => { - const [isHover, setIsHover] = useState(false); - - const { className, style, onClick } = props; - const { isDarkTheme } = useAppSelector((state) => state.theme); - - return ( - <> - - - ); -}; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React, { useState } from 'react'; +import { + ReactComponent as PrevIcon, +} from '../../../../static/icons/arrow-left_icon.svg'; +import { useAppSelector } from '../../../../store/hooks'; + +export const SamplePrevArrow: React.FC = (props: any) => { + const [isHover, setIsHover] = useState(false); + + const { className, style, onClick } = props; + const { isDarkTheme } = useAppSelector((state) => state.theme); + + return ( + <> + + + ); +}; diff --git a/src/modules/HomePage/componets/MainSlider/index.ts b/src/modules/HomePage/componets/MainSlider/index.ts index c00b339..df0fb29 100644 --- a/src/modules/HomePage/componets/MainSlider/index.ts +++ b/src/modules/HomePage/componets/MainSlider/index.ts @@ -1 +1 @@ -export * from './MainSlider'; +export * from './MainSlider'; diff --git a/src/modules/HomePage/componets/MainSlider/slick-theme.scss b/src/modules/HomePage/componets/MainSlider/slick-theme.scss index b9b6baa..28f5c05 100644 --- a/src/modules/HomePage/componets/MainSlider/slick-theme.scss +++ b/src/modules/HomePage/componets/MainSlider/slick-theme.scss @@ -1,126 +1,126 @@ -@charset "UTF-8"; -@import '../../../../styles/utils/mixins/_mixin-media.scss'; -@import '../../../../styles/utils/variables/colors'; - -/* Arrows */ -@include onMobile { - .slick-prev, - .slick-next, - .slick-arrow { - display: none; - visibility: hidden; - } -} - -@include onTablet { - .slick-arrow { - position: absolute; - height: 400px; - width: 32px; - cursor: pointer; - border: 1px solid $color__secondary; - border-radius: 48px; - background-color: $color__white; - top: 46.5% ; - -webkit-transform: translate(0, -50%); - -ms-transform: translate(0, -50%); - transform: translate(0, -50%); - padding: 0; - - &:hover { - border-color: $color__primary; - } - } - - .slick-prev { - left: -49px; - } - - .slick-next { - right: -49px; - } -} - -@include onDesktop { - .slick-arrow { - position: absolute; - height: 400px; - width: 32px; - cursor: pointer; - border: 1px solid $color__secondary; - border-radius: 48px; - background-color: $color__white; - top: 46.5% ; - -webkit-transform: translate(0, -50%); - -ms-transform: translate(0, -50%); - transform: translate(0, -50%); - padding: 0; - - &:hover { - border-color: $color__primary; - } - } - - .slick-prev { - left: -49px; - } - - .slick-next { - right: -49px; - } -} - -/* Dots */ -.slick-dots { - margin-top: 8px; - list-style: none; - text-align: center; - width: 100%; - - li { - position: relative; - display: inline-block; - - button { - border: 0; - background: transparent; - width: 24px; - height: 24px; - margin: 0 4px; - padding: 0; - cursor: pointer; - color: transparent; - - &:before { - display: block; - position: absolute; - content: ''; - width: 14px; - height: 4px; - margin-top: 7px; - margin-left: 4px; - background-color: $color__elements; - opacity: 1; - } - } - - &.slick-active button:before { - background-color: $color__primary; - opacity: 1; - } - } - - &__dark { - li { - button { - &:before { - background-color: #3b3e4a; - } - } - - &.slick-active button:before { - background-color: #f1f2f9; - } - } - } -} +@charset "UTF-8"; +@import '../../../../styles/utils/mixins/_mixin-media.scss'; +@import '../../../../styles/utils/variables/colors'; + +/* Arrows */ +@include onMobile { + .slick-prev, + .slick-next, + .slick-arrow { + display: none; + visibility: hidden; + } +} + +@include onTablet { + .slick-arrow { + position: absolute; + height: 400px; + width: 32px; + cursor: pointer; + border: 1px solid $color__secondary; + border-radius: 48px; + background-color: $color__white; + top: 46.5% ; + -webkit-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); + padding: 0; + + &:hover { + border-color: $color__primary; + } + } + + .slick-prev { + left: -49px; + } + + .slick-next { + right: -49px; + } +} + +@include onDesktop { + .slick-arrow { + position: absolute; + height: 400px; + width: 32px; + cursor: pointer; + border: 1px solid $color__secondary; + border-radius: 48px; + background-color: $color__white; + top: 46.5% ; + -webkit-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); + padding: 0; + + &:hover { + border-color: $color__primary; + } + } + + .slick-prev { + left: -49px; + } + + .slick-next { + right: -49px; + } +} + +/* Dots */ +.slick-dots { + margin-top: 8px; + list-style: none; + text-align: center; + width: 100%; + + li { + position: relative; + display: inline-block; + + button { + border: 0; + background: transparent; + width: 24px; + height: 24px; + margin: 0 4px; + padding: 0; + cursor: pointer; + color: transparent; + + &:before { + display: block; + position: absolute; + content: ''; + width: 14px; + height: 4px; + margin-top: 7px; + margin-left: 4px; + background-color: $color__elements; + opacity: 1; + } + } + + &.slick-active button:before { + background-color: $color__primary; + opacity: 1; + } + } + + &__dark { + li { + button { + &:before { + background-color: #3b3e4a; + } + } + + &.slick-active button:before { + background-color: #f1f2f9; + } + } + } +} diff --git a/src/modules/HomePage/componets/MainSlider/slick.scss b/src/modules/HomePage/componets/MainSlider/slick.scss index c229b98..8e6a0bb 100644 --- a/src/modules/HomePage/componets/MainSlider/slick.scss +++ b/src/modules/HomePage/componets/MainSlider/slick.scss @@ -1,100 +1,100 @@ -/* Slider */ - -.slick-slider { - position: relative; - display: block; - box-sizing: border-box; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -ms-touch-action: pan-y; - touch-action: pan-y; - -webkit-tap-highlight-color: transparent; -} -.slick-list { - position: relative; - overflow: hidden; - display: block; - margin: 0; - padding: 0; - - &:focus { - outline: none; - } - - &.dragging { - cursor: pointer; - cursor: hand; - } -} -.slick-slider .slick-track, -.slick-slider .slick-list { - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -ms-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); -} - -.slick-track { - position: relative; - left: 0; - top: 0; - display: block; - margin-left: auto; - margin-right: auto; - - &:before, - &:after { - content: ""; - display: table; - } - - &:after { - clear: both; - } - - .slick-loading & { - visibility: hidden; - } -} -.slick-slide { - float: left; - height: 100%; - min-height: 1px; - [dir="rtl"] & { - float: right; - } - img { - display: block; - } - &.slick-loading img { - display: none; - } - - display: none; - - &.dragging img { - pointer-events: none; - } - - .slick-initialized & { - display: block; - } - - .slick-loading & { - visibility: hidden; - } - - .slick-vertical & { - display: block; - height: auto; - border: 1px solid transparent; - } -} -.slick-arrow.slick-hidden { - display: none; -} +/* Slider */ + +.slick-slider { + position: relative; + display: block; + box-sizing: border-box; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -ms-touch-action: pan-y; + touch-action: pan-y; + -webkit-tap-highlight-color: transparent; +} +.slick-list { + position: relative; + overflow: hidden; + display: block; + margin: 0; + padding: 0; + + &:focus { + outline: none; + } + + &.dragging { + cursor: pointer; + cursor: hand; + } +} +.slick-slider .slick-track, +.slick-slider .slick-list { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} + +.slick-track { + position: relative; + left: 0; + top: 0; + display: block; + margin-left: auto; + margin-right: auto; + + &:before, + &:after { + content: ""; + display: table; + } + + &:after { + clear: both; + } + + .slick-loading & { + visibility: hidden; + } +} +.slick-slide { + float: left; + height: 100%; + min-height: 1px; + [dir="rtl"] & { + float: right; + } + img { + display: block; + } + &.slick-loading img { + display: none; + } + + display: none; + + &.dragging img { + pointer-events: none; + } + + .slick-initialized & { + display: block; + } + + .slick-loading & { + visibility: hidden; + } + + .slick-vertical & { + display: block; + height: auto; + border: 1px solid transparent; + } +} +.slick-arrow.slick-hidden { + display: none; +} diff --git a/src/modules/HomePage/componets/MainTitle/MainTitle.module.scss b/src/modules/HomePage/componets/MainTitle/MainTitle.module.scss index 32650f4..dda2ac1 100644 --- a/src/modules/HomePage/componets/MainTitle/MainTitle.module.scss +++ b/src/modules/HomePage/componets/MainTitle/MainTitle.module.scss @@ -1,7 +1,7 @@ -@import '../../../../styles/utils/mixins/mixin-media'; - -.mainTitle { - width: 0px; - height: 0px; - overflow: hidden; -} +@import '../../../../styles/utils/mixins/mixin-media'; + +.mainTitle { + width: 0px; + height: 0px; + overflow: hidden; +} diff --git a/src/modules/HomePage/componets/MainTitle/MainTitle.tsx b/src/modules/HomePage/componets/MainTitle/MainTitle.tsx index 6306b65..f7612fc 100644 --- a/src/modules/HomePage/componets/MainTitle/MainTitle.tsx +++ b/src/modules/HomePage/componets/MainTitle/MainTitle.tsx @@ -1,6 +1,6 @@ -import React from 'react'; -import styles from './MainTitle.module.scss'; - -export const MainTitle: React.FC = () => ( -

Product Catalog

-); +import React from 'react'; +import styles from './MainTitle.module.scss'; + +export const MainTitle: React.FC = () => ( +

Product Catalog

+); diff --git a/src/modules/HomePage/componets/MainTitle/index.ts b/src/modules/HomePage/componets/MainTitle/index.ts index 854ba7c..d762f96 100644 --- a/src/modules/HomePage/componets/MainTitle/index.ts +++ b/src/modules/HomePage/componets/MainTitle/index.ts @@ -1 +1 @@ -export * from './MainTitle'; +export * from './MainTitle'; diff --git a/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.module.scss b/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.module.scss index 95af21b..80f807e 100644 --- a/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.module.scss +++ b/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.module.scss @@ -1,104 +1,104 @@ -@import '../../../../styles/blocks/grid'; -@import '../../../../styles/utils/variables/colors'; -@import '../../../../styles/utils/mixins/mixin-media'; -@import '../../../../styles/utils/mixins/mixin-typography'; - -.shopByCategory { - &__title { - margin-bottom: 1.5rem; - @include h2-typography; - } - - &__titleDark { - @include typography-dark; - } - - &__container { - @extend .grid; - } - - &__article { - margin-bottom: 32px; - grid-column: span 4; - color: $color__primary; - @include onTablet { - margin-bottom: 0; - } - - @include onDesktop { - grid-column: span 8; - } - } - - &__subtitle { - @include h4-typography; - } - - &__subtitleDark { - @include typography-dark; - } - - &__content { - color: $color__secondary; - @include bodyText-typography; - } - - &__contentDark { - color: $color__dark-theme__secondary; - } - - &__imgContainer { - position: relative; - height: 0; - padding-bottom: 100%; - border-radius: 8px; - margin-bottom: 1.5rem; - overflow: hidden; - } - - &__imgPhones { - background-color: #6D6474; - } - - &__imgDark { - border-radius: 0px; - } - - &__imgTablets { - background-color: #D3D3D3; - } - - &__imgAccessories { - background-color: #D53C51; - } - - &__picture { - position: absolute; - display: block; - width: 100%; - height: 100%; - left: 10%; - top: 10%; - transition: transform 0.3s ease; - } - - &__pictureTablet { - left: 7%; - top: 5%; - } - - &__pictureAccessories { - left: 3%; - top: 0%; - } - - &__imgContainer:hover &__picture { - transform: scale(1.2); - } -} - -.shopByCategoryDark { - background-color: $color__dark-theme__black; -} - - +@import '../../../../styles/blocks/grid'; +@import '../../../../styles/utils/variables/colors'; +@import '../../../../styles/utils/mixins/mixin-media'; +@import '../../../../styles/utils/mixins/mixin-typography'; + +.shopByCategory { + &__title { + margin-bottom: 1.5rem; + @include h2-typography; + } + + &__titleDark { + @include typography-dark; + } + + &__container { + @extend .grid; + } + + &__article { + margin-bottom: 32px; + grid-column: span 4; + color: $color__primary; + @include onTablet { + margin-bottom: 0; + } + + @include onDesktop { + grid-column: span 8; + } + } + + &__subtitle { + @include h4-typography; + } + + &__subtitleDark { + @include typography-dark; + } + + &__content { + color: $color__secondary; + @include bodyText-typography; + } + + &__contentDark { + color: $color__dark-theme__secondary; + } + + &__imgContainer { + position: relative; + height: 0; + padding-bottom: 100%; + border-radius: 8px; + margin-bottom: 1.5rem; + overflow: hidden; + } + + &__imgPhones { + background-color: #6D6474; + } + + &__imgDark { + border-radius: 0px; + } + + &__imgTablets { + background-color: #D3D3D3; + } + + &__imgAccessories { + background-color: #D53C51; + } + + &__picture { + position: absolute; + display: block; + width: 100%; + height: 100%; + left: 10%; + top: 10%; + transition: transform 0.3s ease; + } + + &__pictureTablet { + left: 7%; + top: 5%; + } + + &__pictureAccessories { + left: 3%; + top: 0%; + } + + &__imgContainer:hover &__picture { + transform: scale(1.2); + } +} + +.shopByCategoryDark { + background-color: $color__dark-theme__black; +} + + diff --git a/src/modules/HomePage/componets/ShopByCategory/index.ts b/src/modules/HomePage/componets/ShopByCategory/index.ts index 8081526..2996221 100644 --- a/src/modules/HomePage/componets/ShopByCategory/index.ts +++ b/src/modules/HomePage/componets/ShopByCategory/index.ts @@ -1 +1 @@ -export * from './ShopByCategory'; +export * from './ShopByCategory'; diff --git a/src/modules/PhonesPage/PhonesPage.module.scss b/src/modules/PhonesPage/PhonesPage.module.scss index e69de29..ac74948 100644 --- a/src/modules/PhonesPage/PhonesPage.module.scss +++ b/src/modules/PhonesPage/PhonesPage.module.scss @@ -0,0 +1,88 @@ +@import '../../styles/utils/variables/colors'; +@import '../../styles/blocks/grid'; + +.productsPage { + // @include grid; + + &__DARK { + background-color: $color__dark-theme__black; + } + + &__breadCrumbs { + margin-bottom: 24px; + margin-top: 25px; + } + + &__title { + margin-bottom: 8px; + color: $color__primary; + font-size: 32px; + font-style: normal; + font-weight: 800; + line-height: 41px; + letter-spacing: -0.32px; + + &__DARK { + color: $color__dark-theme__white; + } + } + + &__errorButton { + display: flex; + height: 40px; + justify-content: center; + align-items: center; + flex: 1 0 0; + + border-radius: 8px; + border: none; + background: $color__accent; + + color: $color__white; + text-align: center; + font-size: 14px; + font-style: normal; + font-weight: 700; + line-height: 21px; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); + } + + &__DARK { + background: #905bff; + border-radius: 0px; + color: $color__dark-theme__white; + border: none; + + &:hover { + background: #a378ff; + } + } + } + + &__counter { + margin-bottom: 32px; + color: #89939a; + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 21px; + + &__DARK { + color: $color__dark-theme__secondary; + } + } + + &__filter { + display: flex; + margin-bottom: 24px; + } + + // &__phonesList { + // } + + &__phoneItem { + margin-bottom: 40px; + } +} diff --git a/src/modules/PhonesPage/PhonesPage.tsx b/src/modules/PhonesPage/PhonesPage.tsx index d78021c..05476f1 100644 --- a/src/modules/PhonesPage/PhonesPage.tsx +++ b/src/modules/PhonesPage/PhonesPage.tsx @@ -1,43 +1,185 @@ -import React, { useEffect, useState } from 'react'; - -// import styles from './PhonesPage.module.scss'; - -import { Breadcrumbs } from '../shared/Breadcrumbs'; -import { ProductList } from '../shared/ProductList'; -import { getProductsWithSearchParams } from '../../api/service'; -import { Loader } from '../shared/Loader'; -import { Pagination } from '../shared/Pagination'; -import { Filtration } from '../shared/Filtration'; -import { EndPoints } from '../../types/Enums'; -import { Product } from '../../types/Product'; - -export const PhonesPage: React.FC = () => { - const [phones, setPhones] = useState([]); - const [isLoading, setIsLoading] = useState(false); - - useEffect(() => { - setIsLoading(true); - getProductsWithSearchParams(EndPoints.Phones) - .then(setPhones) - .finally(() => setIsLoading(false)); - }, []); - - return ( - <> - - -

Mobile phones

-

N models

- - - - {isLoading ? ( - - ) : ( - - )} - - - - ); -}; +import React, { useEffect, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import classNames from 'classnames'; + +import styles from './PhonesPage.module.scss'; + +import { Breadcrumbs } from '../shared/Breadcrumbs'; +import { ProductList } from '../shared/ProductList'; +import { getProductsWithSearchParams } from '../../api/service'; +import { Loader } from '../shared/Loader'; +import { Pagination } from '../shared/Pagination'; +import { Filtration } from '../shared/Filtration'; +import { EndPoints, ProductsAmount } from '../../types/Enums'; +import { Product, QueryParams } from '../../types/Product'; +import { SortBy } from '../../types/SortBy'; +import { useAppSelector } from '../../store/hooks'; +import { getSearchWith } from '../../utils/getSearchWith'; + +type Props = { + title: string; + loadData: ( + EndPoint: EndPoints, + params?: QueryParams + ) => Promise; + loadAmount: () => Promise; +}; + +export const ProductsPage: React.FC = ({ + title, + loadData, + loadAmount, +}) => { + const { isDarkTheme } = useAppSelector((state) => state.theme); + const [products, setProducts] = useState([]); + const [isLoading, setIsLoading] = useState(false); + const [error, setError] = useState(''); + const [totalAmount, setTotalAmount] = useState(0); + const [searchParams] = useSearchParams(); + + const defaultPerPage = 'all'; + const defaultSortBy = SortBy.Newest; + const defaultPage = 1; + + const sort = searchParams.get('sort'); + const page = searchParams.get('page'); + const perPage = searchParams.get('perPage'); + + useEffect(() => { + if (searchParams.toString() === '') { + getSearchWith(searchParams, + { sort: defaultSortBy, page: defaultPage, perPage: defaultPerPage }); + } + }); + + const perPageString: string = perPage || defaultPerPage; + const currentPageNumber: number = page !== null + ? parseInt(page, 10) + : defaultPage; + const sortByEnum: SortBy = sort ? (sort as SortBy) : defaultSortBy; + + useEffect(() => { + loadAmount().then((amount) => setTotalAmount(amount.phones)); + }, [loadAmount]); + + const getTotalPages = () => { + if (perPageString === 'All') { + return 1; + } + + return totalAmount / Number(perPage); + }; + + const totalPages = getTotalPages(); + + const showPagination = () => { + if (perPage === 'All') { + return false; + } + + if (totalAmount < Number(perPage)) { + return false; + } + + return ( + + ); + }; + + useEffect(() => { + setIsLoading(true); + getProductsWithSearchParams(EndPoints.Phones) + .then(setProducts) + .finally(() => setIsLoading(false)); + }, [sortByEnum, perPageString, currentPageNumber, error, loadData]); + + // useEffect(() => { + // setIsLoading(true); + // loadData( + // perPageString, + // currentPageNumber, + // sortByEnum, + // ) + // .then(setProducts) + // .catch(setError) + // .finally(() => setIsLoading(false)); + // }, [sortByEnum, perPageString, currentPageNumber, error, loadData]); + + return ( +
+

+ +

+ +

+ {title} +

+ + {isLoading && ( + + )} + + {!isLoading && error && ( + <> +

+ {error} +

+ + + + )} + + {!isLoading && !error && ( + <> + {totalAmount + ? ( +

+ {`${totalAmount} modeles`} +

+ ) : ( +

+ {`There are no ${title.toLowerCase()} yet`} +

+ )} + +
+ +
+ +
+ +
+ + {showPagination()} + + )} +
+ ); +}; diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx index c9766cf..af39797 100644 --- a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx @@ -8,21 +8,23 @@ import styles from './ProductDetailsPage.module.scss'; import { Breadcrumbs } from '../shared/Breadcrumbs'; import { BackButton } from '../shared/BackButton'; // import { ProductDetailsSlider } from './components/ProductDetailsSlider'; +import { EndPoints } from '../../types/Enums'; import { ProductAbout } from './components/ProductDetailsSlider/ProductAbout/ProductAbout'; import { ProductTechspec } from './components/ProductDetailsSlider/ProductTechspec/ProductTechspec'; -import { EndPoints } from '../../types/Enums'; import { Detail } from '../../types/Product'; import { useAppSelector } from '../../store/hooks'; // import { ProductSlider } from '../shared/ProductSlider'; type Props = { loadData: (endPoint: EndPoints, itemId: string) => Promise, + endPoint: EndPoints }; export const ProductDetailsPage: React.FC = ({ loadData, + endPoint, }) => { const { itemId } = useParams(); const [itemDetails, setItemDetails] = useState(); @@ -30,10 +32,10 @@ export const ProductDetailsPage: React.FC = ({ useEffect(() => { if (itemId) { - loadData(EndPoints.Phones, itemId) + loadData(endPoint, itemId) .then(setItemDetails); } - }, [loadData, itemId]); + }, [loadData, itemId, endPoint]); return (
= ({ {/*
- -
*/} + + */} {itemDetails.current.description && ( { + return ( + <> +
+ + ); +}; + +export const PrevArrow: React.FC = () => { + return ( + <> +
+ + ); +}; diff --git a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/BorderColorHandler.ts b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/BorderColorHandler.ts new file mode 100644 index 0000000..f4f67cf --- /dev/null +++ b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/BorderColorHandler.ts @@ -0,0 +1,30 @@ +import styles from './ProductDetailsSlider.module.scss'; + +export const getBorderColor = ( + i: number, currentSlide: number, isDarkTheme: boolean, +) => { + let borderColor; + + switch (true) { + case currentSlide === i && isDarkTheme: + borderColor = styles['dots-border-dark-cur']; + break; + + case currentSlide !== i && isDarkTheme: + borderColor = styles['dots-border-dark']; + break; + + case currentSlide === i && !isDarkTheme: + borderColor = styles['dots-border-light-cur']; + break; + + case currentSlide !== i && !isDarkTheme: + borderColor = styles['dots-border-light']; + break; + + default: + break; + } + + return borderColor; +}; diff --git a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.module.scss b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.module.scss index e69de29..2bf1073 100644 --- a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.module.scss +++ b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.module.scss @@ -0,0 +1,191 @@ +@import '../../../../styles/utils/mixins/mixin-media'; + +.slick-prev-phone, +.slick-next-phone { + display: none; + visibility: hidden; +} + +#phoneImagesSlider { + .slick-track { + display: flex; + height: 464px !important; + } +} + +.phonePhoto { + @include onMobile { + width: 288px; + height: 288px; + object-fit: contain; + margin-inline: auto; + } + + @include onTablet { + width: 288px; + height: 288px; + object-fit: contain; + } + + @include onDesktop { + width: 464px; + height: 464px; + object-fit: contain; + } +}; + +.slick-dots, +.slick-thumb { + cursor: pointer; + width: auto !important; + + @include onMobile { + height: 40px; + width: 51px; + object-fit: contain; + padding-block: 4px; + } + + @include onTablet { + height: 28px; + width: 28px; + object-fit: contain; + padding-block: 4px; + } + + @include onDesktop { + height: 67px; + width: 67px; + object-fit: contain; + padding: 4px; + } +} + +#phone-photo { + width: auto !important; + height: 464px !important; + img { + width: auto !important; + } +} + +.vertical-dots { + ul { + display: flex !important; + flex-direction: column; + margin-right: 16px; + width: auto !important; + margin-top: 0 !important; + + @include onMobile { + flex-direction: row; + margin-right: 0px; + justify-content: center; + margin-top: 16px !important; + } + + @include onTablet { + :last-child { + margin-bottom: 0px; + } + } + + @include onDesktop { + :last-child { + margin-bottom: 0px; + } + } + + li { + width: 79px; + height: 79px; + display: flex; + justify-content: center; + align-items: center; + + @include onTablet { + width: 34px; + height: 34px; + } + + @include onDesktop { + width: 79px; + height: 79px; + display: flex; + justify-content: center; + align-items: center; + } + + @include onMobile { + width: 48px; + height: 48px; + display: flex; + justify-content: center; + align-items: center; + } + + a { + width: 100%; + height: 100%; + display: flex; + justify-content: center; + align-items: center; + } + } + } + + li { + + @include onMobile { + width: 51px !important; + margin-right: 8px; + } + + @include onTablet { + margin-bottom: 8px; + } + + @include onDesktop { + margin-bottom: 16px; + } + } + + @include onTablet { + display: flex; + flex-direction: row-reverse; + margin-right: 16px; + cursor: pointer; + } + + @include onDesktop { + display: flex; + flex-direction: row-reverse; + cursor: pointer; + margin-right: 16px; + } +} + +.dots-container { + display: flex; + flex-direction: column; +} + +.dots-border-light-cur { + border: 1px solid #0F0F11; + border-radius: 4px; +} + +.dots-border-light { + border: 1px solid #E2E6E9; + border-radius: 4px; +} + +.dots-border-dark { + border: 1px solid #3b3e4a; + border-radius: 4px; +} + +.dots-border-dark-cur { + border: 1px solid #f1f2f9; + border-radius: 4px; +} diff --git a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.tsx b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.tsx index b304713..a482af1 100644 --- a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.tsx +++ b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/ProductDetailsSlider.tsx @@ -1,7 +1,105 @@ -import React from 'react'; +/* eslint-disable jsx-a11y/anchor-is-valid */ +import React, { useState } from 'react'; +import Slider from 'react-slick'; -// import styles from './ProductDetailsSlider.module.scss'; +import styles from './ProductDetailsSlider.module.scss'; +import { NextArrow, PrevArrow } from './Arrows'; +import { useAppSelector } from '../../../../store/hooks'; +import { getBorderColor } from './BorderColorHandler'; -export const ProductDetailsSlider: React.FC = () => { - return
Product Detail Slider
; +type Props = { + images: string[] | undefined; +}; + +export const ProductDetailsSlider: React.FC = ({ images }) => { + const [currentSlide, setCurrentSlide] = useState(0); + const { isDarkTheme } = useAppSelector((state) => state.theme); + + const settings = { + customPaging: (i: number) => { + return ( + + {images?.[i]} setCurrentSlide(i)} + onKeyDown={() => setCurrentSlide(i)} + role="presentation" + /> + + ); + }, + dots: true, + infinite: true, + speed: 500, + slidesToShow: 1, + slidesToScroll: 1, + nextArrow: ( + + ), + prevArrow: ( + + ), + vertical: true, + verticalSwiping: true, + responsive: [ + { + breakpoint: 320, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + vertical: false, + verticalSwiping: false, + }, + }, + { + breakpoint: 640, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + vertical: false, + verticalSwiping: false, + }, + }, + { + breakpoint: 1200, + settings: { + slidesToShow: 1, + slidesToScroll: 1, + vertical: true, + verticalSwiping: true, + }, + }, + ], + }; + + return ( +
+ + {images?.map(image => ( +
+ {image} +
+ + ))} +
+
+ ); }; diff --git a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/index.ts b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/index.ts index a393ea6..e800eaa 100644 --- a/src/modules/ProductDetailsPage/components/ProductDetailsSlider/index.ts +++ b/src/modules/ProductDetailsPage/components/ProductDetailsSlider/index.ts @@ -1 +1 @@ -export * from './ProductDetailsSlider'; +export * from './ProductDetailsSlider'; diff --git a/src/modules/ProductDetailsPage/index.ts b/src/modules/ProductDetailsPage/index.ts index 6615089..db22256 100644 --- a/src/modules/ProductDetailsPage/index.ts +++ b/src/modules/ProductDetailsPage/index.ts @@ -1 +1 @@ -export * from './ProductDetailsPage'; +export * from './ProductDetailsPage'; diff --git a/src/modules/TabletsPage/TabletsPage.tsx b/src/modules/TabletsPage/TabletsPage.tsx index d589a30..65858a9 100644 --- a/src/modules/TabletsPage/TabletsPage.tsx +++ b/src/modules/TabletsPage/TabletsPage.tsx @@ -1,7 +1,7 @@ -import './TabletsPage.module.scss'; - -import React from 'react'; - -export const TabletsPage: React.FC = () => { - return

Tablets Page

; -}; +import './TabletsPage.module.scss'; + +import React from 'react'; + +export const TabletsPage: React.FC = () => { + return

Tablets Page

; +}; diff --git a/src/modules/TabletsPage/index.ts b/src/modules/TabletsPage/index.ts index 6988826..33c7713 100644 --- a/src/modules/TabletsPage/index.ts +++ b/src/modules/TabletsPage/index.ts @@ -1 +1 @@ -export * from './TabletsPage'; +export * from './TabletsPage'; diff --git a/src/modules/shared/AddToCart/index.ts b/src/modules/shared/AddToCart/index.ts index ca621b6..2baad75 100644 --- a/src/modules/shared/AddToCart/index.ts +++ b/src/modules/shared/AddToCart/index.ts @@ -1 +1 @@ -export * from './AddToCart'; +export * from './AddToCart'; diff --git a/src/modules/shared/AddToFavourites/index.ts b/src/modules/shared/AddToFavourites/index.ts index 96d230a..36e379f 100644 --- a/src/modules/shared/AddToFavourites/index.ts +++ b/src/modules/shared/AddToFavourites/index.ts @@ -1 +1 @@ -export * from './AddToFavourites'; +export * from './AddToFavourites'; diff --git a/src/modules/shared/BackButton/index.ts b/src/modules/shared/BackButton/index.ts index b68e24e..a51d86d 100644 --- a/src/modules/shared/BackButton/index.ts +++ b/src/modules/shared/BackButton/index.ts @@ -1 +1 @@ -export * from './BackButton'; +export * from './BackButton'; diff --git a/src/modules/shared/Breadcrumbs/Breadcrumbs.module.scss b/src/modules/shared/Breadcrumbs/Breadcrumbs.module.scss index 67ca29d..e226bd8 100644 --- a/src/modules/shared/Breadcrumbs/Breadcrumbs.module.scss +++ b/src/modules/shared/Breadcrumbs/Breadcrumbs.module.scss @@ -1,13 +1,34 @@ -@import '../../../styles/utils/mixins/mixin-media'; +@import '../../../styles/utils/variables/colors'; -.breadcrumbs { - grid-column: span 4; +.breadcrumbContainer { + display: flex; + gap: 8px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +} + +.link { + display: flex; + gap: 8px; + align-items: flex-end; +} + +.content { + color: $color__secondary; + font-size: 12px; + font-weight: 600; - @include onTablet { - grid-column: span 12; + &__prev { + color: $color__primary; } - @include onDesktop { - grid-column: span 24; + &__prev__DARK { + color: $color__dark-theme__white; + } + + &__DARK { + color: $color__dark-theme__secondary; } } + diff --git a/src/modules/shared/Breadcrumbs/Breadcrumbs.tsx b/src/modules/shared/Breadcrumbs/Breadcrumbs.tsx index 9d93f71..6420bea 100644 --- a/src/modules/shared/Breadcrumbs/Breadcrumbs.tsx +++ b/src/modules/shared/Breadcrumbs/Breadcrumbs.tsx @@ -1,7 +1,61 @@ import React from 'react'; +import cn from 'classnames'; +import { Link, useLocation } from 'react-router-dom'; +import { useAppSelector } from '../../../store/hooks'; +import { ReactComponent as HomeIcon } + from '../../../static/icons/home_icon.svg'; +import { ReactComponent as ArrowRightIcon } + from '../../../static/icons/arrow-right_icon.svg'; import styles from './Breadcrumbs.module.scss'; export const Breadcrumbs: React.FC = () => { - return
Breadcrumbs
; + const location = useLocation(); + const { pathname } = location; + const segments = pathname.split('/'); + const currentPage = (segments[segments.length - 1]); + const { isDarkTheme } = useAppSelector((state) => state.theme); + const { origin } = window.location; + + let url = ''; + const breadcrumbLinks = segments.map((segment) => { + url += `${segment}`; + + return ( + + + {segment === '' + ? ( + + ) + : ( +
+ +

+ {segment.charAt(0).toUpperCase() + segment.slice(1)} +

+
+ )} + +
+ ); + }); + + return ( +
+ {breadcrumbLinks} +
+ ); }; diff --git a/src/modules/shared/Breadcrumbs/index.ts b/src/modules/shared/Breadcrumbs/index.ts index ce97754..1a699f6 100644 --- a/src/modules/shared/Breadcrumbs/index.ts +++ b/src/modules/shared/Breadcrumbs/index.ts @@ -1 +1 @@ -export * from './Breadcrumbs'; +export * from './Breadcrumbs'; diff --git a/src/modules/shared/BurgerMenu/index.ts b/src/modules/shared/BurgerMenu/index.ts index cc518a4..0a9f211 100644 --- a/src/modules/shared/BurgerMenu/index.ts +++ b/src/modules/shared/BurgerMenu/index.ts @@ -1 +1 @@ -export * from './BurgerMenu'; +export * from './BurgerMenu'; diff --git a/src/modules/shared/EmptyCart/EmptyCart.module.scss b/src/modules/shared/EmptyCart/EmptyCart.module.scss new file mode 100644 index 0000000..a3d940e --- /dev/null +++ b/src/modules/shared/EmptyCart/EmptyCart.module.scss @@ -0,0 +1,130 @@ +@import '../../../styles/utils/mixins/mixin-media'; +@import '../../../styles/utils/mixins/mixin-typography'; +@import '../../../styles/utils/variables/colors'; + +.container { + height: 420px; + overflow-y: hidden; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + + @include onMobile { + gap: 5px; + } +} + +.cart_icon { + height: 170px; + + &_dark { + height: 170px; + filter: invert(89%); + + @include onMobile { + height: 90px; + } + } + + @include onMobile { + height: 90px; + } +} + +.headline { + @include h2-typography; + text-align: center; + + &_dark { + @include h2-typography; + color: $color__dark-theme__white; + + @include onMobile { + font-size: 24px; + } + } + + @include onMobile { + font-size: 24px; + } +} + + +.subtitle { + text-align: center; + color: $color__secondary; + @include bodyText-typography; + + &_dark { + @include bodyText-typography; + color: $color__dark-theme__white; + } +} + +.button { + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + min-width: 130px; + + border-radius: 8px; + border: none; + background: $color__accent; + margin-top: 12px; + + color: $color__white; + @include buttons-typography; + text-decoration: none; + + transition: scale 0.05s ease; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); + scale: 1.1; + } + + &_dark { + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + min-width: 130px; + + border-radius: 0px; + border: none; + background: $color__dark-theme__accent; + margin-top: 12px; + + color: $color__dark-theme__white; + @include buttons-typography; + text-decoration: none; + + transition: scale 0.05s ease; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); + scale: 1.1; + background: #a378ff; + } + + @include onMobile { + scale: 0.8; + &:hover { + scale: 0.9; + } + margin-top: 5px; + } + } + + @include onMobile { + scale: 0.8; + &:hover { + scale: 0.9; + } + margin-top: 5px; + } +} + diff --git a/src/modules/shared/EmptyCart/EmptyCart.tsx b/src/modules/shared/EmptyCart/EmptyCart.tsx new file mode 100644 index 0000000..2f27b15 --- /dev/null +++ b/src/modules/shared/EmptyCart/EmptyCart.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import cn from 'classnames'; +import { Link } from 'react-router-dom'; +import styles from './EmptyCart.module.scss'; +import { useAppSelector } from '../../../store/hooks'; +import cart_icon from '../../../static/icons/cart_icon.svg'; + +export const EmptyCart: React.FC = () => { + const { isDarkTheme } = useAppSelector(state => state.theme); + + return ( +
+ cart_icon +

+ Your cart is empty +

+

+ You can add an item to your cart by clicking Add to cart button +

+ + Explore the catalog + +
+ ); +}; diff --git a/src/modules/shared/EmptyCart/index.ts b/src/modules/shared/EmptyCart/index.ts new file mode 100644 index 0000000..1ba3911 --- /dev/null +++ b/src/modules/shared/EmptyCart/index.ts @@ -0,0 +1 @@ +export * from './EmptyCart'; diff --git a/src/modules/shared/EmptyFavourites/EmptyFavourites.module.scss b/src/modules/shared/EmptyFavourites/EmptyFavourites.module.scss new file mode 100644 index 0000000..0a34a0e --- /dev/null +++ b/src/modules/shared/EmptyFavourites/EmptyFavourites.module.scss @@ -0,0 +1,130 @@ +@import '../../../styles/utils/mixins/mixin-media'; +@import '../../../styles/utils/mixins/mixin-typography'; +@import '../../../styles/utils/variables/colors'; + +.container { + height: 420px; + overflow-y: hidden; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 10px; + + @include onMobile { + gap: 5px; + } +} + +.fav_icon { + height: 170px; + + &_dark { + height: 170px; + filter: invert(89%); + + @include onMobile { + height: 90px; + } + } + + @include onMobile { + height: 90px; + } +} + +.headline { + @include h2-typography; + text-align: center; + + &_dark { + @include h2-typography; + color: $color__dark-theme__white; + + @include onMobile { + font-size: 24px; + } + } + + @include onMobile { + font-size: 24px; + } +} + + +.subtitle { + text-align: center; + color: $color__secondary; + @include bodyText-typography; + + &_dark { + @include bodyText-typography; + color: $color__dark-theme__white; + } +} + +.button { + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + min-width: 130px; + + border-radius: 8px; + border: none; + background: $color__accent; + margin-top: 12px; + + color: $color__white; + @include buttons-typography; + text-decoration: none; + + transition: scale 0.05s ease; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); + scale: 1.1; + } + + &_dark { + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + min-width: 130px; + + border-radius: 0px; + border: none; + background: $color__dark-theme__accent; + margin-top: 12px; + + color: $color__dark-theme__white; + @include buttons-typography; + text-decoration: none; + + transition: scale 0.05s ease; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); + scale: 1.1; + background: #a378ff; + } + + @include onMobile { + scale: 0.8; + &:hover { + scale: 0.9; + } + margin-top: 5px; + } + } + + @include onMobile { + scale: 0.8; + &:hover { + scale: 0.9; + } + margin-top: 5px; + } +} + diff --git a/src/modules/shared/EmptyFavourites/EmptyFavourites.tsx b/src/modules/shared/EmptyFavourites/EmptyFavourites.tsx new file mode 100644 index 0000000..1143de1 --- /dev/null +++ b/src/modules/shared/EmptyFavourites/EmptyFavourites.tsx @@ -0,0 +1,42 @@ +import React from 'react'; +import cn from 'classnames'; +import { Link } from 'react-router-dom'; +import styles from './EmptyFavourites.module.scss'; +import { useAppSelector } from '../../../store/hooks'; +import fav_icon from '../../../static/icons/favourites_icon.svg'; + +export const EmptyFavourites: React.FC = () => { + const { isDarkTheme } = useAppSelector(state => state.theme); + + return ( +
+ favourites_icon +

+ No favourites yet! +

+

+ You can add an item to your favourites by clicking heart icon +

+ + Bring me back + +
+ ); +}; diff --git a/src/modules/shared/EmptyFavourites/index.ts b/src/modules/shared/EmptyFavourites/index.ts new file mode 100644 index 0000000..0bbd6b4 --- /dev/null +++ b/src/modules/shared/EmptyFavourites/index.ts @@ -0,0 +1 @@ +export * from './EmptyFavourites'; diff --git a/src/modules/shared/Filtration/Filtration.module.scss b/src/modules/shared/Filtration/Filtration.module.scss index e69de29..8d2aaa4 100644 --- a/src/modules/shared/Filtration/Filtration.module.scss +++ b/src/modules/shared/Filtration/Filtration.module.scss @@ -0,0 +1,110 @@ +@import '../../../styles/utils/variables/colors'; + +.filtration { + display: flex; + + &__block { + display: flex; + flex-direction: column; + + &:first-child { + margin-right: 16px; + } + } + + &__label { + color: $color__secondary; + font-size: 12px; + font-style: normal; + font-weight: 700; + line-height: normal; + } + + + &__dropdown { + display: flex; + position: relative; + justify-content: space-between; + align-items: center; + color: $color__primary; + font-size: 14px; + font-weight: 700; + line-height: 21px; + + width: 176px; + height: 40px; + flex-shrink: 0; + border-radius: 8px; + border: 1px solid $color__icons; + background-color: $color__white; + transition: border 0.3s ease; + + &:hover { + border: 1px solid $color__secondary; + } + + &__DARK { + background: $color__dark-theme__surface-2; + border-radius: 0px; + border: 1px solid $color__dark-theme__surface-2; + color: #F1F2F9; + + &:hover { + border: 1px solid $color__dark-theme__icons; + } + } + } + + &__list { + position: absolute; + width: 176px; + margin-top: 8px; + z-index: 1; + display: flex; + flex-direction: column; + + border-radius: 8px; + border: 1px solid $color__elements; + box-shadow: 0px 2px 15px 0px rgba(0, 0, 0, 0.05); + + &__DARK { + border-radius: 0px; + border: 1px solid $color__dark-theme__elements; + background-color: $color__dark-theme__black; + } + } + + &__option { + color: $color__secondary; + font-size: 14px; + font-weight: 500; + line-height: 21px; + background-color: $color__white; + margin-left: 1px; + margin-right: 1px; + transition: border 0.3s ease, background-color 0.3s ease; + + &:first-child { + margin-top: 8px; + } + + &:last-child { + margin-bottom: 8px; + } + + &:hover { + color: $color__primary; + background: $color__white; + } + + &__DARK { + color: $color__dark-theme__secondary; + background-color: $color__dark-theme__black; + + &:hover { + background-color: $color__dark-theme__surface-2; + color: $color__dark-theme__white; + } + } + } +} diff --git a/src/modules/shared/Filtration/Filtration.tsx b/src/modules/shared/Filtration/Filtration.tsx index 1947c30..e346d0a 100644 --- a/src/modules/shared/Filtration/Filtration.tsx +++ b/src/modules/shared/Filtration/Filtration.tsx @@ -1,7 +1,141 @@ -import React from 'react'; - -// import styles from './Filtration.module.scss'; - -export const Filtration: React.FC = () => { - return
Filtration
; -}; +import { useState } from 'react'; +import { Link, useSearchParams } from 'react-router-dom'; +import classNames from 'classnames'; +import { useAppSelector } from '../../../store/hooks'; +import styles from './Filtration.module.scss'; +import { SortBy } from '../../../types/SortBy'; +import { getSearchWith } from '../../../utils/getSearchWith'; +import { + ReactComponent as ArrowDown, +} from '../../../static/buttons/Icons_ArrowDown.svg'; + +const PER_PAGE = ['All', '4', '8', '16']; + +type Props = { + sort: SortBy; + perPage: string; +}; + +export const Filtration: React.FC = ({ sort, perPage }) => { + const { isDarkTheme } = useAppSelector((state) => state.theme); + const [isSortOpen, setIsSortOpen] = useState(false); + const [isPerPageOpen, setIsPerPageOpen] = useState(false); + const [searchParams] = useSearchParams(); + + const getPerPageParams = (newPerPage: string) => { + if (perPage === 'All') { + const search = getSearchWith(searchParams, { perPage: 'all' }); + + return search; + } + + const search = getSearchWith(searchParams, { perPage: newPerPage }); + + return search; + }; + + return ( +
+
+ +
+ + + {isSortOpen && ( +
+ {Object.entries(SortBy).map(([key, value]) => ( + + {key} + + ))} +
+ )} +
+
+ +
+ + +
+ + + {isPerPageOpen && ( +
+ {PER_PAGE.map(amount => ( + + {amount} + + ))} +
+ )} +
+
+
+ ); +}; diff --git a/src/modules/shared/Footer/Footer.module.scss b/src/modules/shared/Footer/Footer.module.scss index d7e213e..f9455c7 100644 --- a/src/modules/shared/Footer/Footer.module.scss +++ b/src/modules/shared/Footer/Footer.module.scss @@ -1,133 +1,133 @@ -@import '../../../../src/styles/utils/variables/colors'; -@import '../../../styles/utils/mixins/mixin-media'; -@import '../../../styles/utils/mixins/mixin-typography'; - -.footer { - display: flex; - justify-content: center; - padding: 32px 0; - width: 100%; - background: $color__white; - box-shadow: 0px -1px 0px 0px $color__elements; - - &__dark { - display: flex; - justify-content: center; - padding: 32px 0; - width: 100%; - box-shadow: 0px -1px 0px 0px $color__dark-theme__elements; - background: $color__dark-theme__black; - - @include onMobile { - justify-content: flex-start; - flex-direction: column; - } - } - - @include onMobile { - justify-content: flex-start; - flex-direction: column; - } -} - -.container { - display: flex; - - @include onMobile { - width: auto; - margin: 0 16px; - gap: 32px; - flex-direction: column; - align-items: flex-start; - } - - @include onTablet { - display: flex; - width: 100%; - justify-content: space-between; - margin: 0 32px; - align-items: center; - height: 32px; - } - - @include onDesktop { - max-width: 1200px; - } -} - -.return_button, -.return_button__dark { - display: flex; - align-items: center; - gap: 16px; - - @include onMobile { - align-self: center; - justify-content: center; - } -} - -.nav_center { - display: flex; - width: 33%; - justify-content: space-between; - - @include onMobile { - flex-direction: column; - gap: 16px; - } -} - -.nav_text { - text-decoration: none; - display: flex; - color: $color__secondary; - font-family: 'Mont', sans-serif; - justify-content: space-between; - @include upperCase-typography; - padding: 18px 12px; - - &__dark { - text-decoration: none; - display: flex; - color: $color__dark-theme__white; - font-family: 'Mont', sans-serif; - justify-content: space-between; - @include upperCase-typography; - padding: 18px 12px; - - @include onMobile { - padding: 0; - } - } - - @include onMobile { - padding: 0; - } -} - -.nav_logo { - height: 32px; - display: flex; - justify-content: flex-start; - - &__dark { - height: 32px; - display: flex; - justify-content: flex-start; - } -} - -.button_top { - display: flex; - color: $color__secondary; - font-family: 'Mont', sans-serif; - text-decoration: none; - @include smallText-typography; -} - -.button_logo { - height: 32px; - width: 32px; - display: flex; -} +@import '../../../../src/styles/utils/variables/colors'; +@import '../../../styles/utils/mixins/mixin-media'; +@import '../../../styles/utils/mixins/mixin-typography'; + +.footer { + display: flex; + justify-content: center; + padding: 32px 0; + width: 100%; + background: $color__white; + box-shadow: 0px -1px 0px 0px $color__elements; + + &__dark { + display: flex; + justify-content: center; + padding: 32px 0; + width: 100%; + box-shadow: 0px -1px 0px 0px $color__dark-theme__elements; + background: $color__dark-theme__black; + + @include onMobile { + justify-content: flex-start; + flex-direction: column; + } + } + + @include onMobile { + justify-content: flex-start; + flex-direction: column; + } +} + +.container { + display: flex; + + @include onMobile { + width: auto; + margin: 0 16px; + gap: 32px; + flex-direction: column; + align-items: flex-start; + } + + @include onTablet { + display: flex; + width: 100%; + justify-content: space-between; + margin: 0 32px; + align-items: center; + height: 32px; + } + + @include onDesktop { + max-width: 1200px; + } +} + +.return_button, +.return_button__dark { + display: flex; + align-items: center; + gap: 16px; + + @include onMobile { + align-self: center; + justify-content: center; + } +} + +.nav_center { + display: flex; + width: 33%; + justify-content: space-between; + + @include onMobile { + flex-direction: column; + gap: 16px; + } +} + +.nav_text { + text-decoration: none; + display: flex; + color: $color__secondary; + font-family: 'Mont', sans-serif; + justify-content: space-between; + @include upperCase-typography; + padding: 18px 12px; + + &__dark { + text-decoration: none; + display: flex; + color: $color__dark-theme__white; + font-family: 'Mont', sans-serif; + justify-content: space-between; + @include upperCase-typography; + padding: 18px 12px; + + @include onMobile { + padding: 0; + } + } + + @include onMobile { + padding: 0; + } +} + +.nav_logo { + height: 32px; + display: flex; + justify-content: flex-start; + + &__dark { + height: 32px; + display: flex; + justify-content: flex-start; + } +} + +.button_top { + display: flex; + color: $color__secondary; + font-family: 'Mont', sans-serif; + text-decoration: none; + @include smallText-typography; +} + +.button_logo { + height: 32px; + width: 32px; + display: flex; +} diff --git a/src/modules/shared/Footer/index.ts b/src/modules/shared/Footer/index.ts index ddcc5a9..df19f26 100644 --- a/src/modules/shared/Footer/index.ts +++ b/src/modules/shared/Footer/index.ts @@ -1 +1 @@ -export * from './Footer'; +export * from './Footer'; diff --git a/src/modules/shared/Header/index.ts b/src/modules/shared/Header/index.ts index 266dec8..8cc2fe3 100644 --- a/src/modules/shared/Header/index.ts +++ b/src/modules/shared/Header/index.ts @@ -1 +1 @@ -export * from './Header'; +export * from './Header'; diff --git a/src/modules/shared/Images/Icons/Favourites-icon_Default.svg b/src/modules/shared/Images/Icons/Favourites-icon_Default.svg new file mode 100644 index 0000000..7c61ff6 --- /dev/null +++ b/src/modules/shared/Images/Icons/Favourites-icon_Default.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/modules/shared/Images/Icons/Favourites-icon_Selected.svg b/src/modules/shared/Images/Icons/Favourites-icon_Selected.svg new file mode 100644 index 0000000..c236532 --- /dev/null +++ b/src/modules/shared/Images/Icons/Favourites-icon_Selected.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/modules/shared/Images/Icons/Favourites-icon_darkDefault.svg b/src/modules/shared/Images/Icons/Favourites-icon_darkDefault.svg new file mode 100644 index 0000000..465d16b --- /dev/null +++ b/src/modules/shared/Images/Icons/Favourites-icon_darkDefault.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/modules/shared/Images/Icons/Favourites-icon_darkSelected.svg b/src/modules/shared/Images/Icons/Favourites-icon_darkSelected.svg new file mode 100644 index 0000000..5721b4e --- /dev/null +++ b/src/modules/shared/Images/Icons/Favourites-icon_darkSelected.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/modules/shared/Loader/Loader.module.scss b/src/modules/shared/Loader/Loader.module.scss index 232a8f5..b8b4381 100644 --- a/src/modules/shared/Loader/Loader.module.scss +++ b/src/modules/shared/Loader/Loader.module.scss @@ -1,6 +1,6 @@ @import '../../../styles/utils/variables/colors'; -.Loader { +.loader { display: flex; width: 100%; justify-content: center; @@ -11,9 +11,14 @@ width: 4em; height: 4em; margin: 7em auto; - border: 0.5em solid #ddd; + border: 0.5em solid $color__icons; border-left-color: $color__accent; animation: load8 1.2s infinite linear; + + &__DARK { + border-color: $color__dark-theme__icons; + border-left-color: $color__dark-theme__accent; + } } } diff --git a/src/modules/shared/Loader/Loader.tsx b/src/modules/shared/Loader/Loader.tsx index ac7609d..605a8c3 100644 --- a/src/modules/shared/Loader/Loader.tsx +++ b/src/modules/shared/Loader/Loader.tsx @@ -1,9 +1,18 @@ import React from 'react'; +import cn from 'classnames'; import styles from './Loader.module.scss'; +import { useAppSelector } from '../../../store/hooks'; -export const Loader: React.FC = () => ( +export const Loader: React.FC = () => { + const { isDarkTheme } = useAppSelector((state) => state.theme); -
-
-
-); + return ( +
+
+
+ ); +}; diff --git a/src/modules/shared/Loader/index.tsx b/src/modules/shared/Loader/index.tsx index d5ce981..8f8364e 100644 --- a/src/modules/shared/Loader/index.tsx +++ b/src/modules/shared/Loader/index.tsx @@ -1 +1 @@ -export * from './Loader'; +export * from './Loader'; diff --git a/src/modules/shared/Pagination/Pagination.module.scss b/src/modules/shared/Pagination/Pagination.module.scss index e69de29..a05d2fd 100644 --- a/src/modules/shared/Pagination/Pagination.module.scss +++ b/src/modules/shared/Pagination/Pagination.module.scss @@ -0,0 +1,101 @@ +@import '../../../styles/utils/variables/colors'; + +.pagination { + display: flex; + align-items: center; + flex-direction: row; + justify-content: center; + + &__numbers { + margin-left: 16px; + margin-right: 16px; + display: flex; + flex-direction: row; + } + + &__item { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + color: #000; + border-radius: 48px; + border: 1px solid $color__elements; + transition: border 0.3s ease, background-color 0.3s ease; + + &:hover { + border: 1px solid $color__primary; + } + + &__active { + background-color: $color__primary; + } + + &__DARK { + border-radius: 0px; + border: none; + background-color: $color__dark-theme__surface-1; + + &:hover { + background-color: $color__dark-theme__elements; + } + + &__active { + background-color: $color__dark-theme__accent; + } + } + } + + &__number { + color: #000; + + &__SELECTED { + color: $color__white; + } + + &__DARK { + color: $color__dark-theme__white; + + &__SELECTED { + color: $color__dark-theme__white; + } + } + } + + &__arrow { + display: flex; + align-items: center; + justify-content: center; + width: 32px; + height: 32px; + border-radius: 48px; + border: 1px solid $color__elements; + transition: border 0.3s ease, background-color 0.3s ease; + border: 1px solid $color__icons; + background-repeat: no-repeat; + + &:hover { + border: 1px solid $color__primary; + } + + &__DISABLED { + border: 1px solid $color__elements; + } + + &__DARK { + border-radius: 0px; + background-color: $color__dark-theme__surface-2; + border: none; + + &:hover { + background-color: $color__dark-theme__icons; + } + + &__DISABLED { + border: 1px solid $color__dark-theme__elements; + background-color: none; + } + } + } +} \ No newline at end of file diff --git a/src/modules/shared/Pagination/Pagination.tsx b/src/modules/shared/Pagination/Pagination.tsx index 9fec297..07c87f2 100644 --- a/src/modules/shared/Pagination/Pagination.tsx +++ b/src/modules/shared/Pagination/Pagination.tsx @@ -1,7 +1,126 @@ -import React from 'react'; - -// import styles from './Pagination.module.scss'; - -export const Pagination: React.FC = () => { - return
Pagination
; -}; +import cn from 'classnames'; +import { Link, useSearchParams } from 'react-router-dom'; +import styles from './Pagination.module.scss'; +import { useAppSelector } from '../../../store/hooks'; +import { getPages } from '../../../utils/getPages'; +import { getSearchWith } from '../../../utils/getSearchWith'; + +import { + ReactComponent as NextIcon, +} from '../../../static/buttons/Button_DefaultArrowLeft.svg'; + +import { + ReactComponent as PrevIcon, +} from '../../../static/buttons/Button_DefaultArrowRight.svg'; + +type Props = { + totalPages: number; + currentPage: number; +}; + +export const Pagination: React.FC = ({ + totalPages, + currentPage, +}) => { + const { isDarkTheme } = useAppSelector((state) => state.theme); + const [searchParams] = useSearchParams(); + const isFirstPage = currentPage === 1; + const isLastPage = currentPage === totalPages; + + const pages = getPages(totalPages, currentPage); + + const findColorForArrow = (isDisabled: boolean) => { + if (isDarkTheme && isDisabled) { + return '#4A4D58'; + } + + if (isDarkTheme) { + return '#F1F2F9'; + } + + if (isDisabled) { + return '#B4BDC3'; + } + + return '#0F0F11'; + }; + + return ( +
    +
  • + + + +
  • + +
    + {pages.map(page => ( +
  • + + {page} + +
  • + ))} +
    + +
  • + + + +
  • +
+ ); +}; diff --git a/src/modules/shared/Pagination/index.ts b/src/modules/shared/Pagination/index.ts index e016c96..7f74af1 100644 --- a/src/modules/shared/Pagination/index.ts +++ b/src/modules/shared/Pagination/index.ts @@ -1 +1 @@ -export * from './Pagination'; +export * from './Pagination'; diff --git a/src/modules/shared/PhoneCard/PhoneCard.tsx b/src/modules/shared/PhoneCard/PhoneCard.tsx new file mode 100644 index 0000000..1cb2bbb --- /dev/null +++ b/src/modules/shared/PhoneCard/PhoneCard.tsx @@ -0,0 +1,123 @@ +import cn from 'classnames'; +import style from './PhoneCard.module.scss'; +import { useAppSelector } from '../../../store/hooks'; +import { Phone } from '../../../types/Phone'; + +type Props = { + phone: Phone; +}; + +export const PhoneCard: React.FC = ({ phone }) => { + const { + name, + fullPrice, + price, + screen, + capacity, + ram, + image, + } = phone; + + const { isDarkTheme } = useAppSelector((state) => state.theme); + const isItemSelected = false; + + const characteristicsArray = [ + { label: 'Screen', state: screen }, + { label: 'Capacity', state: capacity }, + { label: 'RAM', state: ram }, + ]; + + return ( +
+
+ {name} +
+ +

+ {name} +

+ +
+

+ {`$${price}`} +

+ + {price !== fullPrice && ( +

+ {`$${fullPrice}`} +

+ )} +
+ +
+ {characteristicsArray.map((characteristic) => ( +
+

+ {characteristic.label} +

+ +

+ {characteristic.state} +

+
+ ))} +
+ +
+ + +
+
+ ); +}; diff --git a/src/modules/shared/ProductCard/ProductCard.module.scss b/src/modules/shared/ProductCard/ProductCard.module.scss index 6c6b195..5fee706 100644 --- a/src/modules/shared/ProductCard/ProductCard.module.scss +++ b/src/modules/shared/ProductCard/ProductCard.module.scss @@ -1,179 +1,179 @@ -@import '../../../styles/utils/variables/colors'; -@import '../../../styles/utils/typography'; -@import '../../../styles/utils/mixins/mixin-media'; - -.productCard { - display: flex; - width: 287px; - padding: 32px; - flex-direction: column; - align-items: flex-start; - flex-shrink: 0; - flex-wrap: wrap; - align-content: flex-start; - box-sizing: border-box; - // margin: 0 auto; - grid-column: span 4; - - border-radius: 8px; - border: 1px solid $color__elements; - - @include onTablet { - grid-column: span 6; - width: 288px; - } - - @media (min-width: 768px) { - grid-column: span 4; - } - - @include onDesktop { - grid-column: span 6; - width: 272px; - - } - - transition: transform 0.3s ease, box-shadow 0.3s ease; - - - &:hover { - overflow: hidden; - transform: scale(1.01); - box-shadow: 0px 3px 13px 0px $color__secondary; - } - - &__DARK { - background: #161827; - border-radius: 0px; - border: none; - } - - &__image__container { - display: flex; - width: 100%; - height: 196px; - justify-content: center; - } - - &__image { - display: block; - height: 196px; - } - - &__name { - height: 58px; - margin-top: 8px; - padding-top: 16px; - color: $color__primary; - - font-size: 14px; - font-weight: 600; - line-height: 21px; - - &__DARK { - color: $color__dark-theme__white; - } - } - - &__price { - display: flex; - margin-top: 8px; - position: relative; - padding-bottom: 8px; - - &::after { - content: ''; - position: absolute; - bottom: 0; - display: block; - width: 208px; - height: 1px; - background: $color__elements; - } - - &__DARK { - &::after { - background: $color__dark-theme__elements; - } - } - } - - &__realPrice { - color: $color__primary; - - font-size: 22px; - font-weight: 800; - line-height: 140%; - - &__DARK { - color: $color__dark-theme__white; - } - } - - &__fullPrice { - margin-left: 8px; - color: $color__secondary; - - font-size: 22px; - font-weight: 500; - line-height: normal; - text-decoration-line: line-through; - - &__DARK { - color: #75767f; - } - } - - &__characteristics { - display: flex; - padding: 8px 0px; - flex-direction: column; - align-items: center; - gap: 8px; - align-self: stretch; - - &__item { - display: flex; - justify-content: space-between; - align-items: center; - align-self: stretch; - } - - &__label { - display: block; - color: $color__secondary; - - font-size: 12px; - font-weight: 600; - line-height: normal; - - &__DARK { - color: #75767f; - } - } - - &__state { - display: block; - color: $color__primary; - - text-align: right; - font-size: 12px; - font-weight: 700; - line-height: normal; - - &__DARK { - color: $color__dark-theme__white; - } - } - } - - &__buttons { - display: flex; - margin-top: 8px; - justify-content: space-between; - align-items: center; - align-self: stretch; - padding-bottom: 0px; - margin-bottom: 0px; - } -} +@import '../../../styles/utils/variables/colors'; +@import '../../../styles/utils/typography'; +@import '../../../styles/utils/mixins/mixin-media'; + +.productCard { + display: flex; + width: 287px; + padding: 32px; + flex-direction: column; + align-items: flex-start; + flex-shrink: 0; + flex-wrap: wrap; + align-content: flex-start; + box-sizing: border-box; + // margin: 0 auto; + grid-column: span 4; + + border-radius: 8px; + border: 1px solid $color__elements; + + @include onTablet { + grid-column: span 6; + width: 288px; + } + + @media (min-width: 768px) { + grid-column: span 4; + } + + @include onDesktop { + grid-column: span 6; + width: 272px; + + } + + transition: transform 0.3s ease, box-shadow 0.3s ease; + + + &:hover { + overflow: hidden; + transform: scale(1.01); + box-shadow: 0px 3px 13px 0px $color__secondary; + } + + &__DARK { + background: #161827; + border-radius: 0px; + border: none; + } + + &__image__container { + display: flex; + width: 100%; + height: 196px; + justify-content: center; + } + + &__image { + display: block; + height: 196px; + } + + &__name { + height: 58px; + margin-top: 8px; + padding-top: 16px; + color: $color__primary; + + font-size: 14px; + font-weight: 600; + line-height: 21px; + + &__DARK { + color: $color__dark-theme__white; + } + } + + &__price { + display: flex; + margin-top: 8px; + position: relative; + padding-bottom: 8px; + + &::after { + content: ''; + position: absolute; + bottom: 0; + display: block; + width: 208px; + height: 1px; + background: $color__elements; + } + + &__DARK { + &::after { + background: $color__dark-theme__elements; + } + } + } + + &__realPrice { + color: $color__primary; + + font-size: 22px; + font-weight: 800; + line-height: 140%; + + &__DARK { + color: $color__dark-theme__white; + } + } + + &__fullPrice { + margin-left: 8px; + color: $color__secondary; + + font-size: 22px; + font-weight: 500; + line-height: normal; + text-decoration-line: line-through; + + &__DARK { + color: #75767f; + } + } + + &__characteristics { + display: flex; + padding: 8px 0px; + flex-direction: column; + align-items: center; + gap: 8px; + align-self: stretch; + + &__item { + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; + } + + &__label { + display: block; + color: $color__secondary; + + font-size: 12px; + font-weight: 600; + line-height: normal; + + &__DARK { + color: #75767f; + } + } + + &__state { + display: block; + color: $color__primary; + + text-align: right; + font-size: 12px; + font-weight: 700; + line-height: normal; + + &__DARK { + color: $color__dark-theme__white; + } + } + } + + &__buttons { + display: flex; + margin-top: 8px; + justify-content: space-between; + align-items: center; + align-self: stretch; + padding-bottom: 0px; + margin-bottom: 0px; + } +} diff --git a/src/modules/shared/ProductList/ProductList.module.scss b/src/modules/shared/ProductList/ProductList.module.scss index 99233f9..6667e2b 100644 --- a/src/modules/shared/ProductList/ProductList.module.scss +++ b/src/modules/shared/ProductList/ProductList.module.scss @@ -1,7 +1,7 @@ -@import '../../../styles/blocks/grid'; - - .gridContainer { - @extend .grid; - grid-row-gap: 40px; - } - +@import '../../../styles/blocks/grid'; + + .gridContainer { + @extend .grid; + grid-row-gap: 40px; + } + diff --git a/src/modules/shared/ProductList/index.ts b/src/modules/shared/ProductList/index.ts index c71910a..6c99401 100644 --- a/src/modules/shared/ProductList/index.ts +++ b/src/modules/shared/ProductList/index.ts @@ -1 +1 @@ -export * from './ProductList'; +export * from './ProductList'; diff --git a/src/modules/shared/ProductSlider/ProductSlider.module.scss b/src/modules/shared/ProductSlider/ProductSlider.module.scss index e0f9e26..3626ef9 100644 --- a/src/modules/shared/ProductSlider/ProductSlider.module.scss +++ b/src/modules/shared/ProductSlider/ProductSlider.module.scss @@ -1,72 +1,72 @@ -@import '../../../styles/blocks/_grid'; -@import '../../../styles/utils/mixins/mixin-typography'; - -.header { - @include h2-typography; - - @include onMobile { - width: 215px; - } - - &__DARK { - @include typography-dark; - } -} - -.slick-slide-card, -.slick-clone-card, -.slide-card { - width: 272px !important; -} - -.slick-next-small, -.slick-prev-small { - position: absolute; - height: 32px; - width: 32px; - cursor: pointer; - border: 1px solid $color__secondary; - border-radius: 48px; - background-color: $color__white; - top: -8.6% ; - -webkit-transform: translate(0, -50%); - -ms-transform: translate(0, -50%); - transform: translate(0, -50%); - padding: 0; - - @include onMobile { - top:-7.7%; - } -} - -.slick-prev-small { - right: 48px; -} - -.slick-next-small { - right: 0px; -} - -.icon { - display: block; - margin: auto auto; -} - -.slick-list-card { - height: 505px; - width: 100%; -} - -.slider-card { - margin-top: 24px; - position: relative; - width: 100%; - height: 100%; - border: none; - box-sizing: border-box; -} - -.slick-dots-product { - display: none; - visibility: hidden; -} +@import '../../../styles/blocks/_grid'; +@import '../../../styles/utils/mixins/mixin-typography'; + +.header { + @include h2-typography; + + @include onMobile { + width: 215px; + } + + &__DARK { + @include typography-dark; + } +} + +.slick-slide-card, +.slick-clone-card, +.slide-card { + width: 272px !important; +} + +.slick-next-small, +.slick-prev-small { + position: absolute; + height: 32px; + width: 32px; + cursor: pointer; + border: 1px solid $color__secondary; + border-radius: 48px; + background-color: $color__white; + top: -8.6% ; + -webkit-transform: translate(0, -50%); + -ms-transform: translate(0, -50%); + transform: translate(0, -50%); + padding: 0; + + @include onMobile { + top:-7.7%; + } +} + +.slick-prev-small { + right: 48px; +} + +.slick-next-small { + right: 0px; +} + +.icon { + display: block; + margin: auto auto; +} + +.slick-list-card { + height: 505px; + width: 100%; +} + +.slider-card { + margin-top: 24px; + position: relative; + width: 100%; + height: 100%; + border: none; + box-sizing: border-box; +} + +.slick-dots-product { + display: none; + visibility: hidden; +} diff --git a/src/modules/shared/ProductSlider/SampleNextArrow.tsx b/src/modules/shared/ProductSlider/SampleNextArrow.tsx index 27ff0c3..beb8682 100644 --- a/src/modules/shared/ProductSlider/SampleNextArrow.tsx +++ b/src/modules/shared/ProductSlider/SampleNextArrow.tsx @@ -1,61 +1,61 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useState } from 'react'; -import { - ReactComponent as NextIcon, -} from '../../../static/icons/arrow-right_icon.svg'; -import styles from './ProductSlider.module.scss'; -import { - chooseArrowColor, chooseBackgroundColor, chooseIconColor, -} from './colorHandlers'; -import { useAppSelector } from '../../../store/hooks'; - -type Props = { - handleClick: () => void; - style?: Record - currentSlide: number; - maxSlides: number; -}; - -export const SampleNextArrow: React.FC = (props: any) => { - const [isHover, setIsHover] = useState(false); - - const { - style, onClick, handleClick, currentSlide, maxSlides, - } = props; - const { isDarkTheme } = useAppSelector((state) => state.theme); - // const isDarkTheme = true; - const isNotDisabled = currentSlide < maxSlides; - - return ( - <> - - - ); -}; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React, { useState } from 'react'; +import { + ReactComponent as NextIcon, +} from '../../../static/icons/arrow-right_icon.svg'; +import styles from './ProductSlider.module.scss'; +import { + chooseArrowColor, chooseBackgroundColor, chooseIconColor, +} from './colorHandlers'; +import { useAppSelector } from '../../../store/hooks'; + +type Props = { + handleClick: () => void; + style?: Record + currentSlide: number; + maxSlides: number; +}; + +export const SampleNextArrow: React.FC = (props: any) => { + const [isHover, setIsHover] = useState(false); + + const { + style, onClick, handleClick, currentSlide, maxSlides, + } = props; + const { isDarkTheme } = useAppSelector((state) => state.theme); + // const isDarkTheme = true; + const isNotDisabled = currentSlide < maxSlides; + + return ( + <> + + + ); +}; diff --git a/src/modules/shared/ProductSlider/SamplePrevArrow.tsx b/src/modules/shared/ProductSlider/SamplePrevArrow.tsx index 177a356..58932b1 100644 --- a/src/modules/shared/ProductSlider/SamplePrevArrow.tsx +++ b/src/modules/shared/ProductSlider/SamplePrevArrow.tsx @@ -1,58 +1,58 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import React, { useState } from 'react'; -import { - ReactComponent as PrevIcon, -} from '../../../static/icons/arrow-left_icon.svg'; -import styles from './ProductSlider.module.scss'; -import { - chooseArrowColor, chooseBackgroundColor, chooseIconColor, -} from './colorHandlers'; -import { useAppSelector } from '../../../store/hooks'; - -type Props = { - handleClick: () => void; - currentSlide: number; -}; - -export const SamplePrevArrow: React.FC = (props: any) => { - const [isHover, setIsHover] = useState(false); - - const { - style, onClick, handleClick, currentSlide, - } = props; - const { isDarkTheme } = useAppSelector((state) => state.theme); - // const isDarkTheme = true; - const isNotDisabled = currentSlide > 0; - - return ( - <> - - - ); -}; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import React, { useState } from 'react'; +import { + ReactComponent as PrevIcon, +} from '../../../static/icons/arrow-left_icon.svg'; +import styles from './ProductSlider.module.scss'; +import { + chooseArrowColor, chooseBackgroundColor, chooseIconColor, +} from './colorHandlers'; +import { useAppSelector } from '../../../store/hooks'; + +type Props = { + handleClick: () => void; + currentSlide: number; +}; + +export const SamplePrevArrow: React.FC = (props: any) => { + const [isHover, setIsHover] = useState(false); + + const { + style, onClick, handleClick, currentSlide, + } = props; + const { isDarkTheme } = useAppSelector((state) => state.theme); + // const isDarkTheme = true; + const isNotDisabled = currentSlide > 0; + + return ( + <> + + + ); +}; diff --git a/src/modules/shared/ProductSlider/colorHandlers.ts b/src/modules/shared/ProductSlider/colorHandlers.ts index 29d43df..1e66791 100644 --- a/src/modules/shared/ProductSlider/colorHandlers.ts +++ b/src/modules/shared/ProductSlider/colorHandlers.ts @@ -1,100 +1,100 @@ -export const chooseIconColor = ( - isDarkTheme: boolean, isNotDisabled:boolean, -) => { - let iconColor; - - switch (true) { - case isDarkTheme && !isNotDisabled: - iconColor = '#3b3e4a'; - break; - - case isDarkTheme: - iconColor = '#f1f2f9'; - break; - - case !isNotDisabled: - iconColor = '#b4bdc3'; - break; - - default: - iconColor = '#0f0f11'; - } - - return iconColor; -}; - -export const chooseArrowColor = ( - isHover: boolean, - isNotDisabled:boolean, - isDarkTheme: boolean, -) => { - let arrowBorder; - - switch (true) { - case isDarkTheme && !isNotDisabled && isHover: - arrowBorder = '#3b3e4a'; - break; - - case isDarkTheme && !isNotDisabled && !isHover: - arrowBorder = '#3b3e4a'; - break; - - case isDarkTheme && isNotDisabled && isHover: - arrowBorder = '#4a4d58'; - break; - - case isDarkTheme && isNotDisabled && !isHover: - arrowBorder = '#323542'; - break; - - case !isNotDisabled: - arrowBorder = '#e2e6e9'; - break; - - case !isNotDisabled && isHover: - arrowBorder = '#e2e6e9'; - break; - - case isHover: - arrowBorder = '#0f0f11'; - break; - - default: - arrowBorder = '#b4bdc3'; - break; - } - - return arrowBorder; -}; - -export const chooseBackgroundColor = ( - isHover: boolean, - isNotDisabled:boolean, - isDarkTheme: boolean, -) => { - let backgroundColor; - - switch (true) { - case !isNotDisabled && isDarkTheme && isHover: - backgroundColor = '#0f1121'; - break; - - case !isNotDisabled && isDarkTheme && !isHover: - backgroundColor = '#0f1121'; - break; - - case isNotDisabled && isDarkTheme && isHover: - backgroundColor = '#4a4d58'; - break; - - case isNotDisabled && isDarkTheme && !isHover: - backgroundColor = '#323542'; - break; - - default: - backgroundColor = '#fff'; - break; - } - - return backgroundColor; -}; +export const chooseIconColor = ( + isDarkTheme: boolean, isNotDisabled:boolean, +) => { + let iconColor; + + switch (true) { + case isDarkTheme && !isNotDisabled: + iconColor = '#3b3e4a'; + break; + + case isDarkTheme: + iconColor = '#f1f2f9'; + break; + + case !isNotDisabled: + iconColor = '#b4bdc3'; + break; + + default: + iconColor = '#0f0f11'; + } + + return iconColor; +}; + +export const chooseArrowColor = ( + isHover: boolean, + isNotDisabled:boolean, + isDarkTheme: boolean, +) => { + let arrowBorder; + + switch (true) { + case isDarkTheme && !isNotDisabled && isHover: + arrowBorder = '#3b3e4a'; + break; + + case isDarkTheme && !isNotDisabled && !isHover: + arrowBorder = '#3b3e4a'; + break; + + case isDarkTheme && isNotDisabled && isHover: + arrowBorder = '#4a4d58'; + break; + + case isDarkTheme && isNotDisabled && !isHover: + arrowBorder = '#323542'; + break; + + case !isNotDisabled: + arrowBorder = '#e2e6e9'; + break; + + case !isNotDisabled && isHover: + arrowBorder = '#e2e6e9'; + break; + + case isHover: + arrowBorder = '#0f0f11'; + break; + + default: + arrowBorder = '#b4bdc3'; + break; + } + + return arrowBorder; +}; + +export const chooseBackgroundColor = ( + isHover: boolean, + isNotDisabled:boolean, + isDarkTheme: boolean, +) => { + let backgroundColor; + + switch (true) { + case !isNotDisabled && isDarkTheme && isHover: + backgroundColor = '#0f1121'; + break; + + case !isNotDisabled && isDarkTheme && !isHover: + backgroundColor = '#0f1121'; + break; + + case isNotDisabled && isDarkTheme && isHover: + backgroundColor = '#4a4d58'; + break; + + case isNotDisabled && isDarkTheme && !isHover: + backgroundColor = '#323542'; + break; + + default: + backgroundColor = '#fff'; + break; + } + + return backgroundColor; +}; diff --git a/src/modules/shared/ProductSlider/index.ts b/src/modules/shared/ProductSlider/index.ts index 5349238..252db7d 100644 --- a/src/modules/shared/ProductSlider/index.ts +++ b/src/modules/shared/ProductSlider/index.ts @@ -1 +1 @@ -export * from './ProductSlider'; +export * from './ProductSlider'; diff --git a/src/modules/shared/ProductSlider/slick-theme.scss b/src/modules/shared/ProductSlider/slick-theme.scss new file mode 100644 index 0000000..ee0c05b --- /dev/null +++ b/src/modules/shared/ProductSlider/slick-theme.scss @@ -0,0 +1,132 @@ +@charset "UTF-8"; +@import '../../../styles/blocks/grid'; +@import '../../../styles/utils/variables/colors'; + +/* Arrows */ +// @include onMobile { +// .slick-prev, +// .slick-next, +// .slick-arrow { +// display: none; +// visibility: hidden; +// } +// } + +// @include onTablet { +// .slick-arrow { +// position: absolute; +// height: 400px; +// width: 32px; +// cursor: pointer; +// border: 1px solid $color__secondary; +// border-radius: 48px; +// background-color: $color__white; +// top: 46.5% ; +// -webkit-transform: translate(0, -50%); +// -ms-transform: translate(0, -50%); +// transform: translate(0, -50%); +// padding: 0; + +// &:hover { +// border-color: $color__primary; +// } +// } + +// .slick-prev { +// left: -49px; +// } + +// .slick-next { +// right: -49px; +// } +// } + + // @include onDesktop { + // .slick-arrow { + // position: absolute; + // height: 400px; + // width: 32px; + // cursor: pointer; + // border: 1px solid $color__secondary; + // border-radius: 48px; + // background-color: $color__white; + // top: 46.5% ; + // -webkit-transform: translate(0, -50%); + // -ms-transform: translate(0, -50%); + // transform: translate(0, -50%); + // padding: 0; + + // &:hover { + // border-color: $color__primary; + // } + // } + + // .slick-prev { + // left: -49px; + // } + + // .slick-next { + // right: -49px; + // } + // } + +/* Dots */ + +// .slick-dots-product { +// display: none; +// visibility: hidden; +// } + +// .slick-dots { +// margin-top: 8px; +// list-style: none; +// text-align: center; +// width: 100%; + +// li { +// position: relative; +// display: inline-block; + +// button { +// border: 0; +// background: transparent; +// width: 24px; +// height: 24px; +// margin: 0 4px; +// padding: 0; +// cursor: pointer; +// color: transparent; + +// &:before { +// display: block; +// position: absolute; +// content: ''; +// width: 14px; +// height: 4px; +// margin-top: 7px; +// margin-left: 4px; +// background-color: $color__elements; +// opacity: 1; +// } +// } + +// &.slick-active button:before { +// background-color: $color__primary; +// opacity: 1; +// } +// } + +// &__dark { +// li { +// button { +// &:before { +// background-color: #3b3e4a; +// } +// } + +// &.slick-active button:before { +// background-color: #f1f2f9; +// } +// } +// } +// } diff --git a/src/modules/shared/ProductSlider/slick.scss b/src/modules/shared/ProductSlider/slick.scss index 4c37447..649646d 100644 --- a/src/modules/shared/ProductSlider/slick.scss +++ b/src/modules/shared/ProductSlider/slick.scss @@ -1,118 +1,118 @@ -@import '../../../styles/utils/mixins/mixin-media'; - -/* Slider */ - -.slick-slider { - position: relative; - display: block; - box-sizing: border-box; - -webkit-touch-callout: none; - -webkit-user-select: none; - -khtml-user-select: none; - -moz-user-select: none; - -ms-user-select: none; - user-select: none; - -ms-touch-action: pan-y; - touch-action: pan-y; - -webkit-tap-highlight-color: transparent; -} - -.slick-slider .slick-track, -.slick-slider .slick-list { - -webkit-transform: translate3d(0, 0, 0); - -moz-transform: translate3d(0, 0, 0); - -ms-transform: translate3d(0, 0, 0); - -o-transform: translate3d(0, 0, 0); - transform: translate3d(0, 0, 0); -} - -.slick-list-product { - position: relative; - overflow: hidden; - display: block; - height: 505px !important; - margin: 0; - padding:0 !important; - - &:focus { - outline: none; - } - - &.dragging { - cursor: pointer; - cursor: hand; - } -} - -.slick-track-product { - position: relative; - left: 0; - top: 0; - display: flex !important; - margin-left: auto; - margin-right: auto; - height: 505px !important; - justify-content: space-between; - - &:before, - &:after { - content: ""; - display: table; - } - - &:after { - clear: both; - } - - .slick-loading & { - visibility: hidden; - } -} - -.slick-slide { - float: left; - height: 100%; - min-height: 1px; - [dir="rtl"] & { - float: right; - } - img { - display: block; - } - &.slick-loading img { - display: none; - } - - display: none; - - &.dragging img { - pointer-events: none; - } - - .slick-initialized & { - display: block; - } - - .slick-loading & { - visibility: hidden; - } - - .slick-vertical & { - display: block; - height: auto; - border: 1px solid transparent; - } -} -.slick-arrow.slick-hidden { - display: none; -} - -#phonesSlider { - .slick-track { - display: flex; - } - .slick-slide { - width: 272px !important; - margin: 0px auto 0px 0px; - } -} +@import '../../../styles/utils/mixins/mixin-media'; + +/* Slider */ + +.slick-slider { + position: relative; + display: block; + box-sizing: border-box; + -webkit-touch-callout: none; + -webkit-user-select: none; + -khtml-user-select: none; + -moz-user-select: none; + -ms-user-select: none; + user-select: none; + -ms-touch-action: pan-y; + touch-action: pan-y; + -webkit-tap-highlight-color: transparent; +} + +.slick-slider .slick-track, +.slick-slider .slick-list { + -webkit-transform: translate3d(0, 0, 0); + -moz-transform: translate3d(0, 0, 0); + -ms-transform: translate3d(0, 0, 0); + -o-transform: translate3d(0, 0, 0); + transform: translate3d(0, 0, 0); +} + +.slick-list-product { + position: relative; + overflow: hidden; + display: block; + height: 505px !important; + margin: 0; + padding:0 !important; + + &:focus { + outline: none; + } + + &.dragging { + cursor: pointer; + cursor: hand; + } +} + +.slick-track-product { + position: relative; + left: 0; + top: 0; + display: flex !important; + margin-left: auto; + margin-right: auto; + height: 505px !important; + justify-content: space-between; + + &:before, + &:after { + content: ""; + display: table; + } + + &:after { + clear: both; + } + + .slick-loading & { + visibility: hidden; + } +} + +.slick-slide { + float: left; + height: 100%; + min-height: 1px; + [dir="rtl"] & { + float: right; + } + img { + display: block; + } + &.slick-loading img { + display: none; + } + + display: none; + + &.dragging img { + pointer-events: none; + } + + .slick-initialized & { + display: block; + } + + .slick-loading & { + visibility: hidden; + } + + .slick-vertical & { + display: block; + height: auto; + border: 1px solid transparent; + } +} +.slick-arrow.slick-hidden { + display: none; +} + +#phonesSlider { + .slick-track { + display: flex; + } + .slick-slide { + width: 272px !important; + margin: 0px auto 0px 0px; + } +} diff --git a/src/modules/shared/components/PhoneCard/PhoneCard.module.scss b/src/modules/shared/components/PhoneCard/PhoneCard.module.scss new file mode 100644 index 0000000..5001234 --- /dev/null +++ b/src/modules/shared/components/PhoneCard/PhoneCard.module.scss @@ -0,0 +1,246 @@ +@import '../../../../styles/utils/variables/colors'; + +.productCard { + display: flex; + width: 272px; + padding: 32px; + flex-direction: column; + align-items: flex-start; + flex-shrink: 0; + flex-wrap: wrap; + align-content: flex-start; + box-sizing: border-box; + + border-radius: 8px; + border: 1px solid $color__elements; + + &__DARK { + background: #161827; + border-radius: 0px; + } + + &__image__container { + display: flex; + width: 208px; + height: 196px; + justify-content: center; + } + + &__image { + display: block; + height: 196px; + } + + &__name { + margin-top: 8px; + padding-top: 16px; + color: $color__primary; + + font-size: 14px; + font-style: normal; + font-weight: 600; + line-height: 21px; + + &__DARK { + color: $color__dark-theme-white; + } + } + + &__price { + display: flex; + margin-top: 8px; + position: relative; + padding-bottom: 8px; + + &::after { + content: ''; + position: absolute; + bottom: 0; + display: block; + width: 208px; + height: 1px; + background: $color__elements; + } + + &__DARK { + &::after { + background: $color__dark-theme-elements; + } + } + } + + &__realPrice { + color: $color__primary; + + font-size: 22px; + font-style: normal; + font-weight: 800; + line-height: 140%; + + &__DARK { + color: $color__dark-theme-white; + } + } + + &__fullPrice { + margin-left: 8px; + color: $color__secondary; + + font-size: 22px; + font-style: normal; + font-weight: 500; + line-height: normal; + text-decoration-line: line-through; + + &__DARK { + color: #75767f; + } + } + + &__characteristics { + display: flex; + padding: 8px 0px; + flex-direction: column; + align-items: center; + gap: 8px; + align-self: stretch; + + &__item { + display: flex; + justify-content: space-between; + align-items: center; + align-self: stretch; + } + + &__label { + display: block; + color: $color__secondary; + + font-size: 12px; + font-style: normal; + font-weight: 600; + line-height: normal; + + &__DARK { + color: #75767f; + } + } + + &__state { + display: block; + color: $color__primary; + + text-align: right; + font-size: 12px; + font-style: normal; + font-weight: 700; + line-height: normal; + + &__DARK { + color: $color__dark-theme-white; + } + } + } + + &__buttons { + display: flex; + margin-top: 8px; + justify-content: space-between; + align-items: center; + align-self: stretch; + padding-bottom: 0px; + margin-bottom: 0px; + } + + &__addToCart { + display: flex; + height: 40px; + justify-content: center; + align-items: center; + flex: 1 0 0; + + border-radius: 8px; + border: none; + background: $color__accent; + + color: $color__white; + text-align: center; + font-size: 14px; + font-style: normal; + font-weight: 700; + line-height: 21px; + + &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); + } + + &__SELECTED { + border-radius: 8px; + border: 1px solid $color__elements; + background: $color__white; + color: $color__accent; + } + + &__DARK { + background: #905bff; + border-radius: 0px; + color: $color__dark-theme-white; + border: none; + + &:hover { + background: #a378ff; + } + + &__SELECTED { + background: $color__dark-theme-secondary; + color: $color__dark-theme-white; + } + } + } + + &__addToFavourite { + background-image: url('../../Images/Icons/Favourites-icon_Default.svg'); + + margin-left: 8px; + width: 40px; + height: 40px; + flex-shrink: 0; + background-repeat: no-repeat; + background-position: center; + background-color: $color__white; + + border-radius: 48px; + border: 1px solid $color__icons; + + &:hover { + border: 1px solid $color__primary; + } + + &__SELECTED { + background-image: url('../../Images/Icons/Favourites-icon_Selected.svg'); + + border: 1px solid $color__elements; + } + + &__DARK { + background-image: url('../../Images/Icons/Favourites-icon_darkDefault.svg'); + + background-color: $color__dark-theme-secondary; + border-radius: 0px; + border: none; + color: $color__dark-theme-white; + + &:hover { + background-color: #4a4d58; + } + + &__SELECTED { + background-image: url('../../Images/Icons/Favourites-icon_darkSelected.svg'); + + border: 1px solid $color__dark-theme-elements; + border-radius: 0px; + background-color: $color__dark-theme-secondary; + } + } + } +} diff --git a/src/modules/shared/components/PhoneCard/PhoneCard.tsx b/src/modules/shared/components/PhoneCard/PhoneCard.tsx new file mode 100644 index 0000000..d98cc3d --- /dev/null +++ b/src/modules/shared/components/PhoneCard/PhoneCard.tsx @@ -0,0 +1,149 @@ +/* eslint-disable jsx-a11y/anchor-has-content */ +/* eslint-disable jsx-a11y/control-has-associated-label */ +import cn from 'classnames'; +import style from './PhoneCard.module.scss'; +import { useAppSelector } from '../../../../store/hooks'; +import { Phone } from '../../../../types/Phone'; + +type Props = { + phoneItem: Phone; +}; + +export const PhoneCard: React.FC = ({ phoneItem }) => { + const { + name, + fullPrice, + price, + screen, + capacity, + ram, + image, + } = phoneItem; + + const { isDarkTheme } = useAppSelector((state) => state.theme); + const isItemSelected = true; + + const showDiscountPrice = () => { + if (price !== fullPrice) { + return ( +

+ {`$${fullPrice}`} +

+ ); + } + + return false; + }; + + const characteristicsArray = [ + { label: 'Screen', state: screen }, + { label: 'Capacity', state: capacity }, + { label: 'RAM', state: ram }, + ]; + + return ( +
+
+ {name} +
+ +

+ {name} +

+ +
+

+ {`$${price}`} +

+ + {showDiscountPrice()} +
+ +
+ {characteristicsArray.map((characteristic) => ( +
+

+ {characteristic.label} +

+ +

+ {characteristic.state} +

+
+ ))} +
+ +
+ + + */} +
+
+ ); +}; diff --git a/src/modules/shared/components/PhoneCard/index.ts b/src/modules/shared/components/PhoneCard/index.ts new file mode 100644 index 0000000..53e9b01 --- /dev/null +++ b/src/modules/shared/components/PhoneCard/index.ts @@ -0,0 +1 @@ +export * from './PhoneCard'; diff --git a/src/static/api/phones/apple-iphone-7-32gb-black.json b/src/static/api/phones/apple-iphone-7-32gb-black.json new file mode 100644 index 0000000..e661783 --- /dev/null +++ b/src/static/api/phones/apple-iphone-7-32gb-black.json @@ -0,0 +1,48 @@ + +{ + "id": "apple-iphone-7-32gb-black", + "namespaceId": "apple-iphone-7", + "name": "Apple iPhone 7 32GB Black", + "capacityAvailable": ["32GB"], + "capacity": "32GB", + "priceRegular": 400, + "priceDiscount": 375, + "colorsAvailable": ["black", "rosegold", "gold", "silver"], + "color": "black", + "images": [ + "img/phones/apple-iphone-7/black/00.jpg", + "img/phones/apple-iphone-7/black/01.jpg", + "img/phones/apple-iphone-7/black/02.jpg", + "img/phones/apple-iphone-7/black/03.jpg", + "img/phones/apple-iphone-7/black/04.jpg" + ], + "description": [ + { + "title": "And then there was Pro", + "text": [ + "A transformative triple-camera system that adds tons of capability without complexity.", + "An unprecedented leap in battery life. And a mind-blowing chip that doubles down on machine learning and pushes the boundaries of what a smartphone can do. Welcome to the first iPhone powerful enough to be called Pro." + ] + }, + { + "title": "Camera", + "text": [ + "Meet the first triple-camera system to combine cutting-edge technology with the legendary simplicity of iPhone. Capture up to four times more scene. Get beautiful images in drastically lower light. Shoot the highest-quality video in a smartphone โ€” then edit with the same tools you love for photos. Youโ€™ve never shot with anything like it." + ] + }, + { + "title": "Shoot it. Flip it. Zoom it. Crop it. Cut it. Light it. Tweak it. Love it.", + "text": [ + "iPhone 11 Pro lets you capture videos that are beautifully true to life, with greater detail and smoother motion. Epic processing power means it can shoot 4K video with extended dynamic range and cinematic video stabilization โ€” all at 60 fps. You get more creative control, too, with four times more scene and powerful new editing tools to play with." + ] + } + ], + "screen": "4.7' IPS", + "resolution": "1334x750", + "processor": "Apple A10", + "ram": "2GB", + "camera": "12 Mp + 7 Mp", + "zoom": "Digital, 5x", + "cell": ["GPRS", "EDGE", "WCDMA", "UMTS", "HSPA", "LTE"] +} + diff --git a/src/static/api/phones/apple-iphone-7-32gb-rosegold.json b/src/static/api/phones/apple-iphone-7-32gb-rosegold.json new file mode 100644 index 0000000..e69de29 diff --git a/src/static/api/phones/apple-iphone-7-plus-32gb-black.json b/src/static/api/phones/apple-iphone-7-plus-32gb-black.json new file mode 100644 index 0000000..19caecb --- /dev/null +++ b/src/static/api/phones/apple-iphone-7-plus-32gb-black.json @@ -0,0 +1,48 @@ + +{ + "id": "apple-iphone-7-plus-32gb-black", + "namespaceId": "apple-iphone-7-plus", + "name": "Apple iPhone 7 Plus 32GB Black", + "capacityAvailable": ["32GB"], + "capacity": "32GB", + "priceRegular": 540, + "priceDiscount": 500, + "colorsAvailable": ["black", "rosegold", "gold", "silver"], + "color": "black", + "images": [ + "img/phones/apple-iphone-7-plus/black/00.jpg", + "img/phones/apple-iphone-7-plus/black/01.jpg", + "img/phones/apple-iphone-7-plus/black/02.jpg", + "img/phones/apple-iphone-7-plus/black/03.jpg", + "img/phones/apple-iphone-7-plus/black/04.jpg" + ], + "description": [ + { + "title": "And then there was Pro", + "text": [ + "A transformative triple-camera system that adds tons of capability without complexity.", + "An unprecedented leap in battery life. And a mind-blowing chip that doubles down on machine learning and pushes the boundaries of what a smartphone can do. Welcome to the first iPhone powerful enough to be called Pro." + ] + }, + { + "title": "Camera", + "text": [ + "Meet the first triple-camera system to combine cutting-edge technology with the legendary simplicity of iPhone. Capture up to four times more scene. Get beautiful images in drastically lower light. Shoot the highest-quality video in a smartphone โ€” then edit with the same tools you love for photos. Youโ€™ve never shot with anything like it." + ] + }, + { + "title": "Shoot it. Flip it. Zoom it. Crop it. Cut it. Light it. Tweak it. Love it.", + "text": [ + "iPhone 11 Pro lets you capture videos that are beautifully true to life, with greater detail and smoother motion. Epic processing power means it can shoot 4K video with extended dynamic range and cinematic video stabilization โ€” all at 60 fps. You get more creative control, too, with four times more scene and powerful new editing tools to play with." + ] + } + ], + "screen": "5.5' IPS", + "resolution": "1920x1080", + "processor": "Apple A10", + "ram": "3GB", + "camera": "12 Mp + 7 Mp", + "zoom": "Digital, 10x / Optical, 2x", + "cell": ["GPRS", "EDGE", "WCDMA", "UMTS", "HSPA", "LTE"] +} + diff --git a/src/static/api/phones/apple-iphone-7-plus-32gb-gold.json b/src/static/api/phones/apple-iphone-7-plus-32gb-gold.json new file mode 100644 index 0000000..dea5c38 --- /dev/null +++ b/src/static/api/phones/apple-iphone-7-plus-32gb-gold.json @@ -0,0 +1,48 @@ + +{ + "id": "apple-iphone-7-plus-32gb-gold", + "namespaceId": "apple-iphone-7-plus", + "name": "Apple iPhone 7 Plus 32GB Gold", + "capacityAvailable": ["32GB"], + "capacity": "32GB", + "priceRegular": 540, + "priceDiscount": 500, + "colorsAvailable": ["black", "rosegold", "gold", "silver"], + "color": "gold", + "images": [ + "img/phones/apple-iphone-7-plus/gold/00.jpg", + "img/phones/apple-iphone-7-plus/gold/01.jpg", + "img/phones/apple-iphone-7-plus/gold/02.jpg", + "img/phones/apple-iphone-7-plus/gold/03.jpg", + "img/phones/apple-iphone-7-plus/gold/04.jpg" + ], + "description": [ + { + "title": "And then there was Pro", + "text": [ + "A transformative triple-camera system that adds tons of capability without complexity.", + "An unprecedented leap in battery life. And a mind-blowing chip that doubles down on machine learning and pushes the boundaries of what a smartphone can do. Welcome to the first iPhone powerful enough to be called Pro." + ] + }, + { + "title": "Camera", + "text": [ + "Meet the first triple-camera system to combine cutting-edge technology with the legendary simplicity of iPhone. Capture up to four times more scene. Get beautiful images in drastically lower light. Shoot the highest-quality video in a smartphone โ€” then edit with the same tools you love for photos. Youโ€™ve never shot with anything like it." + ] + }, + { + "title": "Shoot it. Flip it. Zoom it. Crop it. Cut it. Light it. Tweak it. Love it.", + "text": [ + "iPhone 11 Pro lets you capture videos that are beautifully true to life, with greater detail and smoother motion. Epic processing power means it can shoot 4K video with extended dynamic range and cinematic video stabilization โ€” all at 60 fps. You get more creative control, too, with four times more scene and powerful new editing tools to play with." + ] + } + ], + "screen": "5.5' IPS", + "resolution": "1920x1080", + "processor": "Apple A10", + "ram": "3GB", + "camera": "12 Mp + 7 Mp", + "zoom": "Digital, 10x / Optical, 2x", + "cell": ["GPRS", "EDGE", "WCDMA", "UMTS", "HSPA", "LTE"] +} + diff --git a/src/static/api/phones/apple-iphone-7-plus-32gb-rosegold.json b/src/static/api/phones/apple-iphone-7-plus-32gb-rosegold.json new file mode 100644 index 0000000..e69de29 diff --git a/src/static/api/phones/apple-iphone-7-plus-32gb-silver.json b/src/static/api/phones/apple-iphone-7-plus-32gb-silver.json new file mode 100644 index 0000000..e69de29 diff --git a/src/static/banners/bannersData.js b/src/static/banners/bannersData.js index 1bb5d1b..8f7b99d 100644 --- a/src/static/banners/bannersData.js +++ b/src/static/banners/bannersData.js @@ -2,16 +2,16 @@ export const bannersData = [ { id: 1, title: 'banner-phones', - photo: '/banner-phones.png', + photo: '/banners/banner-phones.webp', }, { id: 2, title: 'banner-tablets', - photo: '/banner-tablets.png', + photo: '/banners/banner-tablets.webp', }, { id: 3, title: 'banner-accessories', - photo: '/banner-accessories.png', + photo: '/banners/banner-accessories.webp', }, ]; diff --git a/src/static/buttons/Button_DarkDefaultArrowLeft.svg b/src/static/buttons/Button_DarkDefaultArrowLeft.svg new file mode 100644 index 0000000..ea1fdf9 --- /dev/null +++ b/src/static/buttons/Button_DarkDefaultArrowLeft.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DarkDefaultArrowRight.svg b/src/static/buttons/Button_DarkDefaultArrowRight.svg new file mode 100644 index 0000000..c14adfa --- /dev/null +++ b/src/static/buttons/Button_DarkDefaultArrowRight.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DarkDisabledArrowLeft.svg b/src/static/buttons/Button_DarkDisabledArrowLeft.svg new file mode 100644 index 0000000..f888fc5 --- /dev/null +++ b/src/static/buttons/Button_DarkDisabledArrowLeft.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DarkDisabledArrowRight.svg b/src/static/buttons/Button_DarkDisabledArrowRight.svg new file mode 100644 index 0000000..78ead13 --- /dev/null +++ b/src/static/buttons/Button_DarkDisabledArrowRight.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DefaultArrowLeft.svg b/src/static/buttons/Button_DefaultArrowLeft.svg new file mode 100644 index 0000000..d2bb8d9 --- /dev/null +++ b/src/static/buttons/Button_DefaultArrowLeft.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DefaultArrowRight.svg b/src/static/buttons/Button_DefaultArrowRight.svg new file mode 100644 index 0000000..bc43ed0 --- /dev/null +++ b/src/static/buttons/Button_DefaultArrowRight.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DisabledArrowLeft.svg b/src/static/buttons/Button_DisabledArrowLeft.svg new file mode 100644 index 0000000..2f44f37 --- /dev/null +++ b/src/static/buttons/Button_DisabledArrowLeft.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Button_DisabledArrowRight.svg b/src/static/buttons/Button_DisabledArrowRight.svg new file mode 100644 index 0000000..abdbd71 --- /dev/null +++ b/src/static/buttons/Button_DisabledArrowRight.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Icons_ArrowDown.svg b/src/static/buttons/Icons_ArrowDown.svg new file mode 100644 index 0000000..8819d34 --- /dev/null +++ b/src/static/buttons/Icons_ArrowDown.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Icons_ArrowUp.svg b/src/static/buttons/Icons_ArrowUp.svg new file mode 100644 index 0000000..1579b82 --- /dev/null +++ b/src/static/buttons/Icons_ArrowUp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Icons_DarkArrowDown.svg b/src/static/buttons/Icons_DarkArrowDown.svg new file mode 100644 index 0000000..4979e91 --- /dev/null +++ b/src/static/buttons/Icons_DarkArrowDown.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/Icons_DarktArrowUp.svg b/src/static/buttons/Icons_DarktArrowUp.svg new file mode 100644 index 0000000..cc4bfb2 --- /dev/null +++ b/src/static/buttons/Icons_DarktArrowUp.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/static/buttons/fovourites-default_button.svg b/src/static/buttons/fovourites-default_button.svg index 36393d5..49e569a 100644 --- a/src/static/buttons/fovourites-default_button.svg +++ b/src/static/buttons/fovourites-default_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/fovourites-default_button_dark.svg b/src/static/buttons/fovourites-default_button_dark.svg index e6242ff..dba3aa4 100644 --- a/src/static/buttons/fovourites-default_button_dark.svg +++ b/src/static/buttons/fovourites-default_button_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/fovourites-hover_button.svg b/src/static/buttons/fovourites-hover_button.svg index 28c7b9c..7e2892b 100644 --- a/src/static/buttons/fovourites-hover_button.svg +++ b/src/static/buttons/fovourites-hover_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/fovourites-selected_button.svg b/src/static/buttons/fovourites-selected_button.svg index d7c7caf..48a8c19 100644 --- a/src/static/buttons/fovourites-selected_button.svg +++ b/src/static/buttons/fovourites-selected_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/fovourites-selected_button_dark.svg b/src/static/buttons/fovourites-selected_button_dark.svg index 25ac412..b529c28 100644 --- a/src/static/buttons/fovourites-selected_button_dark.svg +++ b/src/static/buttons/fovourites-selected_button_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/pagination-default__button.svg b/src/static/buttons/pagination-default__button.svg index ce60ac0..b1cc0d4 100644 --- a/src/static/buttons/pagination-default__button.svg +++ b/src/static/buttons/pagination-default__button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/pagination-default_button_dark.svg b/src/static/buttons/pagination-default_button_dark.svg index 39b774b..5f12eda 100644 --- a/src/static/buttons/pagination-default_button_dark.svg +++ b/src/static/buttons/pagination-default_button_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/pagination-hover_button.svg b/src/static/buttons/pagination-hover_button.svg index a2e92f6..780250c 100644 --- a/src/static/buttons/pagination-hover_button.svg +++ b/src/static/buttons/pagination-hover_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/pagination-selected__button.svg b/src/static/buttons/pagination-selected__button.svg index 9dcdede..54c5cc4 100644 --- a/src/static/buttons/pagination-selected__button.svg +++ b/src/static/buttons/pagination-selected__button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-right-button-default_button_dark.svg b/src/static/buttons/slider-right-button-default_button_dark.svg index c016265..5672883 100644 --- a/src/static/buttons/slider-right-button-default_button_dark.svg +++ b/src/static/buttons/slider-right-button-default_button_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-right-button-disabled_button.svg b/src/static/buttons/slider-right-button-disabled_button.svg index 83de078..78ce63f 100644 --- a/src/static/buttons/slider-right-button-disabled_button.svg +++ b/src/static/buttons/slider-right-button-disabled_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-right-button-disabled_button_dark.svg b/src/static/buttons/slider-right-button-disabled_button_dark.svg index 069b974..cbe6546 100644 --- a/src/static/buttons/slider-right-button-disabled_button_dark.svg +++ b/src/static/buttons/slider-right-button-disabled_button_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-right-button-hover_button.svg b/src/static/buttons/slider-right-button-hover_button.svg index 037a08e..0dd46fb 100644 --- a/src/static/buttons/slider-right-button-hover_button.svg +++ b/src/static/buttons/slider-right-button-hover_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-right-default__button.svg b/src/static/buttons/slider-right-default__button.svg index aa7038c..4ac81f9 100644 --- a/src/static/buttons/slider-right-default__button.svg +++ b/src/static/buttons/slider-right-default__button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-up-button-default_button.svg b/src/static/buttons/slider-up-button-default_button.svg index f04c768..1e46ee1 100644 --- a/src/static/buttons/slider-up-button-default_button.svg +++ b/src/static/buttons/slider-up-button-default_button.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/buttons/slider-up-button-default_button_dark.svg b/src/static/buttons/slider-up-button-default_button_dark.svg index ff85414..be4386f 100644 --- a/src/static/buttons/slider-up-button-default_button_dark.svg +++ b/src/static/buttons/slider-up-button-default_button_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/icons/arrow-down_icon.svg b/src/static/icons/arrow-down_icon.svg index 8819d34..0b22de4 100644 --- a/src/static/icons/arrow-down_icon.svg +++ b/src/static/icons/arrow-down_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/arrow-left_icon.svg b/src/static/icons/arrow-left_icon.svg index e351a43..4432259 100644 --- a/src/static/icons/arrow-left_icon.svg +++ b/src/static/icons/arrow-left_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/arrow-right_icon.svg b/src/static/icons/arrow-right_icon.svg index 3f0fdf6..5ff3bbd 100644 --- a/src/static/icons/arrow-right_icon.svg +++ b/src/static/icons/arrow-right_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/arrow-up_icon.svg b/src/static/icons/arrow-up_icon.svg index 1579b82..fa2b55d 100644 --- a/src/static/icons/arrow-up_icon.svg +++ b/src/static/icons/arrow-up_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/cart_icon.svg b/src/static/icons/cart_icon.svg index 0cbe9f9..b2653e2 100644 --- a/src/static/icons/cart_icon.svg +++ b/src/static/icons/cart_icon.svg @@ -1,7 +1,7 @@ - - - - - - - + + + + + + + diff --git a/src/static/icons/close_icon.svg b/src/static/icons/close_icon.svg index 637dbd8..447f12e 100644 --- a/src/static/icons/close_icon.svg +++ b/src/static/icons/close_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/favourites_icon.svg b/src/static/icons/favourites_icon.svg index 4c265c2..d21ff1b 100644 --- a/src/static/icons/favourites_icon.svg +++ b/src/static/icons/favourites_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/home_icon.svg b/src/static/icons/home_icon.svg index 8ff4bba..7cff7c2 100644 --- a/src/static/icons/home_icon.svg +++ b/src/static/icons/home_icon.svg @@ -1,4 +1,4 @@ - - + + diff --git a/src/static/icons/menu_icon.svg b/src/static/icons/menu_icon.svg index b246df7..65d4b20 100644 --- a/src/static/icons/menu_icon.svg +++ b/src/static/icons/menu_icon.svg @@ -1,9 +1,9 @@ - - - - - - - - - + + + + + + + + + diff --git a/src/static/icons/minus_icon.svg b/src/static/icons/minus_icon.svg index 0547f12..235b2d8 100644 --- a/src/static/icons/minus_icon.svg +++ b/src/static/icons/minus_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/plus_icon.svg b/src/static/icons/plus_icon.svg index 3dd6377..974f7e1 100644 --- a/src/static/icons/plus_icon.svg +++ b/src/static/icons/plus_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/icons/search_icon.svg b/src/static/icons/search_icon.svg index 3541cda..9f9e97b 100644 --- a/src/static/icons/search_icon.svg +++ b/src/static/icons/search_icon.svg @@ -1,5 +1,5 @@ - - - - - + + + + + diff --git a/src/static/selectors/color-default_selector.svg b/src/static/selectors/color-default_selector.svg index 251b9ac..c26376a 100644 --- a/src/static/selectors/color-default_selector.svg +++ b/src/static/selectors/color-default_selector.svg @@ -1,10 +1,10 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/src/static/selectors/color-default_selector_dark.svg b/src/static/selectors/color-default_selector_dark.svg index 879b80e..6379ca0 100644 --- a/src/static/selectors/color-default_selector_dark.svg +++ b/src/static/selectors/color-default_selector_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/selectors/color-hover_selector.svg b/src/static/selectors/color-hover_selector.svg index 72ca0f1..2cdfccf 100644 --- a/src/static/selectors/color-hover_selector.svg +++ b/src/static/selectors/color-hover_selector.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/selectors/color-hover_selector_dark.svg b/src/static/selectors/color-hover_selector_dark.svg index 31132ed..0aa9767 100644 --- a/src/static/selectors/color-hover_selector_dark.svg +++ b/src/static/selectors/color-hover_selector_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/selectors/color-selected_selector.svg b/src/static/selectors/color-selected_selector.svg index 4269a61..b03860f 100644 --- a/src/static/selectors/color-selected_selector.svg +++ b/src/static/selectors/color-selected_selector.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/static/selectors/color-selected_selector_dark.svg b/src/static/selectors/color-selected_selector_dark.svg index 4035997..1f9a9da 100644 --- a/src/static/selectors/color-selected_selector_dark.svg +++ b/src/static/selectors/color-selected_selector_dark.svg @@ -1,4 +1,4 @@ - - - - + + + + diff --git a/src/store/reducers/themeSlice.ts b/src/store/reducers/themeSlice.ts index 0e58ed1..dede34a 100644 --- a/src/store/reducers/themeSlice.ts +++ b/src/store/reducers/themeSlice.ts @@ -1,26 +1,26 @@ -/* eslint-disable no-param-reassign */ -import { createSlice } from '@reduxjs/toolkit'; -import { localClient } from '../../utils/localClient'; - -export interface ThemeState { - isDarkTheme: boolean; -} - -const initialState: ThemeState = { - isDarkTheme: - localClient.read('isDarkTheme') || localClient.init('isDarkTheme', false), -}; - -const themeSlice = createSlice({ - name: 'theme', - initialState, - reducers: { - change: (state) => { - state.isDarkTheme = !state.isDarkTheme; - localClient.write('isDarkTheme', state.isDarkTheme); - }, - }, -}); - -export default themeSlice.reducer; -export const { actions } = themeSlice; +/* eslint-disable no-param-reassign */ +import { createSlice } from '@reduxjs/toolkit'; +import { localClient } from '../../utils/localClient'; + +export interface ThemeState { + isDarkTheme: boolean; +} + +const initialState: ThemeState = { + isDarkTheme: + localClient.read('isDarkTheme') || localClient.init('isDarkTheme', false), +}; + +const themeSlice = createSlice({ + name: 'theme', + initialState, + reducers: { + change: (state) => { + state.isDarkTheme = !state.isDarkTheme; + localClient.write('isDarkTheme', state.isDarkTheme); + }, + }, +}); + +export default themeSlice.reducer; +export const { actions } = themeSlice; diff --git a/src/store/store.ts b/src/store/store.ts index e1eef62..8072788 100644 --- a/src/store/store.ts +++ b/src/store/store.ts @@ -1,25 +1,25 @@ -import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; - -import themeReducer from './reducers/themeSlice'; -import favoritesReducer from './reducers/favoritesSlice'; -import cartReducer from './reducers/cartSlice'; - -export const store = configureStore({ - reducer: { - theme: themeReducer, - favorites: favoritesReducer, - cart: cartReducer, - }, -}); - -export type AppDispatch = typeof store.dispatch; -export type RootState = ReturnType; - -/* eslint-disable @typescript-eslint/indent */ -export type AppThunk = ThunkAction< - ReturnType, - RootState, - unknown, - Action ->; -/* eslint-enable @typescript-eslint/indent */ +import { configureStore, ThunkAction, Action } from '@reduxjs/toolkit'; + +import themeReducer from './reducers/themeSlice'; +import favoritesReducer from './reducers/favoritesSlice'; +import cartReducer from './reducers/cartSlice'; + +export const store = configureStore({ + reducer: { + theme: themeReducer, + favorites: favoritesReducer, + cart: cartReducer, + }, +}); + +export type AppDispatch = typeof store.dispatch; +export type RootState = ReturnType; + +/* eslint-disable @typescript-eslint/indent */ +export type AppThunk = ThunkAction< + ReturnType, + RootState, + unknown, + Action +>; +/* eslint-enable @typescript-eslint/indent */ diff --git a/src/styles/blocks/_grid.scss b/src/styles/blocks/_grid.scss index 3f7993e..4c3ce38 100644 --- a/src/styles/blocks/_grid.scss +++ b/src/styles/blocks/_grid.scss @@ -1,22 +1,22 @@ -@import '../utils/mixins/mixin-media'; - -$columns--phone: 4; -$columns--tablet: 12; -$columns--desktop: 24; - -.grid { - display: grid; - column-gap: 16px; - - @include onMobile { - grid-template-columns: repeat($columns--phone, 1fr); - } - - @include onTablet { - grid-template-columns: repeat($columns--tablet, 1fr); - } - - @include onDesktop { - grid-template-columns: repeat($columns--desktop, 1fr); - } -} +@import '../utils/mixins/mixin-media'; + +$columns--phone: 4; +$columns--tablet: 12; +$columns--desktop: 24; + +.grid { + display: grid; + column-gap: 16px; + + @include onMobile { + grid-template-columns: repeat($columns--phone, 1fr); + } + + @include onTablet { + grid-template-columns: repeat($columns--tablet, 1fr); + } + + @include onDesktop { + grid-template-columns: repeat($columns--desktop, 1fr); + } +} diff --git a/src/styles/blocks/_page.scss b/src/styles/blocks/_page.scss index 782ae90..c86a761 100644 --- a/src/styles/blocks/_page.scss +++ b/src/styles/blocks/_page.scss @@ -1,56 +1,56 @@ -@import '../utils/variables/colors'; -@import '../utils/mixins/mixin-media'; -@import '../utils/mixins/mixin-typography'; - -@mixin defaultMarginButton { - margin-bottom: 56px; - - @include onTablet { - margin-bottom: 64px; - } - - @include onDesktop { - margin-bottom: 80px; - } -} - -.page { - &__content { - display: flex; - flex-direction: column; - min-height: 100vh; - } - - &__main { - flex-grow: 1; - } - - &__main-title { - margin-bottom: 24px; - @include h1-typography; - - @include onTablet { - margin-bottom: 32px; - } - - @include onDesktop { - margin-bottom: 56px; - } - - &__DARK { - @include typography-dark; - } - } - - &__categories { - @include defaultMarginButton; - } - - &__main-slider { - @include defaultMarginButton; - } - - &__product-slider { - @include defaultMarginButton; - } -} +@import '../utils/variables/colors'; +@import '../utils/mixins/mixin-media'; +@import '../utils/mixins/mixin-typography'; + +@mixin defaultMarginButton { + margin-bottom: 56px; + + @include onTablet { + margin-bottom: 64px; + } + + @include onDesktop { + margin-bottom: 80px; + } +} + +.page { + &__content { + display: flex; + flex-direction: column; + min-height: 100vh; + } + + &__main { + flex-grow: 1; + } + + &__main-title { + margin-bottom: 24px; + @include h1-typography; + + @include onTablet { + margin-bottom: 32px; + } + + @include onDesktop { + margin-bottom: 56px; + } + + &__DARK { + @include typography-dark; + } + } + + &__categories { + @include defaultMarginButton; + } + + &__main-slider { + @include defaultMarginButton; + } + + &__product-slider { + @include defaultMarginButton; + } +} diff --git a/src/styles/utils/_normalise.scss b/src/styles/utils/_normalise.scss new file mode 100644 index 0000000..b86d165 --- /dev/null +++ b/src/styles/utils/_normalise.scss @@ -0,0 +1,3 @@ +html { + box-sizing: border-box; +} diff --git a/src/styles/utils/_normalize.scss b/src/styles/utils/_normalize.scss index b86d165..55f9617 100644 --- a/src/styles/utils/_normalize.scss +++ b/src/styles/utils/_normalize.scss @@ -1,3 +1,3 @@ -html { - box-sizing: border-box; -} +html { + box-sizing: border-box; +} diff --git a/src/styles/utils/_reset.scss b/src/styles/utils/_reset.scss index 07b9405..d9bc17c 100644 --- a/src/styles/utils/_reset.scss +++ b/src/styles/utils/_reset.scss @@ -1,56 +1,56 @@ -html, -body, -div, -span, -h1, -h2, -h3, -h4, -h5, -h6, -p, -a, -address, -img, -ol, -ul, -li, -form, -article, -aside, -footer, -header, -menu, -nav, -section, -input { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - vertical-align: baseline; - text-decoration: none; -} - -/* HTML5 display-role reset for older browsers */ -article, -aside, -footer, -header, -nav, -section { - display: block; -} - -body { - line-height: 1; -} - -ol, -ul { - list-style: none; -} - -iframe { - display: none; -} +html, +body, +div, +span, +h1, +h2, +h3, +h4, +h5, +h6, +p, +a, +address, +img, +ol, +ul, +li, +form, +article, +aside, +footer, +header, +menu, +nav, +section, +input { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + vertical-align: baseline; + text-decoration: none; +} + +/* HTML5 display-role reset for older browsers */ +article, +aside, +footer, +header, +nav, +section { + display: block; +} + +body { + line-height: 1; +} + +ol, +ul { + list-style: none; +} + +iframe { + display: none; +} diff --git a/src/styles/utils/mixins/_mixin-media.scss b/src/styles/utils/mixins/_mixin-media.scss index 66e44c7..a1fe892 100644 --- a/src/styles/utils/mixins/_mixin-media.scss +++ b/src/styles/utils/mixins/_mixin-media.scss @@ -1,17 +1,17 @@ -@mixin onMobile { - @media (max-width: 639px) { - @content; - } -} - -@mixin onTablet { - @media (min-width: 640px) { - @content; - } -} - -@mixin onDesktop { - @media (min-width: 1200px) { - @content; - } -} +@mixin onMobile { + @media (max-width: 639px) { + @content; + } +} + +@mixin onTablet { + @media (min-width: 640px) { + @content; + } +} + +@mixin onDesktop { + @media (min-width: 1200px) { + @content; + } +} diff --git a/src/styles/utils/mixins/_mixin-typography.scss b/src/styles/utils/mixins/_mixin-typography.scss index d5e639b..1d0fb2e 100644 --- a/src/styles/utils/mixins/_mixin-typography.scss +++ b/src/styles/utils/mixins/_mixin-typography.scss @@ -1,81 +1,81 @@ -@import '../variables/colors'; -@import './mixin-media'; - -@mixin typography-dark { - color: $color__dark-theme__white; -} - -@mixin h1-typography { - font-size: 32px; - font-weight: 800; - line-height: 41px; - letter-spacing: -0.32px; - color: $color__primary; - - @include onTablet { - font-size: 48px; - line-height: 56px; - letter-spacing: -0.48px; - } -} - -@mixin h2-typography { - font-size: 22px; - font-weight: 800; - line-height: 31px; - letter-spacing: -0.32px; - color: $color__primary; - - @include onTablet { - font-size: 32px; - line-height: 41px; - letter-spacing: -0.48px; - } -} - -@mixin h3-typography { - font-size: 20px; - font-weight: 700; - color: $color__primary; - - @include onTablet { - font-size: 22px; - font-weight: 800; - line-height: 140%; - } -} - -@mixin h4-typography { - font-size: 16px; - font-weight: 700; - color: $color__primary; - - @include onTablet { - font-size: 20px; - } -} - -@mixin upperCase-typography { - font-size: 12px; - font-weight: 800; - line-height: 11px; - letter-spacing: 0.48px; - text-transform: uppercase; -} - -@mixin buttons-typography { - font-size: 14px; - font-weight: 700; - line-height: 21px; -} - -@mixin bodyText-typography { - font-size: 14px; - font-weight: 600; - line-height: 21px; -} - -@mixin smallText-typography { - font-size: 12px; - font-weight: 700; -} +@import '../variables/colors'; +@import './mixin-media'; + +@mixin typography-dark { + color: $color__dark-theme__white; +} + +@mixin h1-typography { + font-size: 32px; + font-weight: 800; + line-height: 41px; + letter-spacing: -0.32px; + color: $color__primary; + + @include onTablet { + font-size: 48px; + line-height: 56px; + letter-spacing: -0.48px; + } +} + +@mixin h2-typography { + font-size: 22px; + font-weight: 800; + line-height: 31px; + letter-spacing: -0.32px; + color: $color__primary; + + @include onTablet { + font-size: 32px; + line-height: 41px; + letter-spacing: -0.48px; + } +} + +@mixin h3-typography { + font-size: 20px; + font-weight: 700; + color: $color__primary; + + @include onTablet { + font-size: 22px; + font-weight: 800; + line-height: 140%; + } +} + +@mixin h4-typography { + font-size: 16px; + font-weight: 700; + color: $color__primary; + + @include onTablet { + font-size: 20px; + } +} + +@mixin upperCase-typography { + font-size: 12px; + font-weight: 800; + line-height: 11px; + letter-spacing: 0.48px; + text-transform: uppercase; +} + +@mixin buttons-typography { + font-size: 14px; + font-weight: 700; + line-height: 21px; +} + +@mixin bodyText-typography { + font-size: 14px; + font-weight: 600; + line-height: 21px; +} + +@mixin smallText-typography { + font-size: 12px; + font-weight: 700; +} diff --git a/src/styles/utils/variables/_colors.scss b/src/styles/utils/variables/_colors.scss index 6d78991..737740c 100644 --- a/src/styles/utils/variables/_colors.scss +++ b/src/styles/utils/variables/_colors.scss @@ -19,3 +19,4 @@ $color__dark-theme__surface-2: #323542; $color__dark-theme__black: #0f1121; $color__dark-theme__white: #f1f2f9; $color__dark-theme__accent: #905bff; +$color__dark-theme__accent__hover: #a378ff; diff --git a/src/types/Phone.ts b/src/types/Phone.ts index 020ce9a..7e18d39 100644 --- a/src/types/Phone.ts +++ b/src/types/Phone.ts @@ -1,19 +1,19 @@ -export interface Phone { - id: number; - category: string; - phoneId: string; - itemId: string; - name: string; - fullPrice: number; - price: number; - screen: string; - capacity: string; - color: string; - ram: string; - year: number; - image: string; -} - -export interface PhoneWithAmount extends Phone { - amount: number; -} +export interface Phone { + id: number; + category: string; + phoneId: string; + itemId: string; + name: string; + fullPrice: number; + price: number; + screen: string; + capacity: string; + color: string; + ram: string; + year: number; + image: string; +} + +export interface PhoneWithAmount extends Phone { + amount: number; +} diff --git a/src/types/PhoneItem.ts b/src/types/PhoneItem.ts new file mode 100644 index 0000000..ce8a0c4 --- /dev/null +++ b/src/types/PhoneItem.ts @@ -0,0 +1,16 @@ +export type PhoneItem = { + id: string; + name: string; + image: string; + price: number; + fullPrice: number; + year: number; + screen?: string; + capacity?: string; + ram?: string; + color?: string; + description?: { + title: string; + text: string[]; + }[]; +}; diff --git a/src/types/SearchParams.ts b/src/types/SearchParams.ts new file mode 100644 index 0000000..94ecc10 --- /dev/null +++ b/src/types/SearchParams.ts @@ -0,0 +1,3 @@ +export type SearchParams = { + [key: string]: string | number, +}; diff --git a/src/utils/axiosClient.ts b/src/utils/axiosClient.ts index dbd36cf..6aeaf46 100644 --- a/src/utils/axiosClient.ts +++ b/src/utils/axiosClient.ts @@ -1,35 +1,35 @@ -/* eslint-disable @typescript-eslint/no-explicit-any */ -import axios from 'axios'; - -const instance = axios.create({ - baseURL: 'https://fe-aug23-team4-nice-gadgets-api.onrender.com', -}); - -function wait(delay: number) { - return new Promise((resolve) => setTimeout(resolve, delay)); -} - -export const axiosClient = { - async get(url: string) { - await wait(500); - const response = await instance.get(url); - - return response.data; - }, - - async post(url: string, data: any) { - const response = await instance.post(url, data); - - return response.data; - }, - - async patch(url: string, data: any) { - const response = await instance.patch(url, data); - - return response.data; - }, - - async delete(url: string) { - return instance.delete(url); - }, -}; +/* eslint-disable @typescript-eslint/no-explicit-any */ +import axios from 'axios'; + +const instance = axios.create({ + baseURL: 'https://fe-aug23-team4-nice-gadgets-api.onrender.com', +}); + +function wait(delay: number) { + return new Promise((resolve) => setTimeout(resolve, delay)); +} + +export const axiosClient = { + async get(url: string) { + await wait(500); + const response = await instance.get(url); + + return response.data; + }, + + async post(url: string, data: any) { + const response = await instance.post(url, data); + + return response.data; + }, + + async patch(url: string, data: any) { + const response = await instance.patch(url, data); + + return response.data; + }, + + async delete(url: string) { + return instance.delete(url); + }, +}; diff --git a/src/utils/getPages.ts b/src/utils/getPages.ts new file mode 100644 index 0000000..faafe00 --- /dev/null +++ b/src/utils/getPages.ts @@ -0,0 +1,18 @@ +export const getPages = (totalPages: number, currentPage: number) => { + const maxPagesToShow = 5; + const pages = []; + + let startPage = currentPage; + let endPage = currentPage + (maxPagesToShow - 1); + + if (endPage > totalPages) { + endPage = totalPages; + startPage = Math.max(totalPages - (maxPagesToShow - 1), 1); + } + + for (let i = startPage; i <= endPage; i += 1) { + pages.push(i); + } + + return pages; +}; diff --git a/src/utils/getSearchWith.ts b/src/utils/getSearchWith.ts new file mode 100644 index 0000000..34e3093 --- /dev/null +++ b/src/utils/getSearchWith.ts @@ -0,0 +1,27 @@ +import { SearchParams } from '../types/SearchParams'; + +export function getSearchWith( + currentParams: URLSearchParams, + paramsToUpdate: SearchParams, +): string { + const newParams = new URLSearchParams( + currentParams.toString(), + ); + + Object.entries(paramsToUpdate) + .forEach(([key, value]) => { + if (value === null) { + newParams.delete(key); + } else if (Array.isArray(value)) { + newParams.delete(key); + + value.forEach(part => { + newParams.append(key, part); + }); + } else { + newParams.set(key, value.toString()); + } + }); + + return newParams.toString(); +} diff --git a/src/utils/getSortedProducts.ts b/src/utils/getSortedProducts.ts new file mode 100644 index 0000000..8cfd612 --- /dev/null +++ b/src/utils/getSortedProducts.ts @@ -0,0 +1,24 @@ +import { Phone } from '../types/Phone'; + +export const getSortedProducts = ( + recievedProducts: Phone[], + params: URLSearchParams, +) => { + const sortBy = params.get('sort') || ''; + const visibleProducts = [...recievedProducts]; + + visibleProducts.sort((product1: Phone, product2: Phone) => { + switch (sortBy) { + case 'title': + return product1.name.localeCompare(product2.name); + case 'age': + return product2.year - product1.year; + case 'price': + return product1.price - product2.price; + default: + return 0; + } + }); + + return visibleProducts; +}; diff --git a/src/utils/localClient.ts b/src/utils/localClient.ts index 22bfd39..0c0ad02 100644 --- a/src/utils/localClient.ts +++ b/src/utils/localClient.ts @@ -1,55 +1,55 @@ -import { Product } from '../types/Product'; - -export const localClient = { - read: (key: string) => { - const data = window.localStorage.getItem(key); - - try { - return data && JSON.parse(data); - } catch (error) { - return null; - } - }, - - write: (key: string, data: unknown) => { - window.localStorage.setItem(key, JSON.stringify(data, null, 2)); - }, - - add: (key: string, data: unknown) => { - const existingData = localClient.read(key); - - existingData.push(data); - window.localStorage.setItem(key, JSON.stringify(existingData, null, 2)); - }, - - update: (key: string, data: Product) => { - const existingData = localClient.read(key); - const newData = existingData.map((item: Product) => { - return item.id === data.id ? data : item; - }); - - window.localStorage.setItem(key, JSON.stringify(newData, null, 2)); - }, - - delete: (key: string, id: number) => { - const existingData = localClient.read(key); - const newData = existingData.filter((item: Product) => item.id !== id); - - window.localStorage.setItem(key, JSON.stringify(newData, null, 2)); - }, - - find: (key: string, id: number) => { - const existingData: Product[] = localClient.read(key); - - return !!existingData.find((item: Product) => item.id === id); - }, - - // eslint-disable-next-line consistent-return - init: (key: string, initialData: unknown) => { - if (!localClient.read(key)) { - localClient.write(key, initialData); - - return localClient.read(key); - } - }, -}; +import { Product } from '../types/Product'; + +export const localClient = { + read: (key: string) => { + const data = window.localStorage.getItem(key); + + try { + return data && JSON.parse(data); + } catch (error) { + return null; + } + }, + + write: (key: string, data: unknown) => { + window.localStorage.setItem(key, JSON.stringify(data, null, 2)); + }, + + add: (key: string, data: unknown) => { + const existingData = localClient.read(key); + + existingData.push(data); + window.localStorage.setItem(key, JSON.stringify(existingData, null, 2)); + }, + + update: (key: string, data: Product) => { + const existingData = localClient.read(key); + const newData = existingData.map((item: Product) => { + return item.id === data.id ? data : item; + }); + + window.localStorage.setItem(key, JSON.stringify(newData, null, 2)); + }, + + delete: (key: string, id: number) => { + const existingData = localClient.read(key); + const newData = existingData.filter((item: Product) => item.id !== id); + + window.localStorage.setItem(key, JSON.stringify(newData, null, 2)); + }, + + find: (key: string, id: number) => { + const existingData: Product[] = localClient.read(key); + + return !!existingData.find((item: Product) => item.id === id); + }, + + // eslint-disable-next-line consistent-return, @typescript-eslint/no-explicit-any + init: (key: string, initialData: any) => { + if (!localClient.read(key)) { + localClient.write(key, initialData); + + return localClient.read(key); + } + }, +};