-
Notifications
You must be signed in to change notification settings - Fork 1.3k
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
Solution #1201
base: master
Are you sure you want to change the base?
Solution #1201
Changes from 2 commits
de70568
32347c7
2f08574
17a3b25
55e5a34
21a3bc3
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,3 +1,4 @@ | ||
import { useEffect, useState } from 'react'; | ||
import classNames from 'classnames'; | ||
|
||
import 'bulma/css/bulma.css'; | ||
|
@@ -9,52 +10,192 @@ 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 { getUsers } from './api/user'; | ||
import { getPosts } from './api/post'; | ||
import { addComment, deleteComment, getComments } from './api/comment'; | ||
import { Comment } from './types/Comment'; | ||
|
||
export const App = () => { | ||
const [selectedUser, setSelectedUser] = useState<User | null>(null); | ||
const [users, setUsers] = useState<User[]>([]); | ||
const [isLoadingUsers, setIsLoadingUsers] = useState(false); | ||
const [isLoadingPosts, setIsLoadingPosts] = useState(false); | ||
const [isLoadingComments, setIsLoadingComments] = useState(false); | ||
const [error, setError] = useState<string | null>(null); | ||
const [selectedPost, setSelectedPost] = useState<Post | null>(null); | ||
const [posts, setPosts] = useState<Post[]>([]); | ||
const [comments, setComments] = useState<Comment[]>([]); | ||
const [isFormOpen, setIsFormOpen] = useState(false); | ||
|
||
useEffect(() => { | ||
const fetchUsers = async () => { | ||
setIsLoadingUsers(true); | ||
try { | ||
const usersData = await getUsers(); | ||
|
||
setUsers(usersData); | ||
setError(null); | ||
} catch (err) { | ||
setError('Failed to load users'); | ||
} finally { | ||
setIsLoadingUsers(false); | ||
} | ||
}; | ||
|
||
fetchUsers(); | ||
}, []); | ||
|
||
useEffect(() => { | ||
const fetchPosts = async () => { | ||
setIsLoadingPosts(true); | ||
|
||
try { | ||
if (selectedUser) { | ||
const userPosts = await getPosts(selectedUser.id); | ||
|
||
setPosts(userPosts); | ||
setError(null); | ||
} | ||
} catch (err) { | ||
setError('Failed to load posts'); | ||
} finally { | ||
setIsLoadingPosts(false); | ||
} | ||
}; | ||
|
||
fetchPosts(); | ||
}, [selectedUser]); | ||
|
||
<div className="block" data-cy="MainContent"> | ||
<p data-cy="NoSelectedUser">No user selected</p> | ||
useEffect(() => { | ||
const fetchComments = async () => { | ||
setIsLoadingComments(true); | ||
try { | ||
if (selectedPost) { | ||
const postComments = await getComments(selectedPost.id); | ||
|
||
<Loader /> | ||
setComments(postComments); | ||
setError(null); | ||
} | ||
} catch (err) { | ||
setError('Failed to load comments'); | ||
} finally { | ||
setIsLoadingComments(false); | ||
} | ||
}; | ||
|
||
<div | ||
className="notification is-danger" | ||
data-cy="PostsLoadingError" | ||
> | ||
Something went wrong! | ||
fetchComments(); | ||
}, [selectedPost]); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Implement a 300 ms delay for API requests to align with the task requirements. This can be achieved using a |
||
|
||
const handleDeleteComment = async (commentId: number) => { | ||
try { | ||
await deleteComment(commentId); | ||
setComments(currentComments => | ||
currentComments.filter(comment => comment.id !== commentId), | ||
); | ||
} catch (err) { | ||
setError('Unable to delete comment'); | ||
} | ||
}; | ||
|
||
const handleAddComment = async (comment: Comment) => { | ||
setIsLoadingComments(true); | ||
try { | ||
const newComment: Comment = await addComment({ | ||
postId: comment.postId, | ||
name: comment.name, | ||
email: comment.email, | ||
body: comment.body, | ||
}); | ||
|
||
setComments(prevComments => [...prevComments, newComment]); | ||
} catch (err) { | ||
setError('Unable to add a comment'); | ||
} finally { | ||
setIsLoadingComments(false); | ||
} | ||
Comment on lines
+102
to
+117
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. To improve error handling in the |
||
}; | ||
|
||
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"> | ||
{isLoadingUsers || isLoadingPosts ? ( | ||
<Loader /> | ||
) : ( | ||
<UserSelector | ||
selectedUser={selectedUser} | ||
users={users} | ||
onSelect={setSelectedUser} | ||
/> | ||
)} | ||
</div> | ||
|
||
<div className="notification is-warning" data-cy="NoPostsYet"> | ||
No posts yet | ||
<div className="block" data-cy="MainContent"> | ||
{selectedUser && !isLoadingPosts && !error ? ( | ||
posts.length > 0 ? ( | ||
<PostsList | ||
posts={posts} | ||
selectedPost={selectedPost} | ||
onOpen={setSelectedPost} | ||
onClose={() => { | ||
setSelectedPost(null); | ||
}} | ||
/> | ||
) : ( | ||
<div | ||
className="notification is-warning" | ||
data-cy="NoPostsYet" | ||
> | ||
No posts yet | ||
</div> | ||
) | ||
) : ( | ||
<p data-cy="NoSelectedUser">No user selected</p> | ||
)} | ||
</div> | ||
|
||
<PostsList /> | ||
{error && ( | ||
<div | ||
className="notification is-danger" | ||
data-cy="PostsLoadingError" | ||
> | ||
{error} | ||
</div> | ||
)} | ||
</div> | ||
</div> | ||
</div> | ||
|
||
<div | ||
data-cy="Sidebar" | ||
className={classNames( | ||
'tile', | ||
'is-parent', | ||
'is-8-desktop', | ||
'Sidebar', | ||
'Sidebar--open', | ||
{selectedPost && ( | ||
<div | ||
data-cy="Sidebar" | ||
className={classNames( | ||
'tile', | ||
'is-parent', | ||
'is-8-desktop', | ||
'Sidebar', | ||
'Sidebar--open', | ||
)} | ||
> | ||
<div className="tile is-child box is-success "> | ||
<PostDetails | ||
post={selectedPost} | ||
error={error} | ||
comments={comments} | ||
formOpen={setIsFormOpen} | ||
isFormOpen={isFormOpen} | ||
deleteComment={handleDeleteComment} | ||
addComment={handleAddComment} | ||
isLoadingComments={isLoadingComments} | ||
/> | ||
</div> | ||
</div> | ||
)} | ||
> | ||
<div className="tile is-child box is-success "> | ||
<PostDetails /> | ||
</div> | ||
</div> | ||
</div> | ||
</div> | ||
</main> | ||
); | ||
</main> | ||
); | ||
}; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider defining |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,14 @@ | ||
import { Comment, CommentData } from '../types/Comment'; | ||
import { client } from '../utils/fetchClient'; | ||
|
||
export const getComments = (postId: number) => { | ||
return client.get<Comment[]>(`/comments?postId=${postId}`); | ||
}; | ||
|
||
export function deleteComment(commentId: number) { | ||
return client.delete(`/comments/${commentId}`); | ||
} | ||
|
||
export function addComment({ postId, name, email, body }: CommentData) { | ||
return client.post<Comment>('/comments', { postId, name, email, body }); | ||
} |
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}`); | ||
}; |
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'); | ||
}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The use of
math.div($i, 12) * 100%
is incorrect because the multiplication should be part of the division operation to ensure the correct order of operations. You should wrap the entire expression inmath.div
to ensure proper calculation, like this:width: math.div($i * 100%, 12);
.