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

add dynamic list of post task #1206

Open
wants to merge 4 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
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
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ form to add new comments.
Install Prettier Extention and use this [VSCode settings](https://mate-academy.github.io/fe-program/tools/vscode/settings.json) to enable format on save.

> Here is [the working version](https://mate-academy.github.io/react_dynamic-list-of-posts/)
Demo: (https://moskkat40/react_dynamic-list-of-posts/)

1. Learn the `utils/fetchClient.ts` and use it to interact with the API (tests expect that you each API request is sent after 300 ms delay);
1. Initially the `App` shows the `UserSelector` and a paragraph `No user selected` in the main content block.
Expand Down
200 changes: 162 additions & 38 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,53 +8,177 @@
import { PostDetails } from './components/PostDetails';
import { UserSelector } from './components/UserSelector';
import { Loader } from './components/Loader';
import { useEffect, useMemo, useState } from 'react';
import * as servicesUsers from './api/users';
import * as servicesPosts from './api/posts';
import * as servicesComments from './api/comments';
import { Post } from './types/Post';
import { Comment } from './types/Comment';

export const App = () => (
<main className="section">
<div className="container">
<div className="tile is-ancestor">
<div className="tile is-parent">
<div className="tile is-child box is-success">
<div className="block">
<UserSelector />
</div>
export const App = () => {
const [users, setUsers] = useState([]);
const [isUser, setIsUser] = useState(false);
const [posts, setPosts] = useState([]);
const [comments, setComments] = useState([]);
const [currentPostId, setCurrentPostId] = useState(0);
const [openPostId, setOpenPostId] = useState<number | null>(null);
const [isWritePostButton, setIsWritePostButton] = useState(false);
const [loading, setLoading] = useState('');
const [isError, setIsError] = useState(false);

<div className="block" data-cy="MainContent">
<p data-cy="NoSelectedUser">No user selected</p>
useEffect(() => {
setLoading('users');
servicesUsers
.getUsers()
.then(res => {
setUsers(res);
setIsError(false);
})
.catch(() => setIsError(true))
.finally(() => setLoading(''));
}, []);

<Loader />
const getPostsByUserId = (id: number) => {
setLoading('posts');
setIsUser(true);

<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
</div>
return servicesPosts
.getPosts(id)
.then(res => {
setPosts(res);
setIsError(false);
})
.catch(() => setIsError(true))
.finally(() => setLoading(''));
};

const getCommentsByPostId = (postId: number) => {
setLoading('comments');
setCurrentPostId(postId);

servicesComments
.getComments(postId)
.catch(() => setIsError(true))
.then(res => {
setComments(res);
setIsError(false);
})

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The order of .then and .catch in the getCommentsByPostId function is incorrect. According to the checklist, .then should come before .catch to handle the resolved promise before handling any errors. Please switch the order of these methods to ensure proper error handling.

.finally(() => setLoading(''));
};

const addComment = ({ name, email, body, postId }: Comment) => {
setLoading('addComment');

servicesComments
.createComment({ name, email, body, postId })
.then(newComment => {
setComments(currentComments => [...currentComments, newComment]);
})
.finally(() => setLoading(''));
};

const deleteComment = (commentId: number) => {
servicesComments
.deleteComment(commentId)
.then(res =>

Check failure on line 83 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

'res' is defined but never used
setComments(currentComments =>
currentComments.filter(comment => comment.id !== commentId),
),
);
};

const currentPost = useMemo(() => {
return posts.find((post: Post) => post.id === currentPostId);

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Consider returning null when currentPost is not found. This will prevent potential issues when currentPost is used elsewhere in the code.

}, [currentPostId, posts]);

console.log(isError);

Check failure on line 94 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Unexpected console statement

<div className="notification is-warning" data-cy="NoPostsYet">
No posts yet
return (
<main className="section">
<div className="container">
<div className="tile is-ancestor">
<div className="tile is-parent">
<div className="tile is-child box is-success">
<div className="block">
<UserSelector
users={users}
setIsUser={setIsUser}
isUser={isUser}
getPostsByUserId={getPostsByUserId}
setOpenPostId={setOpenPostId}
/>
</div>
{loading === 'users' && <Loader />}
<div className="block" data-cy="MainContent">
<p data-cy="NoSelectedUser">
{!isUser && loading !== 'users' && 'No user selected'}
</p>

<PostsList />
{loading === 'posts' && <Loader />}
{isError && (
<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
</div>
)}

{posts.length === 0 &&
isUser &&
loading !== 'posts' &&
!isError && (
<div

Check failure on line 131 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 18 spaces but found 20
className="notification is-warning"

Check failure on line 132 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 20 spaces but found 22
data-cy="NoPostsYet"

Check failure on line 133 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 20 spaces but found 22
>

Check failure on line 134 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 18 spaces but found 20
No posts yet
</div>

Check failure on line 136 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 18 spaces but found 20
)}

Check failure on line 137 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 16 spaces but found 18

{isUser &&
posts.length > 0 &&
loading !== 'posts' &&
!isError && (
<PostsList

Check failure on line 143 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 18 spaces but found 20
posts={posts}

Check failure on line 144 in src/App.tsx

View workflow job for this annotation

GitHub Actions / run_linter (20.x)

Expected indentation of 20 spaces but found 22
getCommentsByPostId={getCommentsByPostId}
openPostId={openPostId}
setOpenPostId={setOpenPostId}
setIsWritePostButton={setIsWritePostButton}
/>
)}
</div>
</div>
</div>
</div>

<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
{posts.length > 0 && openPostId && (
<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
)}
>
<div className="tile is-child box is-success ">
<PostDetails
comments={comments}
currentPost={currentPost}
addComment={addComment}
isWritePostButton={isWritePostButton}
setIsWritePostButton={setIsWritePostButton}
deleteComment={deleteComment}
loadingComments={loading === 'comments'}
loadingAddNewComment={loading === 'addComment'}
isError={isError}
/>
</div>
</div>
)}
>
<div className="tile is-child box is-success ">
<PostDetails />
</div>
</div>
</div>
</div>
</main>
);
</main>
);
};
19 changes: 19 additions & 0 deletions src/api/comments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Comment } from '../types/Comment';
import { client } from '../utils/fetchClient';

export const getComments = (postId: number) => {
return client.get(`/comments?postId=${postId}`);
};

export const createComment = ({
name,
email,
body,
postId,
}: Omit<Comment, 'id'>) => {
return client.post<Comment>('/comments', { name, email, body, postId });
};

export const deleteComment = (commentId: number) => {
return client.delete(`/comments/${commentId}`);
};
5 changes: 5 additions & 0 deletions src/api/posts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { client } from '../utils/fetchClient';

export const getPosts = (userId: number) => {
return client.get(`/posts?userId=${userId}`);
};
5 changes: 5 additions & 0 deletions src/api/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { client } from '../utils/fetchClient';

export const getUsers = () => {
return client.get('/users');
};
Loading
Loading