diff --git a/src/App.tsx b/src/App.tsx index db56b44b0..85253bcf6 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -1,15 +1,25 @@ -import React from 'react'; +import React, { useContext } from 'react'; import 'bulma/bulma.sass'; import '@fortawesome/fontawesome-free/css/all.css'; import './App.scss'; - import classNames from 'classnames'; -import { PostsList } from './components/PostsList'; -import { PostDetails } from './components/PostDetails'; import { UserSelector } from './components/UserSelector'; +import { + ErrorContext, + PostDataContext, + PostsContext, + UserContext, +} from './components/UserContext/UserContext'; import { Loader } from './components/Loader'; +import { PostsList } from './components/PostsList'; +import { PostDetails } from './components/PostDetails'; export const App: React.FC = () => { + const { isError, isLoadingPosts } = useContext(ErrorContext); + const { user } = useContext(UserContext); + const posts = useContext(PostsContext); + const postDetails = useContext(PostDataContext); + return (
@@ -21,42 +31,56 @@ export const App: React.FC = () => {
-

- No user selected -

- - - -
- Something went wrong! -
+ {!user && ( +

+ No user selected +

+ )} + {isError && ( +
+ Something went wrong! +
+ )} -
- No posts yet -
+ {isLoadingPosts ? : ( + <> + {(posts?.length > 0 && user && !isError) && ( + + )} - + {(posts?.length === 0 && user && !isError) && ( +
+ No posts yet +
+ )} + + )}
-
-
- + {postDetails.postData !== null && ( +
+
+ +
-
+ )}
diff --git a/src/api/comments.ts b/src/api/comments.ts new file mode 100644 index 000000000..c0109a160 --- /dev/null +++ b/src/api/comments.ts @@ -0,0 +1,15 @@ +import { Comment } from '../types/Comment'; +import { Post } from '../types/Post'; +import { client } from '../utils/fetchClient'; + +export const getComments = (postData: Post | null) => { + return client.get(`/comments?postId=${postData?.id}`); +}; + +export const createComment = (comment: Partial) => { + return client.post('/comments', comment); +}; + +export const deleteComment = (commentId: number) => { + return client.delete(`/comments/${commentId}`); +}; diff --git a/src/api/posts.ts b/src/api/posts.ts new file mode 100644 index 000000000..edd22a1dc --- /dev/null +++ b/src/api/posts.ts @@ -0,0 +1,7 @@ +import { Post } from '../types/Post'; +import { User } from '../types/User'; +import { client } from '../utils/fetchClient'; + +export const getPosts = (user: User | null) => { + return client.get(`/posts?userId=${user?.id}`); +}; diff --git a/src/api/users.ts b/src/api/users.ts new file mode 100644 index 000000000..816c8274b --- /dev/null +++ b/src/api/users.ts @@ -0,0 +1,6 @@ +import { User } from '../types/User'; +import { client } from '../utils/fetchClient'; + +export const getUsers = () => { + return client.get('/users'); +}; diff --git a/src/components/NewCommentForm.tsx b/src/components/NewCommentForm.tsx index 73a8a0b45..af97a8d38 100644 --- a/src/components/NewCommentForm.tsx +++ b/src/components/NewCommentForm.tsx @@ -1,103 +1,281 @@ -import React from 'react'; +import React, { useContext, useState } from 'react'; +import classNames from 'classnames'; +import { CommentData } from '../types/Comment'; +import { + CommentsContext, + ErrorContext, +} from './UserContext/UserContext'; +import { createComment } from '../api/comments'; + +type PostId = { + id: number; + userId: number; + title: string; + body: string; +}; + +type NewCommentFormProps = { + postId: PostId, +}; + +export const NewCommentForm: React.FC = ({ postId }) => { + const [submitting, setSubmitting] = useState(false); + const { newCommentSelect } = useContext(CommentsContext); + + const { isError } = useContext(ErrorContext); + + const onAddComment = async ({ name, email, body }: CommentData) => { + const newComment = { + name, + email, + body, + postId: postId.id, + }; + + try { + setSubmitting(true); + await createComment(newComment); + } catch (error) { + setSubmitting(false); + } finally { + setSubmitting(false); + } + }; + + const [hasNameError, setHasNameError] = useState(false); + const [hasEmailError, setHasEmailError] = useState(false); + const [emailValidationError, setEmailValidationError] = useState(false); + const [hasBodyError, setHasBodyError] = useState(false); + + const [{ name, email, body }, setValues] = useState({ + name: '', + email: '', + body: '', + }); + const handleChangeName = ( + event: React.ChangeEvent, + ) => { + setValues(current => ({ ...current, name: event.target.value })); + }; + + const handleChangeEmail = ( + event: React.ChangeEvent, + ) => { + setValues(current => ({ ...current, email: event.target.value })); + }; + + const handleChangeBody = ( + event: React.ChangeEvent, + ) => { + setValues(current => ({ ...current, body: event.target.value })); + }; + + const validateEmail = (em: string) => { + const emailRegex = /^[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,4}$/i; + + return emailRegex.test(em); + }; + + const handleSubmit = async (event: React.FormEvent) => { + event.preventDefault(); + + if (!validateEmail(email)) { + setEmailValidationError(true); + } + + if (!email.trim().length) { + setHasEmailError(true); + } + + if (!name.trim().length) { + setHasNameError(true); + } + + if (!body.length) { + setHasBodyError(true); + } + + if (!validateEmail(email) || !email.trim().length + || !name.trim().length || !body.length) { + return; + } + + await onAddComment({ name, email, body }); + + const newComment = { + name, + email, + body, + postId: postId.id, + id: Date.now(), + }; + + newCommentSelect(newComment); + + setValues(current => ({ ...current, body: '' })); + }; + + const clearForm = () => { + setValues({ + name: '', + email: '', + body: '', + }); + + setHasNameError(false); + setHasEmailError(false); + setEmailValidationError(false); + setHasBodyError(false); + }; + + const focusErrorDisabled = () => { + setEmailValidationError(false); + setHasEmailError(false); + }; -export const NewCommentForm: React.FC = () => { return ( -
-
- - -
- - - - - - - - - -
+ <> + +
+ + +
+ setHasNameError(false)} + /> + + + + + + {hasNameError && ( + + + + )} + +
+ + {hasNameError && ( +

+ Name is required +

+ )} -

- Name is required -

-
- -
- - -
- - - - - - - - -
-

- Email is required -

-
- -
- - -
-