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

Dev #1194

Open
wants to merge 6 commits into
base: master
Choose a base branch
from
Open

Dev #1194

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
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,29 +9,29 @@ Install Prettier Extention and use this [VSCode settings](https://mate-academy.g

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.
- load users from the API on page load;
- implement the `UserSelector` as a dropdown using the given markup;
- load users from the API on page load;
- implement the `UserSelector` as a dropdown using the given markup;
1. When a user is selected load the user's posts form [the API](https://mate-academy.github.io/fe-students-api/) and show them using a table in the main content clock;
- show the `<Loader>` while waiting for the API response;
- show an error notification if `posts` loading fails;
- if the user has no posts show the `No posts yet` notification.
- show the `<Loader>` while waiting for the API response;
- show an error notification if `posts` loading fails;
- if the user has no posts show the `No posts yet` notification.
1. Add the `Sidebar--open` class to the sidebar when a post is selected;
- the post details should appear there immediately;
- the post commnets should be loaded from the API;
- the `Loader` is shown before comments are loaded;
- `CommentsError` notification is show on loading error;
- `NoComments` message is shown if the post does not have comments yet;
- the post details should appear there immediately;
- the post commnets should be loaded from the API;
- the `Loader` is shown before comments are loaded;
- `CommentsError` notification is show on loading error;
- `NoComments` message is shown if the post does not have comments yet;
1. Show the `Write a comment` button below the comments
- after click hide the button and show the form to add new comment;
- the form stays visible until the other post is opened;
- the form should be implemented as a separate component;
- after click hide the button and show the form to add new comment;
- the form stays visible until the other post is opened;
- the form should be implemented as a separate component;
1. The form requires an author's name and email and a comment text.
- show errors only after the form is submitted;
- remove an error on the field change;
- keep the `name` and `email` after the successful submit but clear a comment text;
- The `Clear` button should also clear all errors;
- Add the `is-loading` class to the submit button while waiting for a response;
- Add the new comment received as a response from the `API` to the end of the list;
- show errors only after the form is submitted;
- remove an error on the field change;
- keep the `name` and `email` after the successful submit but clear a comment text;
- The `Clear` button should also clear all errors;
- Add the `is-loading` class to the submit button while waiting for a response;
- Add the new comment received as a response from the `API` to the end of the list;
1. Implement comment deletion
- Delete the commnet immediately not waiting for the server response to improve the UX.
1. (*) Handle `Add` and `Delete` errors so the user can retry
- Delete the comment immediately not waiting for the server response to improve the UX.
1. (\*) Handle `Add` and `Delete` errors so the user can retry
21 changes: 12 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,15 @@
"license": "GPL-3.0",
"dependencies": {
"@fortawesome/fontawesome-free": "^6.5.2",
"bulma": "^1.0.1",
"bulma": "^0.9.4",
"classnames": "^2.5.1",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-transition-group": "^4.4.5"
},
"devDependencies": {
"@cypress/react18": "^2.0.1",
"@mate-academy/scripts": "^1.8.5",
"@mate-academy/scripts": "^1.9.12",
"@mate-academy/students-ts-config": "*",
"@mate-academy/stylelint-config": "*",
"@types/node": "^20.14.10",
Expand Down
148 changes: 111 additions & 37 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,57 +4,131 @@ import 'bulma/css/bulma.css';
import '@fortawesome/fontawesome-free/css/all.css';
import './App.scss';

import { useEffect, useState } from 'react';

import { PostsList } from './components/PostsList';
import { PostDetails } from './components/PostDetails';
import { UserSelector } from './components/UserSelector';
import { Loader } from './components/Loader';

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>
import { User } from './types/User';
import { Post } from './types/Post';
import { Errors } from './types/Errors';

<div className="block" data-cy="MainContent">
<p data-cy="NoSelectedUser">No user selected</p>
import { getUsers } from './api/users';
import { getPosts } from './api/posts';

<Loader />
export const App = () => {
const [usersList, setUsersList] = useState<User[]>([]);
const [selectedUser, setSelectedUser] = useState<User | null>(null);
const [isPostsLoading, setIsPostsLoading] = useState(false);
const [currentError, setCurrentError] = useState<Errors | null>(null);
const [userPosts, setUserPosts] = useState<Post[] | null>(null);
const [openedPost, setOpenedPost] = useState<Post | null>(null);
const [newCommentCreating, setNewCommentCreating] = useState<boolean>(false);

<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
</div>
useEffect(() => {
getUsers()
.then(setUsersList)
.catch(() => setCurrentError(Errors.UsersLoading));
}, []);

useEffect(() => {
if (!selectedUser) {
return;
}

<div className="notification is-warning" data-cy="NoPostsYet">
No posts yet
setCurrentError(null);
setUserPosts(null);
setIsPostsLoading(true);

getPosts(selectedUser.id)
.then(response => {
if (Array.isArray(response)) {
setUserPosts(response);
if (response.length === 0) {
setCurrentError(Errors.NoPosts);
}
} else {
setCurrentError(Errors.PostsLoading);
}
})
.catch(() => setCurrentError(Errors.PostsLoading))
.finally(() => {
setIsPostsLoading(false);
});
}, [selectedUser]);

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={usersList}
selectedUser={selectedUser}
setSelectedUser={setSelectedUser}
/>
</div>

<PostsList />
<div className="block" data-cy="MainContent">
{!selectedUser && (
<p data-cy="NoSelectedUser">No user selected</p>
)}

{isPostsLoading && !currentError && <Loader />}

Choose a reason for hiding this comment

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

The loader is displayed when isPostsLoading is true, but it doesn't account for currentError. This could lead to both the loader and an error message being displayed simultaneously. Consider updating the condition to ensure these states are mutually exclusive.


{currentError === Errors.PostsLoading && (
<div
className="notification is-danger"
data-cy="PostsLoadingError"
>
Something went wrong!
</div>
)}
Comment on lines +83 to +90

Choose a reason for hiding this comment

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

The error message for post loading is displayed when currentError is Errors.PostsLoading. Ensure that this condition is mutually exclusive with the loader to avoid confusing the user.


{currentError === Errors.NoPosts && (
<div className="notification is-warning" data-cy="NoPostsYet">
No posts yet
</div>
)}

{userPosts && userPosts.length > 0 && (
<PostsList
posts={userPosts}
openedPost={openedPost}
setOpenedPost={setOpenedPost}
setNewCommentCreating={setNewCommentCreating}
/>
)}
</div>
</div>
</div>
</div>

<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
'Sidebar--open',
)}
>
<div className="tile is-child box is-success ">
<PostDetails />
<div
data-cy="Sidebar"
className={classNames(
'tile',
'is-parent',
'is-8-desktop',
'Sidebar',
{ 'Sidebar--open': openedPost },
)}
>
<div className="tile is-child box is-success">
{openedPost && (
<PostDetails
post={openedPost}
newCommentCreating={newCommentCreating}
setNewCommentCreating={setNewCommentCreating}
/>
)}
</div>
</div>
</div>
</div>
</div>
</main>
);
</main>
);
};
10 changes: 10 additions & 0 deletions src/api/comments.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import { Comment } from '../types/Comment';
import { client } from '../utils/fetchClient';

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

