Refactor BeerPostHeader, refactor login and register

- Updated BeerPostHeader in /id page to use semantic HTML tags
- Removed the getServerSidesProps fn in /login and /register. Moved the redirect logic over to the client, will redirect on the client side.
- Added delete BeerPost option on the edit page.
This commit is contained in:
Aaron William Po
2023-03-15 21:30:26 -04:00
parent 584e3b349f
commit 6b12cb72c5
8 changed files with 106 additions and 69 deletions

View File

@@ -39,10 +39,10 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
}, [beerPost.createdAt]); }, [beerPost.createdAt]);
return ( return (
<div className="card flex flex-col justify-center bg-base-300"> <main className="card flex flex-col justify-center bg-base-300">
<div className="card-body"> <article className="card-body">
<div className="flex justify-between"> <div className="flex justify-between">
<div> <header>
<h1 className="text-4xl font-bold">{beerPost.name}</h1> <h1 className="text-4xl font-bold">{beerPost.name}</h1>
<h2 className="text-2xl font-semibold"> <h2 className="text-2xl font-semibold">
by{' '} by{' '}
@@ -53,8 +53,7 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
{beerPost.brewery.name} {beerPost.brewery.name}
</Link> </Link>
</h2> </h2>
</div> </header>
<div>
{isPostOwner && ( {isPostOwner && (
<div className="tooltip tooltip-left" data-tip={`Edit '${beerPost.name}'`}> <div className="tooltip tooltip-left" data-tip={`Edit '${beerPost.name}'`}>
<Link <Link
@@ -66,18 +65,17 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
</div> </div>
)} )}
</div> </div>
</div>
<h3 className="italic"> <h3 className="italic">
posted by{' '} posted by{' '}
<Link href={`/users/${beerPost.postedBy.id}`} className="link-hover link"> <Link href={`/users/${beerPost.postedBy.id}`} className="link-hover link">
{beerPost.postedBy.username}{' '} {beerPost.postedBy.username}
</Link> </Link>
<span <span
className="tooltip tooltip-bottom" className="tooltip tooltip-bottom"
data-tip={format(createdAtDate, 'MM/dd/yyyy')} data-tip={format(createdAtDate, 'MM/dd/yyyy')}
> >
{timeDistance} ago {` ${timeDistance}`} ago
</span> </span>
</h3> </h3>
@@ -108,8 +106,8 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
)} )}
</div> </div>
</div> </div>
</div> </article>
</div> </main>
); );
}; };

View File

