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/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/App.scss b/src/App.scss index a44c5a4..f87c10c 100644 --- a/src/App.scss +++ b/src/App.scss @@ -12,3 +12,4 @@ font-family: 'Mont', sans-serif; scroll-behavior: smooth; } + diff --git a/src/Root.tsx b/src/Root.tsx index bd7381e..e587031 100644 --- a/src/Root.tsx +++ b/src/Root.tsx @@ -6,13 +6,20 @@ 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 { FavoritesPage } from './modules/FavoritesPage'; import { CartPage } from './modules/CartPage'; import { NotFoundPage } from './modules/NotFoundPage'; -import { TabletsPage } from './modules/TabletsPage'; -import { AccessoriesPage } from './modules/AccessoriesPage'; +import { PageInProgress } from './modules/PageInProgress'; +import { FavoritesPage } from './modules/FavoritesPage'; +import { + getProductsWithSearchParams as loadPhones, + getProductAmount as loadPhonesAmount, +} from './api/service'; + +const MOBILE_TITLE = 'Mobile phones'; +// const TABLETS_TITLE = 'Tablet'; +// const ACCESSOTIES_TITLE = 'Accessories'; export const Root = () => ( @@ -22,13 +29,39 @@ export const Root = () => ( } /> } /> - } /> + + )} + /> } /> - } /> - } /> + {/* } + /> */} + {/* } + /> */} } /> } /> + } /> } /> diff --git a/src/api/service.ts b/src/api/service.ts index 989dff5..9f81535 100644 --- a/src/api/service.ts +++ b/src/api/service.ts @@ -1,39 +1,42 @@ import { axiosClient } from '../utils/axiosClient'; -import { Phone } from '../types/Phone'; -import { SortBy } from '../types/SortBy'; -import { PhoneDetail } from '../types/PhoneDetail'; +import { Categories, EndPoints, ProductsAmount } from '../types/Enums'; +import { Product, ProductDetail, QueryParams } from '../types/Product'; -export const getPhones = () => { - return axiosClient.get('/phones'); -}; - -export const getPhonesAmount = () => { - return axiosClient.get('/phones/amount'); +export const getProductAmount = (category?: Categories) => { + return axiosClient.get( + `/${EndPoints.Product}/amount/${category}`, + ); }; -export const getPhonesWithSearchParams = ( - sortBy: SortBy, - perPage: number, - page: number, +export const getProductsWithSearchParams = ( + endPoints: EndPoints, + params?: QueryParams, ) => { - return axiosClient.get( - `/phones?sortBy=${sortBy}&perPage=${perPage}&page=${page}`, + const { + page = 1, perPage = 8, sort = 'discount', order = 'asc', + } = params || {}; + + return axiosClient.get( + `/${endPoints}?page=${page}&perPage=${perPage}&sort=${sort}&order=${order}`, ); }; -export const getNewestPhones = () => { - return axiosClient.get('/phones/new'); +export const getNewestProducts = () => { + return axiosClient.get(`/${EndPoints.Product}/new`); }; -export const getPhonesWithDiscount = () => { - return axiosClient.get('/phones/discount'); +export const getProductsWithDiscount = () => { + return axiosClient.get(`/${EndPoints.Product}/discount`); }; -export const getPhoneDetail = (phoneId: string) => { - return axiosClient.get(`/phones/${phoneId}`); +export const getProductDetail = (endPoint: EndPoints, itemId: string) => { + return axiosClient.get(`/${endPoint}/${itemId}`); }; -export const getRecommendedPhones = (phoneId: string) => { - return axiosClient.get(`/phones/${phoneId}/recommended`); +export const getRecommendedProducts = ( + endPoint: EndPoints, + phoneId: string, +) => { + return axiosClient.get(`/${endPoint}/${phoneId}/recommended`); }; 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.tsx b/src/modules/CartPage/CartPage.tsx index e5ed439..aa3cf4d 100644 --- a/src/modules/CartPage/CartPage.tsx +++ b/src/modules/CartPage/CartPage.tsx @@ -1,35 +1,40 @@ -import React, { useEffect, useMemo, useState } from 'react'; +import React, { useMemo, useState } from 'react'; import cn from 'classnames'; import { CartItem } from './components/CartItem'; -import { Phone } from '../../types/Phone'; -import { getNewestPhones } from '../../api/service'; import styles from './CartPage.module.scss'; import { useAppSelector } from '../../store/hooks'; import { BackButton } from '../shared/BackButton'; +import { Modal } from './components/Modal'; export const CartPage: React.FC = () => { - const [phones, setPhones] = useState([]); const { isDarkTheme } = useAppSelector((state) => state.theme); + const { cart } = useAppSelector(state => state.cart); + const [isModalOpen, setModalOpen] = useState(false); - const total = useMemo(() => { - return phones.reduce((accumulator, phone) => accumulator + phone.price, 0); - }, [phones]); + const openModal = () => { + setModalOpen(true); + }; - useEffect(() => { - getNewestPhones() - .then(setPhones) - // eslint-disable-next-line - .catch((error) => console.error('Error fetching data:', error)); - }, []); + const closeModal = () => { + setModalOpen(false); + }; + + const totalPrice = useMemo(() => { + return cart.reduce((acc, phone) => acc + phone.price * phone.amount, 0); + }, [cart]); + + const totalItems = useMemo(() => { + return cart.reduce((acc, phone) => acc + phone.amount, 0); + }, [cart]); return ( +
-

