diff --git a/components/BeerById/BeerInfoHeader.tsx b/components/BeerById/BeerInfoHeader.tsx index 7d8b7e9..eac4c49 100644 --- a/components/BeerById/BeerInfoHeader.tsx +++ b/components/BeerById/BeerInfoHeader.tsx @@ -2,50 +2,29 @@ import Link from 'next/link'; import formatDistanceStrict from 'date-fns/formatDistanceStrict'; import format from 'date-fns/format'; import { FC, useContext, useEffect, useState } from 'react'; -import { FaRegThumbsUp, FaThumbsUp } from 'react-icons/fa'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import UserContext from '@/contexts/userContext'; -import sendCheckIfUserLikesBeerPostRequest from '@/requests/sendCheckIfUserLikesBeerPostRequest'; -import sendLikeRequest from '../../requests/sendLikeRequest'; +import BeerPostLikeButton from './BeerPostLikeButton'; -const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult }> = ({ beerPost }) => { +const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: number }> = ({ + beerPost, + initialLikeCount, +}) => { const createdAtDate = new Date(beerPost.createdAt); const [timeDistance, setTimeDistance] = useState(''); const { user } = useContext(UserContext); - const [loading, setLoading] = useState(true); - const [isLiked, setIsLiked] = useState(false); + const [likeCount, setLikeCount] = useState(initialLikeCount); useEffect(() => { - if (!user) { - setLoading(false); - return; - } - sendCheckIfUserLikesBeerPostRequest(beerPost.id) - .then((currentLikeStatus) => { - setIsLiked(currentLikeStatus); - setLoading(false); - }) - .catch((e) => { - console.error(e); - setLoading(false); - }); - }, [user, beerPost.id]); + setLikeCount(initialLikeCount); + }, [initialLikeCount]); useEffect(() => { setTimeDistance(formatDistanceStrict(new Date(beerPost.createdAt), new Date())); }, [beerPost.createdAt]); - const handleLike = async () => { - try { - await sendLikeRequest(beerPost); - setIsLiked(!isLiked); - } catch (e) { - console.error(e); - } - }; - return (
@@ -75,8 +54,8 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult }> = ({ beerPost }) =>

{beerPost.description}

