Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,11 +19,16 @@ class APIService {
* @param options
* @returns {Promise<{}>}
*/

async request({ url, method = 'GET', headers = {}, ...options }) {
if (!url.match(/^(http|\/\/)/)) url = this.config.baseUrl + url;

// Получаем текущий язык непосредственно перед запросом
const lang = this.services.I18n.getLang();

const res = await fetch(url, {
method,
headers: { ...this.defaultHeaders, ...headers },
headers: { ...this.defaultHeaders, ...headers, 'X-Lang': lang },
...options,
});
return { data: await res.json(), status: res.status, headers: res.headers };
Expand All @@ -34,6 +39,7 @@ class APIService {
* @param name {String} Название заголовка
* @param value {String|null} Значение заголовка
*/

setHeader(name, value = null) {
if (value) {
this.defaultHeaders[name] = value;
Expand Down
18 changes: 15 additions & 3 deletions src/app/article/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useCallback } from 'react';
import { memo, useCallback, useMemo } from 'react';
import { useParams } from 'react-router-dom';
import useStore from '../../hooks/use-store';
import useTranslate from '../../hooks/use-translate';
Expand All @@ -11,12 +11,17 @@ 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 useServices from '../../hooks/use-services';
import shallowequal from 'shallowequal';
import articleActions from '../../store-redux/article/actions';
import commentActions from '../../store-redux/comments/actions';
import HeadLayout from '../../components/head-layout';
import Comments from '../../containers/comments';

function Article() {
const store = useStore();
const { I18n } = useServices();
const currentLang = I18n.getLang();

const dispatch = useDispatch();
// Параметры из пути /articles/:id
Expand All @@ -26,17 +31,20 @@ function Article() {
useInit(() => {
//store.actions.article.load(params.id);
dispatch(articleActions.load(params.id));
}, [params.id]);
dispatch(commentActions.load(params.id))
}, [params.id, currentLang]);

const select = useSelector(
state => ({
article: state.article.data,
waiting: state.article.waiting,
comments: state.comments.comments,
commentsWaiting: state.comments.waiting,
}),
shallowequal,
); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект

const { t } = useTranslate();
const t = useTranslate();

const callbacks = {
// Добавление в корзину
Expand All @@ -56,6 +64,10 @@ function Article() {
<Spinner active={select.waiting}>
<ArticleCard article={select.article} onAdd={callbacks.addToBasket} t={t} />
</Spinner>

<Spinner active={select.commentsWaiting}>
<Comments articleId={params.id} commentsList={select.comments} t={t} />
</Spinner>
</PageLayout>
</>
);
Expand Down
3 changes: 1 addition & 2 deletions src/app/basket/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ 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';
Expand Down Expand Up @@ -30,7 +29,7 @@ function Basket() {
}, [store]),
};

const { t } = useTranslate();
const t = useTranslate();

const renders = {
itemBasket: useCallback(
Expand Down
2 changes: 1 addition & 1 deletion src/app/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import HeadLayout from '../../components/head-layout';
import Form from '../../components/form';

function Login() {
const { t } = useTranslate();
const t = useTranslate();
const location = useLocation();
const navigate = useNavigate();
const store = useStore();
Expand Down
7 changes: 5 additions & 2 deletions src/app/main/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { memo } from 'react';
import useServices from '../../hooks/use-services';
import useStore from '../../hooks/use-store';
import useTranslate from '../../hooks/use-translate';
import useInit from '../../hooks/use-init';
Expand All @@ -13,16 +14,18 @@ import HeadLayout from '../../components/head-layout';

function Main() {
const store = useStore();
const { I18n } = useServices();
const currentLang = I18n.getLang();

useInit(
async () => {
await Promise.all([store.actions.catalog.initParams(), store.actions.categories.load()]);
},
[],
[currentLang],
true,
);

const { t } = useTranslate();
const t = useTranslate();

return (
<>
Expand Down
4 changes: 2 additions & 2 deletions src/app/profile/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ function Profile() {
waiting: state.profile.waiting,
}));

const { t } = useTranslate();
const t = useTranslate();

return (
<>
Expand All @@ -37,7 +37,7 @@ function Profile() {
<PageLayout>
<Navigation />
<Spinner active={select.waiting}>
<ProfileCard data={select.profile} />
<ProfileCard data={select.profile} t={t} />
</Spinner>
</PageLayout>
</>
Expand Down
10 changes: 5 additions & 5 deletions src/components/article-card/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,29 +6,29 @@ import Button from '../button';
import './style.css';

function ArticleCard(props) {
const { article, onAdd = () => {}, t = text => text } = props;
const { article, onAdd = () => { }, t = text => text } = props;
const cn = bem('ArticleCard');
return (
<div className={cn()}>
<div className={cn('description')}>{article.description}</div>
<div className={cn('prop-wrapper')}>
<div className={cn('prop')}>
<div className={cn('label')}>Страна производитель:</div>
<div className={cn('label')}>{t('article.country')}</div>
<div className={cn('value')}>
{article.madeIn?.title} ({article.madeIn?.code})
</div>
</div>
<div className={cn('prop')}>
<div className={cn('label')}>Категория:</div>
<div className={cn('label')}>{t('article.category')}</div>
<div className={cn('value')}>{article.category?.title}</div>
</div>
<div className={cn('prop')}>
<div className={cn('label')}>Год выпуска:</div>
<div className={cn('label')}>{t('article.year')}</div>
<div className={cn('value')}>{article.edition}</div>
</div>
</div>
<div className={cn('prop', { size: 'big' })}>
<div className={cn('label')}>Цена:</div>
<div className={cn('label')}>{t('article.price')}</div>
<div className={cn('value')}>{numberFormat(article.price)} ₽</div>
</div>
<Button style="primary" onClick={() => onAdd(article._id)} title={t('article.add')} />
Expand Down
5 changes: 3 additions & 2 deletions src/components/button/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,12 @@ import PropTypes from 'prop-types';
import { cn as bem } from '@bem-react/classname';
import './style.css';

function Button({ onClick = () => {}, title, style, type = 'button' }) {
function Button({ onClick = () => { }, title, style, type = 'button', className }) {
const cn = bem('Button');

return (
<div className={cn()}>
<button type={type} className={cn({ style })} onClick={() => onClick()}>{title}</button>
<button type={type} className={cn({ style }, className)} onClick={onClick}>{title}</button>
</div>
);
}
Expand All @@ -18,6 +18,7 @@ Button.propTypes = {
title: PropTypes.string,
style: PropTypes.oneOf(['text', 'primary', 'delete', 'outline']),
type: PropTypes.oneOf(['button', 'submit']),
className: PropTypes.string,
};

export default memo(Button);
31 changes: 31 additions & 0 deletions src/components/comment-form/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import React, { useState, forwardRef } from 'react';
import './style.css';
import PropTypes from 'prop-types';
import Button from '../button';

const CommentForm = forwardRef(({ onSubmit, level = 0, t }, ref) => {
const [commentText, setCommentText] = useState('');

const marginLeft = level * 40;

const handleSubmit = e => {
e.preventDefault();
onSubmit(commentText)
}

return (
<form style={{ marginLeft: marginLeft }} className='Comment-form' ref={ref}>
<h2 className='Comment-form-title'>{t('comments.formTitle')}</h2>
<textarea value={commentText} onChange={e => setCommentText(e.target.value)} className='Comment-form-field' />
<Button style='primary' title={t('comments.formSend')} type='submit' onClick={e => handleSubmit(e)} />
</form>
)
})

CommentForm.propTypes = {
onSubmit: PropTypes.func,
level: PropTypes.number,
t: PropTypes.func,
}

export default React.memo(CommentForm);
23 changes: 23 additions & 0 deletions src/components/comment-form/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
.Comment-form{
display: flex;
flex-direction: column;
gap: 16px;
margin-block: 18px;
}

.Comment-form-title{
font-size: 16px;
}

.Comment-form-field{
border: 1px solid var(--filter-border);
border-radius: 4px;
height: 80px;
resize: none;
padding: 8px 12px;
font-size: 14px;
}

.Comment-form-field:focus{
outline: 1px solid var(--primary);
}
44 changes: 44 additions & 0 deletions src/components/comment-list/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import React from 'react';
import './style.css';
import PropTypes from 'prop-types';
import Comment from '../comment';

function CommentList({ commentsList, replyToCommentId, handleReplyComment, checkAuth, t }) {

const renderComments = (comments, level = 0) => {
return comments.map((comment) => (
<React.Fragment key={comment.id || comment._id}>
<Comment
commentId={comment.id || comment._id}
username={comment.author.profile.name}
dateCreate={comment.dateCreate}
text={comment.text}
level={level}
onClick={handleReplyComment}
replyToCommentId={replyToCommentId}
checkAuth={checkAuth}
t={t}
/>
{comment.children && comment.children.length > 0 && renderComments(comment.children, level + 1)}
{replyToCommentId === comment.id || replyToCommentId === comment._id && checkAuth(level)}
</React.Fragment>
));
};

return (
<div className='Comments-list'>
{renderComments(commentsList)}
{!replyToCommentId && checkAuth()}
</div>
)
}

CommentList.propTypes = {
commentsList: PropTypes.array.isRequired,
replyToCommentId: PropTypes.string,
handleReplyComment: PropTypes.func,
checkAuth: PropTypes.func,
t: PropTypes.func,
}

export default React.memo(CommentList);
5 changes: 5 additions & 0 deletions src/components/comment-list/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
.Comments-list{
display: flex;
flex-direction: column;
gap: 16px;
}
23 changes: 23 additions & 0 deletions src/components/comment-non-session/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
import React, { forwardRef } from 'react';
import './style.css';
import PropTypes from 'prop-types';
import { Link } from 'react-router-dom';

const CommentNonSession = forwardRef(({ url = '/login', level = 0 }, ref) => {
const marginLeft = level * 40;

return (
<div style={{ marginLeft: marginLeft }} className='Comment-non-session' ref={ref}>
<Link to={url} className='Comment-non-session-link'>Войдите</Link>
<span>, чтобы иметь возможность комментировать</span>
</div>
)
})

CommentNonSession.propTypes = {
url: PropTypes.string,
level: PropTypes.number,
t: PropTypes.func,
}

export default React.memo(CommentNonSession);
7 changes: 7 additions & 0 deletions src/components/comment-non-session/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
.Comment-non-session{
margin-block: 18px;
}

.Comment-non-session-link{
color: var(--primary);
}
40 changes: 40 additions & 0 deletions src/components/comment/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
import React from 'react';
import './style.css';
import PropTypes from 'prop-types';
import Button from '../button';
import { formatDate } from '../../utils/format-date';
import CommentList from '../comment-list';

function Comment({ commentId, username, dateCreate, text, level, onClick, t }) {
const marginL = level > 8
? 7 * 40
: level * 40;


return (
<div style={{ marginLeft: marginL }} className={`Comment`}>
<div className='Comment-head'>
<span className='Comment-head-author'>{username}</span>
<span className='Comment-head-date'>{formatDate(dateCreate, t)}</span>
</div>
<div className='Comment-body'>
<p className='Comment-body-text'>{text}</p>
</div>
<Button style={'text'} title={t('comments.reply')} className={'comment-btn'} onClick={() => onClick(commentId)} />
</div>
)
}

Comment.propTypes = {
commentId: PropTypes.string,
username: PropTypes.string,
dateCreate: PropTypes.string,
text: PropTypes.string,
level: PropTypes.number.isRequired,
onClick: PropTypes.func,
handleReplyComment: PropTypes.func,
t: PropTypes.func,
}

export default React.memo(Comment)

Loading