mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Merge pull request #8 from aaronpo97/auth
Update page auth HOF type definitions
This commit is contained in:
@@ -5,6 +5,7 @@ import { FC, useContext, useEffect, useState } from 'react';
|
||||
import { BeerPostQueryResult } from '@/services/BeerPost/schema/BeerPostQueryResult';
|
||||
|
||||
import UserContext from '@/contexts/userContext';
|
||||
import { FaRegEdit } from 'react-icons/fa';
|
||||
import BeerPostLikeButton from './BeerPostLikeButton';
|
||||
|
||||
const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: number }> = ({
|
||||
@@ -16,6 +17,18 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
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(() => {
|
||||
setLikeCount(initialLikeCount);
|
||||
@@ -28,16 +41,31 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
|
||||
return (
|
||||
<div className="card flex flex-col justify-center bg-base-300">
|
||||
<div className="card-body">
|
||||
<h1 className="text-4xl font-bold">{beerPost.name}</h1>
|
||||
<h2 className="text-2xl font-semibold">
|
||||
by{' '}
|
||||
<Link
|
||||
href={`/breweries/${beerPost.brewery.id}`}
|
||||
className="link-hover link text-2xl font-semibold"
|
||||
>
|
||||
{beerPost.brewery.name}
|
||||
</Link>
|
||||
</h2>
|
||||
<div className="flex justify-between">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold">{beerPost.name}</h1>
|
||||
<h2 className="text-2xl font-semibold">
|
||||
by{' '}
|
||||
<Link
|
||||
href={`/breweries/${beerPost.brewery.id}`}
|
||||
className="link-hover link text-2xl font-semibold"
|
||||
>
|
||||
{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">
|
||||
posted by{' '}
|
||||
@@ -53,7 +81,7 @@ const BeerInfoHeader: FC<{ beerPost: BeerPostQueryResult; initialLikeCount: numb
|
||||
</h3>
|
||||
|
||||
<p>{beerPost.description}</p>
|
||||
<div className="mt-5 flex justify-between">
|
||||
<div className="flex justify-between">
|
||||
<div className="space-y-1">
|
||||
<div>
|
||||
<Link
|
||||
|
||||
@@ -161,24 +161,28 @@ const BeerForm: FunctionComponent<BeerFormProps> = ({
|
||||
/>
|
||||
</FormSegment>
|
||||
|
||||
<FormInfo>
|
||||
<FormLabel htmlFor="typeId">Type</FormLabel>
|
||||
<FormError>{errors.typeId?.message}</FormError>
|
||||
</FormInfo>
|
||||
<FormSegment>
|
||||
<FormSelect
|
||||
disabled={isSubmitting}
|
||||
formRegister={register('typeId')}
|
||||
error={!!errors.typeId}
|
||||
id="typeId"
|
||||
options={types.map((beerType) => ({
|
||||
value: beerType.id,
|
||||
text: beerType.name,
|
||||
}))}
|
||||
placeholder="Beer type"
|
||||
message="Pick a beer type"
|
||||
/>
|
||||
</FormSegment>
|
||||
{formType === 'create' && types.length && (
|
||||
<>
|
||||
<FormInfo>
|
||||
<FormLabel htmlFor="typeId">Type</FormLabel>
|
||||
<FormError>{errors.typeId?.message}</FormError>
|
||||
</FormInfo>
|
||||
<FormSegment>
|
||||
<FormSelect
|
||||
disabled={isSubmitting}
|
||||
formRegister={register('typeId')}
|
||||
error={!!errors.typeId}
|
||||
id="typeId"
|
||||
options={types.map((beerType) => ({
|
||||
value: beerType.id,
|
||||
text: beerType.name,
|
||||
}))}
|
||||
placeholder="Beer type"
|
||||
message="Pick a beer type"
|
||||
/>
|
||||
</FormSegment>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!isSubmitting && (
|
||||
<Button type="submit" isSubmitting={isSubmitting}>{`${
|
||||
|
||||
@@ -1,24 +1,32 @@
|
||||
import { GetServerSideProps, GetServerSidePropsContext } from 'next';
|
||||
import { GetServerSidePropsContext, GetServerSidePropsResult, PreviewData } from 'next';
|
||||
import { ParsedUrlQuery } from 'querystring';
|
||||
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 =
|
||||
(fn?: GetServerSideProps) => async (context: GetServerSidePropsContext) => {
|
||||
<P extends { [key: string]: any } = { [key: string]: any }>(
|
||||
fn?: ExtendedGetServerSideProps<P>,
|
||||
) =>
|
||||
async (context: GetServerSidePropsContext) => {
|
||||
try {
|
||||
const { req } = context;
|
||||
await getLoginSession(req);
|
||||
const session = await getLoginSession(req);
|
||||
|
||||
if (!fn) {
|
||||
return { props: {} };
|
||||
}
|
||||
return await fn(context);
|
||||
|
||||
return await fn(context, session);
|
||||
} catch (error) {
|
||||
console.log(error);
|
||||
return {
|
||||
redirect: {
|
||||
destination: '/login',
|
||||
permanent: false,
|
||||
},
|
||||
};
|
||||
return { redirect: { destination: '/login', permanent: false } };
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@@ -12,7 +12,6 @@ const useUser = () => {
|
||||
const response = await fetch(url);
|
||||
|
||||
if (!response.ok) {
|
||||
document.cookie = 'token=; expires=Thu, 01 Jan 1970 00:00:00 UTC; path=/;';
|
||||
throw new Error(response.statusText);
|
||||
}
|
||||
|
||||
|
||||
4040
package-lock.json
generated
4040
package-lock.json
generated
File diff suppressed because it is too large
Load Diff
42
package.json
42
package.json
@@ -10,59 +10,55 @@
|
||||
"format": "npx prettier . --write",
|
||||
"prestart": "npm run build",
|
||||
"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"
|
||||
},
|
||||
"dependencies": {
|
||||
"@hapi/iron": "7.0.0",
|
||||
"@hapi/iron": "^7.0.1",
|
||||
"@hookform/resolvers": "^2.9.10",
|
||||
"@next/font": "13.1.6",
|
||||
"@prisma/client": "^4.9.0",
|
||||
"@prisma/client": "^4.10.1",
|
||||
"argon2": "^0.30.3",
|
||||
"cloudinary": "^1.33.0",
|
||||
"cloudinary": "^1.34.0",
|
||||
"cookie": "0.5.0",
|
||||
"date-fns": "^2.29.3",
|
||||
"multer": "^1.4.5-lts.1",
|
||||
"multer": "^2.0.0-rc.4",
|
||||
"multer-storage-cloudinary": "^4.0.0",
|
||||
"next": "13.1.6",
|
||||
"next": "^13.2.1",
|
||||
"next-connect": "^1.0.0-next.3",
|
||||
"passport": "^0.6.0",
|
||||
"passport-local": "^1.0.0",
|
||||
"pino": "^8.8.0",
|
||||
"pino-pretty": "^9.1.1",
|
||||
"pino": "^8.11.0",
|
||||
"pino-pretty": "^9.3.0",
|
||||
"react": "18.2.0",
|
||||
"react-daisyui": "^3.0.2",
|
||||
"react-daisyui": "^3.0.3",
|
||||
"react-dom": "18.2.0",
|
||||
"react-hook-form": "^7.43.0",
|
||||
"react-hook-form": "^7.43.2",
|
||||
"react-icons": "^4.7.1",
|
||||
"swr": "^2.0.3",
|
||||
"zod": "^3.20.2"
|
||||
"zod": "^3.20.6"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@faker-js/faker": "^7.6.0",
|
||||
"@mermaid-js/mermaid-cli": "^9.3.0",
|
||||
"@types/cookie": "^0.5.1",
|
||||
"@types/multer": "^1.4.7",
|
||||
"@types/node": "^18.13.0",
|
||||
"@types/node": "^18.14.1",
|
||||
"@types/passport-local": "^1.0.35",
|
||||
"@types/react": "18.0.27",
|
||||
"@types/react-dom": "18.0.10",
|
||||
"@types/react": "^18.0.28",
|
||||
"@types/react-dom": "^18.0.11",
|
||||
"autoprefixer": "^10.4.13",
|
||||
"daisyui": "^2.50.0",
|
||||
"daisyui": "^2.51.0",
|
||||
"dotenv-cli": "^7.0.0",
|
||||
"eslint": "8.33.0",
|
||||
"eslint": "^8.34.0",
|
||||
"eslint-config-airbnb-base": "15.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-plugin-react": "^7.32.2",
|
||||
"postcss": "^8.4.21",
|
||||
"prettier": "^2.8.3",
|
||||
"prettier-plugin-jsdoc": "^0.4.2",
|
||||
"prettier-plugin-tailwindcss": "^0.2.2",
|
||||
"prisma": "^4.9.0",
|
||||
"prisma-erd-generator": "^1.2.5",
|
||||
"tailwindcss": "^3.2.4",
|
||||
"prettier-plugin-tailwindcss": "^0.2.3",
|
||||
"prisma": "^4.10.1",
|
||||
"tailwindcss": "^3.2.7",
|
||||
"ts-node": "^10.9.1",
|
||||
"typescript": "^4.9.5"
|
||||
}
|
||||
|
||||
51
pages/beers/[id]/edit.tsx
Normal file
51
pages/beers/[id]/edit.tsx
Normal 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)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
@@ -106,7 +106,7 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({
|
||||
Next Comments
|
||||
</Link>
|
||||
<Link
|
||||
className={`btn btn-outline ${
|
||||
className={`btn-outline btn ${
|
||||
commentsPageNum === commentsPageCount
|
||||
? 'btn-disabled pointer-events-none'
|
||||
: 'pointer-events-auto'
|
||||
@@ -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 beerTypes = await DBClient.instance.beerType.findMany();
|
||||
|
||||
|
||||
@@ -5,13 +5,6 @@ generator client {
|
||||
provider = "prisma-client-js"
|
||||
}
|
||||
|
||||
generator erdSVG {
|
||||
provider = "prisma-erd-generator"
|
||||
output = "./ERD.svg"
|
||||
includeRelationFromFields = true
|
||||
theme = "neutral"
|
||||
}
|
||||
|
||||
datasource db {
|
||||
provider = "postgresql"
|
||||
url = env("DATABASE_URL")
|
||||
|
||||
@@ -32,7 +32,7 @@ const createNewBeerPosts = async ({
|
||||
abv: Math.floor(Math.random() * (12 - 4) + 4),
|
||||
ibu: Math.floor(Math.random() * (60 - 10) + 10),
|
||||
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 } },
|
||||
postedBy: { connect: { id: user.id } },
|
||||
type: { connect: { id: beerType.id } },
|
||||
|
||||
3
vercel.json
Normal file
3
vercel.json
Normal file
@@ -0,0 +1,3 @@
|
||||
{
|
||||
"buildCommand": "npx prisma generate && npx prisma migrate deploy && next build"
|
||||
}
|
||||
Reference in New Issue
Block a user