mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-02-16 10:42:08 +00:00
Update page auth HOF type definitions
Added vercel config, update packages
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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}>{`${
|
||||||
|
|||||||
@@ -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,
|
|
||||||
},
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -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
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",
|
"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
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
|
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'
|
||||||
@@ -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();
|
||||||
|
|
||||||
|
|||||||
@@ -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")
|
||||||
|
|||||||
@@ -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
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