-
-
+
+
= ({ beerPost }) => {beerPost.abv}% ABV {beerPost.ibu} IBU
+
+ Liked by {likeCount} users +
-
+
{user && ( - + )}
diff --git a/components/BeerById/BeerPostLikeButton.tsx b/components/BeerById/BeerPostLikeButton.tsx new file mode 100644 index 0000000..ed7c2e6 --- /dev/null +++ b/components/BeerById/BeerPostLikeButton.tsx @@ -0,0 +1,69 @@ +import UserContext from '@/contexts/userContext'; +import sendCheckIfUserLikesBeerPostRequest from '@/requests/sendCheckIfUserLikesBeerPostRequest'; +import sendLikeRequest from '@/requests/sendLikeRequest'; +import { Dispatch, FC, SetStateAction, useContext, useEffect, useState } from 'react'; +import { FaThumbsUp, FaRegThumbsUp } from 'react-icons/fa'; + +const BeerPostLikeButton: FC<{ + beerPostId: string; + setLikeCount: Dispatch>; +}> = ({ beerPostId, setLikeCount }) => { + const [loading, setLoading] = useState(true); + const [isLiked, setIsLiked] = useState(false); + + const { user } = useContext(UserContext); + + useEffect(() => { + if (!user) { + setLoading(false); + return; + } + sendCheckIfUserLikesBeerPostRequest(beerPostId) + .then((currentLikeStatus) => { + setIsLiked(currentLikeStatus); + setLoading(false); + }) + .catch(() => { + setLoading(false); + }); + }, [user, beerPostId]); + + const handleLike = async () => { + try { + setLoading(true); + await sendLikeRequest(beerPostId); + setIsLiked(!isLiked); + setLikeCount((prevCount) => prevCount + (isLiked ? -1 : 1)); + setLoading(false); + } catch (error) { + setLoading(false); + } + }; + + return ( + + ); +}; + +export default BeerPostLikeButton; diff --git a/pages/beers/[id].tsx b/pages/beers/[id].tsx index 07fc531..3c8a956 100644 --- a/pages/beers/[id].tsx +++ b/pages/beers/[id].tsx @@ -26,6 +26,7 @@ interface BeerPageProps { })[]; beerComments: BeerCommentQueryResultArrayT; commentsPageCount: number; + likeCount: number; } const BeerByIdPage: NextPage = ({ @@ -33,6 +34,7 @@ const BeerByIdPage: NextPage = ({ beerRecommendations, beerComments, commentsPageCount, + likeCount, }) => { const { user } = useContext(UserContext); const [comments, setComments] = useState(beerComments); @@ -66,7 +68,7 @@ const BeerByIdPage: NextPage = ({
- +
@@ -153,12 +155,16 @@ export const getServerSideProps: GetServerSideProps = async (cont where: { beerPostId: beerPost.id }, }); const pageCount = numberOfPosts ? Math.ceil(numberOfPosts / pageSize) : 0; + const likeCount = await DBClient.instance.beerPostLike.count({ + where: { beerPostId: beerPost.id }, + }); const props = { beerPost: JSON.parse(JSON.stringify(beerPost)), beerRecommendations: JSON.parse(JSON.stringify(beerRecommendations)), beerComments: JSON.parse(JSON.stringify(beerComments)), commentsPageCount: JSON.parse(JSON.stringify(pageCount)), + likeCount: JSON.parse(JSON.stringify(likeCount)), }; return { props }; diff --git a/requests/sendLikeRequest.ts b/requests/sendLikeRequest.ts index e9d235b..2e8e807 100644 --- a/requests/sendLikeRequest.ts +++ b/requests/sendLikeRequest.ts @@ -1,8 +1,7 @@ -import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; -const sendLikeRequest = async (beerPost: BeerPostQueryResult) => { - const response = await fetch(`/api/beers/${beerPost.id}/like`, { +const sendLikeRequest = async (beerPostId: string) => { + const response = await fetch(`/api/beers/${beerPostId}/like`, { method: 'POST', headers: { 'Content-Type': 'application/json', diff --git a/services/BeerComment/schema/BeerCommentQueryResult.ts b/services/BeerComment/schema/BeerCommentQueryResult.ts index b80894e..ad6841a 100644 --- a/services/BeerComment/schema/BeerCommentQueryResult.ts +++ b/services/BeerComment/schema/BeerCommentQueryResult.ts @@ -4,7 +4,7 @@ export const BeerCommentQueryResult = z.object({ id: z.string().uuid(), content: z.string().min(1).max(300), rating: z.number().int().min(1).max(5), - createdAt: z.date().or(z.string().datetime()), + createdAt: z.coerce.date(), postedBy: z.object({ id: z.string().uuid(), username: z.string().min(1).max(50), diff --git a/services/BeerComment/schema/CreateBeerCommentValidationSchema.ts b/services/BeerComment/schema/CreateBeerCommentValidationSchema.ts index f12ce66..5d02670 100644 --- a/services/BeerComment/schema/CreateBeerCommentValidationSchema.ts +++ b/services/BeerComment/schema/CreateBeerCommentValidationSchema.ts @@ -3,20 +3,14 @@ import { z } from 'zod'; const BeerCommentValidationSchema = z.object({ content: z .string() - .min(1, { - message: 'Comment must not be empty.', - }) - .max(300, { - message: 'Comment must be less than 300 characters.', - }), + .min(1, { message: 'Comment must not be empty.' }) + .max(300, { message: 'Comment must be less than 300 characters.' }), rating: z .number() .int() .min(1, { message: 'Rating must be greater than 1.' }) .max(5, { message: 'Rating must be less than 5.' }), - beerPostId: z.string().uuid({ - message: 'Beer post ID must be a valid UUID.', - }), + beerPostId: z.string().uuid({ message: 'Beer post ID must be a valid UUID.' }), }); export default BeerCommentValidationSchema;