([]);
+ const [numberToShow, setNumberToShow] = useState(6);
+ const { isMobileScreen } = useProfile();
+
+ const findNumbersToShow = useCallback(() => {
+ if (isMobileScreen) {
+ setNumberToShow(6);
+ return;
+ }
+
+ setNumberToShow(5);
+ }, [isMobileScreen]);
+
+ useEffect(() => {
+ findNumbersToShow();
+ }, [findNumbersToShow, isMobileScreen]);
+
+ const handleCategoryToggle = () => {
+ if (numberToShow < 12) {
+ setNumberToShow(12);
+ return;
+ }
+ findNumbersToShow();
+ };
+
+ useEffect(() => {
+ api.categoriesList().then((categories) => setCategories(categories));
+ }, []);
+ return (
+ <>
+
+
+
+
+ {categories.slice(0, numberToShow).map((category) => (
+ -
+
+
+
+
+ ))}
+
+
+
+ {numberToShow < 12 ? 'Развернуть' : 'Свернуть'}
+
+
+
+ >
+ );
+};
+
+export default CatalogPromo;
diff --git a/src/components/energy-value/index.tsx b/src/components/energy-value/index.tsx
index 2208227f..5d93a9d8 100644
--- a/src/components/energy-value/index.tsx
+++ b/src/components/energy-value/index.tsx
@@ -2,10 +2,10 @@ import clsx from 'clsx';
import styles from './energy-value.module.scss';
type Props = {
- kcal?: number;
- proteins?: number;
- fats?: number;
- carbohydrates?: number;
+ kcal: number;
+ proteins: number;
+ fats: number;
+ carbonhydrates: number;
className?: string;
};
@@ -13,7 +13,7 @@ export default function EnergyValue({
kcal = 0,
proteins = 0,
fats = 0,
- carbohydrates = 0,
+ carbonhydrates = 0,
className,
}: Props) {
return (
@@ -28,7 +28,7 @@ export default function EnergyValue({
углеводы
- {`${carbohydrates}г`}
+ {`${carbonhydrates}г`}
ккал
diff --git a/src/components/making-order-btn/index.tsx b/src/components/making-order-btn/index.tsx
index 2627ae28..8c6c7922 100644
--- a/src/components/making-order-btn/index.tsx
+++ b/src/components/making-order-btn/index.tsx
@@ -1,15 +1,19 @@
import React from 'react';
import styles from './making-order-btn.module.scss';
import Button from '@components/Button';
+import type { ButtonProps } from '@components/Button';
-interface MakingOrderBtnProps {
- onClick?: () => void;
-}
+interface MakingOrderBtnProps extends Pick {}
-const MakingOrderBtn: React.FC = ({ onClick }) => {
+const MakingOrderBtn: React.FC = ({ onClick, disabled }) => {
return (
-
+
Нажимая на кнопку «Оформить заказ», вы соглашаетесь
с условиями обработки персональных данных, а также с условиями
diff --git a/src/components/making-order-btn/making-order-btn.module.scss b/src/components/making-order-btn/making-order-btn.module.scss
index 4daa8648..33b7852c 100644
--- a/src/components/making-order-btn/making-order-btn.module.scss
+++ b/src/components/making-order-btn/making-order-btn.module.scss
@@ -47,8 +47,8 @@
transition: 0.5s;
&:disabled {
- background-color: black;
- cursor: not-allowed;
+ background-color: $accent-color-lightest-green;
+ cursor: initial;
}
}
diff --git a/src/components/navigation-icons/index.tsx b/src/components/navigation-icons/index.tsx
index 6077540b..5a504c00 100644
--- a/src/components/navigation-icons/index.tsx
+++ b/src/components/navigation-icons/index.tsx
@@ -3,10 +3,12 @@ import { Link } from 'react-router-dom';
import { useAuth } from '@hooks/use-auth.ts';
import styles from './navigation-icons.module.scss';
import { usePopup } from '@hooks/use-popup.ts';
+import { useCart } from '@hooks/use-cart-context';
const NavigationIcons: React.FC = () => {
const { isLoggedIn } = useAuth();
const { handleOpenPopup } = usePopup();
+ const { cartData } = useCart();
return (
@@ -20,6 +22,7 @@ const NavigationIcons: React.FC = () => {
{
);
};
diff --git a/src/components/shopping-item/index.tsx b/src/components/shopping-item/index.tsx
index 62b73959..f6bd2b66 100644
--- a/src/components/shopping-item/index.tsx
+++ b/src/components/shopping-item/index.tsx
@@ -50,9 +50,7 @@ const ShoppingItem: React.FC
= (props) => {
className={`${styles.item_btn} ${styles.item__decrease_btn}`}
onClick={handleDecreaseClick}
>
- {`${product.quantity} шт`}
+ {product.quantity}
= ({ title, link, type }) => {
+ return (
+
+
+ {title}
+
+
+
+ );
+};
+
+export default TitleArrowLink;
diff --git a/src/components/title-arrow-link/title-arrow-link.module.scss b/src/components/title-arrow-link/title-arrow-link.module.scss
new file mode 100644
index 00000000..616ae385
--- /dev/null
+++ b/src/components/title-arrow-link/title-arrow-link.module.scss
@@ -0,0 +1,59 @@
+@use '@scss/variables' as *;
+
+.link__title {
+ color: $active-text-color;
+ font-family: $ubuntu-font;
+ font-size: 30px;
+ font-weight: 700;
+ line-height: 140%;
+ margin: 0;
+ padding-right: 5px;
+
+ @media screen and (width <= 768px) {
+ font-size: 24px;
+ padding-right: 3px;
+ }
+}
+
+.link__title_type_smallFont {
+ @media screen and (width <= 768px) {
+ font-size: 18px;
+ }
+}
+
+.link__arrow {
+ width: 20px;
+ height: 32px;
+ margin-top: auto;
+ background-position: center;
+ background-size: cover;
+ background-repeat: no-repeat;
+ stroke: $active-text-color;
+
+ @media screen and (width <= 768px) {
+ width: 18px;
+ height: 28px;
+ }
+}
+
+.link__arrow_type_small {
+ @media screen and (width <= 768px) {
+ width: 14px;
+ height: 20px;
+ }
+}
+
+.link {
+ text-decoration: none;
+ display: flex;
+ max-width: max-content;
+
+ &:hover .link__title,
+ &:hover .link__arrow {
+ stroke: $accent-color-bright-green;
+ color: $accent-color-bright-green;
+ transition:
+ color 0.2s ease-in-out,
+ stroke 0.2s ease-in-out;
+ }
+}
diff --git a/src/data/dataExamples.ts b/src/data/dataExamples.ts
index 6adac500..b4ffaa99 100644
--- a/src/data/dataExamples.ts
+++ b/src/data/dataExamples.ts
@@ -1,38 +1,5 @@
import TomatoesForProductPage from '@images/tomatoes_for_product_page.png';
-export const mainPageBlockLinks = [
- {
- title: 'Овощи',
- link: 'vegetables-and-herbs',
- backgroundImage: '/383bea75365411fd84f9c6c47e081cab.jpeg',
- gridArea: 'a',
- },
- {
- title: 'Фрукты',
- link: 'fruits',
- backgroundImage: '/e8ab3ec81c9d3e4cc473a3d6ae86bc5a.jpeg',
- gridArea: 'b',
- },
- {
- title: 'Орехи',
- link: 'nuts-dried-fruits',
- backgroundImage: '/05c2db3efd894fa03b952abf2d5a88ee.jpeg',
- gridArea: 'c',
- },
- {
- title: 'Молочные продукты',
- link: 'dairy',
- backgroundImage: '/5b5f8ca8a8f4f583ac88b6e80a646e10.jpeg',
- gridArea: 'd',
- },
- {
- title: 'Мясо и птица',
- link: 'meat-and-poultry',
- backgroundImage: '/4e039f6d2c33797f4fd913bd642549f0.jpeg',
- gridArea: 'e',
- },
-];
-
export const products = [
{
cardName: 'Помидоры черри',
diff --git a/src/pages/category/category.module.scss b/src/pages/category/category.module.scss
index 0373995c..c1ed940d 100644
--- a/src/pages/category/category.module.scss
+++ b/src/pages/category/category.module.scss
@@ -66,8 +66,10 @@
.category__sorting {
width: 328px;
margin-right: 20px;
+ padding-top: 58px;
@media screen and (width <= 768px) {
+ padding-top: 0;
margin-right: 0;
max-width: 280px;
}
diff --git a/src/pages/checkout/checkout.module.scss b/src/pages/checkout/checkout.module.scss
index 9e58144c..9465cb01 100644
--- a/src/pages/checkout/checkout.module.scss
+++ b/src/pages/checkout/checkout.module.scss
@@ -513,8 +513,8 @@
transition: 0.5s;
&:disabled {
- background-color: black;
- cursor: not-allowed;
+ background-color: $accent-color-lightest-green;
+ cursor: initial;
}
}
diff --git a/src/pages/checkout/index.tsx b/src/pages/checkout/index.tsx
index a88faaa4..e6afff76 100644
--- a/src/pages/checkout/index.tsx
+++ b/src/pages/checkout/index.tsx
@@ -431,6 +431,7 @@ const Checkout: React.FC = () => {
!isLoggedIn && !isValid ? `${styles['orderse__buttonStyle_error']}` : ''
}`}
onClick={handleSubmitOrder}
+ disabled={cartData.products.length === 0}
>
Оформить заказ
diff --git a/src/pages/home/home.module.scss b/src/pages/home/home.module.scss
index 83406b75..6d356395 100644
--- a/src/pages/home/home.module.scss
+++ b/src/pages/home/home.module.scss
@@ -15,15 +15,16 @@
}
.home__catalogSection {
- margin-top: 120px;
+ max-width: 80%;
+ margin: 120px auto 0;
@media screen and (width <= 768px) {
margin: 50px auto 0;
- max-width: 550px;
+ max-width: calc(100% - 2 * 20px);
}
@media screen and (width <= 400px) {
- margin-top: 0;
+ margin: 0 auto;
}
}
diff --git a/src/pages/home/index.tsx b/src/pages/home/index.tsx
index 31d6b21f..4721f3fe 100644
--- a/src/pages/home/index.tsx
+++ b/src/pages/home/index.tsx
@@ -4,11 +4,10 @@ import styles from './home.module.scss';
import InfoCard from '@components/info-card';
import usefulProductsIcon from '@images/useful-products-icon.svg';
import roundTheClockDelivery from '@images/round-the-clock-delivery.svg';
-import CardCatalogLink from '@components/card-catalog-link';
-import { mainPageBlockLinks } from '@data/dataExamples.ts';
import TopSellingThisWeek from '@components/top-selling-this-week';
import AboutCompany from '@components/about-company/index.tsx';
import OurBlock from '@components/our-block';
+import CatalogPromo from '@components/catalog-promo';
const Home: React.FC = () => {
return (
@@ -31,7 +30,7 @@ const Home: React.FC = () => {
/>
diff --git a/src/pages/profile/profile-favorites/index.tsx b/src/pages/profile/profile-favorites/index.tsx
index 6a12cd1c..ff6129d8 100644
--- a/src/pages/profile/profile-favorites/index.tsx
+++ b/src/pages/profile/profile-favorites/index.tsx
@@ -4,6 +4,7 @@ import api from '@services/api';
import ProductCard from '@components/product-card';
import ReturnBackButton from '@components/profile-components/return-back-button';
import { useProfile } from '@hooks/use-profile';
+import { useCart } from '@hooks/use-cart-context';
import type { Product } from '@services/generated-api/data-contracts';
import { Link } from 'react-router-dom';
@@ -14,11 +15,17 @@ export default function ProfileFavorites() {
error: '',
});
const [products, setProducts] = useState>([]);
- const [checkboxesValues, setCheckboxesValue] = useState([]);
+ const [checkboxesValues, setCheckboxesValue] = useState>({});
const { isMobileScreen } = useProfile();
+ const { updateCart } = useCart();
useEffect(() => {
- products && setCheckboxesValue(products.map(() => false));
+ products &&
+ products.forEach((product) => {
+ setCheckboxesValue((prev) => {
+ return { ...prev, [product.id]: false };
+ });
+ });
}, [products]);
useEffect(() => {
@@ -46,18 +53,40 @@ export default function ProfileFavorites() {
});
}, []);
- const onCheckButton = (index: number) => {
+ const onCheckButton = (id: number) => {
return () => {
- setCheckboxesValue(
- checkboxesValues.map((value, i) => (i === index ? !value : value))
- );
+ setCheckboxesValue((prev) => {
+ return { ...prev, [id]: !prev[id] };
+ });
};
};
- const isChooseAll = checkboxesValues.every((el) => el) || !products.length;
+ const isChooseAll =
+ Object.values(checkboxesValues).every((el) => el) || !products.length;
const toggleAll = () => {
- setCheckboxesValue(checkboxesValues.map(() => (isChooseAll ? false : true)));
+ if (isChooseAll) {
+ Object.keys(checkboxesValues).forEach((key) => {
+ setCheckboxesValue((prev) => {
+ return { ...prev, [key]: false };
+ });
+ });
+ } else {
+ Object.keys(checkboxesValues).forEach((key) => {
+ setCheckboxesValue((prev) => {
+ return { ...prev, [key]: true };
+ });
+ });
+ }
+ };
+
+ const handleAddToCart = () => {
+ const dataToSend = Object.keys(checkboxesValues)
+ .filter((i) => checkboxesValues[i as unknown as keyof typeof checkboxesValues])
+ .map((item) => ({ id: Number(item), quantity: 1 }));
+ updateCart(dataToSend);
+
+ window.scroll(0, 0);
};
return (
@@ -71,7 +100,7 @@ export default function ProfileFavorites() {
{!productsLoadingStatus.inProcess && products.length ? (
- products.map((product, index) => (
+ products.map((product) => (
-
)}
- В корзину
+ i)}
+ >
+ В корзину
+
);
}
diff --git a/src/pages/recipe/index.tsx b/src/pages/recipe/index.tsx
index f5ef4883..cf83ad34 100644
--- a/src/pages/recipe/index.tsx
+++ b/src/pages/recipe/index.tsx
@@ -14,31 +14,29 @@ import { usePopup } from '@hooks/use-popup';
import PopupRecipe from '@components/popups/popup-recipe';
type ReceipeIngredientInfoProps = {
+ amount: number;
+ final_price: number;
id: number;
- name: string;
- measure_unit: string;
- quantity: number;
ingredient_photo: string;
- amount_of_pack: number;
- amount?: number;
- price?: number;
+ measure_unit: string;
+ name: string;
+ need_to_buy: number;
+ quantity_in_recipe: number;
};
type ReceipeInfoProps = {
- id: number;
author: number;
- name: string;
- text: string;
+ carbohydrates: number;
+ cooking_time: number;
+ fats: number;
+ id: number;
image: string;
ingredients: ReceipeIngredientInfoProps[];
- total_ingredients?: string;
- recipe_nutrients?: {
- proteins: number;
- fats: number;
- carbonhydrates: number;
- kcal: number;
- };
- cooking_time: number;
+ kcal: number;
+ name: string;
+ proteins: number;
+ text: string;
+ total_ingredients?: number;
};
const Recipe: React.FC = () => {
@@ -50,6 +48,12 @@ const Recipe: React.FC = () => {
const [recipeByLines, setRecipeByLines] = useState(['']);
const [numeralizeWord, setNumeralizeWord] = useState('');
const { reset } = useCart();
+ const [recipeNutrients, setRecipeNutrients] = useState({
+ proteins: 0,
+ fats: 0,
+ carbonhydrates: 0,
+ kcal: 0,
+ });
useEffect(() => {
if (!id) {
@@ -61,33 +65,13 @@ const Recipe: React.FC = () => {
const recipe = await api.getRecipeById(recipeId);
setRecipeByLines(recipe.text.split('\n'));
setNumeralizeWord(declOfNum(recipe.cooking_time, ['минута', 'минуты', 'минут']));
-
- const promises = recipe.ingredients.map(
- (ingredient: ReceipeIngredientInfoProps) => {
- return api.productsRead(ingredient.id);
- }
- );
-
- const newProducts = await Promise.all(promises);
- const filteredProducts = newProducts.filter((product) => product !== null);
-
- const addFieldsToRecipe = (prevReceipe: ReceipeInfoProps) => {
- filteredProducts.map((product) => {
- const index = prevReceipe.ingredients.findIndex((i) => i.id == product.id);
- if (index === -1) {
- return;
- }
- prevReceipe.ingredients[index].amount = product.amount;
- prevReceipe.ingredients[index].price = product.price;
- prevReceipe.ingredients[index].amount_of_pack = Math.ceil(
- prevReceipe.ingredients[index].quantity / product.amount
- );
- });
-
- return prevReceipe;
- };
-
- setRecipeInfo(addFieldsToRecipe(recipe));
+ setRecipeInfo(recipe);
+ setRecipeNutrients({
+ proteins: recipe.proteins,
+ fats: recipe.fats,
+ carbonhydrates: recipe.carbohydrates,
+ kcal: recipe.kcal,
+ });
};
fetchReceiptAndProducts().finally(() => setIsLoading(false));
@@ -130,10 +114,7 @@ const Recipe: React.FC = () => {
-
+
diff --git a/src/pages/shopping-cart/index.tsx b/src/pages/shopping-cart/index.tsx
index e73cfa1c..601fcb44 100644
--- a/src/pages/shopping-cart/index.tsx
+++ b/src/pages/shopping-cart/index.tsx
@@ -118,7 +118,10 @@ const ShoppingCart: React.FC = () => {
-
+
)}
diff --git a/src/services/api.ts b/src/services/api.ts
index ae81f660..e7c10954 100644
--- a/src/services/api.ts
+++ b/src/services/api.ts
@@ -31,7 +31,7 @@ class Api {
_checkResponse(res: Response) {
if (res.ok) {
- if (res.status === 204) return res;
+ if (res.status === 204 || res.status === 205) return res;
return res.json();
}
diff --git a/src/services/generated-api/data-contracts.ts b/src/services/generated-api/data-contracts.ts
index 36b5a8f8..dc6e447d 100644
--- a/src/services/generated-api/data-contracts.ts
+++ b/src/services/generated-api/data-contracts.ts
@@ -30,6 +30,7 @@ export interface Category {
* @maxLength 100
*/
name: string;
+ image: string;
/**
* Slug
* @format slug