@@ -50,6 +50,18 @@ const EditBeerPostForm: FC<EditBeerPostFormProps> = ({ previousValues }) => {
} }
}; };
const onDelete = async () => {
try {
const response = await fetch(`/api/beers/${previousValues.id}`, {
method: 'DELETE',
});
if (response.status === 200) {
router.push('/beers');
}
} catch (e) {
console.error(e);
}
};
return ( return (
<form className="form-control" onSubmit={handleSubmit(onSubmit)}> <form className="form-control" onSubmit={handleSubmit(onSubmit)}>
<div className="mb-5"> <div className="mb-5">
@@ -116,10 +128,17 @@ const EditBeerPostForm: FC<EditBeerPostFormProps> = ({ previousValues }) => {
/> />
</FormSegment> </FormSegment>
<div className="mt-2"> <div className="mt-2 space-y-4">
<Button type="submit" isSubmitting={isSubmitting}> <Button type="submit" isSubmitting={isSubmitting}>
{isSubmitting ? 'Submitting...' : 'Submit'} {isSubmitting ? 'Submitting...' : 'Submit'}
</Button> </Button>
<button
className={`btn-primary btn w-full rounded-xl ${isSubmitting ? 'loading' : ''}`}
type="button"
onClick={onDelete}
>
Delete
</button>
</div> </div>
</form> </form>
); );

View File

@@ -1,28 +0,0 @@
import { GetServerSideProps, GetServerSidePropsContext, Redirect } from 'next';
import { getLoginSession } from '@/config/auth/session';
import findUserById from '@/services/User/findUserById';
const redirectIfLoggedIn = (redirect: Redirect) => {
const fn: GetServerSideProps = async (context: GetServerSidePropsContext) => {
try {
const { req } = context;
const session = await getLoginSession(req);
const { id } = session;
const user = await findUserById(id);
if (!user) {
throw new Error('Could not get user.');
}
return { redirect };
} catch {
return { props: {} };
}
};
return fn;
};
export default redirectIfLoggedIn;

View File

@@ -0,0 +1,17 @@
import UserContext from '@/contexts/userContext';
import { useRouter } from 'next/router';
import { useContext, useEffect } from 'react';
const useRedirectWhenLoggedIn = () => {
const { user } = useContext(UserContext);
const router = useRouter();
useEffect(() => {
if (!user) {
return;
}
router.push('/');
}, [user, router]);
};
export default useRedirectWhenLoggedIn;

View File

@@ -6,20 +6,25 @@ import editBeerPostById from '@/services/BeerPost/editBeerPostById';
import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema'; import EditBeerPostValidationSchema from '@/services/BeerPost/schema/EditBeerPostValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import { createRouter } from 'next-connect'; import { createRouter, NextHandler } from 'next-connect';
import { z } from 'zod'; import { z } from 'zod';
import ServerError from '@/config/util/ServerError'; import ServerError from '@/config/util/ServerError';
import DBClient from '@/prisma/DBClient';
interface EditBeerPostRequest extends UserExtendedNextApiRequest { interface BeerPostRequest extends UserExtendedNextApiRequest {
query: { id: string }; query: { id: string };
}
interface EditBeerPostRequest extends BeerPostRequest {
body: z.infer<typeof EditBeerPostValidationSchema>; body: z.infer<typeof EditBeerPostValidationSchema>;
} }
const editBeerPost = async ( const checkIfBeerPostOwner = async (
req: EditBeerPostRequest, req: BeerPostRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>, res: NextApiResponse,
next: NextHandler,
) => { ) => {
const { body, user, query } = req; const { user, query } = req;
const { id } = query; const { id } = query;
const beerPost = await getBeerPostById(id); const beerPost = await getBeerPostById(id);
@@ -32,6 +37,18 @@ const editBeerPost = async (
throw new ServerError('You cannot edit that beer post.', 403); throw new ServerError('You cannot edit that beer post.', 403);
} }
next();
};
const editBeerPost = async (
req: EditBeerPostRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => {
const {
body,
query: { id },
} = req;
await editBeerPostById(id, body); await editBeerPostById(id, body);
res.status(200).json({ res.status(200).json({
@@ -41,12 +58,32 @@ const editBeerPost = async (
}); });
}; };
const deleteBeerPost = async (req: BeerPostRequest, res: NextApiResponse) => {
const {
query: { id },
} = req;
const deleted = await DBClient.instance.beerPost.delete({
where: { id },
});
if (!deleted) {
throw new ServerError('Beer post not found', 404);
}
res.status(200).json({
message: 'Beer post deleted successfully',
success: true,
statusCode: 200,
});
};
const router = createRouter< const router = createRouter<
EditBeerPostRequest, EditBeerPostRequest,
NextApiResponse<z.infer<typeof APIResponseValidationSchema>> NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
>(); >();
router.put(getCurrentUser, editBeerPost); router.put(getCurrentUser, checkIfBeerPostOwner, editBeerPost);
router.delete(getCurrentUser, checkIfBeerPostOwner, deleteBeerPost);
const handler = router.handler(NextConnectOptions); const handler = router.handler(NextConnectOptions);

View File

@@ -6,9 +6,11 @@ import Image from 'next/image';
import { FaUserCircle } from 'react-icons/fa'; import { FaUserCircle } from 'react-icons/fa';
import Head from 'next/head'; import Head from 'next/head';
import Link from 'next/link'; import Link from 'next/link';
import redirectIfLoggedIn from '@/getServerSideProps/redirectIfLoggedIn';
import useRedirectWhenLoggedIn from '@/hooks/useRedirectIfLoggedIn';
const LoginPage: NextPage = () => { const LoginPage: NextPage = () => {
useRedirectWhenLoggedIn();
return ( return (
<Layout> <Layout>
<Head> <Head>
@@ -52,8 +54,3 @@ const LoginPage: NextPage = () => {
}; };
export default LoginPage; export default LoginPage;
export const getServerSideProps = redirectIfLoggedIn({
destination: '/',
permanent: false,
});

View File

@@ -1,12 +1,15 @@
import RegisterUserForm from '@/components/RegisterUserForm'; import RegisterUserForm from '@/components/RegisterUserForm';
import FormPageLayout from '@/components/ui/forms/FormPageLayout'; import FormPageLayout from '@/components/ui/forms/FormPageLayout';
import Layout from '@/components/ui/Layout'; import Layout from '@/components/ui/Layout';
import redirectIfLoggedIn from '@/getServerSideProps/redirectIfLoggedIn';
import useRedirectWhenLoggedIn from '@/hooks/useRedirectIfLoggedIn';
import { NextPage } from 'next'; import { NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import { BiUser } from 'react-icons/bi'; import { BiUser } from 'react-icons/bi';
const RegisterUserPage: NextPage = () => { const RegisterUserPage: NextPage = () => {
useRedirectWhenLoggedIn();
return ( return (
<Layout> <Layout>
<Head> <Head>
@@ -26,8 +29,3 @@ const RegisterUserPage: NextPage = () => {
}; };
export default RegisterUserPage; export default RegisterUserPage;
export const getServerSideProps = redirectIfLoggedIn({
destination: '/',
permanent: false,
});

View File

@@ -36,7 +36,6 @@ const sendCreateBeerCommentRequest = async ({
throw new Error('Invalid API response'); throw new Error('Invalid API response');
} }
console.log(parsedResponse);
const parsedPayload = BeerCommentQueryResult.safeParse(parsedResponse.data.payload); const parsedPayload = BeerCommentQueryResult.safeParse(parsedResponse.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {