From a26cdf95e38f9194c39119ea363d2c27f9e9d0db Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Thu, 24 Oct 2024 13:41:08 -0400 Subject: [PATCH 01/20] Home Page Completed --- src/App.scss | 1 - src/App.tsx | 7 - .../ButtonsAddCardFav.module.scss | 44 +++++ .../ButtonsAddCardFav/ButtonsAddCardFav.tsx | 40 ++++ .../FavoriteIcon/FavoriteIcon.module.scss | 36 ++++ .../FavoriteIcon/FavoriteIcon.tsx | 33 ++++ .../ButtonsAddCardFav/FavoriteIcon/index.ts | 1 + src/components/ButtonsAddCardFav/index.ts | 1 + .../Categories/Categories.module.scss | 68 +++++++ src/components/Categories/Categories.tsx | 67 +++++++ src/components/Categories/index.ts | 1 + .../FavCarIcons/FavCarIcons.module.scss | 58 ++++++ src/components/FavCarIcons/FavCarIcons.tsx | 64 +++++++ src/components/FavCarIcons/index.ts | 1 + src/components/Footer/Footer.module.scss | 49 +++++ src/components/Footer/Footer.tsx | 44 +++++ .../FooterLinks/FooterLinks.module.scss | 16 ++ .../Footer/FooterLinks/FooterLinks.tsx | 30 +++ src/components/Footer/FooterLinks/index.ts | 1 + src/components/Footer/index.ts | 1 + src/components/GlobalProvider.tsx | 178 ++++++++++++++++++ src/components/Header/Header.module.scss | 61 ++++++ src/components/Header/Header.tsx | 38 ++++ .../MenuCloseIcons/MenuCloseIcons.module.scss | 46 +++++ .../Header/MenuCloseIcons/MenuCloseIcons.tsx | 33 ++++ src/components/Header/MenuCloseIcons/index.ts | 1 + src/components/Header/index.ts | 1 + .../HeaderLinks/HeaderLinks.module.scss | 36 ++++ src/components/HeaderLinks/HeaderLinks.tsx | 38 ++++ src/components/HeaderLinks/index.ts | 1 + src/components/Loader/Loader.scss | 26 +++ src/components/Loader/Loader.tsx | 10 + src/components/Loader/index.tsx | 1 + .../MobileMenu/MobileMenu.module.scss | 37 ++++ src/components/MobileMenu/MobileMenu.tsx | 27 +++ src/components/MobileMenu/index.ts | 1 + src/components/PhoneSlider/PhoneSlider.scss | 83 ++++++++ src/components/PhoneSlider/PhoneSlider.tsx | 99 ++++++++++ src/components/PhoneSlider/index.ts | 1 + .../PicturesSlider/PictureSlider.scss | 86 +++++++++ .../PicturesSlider/PicturesSlider.tsx | 67 +++++++ src/components/PicturesSlider/index.ts | 1 + .../ProductCard/ProductCard.module.scss | 83 ++++++++ src/components/ProductCard/ProductCard.tsx | 58 ++++++ src/components/ProductCard/index.ts | 1 + src/components/SiteLogo/SiteLogo.module.scss | 18 ++ src/components/SiteLogo/SiteLogo.tsx | 18 ++ src/components/SiteLogo/index.ts | 1 + src/icons/Close.svg | 3 + src/icons/Cursor Mouse Mice.png | Bin 0 -> 821 bytes src/icons/Home.png | Bin 0 -> 370 bytes src/icons/Logo-dark.png | Bin 0 -> 2519 bytes src/icons/Logo.png | Bin 0 -> 2587 bytes src/icons/Menu.svg | 5 + src/icons/Minus.png | Bin 0 -> 160 bytes src/icons/Plus.png | Bin 0 -> 228 bytes src/icons/Search.png | Bin 0 -> 401 bytes src/icons/Shopping bag + Counter(Cart).png | Bin 0 -> 794 bytes src/icons/arrowLeft.svg | 41 ++++ src/icons/arrowLeft_disabled.svg | 42 +++++ src/icons/arrowRight.svg | 41 ++++ src/icons/arrowRight_disabled.svg | 42 +++++ src/icons/arrowUp.svg | 3 + src/icons/cart.svg | 5 + src/icons/dark_mode.svg | 8 + src/icons/favorite.svg | 3 + src/icons/favorite_selected.svg | 3 + src/icons/icons8-dark-mode-64.png | Bin 0 -> 1593 bytes src/index.tsx | 9 +- src/modules/App/App.module.scss | 43 +++++ src/modules/App/App.tsx | 61 ++++++ src/modules/CartPage/CartPage.module.scss | 1 + src/modules/CartPage/CartPage.tsx | 3 + src/modules/CartPage/index.ts | 1 + .../FavotitePage/FavotitePage.module.scss | 1 + src/modules/FavotitePage/FavotitePage.tsx | 3 + src/modules/FavotitePage/index.ts | 1 + src/modules/HomePage/HomePage.module.scss | 80 ++++++++ src/modules/HomePage/HomePage.tsx | 56 ++++++ src/modules/HomePage/index.ts | 1 + src/modules/PhonePage/PhonePage.module.scss | 0 src/modules/PhonePage/PhonePage.tsx | 66 +++++++ src/modules/PhonePage/index.ts | 1 + src/modules/Root/Root.tsx | 64 +++++++ src/modules/Root/index.ts | 1 + src/modules/constants/index.ts | 1 + src/notes.txt | 7 + src/styles/icons.scss | 18 ++ src/styles/main.scss | 62 ++++++ src/styles/mixines.scss | 73 +++++++ src/styles/theme.scss | 24 +++ src/styles/typography.scss | 111 +++++++++++ src/styles/variables.scss | 5 + src/types/MenuItems.ts | 5 + src/types/Product.ts | 16 ++ src/types/ProductItem.ts | 21 +++ src/utils/LocalStorage.ts | 22 +++ src/utils/accessLocalStorage.ts | 28 +++ src/utils/fetch.ts | 43 +++++ src/utils/getCategoriesList.ts | 5 + src/utils/getHotDealsProducts.ts | 39 ++++ src/utils/getNewProducts.ts | 5 + src/utils/getProductById.ts | 5 + src/utils/getProductItemByID.ts | 5 + src/utils/getProducts.ts | 6 + src/utils/getProductsByCategory.ts | 8 + 106 files changed, 2701 insertions(+), 10 deletions(-) delete mode 100644 src/App.scss delete mode 100644 src/App.tsx create mode 100644 src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss create mode 100644 src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx create mode 100644 src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss create mode 100644 src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx create mode 100644 src/components/ButtonsAddCardFav/FavoriteIcon/index.ts create mode 100644 src/components/ButtonsAddCardFav/index.ts create mode 100644 src/components/Categories/Categories.module.scss create mode 100644 src/components/Categories/Categories.tsx create mode 100644 src/components/Categories/index.ts create mode 100644 src/components/FavCarIcons/FavCarIcons.module.scss create mode 100644 src/components/FavCarIcons/FavCarIcons.tsx create mode 100644 src/components/FavCarIcons/index.ts create mode 100644 src/components/Footer/Footer.module.scss create mode 100644 src/components/Footer/Footer.tsx create mode 100644 src/components/Footer/FooterLinks/FooterLinks.module.scss create mode 100644 src/components/Footer/FooterLinks/FooterLinks.tsx create mode 100644 src/components/Footer/FooterLinks/index.ts create mode 100644 src/components/Footer/index.ts create mode 100644 src/components/GlobalProvider.tsx create mode 100644 src/components/Header/Header.module.scss create mode 100644 src/components/Header/Header.tsx create mode 100644 src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss create mode 100644 src/components/Header/MenuCloseIcons/MenuCloseIcons.tsx create mode 100644 src/components/Header/MenuCloseIcons/index.ts create mode 100644 src/components/Header/index.ts create mode 100644 src/components/HeaderLinks/HeaderLinks.module.scss create mode 100644 src/components/HeaderLinks/HeaderLinks.tsx create mode 100644 src/components/HeaderLinks/index.ts create mode 100644 src/components/Loader/Loader.scss create mode 100644 src/components/Loader/Loader.tsx create mode 100644 src/components/Loader/index.tsx create mode 100644 src/components/MobileMenu/MobileMenu.module.scss create mode 100644 src/components/MobileMenu/MobileMenu.tsx create mode 100644 src/components/MobileMenu/index.ts create mode 100644 src/components/PhoneSlider/PhoneSlider.scss create mode 100644 src/components/PhoneSlider/PhoneSlider.tsx create mode 100644 src/components/PhoneSlider/index.ts create mode 100644 src/components/PicturesSlider/PictureSlider.scss create mode 100644 src/components/PicturesSlider/PicturesSlider.tsx create mode 100644 src/components/PicturesSlider/index.ts create mode 100644 src/components/ProductCard/ProductCard.module.scss create mode 100644 src/components/ProductCard/ProductCard.tsx create mode 100644 src/components/ProductCard/index.ts create mode 100644 src/components/SiteLogo/SiteLogo.module.scss create mode 100644 src/components/SiteLogo/SiteLogo.tsx create mode 100644 src/components/SiteLogo/index.ts create mode 100644 src/icons/Close.svg create mode 100644 src/icons/Cursor Mouse Mice.png create mode 100644 src/icons/Home.png create mode 100644 src/icons/Logo-dark.png create mode 100644 src/icons/Logo.png create mode 100644 src/icons/Menu.svg create mode 100644 src/icons/Minus.png create mode 100644 src/icons/Plus.png create mode 100644 src/icons/Search.png create mode 100644 src/icons/Shopping bag + Counter(Cart).png create mode 100644 src/icons/arrowLeft.svg create mode 100644 src/icons/arrowLeft_disabled.svg create mode 100644 src/icons/arrowRight.svg create mode 100644 src/icons/arrowRight_disabled.svg create mode 100644 src/icons/arrowUp.svg create mode 100644 src/icons/cart.svg create mode 100644 src/icons/dark_mode.svg create mode 100644 src/icons/favorite.svg create mode 100644 src/icons/favorite_selected.svg create mode 100644 src/icons/icons8-dark-mode-64.png create mode 100644 src/modules/App/App.module.scss create mode 100644 src/modules/App/App.tsx create mode 100644 src/modules/CartPage/CartPage.module.scss create mode 100644 src/modules/CartPage/CartPage.tsx create mode 100644 src/modules/CartPage/index.ts create mode 100644 src/modules/FavotitePage/FavotitePage.module.scss create mode 100644 src/modules/FavotitePage/FavotitePage.tsx create mode 100644 src/modules/FavotitePage/index.ts create mode 100644 src/modules/HomePage/HomePage.module.scss create mode 100644 src/modules/HomePage/HomePage.tsx create mode 100644 src/modules/HomePage/index.ts create mode 100644 src/modules/PhonePage/PhonePage.module.scss create mode 100644 src/modules/PhonePage/PhonePage.tsx create mode 100644 src/modules/PhonePage/index.ts create mode 100644 src/modules/Root/Root.tsx create mode 100644 src/modules/Root/index.ts create mode 100644 src/modules/constants/index.ts create mode 100644 src/notes.txt create mode 100644 src/styles/icons.scss create mode 100644 src/styles/main.scss create mode 100644 src/styles/mixines.scss create mode 100644 src/styles/theme.scss create mode 100644 src/styles/typography.scss create mode 100644 src/styles/variables.scss create mode 100644 src/types/MenuItems.ts create mode 100644 src/types/Product.ts create mode 100644 src/types/ProductItem.ts create mode 100644 src/utils/LocalStorage.ts create mode 100644 src/utils/accessLocalStorage.ts create mode 100644 src/utils/fetch.ts create mode 100644 src/utils/getCategoriesList.ts create mode 100644 src/utils/getHotDealsProducts.ts create mode 100644 src/utils/getNewProducts.ts create mode 100644 src/utils/getProductById.ts create mode 100644 src/utils/getProductItemByID.ts create mode 100644 src/utils/getProducts.ts create mode 100644 src/utils/getProductsByCategory.ts diff --git a/src/App.scss b/src/App.scss deleted file mode 100644 index 71bc413aad..0000000000 --- a/src/App.scss +++ /dev/null @@ -1 +0,0 @@ -// not empty diff --git a/src/App.tsx b/src/App.tsx deleted file mode 100644 index 372e4b4206..0000000000 --- a/src/App.tsx +++ /dev/null @@ -1,7 +0,0 @@ -import './App.scss'; - -export const App = () => ( -
-

Product Catalog

