From 45e6fd6fac0acc562371afee1fe5c90acb273927 Mon Sep 17 00:00:00 2001 From: Den2856 Date: Sat, 19 Apr 2025 19:48:14 +0300 Subject: [PATCH 1/4] =?UTF-8?q?=D0=9C=D0=BE=D0=B8=20=D0=B8=D0=B7=D0=BC?= =?UTF-8?q?=D0=B5=D0=BD=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B2=20=D0=B2=D0=B5?= =?UTF-8?q?=D1=82=D0=BA=D0=B5=20lecture-5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 90 +++++++++++++++ package.json | 1 + src/api/index.js | 48 +++----- src/app/article/index.js | 103 +++++++++-------- src/app/basket/index.js | 7 +- src/app/index.js | 3 +- src/app/profile/index.js | 4 +- src/assets/icon/cart_empty.png | Bin 0 -> 3481 bytes src/components/article-card/index.js | 8 +- src/components/basket-total/index.js | 11 ++ src/components/basket-total/style.css | 12 ++ src/components/category-select/index.js | 36 ++++++ src/components/comments-section/index.js | 131 +++++++++++++++++++++ src/components/comments-section/style.css | 134 ++++++++++++++++++++++ src/components/profile-card/index.js | 12 +- src/components/select/index.js | 99 ++++++++++++---- src/components/select/style.css | 64 ++++++++--- src/components/spinner/index.js | 8 +- src/components/spinner/style.css | 26 ++++- src/config.js | 4 + src/containers/catalog-list/index.js | 61 +++++----- src/containers/navigation/index.js | 5 +- src/containers/top-head/index.js | 3 +- src/hooks/use-article.js | 18 +++ src/hooks/use-comments.js | 52 +++++++++ src/hooks/use-selector.js | 2 +- src/hooks/use-translate.js | 17 +-- src/i18n/service.js | 35 ++++++ src/i18n/translate.js | 6 +- src/i18n/translations/en.json | 10 ++ src/i18n/translations/ru.json | 10 ++ src/index.js | 13 +-- src/services.js | 25 ++-- src/store-redux/comments/actions.js | 2 + src/store-redux/comments/reducer.js | 40 +++++++ src/store-redux/exports.js | 1 + src/store/comments/index.js | 86 ++++++++++++++ src/store/exports.js | 2 +- src/store/session/index.js | 1 + webpack.config.js | 4 + 40 files changed, 980 insertions(+), 214 deletions(-) create mode 100644 src/assets/icon/cart_empty.png create mode 100644 src/components/category-select/index.js create mode 100644 src/components/comments-section/index.js create mode 100644 src/components/comments-section/style.css create mode 100644 src/hooks/use-article.js create mode 100644 src/hooks/use-comments.js create mode 100644 src/i18n/service.js create mode 100644 src/store-redux/comments/actions.js create mode 100644 src/store-redux/comments/reducer.js create mode 100644 src/store/comments/index.js diff --git a/package-lock.json b/package-lock.json index 59bb3583a..2e2b2a549 100644 --- a/package-lock.json +++ b/package-lock.json @@ -29,6 +29,7 @@ "babel-loader": "^9.1.3", "cross-env": "^7.0.3", "css-loader": "^7.1.2", + "file-loader": "^6.2.0", "html-webpack-plugin": "^5.6.0", "jest": "^29.7.0", "mini-css-extract-plugin": "^2.9.1", @@ -3988,6 +3989,16 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "node_modules/big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "*" + } + }, "node_modules/binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -5006,6 +5017,16 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "node_modules/emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } + }, "node_modules/encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -5327,6 +5348,27 @@ "bser": "2.1.1" } }, + "node_modules/file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "license": "MIT", + "dependencies": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + }, + "engines": { + "node": ">= 10.13.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + }, + "peerDependencies": { + "webpack": "^4.0.0 || ^5.0.0" + } + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -8148,6 +8190,21 @@ "node": ">=6.11.5" } }, + "node_modules/loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "license": "MIT", + "dependencies": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + }, + "engines": { + "node": ">=8.9.0" + } + }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", @@ -13919,6 +13976,12 @@ "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==", "dev": true }, + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==", + "dev": true + }, "binary-extensions": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", @@ -14639,6 +14702,12 @@ "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, + "emojis-list": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/emojis-list/-/emojis-list-3.0.0.tgz", + "integrity": "sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==", + "dev": true + }, "encodeurl": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/encodeurl/-/encodeurl-2.0.0.tgz", @@ -14889,6 +14958,16 @@ "bser": "2.1.1" } }, + "file-loader": { + "version": "6.2.0", + "resolved": "https://registry.npmjs.org/file-loader/-/file-loader-6.2.0.tgz", + "integrity": "sha512-qo3glqyTa61Ytg4u73GultjHGjdRyig3tG6lPtyX/jOEJvHif9uB0/OCI2Kif6ctF3caQTW2G5gym21oAsI4pw==", + "dev": true, + "requires": { + "loader-utils": "^2.0.0", + "schema-utils": "^3.0.0" + } + }, "fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -16910,6 +16989,17 @@ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==", "dev": true }, + "loader-utils": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", + "dev": true, + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^3.0.0", + "json5": "^2.1.2" + } + }, "locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", diff --git a/package.json b/package.json index 5da2f1714..707304945 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "babel-loader": "^9.1.3", "cross-env": "^7.0.3", "css-loader": "^7.1.2", + "file-loader": "^6.2.0", "html-webpack-plugin": "^5.6.0", "jest": "^29.7.0", "mini-css-extract-plugin": "^2.9.1", diff --git a/src/api/index.js b/src/api/index.js index a5f073295..fbdad2cb5 100644 --- a/src/api/index.js +++ b/src/api/index.js @@ -1,8 +1,4 @@ class APIService { - /** - * @param services {Services} Менеджер сервисов - * @param config {Object} - */ constructor(services, config = {}) { this.services = services; this.config = config; @@ -11,35 +7,25 @@ class APIService { }; } - /** - * HTTP запрос - * @param url - * @param method - * @param headers - * @param options - * @returns {Promise<{}>} - */ - async request({ url, method = 'GET', headers = {}, ...options }) { - if (!url.match(/^(http|\/\/)/)) url = this.config.baseUrl + url; - const res = await fetch(url, { - method, - headers: { ...this.defaultHeaders, ...headers }, - ...options, - }); - return { data: await res.json(), status: res.status, headers: res.headers }; + setHeader(name, value = null) { + if (value != null) this.defaultHeaders[name] = value; + else delete this.defaultHeaders[name]; } - /** - * Установка или сброс заголовка - * @param name {String} Название заголовка - * @param value {String|null} Значение заголовка - */ - setHeader(name, value = null) { - if (value) { - this.defaultHeaders[name] = value; - } else if (this.defaultHeaders[name]) { - delete this.defaultHeaders[name]; - } + async request({ url, method='GET', headers={}, ...opts }) { + if (!/^(http|\/\/)/.test(url)) url = this.config.baseUrl + url; + + const langHeader = this.services.i18n.lang; + const finalHeaders = { + ...this.defaultHeaders, + 'Accept-Language': langHeader, + ...headers, + }; + + const res = await fetch(url, { method, headers: finalHeaders, ...opts }); + const data = await res.json(); + + return { data, status: res.status, headers: res.headers }; } } diff --git a/src/app/article/index.js b/src/app/article/index.js index 54f037b64..7ff8bdf73 100644 --- a/src/app/article/index.js +++ b/src/app/article/index.js @@ -1,64 +1,71 @@ -import { memo, useCallback } from 'react'; -import { useParams } from 'react-router-dom'; -import useStore from '../../hooks/use-store'; -import useTranslate from '../../hooks/use-translate'; -import useInit from '../../hooks/use-init'; -import PageLayout from '../../components/page-layout'; -import Head from '../../components/head'; -import Navigation from '../../containers/navigation'; -import Spinner from '../../components/spinner'; -import ArticleCard from '../../components/article-card'; -import LocaleSelect from '../../containers/locale-select'; -import TopHead from '../../containers/top-head'; -import { useDispatch, useSelector } from 'react-redux'; -import shallowequal from 'shallowequal'; -import articleActions from '../../store-redux/article/actions'; -import HeadLayout from '../../components/head-layout'; +import React, { memo, useCallback } from 'react' +import { useParams } from 'react-router-dom' +import useStore from '../../hooks/use-store' +import useTranslate from '../../hooks/use-translate' +import { useArticle } from '../../hooks/use-article' +import { useComments } from '../../hooks/use-comments' +import PageLayout from '../../components/page-layout' +import Head from '../../components/head' +import Spinner from '../../components/spinner' +import ArticleCard from '../../components/article-card' +import LocaleSelect from '../../containers/locale-select' +import TopHead from '../../containers/top-head' +import Navigation from '../../containers/navigation' +import HeadLayout from '../../components/head-layout' +import CommentsSection from '../../components/comments-section' function Article() { - const store = useStore(); + const store = useStore() + const { t, lang } = useTranslate() + const { id } = useParams() + const { article, waiting: artLoading } = useArticle(id) + const { + comments, waiting: comLoading, + replyTo, text, setText, + onReplyClick, onSubmit + } = useComments(id) - const dispatch = useDispatch(); - // Параметры из пути /articles/:id - - const params = useParams(); - - useInit(() => { - //store.actions.article.load(params.id); - dispatch(articleActions.load(params.id)); - }, [params.id]); - - const select = useSelector( - state => ({ - article: state.article.data, - waiting: state.article.waiting, - }), - shallowequal, - ); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект - - const { t } = useTranslate(); - - const callbacks = { - // Добавление в корзину - addToBasket: useCallback(_id => store.actions.basket.addToBasket(_id), [store]), - }; + const isAuth = Boolean(localStorage.getItem('token')) + const addToBasket = useCallback( + id => store.actions.basket.addToBasket(id), + [store] + ) return ( <> - + - - + + - - + + + +
+

