Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ДЗ Lecture 5 #409

Open
wants to merge 38 commits into
base: lecture-5
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
38 commits
Select commit Hold shift + click to select a range
e76a78b
editorconfig
VladimirShestakov Nov 28, 2023
1fa7f72
editorconfig and vercel rewrites
VladimirShestakov Dec 5, 2023
0fe02a7
updates packages
VladimirShestakov Dec 12, 2023
3a4888c
fix pagination
VladimirShestakov Dec 12, 2023
2081cab
fix ci
VladimirShestakov Feb 21, 2024
6aee3f6
fix ci
VladimirShestakov Feb 29, 2024
965c1b1
fix ci
VladimirShestakov Mar 7, 2024
4ba4392
fix ci
VladimirShestakov Mar 14, 2024
1f3b265
added prettier
VladimirShestakov Sep 9, 2024
1a3af94
packages updated
VladimirShestakov Sep 9, 2024
b54a3a6
packages updated
VladimirShestakov Sep 9, 2024
9f9aa42
packages updated, using query.rest api
VladimirShestakov Sep 9, 2024
3a3cf92
packages & ci updated, using query.rest api
VladimirShestakov Sep 9, 2024
0327606
fix packages
VladimirShestakov Sep 9, 2024
7b4e38f
feat: unselect for note; set unique code for new note; set and output…
Rednata Sep 10, 2024
ed6c78a
fix: unselect record when click on delete btn; title for selected record
Rednata Sep 13, 2024
35bf00c
fix packages
VladimirShestakov Sep 16, 2024
7ca6a80
Merge branch 'lecture-2' of https://github.com/ylabio/react-webinar-3…
Rednata Sep 17, 2024
8e40030
remove defaultProps, count for records & select for records. Add: spl…
Rednata Sep 17, 2024
0a06c83
feat: add styles and close/open for Modal
Rednata Sep 18, 2024
066c4b2
feat: create cart in state; reuse for Controls-component; add items t…
Rednata Sep 19, 2024
e79c1d1
feat: count items and sum of items in cart
Rednata Sep 19, 2024
d8901f1
feat: delete items from cart
Rednata Sep 19, 2024
30b29e8
feat: add PropTypes for props
Rednata Sep 19, 2024
832a1f7
refactor function "addItemToCart"
Rednata Sep 19, 2024
375f5d9
refactor: style for Modal, functions in store (addItemToCat, getSumOf…
Rednata Sep 21, 2024
20229c3
feat: add ItemCart component and render different item-components in …
Rednata Sep 22, 2024
b5bf938
fix packages
VladimirShestakov Sep 23, 2024
4762858
merge
Rednata Sep 25, 2024
64093f3
resolve conflicts
Rednata Sep 25, 2024
1fe229b
fix packages
VladimirShestakov Sep 30, 2024
7cac612
fix api-examples
VladimirShestakov Sep 30, 2024
520e18d
resolve conflict in controls>style.css
Rednata Oct 1, 2024
9d3e64a
Merge remote-tracking branch 'upstream/lecture-4' into lecture-4
Rednata Oct 2, 2024
ac644c5
Merge remote-tracking branch 'upstream/lecture-5' into lecture-5
Rednata Oct 8, 2024
d45f7ad
get comments and style them
Rednata Oct 10, 2024
de25d9c
add ansqwer comment
Rednata Oct 10, 2024
57ff0a5
reload for page after post comment
Rednata Oct 10, 2024
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,548 changes: 650 additions & 7,898 deletions package-lock.json

Large diffs are not rendered by default.

7 changes: 4 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"description": "Example project for webinar",
"main": "index.js",
"scripts": {
"start": "webpack serve",
"start": "webpack serve --open",
"build": "cross-env NODE_ENV=production webpack",
"test": "jest"
},
Expand All @@ -20,6 +20,7 @@
"homepage": "https://github.com/ylabio/react-webinar-3#readme",
"dependencies": {
"@bem-react/classname": "^1.6.0",
"@redux-devtools/extension": "^3.3.0",
"lodash.debounce": "^4.0.8",
"lodash.throttle": "^4.1.1",
"prop-types": "^15.8.1",
Expand All @@ -41,9 +42,9 @@
"html-webpack-plugin": "^5.6.0",
"jest": "^29.7.0",
"mini-css-extract-plugin": "^2.9.1",
"prettier": "3.3.3",
"webpack": "^5.94.0",
"webpack-cli": "^5.1.4",
"webpack-dev-server": "^5.1.0",
"prettier": "3.3.3"
"webpack-dev-server": "^5.1.0"
}
}
94 changes: 92 additions & 2 deletions src/app/article/index.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { memo, useCallback, useMemo } from 'react';
import { memo, useCallback, useEffect, useMemo, useState } from 'react';
import { useParams } from 'react-router-dom';
import useStore from '../../hooks/use-store';
import useTranslate from '../../hooks/use-translate';
Expand All @@ -13,6 +13,12 @@ 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 userCommentActions from '../../store-redux/user-comment/actions';
import CommentsList from '../../containers/comments-list';
import useSelectorCustom from '../../hooks/use-selector';
import NonAuth from '../../components/non-auth';
import CommentFormLayout from '../../components/comment-form-layout';