-
-); diff --git a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss new file mode 100644 index 0000000000..530094d4d5 --- /dev/null +++ b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss @@ -0,0 +1,44 @@ +@import '../../styles/main.scss'; + +.container { + width: 100%; + height: 40px; + display: flex; + gap: 8px; +} + + +.addToCart { + &_container { + background-color: var(--c-primary); + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + + &:hover { + box-shadow: 0px 3px 13px 0px #17203166; + } + + &_inCart { + background-color: var(--c-backgroud); + border: 1px solid var(--c-elements) + } + } + + &_text { + @extend %buttons; + + &_inCart { + color: var(--c-green); + } + } +} + +.favorite_container { + + &:hover { + box-shadow: 0px 3px 13px 0px #17203166; + } +} diff --git a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx new file mode 100644 index 0000000000..ed724e62cb --- /dev/null +++ b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx @@ -0,0 +1,40 @@ +import style from './ButtonsAddCardFav.module.scss'; +import classNames from 'classnames'; +import React, { useContext } from 'react'; +import { DispatchContext, StateContext } from '../GlobalProvider'; +import { FavoriteIcon } from './FavoriteIcon'; + +type Props = { + productId: string; +}; + +export const ButtonsAddCardFav: React.FC = ({ productId }) => { + const { productsInCart } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + const prodInCart = !!productsInCart.find(prod => prod.itemId === productId); + + return ( +
+
+ dispatch({ type: 'setCartProducts', payload: productId }) + } + > +
+ Add to cart +
+
+ +
+ +
+
+ ); +}; diff --git a/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss new file mode 100644 index 0000000000..3cecded326 --- /dev/null +++ b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss @@ -0,0 +1,36 @@ +@import '../../../styles/main.scss'; + +.container { + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; + height: 100%; + + &_icon { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + height: 100%; + } +} + +.icon { + &_container { + @extend %icon_container; + width: 48px; + height: 100% + } + + &_favorite { + @include iconBg(url('../../../icons/favorite.svg')); + + + } + +} + +.selected { + @include iconBg(url('../../../icons/favorite_selected.svg')); +} diff --git a/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx new file mode 100644 index 0000000000..ffc5927650 --- /dev/null +++ b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx @@ -0,0 +1,33 @@ +import style from './FavoriteIcon.module.scss'; +import classNames from 'classnames'; +import React, { useContext } from 'react'; +import { DispatchContext, StateContext } from '../../GlobalProvider'; + +type Props = { + curProductId: string; +}; +export const FavoriteIcon: React.FC = ({ curProductId }) => { + const { productsInFavorive } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + const prodInFavorite = !!productsInFavorive.find( + prod => prod.itemId === curProductId, + ); + + return ( +
+ dispatch({ type: 'setFavoriteProducts', payload: curProductId }) + } + > +
+
+ ); +}; diff --git a/src/components/ButtonsAddCardFav/FavoriteIcon/index.ts b/src/components/ButtonsAddCardFav/FavoriteIcon/index.ts new file mode 100644 index 0000000000..b44fd9acf6 --- /dev/null +++ b/src/components/ButtonsAddCardFav/FavoriteIcon/index.ts @@ -0,0 +1 @@ +export * from './FavoriteIcon'; diff --git a/src/components/ButtonsAddCardFav/index.ts b/src/components/ButtonsAddCardFav/index.ts new file mode 100644 index 0000000000..fbce0eaaad --- /dev/null +++ b/src/components/ButtonsAddCardFav/index.ts @@ -0,0 +1 @@ +export * from './ButtonsAddCardFav'; diff --git a/src/components/Categories/Categories.module.scss b/src/components/Categories/Categories.module.scss new file mode 100644 index 0000000000..140e4c5f54 --- /dev/null +++ b/src/components/Categories/Categories.module.scss @@ -0,0 +1,68 @@ +@import '../../styles/main.scss'; + +.container { + display: flex; + flex-direction: column; + +} + +.categories_container { + display: flex; + flex-direction: column; + gap: 32px; + + @include ontablet { + flex-direction: row; + justify-content: space-between; + gap: 16px; + } +} + +h2.title { + padding-bottom: 24px; +} + +.category { + &_container { + width: 100%; + box-sizing: border-box; + } + + &_link { + box-sizing: border-box; + display: flex; + height: 100%; + padding-bottom: 24px; + } + + &_title { + padding-bottom: 8px; + } + + &_count { + color: var(--c-secondary); + } +} + +.image { + width: 100%; + aspect-ratio: 1/1; + + @include hover(transform, scale(1.05)); + + + &_phones { + background: url('../../../public/img/category-phones.png') no-repeat left/cover; + background-color: #6d6474; + } + + &_tablet { + background: url('../../../public/img/category-tablets.png') no-repeat left/cover; + background-color: #8d8d92; + } + + &_accessories { + background: url('../../../public/img/category-accessories.png') no-repeat left/cover; + background-color: #973d5f; + } +} diff --git a/src/components/Categories/Categories.tsx b/src/components/Categories/Categories.tsx new file mode 100644 index 0000000000..f47fdb8aec --- /dev/null +++ b/src/components/Categories/Categories.tsx @@ -0,0 +1,67 @@ +import classNames from 'classnames'; +import style from './Categories.module.scss'; +import { Link } from 'react-router-dom'; +import { useContext } from 'react'; +import { StateContext } from '../GlobalProvider'; + +export const Categories = () => { + const { products } = useContext(StateContext); + + return ( +
+

Shop by category

+ +
+
+ +
+ + +

+ {'Mobile phones'} +

+ +

+ {products.filter(prod => prod.category === 'phones').length} models +

+
+
+ +
+ + +

{'Tablets'}

+ +

+ {products.filter(prod => prod.category === 'tablets').length} models +

+
+
+ +
+ + +

{'Accessories'}

+ +

+ { + products.filter(prod => { + return prod.category === 'accessories'; + }).length + }{' '} + models +

+
+
+
+ ); +}; diff --git a/src/components/Categories/index.ts b/src/components/Categories/index.ts new file mode 100644 index 0000000000..79c7c7dcde --- /dev/null +++ b/src/components/Categories/index.ts @@ -0,0 +1 @@ +export * from './Categories'; diff --git a/src/components/FavCarIcons/FavCarIcons.module.scss b/src/components/FavCarIcons/FavCarIcons.module.scss new file mode 100644 index 0000000000..68746ad3f4 --- /dev/null +++ b/src/components/FavCarIcons/FavCarIcons.module.scss @@ -0,0 +1,58 @@ + @import '../../styles/icons'; + + .container { + display: flex; + justify-content: flex-end; + align-items: center; + width: 100%; + height: 100%; + + &_icon { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + height: 100%; + + &_link { + display: flex; + justify-content: flex-end; + align-items: center; + + } + } + } + + .icon { + &_container { + @extend %icon_container; + + @include ontablet { + width: 48px; + height: 48px; + } + + @include ondesktop { + width: 64px; + height: 64px; + } + } + + &_dark { + @include iconBg(url('../../icons/dark_mode.svg')); + + } + + &_favorite { + @include iconBg(url('../../icons/favorite.svg')); + } + + &_cart { + @include iconBg(url('../../icons/cart.svg')); + } + } + + .mobile_menu { + width: 100%; + height: 64px; + } diff --git a/src/components/FavCarIcons/FavCarIcons.tsx b/src/components/FavCarIcons/FavCarIcons.tsx new file mode 100644 index 0000000000..dab64cb903 --- /dev/null +++ b/src/components/FavCarIcons/FavCarIcons.tsx @@ -0,0 +1,64 @@ +import React, { useContext } from 'react'; +import { NavLink } from 'react-router-dom'; +import classNames from 'classnames'; + +import style from './FavCarIcons.module.scss'; +import { DispatchContext, StateContext } from '../GlobalProvider'; + +type Props = { mobileView?: boolean }; +export const FavCarIcons: React.FC = ({ mobileView = false }) => { + const dispatch = useContext(DispatchContext); + const { inDarkMode } = useContext(StateContext); + + return ( +
+
+
{ + dispatch({ type: 'setInDarkMode', payload: !inDarkMode }); + document.body.classList.toggle('theme_dark'); + }} + > +
+
+ + + classNames(style.icon_container, style.icon_container_favorite, { + isActive_link: isActive, + [style.mobile_menu]: mobileView, + }) + } + onClick={() => dispatch({ type: 'setShowMenu', payload: false })} + > +
+ + + + classNames(style.icon_container, style.icon_container_cart, { + isActive_link: isActive, + [style.mobile_menu]: mobileView, + }) + } + onClick={() => dispatch({ type: 'setShowMenu', payload: false })} + > +
+ +
+
+ ); +}; diff --git a/src/components/FavCarIcons/index.ts b/src/components/FavCarIcons/index.ts new file mode 100644 index 0000000000..e21f84f2ff --- /dev/null +++ b/src/components/FavCarIcons/index.ts @@ -0,0 +1 @@ +export * from './FavCarIcons'; diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss new file mode 100644 index 0000000000..94c052e72b --- /dev/null +++ b/src/components/Footer/Footer.module.scss @@ -0,0 +1,49 @@ +@import '../../styles/main'; + +.container { + display: flex; + flex-direction: column; + justify-content: space-between; + row-gap: 32px; + height: 100%; + width: 100%; + padding: 32px 16px; + + box-shadow: 0 1px 0 0 var(--c-elements); + + &_logo { + display: flex; + box-sizing: border-box; + align-items: center; + height: 32px; + width: 100%; + } + + &_back_top { + display: flex; + justify-content: center; + align-items: center; + column-gap: 16px; + width: 100%; + } +} + +.back_top_text { + @extend %small-text; + cursor: pointer; + color: var(--c-secondary); +} + +.icon { + &_container { + box-sizing: border-box; + @extend %icon_container; + width: 32px; + height: 32px; + } + + &_upArrow { + @include iconBg(url('../../icons/arrowUp.svg')); + + } +} diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx new file mode 100644 index 0000000000..38c4b95f5d --- /dev/null +++ b/src/components/Footer/Footer.tsx @@ -0,0 +1,44 @@ +import classNames from 'classnames'; + +import style from './Footer.module.scss'; +import { FooterLinks } from './FooterLinks'; +import { SiteLogo } from '../SiteLogo'; + +export const Footer = () => { + const scrollToTop = () => { + window.scrollTo(0, 0); + }; + + return ( +
+
+ +
+ +
+ +
+ +
+
scrollToTop()} + > + Back to top +
+ +
+
scrollToTop()} + /> +
+
+
+ ); +}; diff --git a/src/components/Footer/FooterLinks/FooterLinks.module.scss b/src/components/Footer/FooterLinks/FooterLinks.module.scss new file mode 100644 index 0000000000..701a8674ca --- /dev/null +++ b/src/components/Footer/FooterLinks/FooterLinks.module.scss @@ -0,0 +1,16 @@ +@import '../../../styles/main.scss'; + +.container { + display: flex; + flex-direction: column; + align-items: flex-start; + row-gap: 16px; + +} + +.link { + @extend %uppercase; + @extend %link; + + @include hover; +} diff --git a/src/components/Footer/FooterLinks/FooterLinks.tsx b/src/components/Footer/FooterLinks/FooterLinks.tsx new file mode 100644 index 0000000000..5e56d1c892 --- /dev/null +++ b/src/components/Footer/FooterLinks/FooterLinks.tsx @@ -0,0 +1,30 @@ +import classNames from 'classnames'; +import style from './FooterLinks.module.scss'; +import { Link } from 'react-router-dom'; + +export const FooterLinks: React.FC = () => { + return ( +
+ + Github + + + + Contacts + + + + rights + +
+ ); +}; diff --git a/src/components/Footer/FooterLinks/index.ts b/src/components/Footer/FooterLinks/index.ts new file mode 100644 index 0000000000..fe667de823 --- /dev/null +++ b/src/components/Footer/FooterLinks/index.ts @@ -0,0 +1 @@ +export * from './FooterLinks'; diff --git a/src/components/Footer/index.ts b/src/components/Footer/index.ts new file mode 100644 index 0000000000..ddcc5a9cd1 --- /dev/null +++ b/src/components/Footer/index.ts @@ -0,0 +1 @@ +export * from './Footer'; diff --git a/src/components/GlobalProvider.tsx b/src/components/GlobalProvider.tsx new file mode 100644 index 0000000000..fc2f98a661 --- /dev/null +++ b/src/components/GlobalProvider.tsx @@ -0,0 +1,178 @@ +import React, { useEffect, useReducer } from 'react'; +import { Product } from '../types/Product'; +import { getProducts } from '../utils/getProducts'; +import { getProductById } from '../utils/getProductById'; +import { MenuItems } from '../modules/constants'; +import { getProductsByCategory } from '../utils/getProductsByCategory'; +import { ProductItem } from '../types/ProductItem'; + +type State = { + showMenu: boolean; + inDarkMode: boolean; + products: Product[]; + phones: ProductItem[]; + tablets: ProductItem[]; + accessories: ProductItem[]; + productsInCart: Product[]; + productsInFavorive: Product[]; + loading: boolean; +}; + +type Action = + | { type: 'setShowMenu'; payload: boolean } + | { type: 'setInDarkMode'; payload: boolean } + | { type: 'setProducts'; payload: Product[] } + | { type: 'setPhones'; payload: ProductItem[] } + | { type: 'setTablets'; payload: ProductItem[] } + | { type: 'setAccessories'; payload: ProductItem[] } + | { type: 'setCartProducts'; payload: string } + | { type: 'setFavoriteProducts'; payload: string } + | { type: 'setLoading'; payload: boolean }; + +function addProduct(data: Product[], target: Product) { + return [...data, target]; +} + +function removeProduct(data: Product[], target: Product) { + return [...data.filter(item => item.itemId !== target.itemId)]; +} + +function reducer(state: State, action: Action): State { + switch (action.type) { + case 'setShowMenu': + return { ...state, showMenu: action.payload }; + + case 'setInDarkMode': + return { ...state, inDarkMode: action.payload }; + + case 'setProducts': + return { ...state, products: action.payload }; + + case 'setPhones': + return { ...state, phones: action.payload }; + + case 'setTablets': + return { ...state, tablets: action.payload }; + + case 'setAccessories': + return { ...state, accessories: action.payload }; + + case 'setCartProducts': + const curProd = getProductById(state.products, action.payload); + + if (curProd) { + if ( + !state.productsInCart.find(prod => prod.itemId === curProd.itemId) + ) { + return { + ...state, + productsInCart: addProduct(state.productsInCart, curProd), + }; + } else { + return { + ...state, + productsInCart: removeProduct(state.productsInCart, curProd), + }; + } + } + + break; + + case 'setFavoriteProducts': + const curFavProd = getProductById(state.products, action.payload); + + if (curFavProd) { + if ( + !state.productsInFavorive.find( + prod => prod.itemId === curFavProd.itemId, + ) + ) { + return { + ...state, + productsInFavorive: addProduct( + state.productsInFavorive, + curFavProd, + ), + }; + } else { + return { + ...state, + productsInFavorive: removeProduct( + state.productsInFavorive, + curFavProd, + ), + }; + } + } + + break; + + case 'setLoading': + return { ...state, loading: action.payload }; + } + + return { ...state }; +} + +const initialState: State = { + showMenu: false, + inDarkMode: false, + products: [], + phones: [], + tablets: [], + accessories: [], + productsInCart: [], + productsInFavorive: [], + loading: false, +}; + +export const StateContext = React.createContext(initialState); + +export const DispatchContext = React.createContext>( + () => {}, +); + +type Props = { + children: React.ReactNode; +}; + +export const GlobalProvider: React.FC = ({ children }) => { + const [state, dispatch] = useReducer(reducer, initialState); + + useEffect(() => { + dispatch({ type: 'setLoading', payload: true }); + + const prodsPromise = getProducts().then(res => { + dispatch({ type: 'setProducts', payload: res }); + }); + + const phonesPromise = getProductsByCategory(MenuItems.phones).then(res => { + dispatch({ type: 'setPhones', payload: res }); + }); + + const tabletsPromise = getProductsByCategory(MenuItems.tablets).then( + res => { + dispatch({ type: 'setTablets', payload: res }); + }, + ); + + const accesPromise = getProductsByCategory(MenuItems.accessories).then( + res => { + dispatch({ type: 'setAccessories', payload: res }); + }, + ); + + Promise.allSettled([ + prodsPromise, + phonesPromise, + tabletsPromise, + accesPromise, + ]).finally(() => dispatch({ type: 'setLoading', payload: false })); + }, []); + + return ( + + {children} + + ); +}; diff --git a/src/components/Header/Header.module.scss b/src/components/Header/Header.module.scss new file mode 100644 index 0000000000..2822f52e56 --- /dev/null +++ b/src/components/Header/Header.module.scss @@ -0,0 +1,61 @@ +@import '../../styles/main'; + + +.container { + display: flex; + align-items: center; + gap: 16px; + width: 100%; + height: 48px; + padding: 0; + box-shadow: 0 1px 0 0 var(--c-elements); + + @include ondesktop { + height: 64px; + gap: 24px; + } +} + +.header { + &_logo { + display: flex; + box-sizing: border-box; + align-items: center; + height: 22px; + padding-inline: 16px; + min-width: 96px; + + @include ondesktop { + padding-inline: 24px; + } + } + + &_links { + display: none; + + @include ontablet { + display: block; + height: 100%; + } + } + + &_icons { + width: 100%; + height: 100%; + + &_fav_cart { + display: none; + + + @include ontablet { + display: block; + height: 100%; + } + } + + &_menu { + height: 100%; + } + } + +} diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx new file mode 100644 index 0000000000..4ec907dfc2 --- /dev/null +++ b/src/components/Header/Header.tsx @@ -0,0 +1,38 @@ +import style from './Header.module.scss'; +import classNames from 'classnames'; +import { HeaderLinks } from '../HeaderLinks'; +import { FavCarIcons } from '../FavCarIcons'; +import { StateContext } from '../GlobalProvider'; +import { useContext } from 'react'; +import { MenuCloseIcons } from './MenuCloseIcons'; +import { SiteLogo } from '../SiteLogo'; + +export const Header = () => { + const { showMenu } = useContext(StateContext); + + return ( +
+
+ +
+ + {!showMenu && ( +
+ +
+ )} + +
+ {!showMenu && ( +
+ +
+ )} + +
+ +
+
+
+ ); +}; diff --git a/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss b/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss new file mode 100644 index 0000000000..37f786b0aa --- /dev/null +++ b/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss @@ -0,0 +1,46 @@ +@import '../../../styles/main.scss'; + +.container { + display: flex; + justify-content: flex-end; + align-items: center; + height: 100%; + + &_menu { + display: flex; + justify-content: flex-end; + align-items: center; + height: 100%; + width: 100%; + + @include ontablet { + display: none; + } + } + + &_close { + display: flex; + justify-content: flex-end; + align-items: center; + height: 100%; + width: 100%; + } +} + +.icon { + &_container { + @extend %icon_container; + width: 48px; + height: 48px; + } + + &_menu { + @include iconBg(url('../../../icons/Menu.svg')); + + } + + &_close { + @include iconBg(url('../../../icons/Close.svg')); + + } +} diff --git a/src/components/Header/MenuCloseIcons/MenuCloseIcons.tsx b/src/components/Header/MenuCloseIcons/MenuCloseIcons.tsx new file mode 100644 index 0000000000..cedf425bfe --- /dev/null +++ b/src/components/Header/MenuCloseIcons/MenuCloseIcons.tsx @@ -0,0 +1,33 @@ +import classNames from 'classnames'; +import style from './MenuCloseIcons.module.scss'; +import { useContext } from 'react'; +import { DispatchContext, StateContext } from '../../GlobalProvider'; + +export const MenuCloseIcons = () => { + const { showMenu } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + + return ( +
+ {!showMenu ? ( +
+
dispatch({ type: 'setShowMenu', payload: true })} + > +
+
+
+ ) : ( +
+
dispatch({ type: 'setShowMenu', payload: false })} + > +
+
+
+ )} +
+ ); +}; diff --git a/src/components/Header/MenuCloseIcons/index.ts b/src/components/Header/MenuCloseIcons/index.ts new file mode 100644 index 0000000000..f59aa1f445 --- /dev/null +++ b/src/components/Header/MenuCloseIcons/index.ts @@ -0,0 +1 @@ +export * from './MenuCloseIcons'; diff --git a/src/components/Header/index.ts b/src/components/Header/index.ts new file mode 100644 index 0000000000..266dec8a1b --- /dev/null +++ b/src/components/Header/index.ts @@ -0,0 +1 @@ +export * from './Header'; diff --git a/src/components/HeaderLinks/HeaderLinks.module.scss b/src/components/HeaderLinks/HeaderLinks.module.scss new file mode 100644 index 0000000000..c80256b067 --- /dev/null +++ b/src/components/HeaderLinks/HeaderLinks.module.scss @@ -0,0 +1,36 @@ + @import '../../styles/main'; + @import '../../styles/theme'; + + .container { + display: flex; + justify-content: space-between; + column-gap: 32px; + box-sizing: border-box; + + @include ondesktop { + column-gap: 64px; + } + + &_isMobileMenu { + flex-direction: column; + align-items: center; + row-gap: 16px; + } + } + + .link { + @extend %uppercase; + @extend %link; + padding: 17px 0; + + + @include ondesktop { + padding: 25px 0; + } + + &_isMobileMenu { + padding-block: 8px; + } + + @include hover; + } diff --git a/src/components/HeaderLinks/HeaderLinks.tsx b/src/components/HeaderLinks/HeaderLinks.tsx new file mode 100644 index 0000000000..8898d4fc2b --- /dev/null +++ b/src/components/HeaderLinks/HeaderLinks.tsx @@ -0,0 +1,38 @@ +import classNames from 'classnames'; +import { NavLink } from 'react-router-dom'; +import React, { useContext } from 'react'; + +import style from './HeaderLinks.module.scss'; +import { MenuItems } from '../../types/MenuItems'; +import { DispatchContext, StateContext } from '../GlobalProvider'; + +export const HeaderLinks: React.FC = () => { + const { showMenu } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + + return ( +
+ {['home', ...Object.values(MenuItems)].map(item => { + return ( + + classNames(style.link, { + isActive_link: isActive, + [style.link_isMobileMenu]: showMenu, + }) + } + onClick={() => dispatch({ type: 'setShowMenu', payload: false })} + > + {item} + + ); + })} +
+ ); +}; diff --git a/src/components/HeaderLinks/index.ts b/src/components/HeaderLinks/index.ts new file mode 100644 index 0000000000..1e4303a4a2 --- /dev/null +++ b/src/components/HeaderLinks/index.ts @@ -0,0 +1 @@ +export * from './HeaderLinks'; diff --git a/src/components/Loader/Loader.scss b/src/components/Loader/Loader.scss new file mode 100644 index 0000000000..1413677cb8 --- /dev/null +++ b/src/components/Loader/Loader.scss @@ -0,0 +1,26 @@ +.Loader { + display: flex; + width: 100%; + justify-content: center; + align-items: center; + + &__content { + border-radius: 50%; + width: 10em; + height: 10em; + margin: 5em auto; + border: 0.3em solid #ddd; + border-left-color: #000; + animation: load8 1.2s infinite linear; + } +} + +@keyframes load8 { + 0% { + transform: rotate(0deg); + } + + 100% { + transform: rotate(360deg); + } +} diff --git a/src/components/Loader/Loader.tsx b/src/components/Loader/Loader.tsx new file mode 100644 index 0000000000..b9a7378e88 --- /dev/null +++ b/src/components/Loader/Loader.tsx @@ -0,0 +1,10 @@ +import './Loader.scss'; + +export const Loader = () => ( +
+
+
+); diff --git a/src/components/Loader/index.tsx b/src/components/Loader/index.tsx new file mode 100644 index 0000000000..d5ce981151 --- /dev/null +++ b/src/components/Loader/index.tsx @@ -0,0 +1 @@ +export * from './Loader'; diff --git a/src/components/MobileMenu/MobileMenu.module.scss b/src/components/MobileMenu/MobileMenu.module.scss new file mode 100644 index 0000000000..6e5f412dfc --- /dev/null +++ b/src/components/MobileMenu/MobileMenu.module.scss @@ -0,0 +1,37 @@ +@import '../../styles/main'; + +.wraper { + display: flex; + position: absolute; + width: 100%; + height: calc(100vh - 48px); + transition: all .3s; + transform: translate(100vw); + z-index: 1; + + &_up { + flex-grow: 1; + transform: translate(0%); + } +} + +.container { + display: flex; + flex-direction: column; + flex-grow: 1; + + &_links { + padding: 24px 16px 0; + flex-grow: 1; + } + + &_icons { + height: 64px; + width: auto; + } +} + +// .slide-pane { +// background-color: red; +// /* Change the background color */ +// } diff --git a/src/components/MobileMenu/MobileMenu.tsx b/src/components/MobileMenu/MobileMenu.tsx new file mode 100644 index 0000000000..c64ee79412 --- /dev/null +++ b/src/components/MobileMenu/MobileMenu.tsx @@ -0,0 +1,27 @@ +import classNames from 'classnames'; +import style from './MobileMenu.module.scss'; +import { HeaderLinks } from '../HeaderLinks'; +import { FavCarIcons } from '../FavCarIcons'; +import { useContext } from 'react'; +import { StateContext } from '../GlobalProvider'; + +export const MobileMenu = () => { + const { showMenu } = useContext(StateContext); + + return ( +
+
+
+ +
+
+ +
+
+
+ ); +}; diff --git a/src/components/MobileMenu/index.ts b/src/components/MobileMenu/index.ts new file mode 100644 index 0000000000..298a8ff2b8 --- /dev/null +++ b/src/components/MobileMenu/index.ts @@ -0,0 +1 @@ +export * from './MobileMenu'; diff --git a/src/components/PhoneSlider/PhoneSlider.scss b/src/components/PhoneSlider/PhoneSlider.scss new file mode 100644 index 0000000000..f520fcefa4 --- /dev/null +++ b/src/components/PhoneSlider/PhoneSlider.scss @@ -0,0 +1,83 @@ +@import '../../styles/main.scss'; + + +.phoneSlider.container { + box-sizing: border-box; + position: relative; + width: 100%; + +} + +.phoneSlider .title { + display: flex; + justify-content: space-between; + align-items: center; + gap: 72px; + min-height: 41px; + padding-bottom: 24px; +} + + + +.phoneSlider.swiper { + &_container { + width: 100%; + height: 100%; + } + + width: 100%; + height: 100%; + + overflow: hidden; +} + +.phoneSlider .swiper-slide { + text-align: center; + font-size: 18px; + background: #fff; + display: flex; + justify-content: center; + align-items: center; +} + + +.phoneSlider .swiper-slide { + width: 212px; + + @include ontablet { + width: 237px; + } + + @include ondesktop { + width: 272px; + } +} + +.phoneSlider .controls { + display: flex; + gap: 16px; + height: 32px; +} + +.phoneSlider .icon { + &_container { + @extend %icon_container; + + } + + &_right { + @include iconBg(url('../../icons/arrowRight.svg')); + + &_disabled { + @include iconBg(url('../../icons/arrowRight_disabled.svg')) + } + } + + &_left { + @include iconBg(url('../../icons/arrowLeft.svg')); + + &_disabled { + @include iconBg(url('../../icons/arrowLeft_disabled.svg')); + } + } +} diff --git a/src/components/PhoneSlider/PhoneSlider.tsx b/src/components/PhoneSlider/PhoneSlider.tsx new file mode 100644 index 0000000000..4e92e90583 --- /dev/null +++ b/src/components/PhoneSlider/PhoneSlider.tsx @@ -0,0 +1,99 @@ +import 'swiper/css'; +import 'swiper/css/pagination'; +import './PhoneSlider.scss'; + +import classNames from 'classnames'; +import 'swiper/css/navigation'; + +import React, { useRef, useState } from 'react'; +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation } from 'swiper/modules'; +import { ProductCard } from '../ProductCard'; +import { Product } from '../../types/Product'; + +type Props = { + title: string; + products: Product[]; +}; + +export const PhoneSlider: React.FC = ({ title, products }) => { + const sliderRef = useRef>(null); + const prevRef = useRef(null); + const nextRef = useRef(null); + const [realIndex, setIndex] = useState(0); + const [isEnd, setIsEnd] = useState(false); + const handleNavigation = () => { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + setIndex(() => sliderRef?.current?.swiper?.realIndex); + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + setIsEnd(sliderRef.current?.swiper?.isEnd); + }; + + return ( +
+
+

{title}

+ +
+
+
+
+ +
+
+
+
+
+ +
+ { + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line no-param-reassign + swiper.params.navigation.prevEl = prevRef.current; + // eslint-disable-next-line @typescript-eslint/ban-ts-comment + // @ts-ignore + // eslint-disable-next-line no-param-reassign + swiper.params.navigation.nextEl = nextRef.current; + swiper.navigation.init(); + swiper.navigation.update(); + }} + > + {products.map((product: Product) => ( + + + + ))} + +
+
+ ); +}; diff --git a/src/components/PhoneSlider/index.ts b/src/components/PhoneSlider/index.ts new file mode 100644 index 0000000000..c48f164c9d --- /dev/null +++ b/src/components/PhoneSlider/index.ts @@ -0,0 +1 @@ +export * from './PhoneSlider'; diff --git a/src/components/PicturesSlider/PictureSlider.scss b/src/components/PicturesSlider/PictureSlider.scss new file mode 100644 index 0000000000..78f401e97d --- /dev/null +++ b/src/components/PicturesSlider/PictureSlider.scss @@ -0,0 +1,86 @@ +@import '../../styles/main.scss'; + +.photoSwiper_container { + display: flex; + box-sizing: border-box; + width: 100%; + height: 100%; + gap: 19px; +} + +.photoSwiper.swiper { + display: flex; + flex-direction: column; +} + +.photoSwiper .swiper-pagination { + position: relative; + bottom: unset; + height: 24px; + + &-bullet { + height: 4px; + width: 14px; + border-radius: 0%; + background-color: var(--c-primary); + } +} + +.photoSwiper .swiper-slide { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + height: 100vw; + + @include ontablet { + height: 189px; + } + + @include ondesktop { + height: 400px; + } +} + +.photoSwiper img { + height: 100%; +} + + +.photoSwiper_container .icon { + box-sizing: content-box; + + &_container { + @extend %icon_container; + display: none; + flex-shrink: 0; + width: 32px; + height: 100%; + + @include ontablet { + display: flex; + height: 189px; + } + + @include ondesktop { + height: 400px; + } + + } + + &_right { + @include iconBg(url('../../icons/arrowRight.svg')); + + &_disabled { + @include iconBg(url('../../icons/arrowRight_disabled.svg')) + } + } + + &_left { + @include iconBg(url('../../icons/arrowLeft.svg')); + + &_disabled { + @include iconBg(url('../../icons/arrowLeft_disabled.svg')); + } + } +} diff --git a/src/components/PicturesSlider/PicturesSlider.tsx b/src/components/PicturesSlider/PicturesSlider.tsx new file mode 100644 index 0000000000..4568d2c0fc --- /dev/null +++ b/src/components/PicturesSlider/PicturesSlider.tsx @@ -0,0 +1,67 @@ +import 'swiper/css'; +import 'swiper/css/pagination'; +import 'swiper/css/navigation'; + +import classNames from 'classnames'; + +import { useRef } from 'react'; +import { Swiper, SwiperSlide } from 'swiper/react'; +import { Navigation, Pagination } from 'swiper/modules'; +import type SwiperCore from 'swiper'; +import './PictureSlider.scss'; + +export const PicturesSlider = () => { + const swiperRef = useRef(); + + return ( +
+
swiperRef.current?.slidePrev()} + > +
+
+ + { + swiperRef.current = swiper; + }} + className="photoSwiper" + > + + banner-accessorie + + + + banner-phones + + + + banner-tablets + + + +
swiperRef.current?.slideNext()} + > +
+
+
+ ); +}; diff --git a/src/components/PicturesSlider/index.ts b/src/components/PicturesSlider/index.ts new file mode 100644 index 0000000000..81a373f3aa --- /dev/null +++ b/src/components/PicturesSlider/index.ts @@ -0,0 +1 @@ +export * from './PicturesSlider'; diff --git a/src/components/ProductCard/ProductCard.module.scss b/src/components/ProductCard/ProductCard.module.scss new file mode 100644 index 0000000000..e6c47bae10 --- /dev/null +++ b/src/components/ProductCard/ProductCard.module.scss @@ -0,0 +1,83 @@ +@import '../../styles/main.scss'; +@import '../../styles/mixines.scss'; + +.container { + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 8px; + align-items: center; + height: 100%; + width: 100%; + border: 1px solid var(--c-elements); + padding: 32px; + + &_price { + width: 100%; + display: flex; + gap: 8px; + border-bottom: 1px solid var(--c-elements); + + } + + &_specs { + width: 100%; + display: flex; + flex-direction: column; + } + + &_bts { + display: flex; + width: 100%; + } +} + +img.image { + width: 148px; + height: 128px; + object-fit: contain; + + @include ontablet { + width: 173px; + height: 181px; + } + + @include ondesktop { + width: 208px; + height: 196px; + } +} + +h3.price { + &_discount { + color: var(--c-secondary); + text-decoration: line-through; + text-decoration-thickness: 2px; + } + +} + +.divider { + height: 1px; + width: 100%; + border: 1px solid var(--c-elements); +} + +.spec { + &_row { + width: 100%; + display: flex; + justify-content: space-between; + gap: 8px; + padding-block: 8px; + } + + &_name { + @extend %small-text; + color: var(--c-secondary); + } + + &_param { + @extend %small-text; + } +} diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx new file mode 100644 index 0000000000..d4ee5f09b6 --- /dev/null +++ b/src/components/ProductCard/ProductCard.tsx @@ -0,0 +1,58 @@ +import classNames from 'classnames'; +import style from './ProductCard.module.scss'; +import { Product } from '../../types/Product'; +import { ButtonsAddCardFav } from '../ButtonsAddCardFav'; + +type Props = { + product: Product; +}; +export const ProductCard: React.FC = ({ product }) => { + return ( + <> + {product && ( +
+ product image + +

{product.name}

+ +
+

+ ${product.price} +

+ + {product.discountPrice && ( +

+ ${product.discountPrice} +

+ )} +
+ +
+
+

Screen

+

{product.screen}

+
+ +
+

Capacity

+

{product.capacity}

+
+ +
+

RAM

+

{product.ram}

+
+
+ +
+ +
+
+ )} + + ); +}; diff --git a/src/components/ProductCard/index.ts b/src/components/ProductCard/index.ts new file mode 100644 index 0000000000..19d53574e0 --- /dev/null +++ b/src/components/ProductCard/index.ts @@ -0,0 +1 @@ +export * from './ProductCard' diff --git a/src/components/SiteLogo/SiteLogo.module.scss b/src/components/SiteLogo/SiteLogo.module.scss new file mode 100644 index 0000000000..700faefbd0 --- /dev/null +++ b/src/components/SiteLogo/SiteLogo.module.scss @@ -0,0 +1,18 @@ +@import '../../styles/main.scss'; + +.logo { + + &_img { + display: flex; + box-sizing: border-box; + align-items: flex-start; + height: 32px; + width: 90px; + background: url('../../icons/Logo.png') no-repeat center/contain; + @include hover; + + &_darkmode { + background: url('../../icons/Logo-dark.png') no-repeat center/contain; + } + } +} diff --git a/src/components/SiteLogo/SiteLogo.tsx b/src/components/SiteLogo/SiteLogo.tsx new file mode 100644 index 0000000000..fe37026518 --- /dev/null +++ b/src/components/SiteLogo/SiteLogo.tsx @@ -0,0 +1,18 @@ +import { useContext } from 'react'; +import { StateContext } from '../GlobalProvider'; +import style from './SiteLogo.module.scss'; +import classNames from 'classnames'; +import { Link } from 'react-router-dom'; + +export const SiteLogo = () => { + const { inDarkMode } = useContext(StateContext); + + return ( + + ); +}; diff --git a/src/components/SiteLogo/index.ts b/src/components/SiteLogo/index.ts new file mode 100644 index 0000000000..a2cc2164ff --- /dev/null +++ b/src/components/SiteLogo/index.ts @@ -0,0 +1 @@ +export * from './SiteLogo'; diff --git a/src/icons/Close.svg b/src/icons/Close.svg new file mode 100644 index 0000000000..aadcc91fb1 --- /dev/null +++ b/src/icons/Close.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/Cursor Mouse Mice.png b/src/icons/Cursor Mouse Mice.png new file mode 100644 index 0000000000000000000000000000000000000000..11626f20b3f8399729dd2f10a036f08675217f72 GIT binary patch literal 821 zcmV-51Iqk~P)J<90-BNTrMZTfsGtGB}w|! z>-C=F?|byjNFBt;;qh!X`?^>x2JLqHB|S3|wi`u%ggBW_r{82WRN_yqR(pkWZbr^l zC1`OM!Z(>rzRS#rM7>^r%}Cg)gb)sgNpAmmJg&$pBNEkWm6Nbd2}*&8I3JBhKgn@6 zo0a?hzFrB@77}dBQG^eM!=XYToleW4P)I{Ur^H^369}jY;PV=ZMx(J)VP4x@QmGVQ zNEQU`Uj~B##9}dJ#CI2oL;|x>55scT?5$7m8VK+j;0kc)l~C=D-D`r?fI>i@0y7e- zbnLuEuZqw}0kBz#jVUlAv5!7M61u%!uW1S_C~+eN*7|@M0Whu!(`L2ljJG78!4{>X z{K&|c837)dCDW5gCX+Q(;u#~cmJYN0IADVUnM?+v(WoI2Ov#lJ&1Um$JRbiA%leXF zgBiAB4mzBy7}Z3#+kFALdbm-AqH}6ifQwFAt=4C!)43pMA`k?b=0s7HTrSrf$9XUq zyu??(xA9@P3wY%7`TTF8Q22;*RAeE9+Igu| z`hZbgG6w;~na}6*<48C|_!!9XDXeb8QG@y#?n8Bq2f95+tx#Dj_ zU`+w;yjU#0$N4Ap4simjV_4APy1A`QAwZJio$v>DUM`p4G3Vz94_J`Ys?nk(pK>e4 zY9CiBl{X%b=aI@eMQ+>GT!=&>l)F<@;0t!LI4mpK539&+|1KaWVm_ZI$@P`IH|J&x zsf&mHM}0tI;{pyqzyaFxK;4QDZU_4!zJ2x&DdkIL0M=DY00000NkvXXu0mjf_>^hH literal 0 HcmV?d00001 diff --git a/src/icons/Home.png b/src/icons/Home.png new file mode 100644 index 0000000000000000000000000000000000000000..e7668490cdd0df26ff9691330b4fa32d91f9f3e6 GIT binary patch literal 370 zcmV-&0ge8NP){TQGsdk5?zq4IDv6a0BB6uHj9O-i#Bd6NnS&287&Jm4!uWVh2Wn^S z+T>iMvihR}@{!tQlZ1Wk3hG0*(DyNhA9TBMPsX~!%<=ej?W3}2nTpi71ISuR9CL>7 Ql>h($07*qoM6N<$f=IEPApigX literal 0 HcmV?d00001 diff --git a/src/icons/Logo-dark.png b/src/icons/Logo-dark.png new file mode 100644 index 0000000000000000000000000000000000000000..25115853d24130d9ad00d616e3831b264defe222 GIT binary patch literal 2519 zcmV;|2`Ki7P)lSJ{aw$w@4c#9uU_>}0x1UOz@zKE zd+&MYo_o%BeqIB73~BYvm-6ekzwz&fzxmolm@sLbj(rKhH5g-!bXtE$9^BltW;y41 z`t3OStz3?^g8B3;lZjmqGo&@H>r8{f493;EuB-KAlHHf*$ULG*QJ|hm#r_^O#yPO} zZyfzjI+uL$NQ(Vxb$NPvW})7K2@z{|dTaPMPdBQ9@<=xm-F~ljmnM zaTd(&cFha6>sA=B!suIg#kJ#0bO3bQ>g?W$oY$WZ6gBkUTd&R}4db5K?ZEMK zpMuV#&QfmfcV(CmDe{f&xYcHp8*oT!Hh1#2!)s%e!dZ!_;xL|>&Rn{?y4Be(H#bbM zdnP1|weSV-P5Q#dTsivQDyEyl;p(z~;%l!)s%D zEKhTXt})gZ(5Jt7d&%ipKm6d{e;|G&5kGMz`z@FdX(Lb2T5zM)FdmL0vc7@k@kTb2 zSO~_*#&I-En41l>W169>8aB#$L4p0w|XHp{2Tpzc-XZ0$Gt3yEm zE%<7Hmqbi+esJ{E@hgg|qL%VD`zBK#d>wh}*;XEr1 zDNtcb(dBN9Tp0vrv3;YP16EG)k$@dwewfI$!_N{4DB~Z&>%q-e7hSIA9)7U?UFN{Q zl1bfynD(=yr_$$N_{Uz-m{amx89ZK12vF~1#NuWPYV zCKX?hFWG&2Th?9l2vlTz{;xlOIZMx0-@2GTmQxB~?x40&*Jc-N%e5Yx+iiHxG!jHe zcnl1enX0Vf?Y;`k<&q6lPLRSFl&)Y-Shg(vzNXn15Kk)t5|DIVFTx?ove=*Po&^{{ zjC1GD{NbbDpSY`nT}&htgC(aBcwIbn8#){9xybNJv{Z2ZZzjgxFR7kEwh0^gb!VtgH$Si<@VdH_06Af3{lyWnJ*frkCoD=Pa3gf zCqPlX^Y3kL!FJ2~1x$dHaRb>@>>3JPg98vL+;!X;R8l=+xaCzAv8PTm{`rqbZ#;Rp zagSRT5G1nzm=SC2UJo`NY;?{qy!#iJ0BM-)E{q!_%i@|>3xk~y${9CbSS`Rzv)i2p zSI>L3uF?W0R+BN!CzDI&RZRugMflq`;iE^L z*8d)~z6TQ`1|K8BJd^$H$6t6o7K6`&3%uPn-`{9jH!oKj6Kf`XLeEHj3#9ZzQOrfo zcmcE1JU*fvu#O#?%DC22(195+V_3??M_imzn26;qOu(xcnQKwO#gl0VYjbrGOThwo z8G(=q>*un(hSDR9#YGsmNv6~ZgQ>fOxSof7h&wh`LMZ`RqCLmmBEoIKW5yedLp2tQ zS4jreR|Iua#^rq+gFtq)z?a)y0Z}6%jCmAUVVtm>Hx#CoG8ygG(3+rCO zK|fMNZ7wR2=(&v5GU?cy+=|>MM3_Bu69#?FKt8`8*)RcFyygu^N6GzZ*Ax~T29nDnV*~!dTY!#!(%cZ?EZ{Y zJ5eO>&G)1sYIb&tQFuj2L#MSLBYh(%iw92uE+|;*-$ViP=({BPGZZ3%siPZ>Q z^pda}M-lb?P{1OPCHm_?ywEK8Q(EEPpr22U%_&g~11{)K#>=vcIN0mLFP* z3(iom11nL7r=*}EEWA$JomRL)7+a(GXQ+|rWQWT z59CD*^|4QWv1tb`KAs3y`VfNr>Ah4EN;S9)_&)!ftGMkrR1y7Zo>9I+Jof1+)qVse=EG72HI@OGTK0-<*p<(2HsMNS;VZ zH!6MTS=2Z0eM#YUObEqK`dW#Hjcqxlh4K_2X!GG=i5YatB3uCZqW3XySvJ8>eSmtw#3ImQFI>6%w h!Gr_uwcJ#^_J0r5G`bovWR3s;002ovPDHLkV1jOXw}Sux literal 0 HcmV?d00001 diff --git a/src/icons/Logo.png b/src/icons/Logo.png new file mode 100644 index 0000000000000000000000000000000000000000..1cbd99300e9f6dcc81845a9d77b9cc221b0b2a72 GIT binary patch literal 2587 zcmV+$3gq>PP)QLm=if(U3P|-kv7P)xclP`s+9-96oT31SAt9H1gY>SjgPTyP3P{ZN!N0I{xNj_n* z+}YVTZ{ECleM*4A?~Z@@7Fi^r|A@}XSz@8})W_whIseT0Q5mm#ILg%PK3 zO%rVTjrY~Cek!d_=r)GL?aYf`f8nJw?+hf8u<6~KdFG1i!ZCpni!m23kFMci>!RKP z+q3e0tp=}BTfGi%$~O9~e={;-T}!1#3-mq`VJ=;qvqCCm-BcG%(7GmRb-Fl*w6(1Q&J#}*YYuN2=IAR!~ji_n5?d*kyJ!iIju2f)FeUE-i zXf?RvIutgRFJ($mt8n_oGygg_eP;cE`^x*{&p$n9^77vcA8dlb(4Pzqd=HKRG)?B7 z5R?^*K^D#14D<=ZfGOoM9~P+yE$yPIY>J#?TPuS(T%>%H_*)lF#Yf`ThOD9B*&Hw- zo`_B0BEB&?{t^`K7U15U`#2rutSmX!^i{WOWb$#NMaXE6GdLmAK%1NtMG4S96#K^y zIw_0BZExmH+H1>Su?HfF9~SOz|FTpluhw??y-0K*K9r37Z0zaOM0_|7pFj5nj9M6! z?maMu;33VSZxi%!Ko_!Kuk$p8T=a}iY@s1A)(uESnLMA+X=-)yOMvZ2q(WF^@ zd1zc9kL|66P0@p%K#)VlK2xH9zTLUBBCzTXL<|#-8lfDZ?VLl~=#Y(sK7ldzE!qhn%mzje^LU$+?;FttWTcj( z{Y=9Fkc8*GoyY$Rgq*MjUt53sxY~3%paKz}i z%&2i!P$)9w>Iu?Xu>%$w-?Ld^bofE`p@Br(NV}0a^Zv*ME2Gf zD0)D_Uf$aQ%)$9OdOx)LvtiK(!j_Aqf-&ym?Yev}ug88|81%n-;>?LJ#|8%g=+eNe zRoE^(`r8*@`}4bS#4x(OM4?f#;KDwDHg%e^G*B`;1uhC~<{*iVisydt$BOsfgDZuN zO}}2*##O5+Z#mSeyJ#jbjN2&u6^;QI*-GWv*PWF}e=YTWSX&rz?B*!_^0Yfb`lz%z zBJA7ue|vs%Fk#+EBx7d{lc8C`+C`-bTZJ-geo%BvcPr_af3bE4ju?H#X2%(STDUOw zxn%K`Xv{pnyIUQs?biSC;eX4wznSq|_za&Ov}iJIU8)(2SeCXh)r(z(MG(El*)NY6m%rYN9)_L)pR!io4`iexL3JB3f4Y3i0VsNs&b=vQ zN}+{k53!Hj!-9{~AtC@Q&{Ms^QQImNZtMMt8JSIngOk7(^hxx|ql-MI)iIGT4n07Q z7=jWjTZ2APEEWQ}iP(RsLn(j`8!{9;7=rov&V}}X6&njiZdhebI8H}z9~e-}p8bN^ zQ~?Z&mHXh)As!EV=&&E45O0PHIN*oE(F`dT6Lnr?6?q=FB86^r+;kQvtxSx&dfH1{ zk^~bniiaycp*HB6j9PS+?x?*fQS5J;oLY@L(gbwik?Ik?&LfpmDPyJIswQLmEN81}!X}}1v$IGht2{1ZBC||~2K0colIR4J(|N?-Gb#_L<(8A)X!aO!uD7 zOaiqsgTfWeF-I;a3Q#gk*&^BOmbhFbO^ZEfpViG>Kd0cGK+YCzy`dYTLsBp9@#db94x6jKFh?cg>*xy)_oQI~!a zAhU%#OCBC8e3H|HOf1IHgjtR$m>QO4(1c71)8;gKP*ldVreHW| zB+UR(?NaR={i6zqg}^NwczMlK1f@+DlBDm)WFXB+&HPAhbR~~C22J8dgU65L1SuLQ z$Pa08kk1y-4(s9g_5~4U+Jf(lpiS4J2salRJ=v6j>Ym;EG&t9r4b2!EiPq%nCZDqa*crB6BBd8m=JNKC!fiw;jP%g+0Z|}c-`z~2#=2PtxMmk xJj!=)aCp5sP-cnz + + + + diff --git a/src/icons/Minus.png b/src/icons/Minus.png new file mode 100644 index 0000000000000000000000000000000000000000..19c80d474df8e52ea4e3276c38c216708f936369 GIT binary patch literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBzNx2+V@L(#+tUkq85DR}FE(>pG0i@-DyJbw zORDL>;*<5S&NS96xMQ$+nrHn?C5d<0^VW47U=yq2WO=L4q7O8H!PC{xWt~$(699>| BEinK9 literal 0 HcmV?d00001 diff --git a/src/icons/Plus.png b/src/icons/Plus.png new file mode 100644 index 0000000000000000000000000000000000000000..eb42caf4f29ebc932355bc8c101acd57c315cd34 GIT binary patch literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBex;|2V@L(#-HB&88w_|_<3pFIpJlV^T5Zvp z_eg8qBep8;ps16ME84}5Iv?fta{hEef5I#qF_lGP7q%MiX*F@xY1}GtN+`ii{!&VX z=P#C5CMk(VpH){}IQd>C?f(Av5;Lzqbz|p>>wO*~>ia2amVAt2SBzVB)`v{KInTu7 UX1Qhp-N3-$>FVdQ&MBb@0B~nZ{Qv*} literal 0 HcmV?d00001 diff --git a/src/icons/Search.png b/src/icons/Search.png new file mode 100644 index 0000000000000000000000000000000000000000..3da1aa7570f9f68d6b28ad35ae995972e80cb7ab GIT binary patch literal 401 zcmV;C0dD?@P)t6DZjo;`?yF7+ zgMpo<>75@A?X#+gs>&E!rm0=Vv0o_2!!Kw+5UJ%b`-8IgdiEX{z{4}~POIqXMZ*p~ zHue|D9lYvGlgDQQs?d9Uq6LolCmLeLm{FSmD?f0O1m9-?U7vc>_mY4Nl##VT2HtcN zO-6($CV vp$Ybg37RYGP-&7CIbzaW#GbQNvb4a(G|q00000NkvXXu0mjfp&Osc literal 0 HcmV?d00001 diff --git a/src/icons/Shopping bag + Counter(Cart).png b/src/icons/Shopping bag + Counter(Cart).png new file mode 100644 index 0000000000000000000000000000000000000000..42ec61b3918af089efcb4c01ca98936cc6bcaa71 GIT binary patch literal 794 zcmV+#1LgdQP)~?8sYIkY9NcfV;?7la7-_E?5Zvp-Rgb*BU0WB+P zNTDsGovM#z&>`@^;I}=Ic=5W-(_1Q}KUTE$Gs8CUPdZ?F+^*njj4}48V?rd0tPVT<0KzK($%&JkxWLH3dZ*M-^l7opG2LCtCyX&LHk&E^|q{$1!f#@ z?`9W_oz;&ETysEtt_MqM*WM{+9ZQankxmGGYss?q@Frb>6HW%+0UJzQkYMbb1kcuf z!_{%e{zp{rHe|PQGUr^bRA91^FtekVGP9$?kDvPDCN|FIRtet)d@87bWz-3A9U+?% z5}6RNVY4wAEP#jz)0gd9r`~^FIk0b9QY3pw&0H!8xmf(l48yIQQLaW%lLThEfV$Vf z=#UosUTQ_w-_Il1%JWE@lO#Cb-QD9i%;WLMBcqpiwNx-MX4jg7GK|kT;XC>fuU^jW z&jM~;@1l%fG84jOB%Hz#NH*>b4i3V*)Cd3VxIPw?EqRv{vl*`Ck=d>wIlyYHmju9P zN=SZpR|z-UCzZ*6QCFf%CWDU|dk5G&sg32a=+Q2MBBTMX7pWA&>xLkaHJdCy3*>I4Ax=<+8i+8pv=={J(nh}yW zN{WYXU?FKt5_*P!4FyWEs9bCo-wcOR8pcEz$&y$eQvaC#3|9_wh9#vjpeRZXUASs( zp%X1VB7}HHGFSx0a + + + + + diff --git a/src/icons/arrowLeft_disabled.svg b/src/icons/arrowLeft_disabled.svg new file mode 100644 index 0000000000..e215e05745 --- /dev/null +++ b/src/icons/arrowLeft_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/arrowRight.svg b/src/icons/arrowRight.svg new file mode 100644 index 0000000000..589a14e8a9 --- /dev/null +++ b/src/icons/arrowRight.svg @@ -0,0 +1,41 @@ + + + + + + diff --git a/src/icons/arrowRight_disabled.svg b/src/icons/arrowRight_disabled.svg new file mode 100644 index 0000000000..0ff66e8a9e --- /dev/null +++ b/src/icons/arrowRight_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/arrowUp.svg b/src/icons/arrowUp.svg new file mode 100644 index 0000000000..de587604d2 --- /dev/null +++ b/src/icons/arrowUp.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/cart.svg b/src/icons/cart.svg new file mode 100644 index 0000000000..682ac1c5e3 --- /dev/null +++ b/src/icons/cart.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/icons/dark_mode.svg b/src/icons/dark_mode.svg new file mode 100644 index 0000000000..122729f6e2 --- /dev/null +++ b/src/icons/dark_mode.svg @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/src/icons/favorite.svg b/src/icons/favorite.svg new file mode 100644 index 0000000000..e69d519f44 --- /dev/null +++ b/src/icons/favorite.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/favorite_selected.svg b/src/icons/favorite_selected.svg new file mode 100644 index 0000000000..7138d7522b --- /dev/null +++ b/src/icons/favorite_selected.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/icons8-dark-mode-64.png b/src/icons/icons8-dark-mode-64.png new file mode 100644 index 0000000000000000000000000000000000000000..f6a3e2a6987610a610b7ede3f5fd94595544c8a8 GIT binary patch literal 1593 zcmV-92FCe`P)q$gGRCt{2n|X*=RTRfRzbVJc#Kw^p)3FV*Y){Kf(-sNDAcvZ4 zfgp`^XkVx-`lCpVpoG#)qJ5FdNNZX!f`S$eE5;=w+MUVCrLr=0)L$I z*&wf@z4N^u`5pmK-Wt%P4%_@6qyOz%S+4@K=;k;NxKV#YQ7Iub>FD-;z)PvodL8IZ znP7rCy%~5E*r4-hK($p8&Mz+7h6m3)*x5Fj02nePAu{C$LomwFy`OJg4@b z7V@$S)ma<)VNM9Y>-tp%aF~!r0`~%ofYnZu@@nuJO|2Qg89*+@k(2#-RUOS2d3+B% z1k{uwghe80aYDFF*Bq}_xOviHpu*}`t1uO?10VV+_0s}QAO*;+;P64I> zPXIr=XSW0Mixos~;2kG~+ksU&zqyUy9|Ft+cDNV+1Uv&=EtMQ3-af!!;8b9$ezQ`V zH2`?UajcixA5~1_r9EehyH#9&X)jWg5EiFK$%JrV>UUQH8+E@`+H-yzD%}}II)~{$ z-~o+pD2@T%6z{TPAGS`^$spbY^mX?trEZCEK9;R01(yKZ#Pca|G;vWa?ya#1_Xpk; zsWtq%5=J~hom11NBQLf4%BUyP`SVz$N#{-V&k5if<3`?8J{As=?MS9(Ud!gl807Qa zQZ_Vz=TNt_E+8JRO?A8xxCb~a2Ki*Sw2TYnY7o~4z&;V$W(Me7VY4CMiR=0Z?K0#0n)ucM$B_XB$;mNmeR3dI zof`WK&?`dQ>fPq8)MdEffQI?*q4-E#=3Rzjh*aMAE+!*X0Ke)b=A(yd(k?k9{UT1w zg=C1c^pYO~oSg*I#r0lY; z1qCdGm&G-$*as^WC`i&($k6=p@x3`LLBzyj2V}z*l9V9I`>v~C%6^DbJ zqY{domgU{0ETv5@QRy+dorFaaj6j;Amv^xrlt}jtCSeAbb1unXuaixFfLMRY)%9} zBSAl{T)rs7(&HB1ivm-(x7~E84={|iT3w9m$;j1XNpiFyBt2fM9pW@~+>MHs_j%U!uS#kB$6GTMSJgy6gnU3`msEuL}pC~ z8yWMFi^(F7b%reBsCWJEOmdS;uV)Gs$8ugOO@U`Rswy>1#$9< zK`fBa3&KTuaD(%ze3_i(_6ivR+eO}~#O2rG+a~ok?f6O~bb|_}+L|)9!n`QwrdWag zAfXnWeV!qLhjl*8Npx|v#00000NkvXXu0mjf#cu3q literal 0 HcmV?d00001 diff --git a/src/index.tsx b/src/index.tsx index 50470f1508..b3bee57714 100644 --- a/src/index.tsx +++ b/src/index.tsx @@ -1,4 +1,9 @@ import { createRoot } from 'react-dom/client'; -import { App } from './App'; +import { Root } from './modules/Root'; +import { GlobalProvider } from './components/GlobalProvider'; -createRoot(document.getElementById('root') as HTMLElement).render(); +createRoot(document.getElementById('root') as HTMLElement).render( + + + , +); diff --git a/src/modules/App/App.module.scss b/src/modules/App/App.module.scss new file mode 100644 index 0000000000..0c68d8927f --- /dev/null +++ b/src/modules/App/App.module.scss @@ -0,0 +1,43 @@ +@import '../../styles/main'; + + +.container { + display: flex; + flex-direction: column; + min-height: 100vh; + overflow: hidden; + + &_header { + background-color: var(--c-backgroud); + display: flex; + justify-content: center; + position: sticky; + top: 0; + } + + &_mobile_menu { + position: relative; + background-color: var(--c-backgroud); + display: flex; + flex-direction: column; + + } + + &_body { + display: flex; + justify-content: center; + max-width: 1200px; + + + @include ondesktop { + margin: 0 auto; + } + } + + &_footer { + background-color: var(--c-backgroud); + display: flex; + justify-content: center; + box-shadow: 0px -1px 0px 0px #E2E6E9; + } +} diff --git a/src/modules/App/App.tsx b/src/modules/App/App.tsx new file mode 100644 index 0000000000..d47190d7e6 --- /dev/null +++ b/src/modules/App/App.tsx @@ -0,0 +1,61 @@ +import { Outlet, useLocation } from 'react-router-dom'; +import classNames from 'classnames'; + +import style from './App.module.scss'; +import '../../styles/theme.scss'; +import '../../styles/main.scss'; +import { Footer } from '../../components/Footer'; +import { Header } from '../../components/Header'; +import { useContext, useEffect } from 'react'; +import { MobileMenu } from '../../components/MobileMenu'; +import { StateContext } from '../../components/GlobalProvider'; +import { Loader } from '../../components/Loader'; + +const ScrollToTop = () => { + // Extracts pathname property(key) from an object + const { pathname } = useLocation(); + + // Automatically scrolls to top whenever pathname changes + useEffect(() => { + window.scrollTo(0, 0); + }, [pathname]); + + return <>; +}; + +export const App = () => { + const { showMenu, loading } = useContext(StateContext); + + return ( +
+ + +
+
+
+ +
+ +
+ + {!showMenu && ( + <> + {loading ? ( + + ) : ( +
+ +
+ )} + +
+
+
+ + )} +
+ ); +}; diff --git a/src/modules/CartPage/CartPage.module.scss b/src/modules/CartPage/CartPage.module.scss new file mode 100644 index 0000000000..fbada7f1da --- /dev/null +++ b/src/modules/CartPage/CartPage.module.scss @@ -0,0 +1 @@ +@import '../../styles/main.scss'; diff --git a/src/modules/CartPage/CartPage.tsx b/src/modules/CartPage/CartPage.tsx new file mode 100644 index 0000000000..d0c3b9f67e --- /dev/null +++ b/src/modules/CartPage/CartPage.tsx @@ -0,0 +1,3 @@ +// import stytle from './CartPage.module.scss'; + +export const CartPage = () =>

Cart page

; diff --git a/src/modules/CartPage/index.ts b/src/modules/CartPage/index.ts new file mode 100644 index 0000000000..90c010237a --- /dev/null +++ b/src/modules/CartPage/index.ts @@ -0,0 +1 @@ +export * from './CartPage'; diff --git a/src/modules/FavotitePage/FavotitePage.module.scss b/src/modules/FavotitePage/FavotitePage.module.scss new file mode 100644 index 0000000000..fbada7f1da --- /dev/null +++ b/src/modules/FavotitePage/FavotitePage.module.scss @@ -0,0 +1 @@ +@import '../../styles/main.scss'; diff --git a/src/modules/FavotitePage/FavotitePage.tsx b/src/modules/FavotitePage/FavotitePage.tsx new file mode 100644 index 0000000000..7280365de1 --- /dev/null +++ b/src/modules/FavotitePage/FavotitePage.tsx @@ -0,0 +1,3 @@ +import stytle from './FavotitePage.module.scss'; + +export const FavotitePage = () =>

Favotite page