{t('comments.heading')} ({comments.length})

+ + + +
- ); + ) } -export default memo(Article); +export default memo(Article) diff --git a/src/app/basket/index.js b/src/app/basket/index.js index 52a7badfd..865f3b8e1 100644 --- a/src/app/basket/index.js +++ b/src/app/basket/index.js @@ -2,7 +2,7 @@ import { memo, useCallback } from 'react'; import { useDispatch, useStore as useStoreRedux } from 'react-redux'; import useStore from '../../hooks/use-store'; import useSelector from '../../hooks/use-selector'; -import useInit from '../../hooks/use-init'; + import useTranslate from '../../hooks/use-translate'; import ItemBasket from '../../components/item-basket'; import List from '../../components/list'; @@ -21,11 +21,10 @@ function Basket() { })); const callbacks = { - // Удаление из корзины + removeFromBasket: useCallback(_id => store.actions.basket.removeFromBasket(_id), [store]), - // Закрытие любой модалки + closeModal: useCallback(() => { - //store.actions.modals.close(); dispatch(modalsActions.close()); }, [store]), }; diff --git a/src/app/index.js b/src/app/index.js index c54c2afc5..3014dc1e6 100644 --- a/src/app/index.js +++ b/src/app/index.js @@ -1,6 +1,4 @@ -import { useCallback, useContext, useEffect, useState } from 'react'; import { Routes, Route } from 'react-router-dom'; -import useSelector from '../hooks/use-selector'; import useStore from '../hooks/use-store'; import useInit from '../hooks/use-init'; import Main from './main'; @@ -21,6 +19,7 @@ function App() { await store.actions.session.remind(); }); + const activeModal = useSelectorRedux(state => state.modals.name); return ( diff --git a/src/app/profile/index.js b/src/app/profile/index.js index 166d127fb..4b6ea76c6 100644 --- a/src/app/profile/index.js +++ b/src/app/profile/index.js @@ -24,7 +24,7 @@ function Profile() { waiting: state.profile.waiting, })); - const { t } = useTranslate(); +const { t, lang } = useTranslate(); return ( <> @@ -37,7 +37,7 @@ function Profile() { - + diff --git a/src/assets/icon/cart_empty.png b/src/assets/icon/cart_empty.png new file mode 100644 index 0000000000000000000000000000000000000000..ab1c4b449d8267eaef8ab6485b0dcb7d48dbb552 GIT binary patch literal 3481 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 literal 0 HcmV?d00001 diff --git a/src/components/article-card/index.js b/src/components/article-card/index.js index d636d9f88..68875cfef 100644 --- a/src/components/article-card/index.js +++ b/src/components/article-card/index.js @@ -13,22 +13,22 @@ function ArticleCard(props) {
{article.description}
-
Страна производитель:
+
{t('article.country.dev')}:
{article.madeIn?.title} ({article.madeIn?.code})
-
Категория:
+
{t('article.product.cat')}:
{article.category?.title}
-
Год выпуска:
+
{t('article.country.dev')}:
{article.edition}
-
Цена:
+
{t('article.price')}:
{numberFormat(article.price)} ₽
+ )} + {comment.replies.length > 0 && ( +
+ {comment.replies.map(child => ( + + ))} +
+ )} + {isAuthorized && replyTo === comment.id && ( +
+

Новый ответ

+