diff --git a/components/BeerById/BeerCommentForm.tsx b/components/BeerById/BeerCommentForm.tsx index 443825d..9164279 100644 --- a/components/BeerById/BeerCommentForm.tsx +++ b/components/BeerById/BeerCommentForm.tsx @@ -10,6 +10,7 @@ import { z } from 'zod'; import { KeyedMutator } from 'swr'; import BeerCommentQueryResult from '@/services/BeerComment/schema/BeerCommentQueryResult'; +import { useRouter } from 'next/router'; import Button from '../ui/forms/Button'; import FormError from '../ui/forms/FormError'; import FormInfo from '../ui/forms/FormInfo'; @@ -44,6 +45,7 @@ const BeerCommentForm: FunctionComponent = ({ reset({ rating: 0, content: '' }); }, [reset]); + const router = useRouter(); const onSubmit: SubmitHandler> = async ( data, ) => { @@ -55,44 +57,58 @@ const BeerCommentForm: FunctionComponent = ({ beerPostId: beerPost.id, }); reset(); - await mutate(); + + const submitTasks: Promise[] = [ + router.push(`/beers/${beerPost.id}`, undefined, { scroll: false }), + mutate(), + ]; + + await Promise.all(submitTasks); }; const { errors } = formState; return ( -
- - Leave a comment - {errors.content?.message} - - - - - - Rating - {errors.rating?.message} - - { - setRating(value); - setValue('rating', value); - }} - > - - - - - - - + +
+ + Leave a comment + {errors.content?.message} + + + + + + Rating + {errors.rating?.message} + + { + setRating(value); + setValue('rating', value); + }} + > + + + + + + +
+ +
+ +
); }; diff --git a/components/BeerById/BeerPostCommentsPaginationBar.tsx b/components/BeerById/BeerPostCommentsPaginationBar.tsx index 7fad147..1925357 100644 --- a/components/BeerById/BeerPostCommentsPaginationBar.tsx +++ b/components/BeerById/BeerPostCommentsPaginationBar.tsx @@ -3,6 +3,8 @@ import Link from 'next/link'; import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import { z } from 'zod'; +import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'; + interface BeerCommentsPaginationBarProps { commentsPageNum: number; commentsPageCount: number; @@ -15,9 +17,9 @@ const BeerCommentsPaginationBar: FC = ({ beerPost, }) => (
-
+
= ({ }} scroll={false} > - Next Comments + + = ({ }} scroll={false} > - Previous Comments +
diff --git a/components/BeerById/BeerPostCommentsSection.tsx b/components/BeerById/BeerPostCommentsSection.tsx index ae45599..1f9d167 100644 --- a/components/BeerById/BeerPostCommentsSection.tsx +++ b/components/BeerById/BeerPostCommentsSection.tsx @@ -29,6 +29,7 @@ const BeerPostCommentsSection: FC = ({ beerPost }) pageNum, pageSize, }); + return (
@@ -43,7 +44,7 @@ const BeerPostCommentsSection: FC = ({ beerPost })
- {comments && !!commentsPageCount && !isLoading && ( + {comments && !!comments.length && !!commentsPageCount && !isLoading && (
{comments.map((comment) => ( @@ -60,10 +61,16 @@ const BeerPostCommentsSection: FC = ({ beerPost }) {!comments?.length && !isLoading && } {isLoading && ( -
- {Array.from({ length: 5 }).map((_, i) => ( +
+ {Array.from({ length: pageSize }).map((_, i) => ( ))} + +
)}
diff --git a/components/BeerById/BeerPostLikeButton.tsx b/components/BeerById/BeerPostLikeButton.tsx index 60f6870..07311ad 100644 --- a/components/BeerById/BeerPostLikeButton.tsx +++ b/components/BeerById/BeerPostLikeButton.tsx @@ -1,6 +1,6 @@ import useCheckIfUserLikesBeerPost from '@/hooks/useCheckIfUserLikesBeerPost'; import sendLikeRequest from '@/requests/sendLikeRequest'; -import { FC, useState } from 'react'; +import { FC, useEffect, useState } from 'react'; import { FaThumbsUp, FaRegThumbsUp } from 'react-icons/fa'; import { KeyedMutator } from 'swr'; @@ -9,14 +9,18 @@ const BeerPostLikeButton: FC<{ mutateCount: KeyedMutator; }> = ({ beerPostId, mutateCount }) => { const { isLiked, mutate: mutateLikeStatus } = useCheckIfUserLikesBeerPost(beerPostId); - const [loading, setLoading] = useState(false); + const [loading, setLoading] = useState(true); + + useEffect(() => { + setLoading(false); + }, [isLiked]); const handleLike = async () => { try { setLoading(true); await sendLikeRequest(beerPostId); - mutateCount(); - mutateLikeStatus(); + await mutateCount(); + await mutateLikeStatus(); setLoading(false); } catch (e) { setLoading(false); diff --git a/components/BeerById/BeerRecommendations.tsx b/components/BeerById/BeerRecommendations.tsx index 1bda780..e849488 100644 --- a/components/BeerById/BeerRecommendations.tsx +++ b/components/BeerById/BeerRecommendations.tsx @@ -10,7 +10,7 @@ const BeerRecommendations: FunctionComponent = ({ }) => { return (
-
+
{beerRecommendations.map((beerPost) => (
diff --git a/components/BeerById/CommentCardBody.tsx b/components/BeerById/CommentCardBody.tsx index 256f198..b7ac458 100644 --- a/components/BeerById/CommentCardBody.tsx +++ b/components/BeerById/CommentCardBody.tsx @@ -1,7 +1,7 @@ import UserContext from '@/contexts/userContext'; import useTimeDistance from '@/hooks/useTimeDistance'; import BeerCommentQueryResult from '@/services/BeerComment/schema/BeerCommentQueryResult'; -import { format } from 'date-fns'; +import format from 'date-fns/format'; import Link from 'next/link'; import { useContext } from 'react'; import { Rating } from 'react-daisyui'; @@ -69,7 +69,7 @@ const CommentCardBody: React.FC<{ const timeDistance = useTimeDistance(new Date(comment.createdAt)); return ( -
+

diff --git a/components/BeerById/CommentLoadingCardBody.tsx b/components/BeerById/CommentLoadingCardBody.tsx index fa3c6e2..2d0e292 100644 --- a/components/BeerById/CommentLoadingCardBody.tsx +++ b/components/BeerById/CommentLoadingCardBody.tsx @@ -1,12 +1,12 @@ const CommentLoadingCardBody = () => { return ( -
-
+
+
-
+
-
-
+
+
diff --git a/components/BeerIndex/BeerIndexPaginationBar.tsx b/components/BeerIndex/BeerIndexPaginationBar.tsx index 3a8e974..2bbca57 100644 --- a/components/BeerIndex/BeerIndexPaginationBar.tsx +++ b/components/BeerIndex/BeerIndexPaginationBar.tsx @@ -1,5 +1,5 @@ import Link from 'next/link'; - +import { FaArrowLeft, FaArrowRight } from 'react-icons/fa'; import { FC } from 'react'; interface PaginationProps { @@ -15,7 +15,7 @@ const BeerIndexPaginationBar: FC = ({ pageCount, pageNum }) => href={{ pathname: '/beers', query: { page_num: pageNum - 1 } }} scroll={false} > - « + = ({ pageCount, pageNum }) => href={{ pathname: '/beers', query: { page_num: pageNum + 1 } }} scroll={false} > - » +
); diff --git a/components/ui/Layout.tsx b/components/ui/Layout.tsx index b6306b7..d1446e1 100644 --- a/components/ui/Layout.tsx +++ b/components/ui/Layout.tsx @@ -4,10 +4,10 @@ import Navbar from './Navbar'; const Layout: FC<{ children: ReactNode }> = ({ children }) => { return (
-
+
-
{children}
+
{children}
); }; diff --git a/getServerSideProps/withPageAuthRequired.ts b/getServerSideProps/withPageAuthRequired.ts index fa90173..faaea66 100644 --- a/getServerSideProps/withPageAuthRequired.ts +++ b/getServerSideProps/withPageAuthRequired.ts @@ -2,6 +2,21 @@ import { GetServerSidePropsContext, GetServerSidePropsResult, PreviewData } from import { ParsedUrlQuery } from 'querystring'; import { getLoginSession } from '../config/auth/session'; +/** + * Represents a type definition for a function that handles server-side rendering with + * extended capabilities. + * + * @template P - A generic type that represents an object with string keys and any values. + * It defaults to an empty object. + * @template Q - A generic type that represents a parsed URL query object. It defaults to + * the ParsedUrlQuery type. + * @template D - A generic type that represents preview data. It defaults to the + * PreviewData type. + * @param context - The context object containing information about the incoming HTTP + * request. + * @param session - An awaited value of the return type of the getLoginSession function. + * @returns - A promise that resolves to the result of the server-side rendering process. + */ export type ExtendedGetServerSideProps< P extends { [key: string]: any } = { [key: string]: any }, Q extends ParsedUrlQuery = ParsedUrlQuery, @@ -11,6 +26,20 @@ export type ExtendedGetServerSideProps< session: Awaited>, ) => Promise>; +/** + * A Higher Order Function that adds authentication requirement to a Next.js server-side + * page component. + * + * @param fn An async function that receives the GetServerSidePropsContext and + * authenticated session as arguments and returns a GetServerSidePropsResult with props + * for the wrapped component. + * @returns A promise that resolves to a GetServerSidePropsResult object with props for + * the wrapped component. If authentication is successful, the GetServerSidePropsResult + * will include props generated by the wrapped component's getServerSideProps method. If + * authentication fails, the GetServerSidePropsResult will include a redirect to the + * login page. + */ + const withPageAuthRequired =

( fn?: ExtendedGetServerSideProps

, diff --git a/hooks/useBeerPostComments.ts b/hooks/useBeerPostComments.ts index 3241270..bbcac7b 100644 --- a/hooks/useBeerPostComments.ts +++ b/hooks/useBeerPostComments.ts @@ -40,7 +40,7 @@ const useBeerPostComments = ({ pageNum, id, pageSize }: UseBeerPostCommentsProps throw new Error(parsedPayload.error.message); } - const pageCount = Math.ceil(parseInt(count as string, 10) / 10); + const pageCount = Math.ceil(parseInt(count as string, 10) / pageSize); return { comments: parsedPayload.data, pageCount }; }, ); diff --git a/package.json b/package.json index c6443f4..7c1d3a4 100644 --- a/package.json +++ b/package.json @@ -69,6 +69,7 @@ "prettier-plugin-tailwindcss": "^0.2.3", "prisma": "^4.10.1", "tailwindcss": "^3.2.7", + "tailwindcss-animate": "^1.0.5", "ts-node": "^10.9.1", "typescript": "^4.9.5" } diff --git a/pages/_app.tsx b/pages/_app.tsx index 6069da6..69cfbaf 100644 --- a/pages/_app.tsx +++ b/pages/_app.tsx @@ -3,12 +3,28 @@ import useUser from '@/hooks/useUser'; import '@/styles/globals.css'; import type { AppProps } from 'next/app'; +import { Roboto } from 'next/font/google'; + +const roboto = Roboto({ + weight: ['100', '300', '400', '500', '700', '900'], + subsets: ['latin'], +}); + export default function App({ Component, pageProps }: AppProps) { const { user, isLoading, error } = useUser(); return ( - - - + <> + + + + + ); } diff --git a/styles/globals.css b/styles/globals.css index 19156a0..b5c61c9 100644 --- a/styles/globals.css +++ b/styles/globals.css @@ -1,11 +1,3 @@ -@import url('https://fonts.googleapis.com/css2?family=Exo+2:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&display=swap'); - @tailwind base; @tailwind components; @tailwind utilities; - -@layer base { - html { - font-family: 'Exo 2', sans-serif; - } -} diff --git a/tailwind.config.js b/tailwind.config.js index 01c5601..06228bf 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -9,26 +9,29 @@ module.exports = { theme: { extend: {}, }, - plugins: [require('daisyui')], + plugins: [ + require('daisyui'), + require('tailwindcss-animate') + ], daisyui: { logs: false, themes: [ { default: { - primary: 'hsl(227, 46%, 25%)', - secondary: 'hsl(47, 100%, 80%)', + primary: 'hsl(227, 23%, 20%)', + secondary: '#ABA9C3', + error: '#c17c74', accent: '#fe3bd9', neutral: '#131520', info: '#0A7CFF', success: '#8ACE2B', warning: '#F9D002', - error: '#CF1259', 'primary-content': '#FAF9F6', 'error-content': '#FAF9F6', - 'base-100': 'hsl(190, 4%, 11%)', - 'base-200': 'hsl(190, 4%, 9%)', - 'base-300': 'hsl(190, 4%, 8%)', + 'base-100': 'hsl(190, 4%, 9%)', + 'base-200': 'hsl(190, 4%, 8%)', + 'base-300': 'hsl(190, 4%, 5%)', }, }, ],