{
- {phones.map((phone) => ( - + {cart.map((phone) => ( + ))}
@@ -55,7 +63,7 @@ export const CartPage: React.FC = () => { [styles.contentDark]: isDarkTheme, })} > - {`$ ${total}`} + {`$ ${totalPrice}`}

{ [styles.amountContent__DARK]: isDarkTheme, })} > - {`Total for ${phones.length} items`} + {`Total for ${totalItems} items`}

{ +

diff --git a/src/modules/CartPage/components/CartItem/CartItem.module.scss b/src/modules/CartPage/components/CartItem/CartItem.module.scss index d188919..a4cf630 100644 --- a/src/modules/CartPage/components/CartItem/CartItem.module.scss +++ b/src/modules/CartPage/components/CartItem/CartItem.module.scss @@ -56,6 +56,7 @@ display: flex; gap: 13px; align-items: center; + cursor: pointer; } .content { @@ -85,9 +86,10 @@ } } -.closeButton { +.deleteButton { border: none; background-color: transparent; + cursor: pointer; } .changeAmountButton { diff --git a/src/modules/CartPage/components/CartItem/CartItem.tsx b/src/modules/CartPage/components/CartItem/CartItem.tsx index d95ea6d..2cb46f7 100644 --- a/src/modules/CartPage/components/CartItem/CartItem.tsx +++ b/src/modules/CartPage/components/CartItem/CartItem.tsx @@ -1,8 +1,7 @@ import React from 'react'; - import cn from 'classnames'; -import { useAppSelector } from '../../../../store/hooks'; -import { Phone } from '../../../../types/Phone'; +import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; +import { actions as cartActions } from '../../../../store/reducers/cartSlice'; import styles from './CartItem.module.scss'; import { ReactComponent as CloseIcon } from '../../../../static/icons/close_icon.svg'; @@ -10,14 +9,15 @@ import { ReactComponent as MinusIcon } from '../../../../static/icons/minus_icon.svg'; import { ReactComponent as PlusIcon } from '../../../../static/icons/plus_icon.svg'; +import { ProductWithAmount } from '../../../../types/Product'; type Props = { - phone: Phone | null; + phone: ProductWithAmount; }; export const CartItem: React.FC = ({ phone }) => { const { isDarkTheme } = useAppSelector((state) => state.theme); - const isMinusDisabled = true; + const dispatch = useAppDispatch(); const getIconColor = (isTheme: boolean, isDisabled: boolean) => { if (isTheme) { @@ -37,9 +37,6 @@ export const CartItem: React.FC = ({ phone }) => { return isDisabled ? {} : styles.changeAmountButton__ACTIVE; }; - const minusIconColor = getIconColor(isDarkTheme, isMinusDisabled); - const minusClass = getClass(isDarkTheme, isMinusDisabled); - return (
= ({ phone }) => { })} >
- {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */} -
{phone?.name} = ({ phone }) => {
- {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */}

- 1 + {phone?.amount}

- {/* eslint-disable-next-line jsx-a11y/control-has-associated-label */} @@ -108,7 +114,7 @@ export const CartItem: React.FC = ({ phone }) => { [styles.darkContent]: isDarkTheme, })} > - {`$ ${phone?.price}`} + {phone ? `$ ${phone.price * phone.amount}` : ''}

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/CartPage/components/Modal/Modal.module.scss b/src/modules/CartPage/components/Modal/Modal.module.scss new file mode 100644 index 0000000..44a1ad7 --- /dev/null +++ b/src/modules/CartPage/components/Modal/Modal.module.scss @@ -0,0 +1,79 @@ +@import '../../../../styles/utils/variables/colors'; +@import '../../../../styles/utils/mixins/mixin-typography'; +@import '../../../../styles/utils/mixins/mixin-media'; + +.modal { + position: fixed; + top: 0; + left: 0; + width: 100%; + height: 100%; + background-color: rgba(15, 15, 17, 0.7); + display: flex; + justify-content: center; + align-items: center; + z-index: 1000; +} + +.smile { + width: 50px; + height: 50px; +} + +.content { + width: 50vw; + height: 30vh; + display: flex; + flex-direction: column; + align-items: center; + justify-content: space-around; + text-align: center; + color: $color__primary; + background-color: $color__elements; + padding: 40px; + border-radius: 8px; + z-index: 1001; + @include h2-typography; + + &__DARK { + color: $color__dark-theme__white; + background-color: $color__dark-theme__surface-2; + border-radius: 0; + } +} + +.buttonsContainer { + display: flex; + width: 100%; + justify-content: space-around; +} + +.modalButton { + width: 40%; + padding-block: 0.5em; + text-align: center; + border: none; + border-radius: 8px; + color: $color__white; + @include buttons-typography; + background-color: $color__accent; + cursor: pointer; + + &__DARK { + color:$color__dark-theme__white; + background-color: $color__dark-theme__accent; + border-radius: 0; + } + + @include onTablet { + width: 30%; + } + + @media (min-width: 900px) { + padding-block: 1em; + } +} + +.modalButton:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.40); +} diff --git a/src/modules/CartPage/components/Modal/Modal.tsx b/src/modules/CartPage/components/Modal/Modal.tsx new file mode 100644 index 0000000..f4f45f4 --- /dev/null +++ b/src/modules/CartPage/components/Modal/Modal.tsx @@ -0,0 +1,65 @@ +import React from 'react'; +import cn from 'classnames'; +import { useAppDispatch, useAppSelector } from '../../../../store/hooks'; +import { actions as cartActions } from '../../../../store/reducers/cartSlice'; +import styles from './Modal.module.scss'; +import smile from './img/thinking-smile.png'; + +type Props = { + isOpen: boolean; + onClose: () => void; +}; + +export const Modal: React.FC = ({ + isOpen, + onClose, +}) => { + const dispatch = useAppDispatch(); + const { isDarkTheme } = useAppSelector((state) => state.theme); + + if (!isOpen) { + return null; + } + + const clearCartHandler = () => { + dispatch(cartActions.clear()); + onClose(); + }; + + return ( +
+
+

Would you like to place an order?

+ thinking-smile +
+ + +
+
+
+ ); +}; diff --git a/src/modules/CartPage/components/Modal/img/thinking-smile.png b/src/modules/CartPage/components/Modal/img/thinking-smile.png new file mode 100644 index 0000000..554087d Binary files /dev/null and b/src/modules/CartPage/components/Modal/img/thinking-smile.png differ diff --git a/src/modules/CartPage/components/Modal/index.ts b/src/modules/CartPage/components/Modal/index.ts new file mode 100644 index 0000000..cb89ee1 --- /dev/null +++ b/src/modules/CartPage/components/Modal/index.ts @@ -0,0 +1 @@ +export * from './Modal'; diff --git a/src/modules/FavoritesPage/FavoritesPage.module.scss b/src/modules/FavoritesPage/FavoritesPage.module.scss index 8a79c59..c0390c7 100644 --- a/src/modules/FavoritesPage/FavoritesPage.module.scss +++ b/src/modules/FavoritesPage/FavoritesPage.module.scss @@ -1,67 +1,53 @@ -@import '../../styles/utils/variables/colors'; -@import '../../styles/utils/mixins/mixin-media'; -@import '../../styles/blocks/grid'; - -.favouritesPage { - padding: 1rem 1.5625rem 3.5rem; - background-color: $color__hover-and-bg; - - @include onTablet { - padding-inline: 1.5rem; - padding-bottom: 4rem; - } - - @include onDesktop { - padding-inline: 2rem; - padding-bottom: 5rem; - } - - &__top { - display: flex; - gap: 8px; - - &__content { - color: $color__secondary; - font-size: 12px; - font-weight: 700; - line-height: normal; - } - } - - &__title { - margin-top: 1.5rem; - color: $color__primary; - font-size: 32px; - font-weight: 800; - line-height: 128%; - letter-spacing: -0.32px; - - @include onTablet { - font-size: 48px; - line-height: 116%; - letter-spacing: -0.48px; - } - - &__dark { - color: $color__dark-theme__white; - } -} - - &__content { - margin-top: 0.5rem; - margin-bottom: 2rem; - max-width: 1200px; - color: $color__secondary; - font-size: 14px; - font-weight: 600; - line-height: 150%; - - @include onTablet { - margin-bottom: 2.5rem; - } - } -} - -.favouritesPageDark { - background-color: #0F1121; -} +@import '../../styles/utils/variables/colors'; +@import '../../styles/utils/mixins/mixin-media'; +@import '../../styles/blocks/grid'; + +.favouritesPage { + padding-top: 1.5rem; + padding-bottom: 3.5rem; + + @include onTablet { + padding-bottom: 4rem; + } + + @include onDesktop { + padding-bottom: 5rem; + } + + &__title { + margin-top: 1.5rem; + color: $color__primary; + font-size: 32px; + font-weight: 800; + line-height: 128%; + letter-spacing: -0.32px; + + @include onTablet { + font-size: 48px; + line-height: 116%; + letter-spacing: -0.48px; + } + + &__dark { + color: $color__dark-theme__white; + } +} + + &__content { + margin-top: 0.5rem; + margin-bottom: 2rem; + max-width: 1200px; + color: $color__secondary; + font-size: 14px; + font-weight: 600; + line-height: 150%; + + @include onTablet { + margin-bottom: 2.5rem; + } + } +} + +.favouritesPageDark { + background-color: #0F1121; +} diff --git a/src/modules/FavoritesPage/FavoritesPage.tsx b/src/modules/FavoritesPage/FavoritesPage.tsx index 3fa8051..f55f156 100644 --- a/src/modules/FavoritesPage/FavoritesPage.tsx +++ b/src/modules/FavoritesPage/FavoritesPage.tsx @@ -1,76 +1,33 @@ -import React from 'react'; -import { Link } from 'react-router-dom'; -import cn from 'classnames'; -import { useAppSelector } from '../../store/hooks'; -import styles from './FavoritesPage.module.scss'; -import arrowIcon from '../../static/icons/arrow-right_icon.svg'; -import { Loader } from '../shared/Loader'; -// import { Phone } from '../../types/Phone'; -import { ProductList } from '../shared/ProductList'; -import { HomeIcon } from './HomeIcon'; -// import { getNewestPhones } from '../../api/service'; - -export const FavoritesPage: React.FC = () => { - // const [phones, setPhones] = useState([]); - - // useEffect(() => { - // const fetchData = async () => { - // try { - // const newPhones = await getNewestPhones(); - - // setPhones(newPhones); - // } catch (error) { - // // eslint-disable-next-line - // console.error('Error fetching data:', error); - // } - // }; - - // fetchData(); - // }, []); - - // const favourites: Phone[] = phones; - // const isThemeDark = false; - const { isDarkTheme } = useAppSelector((state) => state.theme); - const { favorites } = useAppSelector(state => state.favorites); - const isLoading = false; - - return ( -
- {isLoading ? ( - - ) : ( - <> -
- - - - - arrow-icon - {/* I will leave icon coz we will make a component later */} -

Favourites

-
-

- Favourites -

-

- {`${favorites.length || 0} items`} -

- - - - )} -
- ); -}; +import React from 'react'; +import cn from 'classnames'; +import { useAppSelector } from '../../store/hooks'; +import styles from './FavoritesPage.module.scss'; +import { ProductList } from '../shared/ProductList'; +import { Breadcrumbs } from '../shared/Breadcrumbs'; + +export const FavoritesPage: React.FC = () => { + const { isDarkTheme } = useAppSelector((state) => state.theme); + const { favorites } = useAppSelector(state => state.favorites); + + return ( +
+ +

+ Favourites +

+

+ {`${favorites.length || 0} items`} +

+ + +
+ ); +}; 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 67d505f..1a5188e 100644 --- a/src/modules/HomePage/HomePage.tsx +++ b/src/modules/HomePage/HomePage.tsx @@ -8,20 +8,18 @@ import { MainTitle } from './componets/MainTitle'; import { MainSlider } from './componets/MainSlider'; import { ProductSlider } from '../shared/ProductSlider'; import { useAppSelector } from '../../store/hooks'; -import { Phone } from '../../types/Phone'; -import { getNewestPhones, getPhonesWithDiscount } from '../../api/service'; +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 [newPhones, setNewPhones] = useState([]); + const [phonesWithDiscount, setPhonesWithDiscount] = useState([]); const { isDarkTheme } = useAppSelector((state) => state.theme); useEffect(() => { - getNewestPhones() - .then(setNewPhones); + getNewestProducts().then(setNewPhones); - getPhonesWithDiscount() - .then(setPhonesWithDiscount); + getProductsWithDiscount().then(setPhonesWithDiscount); }, []); return ( 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/ShopByCategory.tsx b/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.tsx index d177daa..f92f968 100644 --- a/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.tsx +++ b/src/modules/HomePage/componets/ShopByCategory/ShopByCategory.tsx @@ -1,152 +1,143 @@ -import React, { useEffect, useState } from 'react'; -import { Link } from 'react-router-dom'; -import cn from 'classnames'; -import { useAppSelector } from '../../../../store/hooks'; -import styles from './ShopByCategory.module.scss'; -import categoryPhone from '../../../../static/banners/category-phones.png'; -import categoryTablet from '../../../../static/banners/category-tablets.png'; -import categoryAccessories - from '../../../../static/banners/category-accessories.png'; -import { getPhonesAmount } from '../../../../api/service'; - -interface ProductsAmount { - phones: number; - tablets: number; - accessories: number; -} - -export const ShopByCategory: React.FC = () => { - const { isDarkTheme } = useAppSelector((state) => state.theme); - const [productsAmount, setProductsAmount] = useState({ - phones: 0, - tablets: 0, - accessories: 0, - }); - - useEffect(() => { - getPhonesAmount() - .then((amount) => setProductsAmount(prev => ({ - ...prev, - phones: amount, - }))); - }, []); - - return ( -
-

- Shop by category -

- -
-
- -
- mobile-phones -
-

- Mobile phones -

- -

- {`${productsAmount.phones} models`} -

- -
- -
- -
- tablets -
-

- Tablets -

-

- {`${productsAmount.tablets} models`} -

- -
- -
- -
- accessories -
-

- Accessories -

-

- {`${productsAmount.accessories} models`} -

- -
-
-
- ); -}; +import React, { useEffect, useState } from 'react'; +import { Link } from 'react-router-dom'; +import cn from 'classnames'; +import { useAppSelector } from '../../../../store/hooks'; +import styles from './ShopByCategory.module.scss'; +import categoryPhone from '../../../../static/banners/category-phones.png'; +import categoryTablet from '../../../../static/banners/category-tablets.png'; +import categoryAccessories + from '../../../../static/banners/category-accessories.png'; +import { getProductAmount } from '../../../../api/service'; +import { Categories, ProductsAmount } from '../../../../types/Enums'; + +export const ShopByCategory: React.FC = () => { + const { isDarkTheme } = useAppSelector((state) => state.theme); + const [productsAmount, setProductsAmount] = useState({ + phones: 0, + tablets: 0, + accessories: 0, + }); + + useEffect(() => { + getProductAmount(Categories.All).then(setProductsAmount); + }, []); + + return ( +
+

+ Shop by category +

+ +
+
+ +
+ mobile-phones +
+

+ Mobile phones +

+ +

+ {`${productsAmount.phones} models`} +

+ +
+ +
+ +
+ tablets +
+

+ Tablets +

+

+ {`${productsAmount.tablets} models`} +

+ +
+ +
+ +
+ accessories +
+

+ Accessories +

+

+ {`${productsAmount.accessories} models`} +

+ +
+
+
+ ); +}; 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/NotFoundPage/NotFoundPage.module.scss b/src/modules/NotFoundPage/NotFoundPage.module.scss index ce7e13c..6bd5918 100644 --- a/src/modules/NotFoundPage/NotFoundPage.module.scss +++ b/src/modules/NotFoundPage/NotFoundPage.module.scss @@ -2,23 +2,29 @@ @import '../../styles/utils/mixins/mixin-media'; @import '../../styles/utils/mixins/mixin-typography'; -$footerAndHeaderHeight: 144px; -$footerAndHeaderHeightTablet: 305px; +$footerAndHeaderHeighTablet: 144px; +$footerAndHeaderHeightMobile: 257px; $footerAndHeaderHeightDesktop: 160px; .container { - height: calc(100vh - #{$footerAndHeaderHeight}); + overflow-y: hidden; display: flex; flex-direction: column; justify-content: center; align-items: center; gap: 10px; + padding: 0 20px; @include onMobile { - height: calc(100vh - #{$footerAndHeaderHeightTablet}); + height: calc(100vh - #{$footerAndHeaderHeightMobile}); gap: 5px; } + @include onTablet { + height: calc(100vh - #{$footerAndHeaderHeighTablet}); + } + + @include onDesktop { height: calc(100vh - #{$footerAndHeaderHeightDesktop}); } @@ -33,13 +39,20 @@ $footerAndHeaderHeightDesktop: 160px; } } -.error_message { - color: $color__secondary; - @include bodyText-typography; +.headline { + @include h2-typography; &_dark { - @include bodyText-typography; + @include h2-typography; color: $color__dark-theme__white; + + @include onMobile { + font-size: 24px; + } + } + + @include onMobile { + font-size: 24px; } } @@ -50,6 +63,14 @@ $footerAndHeaderHeightDesktop: 160px; &_dark { @include bodyText-typography; color: $color__dark-theme__white; + + @include onMobile { + display: none; + } + } + + @include onMobile { + display: none; } @include onMobile { @@ -57,56 +78,76 @@ $footerAndHeaderHeightDesktop: 160px; } } -.headline { - @include h2-typography; +.error_message { + text-align: center; + color: $color__secondary; + @include bodyText-typography; &_dark { - @include h2-typography; + @include bodyText-typography; color: $color__dark-theme__white; } - - @include onMobile { - font-size: 24px; - } } .button { - margin-top: 12px; - cursor: pointer; + display: flex; + justify-content: center; + align-items: center; padding: 15px; - border-radius: 15px; - border: 2px solid #3B3E4A; - transition: scale 0.05s ease; - border-radius: 12px; + + border-radius: 8px; + border: none; + background: $color__accent; + margin-top: 12px; + + color: $color__white; @include buttons-typography; text-decoration: none; - color: $color__primary; + + transition: scale 0.05s ease; &:hover { + box-shadow: 0px 3px 13px 0px rgba(23, 32, 49, 0.4); scale: 1.1; } &_dark { - margin-top: 12px; - cursor: pointer; - padding: 15px; - border-radius: 15px; - border: 2px solid $color__dark-theme__white; - transition: scale 0.05s ease; - border-radius: 12px; - @include buttons-typography; - text-decoration: none; - color: $color__dark-theme__white; - background-color: #3B3E4A; + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + + 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; - padding: 8px; - font-size: 12px; } } diff --git a/src/modules/NotFoundPage/NotFoundPage.tsx b/src/modules/NotFoundPage/NotFoundPage.tsx index 68a8aa6..72fc831 100644 --- a/src/modules/NotFoundPage/NotFoundPage.tsx +++ b/src/modules/NotFoundPage/NotFoundPage.tsx @@ -1,7 +1,8 @@ import { Link } from 'react-router-dom'; +import cn from 'classnames'; import styles from './NotFoundPage.module.scss'; -// import not_found_logo_dark from '../../static/images/not_found_logo_dark.jpg'; -// import not_found_logo from '../../static/images/not_found_logo_bright.png'; +import not_found_logo_dark from '../../static/images/not_found_logo_dark.jpg'; +import not_found_logo from '../../static/images/not_found_logo_bright.png'; import { useAppSelector } from '../../store/hooks'; export const NotFoundPage: React.FC = () => { @@ -10,25 +11,34 @@ export const NotFoundPage: React.FC = () => { return (
page_not_found_logo -

+

Ooops!

-

+

Error 404 - Page Not Found.

-

The page you are looking for could not be found.

- + Bring me back
diff --git a/src/modules/PageInProgress/PageInProgress.module.scss b/src/modules/PageInProgress/PageInProgress.module.scss new file mode 100644 index 0000000..912f5ef --- /dev/null +++ b/src/modules/PageInProgress/PageInProgress.module.scss @@ -0,0 +1,211 @@ +@import '../../styles/utils/mixins/mixin-media'; +@import '../../styles/utils/mixins/mixin-typography'; + +$footerAndHeaderHeightTablet: 144px; +$footerAndHeaderHeightMobile: 257px; +$footerAndHeaderHeightDesktop: 160px; + +.container { + overflow-y: hidden; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + gap: 12px; + padding: 0 20px; + + @include onMobile { + height: calc(100vh - #{$footerAndHeaderHeightMobile}); + } + + @include onTablet { + height: calc(100vh - #{$footerAndHeaderHeightTablet}); + } + + @include onDesktop { + height: calc(100vh - #{$footerAndHeaderHeightDesktop}); + } +} + +.headline { + @include h1-typography; + + &_dark { + @include h1-typography; + color: $color__dark-theme__white; + } + + @include onMobile { + font-size: 20px; + } +} + +.subtitle { + text-align: center; + color: $color__secondary; + @include bodyText-typography; + + &_dark { + @include bodyText-typography; + color: $color__dark-theme__white; + } +} + +.page_in_progress_icon { + height: 170px; + + &_dark { + height: 170px; + filter: invert(89%); + + @include onMobile { + height: 90px; + } + } + + @include onMobile { + height: 90px; + } +} + +.button_container { + display: flex; + margin-top: 20px; + gap: 30px; + + @include onMobile { + margin-top: 0; + flex-direction: column; + align-items: center; + gap: 0; + } +} + +.button_git { + 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; + + 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; + } +} + +.button_back { + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + min-width: 130px; + + border-radius: 8px; + 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; + } + + &_dark { + display: flex; + justify-content: center; + align-items: center; + padding: 15px; + + border-radius: 0px; + 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; + background: $color__accent; + } + + @include onMobile { + scale: 0.8; + &:hover { + scale: 0.9; + } + margin-top: 5px; + } + } +} diff --git a/src/modules/PageInProgress/PageInProgress.tsx b/src/modules/PageInProgress/PageInProgress.tsx new file mode 100644 index 0000000..b12fdb9 --- /dev/null +++ b/src/modules/PageInProgress/PageInProgress.tsx @@ -0,0 +1,55 @@ +import React from 'react'; +import { Link } from 'react-router-dom'; +import cn from 'classnames'; +import styles from './PageInProgress.module.scss'; +import { useAppSelector } from '../../store/hooks'; +import cog_icon from '../../static/icons/cog-icon.svg'; + +export const PageInProgress: React.FC = () => { + const { isDarkTheme } = useAppSelector(state => state.theme); + + return ( +
+ page_in_progress_icon +

+ Coming soon! +

+ {/* eslint-disable */} +

+ This section is currently in development. Please return later or follow us on Github to get notified of updates. +

+ {/* eslint-enable */} +
+ + Bring me back + + + Our Github page + +
+
+ ); +}; diff --git a/src/modules/PageInProgress/index.ts b/src/modules/PageInProgress/index.ts new file mode 100644 index 0000000..fe4592a --- /dev/null +++ b/src/modules/PageInProgress/index.ts @@ -0,0 +1 @@ +export * from './PageInProgress'; 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 9521eb6..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 { useSearchParams } from 'react-router-dom'; +import classNames from 'classnames'; -// import styles from './PhonesPage.module.scss'; - -import { Phone } from '../../types/Phone'; +import styles from './PhonesPage.module.scss'; import { Breadcrumbs } from '../shared/Breadcrumbs'; import { ProductList } from '../shared/ProductList'; -import { getPhones } from '../../api/service'; +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 PhonesPage: React.FC = () => { - const [phones, setPhones] = useState([]); +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); - getPhones() - .then(setPhones) + getProductsWithSearchParams(EndPoints.Phones) + .then(setProducts) .finally(() => setIsLoading(false)); - }, []); + }, [sortByEnum, perPageString, currentPageNumber, error, loadData]); - return ( - <> - + // useEffect(() => { + // setIsLoading(true); + // loadData( + // perPageString, + // currentPageNumber, + // sortByEnum, + // ) + // .then(setProducts) + // .catch(setError) + // .finally(() => setIsLoading(false)); + // }, [sortByEnum, perPageString, currentPageNumber, error, loadData]); -

Mobile phones

-

N models

+ return ( +
+

+ +

- +

+ {title} +

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

+ {error} +

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

+ {`${totalAmount} modeles`} +

+ ) : ( +

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

+ )} + +
+ +
+ +
+ +
+ + {showPagination()} + + )} +
); }; 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/AddToCart.tsx b/src/modules/shared/AddToCart/AddToCart.tsx index aa189e9..b663fba 100644 --- a/src/modules/shared/AddToCart/AddToCart.tsx +++ b/src/modules/shared/AddToCart/AddToCart.tsx @@ -4,10 +4,10 @@ import cn from 'classnames'; import styles from './AddToCart.module.scss'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; import { actions as cartActions } from '../../../store/reducers/cartSlice'; -import { Phone } from '../../../types/Phone'; +import { Product } from '../../../types/Product'; type Props = { - productItem: Phone; + productItem: Product; }; export const AddToCart: React.FC = ({ productItem }) => { @@ -20,7 +20,7 @@ export const AddToCart: React.FC = ({ productItem }) => { !!cart.find(cartItem => cartItem.id === productItem.id), ); - const handleAddToCart = (product: Phone) => { + const handleAddToCart = (product: Product) => { if (!isSelected) { dispatch(cartActions.add(product)); setIsSelected(true); 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/AddToFavourites.tsx b/src/modules/shared/AddToFavourites/AddToFavourites.tsx index 8fabced..c022984 100644 --- a/src/modules/shared/AddToFavourites/AddToFavourites.tsx +++ b/src/modules/shared/AddToFavourites/AddToFavourites.tsx @@ -4,10 +4,10 @@ import styles from './AddToFavourites.module.scss'; import { useAppDispatch, useAppSelector } from '../../../store/hooks'; import { actions as favouritiesActions } from '../../../store/reducers/favoritesSlice'; -import { Phone } from '../../../types/Phone'; +import { Product } from '../../../types/Product'; type Props = { - productItem: Phone; + productItem: Product; }; export const AddToFavourites: React.FC = ({ productItem }) => { @@ -19,7 +19,7 @@ export const AddToFavourites: React.FC = ({ productItem }) => { !!favorites.find(favorite => favorite.id === productItem.id), ); - const handleFavouritiesButton = (product: Phone) => { + const handleFavouritiesButton = (product: Product) => { if (isSelected) { dispatch(favouritiesActions.remove(product.id)); setIsSelected(false); 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/BackButton.module.scss b/src/modules/shared/BackButton/BackButton.module.scss index c698a50..32baaa6 100644 --- a/src/modules/shared/BackButton/BackButton.module.scss +++ b/src/modules/shared/BackButton/BackButton.module.scss @@ -11,6 +11,7 @@ .backButtonText { color: $color__secondary; + font-family: 'Mont'; @include smallText-typography; padding-right: 15px; 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 e69de29..e226bd8 100644 --- a/src/modules/shared/Breadcrumbs/Breadcrumbs.module.scss +++ b/src/modules/shared/Breadcrumbs/Breadcrumbs.module.scss @@ -0,0 +1,34 @@ +@import '../../../styles/utils/variables/colors'; + +.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; + + &__prev { + color: $color__primary; + } + + &__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 983766e..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'; +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/Footer.tsx b/src/modules/shared/Footer/Footer.tsx index 4dc50dd..a171928 100644 --- a/src/modules/shared/Footer/Footer.tsx +++ b/src/modules/shared/Footer/Footer.tsx @@ -40,11 +40,12 @@ export const Footer: React.FC = () => { className={cn([styles.nav_text], { [styles.nav_text__dark]: isDarkTheme, })} + target="_blank" > Github { Contacts {
+ Back to Top 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..d89e67d 100644 --- a/src/modules/shared/Loader/Loader.tsx +++ b/src/modules/shared/Loader/Loader.tsx @@ -1,9 +1,19 @@ 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/ProductCard/ProductCard.tsx b/src/modules/shared/ProductCard/ProductCard.tsx index 1614b67..a456c98 100644 --- a/src/modules/shared/ProductCard/ProductCard.tsx +++ b/src/modules/shared/ProductCard/ProductCard.tsx @@ -2,12 +2,12 @@ import { Link } from 'react-router-dom'; import cn from 'classnames'; import style from './ProductCard.module.scss'; import { useAppSelector } from '../../../store/hooks'; -import { Phone } from '../../../types/Phone'; import { AddToCart } from '../AddToCart'; import { AddToFavourites } from '../AddToFavourites'; +import { Product } from '../../../types/Product'; type Props = { - phone: Phone; + phone: Product; }; export const ProductCard: React.FC = ({ phone }) => { @@ -36,7 +36,7 @@ export const ProductCard: React.FC = ({ phone }) => { })} > {name} 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/ProductList.tsx b/src/modules/shared/ProductList/ProductList.tsx index f136dc2..c0006be 100644 --- a/src/modules/shared/ProductList/ProductList.tsx +++ b/src/modules/shared/ProductList/ProductList.tsx @@ -2,11 +2,11 @@ import React from 'react'; import styles from './ProductList.module.scss'; -import { Phone } from '../../../types/Phone'; import { ProductCard } from '../ProductCard'; +import { Product } from '../../../types/Product'; type Props = { - phones: Phone[]; + phones: Product[]; }; export const ProductList: React.FC = ({ phones }) => { 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/ProductSlider.tsx b/src/modules/shared/ProductSlider/ProductSlider.tsx index 18c82ec..6fcdaae 100644 --- a/src/modules/shared/ProductSlider/ProductSlider.tsx +++ b/src/modules/shared/ProductSlider/ProductSlider.tsx @@ -6,13 +6,13 @@ import styles from './ProductSlider.module.scss'; import './slick.scss'; import { SampleNextArrow } from './SampleNextArrow'; import { SamplePrevArrow } from './SamplePrevArrow'; -import { Phone } from '../../../types/Phone'; import { useAppSelector } from '../../../store/hooks'; +import { Product } from '../../../types/Product'; import { SlideCard } from './SlideCard'; type Props = { title: string; - phones: Phone[]; + phones: Product[]; }; export const ProductSlider: React.FC = ({ title, phones }) => { 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/SlideCard.tsx b/src/modules/shared/ProductSlider/SlideCard.tsx index c8096a3..a5d1294 100644 --- a/src/modules/shared/ProductSlider/SlideCard.tsx +++ b/src/modules/shared/ProductSlider/SlideCard.tsx @@ -2,11 +2,11 @@ import React from 'react'; import cn from 'classnames'; import { ProductCard } from '../ProductCard'; -import { Phone } from '../../../types/Phone'; +import { Product } from '../../../types/Product'; import styles from './ProductSlider.module.scss'; type Props = { - phone: Phone, + phone: Product; }; export const SlideCard: React.FC = ({ phone, ...prop }) => { 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.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..4b7fd6f 100644 --- a/src/static/banners/bannersData.js +++ b/src/static/banners/bannersData.js @@ -1,17 +1,17 @@ -export const bannersData = [ - { - id: 1, - title: 'banner-phones', - photo: '/banner-phones.png', - }, - { - id: 2, - title: 'banner-tablets', - photo: '/banner-tablets.png', - }, - { - id: 3, - title: 'banner-accessories', - photo: '/banner-accessories.png', - }, -]; +export const bannersData = [ + { + id: 1, + title: 'banner-phones', + photo: '/banner-phones.png', + }, + { + id: 2, + title: 'banner-tablets', + photo: '/banner-tablets.png', + }, + { + id: 3, + title: 'banner-accessories', + photo: '/banner-accessories.png', + }, +]; 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/cog-icon.svg b/src/static/icons/cog-icon.svg new file mode 100644 index 0000000..177771f --- /dev/null +++ b/src/static/icons/cog-icon.svg @@ -0,0 +1,4 @@ + + + + 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/cartSlice.ts b/src/store/reducers/cartSlice.ts index 26ddd15..269fa72 100644 --- a/src/store/reducers/cartSlice.ts +++ b/src/store/reducers/cartSlice.ts @@ -1,15 +1,15 @@ /* eslint-disable no-param-reassign */ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { Phone, PhoneWithAmount } from '../../types/Phone'; import { localClient } from '../../utils/localClient'; +import { Product, ProductWithAmount } from '../../types/Product'; export interface CartState { - cart: PhoneWithAmount[]; + cart: ProductWithAmount[]; } -function prepareProductForCart(phone: Phone): PhoneWithAmount { - return Object.assign(phone, { amount: 1 }); +function prepareProductForCart(product: Product): ProductWithAmount { + return Object.assign(product, { amount: 1 }); } const initialState: CartState = { @@ -20,7 +20,7 @@ const cartSlice = createSlice({ name: 'cart', initialState, reducers: { - add: (state, action: PayloadAction) => { + add: (state, action: PayloadAction) => { const preparedProduct = prepareProductForCart(action.payload); state.cart.push(preparedProduct); @@ -49,6 +49,11 @@ const cartSlice = createSlice({ } }); }, + + clear: (state) => { + state.cart = []; + localClient.write('cart', []); + }, }, }); diff --git a/src/store/reducers/favoritesSlice.ts b/src/store/reducers/favoritesSlice.ts index d582893..22d7841 100644 --- a/src/store/reducers/favoritesSlice.ts +++ b/src/store/reducers/favoritesSlice.ts @@ -1,11 +1,11 @@ /* eslint-disable no-param-reassign */ import { PayloadAction, createSlice } from '@reduxjs/toolkit'; -import { Phone } from '../../types/Phone'; import { localClient } from '../../utils/localClient'; +import { Product } from '../../types/Product'; export interface FavoritesState { - favorites: Phone[]; + favorites: Product[]; } const initialState: FavoritesState = { @@ -16,7 +16,7 @@ const favoritesSlice = createSlice({ name: 'favorites', initialState, reducers: { - add: (state, action: PayloadAction) => { + add: (state, action: PayloadAction) => { state.favorites.push(action.payload); localClient.add('favorites', action.payload); }, 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..baa4b5f 100644 --- a/src/styles/utils/variables/_colors.scss +++ b/src/styles/utils/variables/_colors.scss @@ -1,21 +1,21 @@ -// Bright theme -$color__accent: #f86800; -$color__secondary-accent: #476df4; -$color__primary: #0f0f11; -$color__secondary: #89939a; -$color__icons: #b4bdc3; -$color__elements: #e2e6e9; -$color__hover-and-bg: #fafbfc; -$color__white: #ffffff; -$color__green: #27ae60; -$color__red: #eb5757; - -// Dark theme -$color__dark-theme__secondary: #75767f; -$color__dark-theme__icons: #4a4d58; -$color__dark-theme__elements: #3b3e4a; -$color__dark-theme__surface-1: #161827; -$color__dark-theme__surface-2: #323542; -$color__dark-theme__black: #0f1121; -$color__dark-theme__white: #f1f2f9; -$color__dark-theme__accent: #905bff; +// Bright theme +$color__accent: #f86800; +$color__secondary-accent: #476df4; +$color__primary: #0f0f11; +$color__secondary: #89939a; +$color__icons: #b4bdc3; +$color__elements: #e2e6e9; +$color__hover-and-bg: #fafbfc; +$color__white: #ffffff; +$color__green: #27ae60; +$color__red: #eb5757; + +// Dark theme +$color__dark-theme__secondary: #75767f; +$color__dark-theme__icons: #4a4d58; +$color__dark-theme__elements: #3b3e4a; +$color__dark-theme__surface-1: #161827; +$color__dark-theme__surface-2: #323542; +$color__dark-theme__black: #0f1121; +$color__dark-theme__white: #f1f2f9; +$color__dark-theme__accent: #905bff; diff --git a/src/types/Enums.ts b/src/types/Enums.ts new file mode 100644 index 0000000..5982f23 --- /dev/null +++ b/src/types/Enums.ts @@ -0,0 +1,19 @@ +export enum Categories { + All = 'all', + Phones = 'phones', + Tablets = 'tablets', + Accessories = 'accessories', +} + +export enum EndPoints { + Product = 'products', + Phones = 'phones', + Tablets = 'tablets', + Accessories = 'accessories', +} + +type ProductsAmountKey = 'phones' | 'tablets' | 'accessories'; + +export type ProductsAmount = { + [key in ProductsAmountKey]: number; +}; 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/Product.ts b/src/types/Product.ts new file mode 100644 index 0000000..80db95b --- /dev/null +++ b/src/types/Product.ts @@ -0,0 +1,51 @@ +export interface Product { + id: number; + category: string; + itemId: string; + name: string; + fullPrice: number; + price: number; + screen: string; + capacity: string; + color: string; + ram: string; + year: number; + image: string; +} + +interface Description { + title: string; + text: string[]; +} + +export interface ProductDetail { + id: string; + namespaceId: string; + name: string; + capacityAvailable: string[]; + capacity: string; + priceRegular: number; + priceDiscount: number; + colorsAvailable: string[]; + color: string; + images: string[]; + description: Description[]; + screen: string; + resolution: string; + processor: string; + ram: string; + camera: string; + zoom: string; + cell: string[]; +} + +export interface QueryParams { + page: string; + perPage: string; + sort: string; + order: 'asc' | 'desc'; +} + +export interface ProductWithAmount extends Product { + amount: number; +} 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 ae8a7b2..0c0ad02 100644 --- a/src/utils/localClient.ts +++ b/src/utils/localClient.ts @@ -1,4 +1,4 @@ -import { Phone } from '../types/Phone'; +import { Product } from '../types/Product'; export const localClient = { read: (key: string) => { @@ -15,16 +15,16 @@ export const localClient = { window.localStorage.setItem(key, JSON.stringify(data, null, 2)); }, - add: (key: string, data: Phone) => { + 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: Phone) => { + update: (key: string, data: Product) => { const existingData = localClient.read(key); - const newData = existingData.map((item: Phone) => { + const newData = existingData.map((item: Product) => { return item.id === data.id ? data : item; }); @@ -33,19 +33,19 @@ export const localClient = { delete: (key: string, id: number) => { const existingData = localClient.read(key); - const newData = existingData.filter((item: Phone) => item.id !== id); + 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: Phone[] = localClient.read(key); + const existingData: Product[] = localClient.read(key); - return !!existingData.find((item: Phone) => item.id === id); + return !!existingData.find((item: Product) => item.id === id); }, - // eslint-disable-next-line consistent-return - init: (key: string, initialData: unknown) => { + // eslint-disable-next-line consistent-return, @typescript-eslint/no-explicit-any + init: (key: string, initialData: any) => { if (!localClient.read(key)) { localClient.write(key, initialData);