Update page auth HOF type definitions

Added vercel config, update packages
This commit is contained in:
Aaron William Po
2023-02-25 19:17:49 -05:00
parent 6f604b9768
commit 11b3304c54
12 changed files with 841 additions and 3429 deletions

View File

@@ -5,6 +5,7 @@ import { FC, useContext, useEffect, useState } from 'react';
import { BeerPostQueryResult } from '@/services/BeerPost/schema/BeerPostQueryResult'; import { BeerPostQueryResult } from '@/services/BeerPost/schema/BeerPostQueryResult';
import UserContext from '@/contexts/userContext'; import UserContext from '@/contexts/userContext';
import { FaRegEdit } from 'react-icons/fa';
import BeerPostLikeButton from './BeerPostLikeButton'; import BeerPostLikeButton from './BeerPostLikeButton';
const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: number }> = ({ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: number }> = ({
@@ -16,6 +17,18 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
const [likeCount, setLikeCount] = useState(initialLikeCount); const [likeCount, setLikeCount] = useState(initialLikeCount);
const [isPostOwner, setIsPostOwner] = useState(false);
useEffect(() => {
const idMatches = user && beerPost.postedBy.id === user.id;
if (!(user && idMatches)) {
setIsPostOwner(false);
return;
}
setIsPostOwner(true);
}, [user, beerPost]);
useEffect(() => { useEffect(() => {
setLikeCount(initialLikeCount); setLikeCount(initialLikeCount);
@@ -28,16 +41,31 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
return ( return (
<div className="card flex flex-col justify-center bg-base-300"> <div className="card flex flex-col justify-center bg-base-300">
<div className="card-body"> <div className="card-body">
<h1 className="text-4xl font-bold">{beerPost.name}</h1> <div className="flex justify-between">
<h2 className="text-2xl font-semibold"> <div>
by{' '} <h1 className="text-4xl font-bold">{beerPost.name}</h1>
<Link <h2 className="text-2xl font-semibold">
href={`/breweries/${beerPost.brewery.id}`} by{' '}
className="link-hover link text-2xl font-semibold" <Link
> href={`/breweries/${beerPost.brewery.id}`}
{beerPost.brewery.name} className="link-hover link text-2xl font-semibold"
</Link> >
</h2> {beerPost.brewery.name}
</Link>
</h2>
</div>
<div>
{isPostOwner && (
<Link
className="btn-outline btn-sm btn gap-2 rounded-2xl"
href={`/beers/${beerPost.id}/edit`}
>
<FaRegEdit className="text-xl" />
Edit
</Link>
)}
</div>
</div>
<h3 className="italic"> <h3 className="italic">
posted by{' '} posted by{' '}
@@ -53,7 +81,7 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
</h3> </h3>
<p>{beerPost.description}</p> <p>{beerPost.description}</p>
<div className="mt-5 flex justify-between"> <div className="flex justify-between">
<div className="space-y-1"> <div className="space-y-1">
<div> <div>
<Link <Link

View File

@@ -161,24 +161,28 @@ const BeerForm: FunctionComponent<BeerFormProps> = ({
/> />
</FormSegment> </FormSegment>
<FormInfo> {formType === 'create' && types.length && (
<FormLabel htmlFor="typeId">Type</FormLabel> <>
<FormError>{errors.typeId?.message}</FormError> <FormInfo>
</FormInfo> <FormLabel htmlFor="typeId">Type</FormLabel>
<FormSegment> <FormError>{errors.typeId?.message}</FormError>
<FormSelect </FormInfo>
disabled={isSubmitting} <FormSegment>
formRegister={register('typeId')} <FormSelect
error={!!errors.typeId} disabled={isSubmitting}
id="typeId" formRegister={register('typeId')}
options={types.map((beerType) => ({ error={!!errors.typeId}
value: beerType.id, id="typeId"
text: beerType.name, options={types.map((beerType) => ({
}))} value: beerType.id,
placeholder="Beer type" text: beerType.name,
message="Pick a beer type" }))}
/> placeholder="Beer type"
</FormSegment> message="Pick a beer type"
/>
</FormSegment>
</>
)}
{!isSubmitting && ( {!isSubmitting && (
<Button type="submit" isSubmitting={isSubmitting}>{`${ <Button type="submit" isSubmitting={isSubmitting}>{`${

View File

@@ -1,24 +1,32 @@
import { GetServerSideProps, GetServerSidePropsContext } from 'next'; import { GetServerSidePropsContext, GetServerSidePropsResult, PreviewData } from 'next';
import { ParsedUrlQuery } from 'querystring';
import { getLoginSession } from './session'; import { getLoginSession } from './session';
export type ExtendedGetServerSideProps<
P extends { [key: string]: any } = { [key: string]: any },
Q extends ParsedUrlQuery = ParsedUrlQuery,
D extends PreviewData = PreviewData,
> = (
context: GetServerSidePropsContext<Q, D>,
session: Awaited<ReturnType<typeof getLoginSession>>,
) => Promise<GetServerSidePropsResult<P>>;
const withPageAuthRequired = const withPageAuthRequired =
(fn?: GetServerSideProps) => async (context: GetServerSidePropsContext) => { <P extends { [key: string]: any } = { [key: string]: any }>(
fn?: ExtendedGetServerSideProps<P>,
) =>
async (context: GetServerSidePropsContext) => {
try { try {
const { req } = context; const { req } = context;
await getLoginSession(req); const session = await getLoginSession(req);
if (!fn) { if (!fn) {
return { props: {} }; return { props: {} };
} }
return await fn(context);
return await fn(context, session);
} catch (error) { } catch (error) {
console.log(error); return { redirect: { destination: '/login', permanent: false } };
return {
redirect: {
destination: '/login',
permanent: false,
},
};
} }
}; };

View File

@@ -12,7 +12,6 @@ const useUser = () => {
const response = await fetch(url); const response = await fetch(url);
if (!response.ok) { if (!response.ok) {
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
throw new Error(response.statusText); throw new Error(response.statusText);
} }

4040
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@@ -10,59 +10,55 @@
"format": "npx prettier . --write", "format": "npx prettier . --write",
"prestart": "npm run build", "prestart": "npm run build",
"prismaDev": "dotenv -e .env.local prisma migrate dev", "prismaDev": "dotenv -e .env.local prisma migrate dev",
"vercel-build": "DISABLE_ERD=true npx prisma generate && npx prisma migrate deploy && next build",
"seed": "npx ts-node ./prisma/seed/index.ts" "seed": "npx ts-node ./prisma/seed/index.ts"
}, },
"dependencies": { "dependencies": {
"@hapi/iron": "7.0.0", "@hapi/iron": "^7.0.1",
"@hookform/resolvers": "^2.9.10", "@hookform/resolvers": "^2.9.10",
"@next/font": "13.1.6", "@prisma/client": "^4.10.1",
"@prisma/client": "^4.9.0",
"argon2": "^0.30.3", "argon2": "^0.30.3",
"cloudinary": "^1.33.0", "cloudinary": "^1.34.0",
"cookie": "0.5.0", "cookie": "0.5.0",
"date-fns": "^2.29.3", "date-fns": "^2.29.3",
"multer": "^1.4.5-lts.1", "multer": "^2.0.0-rc.4",
"multer-storage-cloudinary": "^4.0.0", "multer-storage-cloudinary": "^4.0.0",
"next": "13.1.6", "next": "^13.2.1",
"next-connect": "^1.0.0-next.3", "next-connect": "^1.0.0-next.3",
"passport": "^0.6.0", "passport": "^0.6.0",
"passport-local": "^1.0.0", "passport-local": "^1.0.0",
"pino": "^8.8.0", "pino": "^8.11.0",
"pino-pretty": "^9.1.1", "pino-pretty": "^9.3.0",
"react": "18.2.0", "react": "18.2.0",
"react-daisyui": "^3.0.2", "react-daisyui": "^3.0.3",
"react-dom": "18.2.0", "react-dom": "18.2.0",
"react-hook-form": "^7.43.0", "react-hook-form": "^7.43.2",
"react-icons": "^4.7.1", "react-icons": "^4.7.1",
"swr": "^2.0.3", "swr": "^2.0.3",
"zod": "^3.20.2" "zod": "^3.20.6"
}, },
"devDependencies": { "devDependencies": {
"@faker-js/faker": "^7.6.0", "@faker-js/faker": "^7.6.0",
"@mermaid-js/mermaid-cli": "^9.3.0",
"@types/cookie": "^0.5.1", "@types/cookie": "^0.5.1",
"@types/multer": "^1.4.7", "@types/multer": "^1.4.7",
"@types/node": "^18.13.0", "@types/node": "^18.14.1",
"@types/passport-local": "^1.0.35", "@types/passport-local": "^1.0.35",
"@types/react": "18.0.27", "@types/react": "^18.0.28",
"@types/react-dom": "18.0.10", "@types/react-dom": "^18.0.11",
"autoprefixer": "^10.4.13", "autoprefixer": "^10.4.13",
"daisyui": "^2.50.0", "daisyui": "^2.51.0",
"dotenv-cli": "^7.0.0", "dotenv-cli": "^7.0.0",
"eslint": "8.33.0", "eslint": "^8.34.0",
"eslint-config-airbnb-base": "15.0.0", "eslint-config-airbnb-base": "15.0.0",
"eslint-config-airbnb-typescript": "17.0.0", "eslint-config-airbnb-typescript": "17.0.0",
"eslint-config-next": "^13.1.6", "eslint-config-next": "^13.2.1",
"eslint-config-prettier": "^8.6.0", "eslint-config-prettier": "^8.6.0",
"eslint-plugin-react": "^7.32.2", "eslint-plugin-react": "^7.32.2",
"postcss": "^8.4.21", "postcss": "^8.4.21",
"prettier": "^2.8.3", "prettier": "^2.8.3",
"prettier-plugin-jsdoc": "^0.4.2", "prettier-plugin-jsdoc": "^0.4.2",
"prettier-plugin-tailwindcss": "^0.2.2", "prettier-plugin-tailwindcss": "^0.2.3",
"prisma": "^4.9.0", "prisma": "^4.10.1",
"prisma-erd-generator": "^1.2.5", "tailwindcss": "^3.2.7",
"tailwindcss": "^3.2.4",
"ts-node": "^10.9.1", "ts-node": "^10.9.1",
"typescript": "^4.9.5" "typescript": "^4.9.5"
} }

51
pages/beers/[id]/edit.tsx Normal file
View File

@@ -0,0 +1,51 @@
import Head from 'next/head';
import React from 'react';
import Layout from '@/components/ui/Layout';
import { NextPage } from 'next';
import withPageAuthRequired from '@/config/auth/withPageAuthRequired';
import getBeerPostById from '@/services/BeerPost/getBeerPostById';
import { BeerPostQueryResult } from '@/services/BeerPost/schema/BeerPostQueryResult';
interface EditPageProps {
beerPost: BeerPostQueryResult;
}
const EditPage: NextPage<EditPageProps> = ({ beerPost }) => {
return (
<Layout>
<Head>
<title>Edit {beerPost.name}</title>
<meta name="description" content={`Edit ${beerPost.name}`} />
</Head>
</Layout>
);
};
export default EditPage;
export const getServerSideProps = withPageAuthRequired<EditPageProps>(
async (context, session) => {
const beerPostId = context.params?.id as string;
const beerPost = await getBeerPostById(beerPostId);
const { id: userId } = session;
if (!beerPost) {
return { notFound: true };
}
const isBeerPostOwner = beerPost.postedBy.id === userId;
if (!isBeerPostOwner) {
return {
redirect: { destination: `/beers/${beerPostId}`, permanent: false },
};
}
return {
props: {
beerPost: JSON.parse(JSON.stringify(beerPost)),
},
};
},
);

View File

@@ -106,7 +106,7 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({
Next Comments Next Comments
</Link> </Link>
<Link <Link
className={`btn btn-outline ${ className={`btn-outline btn ${
commentsPageNum === commentsPageCount commentsPageNum === commentsPageCount
? 'btn-disabled pointer-events-none' ? 'btn-disabled pointer-events-none'
: 'pointer-events-auto' : 'pointer-events-auto'

View File

@@ -31,7 +31,7 @@ const Create: NextPage<CreateBeerPageProps> = ({ breweries, types }) => {
); );
}; };
export const getServerSideProps = withPageAuthRequired(async () => { export const getServerSideProps = withPageAuthRequired<CreateBeerPageProps>(async () => {
const breweryPosts = await getAllBreweryPosts(); const breweryPosts = await getAllBreweryPosts();
const beerTypes = await DBClient.instance.beerType.findMany(); const beerTypes = await DBClient.instance.beerType.findMany();

View File

@@ -5,13 +5,6 @@ generator client {
provider = "prisma-client-js" provider = "prisma-client-js"
} }
generator erdSVG {
provider = "prisma-erd-generator"
output = "./ERD.svg"
includeRelationFromFields = true
theme = "neutral"
}
datasource db { datasource db {
provider = "postgresql" provider = "postgresql"
url = env("DATABASE_URL") url = env("DATABASE_URL")

View File

@@ -32,7 +32,7 @@ const createNewBeerPosts = async ({
abv: Math.floor(Math.random() * (12 - 4) + 4), abv: Math.floor(Math.random() * (12 - 4) + 4),
ibu: Math.floor(Math.random() * (60 - 10) + 10), ibu: Math.floor(Math.random() * (60 - 10) + 10),
name: faker.commerce.productName(), name: faker.commerce.productName(),
description: faker.lorem.lines(24), description: faker.lorem.lines(42).replace(/(\r\n|\n|\r)/gm, ' '),
brewery: { connect: { id: breweryPost.id } }, brewery: { connect: { id: breweryPost.id } },
postedBy: { connect: { id: user.id } }, postedBy: { connect: { id: user.id } },
type: { connect: { id: beerType.id } }, type: { connect: { id: beerType.id } },

3
vercel.json Normal file
View File

@@ -0,0 +1,3 @@
{
"buildCommand": "npx prisma generate && npx prisma migrate deploy && next build"
}