; diff --git a/src/modules/FavotitePage/index.ts b/src/modules/FavotitePage/index.ts new file mode 100644 index 0000000000..51e7912bc3 --- /dev/null +++ b/src/modules/FavotitePage/index.ts @@ -0,0 +1 @@ +export * from './FavotitePage'; diff --git a/src/modules/HomePage/HomePage.module.scss b/src/modules/HomePage/HomePage.module.scss new file mode 100644 index 0000000000..505b4f9451 --- /dev/null +++ b/src/modules/HomePage/HomePage.module.scss @@ -0,0 +1,80 @@ +@import '../../styles/main'; + +.container { + width: 100%; + display: flex; + flex-direction: column; + gap: 24px; + padding-block: 24px 64px; + + @include ontablet { + gap: 32px; + } + + @include ondesktop { + gap: 56px; + } + + &_homepage { + box-sizing: border-box; + display: flex; + flex-direction: column; + row-gap: 56px; + + width: 100%; + + @include ontablet { + row-gap: 64px; + } + + @include ondesktop { + row-gap: 80px; + } + } +} + +.title { + @include pageGrid; + + &_text { + grid-column: 1/-1; + + } +} + +.photoSlide { + box-sizing: border-box; + + + @include ontablet { + padding-inline: 24px; + min-height: 221px; + max-height: 432px; + } +} + +.slider_container { + box-sizing: border-box; + width: 100%; + // height: 439px; + padding-inline: 16px; + + @include ontablet { + // height: 512px; + padding-inline: 24px; + } + + @include ondesktop { + // height: 506px; + padding-inline: 32px; + } + +} + +.container_categories { + @include pageGrid; + + &_body { + grid-column: 1/-1; + } +} diff --git a/src/modules/HomePage/HomePage.tsx b/src/modules/HomePage/HomePage.tsx new file mode 100644 index 0000000000..48700e9982 --- /dev/null +++ b/src/modules/HomePage/HomePage.tsx @@ -0,0 +1,56 @@ +import classNames from 'classnames'; + +import style from './HomePage.module.scss'; +import { PhoneSlider } from '../../components/PhoneSlider'; +import { useContext, useMemo } from 'react'; +import { StateContext } from '../../components/GlobalProvider'; +import { getNewProducts } from '../../utils/getNewProducts'; +import { Categories } from '../../components/Categories'; +import { getHotDealsProducts } from '../../utils/getHotDealsProducts'; +import { PicturesSlider } from '../../components/PicturesSlider'; + +export const HomePage = () => { + const { products, phones, tablets, accessories } = useContext(StateContext); + const newPhone = useMemo(() => getNewProducts(products), [products]); + const hotDealsProducts = useMemo( + () => + getHotDealsProducts(products, [...phones, ...tablets, ...accessories]), + [products, phones, tablets, accessories], + ); + + return ( +
+
+

+ Welcome to Nice Gadgets store! +

+
+ +
+
+ +
+ +
+ +
+ +
+
+ +
+
+ +
+ +
+
+
+ ); +}; diff --git a/src/modules/HomePage/index.ts b/src/modules/HomePage/index.ts new file mode 100644 index 0000000000..11e53da674 --- /dev/null +++ b/src/modules/HomePage/index.ts @@ -0,0 +1 @@ +export * from './HomePage'; diff --git a/src/modules/PhonePage/PhonePage.module.scss b/src/modules/PhonePage/PhonePage.module.scss new file mode 100644 index 0000000000..e69de29bb2 diff --git a/src/modules/PhonePage/PhonePage.tsx b/src/modules/PhonePage/PhonePage.tsx new file mode 100644 index 0000000000..eb534223be --- /dev/null +++ b/src/modules/PhonePage/PhonePage.tsx @@ -0,0 +1,66 @@ +export const PhonePage = () => { + return ( +
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
PHONE PAGE
+
+ ); +}; diff --git a/src/modules/PhonePage/index.ts b/src/modules/PhonePage/index.ts new file mode 100644 index 0000000000..b5a582aec3 --- /dev/null +++ b/src/modules/PhonePage/index.ts @@ -0,0 +1 @@ +export * from './PhonePage'; diff --git a/src/modules/Root/Root.tsx b/src/modules/Root/Root.tsx new file mode 100644 index 0000000000..34062c6dd1 --- /dev/null +++ b/src/modules/Root/Root.tsx @@ -0,0 +1,64 @@ +import { + HashRouter as Router, + Navigate, + Route, + Routes, +} from 'react-router-dom'; + +import { App } from '../App/App'; +import { HomePage } from '../HomePage/HomePage'; +import { PhonePage } from '../PhonePage'; +import { FavotitePage } from '../FavotitePage'; +import { CartPage } from '../CartPage'; + +export const Root = () => { + return ( + + + } + > + } + /> + + + } + /> + + } + /> + + } + /> + + } + /> + + } + /> + + {/* } /> */} + + {/* } /> */} + + + + ); +}; diff --git a/src/modules/Root/index.ts b/src/modules/Root/index.ts new file mode 100644 index 0000000000..c78e011529 --- /dev/null +++ b/src/modules/Root/index.ts @@ -0,0 +1 @@ +export * from './Root'; diff --git a/src/modules/constants/index.ts b/src/modules/constants/index.ts new file mode 100644 index 0000000000..1584ae6745 --- /dev/null +++ b/src/modules/constants/index.ts @@ -0,0 +1 @@ +export * from '../../types/MenuItems'; diff --git a/src/notes.txt b/src/notes.txt new file mode 100644 index 0000000000..c36cde3133 --- /dev/null +++ b/src/notes.txt @@ -0,0 +1,7 @@ +Progress: +stopeped: +- greated grid with media and mixins +- finished scss typography + +Next: +- start builwing header diff --git a/src/styles/icons.scss b/src/styles/icons.scss new file mode 100644 index 0000000000..7ff6eba772 --- /dev/null +++ b/src/styles/icons.scss @@ -0,0 +1,18 @@ +@import './mixines'; + +%icon_container { + cursor: pointer; + box-sizing: border-box; + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + transition: background-color 0.3s; + border: 1px solid var(--c-icons); + width: 32px; + height: 32px; + + &:hover { + border-color: var(--c-primary); + } +} diff --git a/src/styles/main.scss b/src/styles/main.scss new file mode 100644 index 0000000000..70e948d709 --- /dev/null +++ b/src/styles/main.scss @@ -0,0 +1,62 @@ +@import './mixines'; +@import './icons'; +@import './typography'; +@import './variables'; +@import './theme'; + +@font-face { + font-family: Mont; + src: url('../../public/fonts/Mont-Regular.otf'); + font-weight: 500; +} + +@font-face { + font-family: Mont; + src: url('../../public/fonts/Mont-SemiBold.otf'); + font-weight: 600; +} + +@font-face { + font-family: Mont; + src: url('../../public/fonts/Mont-Bold.otf'); + font-weight: bold; +} + +body { + margin: 0; + padding: 0; + background-color: var(--c-backgroud); + min-height: 100vh; + box-sizing: border-box; +} + +html { + font-family: Mont, sans-serif; + scroll-behavior: smooth; + box-sizing: border-box; +} + +p { + margin: 0; +} + +h1 { + margin: 0; +} + +h2 { + margin: 0; +} + +h3 { + margin: 0; +} + +%link { + text-decoration: none; + color: var(--c-secondary); +} + +.isActive_link { + @include isActive; +} diff --git a/src/styles/mixines.scss b/src/styles/mixines.scss new file mode 100644 index 0000000000..1403d39d9c --- /dev/null +++ b/src/styles/mixines.scss @@ -0,0 +1,73 @@ +@import './variables'; +@import './theme.scss'; + +@mixin ondesktop { + @media (min-width: $desktop-min-width) { + @content; + } +} + +@mixin ontablet { + @media (min-width: $tablet-min-width) { + @content; + } +} + +@mixin pageGrid { + box-sizing: border-box; + --columns: 4; + + display: grid; + column-gap: 16px; + padding-inline: $mobil-padding-inline; + + + grid-template-columns: repeat(var(--columns), 1fr); + + @include ontablet { + --columns: 12; + padding-inline: $tablet-padding-inline; + } + + @include ondesktop { + --columns: 24; + grid-template-columns: repeat(var(--columns), 32px); + padding-inline: $desktop-padding-inline; + } +} + +@mixin headings { + + h1, + h2, + h3, + h4, + h5, + h6 { + @content; + } +} + +@mixin hover($property: transform, $toValue: scale(1.1)) { + transition: #{$property} 0.3s; + + &:hover { + color: var(--c-primary); + #{$property}: $toValue; + } +} + +@mixin isActive { + border-bottom: 3px solid var(--c-primary); + color: var(--c-primary); +} + +@mixin iconBg($url, $bgColor: var(--c-primary)) { + // mask: #{$url} no-repeat 50% 50% / 16px; + // background-color: #{$bgColor}; + background-image: #{$url}; + width: 16px; + height: 16px; + background-repeat: no-repeat; + background-size: cover; +} diff --git a/src/styles/theme.scss b/src/styles/theme.scss new file mode 100644 index 0000000000..6cc5a4f0b0 --- /dev/null +++ b/src/styles/theme.scss @@ -0,0 +1,24 @@ +.theme { + --c-accent: #F86800; + --c-accent-secondary: #476DF4; + --c-primary: #313237; + --c-secondary: #89939A; + --c-icons: #B4BDC3; + --c-elements: #E2E6E9; + --c-backgroud: #FFF; + --c-white: #FFF; + --c-green: #27ae60; + + &_dark { + --c-accent: #905BFF; + --c-accent-secondary: #476DF4; + --c-primary: #ffffff; + --c-secondary: #89939A; + --c-icons: #4A4D58; + ; + --c-elements: #3B3E4A; + --c-backgroud: #0F1121; + --c-white: #ffffff; + --c-green: #27ae60; + } +} diff --git a/src/styles/typography.scss b/src/styles/typography.scss new file mode 100644 index 0000000000..768bb73004 --- /dev/null +++ b/src/styles/typography.scss @@ -0,0 +1,111 @@ +@import './theme'; +@import './mixines'; + +@include headings { + font-family: Mont; + letter-spacing: 0; + color: var(--c-primary); +} + +body { + font-family: Mont; + margin: 0; +} + +h1 { + //styleName: Mobile/H1; + font-size: 32px; + font-weight: 800; + line-height: 41px; + letter-spacing: -0.01em; + margin: 0; + text-align: left; + + @include ontablet { + //styleName: Desktop & Tablet/H1; + font-size: 48px; + line-height: 56px; + } +} + +h2 { + //styleName: Mobile/H2; + font-size: 22px; + font-weight: 800; + line-height: 30.8px; + margin: 0; + + @include ontablet { + //styleName: Desktop & Tablet/H2; + font-size: 32px; + line-height: 41px; + letter-spacing: -0.01em; + } +} + +h3 { + //styleName: Mobile/H3; + font-size: 20px; + font-weight: 700; + line-height: 25.56px; + margin: 0; + + @include ontablet { + //styleName: Desktop & Tablet/H3; + font-size: 22px; + font-weight: 800; + line-height: 30.8px; + } +} + +h4 { + //styleName: Mobile/H4; + font-size: 16px; + font-weight: 700; + line-height: 20.45px; + margin: 0; + + @include ontablet { + //styleName: Desktop & Tablet/H4; + font-size: 20px; + font-weight: 700; + line-height: 25.56px; + } +} + +p { + //styleName: Body text 14; + font-size: 14px; + font-weight: 600; + line-height: 21px; + color: var(--c-primary); + margin: 0; +} + +%uppercase { + //styleName: Uppercase; + font-size: 12px; + font-weight: 800; + line-height: 11px; + letter-spacing: 0.04em; + text-transform: uppercase; + margin: 0; +} + +%buttons { + //styleName: Uppercase; + font-size: 12px; + font-weight: 800; + line-height: 11px; + letter-spacing: 0.04em; + color: var(--c-white); + margin: 0; +} + +%small-text { + //styleName: Small text 12; + font-size: 12px; + font-weight: 700; + line-height: 15.34px; + margin: 0; +} diff --git a/src/styles/variables.scss b/src/styles/variables.scss new file mode 100644 index 0000000000..b6bb225209 --- /dev/null +++ b/src/styles/variables.scss @@ -0,0 +1,5 @@ +$desktop-min-width: 1200px; +$tablet-min-width: 640px; +$mobil-padding-inline: 16px; +$tablet-padding-inline: 24px; +$desktop-padding-inline: 32px; diff --git a/src/types/MenuItems.ts b/src/types/MenuItems.ts new file mode 100644 index 0000000000..11316969a1 --- /dev/null +++ b/src/types/MenuItems.ts @@ -0,0 +1,5 @@ +export enum MenuItems { + 'phones' = 'phones', + 'tablets' = 'tablets', + 'accessories' = 'accessories', +} diff --git a/src/types/Product.ts b/src/types/Product.ts new file mode 100644 index 0000000000..04549884e1 --- /dev/null +++ b/src/types/Product.ts @@ -0,0 +1,16 @@ +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; + discountPrice?: number; + discount?: number; +} diff --git a/src/types/ProductItem.ts b/src/types/ProductItem.ts new file mode 100644 index 0000000000..1872c27409 --- /dev/null +++ b/src/types/ProductItem.ts @@ -0,0 +1,21 @@ +export interface ProductItem { + id: string; + category: string; + namespaceId: string; + name: string; + capacityAvailable: string[]; + capacity: string; + priceRegular: number; + priceDiscount: number; + colorsAvailable: string[]; + color: string; + images: string[]; + description: { title: string; text: string[] }[]; + screen: string; + resolution: string; + processor: string; + ram: string; + camera: string; + zoom: string; + cell: string[]; +} diff --git a/src/utils/LocalStorage.ts b/src/utils/LocalStorage.ts new file mode 100644 index 0000000000..8b49f6b222 --- /dev/null +++ b/src/utils/LocalStorage.ts @@ -0,0 +1,22 @@ +export const accessLocalStorage = { + key: 'todos', + get() { + const data = localStorage.getItem(this.key); + + try { + return data ? JSON.parse(data) : []; + } catch { + return []; + } + }, + + set(todos: unknown) { + try { + localStorage.setItem(this.key, JSON.stringify(todos)); + + return this.get; + } catch { + return []; + } + }, +}; diff --git a/src/utils/accessLocalStorage.ts b/src/utils/accessLocalStorage.ts new file mode 100644 index 0000000000..8d42d30dd5 --- /dev/null +++ b/src/utils/accessLocalStorage.ts @@ -0,0 +1,28 @@ +// import { MenuItems } from '../types/MenuItems'; +import { Product } from '../types/Product'; +import { ProductItem } from '../types/ProductItem'; + +// type Keys = keyof typeof MenuItems | 'favorites' | 'cart'; +type Keys = 'favorites' | 'cart'; + +export const accessLocalStorage = { + get(key: Keys) { + const data = localStorage.getItem(key); + + try { + return data ? JSON.parse(data) : []; + } catch { + return []; + } + }, + + set(data: Product[] | ProductItem[], key: Keys) { + try { + localStorage.setItem(key, JSON.stringify(data)); + + return this.get(key); + } catch { + return []; + } + }, +}; diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts new file mode 100644 index 0000000000..a412a54d99 --- /dev/null +++ b/src/utils/fetch.ts @@ -0,0 +1,43 @@ +/* eslint-disable @typescript-eslint/no-explicit-any */ +function wait(delay: number) { + return new Promise(resolve => { + setTimeout(resolve, delay); + }); +} + +export const BASE_API_URL = + 'https://syavayki.github.io/react_phone-catalog/api/'; + +type RequestMethod = 'GET' | 'POST' | 'PATCH' | 'DELETE'; + +function request( + url: string, + method: RequestMethod = 'GET', + data: any = null, +): Promise { + const options: RequestInit = { method }; + + if (data) { + options.body = JSON.stringify(data); + options.headers = { + 'Content-Type': 'application/json; charset=UTF-8', + }; + } + + return wait(3000) + .then(() => fetch(BASE_API_URL + url, options)) + .then(response => { + if (!response.ok) { + throw new Error(); + } + + return response.json(); + }); +} + +export const client = { + get: (url: string) => request(url), + post: (url: string, data: any) => request(url, 'POST', data), + patch: (url: string, data: any) => request(url, 'PATCH', data), + delete: (url: string) => request(url, 'DELETE'), +}; diff --git a/src/utils/getCategoriesList.ts b/src/utils/getCategoriesList.ts new file mode 100644 index 0000000000..3b29017a89 --- /dev/null +++ b/src/utils/getCategoriesList.ts @@ -0,0 +1,5 @@ +import { Product } from '../types/Product'; + +export function getCategoriesList(products: Product[]) { + return new Set(products.map(product => product.category)); +} diff --git a/src/utils/getHotDealsProducts.ts b/src/utils/getHotDealsProducts.ts new file mode 100644 index 0000000000..724050903b --- /dev/null +++ b/src/utils/getHotDealsProducts.ts @@ -0,0 +1,39 @@ +import { Product } from '../types/Product'; +import { ProductItem } from '../types/ProductItem'; +import { getProductItemByID } from './getProductItemByID'; + +export function getHotDealsProducts( + products: Product[], + productItems: ProductItem[], +) { + const newProducts: Product[] = products.map(prod => { + let discount = 0; + const prodItem = getProductItemByID(productItems, prod.itemId); + + if (prodItem) { + discount = prodItem.priceRegular - prodItem.priceDiscount; + } + + return { + ...prod, + discount: discount ? discount : 0, + discountPrice: prodItem ? prodItem.priceDiscount : 0, + }; + }); + + const sorted = newProducts.sort((a, b) => { + if (a.discount && b.discount) { + return b.discount - a.discount; + } + + return 0; + }); + + const cleaned: Product[] = sorted.map(item => { + const { discount, ...rest } = item; + + return rest; + }); + + return cleaned; +} diff --git a/src/utils/getNewProducts.ts b/src/utils/getNewProducts.ts new file mode 100644 index 0000000000..2e1fbe576c --- /dev/null +++ b/src/utils/getNewProducts.ts @@ -0,0 +1,5 @@ +import { Product } from '../types/Product'; + +export function getNewProducts(products: Product[]) { + return products.filter(product => product.year >= 2022); +} diff --git a/src/utils/getProductById.ts b/src/utils/getProductById.ts new file mode 100644 index 0000000000..f925510b11 --- /dev/null +++ b/src/utils/getProductById.ts @@ -0,0 +1,5 @@ +import { Product } from '../types/Product'; + +export function getProductById(products: Product[], itemId: string) { + return products.find(product => product.itemId === itemId); +} diff --git a/src/utils/getProductItemByID.ts b/src/utils/getProductItemByID.ts new file mode 100644 index 0000000000..74fb147b29 --- /dev/null +++ b/src/utils/getProductItemByID.ts @@ -0,0 +1,5 @@ +import { ProductItem } from '../types/ProductItem'; + +export function getProductItemByID(products: ProductItem[], id: string) { + return products.find(product => product.id === id); +} diff --git a/src/utils/getProducts.ts b/src/utils/getProducts.ts new file mode 100644 index 0000000000..7111f4c6e2 --- /dev/null +++ b/src/utils/getProducts.ts @@ -0,0 +1,6 @@ +import { Product } from '../types/Product'; +import { client } from './fetch'; + +export function getProducts(): Promise { + return client.get('products.json'); +} diff --git a/src/utils/getProductsByCategory.ts b/src/utils/getProductsByCategory.ts new file mode 100644 index 0000000000..cdec6ad0f1 --- /dev/null +++ b/src/utils/getProductsByCategory.ts @@ -0,0 +1,8 @@ +import { ProductItem } from '../types/ProductItem'; +import { client } from './fetch'; + +export function getProductsByCategory( + category: string, +): Promise { + return client.get(`${category}.json`); +} From 6f75e2673103d0a662d4a606d3f2ed7cde86eb10 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Thu, 14 Nov 2024 13:08:54 -0500 Subject: [PATCH 02/20] completed --- .../BreadCrumbs/BreadCrumbs.module.scss | 59 ++++ src/components/BreadCrumbs/BreadCrumbs.tsx | 71 ++++ src/components/BreadCrumbs/index.ts | 1 + .../ButtonsAddCardFav.module.scss | 41 +-- .../ButtonsAddCardFav/ButtonsAddCardFav.tsx | 25 +- .../FavoriteIcon/FavoriteIcon.module.scss | 32 +- .../FavoriteIcon/FavoriteIcon.tsx | 17 +- .../CapacitySelector.module.scss | 7 + .../CapacitySelector/CapacitySelector.tsx | 39 +++ src/components/CapacitySelector/index.ts | 1 + src/components/Catalog/Catalog.module.scss | 55 +++ src/components/Catalog/Catalog.tsx | 103 ++++++ .../Catalog/Pagination/Pagination.module.scss | 67 ++++ .../Catalog/Pagination/Pagination.tsx | 166 +++++++++ src/components/Catalog/Pagination/index.ts | 1 + .../Categories/Categories.module.scss | 2 +- .../ColorSelector/ColorSelector.module.scss | 41 +++ .../ColorSelector/ColorSelector.tsx | 44 +++ src/components/ColorSelector/index.ts | 1 + .../FavCarIcons/FavCarIcons.module.scss | 34 +- src/components/FavCarIcons/FavCarIcons.tsx | 18 +- src/components/Footer/Footer.module.scss | 68 ++-- src/components/Footer/Footer.tsx | 14 +- .../FooterLinks/FooterLinks.module.scss | 12 +- .../Footer/FooterLinks/FooterLinks.tsx | 2 +- src/components/GlobalProvider.tsx | 148 +++----- .../MenuCloseIcons/MenuCloseIcons.module.scss | 10 +- .../HeaderLinks/HeaderLinks.module.scss | 1 + src/components/PhoneSlider/PhoneSlider.scss | 44 +-- src/components/PhoneSlider/PhoneSlider.tsx | 22 +- .../PicturesSlider/PictureSlider.scss | 130 +++---- .../PicturesSlider/PicturesSlider.tsx | 8 +- .../ProductCard/ProductCard.module.scss | 91 +++-- src/components/ProductCard/ProductCard.tsx | 36 +- src/components/ProductCard/index.ts | 2 +- src/components/SiteLogo/SiteLogo.module.scss | 9 +- .../SortFilter/Selector/Selector.module.scss | 93 +++++ .../SortFilter/Selector/Selector.tsx | 106 ++++++ src/components/SortFilter/Selector/index.ts | 1 + .../SortFilter/SortFilter.module.scss | 35 ++ src/components/SortFilter/SortFilter.tsx | 33 ++ src/components/SortFilter/index.ts | 1 + src/icons/Close.svg | 43 ++- src/icons/Cursor Mouse Mice.png | Bin 821 -> 0 bytes src/icons/Home.png | Bin 370 -> 0 bytes src/icons/Menu.svg | 53 ++- src/icons/Minus.png | Bin 160 -> 0 bytes src/icons/Plus.png | Bin 228 -> 0 bytes src/icons/Search.png | Bin 401 -> 0 bytes src/icons/Shopping bag + Counter(Cart).png | Bin 794 -> 0 bytes src/icons/arrowDown_disabled.svg | 3 + src/icons/arrowLeft.svg | 15 +- src/icons/arrowRight.svg | 15 +- src/icons/arrowUp.svg | 43 ++- src/icons/arrowUp_disabled.svg | 3 + src/icons/cart.svg | 59 +++- src/icons/darkMode/arrowDown_disabled.svg | 42 +++ src/icons/darkMode/arrowLeft.svg | 42 +++ src/icons/darkMode/arrowLeft_disabled.svg | 42 +++ src/icons/darkMode/arrowRight.svg | 42 +++ src/icons/darkMode/arrowRight_disabled.svg | 42 +++ src/icons/darkMode/arrowUp.svg | 42 +++ src/icons/darkMode/arrowUp_disabled.svg | 42 +++ src/icons/darkMode/cart.svg | 56 +++ src/icons/darkMode/close.svg | 42 +++ src/icons/darkMode/dark_mode.svg | 46 +++ src/icons/darkMode/favorite.svg | 42 +++ src/icons/darkMode/home.svg | 49 +++ src/icons/darkMode/menu.svg | 50 +++ src/icons/darkMode/minus.svg | 42 +++ src/icons/darkMode/minus_disabled.svg | 42 +++ src/icons/darkMode/mouse-cursor.svg | 16 + src/icons/darkMode/plus.svg | 42 +++ src/icons/dark_mode.svg | 48 ++- src/icons/favorite.svg | 43 ++- src/icons/home.svg | 49 +++ src/icons/icons8-dark-mode-64.png | Bin 1593 -> 0 bytes src/icons/minus.svg | 42 +++ src/icons/minus_disabled.svg | 42 +++ src/icons/mouse-cursor.svg | 16 + src/icons/plus.svg | 42 +++ .../AccessoriesPage.module.scss | 6 + .../AccessoriesPage/AccessoriesPage.tsx | 23 ++ src/modules/AccessoriesPage/index.ts | 1 + src/modules/App/App.module.scss | 11 +- src/modules/App/App.tsx | 25 +- .../CartPage/CartItem/CartItem.module.scss | 132 +++++++ src/modules/CartPage/CartItem/CartItem.tsx | 82 +++++ src/modules/CartPage/CartItem/index.ts | 1 + src/modules/CartPage/CartPage.module.scss | 162 ++++++++- src/modules/CartPage/CartPage.tsx | 190 ++++++++++- .../FavotitePage/FavoritePage.module.scss | 6 + src/modules/FavotitePage/FavoritePage.tsx | 21 ++ .../FavotitePage/FavotitePage.module.scss | 1 - src/modules/FavotitePage/FavotitePage.tsx | 3 - src/modules/FavotitePage/index.ts | 2 +- src/modules/HomePage/HomePage.module.scss | 39 +-- src/modules/HomePage/HomePage.tsx | 19 +- src/modules/PhonePage/PhonePage.module.scss | 6 + src/modules/PhonePage/PhonePage.tsx | 76 +---- .../ItemPhoto/ItemPhoto.tsx | 57 ++++ .../ProductDetailsPage/ItemPhoto/index.ts | 1 + .../ProductDetailsPage/ItemPhoto/styles.scss | 63 ++++ .../ProductDetailsPage.module.scss | 295 ++++++++++++++++ .../ProductDetailsPage/ProductDetailsPage.tsx | 322 ++++++++++++++++++ src/modules/ProductDetailsPage/index.ts | 1 + src/modules/Root/Root.tsx | 49 ++- src/modules/TabletPage/TabletPage.module.scss | 6 + src/modules/TabletPage/TabletPage.tsx | 20 ++ src/modules/TabletPage/index.ts | 1 + src/modules/constants/index.ts | 2 +- src/modules/constants/productColors.ts | 45 +++ src/styles/buttons.scss | 33 ++ src/styles/icons.scss | 4 +- src/styles/main.scss | 14 +- src/styles/mixines.scss | 26 +- src/styles/theme.scss | 61 +++- src/styles/typography.scss | 19 +- src/types/SearchParams.ts | 7 + src/types/SortFilter.ts | 5 + src/utils/LocalAccessKeys.ts | 4 + src/utils/LocalStorage.ts | 22 -- src/utils/accessLocalStorage.ts | 61 +++- src/utils/catalogHelper.ts | 50 +++ src/utils/fetch.ts | 4 +- src/utils/getCategoriesList.ts | 5 - src/utils/getHotDealsProducts.ts | 39 --- src/utils/getNewProducts.ts | 5 - src/utils/getProductById.ts | 5 - src/utils/getProductItemByID.ts | 5 - src/utils/getProductItems.ts | 62 ++++ src/utils/getProducts.ts | 48 ++- src/utils/getProductsByCategory.ts | 8 - src/utils/sentenseFormating.ts | 3 + 134 files changed, 4410 insertions(+), 676 deletions(-) create mode 100644 src/components/BreadCrumbs/BreadCrumbs.module.scss create mode 100644 src/components/BreadCrumbs/BreadCrumbs.tsx create mode 100644 src/components/BreadCrumbs/index.ts create mode 100644 src/components/CapacitySelector/CapacitySelector.module.scss create mode 100644 src/components/CapacitySelector/CapacitySelector.tsx create mode 100644 src/components/CapacitySelector/index.ts create mode 100644 src/components/Catalog/Catalog.module.scss create mode 100644 src/components/Catalog/Catalog.tsx create mode 100644 src/components/Catalog/Pagination/Pagination.module.scss create mode 100644 src/components/Catalog/Pagination/Pagination.tsx create mode 100644 src/components/Catalog/Pagination/index.ts create mode 100644 src/components/ColorSelector/ColorSelector.module.scss create mode 100644 src/components/ColorSelector/ColorSelector.tsx create mode 100644 src/components/ColorSelector/index.ts create mode 100644 src/components/SortFilter/Selector/Selector.module.scss create mode 100644 src/components/SortFilter/Selector/Selector.tsx create mode 100644 src/components/SortFilter/Selector/index.ts create mode 100644 src/components/SortFilter/SortFilter.module.scss create mode 100644 src/components/SortFilter/SortFilter.tsx create mode 100644 src/components/SortFilter/index.ts delete mode 100644 src/icons/Cursor Mouse Mice.png delete mode 100644 src/icons/Home.png delete mode 100644 src/icons/Minus.png delete mode 100644 src/icons/Plus.png delete mode 100644 src/icons/Search.png delete mode 100644 src/icons/Shopping bag + Counter(Cart).png create mode 100644 src/icons/arrowDown_disabled.svg create mode 100644 src/icons/arrowUp_disabled.svg create mode 100644 src/icons/darkMode/arrowDown_disabled.svg create mode 100644 src/icons/darkMode/arrowLeft.svg create mode 100644 src/icons/darkMode/arrowLeft_disabled.svg create mode 100644 src/icons/darkMode/arrowRight.svg create mode 100644 src/icons/darkMode/arrowRight_disabled.svg create mode 100644 src/icons/darkMode/arrowUp.svg create mode 100644 src/icons/darkMode/arrowUp_disabled.svg create mode 100644 src/icons/darkMode/cart.svg create mode 100644 src/icons/darkMode/close.svg create mode 100644 src/icons/darkMode/dark_mode.svg create mode 100644 src/icons/darkMode/favorite.svg create mode 100644 src/icons/darkMode/home.svg create mode 100644 src/icons/darkMode/menu.svg create mode 100644 src/icons/darkMode/minus.svg create mode 100644 src/icons/darkMode/minus_disabled.svg create mode 100644 src/icons/darkMode/mouse-cursor.svg create mode 100644 src/icons/darkMode/plus.svg create mode 100644 src/icons/home.svg delete mode 100644 src/icons/icons8-dark-mode-64.png create mode 100644 src/icons/minus.svg create mode 100644 src/icons/minus_disabled.svg create mode 100644 src/icons/mouse-cursor.svg create mode 100644 src/icons/plus.svg create mode 100644 src/modules/AccessoriesPage/AccessoriesPage.module.scss create mode 100644 src/modules/AccessoriesPage/AccessoriesPage.tsx create mode 100644 src/modules/AccessoriesPage/index.ts create mode 100644 src/modules/CartPage/CartItem/CartItem.module.scss create mode 100644 src/modules/CartPage/CartItem/CartItem.tsx create mode 100644 src/modules/CartPage/CartItem/index.ts create mode 100644 src/modules/FavotitePage/FavoritePage.module.scss create mode 100644 src/modules/FavotitePage/FavoritePage.tsx delete mode 100644 src/modules/FavotitePage/FavotitePage.module.scss delete mode 100644 src/modules/FavotitePage/FavotitePage.tsx create mode 100644 src/modules/ProductDetailsPage/ItemPhoto/ItemPhoto.tsx create mode 100644 src/modules/ProductDetailsPage/ItemPhoto/index.ts create mode 100644 src/modules/ProductDetailsPage/ItemPhoto/styles.scss create mode 100644 src/modules/ProductDetailsPage/ProductDetailsPage.module.scss create mode 100644 src/modules/ProductDetailsPage/ProductDetailsPage.tsx create mode 100644 src/modules/ProductDetailsPage/index.ts create mode 100644 src/modules/TabletPage/TabletPage.module.scss create mode 100644 src/modules/TabletPage/TabletPage.tsx create mode 100644 src/modules/TabletPage/index.ts create mode 100644 src/modules/constants/productColors.ts create mode 100644 src/styles/buttons.scss create mode 100644 src/types/SearchParams.ts create mode 100644 src/types/SortFilter.ts create mode 100644 src/utils/LocalAccessKeys.ts delete mode 100644 src/utils/LocalStorage.ts create mode 100644 src/utils/catalogHelper.ts delete mode 100644 src/utils/getCategoriesList.ts delete mode 100644 src/utils/getHotDealsProducts.ts delete mode 100644 src/utils/getNewProducts.ts delete mode 100644 src/utils/getProductById.ts delete mode 100644 src/utils/getProductItemByID.ts create mode 100644 src/utils/getProductItems.ts delete mode 100644 src/utils/getProductsByCategory.ts create mode 100644 src/utils/sentenseFormating.ts diff --git a/src/components/BreadCrumbs/BreadCrumbs.module.scss b/src/components/BreadCrumbs/BreadCrumbs.module.scss new file mode 100644 index 0000000000..c6c3d54bf1 --- /dev/null +++ b/src/components/BreadCrumbs/BreadCrumbs.module.scss @@ -0,0 +1,59 @@ +@import '../../styles/main'; + +.breadcrumbs_container { + display: flex; +} + +.crumb { + @extend %small-text; + + text-decoration: none; + font-weight: 600; + color: var(--c-primary); + + &_last { + color: var(--c-secondary); + cursor: default !important; + } + + + + &_container { + display: flex; + align-items: center; + gap: 8px; + padding-right: 8px; + } +} + +.breadcrumbs_container .icon { + display: flex; + align-items: center; + + &_container { + @extend %icon-container; + + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + border: none; + } + + &_right { + @include icon-bg(var(--fls-right-disabled)); + + width: 16px; + height: 16px; + } + + &_home { + @include icon-bg(var(--fls-home)); + + width: 16px; + height: 16px; + cursor: pointer; + } +} diff --git a/src/components/BreadCrumbs/BreadCrumbs.tsx b/src/components/BreadCrumbs/BreadCrumbs.tsx new file mode 100644 index 0000000000..889a6628a0 --- /dev/null +++ b/src/components/BreadCrumbs/BreadCrumbs.tsx @@ -0,0 +1,71 @@ +import { NavLink, useLocation } from 'react-router-dom'; +import style from './BreadCrumbs.module.scss'; +import classNames from 'classnames'; +import { sentenseFormating } from '../../utils/sentenseFormating'; +import { Product } from '../../types/Product'; +import { ProductItem } from '../../types/ProductItem'; + +type Props = { + product?: Product | ProductItem; +}; +export const BreadCrumbs: React.FC = ({ product }) => { + const location = useLocation(); + + return ( +
+ {location.pathname.split('/').map((crumb, index, arr) => { + const icon = ( +
+
+
+ ); + + if (index !== arr.length - 1) { + if (index === 0) { + return ( +
+ +
+
+
+ + {icon} +
+ ); + } + + return ( +
+ + {sentenseFormating(crumb)} + + {icon} +
+ ); + } + + return ( + + {product ? product.name : sentenseFormating(crumb)} + + ); + })} +
+ ); +}; diff --git a/src/components/BreadCrumbs/index.ts b/src/components/BreadCrumbs/index.ts new file mode 100644 index 0000000000..8ffa35f61f --- /dev/null +++ b/src/components/BreadCrumbs/index.ts @@ -0,0 +1 @@ +export * from './BreadCrumbs'; diff --git a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss index 530094d4d5..d78419a370 100644 --- a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss +++ b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss @@ -1,44 +1,23 @@ -@import '../../styles/main.scss'; +@import '../../styles/main'; .container { + box-sizing: border-box; width: 100%; - height: 40px; + height: 100%; display: flex; gap: 8px; -} - -.addToCart { - &_container { - background-color: var(--c-primary); - height: 100%; + &_addToCart { + box-sizing: border-box; width: 100%; - display: flex; - align-items: center; - justify-content: center; - - &:hover { - box-shadow: 0px 3px 13px 0px #17203166; - } - - &_inCart { - background-color: var(--c-backgroud); - border: 1px solid var(--c-elements) - } } - &_text { - @extend %buttons; + &_favorite { + box-sizing: border-box; + aspect-ratio: 1/1; - &_inCart { - color: var(--c-green); + &:hover { + box-shadow: 0 3px 13px 0 #17203166; } } } - -.favorite_container { - - &:hover { - box-shadow: 0px 3px 13px 0px #17203166; - } -} diff --git a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx index ed724e62cb..3504127dac 100644 --- a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx +++ b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.tsx @@ -3,36 +3,39 @@ import classNames from 'classnames'; import React, { useContext } from 'react'; import { DispatchContext, StateContext } from '../GlobalProvider'; import { FavoriteIcon } from './FavoriteIcon'; +import { Product } from '../../types/Product'; type Props = { productId: string; }; export const ButtonsAddCardFav: React.FC = ({ productId }) => { - const { productsInCart } = useContext(StateContext); + const { inCart } = useContext(StateContext); const dispatch = useContext(DispatchContext); - const prodInCart = !!productsInCart.find(prod => prod.itemId === productId); + const prodInCart = inCart + ? !!inCart.find((prod: Product) => prod.itemId === productId) + : false; return (
- dispatch({ type: 'setCartProducts', payload: productId }) - } + onClick={() => { + dispatch({ type: 'toggleInCart', payload: productId }); + }} >
- Add to cart + {!prodInCart ? 'Add to cart' : 'Added to cart'}
-
+
diff --git a/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss index 3cecded326..dfd36a2b30 100644 --- a/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss +++ b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.module.scss @@ -1,36 +1,18 @@ -@import '../../../styles/main.scss'; - -.container { - display: flex; - justify-content: flex-end; - align-items: center; - width: 100%; - height: 100%; - - &_icon { - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; - height: 100%; - } -} +@import '../../../styles/main'; .icon { &_container { - @extend %icon_container; - width: 48px; - height: 100% + @extend %icon-container; + + height: 100%; + width: 100%; } &_favorite { - @include iconBg(url('../../../icons/favorite.svg')); - - + @include icon-bg(var(--fls-favorite)); } - } .selected { - @include iconBg(url('../../../icons/favorite_selected.svg')); + @include icon-bg(var(--fls-favorite-selected)); } diff --git a/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx index ffc5927650..995f54a346 100644 --- a/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx +++ b/src/components/ButtonsAddCardFav/FavoriteIcon/FavoriteIcon.tsx @@ -2,25 +2,24 @@ import style from './FavoriteIcon.module.scss'; import classNames from 'classnames'; import React, { useContext } from 'react'; import { DispatchContext, StateContext } from '../../GlobalProvider'; +import { Product } from '../../../types/Product'; type Props = { curProductId: string; }; export const FavoriteIcon: React.FC = ({ curProductId }) => { - const { productsInFavorive } = useContext(StateContext); + const { inFavorites } = useContext(StateContext); const dispatch = useContext(DispatchContext); - const prodInFavorite = !!productsInFavorive.find( - prod => prod.itemId === curProductId, - ); + + const prodInFavorite = inFavorites + ? !!inFavorites.find((prod: Product) => prod.itemId === curProductId) + : false; return (
- dispatch({ type: 'setFavoriteProducts', payload: curProductId }) + dispatch({ type: 'toggleInFavorites', payload: curProductId }) } >
void; +}; +export const CapacitySelector: React.FC = ({ + capacities, + selectedCapacity, + onClick, +}) => { + return ( +
+ {capacities.map(capacity => { + return ( +
{ + onClick(capacity); + }} + > +
+ {capacity} +
+
+ ); + })} +
+ ); +}; diff --git a/src/components/CapacitySelector/index.ts b/src/components/CapacitySelector/index.ts new file mode 100644 index 0000000000..a62c133303 --- /dev/null +++ b/src/components/CapacitySelector/index.ts @@ -0,0 +1 @@ +export * from './CapacitySelector'; diff --git a/src/components/Catalog/Catalog.module.scss b/src/components/Catalog/Catalog.module.scss new file mode 100644 index 0000000000..c05d2238a7 --- /dev/null +++ b/src/components/Catalog/Catalog.module.scss @@ -0,0 +1,55 @@ +@import '../../styles/main'; + +.catalog_container { + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 24px; + padding-block: 24px 64px; + width: 100%; + + @include inline-padding; + + & .container { + + &_title { + @include ontablet { + grid-area: 2/ 1/ 2/ -1; + } + + &_text { + padding-bottom: 8px; + } + + &_count { + color: var(--c-secondary); + } + } + + &_catalog { + display: flex; + flex-flow: row wrap; + gap: 40px; + width: 100%; + justify-content: center; + + + @include ontablet { + flex-direction: row; + gap: 16px; + } + } + + &_product { + display: flex; + flex-direction: row; + width: 220px; + + @include ontablet { + flex-wrap: wrap; + width: 230px; + + } + } + } +} diff --git a/src/components/Catalog/Catalog.tsx b/src/components/Catalog/Catalog.tsx new file mode 100644 index 0000000000..bc83554be5 --- /dev/null +++ b/src/components/Catalog/Catalog.tsx @@ -0,0 +1,103 @@ +import classNames from 'classnames'; +import { Product } from '../../types/Product'; +import style from './Catalog.module.scss'; +import { BreadCrumbs } from '../BreadCrumbs'; +import { SortFilter } from '../SortFilter'; +import { ProductCard } from '../ProductCard'; +import { useSearchParams } from 'react-router-dom'; +import { SearchParams } from '../../types/SearchParams'; +import { catalogHelper } from '../../utils/catalogHelper'; +import { useEffect, useState } from 'react'; +import { Pagination } from './Pagination'; +import { getProductItems } from '../../utils/getProductItems'; +import { Loader } from '../Loader'; + +type Props = { + title: string; + products: Product[]; + sortPerPageEnable?: boolean; +}; +export const Catalog: React.FC = ({ + title, + products, + sortPerPageEnable = true, +}) => { + const [searchParams] = useSearchParams(); + const [sortedProducts, setSortedProducts] = useState([]); + const [loading, setLoading] = useState(false); + const [pages, setPages] = useState([[]]); + const [curPage, setCurPage] = useState(0); + + useEffect(() => { + const category = products[0]?.category; + + if (category) { + setLoading(true); + getProductItems + .fetchByCategory(category) + .finally(() => setLoading(false)); + } + }, [products]); + + useEffect(() => { + setSortedProducts(() => + catalogHelper.sort(searchParams.get(SearchParams.order), products), + ); + + setCurPage(() => catalogHelper.getCurrenPageParam(searchParams) - 1); + }, [searchParams, products]); + + useEffect(() => { + setPages(() => + catalogHelper.perPage( + searchParams.get(SearchParams.perPage), + sortedProducts, + ), + ); + }, [sortedProducts, searchParams, products]); + + return ( +
+ {loading ? ( + + ) : ( + <> +
+ +
+ +
+

{title}

+

+ {products.length !== 1 + ? `${products.length} models` + : 'no models'} +

+
+ +
+ {sortPerPageEnable && } +
+ +
+ {pages[curPage] && + pages[curPage].map(product => { + return ( +
+ +
+ ); + })} +
+ +
+ {pages.length > 1 && } +
+ + )} +
+ ); +}; diff --git a/src/components/Catalog/Pagination/Pagination.module.scss b/src/components/Catalog/Pagination/Pagination.module.scss new file mode 100644 index 0000000000..2aaaadd6e9 --- /dev/null +++ b/src/components/Catalog/Pagination/Pagination.module.scss @@ -0,0 +1,67 @@ +@import '../../../styles/main'; + +.pagination_container { + display: flex; + align-items: center; + justify-content: center; + height: 32px; + + & .icon { + &_container { + box-sizing: border-box; + + @extend %icon-container; + + width: 32px; + height: 32px; + background-color: var(--c-buttons-nav); + + + } + + &_Left { + @include icon-bg(var(--fls-left)); + + &_disabled { + @include icon-bg(var(--fls-left-disabled)); + } + } + + &_Right { + @include icon-bg(var(--fls-right)); + + &_disabled { + @include icon-bg(var(--fls-right-disabled)); + } + } + } + + .pages_container { + display: flex; + gap: 8px; + padding-inline: 8px; + + & .page_txt { + line-height: 100%; + vertical-align: middle; + + } + + & .to_end { + display: flex; + gap: 4px; + } + + } + + & .selected_container { + background-color: var(--c-primary); + border-color: var(--c-primary); + } + + & .selected_text { + color: var(--c-background); + + } + +} diff --git a/src/components/Catalog/Pagination/Pagination.tsx b/src/components/Catalog/Pagination/Pagination.tsx new file mode 100644 index 0000000000..998a73598f --- /dev/null +++ b/src/components/Catalog/Pagination/Pagination.tsx @@ -0,0 +1,166 @@ +import classNames from 'classnames'; +import { Product } from '../../../types/Product'; +import style from './Pagination.module.scss'; +import { useSearchParams } from 'react-router-dom'; +import { SearchParams } from '../../../types/SearchParams'; +import { useEffect, useMemo, useState } from 'react'; +import { catalogHelper } from '../../../utils/catalogHelper'; + +const generatePages = (pages: Product[][]) => { + const pgObj = Array.from({ length: pages.length }, (_, i) => i + 1); + + return pgObj; +}; + +type Props = { + pages: Product[][]; +}; +export const Pagination: React.FC = ({ pages }) => { + const [searchParams, setSearchParams] = useSearchParams(); + const [currentPage, setCurrentPage] = useState(1); + const [prevPage, setPrevPage] = useState(1); + const [visiblePages, setVisiblePages] = useState([]); + + const allPages = useMemo(() => generatePages(pages), [pages]); + + useEffect(() => { + setPrevPage(currentPage); + setCurrentPage(catalogHelper.getCurrenPageParam(searchParams)); + }, [searchParams, currentPage]); + + useEffect(() => { + const newVisPgs = allPages; + + if (currentPage < 4) { + setVisiblePages(() => newVisPgs.slice(0, 4)); + + return; + } + + if (currentPage > newVisPgs.length - 3) { + setVisiblePages(() => newVisPgs.slice(newVisPgs.length - 4)); + + return; + } + + if (currentPage > prevPage) { + setVisiblePages(newVisPgs.slice(currentPage - 3, currentPage + 1)); + } else { + setVisiblePages(newVisPgs.slice(currentPage - 2, currentPage + 2)); + } + }, [currentPage, prevPage, allPages]); + + const handlePageSet = (pageNum: number) => { + window.scrollTo(0, 0); + const params = new URLSearchParams(searchParams); + + params.set(SearchParams.page, pageNum.toString()); + setSearchParams(() => params); + }; + + const handleNext = (direction: 'next' | 'prev') => { + const params = new URLSearchParams(searchParams); + let newPageNum = currentPage; + + switch (direction) { + case 'prev': + if (currentPage > 1) { + newPageNum--; + } + + break; + + case 'next': + if (currentPage < pages.length) { + newPageNum++; + } + + break; + } + + params.set(SearchParams.page, newPageNum.toString()); + setSearchParams(() => params); + }; + + return ( +
+
handleNext('prev')} + > +
+
+ +
+ {currentPage > 3 && ( +
+
handlePageSet(1)} + > +
+

1

+
+
+

...

+
+ )} + + {visiblePages.map(pg => { + const selected = pg === currentPage; + + return ( +
handlePageSet(pg)} + > +
+

+ {pg} +

+
+
+ ); + })} + + {currentPage < allPages.length - 2 && ( +
+

...

+ +
handlePageSet(allPages[allPages.length - 1])} + > +
+

+ {allPages[allPages.length - 1]} +

+
+
+
+ )} +
+
handleNext('next')} + > +
+
+
+ ); +}; diff --git a/src/components/Catalog/Pagination/index.ts b/src/components/Catalog/Pagination/index.ts new file mode 100644 index 0000000000..e016c96b72 --- /dev/null +++ b/src/components/Catalog/Pagination/index.ts @@ -0,0 +1 @@ +export * from './Pagination'; diff --git a/src/components/Categories/Categories.module.scss b/src/components/Categories/Categories.module.scss index 140e4c5f54..230d3eb060 100644 --- a/src/components/Categories/Categories.module.scss +++ b/src/components/Categories/Categories.module.scss @@ -1,4 +1,4 @@ -@import '../../styles/main.scss'; +@import '../../styles/main'; .container { display: flex; diff --git a/src/components/ColorSelector/ColorSelector.module.scss b/src/components/ColorSelector/ColorSelector.module.scss new file mode 100644 index 0000000000..c1b5a74dd4 --- /dev/null +++ b/src/components/ColorSelector/ColorSelector.module.scss @@ -0,0 +1,41 @@ +@import '../../styles/main'; + +.color_selector_container { + display: flex; + flex-direction: column; + gap: 8px; + + & .title { + color: var(--c-secondary); + } +} + +.colors_container { + box-sizing: border-box; + display: flex; + gap: 8px; + + & .circle { + box-sizing: border-box; + display: flex; + align-items: center; + border-radius: 50%; + } + + & .outer { + display: flex; + height: 32px; + width: 32px; + border: 2px solid var(--c-elements); + } + + & .inner { + border: 3px solid var(--c-white); + width: 100%; + height: 100%; + } + + & .selected { + border-color: var(--c-primary); + } +} diff --git a/src/components/ColorSelector/ColorSelector.tsx b/src/components/ColorSelector/ColorSelector.tsx new file mode 100644 index 0000000000..2de4f5b06f --- /dev/null +++ b/src/components/ColorSelector/ColorSelector.tsx @@ -0,0 +1,44 @@ +import style from './ColorSelector.module.scss'; +import { productColors } from '../../modules/constants'; +import classNames from 'classnames'; + +type Props = { + colors: string[]; + selectedColor: string; + onClick: (color: string) => void; +}; + +export const ColorSelector: React.FC = ({ + colors, + selectedColor, + onClick, +}) => { + return ( +
+

Available colors

+
+ {colors && + colors.map(color => { + const cleanColor = color.replace(' ', ''); + + return ( +
onClick(cleanColor)} + > +
+
+ ); + })} +
+
+ ); +}; diff --git a/src/components/ColorSelector/index.ts b/src/components/ColorSelector/index.ts new file mode 100644 index 0000000000..3bca0bb79c --- /dev/null +++ b/src/components/ColorSelector/index.ts @@ -0,0 +1 @@ +export * from './ColorSelector'; diff --git a/src/components/FavCarIcons/FavCarIcons.module.scss b/src/components/FavCarIcons/FavCarIcons.module.scss index 68746ad3f4..2090647baf 100644 --- a/src/components/FavCarIcons/FavCarIcons.module.scss +++ b/src/components/FavCarIcons/FavCarIcons.module.scss @@ -24,8 +24,34 @@ } .icon { + display: flex; + align-items: center; + justify-content: center; + position: relative; + + + & .container_count { + position: absolute; + display: flex; + align-items: center; + justify-content: center; + width: 14px; + height: 14px; + border: 1px solid var(--c-white); + border-radius: 50%; + background-color: red; + left: 50%; + bottom: 50%; + } + + & .count { + font-size: 9px; + font-weight: 700; + color: var(--c-white); + } + &_container { - @extend %icon_container; + @extend %icon-container; @include ontablet { width: 48px; @@ -39,16 +65,16 @@ } &_dark { - @include iconBg(url('../../icons/dark_mode.svg')); + @include icon-bg(var(--fls-dark-mode)); } &_favorite { - @include iconBg(url('../../icons/favorite.svg')); + @include icon-bg(var(--fls-favorite)); } &_cart { - @include iconBg(url('../../icons/cart.svg')); + @include icon-bg(var(--fls-cart)); } } diff --git a/src/components/FavCarIcons/FavCarIcons.tsx b/src/components/FavCarIcons/FavCarIcons.tsx index dab64cb903..4987941c9c 100644 --- a/src/components/FavCarIcons/FavCarIcons.tsx +++ b/src/components/FavCarIcons/FavCarIcons.tsx @@ -8,7 +8,7 @@ import { DispatchContext, StateContext } from '../GlobalProvider'; type Props = { mobileView?: boolean }; export const FavCarIcons: React.FC = ({ mobileView = false }) => { const dispatch = useContext(DispatchContext); - const { inDarkMode } = useContext(StateContext); + const { inDarkMode, inCart, inFavorites } = useContext(StateContext); return (
@@ -43,7 +43,13 @@ export const FavCarIcons: React.FC = ({ mobileView = false }) => { } onClick={() => dispatch({ type: 'setShowMenu', payload: false })} > -
+
+ {inFavorites.length > 0 && ( +
+ {inFavorites.length} +
+ )} +
= ({ mobileView = false }) => { } onClick={() => dispatch({ type: 'setShowMenu', payload: false })} > -
+
+ {inCart.length > 0 && ( +
+ {inCart.length} +
+ )} +
diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss index 94c052e72b..abff866f1d 100644 --- a/src/components/Footer/Footer.module.scss +++ b/src/components/Footer/Footer.module.scss @@ -1,49 +1,65 @@ @import '../../styles/main'; -.container { - display: flex; - flex-direction: column; - justify-content: space-between; - row-gap: 32px; - height: 100%; - width: 100%; - padding: 32px 16px; - - box-shadow: 0 1px 0 0 var(--c-elements); - - &_logo { - display: flex; - box-sizing: border-box; - align-items: center; - height: 32px; - width: 100%; - } +.footer { - &_back_top { + &_container { display: flex; - justify-content: center; - align-items: center; - column-gap: 16px; + flex-direction: column; + justify-content: space-between; + row-gap: 32px; + height: 100%; width: 100%; + padding: 32px 16px; + + box-shadow: 0 1px 0 0 var(--c-elements); + + @include ontablet { + flex-direction: row; + padding: 32px; + } + + &_logo { + display: flex; + box-sizing: border-box; + align-items: center; + height: 32px; + width: 100%; + } + + &_back_top { + display: flex; + justify-content: center; + align-items: center; + column-gap: 16px; + width: 100%; + + @include ontablet { + justify-content: flex-end + } + + } } } .back_top_text { @extend %small-text; - cursor: pointer; + + cursor: default; color: var(--c-secondary); } .icon { &_container { box-sizing: border-box; - @extend %icon_container; + + @extend %icon-container; + width: 32px; height: 32px; + background-color: var(--c-buttons-nav); } &_upArrow { - @include iconBg(url('../../icons/arrowUp.svg')); - + @include icon-bg(var(--fls-up)); } } diff --git a/src/components/Footer/Footer.tsx b/src/components/Footer/Footer.tsx index 38c4b95f5d..0d003dea01 100644 --- a/src/components/Footer/Footer.tsx +++ b/src/components/Footer/Footer.tsx @@ -10,16 +10,16 @@ export const Footer = () => { }; return ( -
-
+
+
-
+
-
+
scrollToTop()} @@ -32,11 +32,9 @@ export const Footer = () => { style.icon_container, style.icon_container_upArrow, )} + onClick={() => scrollToTop()} > -
scrollToTop()} - /> +
diff --git a/src/components/Footer/FooterLinks/FooterLinks.module.scss b/src/components/Footer/FooterLinks/FooterLinks.module.scss index 701a8674ca..cf1895e445 100644 --- a/src/components/Footer/FooterLinks/FooterLinks.module.scss +++ b/src/components/Footer/FooterLinks/FooterLinks.module.scss @@ -1,16 +1,22 @@ -@import '../../../styles/main.scss'; +@import '../../../styles/main'; -.container { +.link_container { display: flex; flex-direction: column; align-items: flex-start; row-gap: 16px; + @include ontablet { + height: 100%; + flex-direction: row; + align-items: center; + gap: 14px; + } + } .link { @extend %uppercase; @extend %link; - @include hover; } diff --git a/src/components/Footer/FooterLinks/FooterLinks.tsx b/src/components/Footer/FooterLinks/FooterLinks.tsx index 5e56d1c892..8b6e50c940 100644 --- a/src/components/Footer/FooterLinks/FooterLinks.tsx +++ b/src/components/Footer/FooterLinks/FooterLinks.tsx @@ -4,7 +4,7 @@ import { Link } from 'react-router-dom'; export const FooterLinks: React.FC = () => { return ( -
+
item.itemId !== target.itemId)]; -} + | { type: 'toggleInFavorites'; payload: string } + | { type: 'toggleInCart'; payload: string } + | { type: 'setInFavotites'; payload: Product[] } + | { type: 'setInCart'; payload: Product[] }; function reducer(state: State, action: Action): State { switch (action.type) { @@ -57,61 +48,37 @@ function reducer(state: State, action: Action): State { case 'setAccessories': return { ...state, accessories: action.payload }; - case 'setCartProducts': - const curProd = getProductById(state.products, action.payload); - - if (curProd) { - if ( - !state.productsInCart.find(prod => prod.itemId === curProd.itemId) - ) { - return { - ...state, - productsInCart: addProduct(state.productsInCart, curProd), - }; - } else { - return { - ...state, - productsInCart: removeProduct(state.productsInCart, curProd), - }; - } - } - - break; - - case 'setFavoriteProducts': - const curFavProd = getProductById(state.products, action.payload); - - if (curFavProd) { - if ( - !state.productsInFavorive.find( - prod => prod.itemId === curFavProd.itemId, - ) - ) { - return { - ...state, - productsInFavorive: addProduct( - state.productsInFavorive, - curFavProd, - ), - }; - } else { - return { - ...state, - productsInFavorive: removeProduct( - state.productsInFavorive, - curFavProd, - ), - }; - } - } - - break; - - case 'setLoading': - return { ...state, loading: action.payload }; - } + case 'toggleInFavorites': + accessLocalStorage.toggle( + getProducts.getProductById(state.products, action.payload), + LocalAccessKeys.favorites, + ); + + return { + ...state, + inFavorites: accessLocalStorage.get(LocalAccessKeys.favorites), + }; - return { ...state }; + case 'toggleInCart': + accessLocalStorage.toggle( + getProducts.getProductById(state.products, action.payload), + LocalAccessKeys.cart, + ); + + return { + ...state, + inCart: accessLocalStorage.get(LocalAccessKeys.cart), + }; + + case 'setInFavotites': + return { ...state, inFavorites: action.payload }; + + case 'setInCart': + return { ...state, inCart: action.payload }; + + default: + return { ...state }; + } } const initialState: State = { @@ -121,9 +88,8 @@ const initialState: State = { phones: [], tablets: [], accessories: [], - productsInCart: [], - productsInFavorive: [], - loading: false, + inFavorites: [], + inCart: [], }; export const StateContext = React.createContext(initialState); @@ -140,34 +106,14 @@ export const GlobalProvider: React.FC = ({ children }) => { const [state, dispatch] = useReducer(reducer, initialState); useEffect(() => { - dispatch({ type: 'setLoading', payload: true }); - - const prodsPromise = getProducts().then(res => { - dispatch({ type: 'setProducts', payload: res }); + dispatch({ + type: 'setInFavotites', + payload: accessLocalStorage.get(LocalAccessKeys.favorites), }); - - const phonesPromise = getProductsByCategory(MenuItems.phones).then(res => { - dispatch({ type: 'setPhones', payload: res }); + dispatch({ + type: 'setInCart', + payload: accessLocalStorage.get(LocalAccessKeys.cart), }); - - const tabletsPromise = getProductsByCategory(MenuItems.tablets).then( - res => { - dispatch({ type: 'setTablets', payload: res }); - }, - ); - - const accesPromise = getProductsByCategory(MenuItems.accessories).then( - res => { - dispatch({ type: 'setAccessories', payload: res }); - }, - ); - - Promise.allSettled([ - prodsPromise, - phonesPromise, - tabletsPromise, - accesPromise, - ]).finally(() => dispatch({ type: 'setLoading', payload: false })); }, []); return ( diff --git a/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss b/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss index 37f786b0aa..91f4e55ac8 100644 --- a/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss +++ b/src/components/Header/MenuCloseIcons/MenuCloseIcons.module.scss @@ -1,4 +1,4 @@ -@import '../../../styles/main.scss'; +@import '../../../styles/main'; .container { display: flex; @@ -29,18 +29,18 @@ .icon { &_container { - @extend %icon_container; + @extend %icon-container; + width: 48px; height: 48px; } &_menu { - @include iconBg(url('../../../icons/Menu.svg')); + @include icon-bg(var(--fls-menu)); } &_close { - @include iconBg(url('../../../icons/Close.svg')); - + @include icon-bg(var(--fls-close)); } } diff --git a/src/components/HeaderLinks/HeaderLinks.module.scss b/src/components/HeaderLinks/HeaderLinks.module.scss index c80256b067..db050dbc7a 100644 --- a/src/components/HeaderLinks/HeaderLinks.module.scss +++ b/src/components/HeaderLinks/HeaderLinks.module.scss @@ -21,6 +21,7 @@ .link { @extend %uppercase; @extend %link; + padding: 17px 0; diff --git a/src/components/PhoneSlider/PhoneSlider.scss b/src/components/PhoneSlider/PhoneSlider.scss index f520fcefa4..f3b4dffc7e 100644 --- a/src/components/PhoneSlider/PhoneSlider.scss +++ b/src/components/PhoneSlider/PhoneSlider.scss @@ -1,5 +1,4 @@ -@import '../../styles/main.scss'; - +@import '../../styles/main'; .phoneSlider.container { box-sizing: border-box; @@ -17,8 +16,6 @@ padding-bottom: 24px; } - - .phoneSlider.swiper { &_container { width: 100%; @@ -34,17 +31,14 @@ .phoneSlider .swiper-slide { text-align: center; font-size: 18px; - background: #fff; display: flex; justify-content: center; align-items: center; -} - - -.phoneSlider .swiper-slide { + height: 440px; width: 212px; @include ontablet { + height: 506px; width: 237px; } @@ -60,24 +54,32 @@ } .phoneSlider .icon { - &_container { - @extend %icon_container; + &_right { + @include icon-bg(var(--fls-right)); + } + &_left { + @include icon-bg(var(--fls-left)); } - &_right { - @include iconBg(url('../../icons/arrowRight.svg')); + &_container { + @extend %icon-container; - &_disabled { - @include iconBg(url('../../icons/arrowRight_disabled.svg')) - } - } + background-color: var(--c-buttons-nav); - &_left { - @include iconBg(url('../../icons/arrowLeft.svg')); + &.swiper-button-disabled { + background-color: var(--c-background); + + & .icon { + + &_left { + @include icon-bg(var(--fls-left-disabled)); + } - &_disabled { - @include iconBg(url('../../icons/arrowLeft_disabled.svg')); + &_right { + @include icon-bg(var(--fls-right-disabled)); + } + } } } } diff --git a/src/components/PhoneSlider/PhoneSlider.tsx b/src/components/PhoneSlider/PhoneSlider.tsx index 4e92e90583..fa78037d48 100644 --- a/src/components/PhoneSlider/PhoneSlider.tsx +++ b/src/components/PhoneSlider/PhoneSlider.tsx @@ -5,7 +5,7 @@ import './PhoneSlider.scss'; import classNames from 'classnames'; import 'swiper/css/navigation'; -import React, { useRef, useState } from 'react'; +import React, { useRef } from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; import { Navigation } from 'swiper/modules'; import { ProductCard } from '../ProductCard'; @@ -20,16 +20,6 @@ export const PhoneSlider: React.FC = ({ title, products }) => { const sliderRef = useRef>(null); const prevRef = useRef(null); const nextRef = useRef(null); - const [realIndex, setIndex] = useState(0); - const [isEnd, setIsEnd] = useState(false); - const handleNavigation = () => { - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - setIndex(() => sliderRef?.current?.swiper?.realIndex); - // eslint-disable-next-line @typescript-eslint/ban-ts-comment - // @ts-ignore - setIsEnd(sliderRef.current?.swiper?.isEnd); - }; return (
@@ -42,11 +32,8 @@ export const PhoneSlider: React.FC = ({ title, products }) => { className={classNames('icon_container')} >
@@ -55,11 +42,8 @@ export const PhoneSlider: React.FC = ({ title, products }) => { className={classNames('icon_container')} >
diff --git a/src/components/PicturesSlider/PictureSlider.scss b/src/components/PicturesSlider/PictureSlider.scss index 78f401e97d..f4dc5020c3 100644 --- a/src/components/PicturesSlider/PictureSlider.scss +++ b/src/components/PicturesSlider/PictureSlider.scss @@ -1,86 +1,94 @@ -@import '../../styles/main.scss'; - -.photoSwiper_container { - display: flex; - box-sizing: border-box; - width: 100%; - height: 100%; - gap: 19px; -} +@import '../../styles/main'; -.photoSwiper.swiper { - display: flex; - flex-direction: column; -} +.photoSwiper { + &_container { + display: flex; + box-sizing: border-box; + width: 100%; + height: 100%; + gap: 19px; -.photoSwiper .swiper-pagination { - position: relative; - bottom: unset; - height: 24px; + @include ontablet { + flex-direction: row; + } - &-bullet { - height: 4px; - width: 14px; - border-radius: 0%; - background-color: var(--c-primary); - } -} + & .icon { + box-sizing: content-box; -.photoSwiper .swiper-slide { - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; - height: 100vw; + &_container { + @extend %icon-container; - @include ontablet { - height: 189px; - } + display: none; + flex-shrink: 0; + width: 32px; + height: 100%; + background-color: var(--c-buttons-nav); - @include ondesktop { - height: 400px; - } -} + @include ontablet { + display: flex; + height: 189px; + } -.photoSwiper img { - height: 100%; -} + @include ondesktop { + height: 400px; + } + } -.photoSwiper_container .icon { - box-sizing: content-box; + &_right { + @include icon-bg(var(--fls-right)); - &_container { - @extend %icon_container; - display: none; - flex-shrink: 0; - width: 32px; + &_disabled { + @include icon-bg(var(--fls-right-disabled)) + } + } + + &_left { + @include icon-bg(var(--fls-left)); + + &_disabled { + @include icon-bg(var(--fls-left-disabled)); + } + } + } + } + + &.swiper { + display: flex; + flex-direction: column; + width: 100%; + } + + & img { height: 100%; + } + + & .swiper-slide { + display: flex; + justify-content: center; + align-items: center; + overflow: hidden; + height: 100vw; @include ontablet { - display: flex; height: 189px; } @include ondesktop { height: 400px; } - - } - - &_right { - @include iconBg(url('../../icons/arrowRight.svg')); - - &_disabled { - @include iconBg(url('../../icons/arrowRight_disabled.svg')) - } } - &_left { - @include iconBg(url('../../icons/arrowLeft.svg')); + & .swiper-pagination { + position: relative; + bottom: unset; + height: 24px; - &_disabled { - @include iconBg(url('../../icons/arrowLeft_disabled.svg')); + &-bullet { + height: 4px; + width: 14px; + border-radius: 0%; + background-color: var(--c-primary); } } } diff --git a/src/components/PicturesSlider/PicturesSlider.tsx b/src/components/PicturesSlider/PicturesSlider.tsx index 4568d2c0fc..7073187f1a 100644 --- a/src/components/PicturesSlider/PicturesSlider.tsx +++ b/src/components/PicturesSlider/PicturesSlider.tsx @@ -6,7 +6,7 @@ import classNames from 'classnames'; import { useRef } from 'react'; import { Swiper, SwiperSlide } from 'swiper/react'; -import { Navigation, Pagination } from 'swiper/modules'; +import { Autoplay, Navigation, Pagination } from 'swiper/modules'; import type SwiperCore from 'swiper'; import './PictureSlider.scss'; @@ -28,7 +28,11 @@ export const PicturesSlider = () => { pagination={{ clickable: true, }} - modules={[Pagination, Navigation]} + autoplay={{ + delay: 5000, + disableOnInteraction: true, + }} + modules={[Autoplay, Pagination, Navigation]} onBeforeInit={swiper => { swiperRef.current = swiper; }} diff --git a/src/components/ProductCard/ProductCard.module.scss b/src/components/ProductCard/ProductCard.module.scss index e6c47bae10..63ac088091 100644 --- a/src/components/ProductCard/ProductCard.module.scss +++ b/src/components/ProductCard/ProductCard.module.scss @@ -1,7 +1,7 @@ -@import '../../styles/main.scss'; -@import '../../styles/mixines.scss'; +@import '../../styles/main'; +@import '../../styles/mixines'; -.container { +.prod_card_container { box-sizing: border-box; display: flex; flex-direction: column; @@ -12,31 +12,71 @@ border: 1px solid var(--c-elements); padding: 32px; - &_price { - width: 100%; - display: flex; - gap: 8px; - border-bottom: 1px solid var(--c-elements); + & .title { + cursor: default; + @include hover; + + flex-grow: 1; } - &_specs { - width: 100%; - display: flex; - flex-direction: column; + & .price { + &_container { + width: 100%; + display: flex; + gap: 8px; + border-bottom: 1px solid var(--c-elements); + cursor: default; + } + } - &_bts { - display: flex; - width: 100%; + & .specs { + &_container { + width: 100%; + display: flex; + flex-direction: column; + + .spec { + &_row { + width: 100%; + display: flex; + justify-content: space-between; + gap: 8px; + padding-block: 8px; + cursor: default; + } + + &_name { + @extend %small-text; + + color: var(--c-secondary); + } + + &_param { + @extend %small-text; + } + } + } + } + + & .bts { + &_container { + box-sizing: border-box; + display: flex; + width: 100%; + height: 40px; + } } } img.image { width: 148px; - height: 128px; + height: 130px; object-fit: contain; + @include hover; + @include ontablet { width: 173px; height: 181px; @@ -62,22 +102,3 @@ h3.price { width: 100%; border: 1px solid var(--c-elements); } - -.spec { - &_row { - width: 100%; - display: flex; - justify-content: space-between; - gap: 8px; - padding-block: 8px; - } - - &_name { - @extend %small-text; - color: var(--c-secondary); - } - - &_param { - @extend %small-text; - } -} diff --git a/src/components/ProductCard/ProductCard.tsx b/src/components/ProductCard/ProductCard.tsx index d4ee5f09b6..e52aff7947 100644 --- a/src/components/ProductCard/ProductCard.tsx +++ b/src/components/ProductCard/ProductCard.tsx @@ -2,36 +2,46 @@ import classNames from 'classnames'; import style from './ProductCard.module.scss'; import { Product } from '../../types/Product'; import { ButtonsAddCardFav } from '../ButtonsAddCardFav'; +import { useNavigate } from 'react-router-dom'; type Props = { product: Product; }; + export const ProductCard: React.FC = ({ product }) => { + const navigate = useNavigate(); + + const handleClick = () => { + navigate(`/${product.category}/${product.itemId}`); + }; + return ( <> {product && ( -
+
product image -

{product.name}

+

+ {product.name} +

-
-

- ${product.price} -

+
+

${product.price}

- {product.discountPrice && ( -

- ${product.discountPrice} -

- )} +

+ ${product.fullPrice} +

-
+

Screen

{product.screen}

@@ -48,7 +58,7 @@ export const ProductCard: React.FC = ({ product }) => {
-
+
diff --git a/src/components/ProductCard/index.ts b/src/components/ProductCard/index.ts index 19d53574e0..7ce031c382 100644 --- a/src/components/ProductCard/index.ts +++ b/src/components/ProductCard/index.ts @@ -1 +1 @@ -export * from './ProductCard' +export * from './ProductCard'; diff --git a/src/components/SiteLogo/SiteLogo.module.scss b/src/components/SiteLogo/SiteLogo.module.scss index 700faefbd0..dee753dd6c 100644 --- a/src/components/SiteLogo/SiteLogo.module.scss +++ b/src/components/SiteLogo/SiteLogo.module.scss @@ -1,4 +1,4 @@ -@import '../../styles/main.scss'; +@import '../../styles/main'; .logo { @@ -8,11 +8,10 @@ align-items: flex-start; height: 32px; width: 90px; - background: url('../../icons/Logo.png') no-repeat center/contain; + background: no-repeat center/contain; + background-image: var(--fls-logo); + @include hover; - &_darkmode { - background: url('../../icons/Logo-dark.png') no-repeat center/contain; - } } } diff --git a/src/components/SortFilter/Selector/Selector.module.scss b/src/components/SortFilter/Selector/Selector.module.scss new file mode 100644 index 0000000000..c2f7b9a64a --- /dev/null +++ b/src/components/SortFilter/Selector/Selector.module.scss @@ -0,0 +1,93 @@ +@import '../../../styles/main'; + + +.selector { + &_container { + display: flex; + flex-direction: column; + + .selection_hidden { + transform: scaleY(1); + } + } + + &_button { + display: flex; + border: 1px solid var(--c-elements); + align-items: center; + justify-content: space-between; + padding-inline: 12px; + height: 40px; + + &_title { + cursor: default; + } + + & .icon { + display: flex; + align-items: center; + + &_container { + @extend %icon-container; + + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + + border: none; + + + } + + &_up { + @include icon-bg(var(--fls-up-disabled)); + + width: 16px; + height: 16px; + } + + &_down { + @include icon-bg(var(--fls-down-disabled)); + + width: 16px; + height: 16px; + } + } + } + + &_body { + display: flex; + flex-direction: column; + gap: 5px; + border: 1px solid var(--c-elements); + position: absolute; + top: 4px; + width: 100%; + background-color: var(--c-background); + padding-block: 8px; + transition: all 200ms 0ms ease-in-out; + transform: scaleY(0); + transform-origin: top; + z-index: 1; + + &_container { + position: relative; + } + + &__text { + cursor: default; + color: var(--c-secondary); + font-weight: 500; + text-decoration: none; + padding-inline: 12px; + + @include hover(transform, scale(1.02)); + } + } + + + +} diff --git a/src/components/SortFilter/Selector/Selector.tsx b/src/components/SortFilter/Selector/Selector.tsx new file mode 100644 index 0000000000..4eb75ec091 --- /dev/null +++ b/src/components/SortFilter/Selector/Selector.tsx @@ -0,0 +1,106 @@ +import React, { useEffect, useRef, useState } from 'react'; +import classNames from 'classnames'; +import style from './Selector.module.scss'; +import { useSearchParams } from 'react-router-dom'; +import { SearchParams } from '../../../types/SearchParams'; + +type Props = { + searchParamName: string; + options: string[]; + defaultValue?: number; +}; +export const Selector: React.FC = ({ + searchParamName, + options, + defaultValue = 0, +}) => { + const [searchParams, setSearchParams] = useSearchParams(); + + const [visible, setVisible] = useState(false); + const [selection, setSelection] = useState(defaultValue); + + const selectBodyRef = useRef(null); + const selectButtonRef = useRef(null); + + const handleSelect = (index: number) => { + setVisible(() => false); + setSelection(() => index); + + const params = new URLSearchParams(searchParams); + + params.set(searchParamName, options[index]); + params.delete(SearchParams.page); + setSearchParams(params); + }; + + const handleClicks = (eve: MouseEvent) => { + if (selectBodyRef.current && selectButtonRef.current) { + if (selectButtonRef.current?.contains(eve.target as Node)) { + setVisible(prev => !prev); + } else if (!selectBodyRef.current?.contains(eve.target as Node)) { + setVisible(false); + } + } + }; + + useEffect(() => document.addEventListener('click', handleClicks), []); + + useEffect(() => { + const curIndex = options.findIndex( + opt => opt === searchParams.get(searchParamName), + ); + + if (curIndex >= 0) { + setSelection(curIndex); + + return; + } + + setSelection(defaultValue); + }, [searchParams, defaultValue, options, searchParamName]); + + return ( +
+
+

+ {options[selection]} +

+ +
+
+
+
+ +
+
+ {options.map((opt, index) => { + return ( +

{ + handleSelect(index); + }} + > + {opt} +

+ ); + })} +
+
+
+ ); +}; diff --git a/src/components/SortFilter/Selector/index.ts b/src/components/SortFilter/Selector/index.ts new file mode 100644 index 0000000000..c27310e5d8 --- /dev/null +++ b/src/components/SortFilter/Selector/index.ts @@ -0,0 +1 @@ +export * from './Selector'; diff --git a/src/components/SortFilter/SortFilter.module.scss b/src/components/SortFilter/SortFilter.module.scss new file mode 100644 index 0000000000..5f2038649a --- /dev/null +++ b/src/components/SortFilter/SortFilter.module.scss @@ -0,0 +1,35 @@ +@import '../../styles/main'; + +.search_filter_container { + @include page-grid; + + & .container { + &_title { + color: var(--c-secondary); + } + + &_item { + display: flex; + flex-direction: column; + gap: 4px; + justify-content: space-between; + } + + &_sort { + grid-column: 1/ span 2; + + @include ontablet { + grid-column: 1/ span 4; + } + + } + + &_perPage { + grid-column: 3/ span 2; + + @include ontablet { + grid-column: 5/ span 3; + } + } + } +} diff --git a/src/components/SortFilter/SortFilter.tsx b/src/components/SortFilter/SortFilter.tsx new file mode 100644 index 0000000000..7823226fab --- /dev/null +++ b/src/components/SortFilter/SortFilter.tsx @@ -0,0 +1,33 @@ +import classNames from 'classnames'; +import style from './SortFilter.module.scss'; +import { Selector } from './Selector'; +import { SearchParams } from '../../types/SearchParams'; +import { SortingTypes } from '../../types/SortFilter'; + +const perPage = ['4', '8', '16', 'All']; +const sorting = Object.values(SortingTypes); + +export const SortFilter = () => { + return ( +
+
+

Sort by

+ +
+ +
+

Items on page

+ +
+
+ ); +}; diff --git a/src/components/SortFilter/index.ts b/src/components/SortFilter/index.ts new file mode 100644 index 0000000000..5434a0427d --- /dev/null +++ b/src/components/SortFilter/index.ts @@ -0,0 +1 @@ +export * from './SortFilter'; diff --git a/src/icons/Close.svg b/src/icons/Close.svg index aadcc91fb1..f84188b1e4 100644 --- a/src/icons/Close.svg +++ b/src/icons/Close.svg @@ -1,3 +1,42 @@ - - + + + + + diff --git a/src/icons/Cursor Mouse Mice.png b/src/icons/Cursor Mouse Mice.png deleted file mode 100644 index 11626f20b3f8399729dd2f10a036f08675217f72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 821 zcmV-51Iqk~P)J<90-BNTrMZTfsGtGB}w|! z>-C=F?|byjNFBt;;qh!X`?^>x2JLqHB|S3|wi`u%ggBW_r{82WRN_yqR(pkWZbr^l zC1`OM!Z(>rzRS#rM7>^r%}Cg)gb)sgNpAmmJg&$pBNEkWm6Nbd2}*&8I3JBhKgn@6 zo0a?hzFrB@77}dBQG^eM!=XYToleW4P)I{Ur^H^369}jY;PV=ZMx(J)VP4x@QmGVQ zNEQU`Uj~B##9}dJ#CI2oL;|x>55scT?5$7m8VK+j;0kc)l~C=D-D`r?fI>i@0y7e- zbnLuEuZqw}0kBz#jVUlAv5!7M61u%!uW1S_C~+eN*7|@M0Whu!(`L2ljJG78!4{>X z{K&|c837)dCDW5gCX+Q(;u#~cmJYN0IADVUnM?+v(WoI2Ov#lJ&1Um$JRbiA%leXF zgBiAB4mzBy7}Z3#+kFALdbm-AqH}6ifQwFAt=4C!)43pMA`k?b=0s7HTrSrf$9XUq zyu??(xA9@P3wY%7`TTF8Q22;*RAeE9+Igu| z`hZbgG6w;~na}6*<48C|_!!9XDXeb8QG@y#?n8Bq2f95+tx#Dj_ zU`+w;yjU#0$N4Ap4simjV_4APy1A`QAwZJio$v>DUM`p4G3Vz94_J`Ys?nk(pK>e4 zY9CiBl{X%b=aI@eMQ+>GT!=&>l)F<@;0t!LI4mpK539&+|1KaWVm_ZI$@P`IH|J&x zsf&mHM}0tI;{pyqzyaFxK;4QDZU_4!zJ2x&DdkIL0M=DY00000NkvXXu0mjf_>^hH diff --git a/src/icons/Home.png b/src/icons/Home.png deleted file mode 100644 index e7668490cdd0df26ff9691330b4fa32d91f9f3e6..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 370 zcmV-&0ge8NP){TQGsdk5?zq4IDv6a0BB6uHj9O-i#Bd6NnS&287&Jm4!uWVh2Wn^S z+T>iMvihR}@{!tQlZ1Wk3hG0*(DyNhA9TBMPsX~!%<=ej?W3}2nTpi71ISuR9CL>7 Ql>h($07*qoM6N<$f=IEPApigX diff --git a/src/icons/Menu.svg b/src/icons/Menu.svg index a578c59b0d..4731fe2eb8 100644 --- a/src/icons/Menu.svg +++ b/src/icons/Menu.svg @@ -1,5 +1,50 @@ - - - - + + + + + + + diff --git a/src/icons/Minus.png b/src/icons/Minus.png deleted file mode 100644 index 19c80d474df8e52ea4e3276c38c216708f936369..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 160 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBzNx2+V@L(#+tUkq85DR}FE(>pG0i@-DyJbw zORDL>;*<5S&NS96xMQ$+nrHn?C5d<0^VW47U=yq2WO=L4q7O8H!PC{xWt~$(699>| BEinK9 diff --git a/src/icons/Plus.png b/src/icons/Plus.png deleted file mode 100644 index eb42caf4f29ebc932355bc8c101acd57c315cd34..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 228 zcmeAS@N?(olHy`uVBq!ia0vp^0wB!61|;P_|4#%`oCO|{#S9GG!XV7ZFl&wkP>``W z$lZxy-8q?;Kn_c~qpu?a!^VE@KZ&eBex;|2V@L(#-HB&88w_|_<3pFIpJlV^T5Zvp z_eg8qBep8;ps16ME84}5Iv?fta{hEef5I#qF_lGP7q%MiX*F@xY1}GtN+`ii{!&VX z=P#C5CMk(VpH){}IQd>C?f(Av5;Lzqbz|p>>wO*~>ia2amVAt2SBzVB)`v{KInTu7 UX1Qhp-N3-$>FVdQ&MBb@0B~nZ{Qv*} diff --git a/src/icons/Search.png b/src/icons/Search.png deleted file mode 100644 index 3da1aa7570f9f68d6b28ad35ae995972e80cb7ab..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 401 zcmV;C0dD?@P)t6DZjo;`?yF7+ zgMpo<>75@A?X#+gs>&E!rm0=Vv0o_2!!Kw+5UJ%b`-8IgdiEX{z{4}~POIqXMZ*p~ zHue|D9lYvGlgDQQs?d9Uq6LolCmLeLm{FSmD?f0O1m9-?U7vc>_mY4Nl##VT2HtcN zO-6($CV vp$Ybg37RYGP-&7CIbzaW#GbQNvb4a(G|q00000NkvXXu0mjfp&Osc diff --git a/src/icons/Shopping bag + Counter(Cart).png b/src/icons/Shopping bag + Counter(Cart).png deleted file mode 100644 index 42ec61b3918af089efcb4c01ca98936cc6bcaa71..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 794 zcmV+#1LgdQP)~?8sYIkY9NcfV;?7la7-_E?5Zvp-Rgb*BU0WB+P zNTDsGovM#z&>`@^;I}=Ic=5W-(_1Q}KUTE$Gs8CUPdZ?F+^*njj4}48V?rd0tPVT<0KzK($%&JkxWLH3dZ*M-^l7opG2LCtCyX&LHk&E^|q{$1!f#@ z?`9W_oz;&ETysEtt_MqM*WM{+9ZQankxmGGYss?q@Frb>6HW%+0UJzQkYMbb1kcuf z!_{%e{zp{rHe|PQGUr^bRA91^FtekVGP9$?kDvPDCN|FIRtet)d@87bWz-3A9U+?% z5}6RNVY4wAEP#jz)0gd9r`~^FIk0b9QY3pw&0H!8xmf(l48yIQQLaW%lLThEfV$Vf z=#UosUTQ_w-_Il1%JWE@lO#Cb-QD9i%;WLMBcqpiwNx-MX4jg7GK|kT;XC>fuU^jW z&jM~;@1l%fG84jOB%Hz#NH*>b4i3V*)Cd3VxIPw?EqRv{vl*`Ck=d>wIlyYHmju9P zN=SZpR|z-UCzZ*6QCFf%CWDU|dk5G&sg32a=+Q2MBBTMX7pWA&>xLkaHJdCy3*>I4Ax=<+8i+8pv=={J(nh}yW zN{WYXU?FKt5_*P!4FyWEs9bCo-wcOR8pcEz$&y$eQvaC#3|9_wh9#vjpeRZXUASs( zp%X1VB7}HHGFSx0a + + diff --git a/src/icons/arrowLeft.svg b/src/icons/arrowLeft.svg index 716e854a27..7c3cd2b5ae 100644 --- a/src/icons/arrowLeft.svg +++ b/src/icons/arrowLeft.svg @@ -24,12 +24,12 @@ inkscape:pagecheckerboard="0" showgrid="false" inkscape:zoom="44.9375" - inkscape:cx="7.9888734" - inkscape:cy="8" - inkscape:window-width="1707" - inkscape:window-height="996" - inkscape:window-x="-8" - inkscape:window-y="-8" + inkscape:cx="8" + inkscape:cy="7.9888734" + inkscape:window-width="1920" + inkscape:window-height="1009" + inkscape:window-x="-1928" + inkscape:window-y="-443" inkscape:window-maximized="1" inkscape:current-layer="svg4" /> + id="path2" + style="fill:#313237;fill-opacity:1" /> diff --git a/src/icons/arrowRight.svg b/src/icons/arrowRight.svg index 589a14e8a9..6c9233b116 100644 --- a/src/icons/arrowRight.svg +++ b/src/icons/arrowRight.svg @@ -24,12 +24,12 @@ inkscape:pagecheckerboard="0" showgrid="false" inkscape:zoom="44.9375" - inkscape:cx="7.9888734" - inkscape:cy="8" - inkscape:window-width="1707" - inkscape:window-height="996" - inkscape:window-x="-8" - inkscape:window-y="-8" + inkscape:cx="8" + inkscape:cy="7.9888734" + inkscape:window-width="1920" + inkscape:window-height="1009" + inkscape:window-x="-1928" + inkscape:window-y="-443" inkscape:window-maximized="1" inkscape:current-layer="svg4" /> + id="path2" + style="fill:#313237;fill-opacity:1" /> diff --git a/src/icons/arrowUp.svg b/src/icons/arrowUp.svg index de587604d2..63c0f2be22 100644 --- a/src/icons/arrowUp.svg +++ b/src/icons/arrowUp.svg @@ -1,3 +1,42 @@ - - + + + + + diff --git a/src/icons/arrowUp_disabled.svg b/src/icons/arrowUp_disabled.svg new file mode 100644 index 0000000000..e061a0757f --- /dev/null +++ b/src/icons/arrowUp_disabled.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/icons/cart.svg b/src/icons/cart.svg index 682ac1c5e3..3a0896b4b9 100644 --- a/src/icons/cart.svg +++ b/src/icons/cart.svg @@ -1,5 +1,56 @@ - - - - + + + + + + + diff --git a/src/icons/darkMode/arrowDown_disabled.svg b/src/icons/darkMode/arrowDown_disabled.svg new file mode 100644 index 0000000000..7a1db9ca66 --- /dev/null +++ b/src/icons/darkMode/arrowDown_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/arrowLeft.svg b/src/icons/darkMode/arrowLeft.svg new file mode 100644 index 0000000000..ead0524499 --- /dev/null +++ b/src/icons/darkMode/arrowLeft.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/arrowLeft_disabled.svg b/src/icons/darkMode/arrowLeft_disabled.svg new file mode 100644 index 0000000000..b7448343ff --- /dev/null +++ b/src/icons/darkMode/arrowLeft_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/arrowRight.svg b/src/icons/darkMode/arrowRight.svg new file mode 100644 index 0000000000..3591ba9167 --- /dev/null +++ b/src/icons/darkMode/arrowRight.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/arrowRight_disabled.svg b/src/icons/darkMode/arrowRight_disabled.svg new file mode 100644 index 0000000000..364d78da3f --- /dev/null +++ b/src/icons/darkMode/arrowRight_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/arrowUp.svg b/src/icons/darkMode/arrowUp.svg new file mode 100644 index 0000000000..8ac6b02257 --- /dev/null +++ b/src/icons/darkMode/arrowUp.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/arrowUp_disabled.svg b/src/icons/darkMode/arrowUp_disabled.svg new file mode 100644 index 0000000000..a4852177d2 --- /dev/null +++ b/src/icons/darkMode/arrowUp_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/cart.svg b/src/icons/darkMode/cart.svg new file mode 100644 index 0000000000..dcbf4c8773 --- /dev/null +++ b/src/icons/darkMode/cart.svg @@ -0,0 +1,56 @@ + + + + + + + + diff --git a/src/icons/darkMode/close.svg b/src/icons/darkMode/close.svg new file mode 100644 index 0000000000..83a41be318 --- /dev/null +++ b/src/icons/darkMode/close.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/dark_mode.svg b/src/icons/darkMode/dark_mode.svg new file mode 100644 index 0000000000..5dbc449829 --- /dev/null +++ b/src/icons/darkMode/dark_mode.svg @@ -0,0 +1,46 @@ + + + + + + + + + diff --git a/src/icons/darkMode/favorite.svg b/src/icons/darkMode/favorite.svg new file mode 100644 index 0000000000..6d5166a148 --- /dev/null +++ b/src/icons/darkMode/favorite.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/home.svg b/src/icons/darkMode/home.svg new file mode 100644 index 0000000000..d34f74d836 --- /dev/null +++ b/src/icons/darkMode/home.svg @@ -0,0 +1,49 @@ + + + + + + + diff --git a/src/icons/darkMode/menu.svg b/src/icons/darkMode/menu.svg new file mode 100644 index 0000000000..4f16e51e7c --- /dev/null +++ b/src/icons/darkMode/menu.svg @@ -0,0 +1,50 @@ + + + + + + + + diff --git a/src/icons/darkMode/minus.svg b/src/icons/darkMode/minus.svg new file mode 100644 index 0000000000..0459cbd1bc --- /dev/null +++ b/src/icons/darkMode/minus.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/minus_disabled.svg b/src/icons/darkMode/minus_disabled.svg new file mode 100644 index 0000000000..508edf95cc --- /dev/null +++ b/src/icons/darkMode/minus_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/darkMode/mouse-cursor.svg b/src/icons/darkMode/mouse-cursor.svg new file mode 100644 index 0000000000..b7f0395be8 --- /dev/null +++ b/src/icons/darkMode/mouse-cursor.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/icons/darkMode/plus.svg b/src/icons/darkMode/plus.svg new file mode 100644 index 0000000000..d9a6f26932 --- /dev/null +++ b/src/icons/darkMode/plus.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/dark_mode.svg b/src/icons/dark_mode.svg index 122729f6e2..f8fc136f98 100644 --- a/src/icons/dark_mode.svg +++ b/src/icons/dark_mode.svg @@ -1,8 +1,46 @@ - + + - + - - + + - \ No newline at end of file + diff --git a/src/icons/favorite.svg b/src/icons/favorite.svg index e69d519f44..e76de364e8 100644 --- a/src/icons/favorite.svg +++ b/src/icons/favorite.svg @@ -1,3 +1,42 @@ - - + + + + + diff --git a/src/icons/home.svg b/src/icons/home.svg new file mode 100644 index 0000000000..b6114516b4 --- /dev/null +++ b/src/icons/home.svg @@ -0,0 +1,49 @@ + + + + + + + diff --git a/src/icons/icons8-dark-mode-64.png b/src/icons/icons8-dark-mode-64.png deleted file mode 100644 index f6a3e2a6987610a610b7ede3f5fd94595544c8a8..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 1593 zcmV-92FCe`P)q$gGRCt{2n|X*=RTRfRzbVJc#Kw^p)3FV*Y){Kf(-sNDAcvZ4 zfgp`^XkVx-`lCpVpoG#)qJ5FdNNZX!f`S$eE5;=w+MUVCrLr=0)L$I z*&wf@z4N^u`5pmK-Wt%P4%_@6qyOz%S+4@K=;k;NxKV#YQ7Iub>FD-;z)PvodL8IZ znP7rCy%~5E*r4-hK($p8&Mz+7h6m3)*x5Fj02nePAu{C$LomwFy`OJg4@b z7V@$S)ma<)VNM9Y>-tp%aF~!r0`~%ofYnZu@@nuJO|2Qg89*+@k(2#-RUOS2d3+B% z1k{uwghe80aYDFF*Bq}_xOviHpu*}`t1uO?10VV+_0s}QAO*;+;P64I> zPXIr=XSW0Mixos~;2kG~+ksU&zqyUy9|Ft+cDNV+1Uv&=EtMQ3-af!!;8b9$ezQ`V zH2`?UajcixA5~1_r9EehyH#9&X)jWg5EiFK$%JrV>UUQH8+E@`+H-yzD%}}II)~{$ z-~o+pD2@T%6z{TPAGS`^$spbY^mX?trEZCEK9;R01(yKZ#Pca|G;vWa?ya#1_Xpk; zsWtq%5=J~hom11NBQLf4%BUyP`SVz$N#{-V&k5if<3`?8J{As=?MS9(Ud!gl807Qa zQZ_Vz=TNt_E+8JRO?A8xxCb~a2Ki*Sw2TYnY7o~4z&;V$W(Me7VY4CMiR=0Z?K0#0n)ucM$B_XB$;mNmeR3dI zof`WK&?`dQ>fPq8)MdEffQI?*q4-E#=3Rzjh*aMAE+!*X0Ke)b=A(yd(k?k9{UT1w zg=C1c^pYO~oSg*I#r0lY; z1qCdGm&G-$*as^WC`i&($k6=p@x3`LLBzyj2V}z*l9V9I`>v~C%6^DbJ zqY{domgU{0ETv5@QRy+dorFaaj6j;Amv^xrlt}jtCSeAbb1unXuaixFfLMRY)%9} zBSAl{T)rs7(&HB1ivm-(x7~E84={|iT3w9m$;j1XNpiFyBt2fM9pW@~+>MHs_j%U!uS#kB$6GTMSJgy6gnU3`msEuL}pC~ z8yWMFi^(F7b%reBsCWJEOmdS;uV)Gs$8ugOO@U`Rswy>1#$9< zK`fBa3&KTuaD(%ze3_i(_6ivR+eO}~#O2rG+a~ok?f6O~bb|_}+L|)9!n`QwrdWag zAfXnWeV!qLhjl*8Npx|v#00000NkvXXu0mjf#cu3q diff --git a/src/icons/minus.svg b/src/icons/minus.svg new file mode 100644 index 0000000000..066d275efc --- /dev/null +++ b/src/icons/minus.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/minus_disabled.svg b/src/icons/minus_disabled.svg new file mode 100644 index 0000000000..26c5af1080 --- /dev/null +++ b/src/icons/minus_disabled.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/icons/mouse-cursor.svg b/src/icons/mouse-cursor.svg new file mode 100644 index 0000000000..b7f0395be8 --- /dev/null +++ b/src/icons/mouse-cursor.svg @@ -0,0 +1,16 @@ + + + + + + + + + + + + + + + + diff --git a/src/icons/plus.svg b/src/icons/plus.svg new file mode 100644 index 0000000000..7b2fae4a60 --- /dev/null +++ b/src/icons/plus.svg @@ -0,0 +1,42 @@ + + + + + + diff --git a/src/modules/AccessoriesPage/AccessoriesPage.module.scss b/src/modules/AccessoriesPage/AccessoriesPage.module.scss new file mode 100644 index 0000000000..c3b9bf1178 --- /dev/null +++ b/src/modules/AccessoriesPage/AccessoriesPage.module.scss @@ -0,0 +1,6 @@ +@import '../../styles/main'; + +.tabletPage_container { + width: 100%; + height: 100%; +} diff --git a/src/modules/AccessoriesPage/AccessoriesPage.tsx b/src/modules/AccessoriesPage/AccessoriesPage.tsx new file mode 100644 index 0000000000..12ff2fcf33 --- /dev/null +++ b/src/modules/AccessoriesPage/AccessoriesPage.tsx @@ -0,0 +1,23 @@ +import style from './AccessoriesPage.module.scss'; +import { useContext } from 'react'; +import { StateContext } from '../../components/GlobalProvider'; +import { Catalog } from '../../components/Catalog/Catalog'; +import { getProducts } from '../../utils/getProducts'; +import { MenuItems } from '../../types/MenuItems'; + +export const AccessoriesPage = () => { + const { products } = useContext(StateContext); + const accessories = getProducts.getProductByCategory( + products, + MenuItems.accessories, + ); + + return ( +
+ +
+ ); +}; diff --git a/src/modules/AccessoriesPage/index.ts b/src/modules/AccessoriesPage/index.ts new file mode 100644 index 0000000000..486474aa0b --- /dev/null +++ b/src/modules/AccessoriesPage/index.ts @@ -0,0 +1 @@ +export * from './AccessoriesPage'; diff --git a/src/modules/App/App.module.scss b/src/modules/App/App.module.scss index 0c68d8927f..b36b664596 100644 --- a/src/modules/App/App.module.scss +++ b/src/modules/App/App.module.scss @@ -1,6 +1,5 @@ @import '../../styles/main'; - .container { display: flex; flex-direction: column; @@ -8,7 +7,7 @@ overflow: hidden; &_header { - background-color: var(--c-backgroud); + background-color: var(--c-background); display: flex; justify-content: center; position: sticky; @@ -17,7 +16,7 @@ &_mobile_menu { position: relative; - background-color: var(--c-backgroud); + background-color: var(--c-background); display: flex; flex-direction: column; @@ -26,7 +25,9 @@ &_body { display: flex; justify-content: center; + width: 100%; max-width: 1200px; + flex-grow: 1; @include ondesktop { @@ -35,9 +36,9 @@ } &_footer { - background-color: var(--c-backgroud); + background-color: var(--c-background); display: flex; justify-content: center; - box-shadow: 0px -1px 0px 0px #E2E6E9; + box-shadow: 0 -1px 0 0 #E2E6E9; } } diff --git a/src/modules/App/App.tsx b/src/modules/App/App.tsx index d47190d7e6..42847bab77 100644 --- a/src/modules/App/App.tsx +++ b/src/modules/App/App.tsx @@ -6,16 +6,15 @@ import '../../styles/theme.scss'; import '../../styles/main.scss'; import { Footer } from '../../components/Footer'; import { Header } from '../../components/Header'; -import { useContext, useEffect } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { MobileMenu } from '../../components/MobileMenu'; -import { StateContext } from '../../components/GlobalProvider'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; import { Loader } from '../../components/Loader'; +import { getProducts } from '../../utils/getProducts'; const ScrollToTop = () => { - // Extracts pathname property(key) from an object const { pathname } = useLocation(); - // Automatically scrolls to top whenever pathname changes useEffect(() => { window.scrollTo(0, 0); }, [pathname]); @@ -24,7 +23,19 @@ const ScrollToTop = () => { }; export const App = () => { - const { showMenu, loading } = useContext(StateContext); + const { showMenu } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + const [loading, setLoading] = useState(false); + + useEffect(() => { + setLoading(() => true); + getProducts + .fetchProducts() + .then(res => { + dispatch({ type: 'setProducts', payload: res }); + }) + .finally(() => setLoading(() => false)); + }, [dispatch]); return (
{ {!showMenu && ( <> {loading ? ( - +
+ +
) : (
diff --git a/src/modules/CartPage/CartItem/CartItem.module.scss b/src/modules/CartPage/CartItem/CartItem.module.scss new file mode 100644 index 0000000000..e5de4a49d3 --- /dev/null +++ b/src/modules/CartPage/CartItem/CartItem.module.scss @@ -0,0 +1,132 @@ +@import '../../../styles/main'; + +.cartItem_container { + display: flex; + flex-direction: column; + justify-content: space-between; + border: 1px solid var(--c-elements); + padding: 16px; + gap: 16px; + + @include ontablet { + flex-direction: row; + gap: 24px; + } + + & .icon { + display: flex; + align-items: center; + width: 16px; + height: 16px; + + &_container { + @extend %icon-container; + + background-color: var(--c-buttons-nav); + + &_disabled { + border-color: var(--c-elements); + background-color: var(--c-background); + + &:hover { + border-color: var(--c-elements); + } + } + } + } + + & .top { + display: flex; + align-items: center; + gap: 16px; + + @include ontablet { + gap: 24px; + } + + & .icon { + &_container { + cursor: pointer; + width: 16px; + height: 16px; + border: none; + flex-shrink: 0; + + &_close { + background-color: var(--c-background); + } + } + + &_close { + @include icon-bg(var(--fls-close)); + } + } + + & .container_image { + width: 80px; + height: 80px; + display: flex; + justify-content: center; + align-items: center; + flex-shrink: 0; + } + + & .image { + width: 100%; + height: 100%; + object-fit: contain; + } + + & .name { + text-decoration: none; + color: (var(--c-primary)); + cursor: default; + + @include hover; + } + + } + + & .bottom { + display: flex; + justify-content: space-between; + align-items: center; + gap: 16px; + + @include ontablet { + gap: 24px; + } + + & .counter_container { + display: flex; + align-items: center; + + & .icon { + + &_container { + width: 32px; + height: 32px; + flex-shrink: 0; + } + + &_plus { + @include icon-bg(var(--fls-plus)); + } + + &_minus { + @include icon-bg(var(--fls-minus)); + + &_disabled { + @include icon-bg(var(--fls-minus-disabled)); + } + } + } + + & .count_text { + width: 32px; + text-align: center; + } + } + + } +} diff --git a/src/modules/CartPage/CartItem/CartItem.tsx b/src/modules/CartPage/CartItem/CartItem.tsx new file mode 100644 index 0000000000..2fe5ca0cf5 --- /dev/null +++ b/src/modules/CartPage/CartItem/CartItem.tsx @@ -0,0 +1,82 @@ +import classNames from 'classnames'; +import { Product } from '../../../types/Product'; +import style from './CartItem.module.scss'; +import { Link } from 'react-router-dom'; + +type Props = { + product: Product; + count: number; + onIncrease: (id: string) => void; + onDecrease: (id: string) => void; + onClear: (id: string) => void; +}; +export const CartItem: React.FC = ({ + product, + count, + onIncrease = () => {}, + onDecrease = () => {}, + onClear = () => {}, +}) => { + return ( +
+ {product && ( + <> +
+
onClear(product.itemId)} + > +
+
+ +
+ phone-image +
+ + + {product.name} + +
+ +
+
+
onDecrease(product.itemId)} + > +
+
+ +

{count}

+ +
onIncrease(product.itemId)} + > +
+
+
+ +

${product.price}

+
+ + )} +
+ ); +}; diff --git a/src/modules/CartPage/CartItem/index.ts b/src/modules/CartPage/CartItem/index.ts new file mode 100644 index 0000000000..37a0553540 --- /dev/null +++ b/src/modules/CartPage/CartItem/index.ts @@ -0,0 +1 @@ +export * from './CartItem'; diff --git a/src/modules/CartPage/CartPage.module.scss b/src/modules/CartPage/CartPage.module.scss index fbada7f1da..96c3f966f6 100644 --- a/src/modules/CartPage/CartPage.module.scss +++ b/src/modules/CartPage/CartPage.module.scss @@ -1 +1,161 @@ -@import '../../styles/main.scss'; +@import '../../styles/main'; + +.cart { + &_container { + width: 100%; + + @include inline-padding; + + padding-block: 24px 56px; + display: flex; + flex-direction: column; + gap: 32px; + + @include ontablet { + padding-block: 40px 64px; + } + + @include ondesktop { + padding-block: 40px 80px; + } + + & .container_nav { + display: flex; + align-items: center; + + & .icon { + display: flex; + align-items: center; + width: 16px; + height: 16px; + + + &_container { + @extend %icon-container; + + width: 16px; + height: 16px; + border: none; + } + + &_left { + @include icon-bg(var(--fls-left)); + } + } + + & p { + color: var(--c-secondary); + cursor: pointer; + + &:hover { + color: var(--c-primary) + } + } + } + + & .container_body { + display: flex; + flex-direction: column; + gap: 32px; + + & .container { + &_cartItems { + display: flex; + flex-direction: column; + gap: 16px; + } + + &_summary { + box-sizing: border-box; + display: flex; + flex-direction: column; + align-items: center; + gap: 16px; + padding: 24px; + border: 1px solid var(--c-elements); + + @include ondesktop { + width: 368px; + } + + & .seperator { + border: 1px solid var(--c-elements); + width: 100%; + } + } + + &_priceTotal { + display: flex; + flex-direction: column; + align-items: center; + + & p { + color: var(--c-secondary); + } + } + + &_checkout { + height: 48px; + width: 100%; + padding: 0; + } + } + } + + & .modal_container { + align-items: center; + background-color: rgba(0, 0, 0, .9); + display: flex; + inset: 0; + justify-content: center; + position: fixed; + z-index: 1000; + + & .modal { + background-color: var(--c-background); + padding: 20px; + display: flex; + flex-direction: column; + gap: 16px; + } + + & .modal_buttons { + display: flex; + justify-content: space-between; + height: 48px; + width: 100%; + + & .modal_bt { + width: 100px; + } + + } + + } + + & .container_noproducts { + display: flex; + align-items: center; + flex-direction: column; + + & .empty_image { + height: 150px; + } + } + + & .container_main { + box-sizing: border-box; + display: flex; + flex-direction: column; + gap: 32px; + width: 100%; + + @include ondesktop { + flex-direction: row; + align-items: flex-start; + gap: 16px; + } + + } + } +} diff --git a/src/modules/CartPage/CartPage.tsx b/src/modules/CartPage/CartPage.tsx index d0c3b9f67e..ae7e27c198 100644 --- a/src/modules/CartPage/CartPage.tsx +++ b/src/modules/CartPage/CartPage.tsx @@ -1,3 +1,189 @@ -// import stytle from './CartPage.module.scss'; +import style from './CartPage.module.scss'; +import { accessLocalStorage } from '../../utils/accessLocalStorage'; +import { LocalAccessKeys } from '../../utils/LocalAccessKeys'; +import { CartItem } from './CartItem'; +import { Product } from '../../types/Product'; +import { v4 as uuidv4 } from 'uuid'; +import { useEffect, useState } from 'react'; +import { getProducts } from '../../utils/getProducts'; +import classNames from 'classnames'; +import { useNavigate } from 'react-router-dom'; -export const CartPage = () =>

Cart page

; +export const CartPage = () => { + const navigate = useNavigate(); + const [products, setProducts] = useState( + accessLocalStorage.get(LocalAccessKeys.cart), + ); + const [uniqueProducts, setUniqueProducts] = useState([]); + const [checkOutActive, setCheckoutActive] = useState(false); + + useEffect(() => { + const uniqueArr: Product[] = []; + + for (let i = 0; i < products.length; i++) { + const prod = products[i]; + + if (uniqueArr.every(uprod => prod.itemId !== uprod.itemId)) { + uniqueArr.push(prod); + } + } + + setUniqueProducts(() => [...uniqueArr].sort((a, b) => b.price - a.price)); + }, [products]); + + function countProducts(itemId: string) { + const data = accessLocalStorage.get(LocalAccessKeys.cart); + + return data.filter((prod: Product) => itemId === prod.itemId).length; + } + + const handleCountIncrease = (id: string) => { + setProducts(prev => { + const newProd = getProducts.getProductById(products, id); + + if (newProd) { + accessLocalStorage.append(newProd, LocalAccessKeys.cart); + + return [...prev, newProd]; + } + + return prev; + }); + }; + + const handleCountDecrease = (id: string) => { + if (countProducts(id) - 1 > 0) { + setProducts(prev => { + const delIndex = prev.findIndex(prod => prod.itemId === id); + const newArr = [...prev]; + + newArr.splice(delIndex, 1); + accessLocalStorage.set(newArr, LocalAccessKeys.cart); + + return newArr; + }); + } + }; + + const handleClearItem = (id: string) => { + const clearedProds = products.filter(prod => prod.itemId !== id); + + setProducts(() => clearedProds); + + accessLocalStorage.set(clearedProds, LocalAccessKeys.cart); + }; + + const getTotalProce = () => { + return products.reduce((acc, cur) => acc + cur.price, 0); + }; + + const clearCart = () => { + setProducts([]); + accessLocalStorage.clearKey(LocalAccessKeys.cart); + }; + + return ( +
+
+
+
+
+ +

navigate(-1)}>Back

+
+ + {products.length > 0 ? ( +
+
+

Cart

+
+
+
+ {uniqueProducts.map(prod => { + return ( + + ); + })} +
+ +
+
+

${getTotalProce()}

+ +

{`Total for ${products.length === 1 ? `${products.length} item` : `${products.length} items`}`}

+
+ +
+ +
setCheckoutActive(true)} + > +
+ Checkout +
+
+
+
+
+ ) : ( +
+

Your Cart is Empty

+ +
+ )} + + {checkOutActive && ( +
+
+

Checkout is not implemented yet.

+ +

Do you want to clear the Cart?

+ +
+
{ + clearCart(); + setCheckoutActive(false); + }} + > +
Clear
+
+ +
setCheckoutActive(() => false)} + > +
Cancel
+
+
+
+
+ )} +
+ ); +}; diff --git a/src/modules/FavotitePage/FavoritePage.module.scss b/src/modules/FavotitePage/FavoritePage.module.scss new file mode 100644 index 0000000000..d5263822c8 --- /dev/null +++ b/src/modules/FavotitePage/FavoritePage.module.scss @@ -0,0 +1,6 @@ +@import '../../styles/main'; + +.favoritePage_container { + width: 100%; + height: 100%; +} diff --git a/src/modules/FavotitePage/FavoritePage.tsx b/src/modules/FavotitePage/FavoritePage.tsx new file mode 100644 index 0000000000..9b500cdf46 --- /dev/null +++ b/src/modules/FavotitePage/FavoritePage.tsx @@ -0,0 +1,21 @@ +import style from './FavoritePage.module.scss'; +import { Catalog } from '../../components/Catalog/Catalog'; +import { useContext, useEffect, useState } from 'react'; +import { StateContext } from '../../components/GlobalProvider'; + +export const FavoritePage = () => { + const { inFavorites } = useContext(StateContext); + const [favorites, setFavorites] = useState(inFavorites); + + useEffect(() => setFavorites(() => inFavorites), [inFavorites]); + + return ( +
+ +
+ ); +}; diff --git a/src/modules/FavotitePage/FavotitePage.module.scss b/src/modules/FavotitePage/FavotitePage.module.scss deleted file mode 100644 index fbada7f1da..0000000000 --- a/src/modules/FavotitePage/FavotitePage.module.scss +++ /dev/null @@ -1 +0,0 @@ -@import '../../styles/main.scss'; diff --git a/src/modules/FavotitePage/FavotitePage.tsx b/src/modules/FavotitePage/FavotitePage.tsx deleted file mode 100644 index 7280365de1..0000000000 --- a/src/modules/FavotitePage/FavotitePage.tsx +++ /dev/null @@ -1,3 +0,0 @@ -import stytle from './FavotitePage.module.scss'; - -export const FavotitePage = () =>

Favotite page

; diff --git a/src/modules/FavotitePage/index.ts b/src/modules/FavotitePage/index.ts index 51e7912bc3..e3ca0d0fcb 100644 --- a/src/modules/FavotitePage/index.ts +++ b/src/modules/FavotitePage/index.ts @@ -1 +1 @@ -export * from './FavotitePage'; +export * from './FavoritePage'; diff --git a/src/modules/HomePage/HomePage.module.scss b/src/modules/HomePage/HomePage.module.scss index 505b4f9451..4e14b0681c 100644 --- a/src/modules/HomePage/HomePage.module.scss +++ b/src/modules/HomePage/HomePage.module.scss @@ -1,5 +1,14 @@ @import '../../styles/main'; +.title { + @include page-grid; + + &_text { + grid-column: 1/-1; + + } +} + .container { width: 100%; display: flex; @@ -15,6 +24,10 @@ gap: 56px; } + & .title { + @include inline-padding; + } + &_homepage { box-sizing: border-box; display: flex; @@ -33,16 +46,7 @@ } } -.title { - @include pageGrid; - - &_text { - grid-column: 1/-1; - - } -} - -.photoSlide { +.photoSlide_wraper { box-sizing: border-box; @@ -56,23 +60,14 @@ .slider_container { box-sizing: border-box; width: 100%; - // height: 439px; - padding-inline: 16px; - @include ontablet { - // height: 512px; - padding-inline: 24px; - } - - @include ondesktop { - // height: 506px; - padding-inline: 32px; - } + @include inline-padding; } .container_categories { - @include pageGrid; + @include inline-padding; + @include page-grid; &_body { grid-column: 1/-1; diff --git a/src/modules/HomePage/HomePage.tsx b/src/modules/HomePage/HomePage.tsx index 48700e9982..71bc2481ea 100644 --- a/src/modules/HomePage/HomePage.tsx +++ b/src/modules/HomePage/HomePage.tsx @@ -4,18 +4,21 @@ import style from './HomePage.module.scss'; import { PhoneSlider } from '../../components/PhoneSlider'; import { useContext, useMemo } from 'react'; import { StateContext } from '../../components/GlobalProvider'; -import { getNewProducts } from '../../utils/getNewProducts'; import { Categories } from '../../components/Categories'; -import { getHotDealsProducts } from '../../utils/getHotDealsProducts'; import { PicturesSlider } from '../../components/PicturesSlider'; +import { getProducts } from '../../utils/getProducts'; export const HomePage = () => { - const { products, phones, tablets, accessories } = useContext(StateContext); - const newPhone = useMemo(() => getNewProducts(products), [products]); + const { products } = useContext(StateContext); + + const newPhone = useMemo( + () => getProducts.getNewProducts(products), + [products], + ); + const hotDealsProducts = useMemo( - () => - getHotDealsProducts(products, [...phones, ...tablets, ...accessories]), - [products, phones, tablets, accessories], + () => getProducts.getHotDealsProducts(products), + [products], ); return ( @@ -27,7 +30,7 @@ export const HomePage = () => {
-
+
diff --git a/src/modules/PhonePage/PhonePage.module.scss b/src/modules/PhonePage/PhonePage.module.scss index e69de29bb2..d661c194af 100644 --- a/src/modules/PhonePage/PhonePage.module.scss +++ b/src/modules/PhonePage/PhonePage.module.scss @@ -0,0 +1,6 @@ +@import '../../styles/main'; + +.phonePage_container { + width: 100%; + height: 100%; +} diff --git a/src/modules/PhonePage/PhonePage.tsx b/src/modules/PhonePage/PhonePage.tsx index eb534223be..ed13fe5b4a 100644 --- a/src/modules/PhonePage/PhonePage.tsx +++ b/src/modules/PhonePage/PhonePage.tsx @@ -1,66 +1,20 @@ +import style from './PhonePage.module.scss'; +import { useContext } from 'react'; +import { StateContext } from '../../components/GlobalProvider'; +import { Catalog } from '../../components/Catalog/Catalog'; +import { getProducts } from '../../utils/getProducts'; +import { MenuItems } from '../../types/MenuItems'; + export const PhonePage = () => { + const { products } = useContext(StateContext); + const phones = getProducts.getProductByCategory(products, MenuItems.phones); + return ( -
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
-
PHONE PAGE
+
+
); }; diff --git a/src/modules/ProductDetailsPage/ItemPhoto/ItemPhoto.tsx b/src/modules/ProductDetailsPage/ItemPhoto/ItemPhoto.tsx new file mode 100644 index 0000000000..7ca6db47db --- /dev/null +++ b/src/modules/ProductDetailsPage/ItemPhoto/ItemPhoto.tsx @@ -0,0 +1,57 @@ +import './styles.scss'; +import 'swiper/css'; +import 'swiper/css/free-mode'; +import 'swiper/css/navigation'; +import 'swiper/css/thumbs'; +import React, { useState } from 'react'; +import { Swiper, SwiperSlide } from 'swiper/react'; +import { SwiperClass } from 'swiper/react'; +import { FreeMode, Navigation, Thumbs } from 'swiper/modules'; +import { ProductItem } from '../../../types/ProductItem'; + +type Props = { + item: ProductItem; +}; + +export const ItemPhoto: React.FC = ({ item }) => { + const [thumbsSwiper, setThumbsSwiper] = useState(null); + + if (!item) { + return

Somthing went wrong

; + } + + const swiperSlideSet = item.images.map((imageLink: string) => { + return ( + + + + ); + }); + + return ( +
+ + {swiperSlideSet} + + + + {swiperSlideSet} + +
+ ); +}; diff --git a/src/modules/ProductDetailsPage/ItemPhoto/index.ts b/src/modules/ProductDetailsPage/ItemPhoto/index.ts new file mode 100644 index 0000000000..cc62838565 --- /dev/null +++ b/src/modules/ProductDetailsPage/ItemPhoto/index.ts @@ -0,0 +1 @@ +export * from './ItemPhoto'; diff --git a/src/modules/ProductDetailsPage/ItemPhoto/styles.scss b/src/modules/ProductDetailsPage/ItemPhoto/styles.scss new file mode 100644 index 0000000000..b2ccfd7275 --- /dev/null +++ b/src/modules/ProductDetailsPage/ItemPhoto/styles.scss @@ -0,0 +1,63 @@ +@import '../../../styles/main'; + +.ItemPhoto_container { + height: 100%; + display: flex; + flex-direction: column; + gap: 16px; + + @include ontablet { + flex-direction: row-reverse; + } + + & .ItemPhoto { + &_thumbs { + // width: 100%; + + & .swiper-wrapper { + @include ontablet { + flex-direction: column; + } + } + + & .swiper-slide { + box-sizing: border-box; + overflow: hidden; + height: 49px; + width: 49px; + display: flex; + justify-content: center; + gap: 8px; + padding: 4px; + border: 1px solid var(--c-elements); + + } + + & .swiper-slide-thumb-active { + border: 1px solid var(--c-primary) + } + } + + &_main { + width: 100%; + height: 288px; + + + & .swiper-wrapper { + height: 100%; + } + + & .swiper-slide { + box-sizing: border-box; + display: flex; + justify-content: center; + height: 100%; + overflow: hidden; + aspect-ratio: 1/1; + + + } + + } + } +} diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss b/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss new file mode 100644 index 0000000000..09fd17725a --- /dev/null +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss @@ -0,0 +1,295 @@ +@import '../../styles/main'; + +.prod_page_container { + box-sizing: border-box; + display: flex; + width: 100%; + max-width: 1200px; + padding-block: 24px 56px; + + @include inline-padding; + + @include ontablet { + padding-block: 24px 64px; + } + + @include ondesktop { + padding-block: 24px 81px; + } + + & .container { + &_body { + box-sizing: border-box; + display: flex; + flex-direction: column; + width: 100%; + + @include ondesktop { + @include page-grid; + + padding-block: 0; + } + } + + &_breadcrumbs { + width: 100%; + padding-bottom: 24px; + + @include ontablet { + padding-bottom: 40px; + } + + @include ondesktop { + grid-row: 1/1; + grid-column: 1/-1; + } + } + + &_back_bt { + display: flex; + position: relative; + align-items: flex-end; + padding-bottom: 16px; + + @include ondesktop { + grid-row: 2/2; + grid-column: 1/-1; + } + + & .icon { + display: flex; + align-items: center; + + &_container { + @extend %icon-container; + + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + width: 16px; + height: 16px; + border: none; + } + + &_right { + @include icon-bg(var(--fls-left)); + + width: 16px; + height: 16px; + } + } + + &_text { + cursor: pointer; + + @extend %small-text; + + color: var(--c-secondary); + line-height: 100%; + + &:hover { + color: var(--c-primary); + } + + } + } + + &_title { + padding-bottom: 32px; + + @include ontablet { + padding-bottom: 40px; + } + + @include ondesktop { + grid-row: 3/3; + grid-column: 1/-1; + } + } + + &_photo_option { + width: 100%; + + @include ontablet { + padding-bottom: 64px; + + @include page-grid; + } + + @include ondesktop { + padding-bottom: 80px; + grid-row: 4/4; + grid-column: 1/-1; + } + + & .photo_slider { + padding-bottom: 40px; + + @include ontablet { + padding-bottom: 0; + grid-column: 1/7; + } + + @include ondesktop { + grid-column: 1/13; + } + + } + + & .options_addCart { + display: flex; + flex-direction: column; + gap: 24px; + padding-bottom: 56px; + + @include ontablet { + padding-bottom: 0; + grid-column: 7/-1; + } + + @include ondesktop { + grid-column: 14/ span 7; + } + + & .container { + &_colors_selection { + display: flex; + flex-direction: column; + gap: 24px; + } + + &_capacity_selection { + display: flex; + flex-direction: column; + gap: 24px; + } + + &_cart_fav_price { + padding-top: 6px; + display: flex; + flex-direction: column; + gap: 16px; + } + + &_price { + display: flex; + gap: 8px; + + & .price_crossout { + color: var(--c-secondary); + text-decoration: line-through; + } + } + + &_cart_favorite { + display: flex; + justify-content: space-between; + width: 100%; + height: 48px; + } + + &_specs { + display: flex; + flex-direction: column; + gap: 8px; + + & .spec_line { + display: flex; + justify-content: space-between; + + & .spec_item { + color: var(--c-secondary); + } + } + } + + } + } + } + + &_about { + display: flex; + flex-direction: column; + gap: 32px; + padding-bottom: 56px; + + @include ontablet { + padding-bottom: 64px; + } + + @include ondesktop { + padding-bottom: 0; + grid-row: 5/5; + grid-column: 1/13; + } + + h3 { + padding-bottom: 16px; + } + + & .sections { + + & h4 { + padding-bottom: 16px; + } + + & p { + color: var(--c-secondary); + } + } + } + + &_full_specs { + padding-bottom: 56px; + + @include ontablet { + padding-bottom: 64px; + } + + @include ondesktop { + padding-bottom: 0; + grid-row: 5/5; + grid-column: 14/-1; + } + + & h3 { + padding-bottom: 16px; + } + + & .full_spec_seperator { + margin-bottom: 30px; + } + + & .full_spec_container { + display: flex; + flex-direction: column; + gap: 8px; + padding: 0; + } + + & .full_spec_line { + display: flex; + justify-content: space-between; + } + + + & .full_spec_item { + color: var(--c-secondary); + } + } + + &_other_like { + overflow: hidden; + + @include ondesktop { + grid-row: 6/6; + grid-column: 1/-1; + } + } + } + + & .container_seperator { + border: 1px solid var(--c-elements); + } +} diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx new file mode 100644 index 0000000000..32b12b02e8 --- /dev/null +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx @@ -0,0 +1,322 @@ +import { Location, useLocation, useNavigate } from 'react-router-dom'; +import style from './ProductDetailsPage.module.scss'; +import { useContext, useEffect, useState } from 'react'; +import { ProductItem } from '../../types/ProductItem'; +import { getProducts } from '../../utils/getProducts'; +import { Loader } from '../../components/Loader'; +import { BreadCrumbs } from '../../components/BreadCrumbs'; +import classNames from 'classnames'; +import { ColorSelector } from '../../components/ColorSelector'; +import { CapacitySelector } from '../../components/CapacitySelector'; +import { getProductItems } from '../../utils/getProductItems'; +import { ButtonsAddCardFav } from '../../components/ButtonsAddCardFav'; +import { PhoneSlider } from '../../components/PhoneSlider'; +import { StateContext } from '../../components/GlobalProvider'; +import { Product } from '../../types/Product'; +import { MenuItems } from '../../types/MenuItems'; +import { ItemPhoto } from './ItemPhoto/ItemPhoto'; + +const getIdFromURL = (location: Location) => { + return location.pathname.split('/').slice(-1)[0]; +}; + +type Props = { + category: MenuItems; +}; + +export const ProductDetailsPage: React.FC = ({ category }) => { + const location = useLocation(); + const navigate = useNavigate(); + const { products } = useContext(StateContext); + const [item, setItem] = useState(); + const [allCatalogProducts, setAllCatalogProducts] = useState( + [], + ); + const [similarProduct, setSimilarProducts] = useState([]); + const [loading, setLoading] = useState(false); + + const curItemId = getIdFromURL(location); + + useEffect(() => { + setLoading(true); + getProductItems + .fetchByCategory(category) + .then(res => setAllCatalogProducts(() => res)) + .finally(() => setLoading(false)); + }, [category]); + + useEffect(() => { + if (!products.length) { + return; + } + + const currentItem = allCatalogProducts.find( + (prod: ProductItem) => prod.id === curItemId, + ); + + setItem(() => currentItem); + + if (currentItem) { + const simItems: ProductItem[] = allCatalogProducts.filter( + itm => itm.namespaceId === currentItem.namespaceId, + ); + + const simProducts: Product[] = []; + + simItems.forEach((itm: ProductItem) => { + const found = getProducts.getProductById(products, itm.id); + + if (found) { + simProducts.push(found); + } + }); + + setSimilarProducts(() => simProducts); + } + }, [products, allCatalogProducts, curItemId]); + + const handleColorChange = (selectedColor: string) => { + if (selectedColor && item && allCatalogProducts.length > 0) { + const newItem = getProductItems.getColorVariant( + allCatalogProducts, + item, + selectedColor, + ); + + if (newItem) { + setItem(() => newItem); + navigate(`/${item?.category}/${newItem.id}`); + } + } + }; + + const handleCapacityChange = (selectedCapacity: string) => { + if (selectedCapacity && item && allCatalogProducts.length > 0) { + const newItem = getProductItems.getCapacityVariant( + allCatalogProducts, + item, + selectedCapacity, + ); + + if (newItem) { + setItem(() => newItem); + navigate(`/${item?.category}/${newItem.id}`); + } + } + }; + + return ( +
+ {loading ? ( +
+ +
+ ) : ( + <> + {item ? ( +
+
+ +
+ +
+
navigate(-1)} + > +
navigate(-1)} + /> +
+ +

navigate(-1)} + > + Back +

+
+ +
+

{item.name}

+
+ +
+
+ +
+ +
+
+ + +
+
+ +
+ + +
+
+ +
+
+

${item.priceDiscount}

+ +

+ ${item.priceRegular} +

+
+ +
+ +
+
+ +
+
+

Screen

+ +

{item.screen}

+
+ +
+

Resolution

+ +

{item.resolution}

+
+ +
+

Processor

+ +

{item.processor}

+
+ +
+

RAM

+ +

{item.ram}

+
+
+
+
+ +
+
+

About

+ +
+
+ + {item.description.map(section => { + return ( +
+

{section.title}

+ + {section.text.map((parag, i) => { + return

{parag}

; + })} +
+ ); + })} +
+ +
+

Tech specs

+ +
+ +
    +
  • +

    Screen

    + +

    {item.screen}

    +
  • + +
  • +

    Resolution

    + +

    + {item.resolution} +

    +
  • + +
  • +

    Processor

    + +

    {item.processor}

    +
  • + +
  • +

    RAM

    + +

    {item.ram}

    +
  • + +
  • +

    Built in memory

    + +

    {item.capacity}

    +
  • + +
  • +

    Camera

    + +

    {item.camera}

    +
  • + +
  • +

    Zoom

    + +

    {item.zoom}

    +
  • + +
  • +

    Cell

    + +

    + {item.cell.map((channel, i) => ( + {channel}, + ))} +

    +
  • +
+
+ +
+ +
+
+ ) : ( +

Problem with phones

+ )} + + )} +
+ ); +}; diff --git a/src/modules/ProductDetailsPage/index.ts b/src/modules/ProductDetailsPage/index.ts new file mode 100644 index 0000000000..6615089e5e --- /dev/null +++ b/src/modules/ProductDetailsPage/index.ts @@ -0,0 +1 @@ +export * from './ProductDetailsPage'; diff --git a/src/modules/Root/Root.tsx b/src/modules/Root/Root.tsx index 34062c6dd1..8988f1d67a 100644 --- a/src/modules/Root/Root.tsx +++ b/src/modules/Root/Root.tsx @@ -8,8 +8,12 @@ import { import { App } from '../App/App'; import { HomePage } from '../HomePage/HomePage'; import { PhonePage } from '../PhonePage'; -import { FavotitePage } from '../FavotitePage'; +import { FavoritePage } from '../FavotitePage'; import { CartPage } from '../CartPage'; +import { TabletPage } from '../TabletPage'; +import { AccessoriesPage } from '../AccessoriesPage'; +import { ProductDetailsPage } from '../ProductDetailsPage'; +import { MenuItems } from '../../types/MenuItems'; export const Root = () => { return ( @@ -34,19 +38,42 @@ export const Root = () => { } /> - } - /> + + } + /> + } + /> + - } - /> + + } + /> + } + /> + + + + } + /> + } + /> + } + element={} /> { element={} /> - {/* } /> */} - {/* } /> */} diff --git a/src/modules/TabletPage/TabletPage.module.scss b/src/modules/TabletPage/TabletPage.module.scss new file mode 100644 index 0000000000..c3b9bf1178 --- /dev/null +++ b/src/modules/TabletPage/TabletPage.module.scss @@ -0,0 +1,6 @@ +@import '../../styles/main'; + +.tabletPage_container { + width: 100%; + height: 100%; +} diff --git a/src/modules/TabletPage/TabletPage.tsx b/src/modules/TabletPage/TabletPage.tsx new file mode 100644 index 0000000000..ae61c0e47a --- /dev/null +++ b/src/modules/TabletPage/TabletPage.tsx @@ -0,0 +1,20 @@ +import style from './TabletPage.module.scss'; +import { useContext } from 'react'; +import { StateContext } from '../../components/GlobalProvider'; +import { Catalog } from '../../components/Catalog/Catalog'; +import { getProducts } from '../../utils/getProducts'; +import { MenuItems } from '../../types/MenuItems'; + +export const TabletPage = () => { + const { products } = useContext(StateContext); + const tablet = getProducts.getProductByCategory(products, MenuItems.tablets); + + return ( +
+ +
+ ); +}; diff --git a/src/modules/TabletPage/index.ts b/src/modules/TabletPage/index.ts new file mode 100644 index 0000000000..74893971d9 --- /dev/null +++ b/src/modules/TabletPage/index.ts @@ -0,0 +1 @@ +export * from './TabletPage'; diff --git a/src/modules/constants/index.ts b/src/modules/constants/index.ts index 1584ae6745..bf232d8ffe 100644 --- a/src/modules/constants/index.ts +++ b/src/modules/constants/index.ts @@ -1 +1 @@ -export * from '../../types/MenuItems'; +export * from './productColors'; diff --git a/src/modules/constants/productColors.ts b/src/modules/constants/productColors.ts new file mode 100644 index 0000000000..46e854c17b --- /dev/null +++ b/src/modules/constants/productColors.ts @@ -0,0 +1,45 @@ +type ProductColors = { + black: string; + blue: string; + coral: string; + gold: string; + graphite: string; + green: string; + midnight: string; + midnightgreen: string; + pink: string; + purple: string; + red: string; + rosegold: string; + sierrablue: string; + silver: string; + skyblue: string; + spaceblack: string; + spacegray: string; + starlight: string; + white: string; + yellow: string; +}; + +export const productColors: ProductColors = { + black: '#3C4042', + blue: '#CED5D9', + coral: '#FF6E5A', + gold: '#F4E8CE', + graphite: '#54524F', + green: '#576856', + midnight: '#232A31', + midnightgreen: '#394C38', + pink: '#FADDD7', + purple: '#594F63', + red: '#FC0324', + rosegold: '#F7E8DD', + sierrablue: '#A7C1D9', + silver: '#F1F2ED', + skyblue: '#276787', + spaceblack: '#403E3D', + spacegray: '#535150', + starlight: '#FAF6F2', + white: '#F6F2EF', + yellow: '#FFE681', +}; diff --git a/src/styles/buttons.scss b/src/styles/buttons.scss new file mode 100644 index 0000000000..fe367289a6 --- /dev/null +++ b/src/styles/buttons.scss @@ -0,0 +1,33 @@ +.buttons { + &_container { + background-color: var(--c-buttons-background); + height: 100%; + display: flex; + align-items: center; + justify-content: center; + padding-inline: 5px; + + &:hover { + box-shadow: 0 3px 13px 0 #17203166; + background-color: var(--c-buttons-background-hover); + } + + &_selected { + background-color: var(--c-buttons-background-selected); + border: 1px solid var(--c-elements) + } + + + } +} + +.buttons_text { + @extend %buttons; + + cursor: default; + color: var(--c-buttons-text); + + &_selected { + color: var(--c-buttons-text-selected); + } +} diff --git a/src/styles/icons.scss b/src/styles/icons.scss index 7ff6eba772..2dfdb5e11e 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -1,7 +1,7 @@ @import './mixines'; -%icon_container { - cursor: pointer; +%icon-container { + cursor: default; box-sizing: border-box; display: flex; justify-content: center; diff --git a/src/styles/main.scss b/src/styles/main.scss index 70e948d709..ccb926bca8 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -1,5 +1,6 @@ @import './mixines'; @import './icons'; +@import './buttons'; @import './typography'; @import './variables'; @import './theme'; @@ -25,7 +26,7 @@ body { margin: 0; padding: 0; - background-color: var(--c-backgroud); + background-color: var(--c-background); min-height: 100vh; box-sizing: border-box; } @@ -38,6 +39,7 @@ html { p { margin: 0; + color: var(--c-primary); } h1 { @@ -58,5 +60,13 @@ h3 { } .isActive_link { - @include isActive; + @include is-active; +} + +.hidden { + display: none; +} + +.loader_container { + flex-grow: 1; } diff --git a/src/styles/mixines.scss b/src/styles/mixines.scss index 1403d39d9c..507574ef04 100644 --- a/src/styles/mixines.scss +++ b/src/styles/mixines.scss @@ -1,5 +1,5 @@ @import './variables'; -@import './theme.scss'; +@import './theme'; @mixin ondesktop { @media (min-width: $desktop-min-width) { @@ -13,25 +13,35 @@ } } -@mixin pageGrid { +@mixin page-grid { box-sizing: border-box; + --columns: 4; display: grid; column-gap: 16px; - padding-inline: $mobil-padding-inline; - grid-template-columns: repeat(var(--columns), 1fr); @include ontablet { --columns: 12; - padding-inline: $tablet-padding-inline; } @include ondesktop { --columns: 24; + grid-template-columns: repeat(var(--columns), 32px); + } +} + +@mixin inline-padding { + padding-inline: $mobil-padding-inline; + + @include ontablet { + padding-inline: $tablet-padding-inline; + } + + @include ondesktop { padding-inline: $desktop-padding-inline; } } @@ -57,14 +67,12 @@ } } -@mixin isActive { +@mixin is-active { border-bottom: 3px solid var(--c-primary); color: var(--c-primary); } -@mixin iconBg($url, $bgColor: var(--c-primary)) { - // mask: #{$url} no-repeat 50% 50% / 16px; - // background-color: #{$bgColor}; +@mixin icon-bg($url, $bgColor: var(--c-primary)) { background-image: #{$url}; width: 16px; height: 16px; diff --git a/src/styles/theme.scss b/src/styles/theme.scss index 6cc5a4f0b0..f3845d81dc 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -1,24 +1,71 @@ .theme { + --c-white: #FFF; + --c-background: var(--c-white); --c-accent: #F86800; --c-accent-secondary: #476DF4; --c-primary: #313237; --c-secondary: #89939A; --c-icons: #B4BDC3; --c-elements: #E2E6E9; - --c-backgroud: #FFF; - --c-white: #FFF; --c-green: #27ae60; + --c-buttons-background: var(--c-primary); + --c-buttons-background-selected: var(--c-white); + --c-buttons-background-hover: var(--c-primary); + --c-buttons-text: var(--c-white); + --c-buttons-text-selected: var(--c-green); + --c-buttons-nav: var(--c-background); + --fls-logo: url('src/icons/Logo.png'); + --fls-right: url('src/icons/arrowRight.svg'); + --fls-right-disabled: url('src/icons/arrowRight_disabled.svg'); + --fls-left: url('src/icons/arrowLeft.svg'); + --fls-left-disabled: url('src/icons/arrowLeft_disabled.svg'); + --fls-up: url('src/icons/arrowUp.svg'); + --fls-up-disabled: url('src/icons/arrowUp_disabled.svg'); + --fls-down-disabled: url('src/icons/arrowDown_disabled.svg'); + --fls-home: url('src/icons/home.svg'); + --fls-favorite: url('src/icons/favorite.svg'); + --fls-favorite-selected: url('src/icons/favorite_selected.svg'); + --fls-cart: url('src/icons/cart.svg'); + --fls-dark-mode: url('src/icons/dark_mode.svg'); + --fls-menu: url('src/icons/menu.svg'); + --fls-close: url('src/icons/close.svg'); + --fls-plus: url('src/icons/plus.svg'); + --fls-minus: url('src/icons/minus.svg'); + --fls-minus-disabled: url('src/icons/minus_disabled.svg'); &_dark { --c-accent: #905BFF; --c-accent-secondary: #476DF4; - --c-primary: #ffffff; - --c-secondary: #89939A; + --c-primary: #fff; + --c-secondary: #75767F; --c-icons: #4A4D58; - ; --c-elements: #3B3E4A; - --c-backgroud: #0F1121; - --c-white: #ffffff; + --c-background: #0F1121; + --c-white: #F1F2F9; --c-green: #27ae60; + --c-buttons-background: var(--c-accent); + --c-buttons-background-selected: #323542; + --c-buttons-background-hover: #A378FF; + --c-buttons-text: var(--c-white); + --c-buttons-text-selected: #F1F2F9; + --c-buttons-nav: #323542; + --fls-logo: url('src/icons/Logo-dark.png'); + --fls-right: url('src/icons/darkMode/arrowRight.svg'); + --fls-right-disabled: url('src/icons/arrowRight_disabled.svg'); + --fls-left: url('src/icons/darkMode/arrowLeft.svg'); + --fls-left-disabled: url('src/icons/arrowLeft_disabled.svg'); + --fls-up: url('src/icons/darkMode/arrowUp.svg'); + --fls-up-disabled: url('src/icons/arrowUp_disabled.svg'); + --fls-down-disabled: url('src/icons/arrowDown_disabled.svg'); + --fls-home: url('src/icons/darkMode/home.svg'); + --fls-favorite: url('src/icons/darkMode/favorite.svg'); + --fls-favorite-selected: url('src/icons/favorite_selected.svg'); + --fls-cart: url('src/icons/darkMode/cart.svg'); + --fls-dark-mode: url('src/icons/darkMode/dark_mode.svg'); + --fls-menu: url('src/icons/darkMode/menu.svg'); + --fls-close: url('src/icons/darkMode/close.svg'); + --fls-plus: url('src/icons/darkMode/plus.svg'); + --fls-minus: url('src/icons/darkMode/minus.svg'); + --fls-minus-disabled: url('src/icons/minus_disabled.svg'); } } diff --git a/src/styles/typography.scss b/src/styles/typography.scss index 768bb73004..22b5a9fde9 100644 --- a/src/styles/typography.scss +++ b/src/styles/typography.scss @@ -2,18 +2,17 @@ @import './mixines'; @include headings { - font-family: Mont; + font-family: Mont, sans-serif;; letter-spacing: 0; color: var(--c-primary); } body { - font-family: Mont; + font-family: Mont, sans-serif;; margin: 0; } h1 { - //styleName: Mobile/H1; font-size: 32px; font-weight: 800; line-height: 41px; @@ -22,21 +21,18 @@ h1 { text-align: left; @include ontablet { - //styleName: Desktop & Tablet/H1; font-size: 48px; line-height: 56px; } } h2 { - //styleName: Mobile/H2; font-size: 22px; font-weight: 800; line-height: 30.8px; margin: 0; @include ontablet { - //styleName: Desktop & Tablet/H2; font-size: 32px; line-height: 41px; letter-spacing: -0.01em; @@ -44,14 +40,12 @@ h2 { } h3 { - //styleName: Mobile/H3; font-size: 20px; font-weight: 700; line-height: 25.56px; margin: 0; @include ontablet { - //styleName: Desktop & Tablet/H3; font-size: 22px; font-weight: 800; line-height: 30.8px; @@ -59,14 +53,12 @@ h3 { } h4 { - //styleName: Mobile/H4; font-size: 16px; font-weight: 700; line-height: 20.45px; margin: 0; @include ontablet { - //styleName: Desktop & Tablet/H4; font-size: 20px; font-weight: 700; line-height: 25.56px; @@ -74,7 +66,6 @@ h4 { } p { - //styleName: Body text 14; font-size: 14px; font-weight: 600; line-height: 21px; @@ -82,8 +73,8 @@ p { margin: 0; } + %uppercase { - //styleName: Uppercase; font-size: 12px; font-weight: 800; line-height: 11px; @@ -93,17 +84,15 @@ p { } %buttons { - //styleName: Uppercase; font-size: 12px; font-weight: 800; line-height: 11px; letter-spacing: 0.04em; - color: var(--c-white); margin: 0; + text-align: center; } %small-text { - //styleName: Small text 12; font-size: 12px; font-weight: 700; line-height: 15.34px; diff --git a/src/types/SearchParams.ts b/src/types/SearchParams.ts new file mode 100644 index 0000000000..eb7bb0722f --- /dev/null +++ b/src/types/SearchParams.ts @@ -0,0 +1,7 @@ +export enum SearchParams { + 'order' = 'order', + 'perPage' = 'perPage', + 'page' = 'page', + 'productColor' = 'productColor', + 'capacity' = 'capacity', +} diff --git a/src/types/SortFilter.ts b/src/types/SortFilter.ts new file mode 100644 index 0000000000..f445ad3560 --- /dev/null +++ b/src/types/SortFilter.ts @@ -0,0 +1,5 @@ +export enum SortingTypes { + 'Newest' = 'Newest', + 'Alphabetical' = 'Alphabetical', + 'Cheapest' = 'Cheapest', +} diff --git a/src/utils/LocalAccessKeys.ts b/src/utils/LocalAccessKeys.ts new file mode 100644 index 0000000000..ca0c27314f --- /dev/null +++ b/src/utils/LocalAccessKeys.ts @@ -0,0 +1,4 @@ +export enum LocalAccessKeys { + 'favorites' = 'favorites', + 'cart' = 'cart', +} diff --git a/src/utils/LocalStorage.ts b/src/utils/LocalStorage.ts deleted file mode 100644 index 8b49f6b222..0000000000 --- a/src/utils/LocalStorage.ts +++ /dev/null @@ -1,22 +0,0 @@ -export const accessLocalStorage = { - key: 'todos', - get() { - const data = localStorage.getItem(this.key); - - try { - return data ? JSON.parse(data) : []; - } catch { - return []; - } - }, - - set(todos: unknown) { - try { - localStorage.setItem(this.key, JSON.stringify(todos)); - - return this.get; - } catch { - return []; - } - }, -}; diff --git a/src/utils/accessLocalStorage.ts b/src/utils/accessLocalStorage.ts index 8d42d30dd5..cc14530075 100644 --- a/src/utils/accessLocalStorage.ts +++ b/src/utils/accessLocalStorage.ts @@ -1,12 +1,16 @@ -// import { MenuItems } from '../types/MenuItems'; import { Product } from '../types/Product'; -import { ProductItem } from '../types/ProductItem'; +import { LocalAccessKeys } from './LocalAccessKeys'; -// type Keys = keyof typeof MenuItems | 'favorites' | 'cart'; -type Keys = 'favorites' | 'cart'; +function addProduct(data: Product[], target: Product) { + return [...data, target]; +} + +function removeProduct(data: Product[], target: Product) { + return [...data.filter(item => item.itemId !== target.itemId)]; +} export const accessLocalStorage = { - get(key: Keys) { + get(key: LocalAccessKeys) { const data = localStorage.getItem(key); try { @@ -16,7 +20,7 @@ export const accessLocalStorage = { } }, - set(data: Product[] | ProductItem[], key: Keys) { + set(data: Product[], key: LocalAccessKeys) { try { localStorage.setItem(key, JSON.stringify(data)); @@ -25,4 +29,49 @@ export const accessLocalStorage = { return []; } }, + + toggle(product: Product | undefined, key: LocalAccessKeys) { + const inMemory = this.get(key); + let newList = []; + + if (product) { + if (!inMemory.find((prod: Product) => prod.itemId === product.itemId)) { + newList = addProduct(inMemory, product); + } else { + newList = removeProduct(inMemory, product); + } + + this.set(newList, key); + } + + return this.get(key); + }, + + append(product: Product | undefined, key: LocalAccessKeys) { + if (product) { + const newList = addProduct(this.get(key), product); + + this.set(newList, key); + + return newList; + } + + return; + }, + + remove(product: Product | undefined, key: LocalAccessKeys) { + if (product) { + const newList = removeProduct(this.get(key), product); + + this.set(newList, key); + + return newList; + } + + return; + }, + + clearKey(key: LocalAccessKeys) { + localStorage.removeItem(key); + }, }; diff --git a/src/utils/catalogHelper.ts b/src/utils/catalogHelper.ts new file mode 100644 index 0000000000..f586f9f8e1 --- /dev/null +++ b/src/utils/catalogHelper.ts @@ -0,0 +1,50 @@ +import { Product } from '../types/Product'; +import { SearchParams } from '../types/SearchParams'; +import { SortingTypes } from '../types/SortFilter'; + +export const catalogHelper = { + sort(order: string | null = null, products: Product[] = []): Product[] { + const sortedProds = [...products]; + + switch (order) { + case SortingTypes.Alphabetical: + default: + sortedProds.sort((a, b) => a.name.localeCompare(b.name)); + break; + + case SortingTypes.Cheapest: + sortedProds.sort((a, b) => a.price - b.price); + break; + + case SortingTypes.Newest: + sortedProds.sort((a, b) => b.year - a.year); + break; + } + + return sortedProds ? sortedProds : []; + }, + + perPage(sizeStr: string | null = '', products: Product[] = []) { + if (!sizeStr || isNaN(parseInt(sizeStr))) { + return [products]; + } + + const size = parseInt(sizeStr); + const chunk = Array.from( + { length: Math.ceil(products.length / size) }, + (_, i) => products.slice(i * size, i * size + size), + ); + + return chunk; + }, + + getCurrenPageParam(searchParams: URLSearchParams) { + const curPgParam = searchParams.get(SearchParams.page); + + if (!curPgParam || isNaN(parseInt(curPgParam))) { + return 1; + } + + return parseInt(curPgParam); + }, +}; diff --git a/src/utils/fetch.ts b/src/utils/fetch.ts index a412a54d99..009201f898 100644 --- a/src/utils/fetch.ts +++ b/src/utils/fetch.ts @@ -24,11 +24,11 @@ function request( }; } - return wait(3000) + return wait(1000) .then(() => fetch(BASE_API_URL + url, options)) .then(response => { if (!response.ok) { - throw new Error(); + throw new Error('Error in Fetch'); } return response.json(); diff --git a/src/utils/getCategoriesList.ts b/src/utils/getCategoriesList.ts deleted file mode 100644 index 3b29017a89..0000000000 --- a/src/utils/getCategoriesList.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Product } from '../types/Product'; - -export function getCategoriesList(products: Product[]) { - return new Set(products.map(product => product.category)); -} diff --git a/src/utils/getHotDealsProducts.ts b/src/utils/getHotDealsProducts.ts deleted file mode 100644 index 724050903b..0000000000 --- a/src/utils/getHotDealsProducts.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { Product } from '../types/Product'; -import { ProductItem } from '../types/ProductItem'; -import { getProductItemByID } from './getProductItemByID'; - -export function getHotDealsProducts( - products: Product[], - productItems: ProductItem[], -) { - const newProducts: Product[] = products.map(prod => { - let discount = 0; - const prodItem = getProductItemByID(productItems, prod.itemId); - - if (prodItem) { - discount = prodItem.priceRegular - prodItem.priceDiscount; - } - - return { - ...prod, - discount: discount ? discount : 0, - discountPrice: prodItem ? prodItem.priceDiscount : 0, - }; - }); - - const sorted = newProducts.sort((a, b) => { - if (a.discount && b.discount) { - return b.discount - a.discount; - } - - return 0; - }); - - const cleaned: Product[] = sorted.map(item => { - const { discount, ...rest } = item; - - return rest; - }); - - return cleaned; -} diff --git a/src/utils/getNewProducts.ts b/src/utils/getNewProducts.ts deleted file mode 100644 index 2e1fbe576c..0000000000 --- a/src/utils/getNewProducts.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Product } from '../types/Product'; - -export function getNewProducts(products: Product[]) { - return products.filter(product => product.year >= 2022); -} diff --git a/src/utils/getProductById.ts b/src/utils/getProductById.ts deleted file mode 100644 index f925510b11..0000000000 --- a/src/utils/getProductById.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { Product } from '../types/Product'; - -export function getProductById(products: Product[], itemId: string) { - return products.find(product => product.itemId === itemId); -} diff --git a/src/utils/getProductItemByID.ts b/src/utils/getProductItemByID.ts deleted file mode 100644 index 74fb147b29..0000000000 --- a/src/utils/getProductItemByID.ts +++ /dev/null @@ -1,5 +0,0 @@ -import { ProductItem } from '../types/ProductItem'; - -export function getProductItemByID(products: ProductItem[], id: string) { - return products.find(product => product.id === id); -} diff --git a/src/utils/getProductItems.ts b/src/utils/getProductItems.ts new file mode 100644 index 0000000000..899a468e8c --- /dev/null +++ b/src/utils/getProductItems.ts @@ -0,0 +1,62 @@ +import { MenuItems } from '../types/MenuItems'; +import { ProductItem } from '../types/ProductItem'; +import { client } from './fetch'; + +export const getProductItems = { + fetchByCategory(category: string): Promise { + return client.get(`${category}.json`); + }, + + fetchAllCategories() { + const allPromises: Promise[] = []; + + Object.values(MenuItems).forEach(category => { + allPromises.push(getProductItems.fetchByCategory(category)); + }); + + return Promise.allSettled(allPromises); + }, + + fetchDetails(category: string, id: string): Promise { + return this.fetchByCategory(category).then((result: ProductItem[]) => + this.getById(result, id), + ); + }, + + getById( + products: ProductItem[] | undefined, + id: string, + ): ProductItem | undefined { + if (products) { + return products.find(product => product.id === id); + } + + return; + }, + + getColorVariant( + products: ProductItem[], + item: ProductItem, + selectedColor: string, + ): ProductItem | undefined { + const { namespaceId, capacity } = { ...item }; + + return products + .filter(itm => itm.namespaceId === namespaceId) + .filter(itm2 => itm2.capacity === capacity) + .find(itm3 => itm3.color === selectedColor); + }, + + getCapacityVariant( + products: ProductItem[], + item: ProductItem, + selectedCapacity: string, + ): ProductItem | undefined { + const { namespaceId, color } = { ...item }; + + return products + .filter(itm => itm.namespaceId === namespaceId) + .filter(itm3 => itm3.color === color) + .find(itm2 => itm2.capacity === selectedCapacity); + }, +}; diff --git a/src/utils/getProducts.ts b/src/utils/getProducts.ts index 7111f4c6e2..d161d41c92 100644 --- a/src/utils/getProducts.ts +++ b/src/utils/getProducts.ts @@ -1,6 +1,48 @@ +import { MenuItems } from '../types/MenuItems'; import { Product } from '../types/Product'; import { client } from './fetch'; -export function getProducts(): Promise { - return client.get('products.json'); -} +export const getProducts = { + fetchProducts(): Promise { + return client.get('products.json'); + }, + + getDiscount(item: Product) { + return item.fullPrice - item.price; + }, + + getHotDealsProducts(products: Product[]) { + const sorted = products.sort((a, b) => { + return this.getDiscount(b) - this.getDiscount(a); + }); + + return sorted; + }, + + getNewProducts(products: Product[]) { + const maxYear: number = products + .map(prod => prod.year) + .reduce((prev, cur) => (cur > prev ? cur : prev), 0); + + return products.filter(product => product.year >= maxYear); + }, + + getProductById(products: Product[], itemId: string) { + if (products) { + return products.find(product => product.itemId === itemId); + } + + return; + }, + + getProductByCategory( + products: Product[], + category: MenuItems, + ): Product[] | undefined { + if (products) { + return products.filter(product => product.category === category); + } + + return; + }, +}; diff --git a/src/utils/getProductsByCategory.ts b/src/utils/getProductsByCategory.ts deleted file mode 100644 index cdec6ad0f1..0000000000 --- a/src/utils/getProductsByCategory.ts +++ /dev/null @@ -1,8 +0,0 @@ -import { ProductItem } from '../types/ProductItem'; -import { client } from './fetch'; - -export function getProductsByCategory( - category: string, -): Promise { - return client.get(`${category}.json`); -} diff --git a/src/utils/sentenseFormating.ts b/src/utils/sentenseFormating.ts new file mode 100644 index 0000000000..147d4f9537 --- /dev/null +++ b/src/utils/sentenseFormating.ts @@ -0,0 +1,3 @@ +export const sentenseFormating = (text: string) => { + return text.charAt(0).toUpperCase() + text.slice(1); +}; From b82babb481b7749ef03955639887d22ea40661a8 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Thu, 14 Nov 2024 14:35:23 -0500 Subject: [PATCH 03/20] fix cart count --- src/components/GlobalProvider.tsx | 4 ++ src/icons/{ => darkMode}/Logo-dark.png | Bin src/modules/CartPage/CartPage.tsx | 55 +++++++++---------------- src/modules/PhonePage/PhonePage.tsx | 7 +++- src/styles/theme.scss | 2 +- 5 files changed, 29 insertions(+), 39 deletions(-) rename src/icons/{ => darkMode}/Logo-dark.png (100%) diff --git a/src/components/GlobalProvider.tsx b/src/components/GlobalProvider.tsx index d47335c96b..cf6f833066 100644 --- a/src/components/GlobalProvider.tsx +++ b/src/components/GlobalProvider.tsx @@ -71,9 +71,13 @@ function reducer(state: State, action: Action): State { }; case 'setInFavotites': + accessLocalStorage.set(action.payload, LocalAccessKeys.favorites); + return { ...state, inFavorites: action.payload }; case 'setInCart': + accessLocalStorage.set(action.payload, LocalAccessKeys.cart); + return { ...state, inCart: action.payload }; default: diff --git a/src/icons/Logo-dark.png b/src/icons/darkMode/Logo-dark.png similarity index 100% rename from src/icons/Logo-dark.png rename to src/icons/darkMode/Logo-dark.png diff --git a/src/modules/CartPage/CartPage.tsx b/src/modules/CartPage/CartPage.tsx index ae7e27c198..aceb8c2f66 100644 --- a/src/modules/CartPage/CartPage.tsx +++ b/src/modules/CartPage/CartPage.tsx @@ -1,19 +1,18 @@ import style from './CartPage.module.scss'; -import { accessLocalStorage } from '../../utils/accessLocalStorage'; -import { LocalAccessKeys } from '../../utils/LocalAccessKeys'; import { CartItem } from './CartItem'; import { Product } from '../../types/Product'; import { v4 as uuidv4 } from 'uuid'; -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { getProducts } from '../../utils/getProducts'; import classNames from 'classnames'; import { useNavigate } from 'react-router-dom'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; export const CartPage = () => { const navigate = useNavigate(); - const [products, setProducts] = useState( - accessLocalStorage.get(LocalAccessKeys.cart), - ); + const { inCart: products } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + const [uniqueProducts, setUniqueProducts] = useState([]); const [checkOutActive, setCheckoutActive] = useState(false); @@ -31,56 +30,40 @@ export const CartPage = () => { setUniqueProducts(() => [...uniqueArr].sort((a, b) => b.price - a.price)); }, [products]); - function countProducts(itemId: string) { - const data = accessLocalStorage.get(LocalAccessKeys.cart); - - return data.filter((prod: Product) => itemId === prod.itemId).length; - } + const countProducts = (itemId: string) => + products.filter((prod: Product) => itemId === prod.itemId).length; const handleCountIncrease = (id: string) => { - setProducts(prev => { - const newProd = getProducts.getProductById(products, id); - - if (newProd) { - accessLocalStorage.append(newProd, LocalAccessKeys.cart); - - return [...prev, newProd]; - } + const newProd = getProducts.getProductById(products, id); - return prev; - }); + if (newProd) { + dispatch({ type: 'setInCart', payload: [...products, newProd] }); + } else { + dispatch({ type: 'setInCart', payload: [...products] }); + } }; const handleCountDecrease = (id: string) => { if (countProducts(id) - 1 > 0) { - setProducts(prev => { - const delIndex = prev.findIndex(prod => prod.itemId === id); - const newArr = [...prev]; - - newArr.splice(delIndex, 1); - accessLocalStorage.set(newArr, LocalAccessKeys.cart); + const delIndex = products.findIndex(prod => prod.itemId === id); + const newArr = [...products]; - return newArr; - }); + newArr.splice(delIndex, 1); + dispatch({ type: 'setInCart', payload: newArr }); } }; const handleClearItem = (id: string) => { const clearedProds = products.filter(prod => prod.itemId !== id); - setProducts(() => clearedProds); - - accessLocalStorage.set(clearedProds, LocalAccessKeys.cart); + dispatch({ type: 'setInCart', payload: clearedProds }); }; const getTotalProce = () => { return products.reduce((acc, cur) => acc + cur.price, 0); }; - const clearCart = () => { - setProducts([]); - accessLocalStorage.clearKey(LocalAccessKeys.cart); - }; + const clearCart = () => dispatch({ type: 'setInCart', payload: [] }); return (
diff --git a/src/modules/PhonePage/PhonePage.tsx b/src/modules/PhonePage/PhonePage.tsx index ed13fe5b4a..d9978cba62 100644 --- a/src/modules/PhonePage/PhonePage.tsx +++ b/src/modules/PhonePage/PhonePage.tsx @@ -1,5 +1,5 @@ import style from './PhonePage.module.scss'; -import { useContext } from 'react'; +import { useContext, useMemo } from 'react'; import { StateContext } from '../../components/GlobalProvider'; import { Catalog } from '../../components/Catalog/Catalog'; import { getProducts } from '../../utils/getProducts'; @@ -7,7 +7,10 @@ import { MenuItems } from '../../types/MenuItems'; export const PhonePage = () => { const { products } = useContext(StateContext); - const phones = getProducts.getProductByCategory(products, MenuItems.phones); + const phones = useMemo( + () => getProducts.getProductByCategory(products, MenuItems.phones), + [products], + ); return (
diff --git a/src/styles/theme.scss b/src/styles/theme.scss index f3845d81dc..c9387bf3ae 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -14,7 +14,7 @@ --c-buttons-text: var(--c-white); --c-buttons-text-selected: var(--c-green); --c-buttons-nav: var(--c-background); - --fls-logo: url('src/icons/Logo.png'); + --fls-logo: url('./src/icons/Logo.png'); --fls-right: url('src/icons/arrowRight.svg'); --fls-right-disabled: url('src/icons/arrowRight_disabled.svg'); --fls-left: url('src/icons/arrowLeft.svg'); From c2cb28cd5547ef862580ca44b66b8d4e5df9a72e Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Sun, 17 Nov 2024 13:28:44 -0500 Subject: [PATCH 04/20] img fix1 --- src/styles/theme.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/styles/theme.scss b/src/styles/theme.scss index c9387bf3ae..fbb69bb017 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -14,7 +14,7 @@ --c-buttons-text: var(--c-white); --c-buttons-text-selected: var(--c-green); --c-buttons-nav: var(--c-background); - --fls-logo: url('./src/icons/Logo.png'); + --fls-logo: url('public\img\icons\xLogo.png'); --fls-right: url('src/icons/arrowRight.svg'); --fls-right-disabled: url('src/icons/arrowRight_disabled.svg'); --fls-left: url('src/icons/arrowLeft.svg'); From 1038a54448fa659c36747f453207075142b18fa5 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 11:06:19 -0500 Subject: [PATCH 05/20] fixed in Pages --- .../PicturesSlider/PicturesSlider.tsx | 2 +- src/icons/Close.svg | 42 ---------- src/icons/Logo.png | Bin 2587 -> 0 bytes src/icons/Menu.svg | 50 ------------ src/icons/arrowDown_disabled.svg | 3 - src/icons/arrowLeft.svg | 42 ---------- src/icons/arrowLeft_disabled.svg | 42 ---------- src/icons/arrowRight.svg | 42 ---------- src/icons/arrowRight_disabled.svg | 42 ---------- src/icons/arrowUp.svg | 42 ---------- src/icons/arrowUp_disabled.svg | 3 - src/icons/cart.svg | 56 -------------- src/icons/darkMode/Logo-dark.png | Bin 2519 -> 0 bytes src/icons/darkMode/arrowDown_disabled.svg | 42 ---------- src/icons/darkMode/arrowLeft.svg | 42 ---------- src/icons/darkMode/arrowLeft_disabled.svg | 42 ---------- src/icons/darkMode/arrowRight.svg | 42 ---------- src/icons/darkMode/arrowRight_disabled.svg | 42 ---------- src/icons/darkMode/arrowUp.svg | 42 ---------- src/icons/darkMode/arrowUp_disabled.svg | 42 ---------- src/icons/darkMode/cart.svg | 56 -------------- src/icons/darkMode/close.svg | 42 ---------- src/icons/darkMode/dark_mode.svg | 46 ----------- src/icons/darkMode/favorite.svg | 42 ---------- src/icons/darkMode/home.svg | 49 ------------ src/icons/darkMode/menu.svg | 50 ------------ src/icons/darkMode/minus.svg | 42 ---------- src/icons/darkMode/minus_disabled.svg | 42 ---------- src/icons/darkMode/mouse-cursor.svg | 16 ---- src/icons/darkMode/plus.svg | 42 ---------- src/icons/dark_mode.svg | 46 ----------- src/icons/favorite.svg | 42 ---------- src/icons/favorite_selected.svg | 3 - src/icons/home.svg | 49 ------------ src/icons/minus.svg | 42 ---------- src/icons/minus_disabled.svg | 42 ---------- src/icons/mouse-cursor.svg | 16 ---- src/icons/plus.svg | 42 ---------- src/styles/main.scss | 6 +- src/styles/theme.scss | 72 +++++++++--------- 40 files changed, 40 insertions(+), 1407 deletions(-) delete mode 100644 src/icons/Close.svg delete mode 100644 src/icons/Logo.png delete mode 100644 src/icons/Menu.svg delete mode 100644 src/icons/arrowDown_disabled.svg delete mode 100644 src/icons/arrowLeft.svg delete mode 100644 src/icons/arrowLeft_disabled.svg delete mode 100644 src/icons/arrowRight.svg delete mode 100644 src/icons/arrowRight_disabled.svg delete mode 100644 src/icons/arrowUp.svg delete mode 100644 src/icons/arrowUp_disabled.svg delete mode 100644 src/icons/cart.svg delete mode 100644 src/icons/darkMode/Logo-dark.png delete mode 100644 src/icons/darkMode/arrowDown_disabled.svg delete mode 100644 src/icons/darkMode/arrowLeft.svg delete mode 100644 src/icons/darkMode/arrowLeft_disabled.svg delete mode 100644 src/icons/darkMode/arrowRight.svg delete mode 100644 src/icons/darkMode/arrowRight_disabled.svg delete mode 100644 src/icons/darkMode/arrowUp.svg delete mode 100644 src/icons/darkMode/arrowUp_disabled.svg delete mode 100644 src/icons/darkMode/cart.svg delete mode 100644 src/icons/darkMode/close.svg delete mode 100644 src/icons/darkMode/dark_mode.svg delete mode 100644 src/icons/darkMode/favorite.svg delete mode 100644 src/icons/darkMode/home.svg delete mode 100644 src/icons/darkMode/menu.svg delete mode 100644 src/icons/darkMode/minus.svg delete mode 100644 src/icons/darkMode/minus_disabled.svg delete mode 100644 src/icons/darkMode/mouse-cursor.svg delete mode 100644 src/icons/darkMode/plus.svg delete mode 100644 src/icons/dark_mode.svg delete mode 100644 src/icons/favorite.svg delete mode 100644 src/icons/favorite_selected.svg delete mode 100644 src/icons/home.svg delete mode 100644 src/icons/minus.svg delete mode 100644 src/icons/minus_disabled.svg delete mode 100644 src/icons/mouse-cursor.svg delete mode 100644 src/icons/plus.svg diff --git a/src/components/PicturesSlider/PicturesSlider.tsx b/src/components/PicturesSlider/PicturesSlider.tsx index 7073187f1a..01e2e282ed 100644 --- a/src/components/PicturesSlider/PicturesSlider.tsx +++ b/src/components/PicturesSlider/PicturesSlider.tsx @@ -40,7 +40,7 @@ export const PicturesSlider = () => { > banner-accessorie diff --git a/src/icons/Close.svg b/src/icons/Close.svg deleted file mode 100644 index f84188b1e4..0000000000 --- a/src/icons/Close.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/Logo.png b/src/icons/Logo.png deleted file mode 100644 index 1cbd99300e9f6dcc81845a9d77b9cc221b0b2a72..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2587 zcmV+$3gq>PP)QLm=if(U3P|-kv7P)xclP`s+9-96oT31SAt9H1gY>SjgPTyP3P{ZN!N0I{xNj_n* z+}YVTZ{ECleM*4A?~Z@@7Fi^r|A@}XSz@8})W_whIseT0Q5mm#ILg%PK3 zO%rVTjrY~Cek!d_=r)GL?aYf`f8nJw?+hf8u<6~KdFG1i!ZCpni!m23kFMci>!RKP z+q3e0tp=}BTfGi%$~O9~e={;-T}!1#3-mq`VJ=;qvqCCm-BcG%(7GmRb-Fl*w6(1Q&J#}*YYuN2=IAR!~ji_n5?d*kyJ!iIju2f)FeUE-i zXf?RvIutgRFJ($mt8n_oGygg_eP;cE`^x*{&p$n9^77vcA8dlb(4Pzqd=HKRG)?B7 z5R?^*K^D#14D<=ZfGOoM9~P+yE$yPIY>J#?TPuS(T%>%H_*)lF#Yf`ThOD9B*&Hw- zo`_B0BEB&?{t^`K7U15U`#2rutSmX!^i{WOWb$#NMaXE6GdLmAK%1NtMG4S96#K^y zIw_0BZExmH+H1>Su?HfF9~SOz|FTpluhw??y-0K*K9r37Z0zaOM0_|7pFj5nj9M6! z?maMu;33VSZxi%!Ko_!Kuk$p8T=a}iY@s1A)(uESnLMA+X=-)yOMvZ2q(WF^@ zd1zc9kL|66P0@p%K#)VlK2xH9zTLUBBCzTXL<|#-8lfDZ?VLl~=#Y(sK7ldzE!qhn%mzje^LU$+?;FttWTcj( z{Y=9Fkc8*GoyY$Rgq*MjUt53sxY~3%paKz}i z%&2i!P$)9w>Iu?Xu>%$w-?Ld^bofE`p@Br(NV}0a^Zv*ME2Gf zD0)D_Uf$aQ%)$9OdOx)LvtiK(!j_Aqf-&ym?Yev}ug88|81%n-;>?LJ#|8%g=+eNe zRoE^(`r8*@`}4bS#4x(OM4?f#;KDwDHg%e^G*B`;1uhC~<{*iVisydt$BOsfgDZuN zO}}2*##O5+Z#mSeyJ#jbjN2&u6^;QI*-GWv*PWF}e=YTWSX&rz?B*!_^0Yfb`lz%z zBJA7ue|vs%Fk#+EBx7d{lc8C`+C`-bTZJ-geo%BvcPr_af3bE4ju?H#X2%(STDUOw zxn%K`Xv{pnyIUQs?biSC;eX4wznSq|_za&Ov}iJIU8)(2SeCXh)r(z(MG(El*)NY6m%rYN9)_L)pR!io4`iexL3JB3f4Y3i0VsNs&b=vQ zN}+{k53!Hj!-9{~AtC@Q&{Ms^QQImNZtMMt8JSIngOk7(^hxx|ql-MI)iIGT4n07Q z7=jWjTZ2APEEWQ}iP(RsLn(j`8!{9;7=rov&V}}X6&njiZdhebI8H}z9~e-}p8bN^ zQ~?Z&mHXh)As!EV=&&E45O0PHIN*oE(F`dT6Lnr?6?q=FB86^r+;kQvtxSx&dfH1{ zk^~bniiaycp*HB6j9PS+?x?*fQS5J;oLY@L(gbwik?Ik?&LfpmDPyJIswQLmEN81}!X}}1v$IGht2{1ZBC||~2K0colIR4J(|N?-Gb#_L<(8A)X!aO!uD7 zOaiqsgTfWeF-I;a3Q#gk*&^BOmbhFbO^ZEfpViG>Kd0cGK+YCzy`dYTLsBp9@#db94x6jKFh?cg>*xy)_oQI~!a zAhU%#OCBC8e3H|HOf1IHgjtR$m>QO4(1c71)8;gKP*ldVreHW| zB+UR(?NaR={i6zqg}^NwczMlK1f@+DlBDm)WFXB+&HPAhbR~~C22J8dgU65L1SuLQ z$Pa08kk1y-4(s9g_5~4U+Jf(lpiS4J2salRJ=v6j>Ym;EG&t9r4b2!EiPq%nCZDqa*crB6BBd8m=JNKC!fiw;jP%g+0Z|}c-`z~2#=2PtxMmk xJj!=)aCp5sP-cnz - - - - - - - diff --git a/src/icons/arrowDown_disabled.svg b/src/icons/arrowDown_disabled.svg deleted file mode 100644 index e51b6915b6..0000000000 --- a/src/icons/arrowDown_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/icons/arrowLeft.svg b/src/icons/arrowLeft.svg deleted file mode 100644 index 7c3cd2b5ae..0000000000 --- a/src/icons/arrowLeft.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/arrowLeft_disabled.svg b/src/icons/arrowLeft_disabled.svg deleted file mode 100644 index e215e05745..0000000000 --- a/src/icons/arrowLeft_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/arrowRight.svg b/src/icons/arrowRight.svg deleted file mode 100644 index 6c9233b116..0000000000 --- a/src/icons/arrowRight.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/arrowRight_disabled.svg b/src/icons/arrowRight_disabled.svg deleted file mode 100644 index 0ff66e8a9e..0000000000 --- a/src/icons/arrowRight_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/arrowUp.svg b/src/icons/arrowUp.svg deleted file mode 100644 index 63c0f2be22..0000000000 --- a/src/icons/arrowUp.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/arrowUp_disabled.svg b/src/icons/arrowUp_disabled.svg deleted file mode 100644 index e061a0757f..0000000000 --- a/src/icons/arrowUp_disabled.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/icons/cart.svg b/src/icons/cart.svg deleted file mode 100644 index 3a0896b4b9..0000000000 --- a/src/icons/cart.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - diff --git a/src/icons/darkMode/Logo-dark.png b/src/icons/darkMode/Logo-dark.png deleted file mode 100644 index 25115853d24130d9ad00d616e3831b264defe222..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 2519 zcmV;|2`Ki7P)lSJ{aw$w@4c#9uU_>}0x1UOz@zKE zd+&MYo_o%BeqIB73~BYvm-6ekzwz&fzxmolm@sLbj(rKhH5g-!bXtE$9^BltW;y41 z`t3OStz3?^g8B3;lZjmqGo&@H>r8{f493;EuB-KAlHHf*$ULG*QJ|hm#r_^O#yPO} zZyfzjI+uL$NQ(Vxb$NPvW})7K2@z{|dTaPMPdBQ9@<=xm-F~ljmnM zaTd(&cFha6>sA=B!suIg#kJ#0bO3bQ>g?W$oY$WZ6gBkUTd&R}4db5K?ZEMK zpMuV#&QfmfcV(CmDe{f&xYcHp8*oT!Hh1#2!)s%e!dZ!_;xL|>&Rn{?y4Be(H#bbM zdnP1|weSV-P5Q#dTsivQDyEyl;p(z~;%l!)s%D zEKhTXt})gZ(5Jt7d&%ipKm6d{e;|G&5kGMz`z@FdX(Lb2T5zM)FdmL0vc7@k@kTb2 zSO~_*#&I-En41l>W169>8aB#$L4p0w|XHp{2Tpzc-XZ0$Gt3yEm zE%<7Hmqbi+esJ{E@hgg|qL%VD`zBK#d>wh}*;XEr1 zDNtcb(dBN9Tp0vrv3;YP16EG)k$@dwewfI$!_N{4DB~Z&>%q-e7hSIA9)7U?UFN{Q zl1bfynD(=yr_$$N_{Uz-m{amx89ZK12vF~1#NuWPYV zCKX?hFWG&2Th?9l2vlTz{;xlOIZMx0-@2GTmQxB~?x40&*Jc-N%e5Yx+iiHxG!jHe zcnl1enX0Vf?Y;`k<&q6lPLRSFl&)Y-Shg(vzNXn15Kk)t5|DIVFTx?ove=*Po&^{{ zjC1GD{NbbDpSY`nT}&htgC(aBcwIbn8#){9xybNJv{Z2ZZzjgxFR7kEwh0^gb!VtgH$Si<@VdH_06Af3{lyWnJ*frkCoD=Pa3gf zCqPlX^Y3kL!FJ2~1x$dHaRb>@>>3JPg98vL+;!X;R8l=+xaCzAv8PTm{`rqbZ#;Rp zagSRT5G1nzm=SC2UJo`NY;?{qy!#iJ0BM-)E{q!_%i@|>3xk~y${9CbSS`Rzv)i2p zSI>L3uF?W0R+BN!CzDI&RZRugMflq`;iE^L z*8d)~z6TQ`1|K8BJd^$H$6t6o7K6`&3%uPn-`{9jH!oKj6Kf`XLeEHj3#9ZzQOrfo zcmcE1JU*fvu#O#?%DC22(195+V_3??M_imzn26;qOu(xcnQKwO#gl0VYjbrGOThwo z8G(=q>*un(hSDR9#YGsmNv6~ZgQ>fOxSof7h&wh`LMZ`RqCLmmBEoIKW5yedLp2tQ zS4jreR|Iua#^rq+gFtq)z?a)y0Z}6%jCmAUVVtm>Hx#CoG8ygG(3+rCO zK|fMNZ7wR2=(&v5GU?cy+=|>MM3_Bu69#?FKt8`8*)RcFyygu^N6GzZ*Ax~T29nDnV*~!dTY!#!(%cZ?EZ{Y zJ5eO>&G)1sYIb&tQFuj2L#MSLBYh(%iw92uE+|;*-$ViP=({BPGZZ3%siPZ>Q z^pda}M-lb?P{1OPCHm_?ywEK8Q(EEPpr22U%_&g~11{)K#>=vcIN0mLFP* z3(iom11nL7r=*}EEWA$JomRL)7+a(GXQ+|rWQWT z59CD*^|4QWv1tb`KAs3y`VfNr>Ah4EN;S9)_&)!ftGMkrR1y7Zo>9I+Jof1+)qVse=EG72HI@OGTK0-<*p<(2HsMNS;VZ zH!6MTS=2Z0eM#YUObEqK`dW#Hjcqxlh4K_2X!GG=i5YatB3uCZqW3XySvJ8>eSmtw#3ImQFI>6%w h!Gr_uwcJ#^_J0r5G`bovWR3s;002ovPDHLkV1jOXw}Sux diff --git a/src/icons/darkMode/arrowDown_disabled.svg b/src/icons/darkMode/arrowDown_disabled.svg deleted file mode 100644 index 7a1db9ca66..0000000000 --- a/src/icons/darkMode/arrowDown_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/arrowLeft.svg b/src/icons/darkMode/arrowLeft.svg deleted file mode 100644 index ead0524499..0000000000 --- a/src/icons/darkMode/arrowLeft.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/arrowLeft_disabled.svg b/src/icons/darkMode/arrowLeft_disabled.svg deleted file mode 100644 index b7448343ff..0000000000 --- a/src/icons/darkMode/arrowLeft_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/arrowRight.svg b/src/icons/darkMode/arrowRight.svg deleted file mode 100644 index 3591ba9167..0000000000 --- a/src/icons/darkMode/arrowRight.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/arrowRight_disabled.svg b/src/icons/darkMode/arrowRight_disabled.svg deleted file mode 100644 index 364d78da3f..0000000000 --- a/src/icons/darkMode/arrowRight_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/arrowUp.svg b/src/icons/darkMode/arrowUp.svg deleted file mode 100644 index 8ac6b02257..0000000000 --- a/src/icons/darkMode/arrowUp.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/arrowUp_disabled.svg b/src/icons/darkMode/arrowUp_disabled.svg deleted file mode 100644 index a4852177d2..0000000000 --- a/src/icons/darkMode/arrowUp_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/cart.svg b/src/icons/darkMode/cart.svg deleted file mode 100644 index dcbf4c8773..0000000000 --- a/src/icons/darkMode/cart.svg +++ /dev/null @@ -1,56 +0,0 @@ - - - - - - - - diff --git a/src/icons/darkMode/close.svg b/src/icons/darkMode/close.svg deleted file mode 100644 index 83a41be318..0000000000 --- a/src/icons/darkMode/close.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/dark_mode.svg b/src/icons/darkMode/dark_mode.svg deleted file mode 100644 index 5dbc449829..0000000000 --- a/src/icons/darkMode/dark_mode.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - diff --git a/src/icons/darkMode/favorite.svg b/src/icons/darkMode/favorite.svg deleted file mode 100644 index 6d5166a148..0000000000 --- a/src/icons/darkMode/favorite.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/home.svg b/src/icons/darkMode/home.svg deleted file mode 100644 index d34f74d836..0000000000 --- a/src/icons/darkMode/home.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - diff --git a/src/icons/darkMode/menu.svg b/src/icons/darkMode/menu.svg deleted file mode 100644 index 4f16e51e7c..0000000000 --- a/src/icons/darkMode/menu.svg +++ /dev/null @@ -1,50 +0,0 @@ - - - - - - - - diff --git a/src/icons/darkMode/minus.svg b/src/icons/darkMode/minus.svg deleted file mode 100644 index 0459cbd1bc..0000000000 --- a/src/icons/darkMode/minus.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/minus_disabled.svg b/src/icons/darkMode/minus_disabled.svg deleted file mode 100644 index 508edf95cc..0000000000 --- a/src/icons/darkMode/minus_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/darkMode/mouse-cursor.svg b/src/icons/darkMode/mouse-cursor.svg deleted file mode 100644 index b7f0395be8..0000000000 --- a/src/icons/darkMode/mouse-cursor.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/icons/darkMode/plus.svg b/src/icons/darkMode/plus.svg deleted file mode 100644 index d9a6f26932..0000000000 --- a/src/icons/darkMode/plus.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/dark_mode.svg b/src/icons/dark_mode.svg deleted file mode 100644 index f8fc136f98..0000000000 --- a/src/icons/dark_mode.svg +++ /dev/null @@ -1,46 +0,0 @@ - - - - - - - - - diff --git a/src/icons/favorite.svg b/src/icons/favorite.svg deleted file mode 100644 index e76de364e8..0000000000 --- a/src/icons/favorite.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/favorite_selected.svg b/src/icons/favorite_selected.svg deleted file mode 100644 index 7138d7522b..0000000000 --- a/src/icons/favorite_selected.svg +++ /dev/null @@ -1,3 +0,0 @@ - - - diff --git a/src/icons/home.svg b/src/icons/home.svg deleted file mode 100644 index b6114516b4..0000000000 --- a/src/icons/home.svg +++ /dev/null @@ -1,49 +0,0 @@ - - - - - - - diff --git a/src/icons/minus.svg b/src/icons/minus.svg deleted file mode 100644 index 066d275efc..0000000000 --- a/src/icons/minus.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/minus_disabled.svg b/src/icons/minus_disabled.svg deleted file mode 100644 index 26c5af1080..0000000000 --- a/src/icons/minus_disabled.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/icons/mouse-cursor.svg b/src/icons/mouse-cursor.svg deleted file mode 100644 index b7f0395be8..0000000000 --- a/src/icons/mouse-cursor.svg +++ /dev/null @@ -1,16 +0,0 @@ - - - - - - - - - - - - - - - - diff --git a/src/icons/plus.svg b/src/icons/plus.svg deleted file mode 100644 index 7b2fae4a60..0000000000 --- a/src/icons/plus.svg +++ /dev/null @@ -1,42 +0,0 @@ - - - - - - diff --git a/src/styles/main.scss b/src/styles/main.scss index ccb926bca8..0fa223222f 100644 --- a/src/styles/main.scss +++ b/src/styles/main.scss @@ -7,19 +7,19 @@ @font-face { font-family: Mont; - src: url('../../public/fonts/Mont-Regular.otf'); + src: url('../fonts/Mont-Regular.otf'); font-weight: 500; } @font-face { font-family: Mont; - src: url('../../public/fonts/Mont-SemiBold.otf'); + src: url('../fonts/Mont-SemiBold.otf'); font-weight: 600; } @font-face { font-family: Mont; - src: url('../../public/fonts/Mont-Bold.otf'); + src: url('../fonts/Mont-Bold.otf'); font-weight: bold; } diff --git a/src/styles/theme.scss b/src/styles/theme.scss index fbb69bb017..639319accb 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -14,24 +14,24 @@ --c-buttons-text: var(--c-white); --c-buttons-text-selected: var(--c-green); --c-buttons-nav: var(--c-background); - --fls-logo: url('public\img\icons\xLogo.png'); - --fls-right: url('src/icons/arrowRight.svg'); - --fls-right-disabled: url('src/icons/arrowRight_disabled.svg'); - --fls-left: url('src/icons/arrowLeft.svg'); - --fls-left-disabled: url('src/icons/arrowLeft_disabled.svg'); - --fls-up: url('src/icons/arrowUp.svg'); - --fls-up-disabled: url('src/icons/arrowUp_disabled.svg'); - --fls-down-disabled: url('src/icons/arrowDown_disabled.svg'); - --fls-home: url('src/icons/home.svg'); - --fls-favorite: url('src/icons/favorite.svg'); - --fls-favorite-selected: url('src/icons/favorite_selected.svg'); - --fls-cart: url('src/icons/cart.svg'); - --fls-dark-mode: url('src/icons/dark_mode.svg'); - --fls-menu: url('src/icons/menu.svg'); - --fls-close: url('src/icons/close.svg'); - --fls-plus: url('src/icons/plus.svg'); - --fls-minus: url('src/icons/minus.svg'); - --fls-minus-disabled: url('src/icons/minus_disabled.svg'); + --fls-logo: url('../img/icons/Logo.png'); + --fls-right: url('../img/icons/arrowRight.svg'); + --fls-right-disabled: url('../img/icons/arrowRight_disabled.svg'); + --fls-left: url('../img/icons/arrowLeft.svg'); + --fls-left-disabled: url('../img/icons/arrowLeft_disabled.svg'); + --fls-up: url('../img/icons/arrowUp.svg'); + --fls-up-disabled: url('../img/icons/arrowUp_disabled.svg'); + --fls-down-disabled: url('../img/icons/arrowDown_disabled.svg'); + --fls-home: url('../img/icons/home.svg'); + --fls-favorite: url('../img/icons/favorite.svg'); + --fls-favorite-selected: url('../img/icons/favorite_selected.svg'); + --fls-cart: url('../img/icons/cart.svg'); + --fls-dark-mode: url('../img/icons/dark_mode.svg'); + --fls-menu: url('../img/icons/menu.svg'); + --fls-close: url('../img/icons/close.svg'); + --fls-plus: url('../img/icons/plus.svg'); + --fls-minus: url('../img/icons/minus.svg'); + --fls-minus-disabled: url('../img/icons/minus_disabled.svg'); &_dark { --c-accent: #905BFF; @@ -49,23 +49,23 @@ --c-buttons-text: var(--c-white); --c-buttons-text-selected: #F1F2F9; --c-buttons-nav: #323542; - --fls-logo: url('src/icons/Logo-dark.png'); - --fls-right: url('src/icons/darkMode/arrowRight.svg'); - --fls-right-disabled: url('src/icons/arrowRight_disabled.svg'); - --fls-left: url('src/icons/darkMode/arrowLeft.svg'); - --fls-left-disabled: url('src/icons/arrowLeft_disabled.svg'); - --fls-up: url('src/icons/darkMode/arrowUp.svg'); - --fls-up-disabled: url('src/icons/arrowUp_disabled.svg'); - --fls-down-disabled: url('src/icons/arrowDown_disabled.svg'); - --fls-home: url('src/icons/darkMode/home.svg'); - --fls-favorite: url('src/icons/darkMode/favorite.svg'); - --fls-favorite-selected: url('src/icons/favorite_selected.svg'); - --fls-cart: url('src/icons/darkMode/cart.svg'); - --fls-dark-mode: url('src/icons/darkMode/dark_mode.svg'); - --fls-menu: url('src/icons/darkMode/menu.svg'); - --fls-close: url('src/icons/darkMode/close.svg'); - --fls-plus: url('src/icons/darkMode/plus.svg'); - --fls-minus: url('src/icons/darkMode/minus.svg'); - --fls-minus-disabled: url('src/icons/minus_disabled.svg'); + --fls-logo: url('../../public/img/icons/Logo-dark.png'); + --fls-right: url('../img/icons/darkMode/arrowRight.svg'); + --fls-right-disabled: url('../img/icons/arrowRight_disabled.svg'); + --fls-left: url('../img/icons/darkMode/arrowLeft.svg'); + --fls-left-disabled: url('../img/icons/arrowLeft_disabled.svg'); + --fls-up: url('../img/icons/darkMode/arrowUp.svg'); + --fls-up-disabled: url('../img/icons/arrowUp_disabled.svg'); + --fls-down-disabled: url('../img/icons/arrowDown_disabled.svg'); + --fls-home: url('../img/icons/darkMode/home.svg'); + --fls-favorite: url('../img/icons/darkMode/favorite.svg'); + --fls-favorite-selected: url('../img/icons/favorite_selected.svg'); + --fls-cart: url('../img/icons/darkMode/cart.svg'); + --fls-dark-mode: url('../img/icons/darkMode/dark_mode.svg'); + --fls-menu: url('../img/icons/darkMode/menu.svg'); + --fls-close: url('../img/icons/darkMode/close.svg'); + --fls-plus: url('../img/icons/darkMode/plus.svg'); + --fls-minus: url('../img/icons/darkMode/minus.svg'); + --fls-minus-disabled: url('../img/icons/minus_disabled.svg'); } } From cfdd19c2e980c514763d1411a5005ad134e379ff Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 11:32:01 -0500 Subject: [PATCH 06/20] added page and product not found --- .../PageNotFoundPage.module.scss | 16 ++++++++++++++++ .../PageNotFoundPage/PageNotFoundPage.tsx | 10 ++++++++++ src/modules/PageNotFoundPage/index.ts | 1 + .../ProductDetailsPage.module.scss | 17 +++++++++++++++++ .../ProductDetailsPage/ProductDetailsPage.tsx | 5 ++++- src/modules/Root/Root.tsx | 6 +++++- 6 files changed, 53 insertions(+), 2 deletions(-) create mode 100644 src/modules/PageNotFoundPage/PageNotFoundPage.module.scss create mode 100644 src/modules/PageNotFoundPage/PageNotFoundPage.tsx create mode 100644 src/modules/PageNotFoundPage/index.ts diff --git a/src/modules/PageNotFoundPage/PageNotFoundPage.module.scss b/src/modules/PageNotFoundPage/PageNotFoundPage.module.scss new file mode 100644 index 0000000000..efe2fa9b5f --- /dev/null +++ b/src/modules/PageNotFoundPage/PageNotFoundPage.module.scss @@ -0,0 +1,16 @@ +@import '../../styles/main'; + +.page_not_found_container{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 20px; + + & .img{ + flex-grow: 1; + width: 30vw; + aspect-ratio: 1/1; + background: url(../../../public/img/page-not-found.png) no-repeat center/contain;; + } +} diff --git a/src/modules/PageNotFoundPage/PageNotFoundPage.tsx b/src/modules/PageNotFoundPage/PageNotFoundPage.tsx new file mode 100644 index 0000000000..30e318025f --- /dev/null +++ b/src/modules/PageNotFoundPage/PageNotFoundPage.tsx @@ -0,0 +1,10 @@ +import style from './PageNotFoundPage.module.scss'; + +export const PageNotFoundPage = () => { + return ( +
+

Sorry Page Not Found

+
+
+ ); +}; diff --git a/src/modules/PageNotFoundPage/index.ts b/src/modules/PageNotFoundPage/index.ts new file mode 100644 index 0000000000..dcae515118 --- /dev/null +++ b/src/modules/PageNotFoundPage/index.ts @@ -0,0 +1 @@ +export * from './PageNotFoundPage'; diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss b/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss index 09fd17725a..fc3e2e0035 100644 --- a/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss @@ -292,4 +292,21 @@ & .container_seperator { border: 1px solid var(--c-elements); } + + .product_not_found_container{ + width: 100%; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 20px; + + & .img{ + flex-grow: 1; + width: 30vw; + aspect-ratio: 1/1; + background: url(../../../public/img/product-not-found.png) no-repeat center/contain;; + } +} + } diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx index 32b12b02e8..a8a0faee62 100644 --- a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx @@ -313,7 +313,10 @@ export const ProductDetailsPage: React.FC = ({ category }) => {
) : ( -

Problem with phones

+
+

Sorry Product Not Found

+
+
)} )} diff --git a/src/modules/Root/Root.tsx b/src/modules/Root/Root.tsx index 8988f1d67a..3c3385b2d9 100644 --- a/src/modules/Root/Root.tsx +++ b/src/modules/Root/Root.tsx @@ -14,6 +14,7 @@ import { TabletPage } from '../TabletPage'; import { AccessoriesPage } from '../AccessoriesPage'; import { ProductDetailsPage } from '../ProductDetailsPage'; import { MenuItems } from '../../types/MenuItems'; +import { PageNotFoundPage } from '../PageNotFoundPage'; export const Root = () => { return ( @@ -81,7 +82,10 @@ export const Root = () => { element={} /> - {/* } /> */} + } + /> From d584b78f77bbd15b2542ce6497842e66d499b74c Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 11:47:29 -0500 Subject: [PATCH 07/20] added page and product not found in catalog --- src/components/Catalog/Catalog.module.scss | 15 +++++++++++++++ src/components/Catalog/Catalog.tsx | 9 +++++++-- .../ProductDetailsPage.module.scss | 2 +- src/styles/theme.scss | 2 +- 4 files changed, 24 insertions(+), 4 deletions(-) diff --git a/src/components/Catalog/Catalog.module.scss b/src/components/Catalog/Catalog.module.scss index c05d2238a7..683a9f32ad 100644 --- a/src/components/Catalog/Catalog.module.scss +++ b/src/components/Catalog/Catalog.module.scss @@ -52,4 +52,19 @@ } } } + + & .products_not_found_container{ + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 20px; + + & .img{ + flex-grow: 1; + width: 30vw; + aspect-ratio: 1/1; + background: url(../../../public/img/product-not-found.png) no-repeat center/contain;; + } +} } diff --git a/src/components/Catalog/Catalog.tsx b/src/components/Catalog/Catalog.tsx index bc83554be5..76ec9a4423 100644 --- a/src/components/Catalog/Catalog.tsx +++ b/src/components/Catalog/Catalog.tsx @@ -29,7 +29,7 @@ export const Catalog: React.FC = ({ const [curPage, setCurPage] = useState(0); useEffect(() => { - const category = products[0]?.category; + const category = products ? products[0].category : undefined; if (category) { setLoading(true); @@ -60,7 +60,7 @@ export const Catalog: React.FC = ({
{loading ? ( - ) : ( + ) : products ? ( <>
@@ -97,6 +97,11 @@ export const Catalog: React.FC = ({ {pages.length > 1 && }
+ ) : ( +
+

Sorry Products Not Found

+
+
)}
); diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss b/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss index fc3e2e0035..f91f2acba2 100644 --- a/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.module.scss @@ -293,7 +293,7 @@ border: 1px solid var(--c-elements); } - .product_not_found_container{ + & .product_not_found_container{ width: 100%; display: flex; flex-direction: column; diff --git a/src/styles/theme.scss b/src/styles/theme.scss index 639319accb..46c8b600da 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -49,7 +49,7 @@ --c-buttons-text: var(--c-white); --c-buttons-text-selected: #F1F2F9; --c-buttons-nav: #323542; - --fls-logo: url('../../public/img/icons/Logo-dark.png'); + --fls-logo: url('../img/icons/darkMode/Logo-dark.png'); --fls-right: url('../img/icons/darkMode/arrowRight.svg'); --fls-right-disabled: url('../img/icons/arrowRight_disabled.svg'); --fls-left: url('../img/icons/darkMode/arrowLeft.svg'); From 7c64a5e656cde1b03ea284f9ed20552eae92661e Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 11:54:44 -0500 Subject: [PATCH 08/20] fixed catalog unnessesary loading --- src/components/Catalog/Catalog.tsx | 18 +----------------- 1 file changed, 1 insertion(+), 17 deletions(-) diff --git a/src/components/Catalog/Catalog.tsx b/src/components/Catalog/Catalog.tsx index 76ec9a4423..68b1f19956 100644 --- a/src/components/Catalog/Catalog.tsx +++ b/src/components/Catalog/Catalog.tsx @@ -9,8 +9,6 @@ import { SearchParams } from '../../types/SearchParams'; import { catalogHelper } from '../../utils/catalogHelper'; import { useEffect, useState } from 'react'; import { Pagination } from './Pagination'; -import { getProductItems } from '../../utils/getProductItems'; -import { Loader } from '../Loader'; type Props = { title: string; @@ -24,21 +22,9 @@ export const Catalog: React.FC = ({ }) => { const [searchParams] = useSearchParams(); const [sortedProducts, setSortedProducts] = useState([]); - const [loading, setLoading] = useState(false); const [pages, setPages] = useState([[]]); const [curPage, setCurPage] = useState(0); - useEffect(() => { - const category = products ? products[0].category : undefined; - - if (category) { - setLoading(true); - getProductItems - .fetchByCategory(category) - .finally(() => setLoading(false)); - } - }, [products]); - useEffect(() => { setSortedProducts(() => catalogHelper.sort(searchParams.get(SearchParams.order), products), @@ -58,9 +44,7 @@ export const Catalog: React.FC = ({ return (
- {loading ? ( - - ) : products ? ( + {products ? ( <>
From 83b3aeb803f44c1f725a0e5d4eb0b3d48abb8d52 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 12:07:49 -0500 Subject: [PATCH 09/20] fixed catalog ready to deploy --- src/components/Catalog/Catalog.tsx | 51 +++++++++++++++++++++--------- 1 file changed, 36 insertions(+), 15 deletions(-) diff --git a/src/components/Catalog/Catalog.tsx b/src/components/Catalog/Catalog.tsx index 68b1f19956..87a41a468e 100644 --- a/src/components/Catalog/Catalog.tsx +++ b/src/components/Catalog/Catalog.tsx @@ -53,8 +53,10 @@ export const Catalog: React.FC = ({

{title}

- {products.length !== 1 - ? `${products.length} models` + {products.length !== 0 + ? products.length !== 1 + ? `${products.length} models` + : '1 model' : 'no models'}

@@ -63,19 +65,38 @@ export const Catalog: React.FC = ({ {sortPerPageEnable && }
-
- {pages[curPage] && - pages[curPage].map(product => { - return ( -
- -
- ); - })} -
+ {products.length ? ( +
+ {pages[curPage] && + pages[curPage].map(product => { + return ( +
+ +
+ ); + })} +
+ ) : ( +

+
+ {pages[curPage] && + pages[curPage].map(product => { + return ( +
+ +
+ ); + })} +
+ {`There are no ${title.toLocaleLowerCase()} yet.`} +

+ )}
{pages.length > 1 && } From f3ff673315be4eb612fdf36b4b617eff5a203eaf Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 19:44:51 -0500 Subject: [PATCH 10/20] added search --- src/components/Catalog/Catalog.tsx | 10 +- src/components/GlobalProvider.tsx | 6 ++ src/components/Header/Header.module.scss | 17 +++- src/components/Header/Header.tsx | 9 +- .../SearchModule/SearchModule.module.scss | 51 ++++++++++ .../Header/SearchModule/SearchModule.tsx | 93 +++++++++++++++++++ src/components/Header/SearchModule/index.ts | 1 + .../AccessoriesPage/AccessoriesPage.tsx | 29 ++++-- src/modules/FavotitePage/FavoritePage.tsx | 8 +- src/modules/HomePage/HomePage.tsx | 11 ++- src/modules/PhonePage/PhonePage.tsx | 29 ++++-- .../ProductDetailsPage/ProductDetailsPage.tsx | 10 +- src/modules/TabletPage/TabletPage.tsx | 30 +++++- src/styles/icons.scss | 2 +- src/styles/theme.scss | 2 + src/types/SearchParams.ts | 1 + src/utils/getProducts.ts | 13 +++ 17 files changed, 293 insertions(+), 29 deletions(-) create mode 100644 src/components/Header/SearchModule/SearchModule.module.scss create mode 100644 src/components/Header/SearchModule/SearchModule.tsx create mode 100644 src/components/Header/SearchModule/index.ts diff --git a/src/components/Catalog/Catalog.tsx b/src/components/Catalog/Catalog.tsx index 87a41a468e..c134012f5e 100644 --- a/src/components/Catalog/Catalog.tsx +++ b/src/components/Catalog/Catalog.tsx @@ -7,24 +7,32 @@ import { ProductCard } from '../ProductCard'; import { useSearchParams } from 'react-router-dom'; import { SearchParams } from '../../types/SearchParams'; import { catalogHelper } from '../../utils/catalogHelper'; -import { useEffect, useState } from 'react'; +import { useContext, useEffect, useState } from 'react'; import { Pagination } from './Pagination'; +import { DispatchContext } from '../GlobalProvider'; type Props = { title: string; products: Product[]; sortPerPageEnable?: boolean; }; + export const Catalog: React.FC = ({ title, products, sortPerPageEnable = true, }) => { + const dispatch = useContext(DispatchContext); const [searchParams] = useSearchParams(); const [sortedProducts, setSortedProducts] = useState([]); const [pages, setPages] = useState([[]]); const [curPage, setCurPage] = useState(0); + useEffect( + () => dispatch({ type: 'setShowSearch', payload: true }), + [dispatch], + ); + useEffect(() => { setSortedProducts(() => catalogHelper.sort(searchParams.get(SearchParams.order), products), diff --git a/src/components/GlobalProvider.tsx b/src/components/GlobalProvider.tsx index cf6f833066..f304e795fa 100644 --- a/src/components/GlobalProvider.tsx +++ b/src/components/GlobalProvider.tsx @@ -7,6 +7,7 @@ import { LocalAccessKeys } from '../utils/LocalAccessKeys'; type State = { showMenu: boolean; + showSearch: boolean; inDarkMode: boolean; products: Product[]; phones: ProductItem[]; @@ -18,6 +19,7 @@ type State = { type Action = | { type: 'setShowMenu'; payload: boolean } + | { type: 'setShowSearch'; payload: boolean } | { type: 'setInDarkMode'; payload: boolean } | { type: 'setProducts'; payload: Product[] } | { type: 'setPhones'; payload: ProductItem[] } @@ -33,6 +35,9 @@ function reducer(state: State, action: Action): State { case 'setShowMenu': return { ...state, showMenu: action.payload }; + case 'setShowSearch': + return { ...state, showSearch: action.payload }; + case 'setInDarkMode': return { ...state, inDarkMode: action.payload }; @@ -87,6 +92,7 @@ function reducer(state: State, action: Action): State { const initialState: State = { showMenu: false, + showSearch: false, inDarkMode: false, products: [], phones: [], diff --git a/src/components/Header/Header.module.scss b/src/components/Header/Header.module.scss index 2822f52e56..62f1ef38d5 100644 --- a/src/components/Header/Header.module.scss +++ b/src/components/Header/Header.module.scss @@ -1,6 +1,5 @@ @import '../../styles/main'; - .container { display: flex; align-items: center; @@ -42,11 +41,19 @@ &_icons { width: 100%; height: 100%; + display: flex; + flex-direction: row-reverse; + + &_search { + @include ontablet { + display: block; + height: 100%; + } + } &_fav_cart { display: none; - @include ontablet { display: block; height: 100%; @@ -54,8 +61,10 @@ } &_menu { - height: 100%; + @include ontablet { + display: block; + height: 100%; + } } } - } diff --git a/src/components/Header/Header.tsx b/src/components/Header/Header.tsx index 4ec907dfc2..fbb683951f 100644 --- a/src/components/Header/Header.tsx +++ b/src/components/Header/Header.tsx @@ -6,9 +6,10 @@ import { StateContext } from '../GlobalProvider'; import { useContext } from 'react'; import { MenuCloseIcons } from './MenuCloseIcons'; import { SiteLogo } from '../SiteLogo'; +import { SearchModule } from './SearchModule'; export const Header = () => { - const { showMenu } = useContext(StateContext); + const { showMenu, showSearch } = useContext(StateContext); return (
@@ -32,6 +33,12 @@ export const Header = () => {
+ + {showSearch && !showMenu && ( +
+ +
+ )}
); diff --git a/src/components/Header/SearchModule/SearchModule.module.scss b/src/components/Header/SearchModule/SearchModule.module.scss new file mode 100644 index 0000000000..658f11a2e1 --- /dev/null +++ b/src/components/Header/SearchModule/SearchModule.module.scss @@ -0,0 +1,51 @@ +@import '../../../styles/main'; + +.container { + box-sizing: border-box; + display: flex; + align-items: center; + height: 100%; + transition: border-color 0.3s; + border: 1px solid var(--c-icons); + + &:hover { + border-color: var(--c-primary); + } + + & .icon { + &_container { + @extend %icon-container; + + border: none; + + @include ontablet { + width: 48px; + height: 48px; + } + + @include ondesktop { + width: 64px; + height: 64px; + } + } + + &_search { + @include icon-bg(var(--fls-search)); + } + } + + & .input { + background-color: red; + all: unset; + padding: 8px 0; + margin: 0 8px; + border: none; + line-height: 11px; + width: 100%; + color: var(--c-primary); + + &::placeholder { + color: var(--c-secondary); + } + } +} diff --git a/src/components/Header/SearchModule/SearchModule.tsx b/src/components/Header/SearchModule/SearchModule.tsx new file mode 100644 index 0000000000..c0bbc1d570 --- /dev/null +++ b/src/components/Header/SearchModule/SearchModule.tsx @@ -0,0 +1,93 @@ +import classNames from 'classnames'; +import style from './SearchModule.module.scss'; +import { useCallback, useEffect, useMemo, useRef, useState } from 'react'; +import { useSearchParams } from 'react-router-dom'; +import { SearchParams } from '../../../types/SearchParams'; + +function debounce(callback: (query: string) => void, delay: number = 300) { + let timerId = 0; + + return (arg: string) => { + window.clearTimeout(timerId); + timerId = window.setTimeout(() => { + callback(arg); + }, delay); + }; +} + +export const SearchModule = () => { + const [showField, setShowField] = useState(false); + const [searchParams, setSearchParams] = useSearchParams(); + const [searchQuery, setSearchQuery] = useState(''); + + useEffect(() => { + setSearchQuery(() => { + const curQuery = searchParams.get(SearchParams.query); + + return curQuery ? curQuery : ''; + }); + }, [searchParams]); + + const updateSearchParams = useCallback( + (val: string) => { + const params = new URLSearchParams(searchParams); + + if (val) { + params.set(SearchParams.query, val); + } else { + params.delete(SearchParams.query); + } + + setSearchParams(() => params); + }, + [searchParams, setSearchParams], + ); + + const searchRef = useRef(null); + + const applyQuery = useMemo( + () => debounce(updateSearchParams), + [updateSearchParams], + ); + + const handleQueryChange = (event: React.ChangeEvent) => { + const val = event.target.value; + + setSearchQuery(() => val); + applyQuery(val); + }; + + const handleClicks = (eve: MouseEvent) => { + if (searchRef.current) { + if (!searchRef.current?.contains(eve.target as Node)) { + setShowField(false); + } + } + }; + + useEffect(() => document.addEventListener('click', handleClicks), []); + + return ( +
+
setShowField(prev => !prev)} + > +
+
+ + {showField && ( + + )} +
+ ); +}; diff --git a/src/components/Header/SearchModule/index.ts b/src/components/Header/SearchModule/index.ts new file mode 100644 index 0000000000..f9326d5786 --- /dev/null +++ b/src/components/Header/SearchModule/index.ts @@ -0,0 +1 @@ +export * from './SearchModule'; diff --git a/src/modules/AccessoriesPage/AccessoriesPage.tsx b/src/modules/AccessoriesPage/AccessoriesPage.tsx index 12ff2fcf33..2863e4cd3c 100644 --- a/src/modules/AccessoriesPage/AccessoriesPage.tsx +++ b/src/modules/AccessoriesPage/AccessoriesPage.tsx @@ -1,15 +1,32 @@ import style from './AccessoriesPage.module.scss'; -import { useContext } from 'react'; -import { StateContext } from '../../components/GlobalProvider'; +import { useContext, useEffect, useMemo } from 'react'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; import { Catalog } from '../../components/Catalog/Catalog'; import { getProducts } from '../../utils/getProducts'; import { MenuItems } from '../../types/MenuItems'; +import { SearchParams } from '../../types/SearchParams'; +import { useSearchParams } from 'react-router-dom'; export const AccessoriesPage = () => { - const { products } = useContext(StateContext); - const accessories = getProducts.getProductByCategory( - products, - MenuItems.accessories, + const { products, showSearch } = useContext(StateContext); + const [searchParams] = useSearchParams(); + const dispatch = useContext(DispatchContext); + + const accessories = useMemo(() => { + const allAccessories = getProducts.getProductByCategory( + products, + MenuItems.accessories, + ); + + return getProducts.getFilteredByQuery( + allAccessories, + searchParams.get(SearchParams.query), + ); + }, [products, searchParams]); + + useEffect( + () => dispatch({ type: 'setShowSearch', payload: true }), + [dispatch, showSearch], ); return ( diff --git a/src/modules/FavotitePage/FavoritePage.tsx b/src/modules/FavotitePage/FavoritePage.tsx index 9b500cdf46..a46bf9d400 100644 --- a/src/modules/FavotitePage/FavoritePage.tsx +++ b/src/modules/FavotitePage/FavoritePage.tsx @@ -1,12 +1,18 @@ import style from './FavoritePage.module.scss'; import { Catalog } from '../../components/Catalog/Catalog'; import { useContext, useEffect, useState } from 'react'; -import { StateContext } from '../../components/GlobalProvider'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; export const FavoritePage = () => { const { inFavorites } = useContext(StateContext); const [favorites, setFavorites] = useState(inFavorites); + const { showSearch } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + useEffect( + () => dispatch({ type: 'setShowSearch', payload: true }), + [dispatch, showSearch], + ); useEffect(() => setFavorites(() => inFavorites), [inFavorites]); return ( diff --git a/src/modules/HomePage/HomePage.tsx b/src/modules/HomePage/HomePage.tsx index 71bc2481ea..7be3dcc9ae 100644 --- a/src/modules/HomePage/HomePage.tsx +++ b/src/modules/HomePage/HomePage.tsx @@ -2,14 +2,21 @@ import classNames from 'classnames'; import style from './HomePage.module.scss'; import { PhoneSlider } from '../../components/PhoneSlider'; -import { useContext, useMemo } from 'react'; -import { StateContext } from '../../components/GlobalProvider'; +import { useContext, useEffect, useMemo } from 'react'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; import { Categories } from '../../components/Categories'; import { PicturesSlider } from '../../components/PicturesSlider'; import { getProducts } from '../../utils/getProducts'; export const HomePage = () => { const { products } = useContext(StateContext); + const { showSearch } = useContext(StateContext); + const dispatch = useContext(DispatchContext); + + useEffect( + () => dispatch({ type: 'setShowSearch', payload: false }), + [dispatch, showSearch], + ); const newPhone = useMemo( () => getProducts.getNewProducts(products), diff --git a/src/modules/PhonePage/PhonePage.tsx b/src/modules/PhonePage/PhonePage.tsx index d9978cba62..a0b729dac8 100644 --- a/src/modules/PhonePage/PhonePage.tsx +++ b/src/modules/PhonePage/PhonePage.tsx @@ -1,15 +1,32 @@ import style from './PhonePage.module.scss'; -import { useContext, useMemo } from 'react'; -import { StateContext } from '../../components/GlobalProvider'; +import { useContext, useEffect, useMemo } from 'react'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; import { Catalog } from '../../components/Catalog/Catalog'; import { getProducts } from '../../utils/getProducts'; import { MenuItems } from '../../types/MenuItems'; +import { useSearchParams } from 'react-router-dom'; +import { SearchParams } from '../../types/SearchParams'; export const PhonePage = () => { - const { products } = useContext(StateContext); - const phones = useMemo( - () => getProducts.getProductByCategory(products, MenuItems.phones), - [products], + const { products, showSearch } = useContext(StateContext); + const [searchParams] = useSearchParams(); + const dispatch = useContext(DispatchContext); + + const phones = useMemo(() => { + const allPhones = getProducts.getProductByCategory( + products, + MenuItems.phones, + ); + + return getProducts.getFilteredByQuery( + allPhones, + searchParams.get(SearchParams.query), + ); + }, [products, searchParams]); + + useEffect( + () => dispatch({ type: 'setShowSearch', payload: true }), + [dispatch, showSearch], ); return ( diff --git a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx index a8a0faee62..48776588ac 100644 --- a/src/modules/ProductDetailsPage/ProductDetailsPage.tsx +++ b/src/modules/ProductDetailsPage/ProductDetailsPage.tsx @@ -11,7 +11,7 @@ import { CapacitySelector } from '../../components/CapacitySelector'; import { getProductItems } from '../../utils/getProductItems'; import { ButtonsAddCardFav } from '../../components/ButtonsAddCardFav'; import { PhoneSlider } from '../../components/PhoneSlider'; -import { StateContext } from '../../components/GlobalProvider'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; import { Product } from '../../types/Product'; import { MenuItems } from '../../types/MenuItems'; import { ItemPhoto } from './ItemPhoto/ItemPhoto'; @@ -27,7 +27,7 @@ type Props = { export const ProductDetailsPage: React.FC = ({ category }) => { const location = useLocation(); const navigate = useNavigate(); - const { products } = useContext(StateContext); + const { products, showSearch } = useContext(StateContext); const [item, setItem] = useState(); const [allCatalogProducts, setAllCatalogProducts] = useState( [], @@ -36,6 +36,12 @@ export const ProductDetailsPage: React.FC = ({ category }) => { const [loading, setLoading] = useState(false); const curItemId = getIdFromURL(location); + const dispatch = useContext(DispatchContext); + + useEffect( + () => dispatch({ type: 'setShowSearch', payload: false }), + [dispatch, showSearch], + ); useEffect(() => { setLoading(true); diff --git a/src/modules/TabletPage/TabletPage.tsx b/src/modules/TabletPage/TabletPage.tsx index ae61c0e47a..14990fa418 100644 --- a/src/modules/TabletPage/TabletPage.tsx +++ b/src/modules/TabletPage/TabletPage.tsx @@ -1,19 +1,39 @@ import style from './TabletPage.module.scss'; -import { useContext } from 'react'; -import { StateContext } from '../../components/GlobalProvider'; +import { useContext, useEffect, useMemo } from 'react'; +import { DispatchContext, StateContext } from '../../components/GlobalProvider'; import { Catalog } from '../../components/Catalog/Catalog'; import { getProducts } from '../../utils/getProducts'; import { MenuItems } from '../../types/MenuItems'; +import { useSearchParams } from 'react-router-dom'; +import { SearchParams } from '../../types/SearchParams'; export const TabletPage = () => { - const { products } = useContext(StateContext); - const tablet = getProducts.getProductByCategory(products, MenuItems.tablets); + const { products, showSearch } = useContext(StateContext); + const [searchParams] = useSearchParams(); + const dispatch = useContext(DispatchContext); + + const tablets = useMemo(() => { + const allTablets = getProducts.getProductByCategory( + products, + MenuItems.tablets, + ); + + return getProducts.getFilteredByQuery( + allTablets, + searchParams.get(SearchParams.query), + ); + }, [products, searchParams]); + + useEffect( + () => dispatch({ type: 'setShowSearch', payload: true }), + [dispatch, showSearch], + ); return (
); diff --git a/src/styles/icons.scss b/src/styles/icons.scss index 2dfdb5e11e..5c2318b61f 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -7,7 +7,7 @@ justify-content: center; align-items: center; overflow: hidden; - transition: background-color 0.3s; + transition: border-color 0.3s; border: 1px solid var(--c-icons); width: 32px; height: 32px; diff --git a/src/styles/theme.scss b/src/styles/theme.scss index 46c8b600da..c836b6ebc6 100644 --- a/src/styles/theme.scss +++ b/src/styles/theme.scss @@ -32,6 +32,7 @@ --fls-plus: url('../img/icons/plus.svg'); --fls-minus: url('../img/icons/minus.svg'); --fls-minus-disabled: url('../img/icons/minus_disabled.svg'); + --fls-search: url('../img/icons/Search.svg'); &_dark { --c-accent: #905BFF; @@ -67,5 +68,6 @@ --fls-plus: url('../img/icons/darkMode/plus.svg'); --fls-minus: url('../img/icons/darkMode/minus.svg'); --fls-minus-disabled: url('../img/icons/minus_disabled.svg'); + --fls-search: url('../img/icons/darkMode/Search.svg'); } } diff --git a/src/types/SearchParams.ts b/src/types/SearchParams.ts index eb7bb0722f..23e4f513d6 100644 --- a/src/types/SearchParams.ts +++ b/src/types/SearchParams.ts @@ -4,4 +4,5 @@ export enum SearchParams { 'page' = 'page', 'productColor' = 'productColor', 'capacity' = 'capacity', + 'query' = 'query', } diff --git a/src/utils/getProducts.ts b/src/utils/getProducts.ts index d161d41c92..6854d6d7d4 100644 --- a/src/utils/getProducts.ts +++ b/src/utils/getProducts.ts @@ -45,4 +45,17 @@ export const getProducts = { return; }, + + getFilteredByQuery( + products: Product[] | undefined, + query: string | null, + ): Product[] | [] { + if (products && query) { + return products.filter(product => + product.name.toLocaleLowerCase().includes(query.toLocaleLowerCase()), + ); + } + + return products; + }, }; From 8f7600779a6a4b3a9086e2f5dae754c2059afd09 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 19:50:38 -0500 Subject: [PATCH 11/20] fix buttom hover --- .../ButtonsAddCardFav/ButtonsAddCardFav.module.scss | 2 +- src/styles/buttons.scss | 9 ++++++--- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss index d78419a370..9f9831c40f 100644 --- a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss +++ b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss @@ -17,7 +17,7 @@ aspect-ratio: 1/1; &:hover { - box-shadow: 0 3px 13px 0 #17203166; + // box-shadow: 0 3px 13px 0 #17203166; } } } diff --git a/src/styles/buttons.scss b/src/styles/buttons.scss index fe367289a6..719663bdb2 100644 --- a/src/styles/buttons.scss +++ b/src/styles/buttons.scss @@ -14,10 +14,13 @@ &_selected { background-color: var(--c-buttons-background-selected); - border: 1px solid var(--c-elements) - } - + border: 1px solid var(--c-elements); + &:hover { + box-shadow: 0 3px 13px 0 #17203166; + background-color: var(--c-buttons-background-selected); + } + } } } From 1e750669d71fa4151f909f0441f12ff2ca737704 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 19:50:51 -0500 Subject: [PATCH 12/20] fix buttom hover2 --- .../ButtonsAddCardFav/ButtonsAddCardFav.module.scss | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss index 9f9831c40f..84043b4572 100644 --- a/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss +++ b/src/components/ButtonsAddCardFav/ButtonsAddCardFav.module.scss @@ -15,9 +15,5 @@ &_favorite { box-sizing: border-box; aspect-ratio: 1/1; - - &:hover { - // box-shadow: 0 3px 13px 0 #17203166; - } } } From 556a1b2ea235bb0e50f31a0c3edb0782fa728d78 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 20:19:32 -0500 Subject: [PATCH 13/20] fix text --- src/components/PicturesSlider/PictureSlider.scss | 3 +-- src/modules/App/App.tsx | 2 ++ 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/components/PicturesSlider/PictureSlider.scss b/src/components/PicturesSlider/PictureSlider.scss index f4dc5020c3..09b1163c73 100644 --- a/src/components/PicturesSlider/PictureSlider.scss +++ b/src/components/PicturesSlider/PictureSlider.scss @@ -32,14 +32,13 @@ @include ondesktop { height: 400px; } - } &_right { @include icon-bg(var(--fls-right)); &_disabled { - @include icon-bg(var(--fls-right-disabled)) + @include icon-bg(var(--fls-right-disabled)); } } diff --git a/src/modules/App/App.tsx b/src/modules/App/App.tsx index 42847bab77..8e6fd35d5e 100644 --- a/src/modules/App/App.tsx +++ b/src/modules/App/App.tsx @@ -44,6 +44,8 @@ export const App = () => { > +

Product Catalog

+
From 11220ed4b12e4fd7820bf9611e1b59ea1e437cfe Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Mon, 18 Nov 2024 20:25:34 -0500 Subject: [PATCH 14/20] updated package.json --- index.html | 17 +++++++++++++---- package.json | 8 +++++--- 2 files changed, 18 insertions(+), 7 deletions(-) diff --git a/index.html b/index.html index 095fb3a453..9d84a3a439 100644 --- a/index.html +++ b/index.html @@ -1,12 +1,21 @@ + - - Vite + React + TS + + Nice Gadgets store! - + +
- + + diff --git a/package.json b/package.json index ae251685c8..0a6735fcad 100644 --- a/package.json +++ b/package.json @@ -7,16 +7,18 @@ "license": "GPL-3.0", "dependencies": { "@fortawesome/fontawesome-free": "^6.5.2", - "bulma": "^1.0.1", "classnames": "^2.5.1", + "eslint-plugin-css": "^0.11.0", "react": "^18.3.1", "react-dom": "^18.3.1", "react-router-dom": "^6.25.1", - "react-transition-group": "^4.4.5" + "swiper": "^11.1.14", + "uuid": "^11.0.3" }, "devDependencies": { "@cypress/react18": "^2.0.1", - "@mate-academy/scripts": "^1.8.5", + "@linthtml/linthtml": "^0.10.1", + "@mate-academy/scripts": "^1.9.12", "@mate-academy/students-ts-config": "*", "@mate-academy/stylelint-config": "*", "@types/node": "^20.14.10", From 0b4badf68bdf0f400904aae5ceb4e361b9074568 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Tue, 19 Nov 2024 07:41:41 -0500 Subject: [PATCH 15/20] fix footer gaps --- src/components/Footer/Footer.module.scss | 19 +++++++++++++++---- .../FooterLinks/FooterLinks.module.scss | 3 ++- 2 files changed, 17 insertions(+), 5 deletions(-) diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss index abff866f1d..8cb90b73e0 100644 --- a/src/components/Footer/Footer.module.scss +++ b/src/components/Footer/Footer.module.scss @@ -1,7 +1,6 @@ @import '../../styles/main'; .footer { - &_container { display: flex; flex-direction: column; @@ -15,7 +14,6 @@ @include ontablet { flex-direction: row; - padding: 32px; } &_logo { @@ -24,6 +22,18 @@ align-items: center; height: 32px; width: 100%; + + @include ontablet { + flex-grow: 1; + flex-basis: 0; + } + } + + &_links { + @include ontablet { + flex-grow: 1; + flex-basis: 0; + } } &_back_top { @@ -34,9 +44,10 @@ width: 100%; @include ontablet { - justify-content: flex-end + flex-grow: 1; + flex-basis: 0; + justify-content: flex-end; } - } } } diff --git a/src/components/Footer/FooterLinks/FooterLinks.module.scss b/src/components/Footer/FooterLinks/FooterLinks.module.scss index cf1895e445..d0775371df 100644 --- a/src/components/Footer/FooterLinks/FooterLinks.module.scss +++ b/src/components/Footer/FooterLinks/FooterLinks.module.scss @@ -4,15 +4,16 @@ display: flex; flex-direction: column; align-items: flex-start; + justify-content: space-between; row-gap: 16px; @include ontablet { height: 100%; + width: 100%; flex-direction: row; align-items: center; gap: 14px; } - } .link { From 963359087cc3884dfc10e2e3daef9cc75defe830 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Tue, 19 Nov 2024 09:21:30 -0500 Subject: [PATCH 16/20] fixed links in footer and added Rights pop-up --- .../CopyrightModal/CopyrightModal.module.scss | 51 +++++++++++ .../Footer/CopyrightModal/CopyrightModal.tsx | 90 +++++++++++++++++++ src/components/Footer/CopyrightModal/index.ts | 1 + .../Footer/FooterLinks/FooterLinks.tsx | 17 +++- 4 files changed, 156 insertions(+), 3 deletions(-) create mode 100644 src/components/Footer/CopyrightModal/CopyrightModal.module.scss create mode 100644 src/components/Footer/CopyrightModal/CopyrightModal.tsx create mode 100644 src/components/Footer/CopyrightModal/index.ts diff --git a/src/components/Footer/CopyrightModal/CopyrightModal.module.scss b/src/components/Footer/CopyrightModal/CopyrightModal.module.scss new file mode 100644 index 0000000000..68acff5cde --- /dev/null +++ b/src/components/Footer/CopyrightModal/CopyrightModal.module.scss @@ -0,0 +1,51 @@ +@import '../../../styles/main'; + +.copyright_container { + position: fixed; + z-index: 1; + left: 0; + top: 0; + width: 100vw; + height: 100vh; + overflow: auto; + background-color: #89939af4; + display: flex; + flex-direction: column; + justify-content: space-around; + align-items: center; + + & .modal_overlay { + box-sizing: border-box; + width: 75vw; + max-width: 600px; + background-color: var(--c-elements); + border-radius: 10px; + box-shadow: 0 0 50px rgba(0, 0, 0, 0.5); + display: flex; + justify-content: center; + } + & .modal_content { + padding: 15px; + } + + & .link { + @extend %uppercase; + @extend %link; + + cursor: pointer; + + @include hover; + } + + & .button { + width: 50%; + height: 40px; + &_wrap { + box-sizing: border-box; + display: flex; + align-items: center; + justify-content: center; + padding-top: 15px; + } + } +} diff --git a/src/components/Footer/CopyrightModal/CopyrightModal.tsx b/src/components/Footer/CopyrightModal/CopyrightModal.tsx new file mode 100644 index 0000000000..a5ff0ecfb2 --- /dev/null +++ b/src/components/Footer/CopyrightModal/CopyrightModal.tsx @@ -0,0 +1,90 @@ +import { useEffect } from 'react'; +import style from './CopyrightModal.module.scss'; +import classNames from 'classnames'; +type Props = { + toggleModal: () => void; +}; +export const CopyrightModal: React.FC = ({ toggleModal }) => { + useEffect(() => { + document.body.style.overflowY = 'hidden'; + + return () => { + document.body.style.overflowY = 'auto'; + }; + }, []); + + return ( +
+
+
+

+ Copyright Notice +

+ +

© 2024 Seva Podolskiy’s Web Studio. All Rights Reserved.

+ +

+ This website and its content, including but not limited to text, + images, graphics, logos, icons, and design elements, are the + exclusive property of Seva Podolskiy’s Web Studio. Unauthorized use, + duplication, reproduction, distribution, or modification of any + content on this site is strictly prohibited without prior written + consent. +

+ +

Permitted Uses:

+ +
    +
  • Personal, non-commercial viewing of the website content.
  • +
  • + Sharing content via social media platforms with proper credit and + a direct link to this website. +
  • +
+ +

Prohibited Uses:

+ +
    +
  • Republishing material from this site without attribution.
  • +
  • Utilizing website content for commercial purposes.
  • +
  • + Using automated tools to scrape or extract data without written + authorization. +
  • +
+ +

+ Seva Podolskiy’s Web Studio respects intellectual property rights + and expects visitors to do the same. If you believe any content on + this site infringes on your copyright, please contact us immediately + at{' '} + + copyright@sevawebstudio.com + + . +

+ +

+ This copyright notice is subject to change without notice. By using + this website, you agree to comply with these terms and respect all + applicable copyright laws. +

+ +

+ Thank you for visiting and respecting the intellectual property on + this website! +

+ +
+
+
Close
+
+
+
+
+
+ ); +}; diff --git a/src/components/Footer/CopyrightModal/index.ts b/src/components/Footer/CopyrightModal/index.ts new file mode 100644 index 0000000000..b4876cd737 --- /dev/null +++ b/src/components/Footer/CopyrightModal/index.ts @@ -0,0 +1 @@ +export * from './CopyrightModal'; diff --git a/src/components/Footer/FooterLinks/FooterLinks.tsx b/src/components/Footer/FooterLinks/FooterLinks.tsx index 8b6e50c940..1c96e522c5 100644 --- a/src/components/Footer/FooterLinks/FooterLinks.tsx +++ b/src/components/Footer/FooterLinks/FooterLinks.tsx @@ -1,19 +1,29 @@ import classNames from 'classnames'; import style from './FooterLinks.module.scss'; import { Link } from 'react-router-dom'; +import { CopyrightModal } from '../CopyrightModal'; +import { useState } from 'react'; export const FooterLinks: React.FC = () => { + const [isVisibleCopyright, setIsVisibleCopyright] = useState(false); + + const toogleModal = () => { + setIsVisibleCopyright(prev => !prev); + }; + return (
+ {isVisibleCopyright && } + Github Contacts @@ -22,8 +32,9 @@ export const FooterLinks: React.FC = () => { toogleModal()} > - rights + Rights
); From 2728ad248cd383fea4969c8612e6797ded0de43d Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Tue, 19 Nov 2024 09:26:18 -0500 Subject: [PATCH 17/20] added favircon --- index.html | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/index.html b/index.html index 9d84a3a439..8dffdf9bde 100644 --- a/index.html +++ b/index.html @@ -8,6 +8,11 @@ content="width=device-width, initial-scale=1.0" /> Nice Gadgets store! + From f861296790fbfd338852fb8137bae95ae1aac50b Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Tue, 19 Nov 2024 09:30:21 -0500 Subject: [PATCH 18/20] fixed cursors --- src/components/Footer/Footer.module.scss | 2 +- src/modules/CartPage/CartItem/CartItem.module.scss | 5 +---- src/styles/buttons.scss | 3 ++- src/styles/icons.scss | 2 +- 4 files changed, 5 insertions(+), 7 deletions(-) diff --git a/src/components/Footer/Footer.module.scss b/src/components/Footer/Footer.module.scss index 8cb90b73e0..2e884881c2 100644 --- a/src/components/Footer/Footer.module.scss +++ b/src/components/Footer/Footer.module.scss @@ -55,7 +55,7 @@ .back_top_text { @extend %small-text; - cursor: default; + cursor: pointer; color: var(--c-secondary); } diff --git a/src/modules/CartPage/CartItem/CartItem.module.scss b/src/modules/CartPage/CartItem/CartItem.module.scss index e5de4a49d3..818a9bab2e 100644 --- a/src/modules/CartPage/CartItem/CartItem.module.scss +++ b/src/modules/CartPage/CartItem/CartItem.module.scss @@ -80,11 +80,10 @@ & .name { text-decoration: none; color: (var(--c-primary)); - cursor: default; + cursor: pointer; @include hover; } - } & .bottom { @@ -102,7 +101,6 @@ align-items: center; & .icon { - &_container { width: 32px; height: 32px; @@ -127,6 +125,5 @@ text-align: center; } } - } } diff --git a/src/styles/buttons.scss b/src/styles/buttons.scss index 719663bdb2..71efea3dec 100644 --- a/src/styles/buttons.scss +++ b/src/styles/buttons.scss @@ -6,6 +6,7 @@ align-items: center; justify-content: center; padding-inline: 5px; + cursor: pointer; &:hover { box-shadow: 0 3px 13px 0 #17203166; @@ -27,7 +28,7 @@ .buttons_text { @extend %buttons; - cursor: default; + cursor: pointer; color: var(--c-buttons-text); &_selected { diff --git a/src/styles/icons.scss b/src/styles/icons.scss index 5c2318b61f..92d22632b6 100644 --- a/src/styles/icons.scss +++ b/src/styles/icons.scss @@ -1,7 +1,7 @@ @import './mixines'; %icon-container { - cursor: default; + cursor: pointer; box-sizing: border-box; display: flex; justify-content: center; From 2c85b22b0a21629542cb0845ead18f0452d07bc2 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Tue, 19 Nov 2024 09:33:48 -0500 Subject: [PATCH 19/20] added transition --- src/styles/buttons.scss | 1 + 1 file changed, 1 insertion(+) diff --git a/src/styles/buttons.scss b/src/styles/buttons.scss index 71efea3dec..608bd81567 100644 --- a/src/styles/buttons.scss +++ b/src/styles/buttons.scss @@ -7,6 +7,7 @@ justify-content: center; padding-inline: 5px; cursor: pointer; + transition: all 0.3s; &:hover { box-shadow: 0 3px 13px 0 #17203166; From 0c3f1ec008f3e87bd867be0a2c80eafe1a6d8040 Mon Sep 17 00:00:00 2001 From: Seva Podolskiy Date: Tue, 19 Nov 2024 11:46:24 -0500 Subject: [PATCH 20/20] fixed catalog and Git link --- src/components/Catalog/Catalog.module.scss | 43 ++++++++----------- .../Footer/FooterLinks/FooterLinks.tsx | 1 + src/styles/mixines.scss | 5 --- 3 files changed, 18 insertions(+), 31 deletions(-) diff --git a/src/components/Catalog/Catalog.module.scss b/src/components/Catalog/Catalog.module.scss index 683a9f32ad..90dfada743 100644 --- a/src/components/Catalog/Catalog.module.scss +++ b/src/components/Catalog/Catalog.module.scss @@ -11,7 +11,6 @@ @include inline-padding; & .container { - &_title { @include ontablet { grid-area: 2/ 1/ 2/ -1; @@ -29,42 +28,34 @@ &_catalog { display: flex; flex-flow: row wrap; - gap: 40px; - width: 100%; - justify-content: center; - + gap: 40px 16px; @include ontablet { + justify-content: space-between; flex-direction: row; - gap: 16px; } } &_product { display: flex; flex-direction: row; - width: 220px; - - @include ontablet { - flex-wrap: wrap; - width: 230px; - - } + flex: 1 1 220px; } } - & .products_not_found_container{ - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - padding: 20px; - - & .img{ - flex-grow: 1; - width: 30vw; - aspect-ratio: 1/1; - background: url(../../../public/img/product-not-found.png) no-repeat center/contain;; + & .products_not_found_container { + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + padding: 20px; + + & .img { + flex-grow: 1; + width: 30vw; + aspect-ratio: 1/1; + background: url(../../../public/img/product-not-found.png) no-repeat + center/contain; + } } } -} diff --git a/src/components/Footer/FooterLinks/FooterLinks.tsx b/src/components/Footer/FooterLinks/FooterLinks.tsx index 1c96e522c5..3f114f7d64 100644 --- a/src/components/Footer/FooterLinks/FooterLinks.tsx +++ b/src/components/Footer/FooterLinks/FooterLinks.tsx @@ -18,6 +18,7 @@ export const FooterLinks: React.FC = () => { Github diff --git a/src/styles/mixines.scss b/src/styles/mixines.scss index 507574ef04..9d4be51162 100644 --- a/src/styles/mixines.scss +++ b/src/styles/mixines.scss @@ -40,14 +40,9 @@ @include ontablet { padding-inline: $tablet-padding-inline; } - - @include ondesktop { - padding-inline: $desktop-padding-inline; - } } @mixin headings { - h1, h2, h3,