Choose a reason for hiding this comment

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

Consider adding error handling for the getComments API call. This can be done by chaining a .catch block to handle potential errors during the request.

};

export const deleteComment = (commentId: number) => {
return client.delete(`/comments/${commentId}`);

Choose a reason for hiding this comment

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

Consider adding error handling for the deleteComment API call. Adding a .catch block will help manage errors and provide feedback to the user if the deletion fails.

};
6 changes: 6 additions & 0 deletions src/api/newComment.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Comment } from '../types/Comment';
import { client } from '../utils/fetchClient';

export const newComment = (comment: Partial<Comment>) => {
return client.post('/comments', comment);

Choose a reason for hiding this comment

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

Consider adding error handling for the newComment API call. Including a .catch block will help manage errors during the request and provide feedback to the user if the comment creation fails.

};
6 changes: 6 additions & 0 deletions src/api/posts.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { Post } from '../types/Post';
import { client } from '../utils/fetchClient';

export const getPosts = (userId: number) => {
return client.get<Post[]>(`/posts?userId=${userId}`);

Choose a reason for hiding this comment

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

Consider adding error handling for the getPosts API call. Adding a .catch block will help manage potential errors during the request and provide feedback to the user if the post retrieval fails.

};
6 changes: 6 additions & 0 deletions src/api/users.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import { User } from '../types/User';
import { client } from '../utils/fetchClient';

export const getUsers = () => {
return client.get<User[]>(`/users`);

Choose a reason for hiding this comment

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

Consider adding error handling for the getUsers API call. Including a .catch block will help manage potential errors during the request and provide feedback to the user if the user retrieval fails.

};
Loading
Loading