function Article() {
const store = useStore();
Expand All @@ -23,23 +29,69 @@ function Article() {
const params = useParams();

useInit(() => {
//store.actions.article.load(params.id);
dispatch(articleActions.load(params.id));
dispatch(commentsActions.load(params.id));
}, [params.id]);

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

const selectCustom = useSelectorCustom(state => ({
exists: state.session.exists,
}));

const [answer, setAnswer] = useState('')
const [newComment, setNewComment] = useState('')
const [commentId, setCommentId] = useState('');
const [placeholder, setPlaceholder] = useState('');

const { t } = useTranslate();

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

// Колбэк на ввод в форме отправки нового комментария
onChangeNewComment: (value) => {
setNewComment(value)
},

// Колбэк на ввод в форме отправки ответа на комментарий
onChangeAnswer: useCallback(value => setAnswer(value), []),

// Отправка нового комментария
onSubmitNewComment: useCallback(e => {
e.preventDefault();
const data = {text: newComment, parent: {'_id': params.id, '_type': 'article'}};
dispatch(userCommentActions.post(data, () => dispatch(commentsActions.load(params.id))));
setNewComment('');
}, [newComment]),

// Отправка ответа на комментарий
onSubmitAnswer: useCallback(e => {
e.preventDefault();
const data = {text: answer, parent: {'_id': commentId, '_type': 'comment'}};
dispatch(userCommentActions.post(data, () => dispatch(commentsActions.load(params.id))));
setAnswer('');
}, [answer]),

// Закрытие формы отправки ответа на комментарий
onCloseForm: useCallback(e => {
dispatch(commentsActions.update(commentId))
}, [store]),

// Показать / скрыть форму для выбранного комментария
showAnsqwerForm: useCallback(item => {
dispatch(commentsActions.update(item.value))
setCommentId(item.value);
setPlaceholder(`Мой ответ для ${item.author.profile.name}`);
}, [store])
};

return (
Expand All @@ -51,6 +103,44 @@ function Article() {
<Navigation />
<Spinner active={select.waiting}>
<ArticleCard article={select.article} onAdd={callbacks.addToBasket} t={t} />
{
select.comments.length
? (
<CommentsList items={select.comments} onClick={callbacks.showAnsqwerForm}>
{
selectCustom.exists
? <CommentFormLayout
padding='small'
name='answer'
value={answer}
title='Новый ответ'
placeholder={placeholder}
onChange={callbacks.onChangeAnswer}
onSubmit={callbacks.onSubmitAnswer}
>
<button type='submit' form='form-comment'>Отправить</button>
<button type='button' onClick={callbacks.onCloseForm} >Отменить</button>
</CommentFormLayout>
: <NonAuth />
}
</CommentsList>
)
: ''
}
{
selectCustom.exists
? <CommentFormLayout
value={newComment}
name='newComment'
title='Новый комментарий'
placeholder='Текст'
onChange={callbacks.onChangeNewComment}
onSubmit={callbacks.onSubmitNewComment}
>
<button type='submit' form='form-comment' >Отправить</button>
</CommentFormLayout>
: <NonAuth />
}
</Spinner>
</PageLayout>
);
Expand Down
50 changes: 50 additions & 0 deletions src/components/cart/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import React from "react";
import PropTypes from 'prop-types';
import './style.css';
import Head from "../head";
import Controls from "../controls";
import List from "../list";
import { separateDigits } from "../../utils";

function Cart({
title="", sum=0, countCart=0, controlTitle="", controlFunc=()=> {}, children=""
}) {

return (
<div className="Cart">
<div className="Cart-head">
<Head title={title}></Head>
<Controls controlTitle={controlTitle} controlFunc={controlFunc}/>
</div>
<div className="Cart-content">
{countCart ?
children :
<p className="Cart-empty">В корзине пусто</p>
}
</div>
<div className="Cart-footer">
{sum ?
(
<>
<span>Итого </span>
<span>{separateDigits(sum)}&nbsp;₽</span>
</>
) : (
''
)
}
</div>
</div>
)
}

export default React.memo(Cart);

Cart.propTypes = {
title: PropTypes.string,
sum: PropTypes.number,
countCart: PropTypes.number,
controlTitle: PropTypes.string,
controlFunc: PropTypes.func,
children: PropTypes.node,
};
37 changes: 37 additions & 0 deletions src/components/cart/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
.Cart {
padding-bottom: 70px;
}

.Cart-content {
padding-top: 79px;
}

.Cart-head {
display: flex;
justify-content: space-between;
align-items: center;
border-radius: 10px 10px 0 0;
background-color: #f5f5f5;
}

.Cart-head .Controls {
display: block;
}

.Cart-empty {
text-align: center;
font-weight: 700;
font-size: 26px;
}

.Cart-footer {
display: flex;
justify-content: flex-end;
column-gap: 50px;
font-weight: 700;
font-size: 18px;
padding-right: 120px;
padding-top: 20px;
padding-bottom: 20px;
/* padding-bottom: 40px; */
}
44 changes: 44 additions & 0 deletions src/components/comment-form-layout/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { memo } from 'react';
import PropTypes from 'prop-types';
import { cn as bem } from '@bem-react/classname';
import './style.css';

function CommentFormLayout({title, padding, value, placeholder='Текст', onSubmit, onChange, children }) {

const cn = bem('CommentFormLayout');

const callbacks = {
onChange: (e) => onChange(e.target.value),
}

return (
<div className={cn({padding})}>
<p className={cn('title')}>{title}</p>
<form className={cn('form')} onSubmit={onSubmit} id='form-comment'>
<textarea
className={cn('textarea')}
name="text"
placeholder={placeholder}
value={value}
onChange={callbacks.onChange} />
</form>
<div className={cn('btns')}>
{children}
</div>
</div>

);
}

// CommentForm.propTypes = {
// items: PropTypes.arrayOf(
// PropTypes.shape({
// key: PropTypes.number,
// link: PropTypes.string,
// title: PropTypes.string,
// }),
// ),
// onNavigate: PropTypes.func,
// };

export default memo(CommentFormLayout);
48 changes: 48 additions & 0 deletions src/components/comment-form-layout/style.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
.CommentFormLayout {
display: flex;
flex-direction: column;
gap: 10px;
padding: 0 20px 20px 20px;
}

.CommentFormLayout-title {
font-weight: 700;
font-size: 12px;
color: #000;
margin: 0;
}


.CommentFormLayout-textarea {
width: 100%;
box-shadow: inset 0 1px 4px 0 rgba(102, 102, 102, 0.1);
background: #fff;
padding: 5px;
}

.CommentFormLayout-textarea::placeholder {
font-weight: 400;
font-size: 14px;
color: #000;
}

.CommentFormLayout-btns {
display: flex;
gap: 10px;
padding-bottom: 30px;
}

.CommentFormLayout-btns>button {
width: max-content;
padding: 2px 8px;
background-color: #efefef;
border-radius: 2px;
border: 1px solid #767676;
min-height: 21px;
font-size: 13px;
}

.CommentFormLayout_padding_small {
padding-left: 0;
padding-bottom: 0;
}
33 changes: 33 additions & 0 deletions src/components/comment-item/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import { memo, useState } from 'react';
import { cn as bem } from '@bem-react/classname';
import './style.css';

function CommentItem({ item, children, onClick }) {
const cn = bem('CommentItem');

const callbacks = {
onClick: () => onClick(item)
}

return (
<li className={cn('item')} style={{marginLeft: (item.level-1)*30 + 'px'}}>
<div className={cn('header')}>
<span className={cn('author')}>{item.author.profile.name}</span>
<span className={cn('date')}>{item.date}</span>
</div>
<div className={cn('body')}>
<div className="" >{item.text}</div>
</div>
<div className={cn('footer')}>
<button
className={cn('btn')}
onClick={callbacks.onClick}
>Ответить</button>
{item.showAnsqwer && children}
</div>

</li>
)
}

export default memo(CommentItem)
Loading
Loading