-
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
Develop #1175
base: master
Are you sure you want to change the base?
Develop #1175
Changes from 3 commits
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 |
---|---|---|
@@ -0,0 +1,18 @@ | ||
import { ApiPath } from '../enums/api-path.enum'; | ||
import { QueryParams } from '../enums/query-params.enum'; | ||
import { Comment } from '../types/Comment'; | ||
import { client } from '../utils/fetchClient'; | ||
|
||
export const getCommentsByPost = (postId: number) => { | ||
return client.get<Comment[]>( | ||
`${ApiPath.COMMENTS}?${QueryParams.POST_ID}=${postId}`, | ||
); | ||
}; | ||
|
||
export const addComment = (comment: Omit<Comment, 'id'>) => { | ||
return client.post<Comment>(ApiPath.COMMENTS, comment); | ||
}; | ||
|
||
export const deleteComment = (commentId: number) => { | ||
return client.delete(`${ApiPath.COMMENTS}/${commentId}`); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
import { client } from '../utils/fetchClient'; | ||
import { Post } from '../types/Post'; | ||
import { ApiPath, QueryParams } from '../enums/enums'; | ||
|
||
export const getPostsByUserId = (userId: number) => { | ||
return client.get<Post[]>( | ||
`${ApiPath.POSTS}?${QueryParams.USER_ID}=${userId}`, | ||
); | ||
}; |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
import { User } from '../types/User'; | ||
import { client } from '../utils/fetchClient'; | ||
import { ApiPath } from '../enums/api-path.enum'; | ||
|
||
export const getUsers = () => client.get<User[]>(ApiPath.USERS); |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,8 +1,87 @@ | ||
import React from 'react'; | ||
import cn from 'classnames'; | ||
import React, { useState } from 'react'; | ||
import { CommentData } from '../types/Comment'; | ||
|
||
type Props = { | ||
postId: number; | ||
onSubmit: (data: CommentData & { postId: number }) => void; | ||
}; | ||
|
||
export const NewCommentForm: React.FC<Props> = ({ postId, onSubmit }) => { | ||
const [formData, setFormData] = useState<CommentData>({ | ||
name: '', | ||
email: '', | ||
body: '', | ||
}); | ||
|
||
const [errors, setErrors] = useState({ | ||
name: false, | ||
email: false, | ||
body: false, | ||
}); | ||
|
||
const [isLoading, setIsLoading] = useState(false); | ||
|
||
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. Good job on using a separate state variable to track the loading state. This is a good practice as it allows you to provide feedback to the user about the loading state. |
||
const handleChange = ( | ||
e: React.ChangeEvent<HTMLInputElement | HTMLTextAreaElement>, | ||
) => { | ||
const { name, value } = e.target; | ||
|
||
setFormData(prevData => ({ | ||
...prevData, | ||
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. The |
||
[name]: value, | ||
})); | ||
setErrors(prevErrors => ({ | ||
...prevErrors, | ||
[name]: false, | ||
})); | ||
}; | ||
|
||
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. Good use of functional updates when setting state. This ensures that you always have the most current state when updating. |
||
const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => { | ||
e.preventDefault(); | ||
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. Great job on using async/await syntax for handling promises. This makes your code easier to read and understand. |
||
|
||
const newErrors = { | ||
name: !formData.name, | ||
email: !formData.email, | ||
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. This code does not check if the input fields are empty but filled with spaces only. This can lead to adding a movie with empty data. You should trim the input values before checking if they are empty. |
||
body: !formData.body, | ||
}; | ||
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. The email validation here is not sufficient. Currently, it only checks if the email field is not empty, but it does not verify if the input is a valid email format. You can use a simple regex pattern to check for a valid email format. |
||
|
||
setErrors(newErrors); | ||
|
||
if (Object.values(newErrors).some(error => error)) { | ||
return; | ||
} | ||
|
||
setIsLoading(true); | ||
|
||
try { | ||
await onSubmit({ ...formData, postId }); | ||
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. Good job on using a try/finally block. This ensures that |
||
|
||
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. There is a potential issue with setting state based on previous state like this. If the |
||
setFormData({ | ||
...formData, | ||
body: '', | ||
}); | ||
} finally { | ||
setIsLoading(false); | ||
} | ||
}; | ||
|
||
const handleClear = () => { | ||
setFormData({ | ||
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. Good job on handling form reset. This improves user experience as they can easily clear the form. |
||
name: '', | ||
email: '', | ||
body: '', | ||
}); | ||
|
||
setErrors({ | ||
name: false, | ||
email: false, | ||
body: false, | ||
}); | ||
}; | ||
|
||
export const NewCommentForm: React.FC = () => { | ||
return ( | ||
<form data-cy="NewCommentForm"> | ||
<form data-cy="NewCommentForm" onSubmit={handleSubmit}> | ||
<div className="field" data-cy="NameField"> | ||
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. Nice job on using semantic HTML elements. This is good for accessibility and SEO. |
||
<label className="label" htmlFor="comment-author-name"> | ||
Author Name | ||
|
@@ -14,24 +93,30 @@ export const NewCommentForm: React.FC = () => { | |
name="name" | ||
id="comment-author-name" | ||
placeholder="Name Surname" | ||
className="input is-danger" | ||
className={cn('input', { 'is-danger': errors.name })} | ||
value={formData.name} | ||
onChange={handleChange} | ||
/> | ||
|
||
<span className="icon is-small is-left"> | ||
<i className="fas fa-user" /> | ||
</span> | ||
|
||
<span | ||
className="icon is-small is-right has-text-danger" | ||
data-cy="ErrorIcon" | ||
> | ||
<i className="fas fa-exclamation-triangle" /> | ||
</span> | ||
{errors.name && ( | ||
<span | ||
className="icon is-small is-right has-text-danger" | ||
data-cy="ErrorIcon" | ||
> | ||
<i className="fas fa-exclamation-triangle" /> | ||
</span> | ||
)} | ||
</div> | ||
|
||
<p className="help is-danger" data-cy="ErrorMessage"> | ||
Name is required | ||
</p> | ||
{errors.name && ( | ||
<p className="help is-danger" data-cy="ErrorMessage"> | ||
Name is required | ||
</p> | ||
)} | ||
</div> | ||
|
||
<div className="field" data-cy="EmailField"> | ||
|
@@ -45,24 +130,30 @@ export const NewCommentForm: React.FC = () => { | |
name="email" | ||
id="comment-author-email" | ||
placeholder="[email protected]" | ||
className="input is-danger" | ||
className={cn('input', { 'is-danger': errors.email })} | ||
value={formData.email} | ||
onChange={handleChange} | ||
/> | ||
|
||
<span className="icon is-small is-left"> | ||
<i className="fas fa-envelope" /> | ||
</span> | ||
|
||
<span | ||
className="icon is-small is-right has-text-danger" | ||
data-cy="ErrorIcon" | ||
> | ||
<i className="fas fa-exclamation-triangle" /> | ||
</span> | ||
{errors.email && ( | ||
<span | ||
className="icon is-small is-right has-text-danger" | ||
data-cy="ErrorIcon" | ||
> | ||
<i className="fas fa-exclamation-triangle" /> | ||
</span> | ||
)} | ||
</div> | ||
|
||
<p className="help is-danger" data-cy="ErrorMessage"> | ||
Email is required | ||
</p> | ||
{errors.email && ( | ||
<p className="help is-danger" data-cy="ErrorMessage"> | ||
Email is required | ||
</p> | ||
)} | ||
</div> | ||
|
||
<div className="field" data-cy="BodyField"> | ||
|
@@ -75,25 +166,36 @@ export const NewCommentForm: React.FC = () => { | |
id="comment-body" | ||
name="body" | ||
placeholder="Type comment here" | ||
className="textarea is-danger" | ||
className={cn('textarea', { 'is-danger': errors.body })} | ||
value={formData.body} | ||
onChange={handleChange} | ||
/> | ||
</div> | ||
|
||
<p className="help is-danger" data-cy="ErrorMessage"> | ||
Enter some text | ||
</p> | ||
{errors.body && ( | ||
<p className="help is-danger" data-cy="ErrorMessage"> | ||
Enter some text | ||
</p> | ||
)} | ||
</div> | ||
|
||
<div className="field is-grouped"> | ||
<div className="control"> | ||
<button type="submit" className="button is-link is-loading"> | ||
<button | ||
type="submit" | ||
className={cn('button is-link', { 'is-loading': isLoading })} | ||
> | ||
Add | ||
</button> | ||
</div> | ||
|
||
<div className="control"> | ||
{/* eslint-disable-next-line react/button-has-type */} | ||
<button type="reset" className="button is-link is-light"> | ||
<button | ||
type="reset" | ||
className="button is-link is-light" | ||
onClick={handleClear} | ||
> | ||
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. The |
||
Clear | ||
</button> | ||
</div> | ||
|
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.
Good job on defining the
Props
type for your component. This helps to ensure type safety for the props that your component receives.