Skip to content
Open

HW 5 #355

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
5 changes: 5 additions & 0 deletions src/api/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,12 @@ class APIService {
this.config = config;
this.defaultHeaders = {
'Content-Type': 'application/json',
'X-Lang': services.i18n.getLang(),
};

this.services.i18n.subscribe((lang) => {
return this.setHeader('X-Lang', lang);
});
}

/**
Expand Down
10 changes: 8 additions & 2 deletions src/app/article/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,14 @@ import TopHead from '../../containers/top-head';
import { useDispatch, useSelector } from 'react-redux';
import shallowequal from 'shallowequal';
import articleActions from '../../store-redux/article/actions';
import commentsActions from '../../store-redux/comments/actions';
import HeadLayout from '../../components/head-layout';
import CommentList from '../../containers/comment-list';


function Article() {
const store = useStore();
const { t, lang } = useTranslate();

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

const select = useSelector(
state => ({
Expand All @@ -36,13 +41,13 @@ function Article() {
shallowequal,
); // Нужно указать функцию для сравнения свойства объекта, так как хуком вернули объект

const { t } = useTranslate();

const callbacks = {
// Добавление в корзину
addToBasket: useCallback(_id => store.actions.basket.addToBasket(_id), [store]),
};


return (
<>
<HeadLayout>
Expand All @@ -56,6 +61,7 @@ function Article() {
<Spinner active={select.waiting}>
<ArticleCard article={select.article} onAdd={callbacks.addToBasket} t={t} />
</Spinner>
<CommentList />
</PageLayout>
</>
);
Expand Down
8 changes: 7 additions & 1 deletion src/app/login/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ function Login() {
e => {
e.preventDefault();
store.actions.session.signIn(data, () => {
console.log(location.state);
// Возврат на страницу, с которой пришли
const back =
location.state?.back && location.state?.back !== location.pathname
Expand All @@ -69,7 +70,12 @@ function Login() {
<PageLayout>
<Navigation />
<SideLayout padding="medium">
<Form onSubmit={callbacks.onSubmit} title={t('auth.title')} submitTitle={t('auth.signIn')}>
<Form
onSubmit={callbacks.onSubmit}
title={t('auth.title')}
submitTitle={t('auth.signIn')}
type="main"
>
<Field label={t('auth.login')} error={select.errors?.login}>
<Input
name="login"
Expand Down
4 changes: 2 additions & 2 deletions src/app/main/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,16 +13,16 @@ import HeadLayout from '../../components/head-layout';

function Main() {
const store = useStore();
const { t, lang } = useTranslate();

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

const { t } = useTranslate();

return (
<>
Expand Down
35 changes: 35 additions & 0 deletions src/components/comment-input/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import { memo, useCallback, useLayoutEffect, useState } from 'react';
import PropTypes from 'prop-types';
import { cn as bem } from '@bem-react/classname';

import './style.css';

function CommentInput(props) {
const { onChange = () => {}, type = 'text' } = props;

// Обработчик изменений в поле
const onChangeHandler = event => {
onChange(event.target.value);
};


const cn = bem('CommentInput');
return (
<textarea
className={cn()}
value={props.value}
type={type}
placeholder={props.placeholder}
onChange={onChangeHandler}
required
/>
);
}

CommentInput.PropTypes = {
onChange: PropTypes.func,
type: PropTypes.string,
}


export default memo(CommentInput);
21 changes: 21 additions & 0 deletions src/components/comment-input/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
.CommentInput {
padding: 8px;
border: 1px solid var(--filter-border);
border-radius: 4px;
font-family: var(--font-family);
resize: none;
height: 88px;
box-sizing: border-box;
width: 100%;
max-width: 1152px;
}

.CommentInput:focus {
outline: none;
}

.CommentInput_isFormPadding {
width: calc(100% + 40px);
max-width: calc(1152px - 40)px;
}

15 changes: 15 additions & 0 deletions src/components/comment-login-message/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import { memo } from 'react';
import { Link } from 'react-router-dom'
import './style.css';

function CommentLoginMessage(back) {
return (
<p className='Login-message'>
<Link to='/login' className='Login-link' state={back}>
Войдите
</Link>, чтобы иметь возможность комментировать
</p>
);
}

export default memo(CommentLoginMessage);
12 changes: 12 additions & 0 deletions src/components/comment-login-message/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.Login-message {
font-size: 16px;
margin-top: 16px;
margin-bottom: 0;
}

.Login-link {
color: var(--primary);
&:hover {
color: var(--main-text);
}
}
37 changes: 37 additions & 0 deletions src/components/comment/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
import { memo } from 'react';
import PropTypes from 'prop-types';
import { cn as bem } from '@bem-react/classname';
import './style.css';
import dateFormat from '../../utils/date-format';


function Comment({
onClick = () => {},
item = {},
buttonTitle,
user = '',
}) {
const cn = bem('Comment');
const isAuthor = user === item.author?.profile.name;
return (
<div className={cn()}>
<div className={cn('info')}>
<h4 className={cn('profile', { isAuthor })}>{item.author?.profile.name}</h4>
<span className={cn('date')}>{dateFormat(item.dateCreate)}</span>
</div>
<div className={cn('main')}>
<p>
{item.text}
</p>
</div>
<button className={cn('button')} onClick={() => onClick(item._id)}>{buttonTitle}</button>
</div>
);
}

Comment.propTypes = {
onClick: PropTypes.func,
buttonTitle: PropTypes.string
};

export default memo(Comment);
49 changes: 49 additions & 0 deletions src/components/comment/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
.Comment {
display: flex;
gap: 6px;
flex-direction: column;
align-items: flex-start;
font-size: 12px;
max-width: 1152px;
}

.Comment-info {
display: flex;
gap: 12px;
align-items: flex-end;
}

.Comment-profile {
font-weight: 700;
}

.Comment-date {
color: #666666;
}

.Comment-main {
overflow-wrap: break-word;
white-space: normal;
max-width: 100%;
}

.Comment-main p {
margin: 0;
font-size: 14px;
}

.Comment-profile_isAuthor {
color: #4B5563;
}

.Comment-button {
padding: 0;
border: none;
color: var(--primary);
font-size: 12px;
line-height: 18px;

&:hover {
color: var(--main-text);
}
}
21 changes: 21 additions & 0 deletions src/components/comments-layout/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import React, { memo } from 'react';
import PropTypes from 'prop-types';
import { cn as bem } from '@bem-react/classname';
import './style.css';

function CommentsLayout({ children, gap = 'large', isPaddingLeft = false }) {
const cn = bem('CommentsLayout');
return (
<div className={cn({ gap, isPaddingLeft })}>
{children}
</div>
);
}

CommentsLayout.PropTypes = {
children: PropTypes.node,
gap: PropTypes.oneOf(['medium', 'large']),
isPaddingLeft: PropTypes.bool,
}

export default memo(CommentsLayout);
17 changes: 17 additions & 0 deletions src/components/comments-layout/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
.CommentsLayout {
display: flex;
flex-direction: column;
max-width: 1192px;
}

.CommentsLayout_gap_large {
gap: 24px;
}

.CommentsLayout_gap_medium {
gap: 16px;
}

.CommentsLayout_isPaddingLeft {
padding-left: 40px;
}
13 changes: 10 additions & 3 deletions src/components/form/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,19 @@ import { cn as bem } from '@bem-react/classname';
import Button from '../button';
import './style.css';

function Form({ title, onSubmit, children, submitTitle }) {
function Form({ title, onSubmit, children, submitTitle, onCancel, cancelTitle, type }) {
const cn = bem('Form');

return (
<form className={cn()} onSubmit={onSubmit}>
<form className={cn({ type })} onSubmit={onSubmit}>
<h2 className={cn('title')}>{title}</h2>
{children}
<Button style="primary" type="submit" title={submitTitle} />
<div className={cn('actions')}>
<Button style="primary" type="submit" title={submitTitle} />
{onCancel &&
<Button style="outline" type="button" title={cancelTitle} onClick={onCancel}/>
}
</div>
</form>
);
}
Expand All @@ -21,6 +26,8 @@ Form.propTypes = {
onSubmit: PropTypes.func,
title: PropTypes.string,
submitTitle: PropTypes.string,
onCancel: PropTypes.func,
cancelTitle: PropTypes.string
};

export default memo(Form);
35 changes: 33 additions & 2 deletions src/components/form/style.css
Original file line number Diff line number Diff line change
@@ -1,7 +1,38 @@
.Form-title {
.Form_type_main h2 {
margin-bottom: 24px;
}

.Form button {
.Form_type_main button {
margin-top: 24px;
}

.Form-actions {
display: flex;
gap: 16px;
}

.Form_type_comment {
margin-top: 24px;
}

.Form_type_comment h2 {
font-size: 16px;
margin-bottom: 16px;
}

.Form_type_comment button {
margin-top: 16px;
}

.Form_type_reply {
margin-top: 8px;
}

.Form_type_reply button {
margin-top: 16px;
}

.Form_type_reply h2 {
font-size: 16px;
margin-bottom: 16px;
}
Loading