From b21924b89c81b37295d88f128d51d4454bf4ed46 Mon Sep 17 00:00:00 2001 From: Aaron William Po Date: Mon, 18 Dec 2023 22:00:46 -0500 Subject: [PATCH] Refactor beer comment requests --- src/components/BeerById/BeerCommentForm.tsx | 9 +- .../BeerById/BeerPostCommentsSection.tsx | 20 ++--- src/components/ui/CommentsComponent.tsx | 49 +++++------ src/requests/comments/beer-comment/index.ts | 87 +++++++++++++++++++ .../sendCreateBeerCommentRequest.ts | 53 ----------- .../comments/beer-comment/types/index.ts | 17 ++++ 6 files changed, 136 insertions(+), 99 deletions(-) create mode 100644 src/requests/comments/beer-comment/index.ts delete mode 100644 src/requests/comments/beer-comment/sendCreateBeerCommentRequest.ts create mode 100644 src/requests/comments/beer-comment/types/index.ts diff --git a/src/components/BeerById/BeerCommentForm.tsx b/src/components/BeerById/BeerCommentForm.tsx index aef448b..cbd4a64 100644 --- a/src/components/BeerById/BeerCommentForm.tsx +++ b/src/components/BeerById/BeerCommentForm.tsx @@ -1,5 +1,3 @@ -import sendCreateBeerCommentRequest from '@/requests/comments/beer-comment/sendCreateBeerCommentRequest'; - import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult'; import { zodResolver } from '@hookform/resolvers/zod'; @@ -11,6 +9,7 @@ import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPost import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema'; import toast from 'react-hot-toast'; import createErrorToast from '@/util/createErrorToast'; +import { sendCreateBeerCommentRequest } from '@/requests/comments/beer-comment'; import CommentForm from '../ui/CommentForm'; interface BeerCommentFormProps { @@ -34,11 +33,7 @@ const BeerCommentForm: FunctionComponent = ({ ) => { const loadingToast = toast.loading('Posting a new comment...'); try { - await sendCreateBeerCommentRequest({ - content: data.content, - rating: data.rating, - beerPostId: beerPost.id, - }); + await sendCreateBeerCommentRequest({ body: data, beerPostId: beerPost.id }); reset(); toast.remove(loadingToast); toast.success('Comment posted successfully.'); diff --git a/src/components/BeerById/BeerPostCommentsSection.tsx b/src/components/BeerById/BeerPostCommentsSection.tsx index 5f83c91..6d574e4 100644 --- a/src/components/BeerById/BeerPostCommentsSection.tsx +++ b/src/components/BeerById/BeerPostCommentsSection.tsx @@ -7,6 +7,10 @@ import { z } from 'zod'; import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments'; import { useRouter } from 'next/router'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema'; +import { + deleteBeerPostCommentRequest, + editBeerPostCommentRequest, +} from '@/requests/comments/beer-comment'; import BeerCommentForm from './BeerCommentForm'; import LoadingComponent from './LoadingComponent'; @@ -29,26 +33,14 @@ const BeerPostCommentsSection: FC = ({ beerPost }) const commentSectionRef: MutableRefObject = useRef(null); const handleDeleteRequest = async (id: string) => { - const response = await fetch(`/api/beer-comments/${id}`, { method: 'DELETE' }); - - if (!response.ok) { - throw new Error('Failed to delete comment.'); - } + deleteBeerPostCommentRequest({ commentId: id }); }; const handleEditRequest = async ( id: string, data: z.infer, ) => { - const response = await fetch(`/api/beer-comments/${id}`, { - method: 'PUT', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ content: data.content, rating: data.rating }), - }); - - if (!response.ok) { - throw new Error('Failed to update comment.'); - } + editBeerPostCommentRequest({ body: data, commentId: id }); }; return ( diff --git a/src/components/ui/CommentsComponent.tsx b/src/components/ui/CommentsComponent.tsx index 20253aa..64c1d10 100644 --- a/src/components/ui/CommentsComponent.tsx +++ b/src/components/ui/CommentsComponent.tsx @@ -6,47 +6,46 @@ import { useInView } from 'react-intersection-observer'; import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments'; import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments'; +import useBeerStyleComments from '@/hooks/data-fetching/beer-style-comments/useBeerStyleComments'; import NoCommentsCard from '../BeerById/NoCommentsCard'; import LoadingComponent from '../BeerById/LoadingComponent'; import CommentCardBody from '../BeerBreweryComments/CommentCardBody'; +type HookReturnType = ReturnType< + typeof useBeerPostComments | typeof useBreweryPostComments | typeof useBeerStyleComments +>; + +type HandleDeleteRequest = (id: string) => Promise; + +type HandleEditRequest = ( + id: string, + data: { content: string; rating: number }, +) => Promise; + interface CommentsComponentProps { + comments: HookReturnType['comments']; commentSectionRef: MutableRefObject; + handleDeleteRequest: HandleDeleteRequest; + handleEditRequest: HandleEditRequest; + isAtEnd: HookReturnType['isAtEnd']; + isLoadingMore: HookReturnType['isLoadingMore']; + mutate: HookReturnType['mutate']; pageSize: number; - size: ReturnType['size']; - setSize: ReturnType< - typeof useBeerPostComments | typeof useBreweryPostComments - >['setSize']; - comments: ReturnType< - typeof useBeerPostComments | typeof useBreweryPostComments - >['comments']; - isAtEnd: ReturnType< - typeof useBeerPostComments | typeof useBreweryPostComments - >['isAtEnd']; - isLoadingMore: ReturnType< - typeof useBeerPostComments | typeof useBreweryPostComments - >['isLoadingMore']; - mutate: ReturnType< - typeof useBeerPostComments | typeof useBreweryPostComments - >['mutate']; - handleDeleteRequest: (id: string) => Promise; - handleEditRequest: ( - id: string, - data: { content: string; rating: number }, - ) => Promise; + setSize: HookReturnType['setSize']; + size: HookReturnType['size']; } const CommentsComponent: FC = ({ - commentSectionRef, comments, + commentSectionRef, + handleDeleteRequest, + handleEditRequest, isAtEnd, isLoadingMore, + mutate, pageSize, setSize, size, - mutate, - handleDeleteRequest, - handleEditRequest, }) => { const { ref: penultimateCommentRef } = useInView({ threshold: 0.1, diff --git a/src/requests/comments/beer-comment/index.ts b/src/requests/comments/beer-comment/index.ts new file mode 100644 index 0000000..7c2d015 --- /dev/null +++ b/src/requests/comments/beer-comment/index.ts @@ -0,0 +1,87 @@ +import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; +import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult'; +import { + SendCreateBeerCommentRequest, + SendDeleteBeerPostCommentRequest, + SendEditBeerPostCommentRequest, +} from './types'; + +export const editBeerPostCommentRequest: SendEditBeerPostCommentRequest = async ({ + body, + commentId, +}) => { + const response = await fetch(`/api/beer-post-comments/${commentId}`, { + method: 'PUT', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify(body), + }); + + if (!response.ok) { + throw new Error('Failed to edit comment'); + } + + const json = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + throw new Error(parsed.error.message); + } + + return parsed.data; +}; + +export const deleteBeerPostCommentRequest: SendDeleteBeerPostCommentRequest = async ({ + commentId, +}) => { + const response = await fetch(`/api/beer-post-comments/${commentId}`, { + method: 'DELETE', + }); + + if (!response.ok) { + throw new Error('Failed to delete comment'); + } + + const json = await response.json(); + + const parsed = APIResponseValidationSchema.safeParse(json); + + if (!parsed.success) { + throw new Error(parsed.error.message); + } + + return parsed.data; +}; + +export const sendCreateBeerCommentRequest: SendCreateBeerCommentRequest = async ({ + beerPostId, + body, +}) => { + const { content, rating } = body; + const response = await fetch(`/api/beers/${beerPostId}/comments`, { + method: 'POST', + headers: { 'Content-Type': 'application/json' }, + body: JSON.stringify({ beerPostId, content, rating }), + }); + if (!response.ok) { + throw new Error(response.statusText); + } + + const data = await response.json(); + + const parsedResponse = APIResponseValidationSchema.safeParse(data); + + if (!parsedResponse.success) { + throw new Error('Invalid API response'); + } + + const parsedPayload = CommentQueryResult.safeParse(parsedResponse.data.payload); + + if (!parsedPayload.success) { + throw new Error('Invalid API response payload'); + } + + return parsedPayload.data; +}; + +export default sendCreateBeerCommentRequest; diff --git a/src/requests/comments/beer-comment/sendCreateBeerCommentRequest.ts b/src/requests/comments/beer-comment/sendCreateBeerCommentRequest.ts deleted file mode 100644 index 41dbde3..0000000 --- a/src/requests/comments/beer-comment/sendCreateBeerCommentRequest.ts +++ /dev/null @@ -1,53 +0,0 @@ -import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult'; -import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema'; - -import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; -import { z } from 'zod'; - -const BeerCommentValidationSchemaWithId = CreateCommentValidationSchema.extend({ - beerPostId: z.string().cuid(), -}); - -/** - * Sends a POST request to the server to create a new beer comment. - * - * @param data The data to be sent to the server. - * @param data.beerPostId The ID of the beer post to comment on. - * @param data.content The content of the comment. - * @param data.rating The rating of the beer. - * @returns A promise that resolves to the created comment. - * @throws An error if the request fails, the API response is invalid, or the API response - * payload is invalid. - */ -const sendCreateBeerCommentRequest = async ({ - beerPostId, - content, - rating, -}: z.infer) => { - const response = await fetch(`/api/beers/${beerPostId}/comments`, { - method: 'POST', - headers: { 'Content-Type': 'application/json' }, - body: JSON.stringify({ beerPostId, content, rating }), - }); - if (!response.ok) { - throw new Error(response.statusText); - } - - const data = await response.json(); - - const parsedResponse = APIResponseValidationSchema.safeParse(data); - - if (!parsedResponse.success) { - throw new Error('Invalid API response'); - } - - const parsedPayload = CommentQueryResult.safeParse(parsedResponse.data.payload); - - if (!parsedPayload.success) { - throw new Error('Invalid API response payload'); - } - - return parsedPayload.data; -}; - -export default sendCreateBeerCommentRequest; diff --git a/src/requests/comments/beer-comment/types/index.ts b/src/requests/comments/beer-comment/types/index.ts new file mode 100644 index 0000000..b790ed6 --- /dev/null +++ b/src/requests/comments/beer-comment/types/index.ts @@ -0,0 +1,17 @@ +import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult'; +import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; +import { z } from 'zod'; + +export type SendEditBeerPostCommentRequest = (args: { + body: { content: string; rating: number }; + commentId: string; +}) => Promise>; + +export type SendDeleteBeerPostCommentRequest = (args: { + commentId: string; +}) => Promise>; + +export type SendCreateBeerCommentRequest = (args: { + beerPostId: string; + body: { content: string; rating: number }; +}) => Promise>;