0 && level < 10 })}>
+
+
+ {comment?.author?.profile.name}
+ {new Date(comment.dateCreate).toLocaleString()}
+
+
{comment.text}
+
+
+ {children?.map(child => (
+
+ ))}
+ {replyToCommentId === comment._id && isLogin && (
+
+ )}
+ {replyToCommentId === comment._id && !isLogin && loginComment}
+
+ );
+}
+
+Comment.propTypes = {
+ comment: PropTypes.object,
+ comments: PropTypes.array,
+ level: PropTypes.number,
+ isLogin: PropTypes.bool,
+ loginComment: PropTypes.node,
+ replyToCommentId: PropTypes.string,
+ setReplyToCommentId: PropTypes.func,
+ onSendReply: PropTypes.func,
+};
+
+export default memo(Comment);
diff --git a/src/components/comment/style.css b/src/components/comment/style.css
new file mode 100644
index 000000000..55a39e3c5
--- /dev/null
+++ b/src/components/comment/style.css
@@ -0,0 +1,47 @@
+.Comment {
+}
+
+.Comment-content {
+ display: flex;
+ flex-direction: column;
+ gap: 6px;
+}
+
+.Comment-wrapper_nested {
+ margin-left: 40px;
+}
+
+.Comment-name {
+ margin: 0;
+ padding: 0;
+ font-family: "Golos Text";
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: 18px;
+ color: #4B5563;
+}
+
+.Comment-date {
+ margin: 0;
+ padding: 0;
+ font-family: "Golos Text";
+ font-size: 12px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 18px;
+ color: #666;
+}
+
+.Comment-text {
+ margin: 0;
+ padding: 0;
+ max-width: 100%;
+ word-wrap: break-word;
+ overflow-wrap: break-word;
+ font-family: "Golos Text";
+ font-size: 14px;
+ font-style: normal;
+ font-weight: 400;
+ line-height: 20px;
+}
diff --git a/src/components/comments-card/index.js b/src/components/comments-card/index.js
new file mode 100644
index 000000000..68fc3a1d2
--- /dev/null
+++ b/src/components/comments-card/index.js
@@ -0,0 +1,117 @@
+import {memo, useCallback, useEffect, useRef, useState} from 'react';
+import PropTypes from 'prop-types';
+import { cn as bem } from '@bem-react/classname';
+import numberFormat from '../../utils/number-format';
+import Button from '../button';
+import './style.css';
+import useTranslate from '../../hooks/use-translate';
+import { useLocation, useNavigate } from 'react-router-dom';
+import useStore from '../../hooks/use-store';
+import useSelector from '../../hooks/use-selector';
+import Comment from '../comment';
+import ReplyForm from '../reply-form';
+import commentsActions from '../../store-redux/comments/actions';
+import { useDispatch } from 'react-redux';
+import listToTree from '../../utils/list-to-tree';
+
+function CommentsCard(props) {
+ // const { article, onAdd = () => {}, t = text => text } = props;
+ const replyLoginRef = useRef(null);
+
+ const cn = bem('CommentsCard');
+ const { t } = useTranslate();
+ const dispatch = useDispatch();
+
+ const navigate = useNavigate();
+ const location = useLocation();
+ const store = useStore();
+
+ const select = useSelector(state => ({
+ exists: state.session.exists,
+ }));
+
+ const [replyToCommentId, setReplyToCommentId] = useState(null);
+
+ // const rootComments = props.comments.filter(
+ // comment => comment.parent._type === 'article' && !comment.isDeleted,
+ // );
+
+ const rootComments = listToTree(props.comments);
+
+ // console.log('Comment Root ----');
+ // console.log(rootComments);
+
+ const callbacks = {
+ // Переход к авторизации
+ onSignIn: useCallback(() => {
+ navigate('/login', { state: { back: location.pathname } });
+ }, [location.pathname]),
+
+ // Отправка комментария
+ onSendReply: useCallback(data => dispatch(commentsActions.reply(data)), [replyToCommentId]),
+ };
+
+ const loginComment = (
+
+
Комментарии ({props.count})
+
+ {rootComments.length > 0 &&
+ rootComments.map(comment => (
+
+ ))}
+
+ {!select.exists && loginComment}
+ {select.exists && !replyToCommentId && !props.isCommentsWait && (
+
{}}
+ type={'article'}
+ _id={props.article._id}
+ onSendReply={callbacks.onSendReply}
+ />
+ )}
+
+ );
+}
+
+CommentsCard.propTypes = {
+ article: PropTypes.object,
+ comments: PropTypes.array,
+ count: PropTypes.number,
+ isCommentsWait: PropTypes.bool
+};
+
+export default memo(CommentsCard);
diff --git a/src/components/comments-card/style.css b/src/components/comments-card/style.css
new file mode 100644
index 000000000..f8e3db9c4
--- /dev/null
+++ b/src/components/comments-card/style.css
@@ -0,0 +1,27 @@
+.CommentsCard {
+ display: flex;
+ flex-direction: column;
+ padding: 16px 0;
+ gap: 24px;
+}
+
+.CommentsCard-comments {
+ display: flex;
+ flex-direction: column;
+ row-gap: 16px;
+}
+
+.CommentsCard-title {
+ font-family: "Montserrat Alternates";
+ font-size: 24px;
+ font-style: normal;
+ font-weight: 700;
+ line-height: 32px;
+}
+
+.CommentsCard-login {
+ margin: 16px 0;
+ display: flex;
+ align-items: center;
+}
+
diff --git a/src/components/reply-form/index.js b/src/components/reply-form/index.js
new file mode 100644
index 000000000..10939ee46
--- /dev/null
+++ b/src/components/reply-form/index.js
@@ -0,0 +1,81 @@
+import { memo, useCallback, useState } from 'react';
+import useTranslate from '../../hooks/use-translate';
+import { useLocation, useNavigate } from 'react-router-dom';
+import useStore from '../../hooks/use-store';
+import useSelector from '../../hooks/use-selector';
+import useInit from '../../hooks/use-init';
+import Button from '../button';
+import { cn as bem } from '@bem-react/classname';
+import './style.css';
+import PropTypes from 'prop-types';
+
+function ReplyForm(props) {
+ const { t } = useTranslate();
+ const location = useLocation();
+ const navigate = useNavigate();
+ const store = useStore();
+
+ const cn = bem('ReplyForm');
+
+ useInit(() => {});
+
+ const select = useSelector(state => ({
+ waiting: state.session.waiting,
+ errors: state.session.errors,
+ }));
+
+ const [data, setData] = useState({
+ text: '',
+ parent: { _id: props._id, _type: props.type },
+ });
+
+ const callbacks = {
+ // Колбэк на ввод в элементах формы
+ onChange: useCallback((value, name) => {
+ setData(prevData => ({ ...prevData, [name]: value }));
+ }, []),
+
+ // Отправка данных формы для авторизации
+ onSubmit: useCallback(
+ e => {
+ e.preventDefault();
+ if (data.text.trim() !== '') {
+ props.onSendReply(data);
+ setData(prevData => ({ ...prevData, text: '' }));
+ props.onChancel();
+ }
+ },
+ [data, props.onSendReply, props.onChancel],
+ ),
+ };
+
+ return (
+ <>
+