diff --git a/src/App.tsx b/src/App.tsx index db56b44b0..67bb814ef 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,4 +1,4 @@ -import React from 'react'; +import React, { useEffect, useState } from 'react'; import 'bulma/bulma.sass'; import '@fortawesome/fontawesome-free/css/all.css'; import './App.scss'; @@ -8,8 +8,41 @@ import { PostsList } from './components/PostsList'; import { PostDetails } from './components/PostDetails'; import { UserSelector } from './components/UserSelector'; import { Loader } from './components/Loader'; +import { getUsers } from './api/users'; +import { User } from './types/User'; +import { getPosts } from './api/posts'; +import { Post } from './types/Post'; export const App: React.FC = () => { + const [posts, setPosts] = useState([]); + const [selectedPost, setSelectedPost] = useState(null); + + const [users, setUsers] = useState([]); + const [selectedUser, setSelectedUser] = useState(null); + + const [openSidebar, setOpenSidebar] = useState(false); + const [isDropdownOpen, setIsDropdownOpen] = useState(false); + + const [loading, setLoading] = useState(false); + const [isError, setIsError] = useState(false); + + useEffect(() => { + getUsers() + .then(setUsers) + .catch(() => setIsError(true)); + }, []); + + useEffect(() => { + if (selectedUser) { + setLoading(true); + setTimeout(() => { + getPosts(selectedUser) + .then(setPosts) + .finally(() => setLoading(false)); + }, 300); + } + }, [selectedUser]); + return (
@@ -17,46 +50,79 @@ export const App: React.FC = () => {
- +
-

- No user selected -

+ {(!selectedUser && !loading) && ( +

+ No user selected +

+ )} - + {loading && ()} -
- Something went wrong! -
+ {isError && ( +
+ Something went wrong! +
+ )} -
- No posts yet -
+ {selectedUser + && !loading + && posts.length === 0 + && !isDropdownOpen + && !isError && ( +
+ No posts yet +
+ )} - + {(posts.length > 0 && !isError && !loading && selectedUser) + && ( + + )}
-
-
- + {posts && ( +
+ {selectedPost && ( +
+ +
+ )}
-
+ )}
diff --git a/src/api/posts.ts b/src/api/posts.ts new file mode 100644 index 000000000..828b2fd1d --- /dev/null +++ b/src/api/posts.ts @@ -0,0 +1,20 @@ +import { Comment } from '../types/Comment'; +import { Post } from '../types/Post'; +import { User } from '../types/User'; +import { client } from '../utils/fetchClient'; + +export const getPosts = (selectedUser: User): Promise => { + return client.get(`/posts?userId=${selectedUser.id}`); +}; + +export const getComments = (selectedPost: Post): Promise => { + return client.get(`/comments?postId=${selectedPost.id}`); +}; + +export const deleteComment = (commentId: number) => { + return client.delete(`/comments/${commentId}`); +}; + +export const createComment = (comment: Omit) => { + return client.post('/comments', comment); +}; diff --git a/src/api/users.ts b/src/api/users.ts new file mode 100644 index 000000000..71121c964 --- /dev/null +++ b/src/api/users.ts @@ -0,0 +1,6 @@ +import { User } from '../types/User'; +import { client } from '../utils/fetchClient'; + +export const getUsers = (): Promise => { + return client.get('/users'); +}; diff --git a/src/components/NewCommentForm.tsx b/src/components/NewCommentForm.tsx index 73a8a0b45..ff16c50db 100644 --- a/src/components/NewCommentForm.tsx +++ b/src/components/NewCommentForm.tsx @@ -1,8 +1,115 @@ -import React from 'react'; +import React, { useState } from 'react'; +import classNames from 'classnames'; +import { Comment } from '../types/Comment'; +import { Post } from '../types/Post'; +import { createComment } from '../api/posts'; + +type Props = { + selectedPost: Post | null, + setComments: React.Dispatch>, + setCommentErrorMessage: (value: boolean) => void, +}; + +export const NewCommentForm: React.FC = ({ + setComments, selectedPost, setCommentErrorMessage, +}) => { + const [isFormLoading, setIsFormLoading] = useState(false); + const [newComment, setNewComment] = useState({ + name: '', + email: '', + text: '', + }); + const [formError, setFormError] = useState({ + name: '', + email: '', + text: '', + }); + + const handleChange + = (event: React.ChangeEvent) => { + const { value, name } = event.target; + + setFormError({ + ...formError, + [name]: '', + }); + + setNewComment(prevNewComment => ({ + ...prevNewComment, + [name]: value, + })); + }; + + const isEmptyField = !newComment.text.length + || !newComment.email.length + || !newComment.name.length; + + const addComment = (event: React.FormEvent) => { + event.preventDefault(); + if (!newComment.name) { + setFormError(rest => ({ + ...rest, + name: 'Name is required', + })); + } + + if (!newComment.email) { + setFormError(rest => ({ + ...rest, + email: 'Email is required', + })); + } + + if (!newComment.text) { + setFormError(rest => ({ + ...rest, + text: 'Enter some text', + })); + } + + if (selectedPost?.id && !isEmptyField) { + setIsFormLoading(true); + + createComment({ + postId: selectedPost?.id, + name: newComment.name.trim(), + email: newComment.email.trim(), + body: newComment.text.trim(), + }) + .then(newCreatedComment => { + setComments(currentComments => ( + [...currentComments, newCreatedComment])); + }) + .catch(() => { + setCommentErrorMessage(true); + }) + .finally(() => { + setNewComment(prevNewComment => ({ + ...prevNewComment, + text: '', + })); + + setIsFormLoading(false); + }); + } + }; + + const onClear = () => { + setFormError({ + name: '', + email: '', + text: '', + }); + + setNewComment({ + name: '', + text: '', + email: '', + }); + }; -export const NewCommentForm: React.FC = () => { return ( -
+
-

- Name is required -

+ {formError.name && ( +

+ {formError.name} +

+ )}
@@ -41,28 +154,34 @@ export const NewCommentForm: React.FC = () => {
- - - + {formError.email && ( + + + + )}
-

- Email is required -

+ {formError.email && ( +

+ {formError.email} +

+ )}
@@ -73,27 +192,40 @@ export const NewCommentForm: React.FC = () => {