mirror of
https://github.com/aaronpo97/the-biergarten-app.git
synced 2026-07-17 01:47:22 +00:00
Move next js project to archive (#207)
This commit is contained in:
21
archive/next-js-web-app/src/pages/404.tsx
Normal file
21
archive/next-js-web-app/src/pages/404.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
|
||||
const NotFound: NextPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>404 Page Not Found</title>
|
||||
<meta name="description" content="404 Page Not Found" />
|
||||
</Head>
|
||||
<div className="mx-2 flex h-dvh flex-col items-center justify-center text-center lg:mx-0">
|
||||
<h1 className="text-3xl font-bold lg:text-5xl">404: Not Found</h1>
|
||||
<h2 className="text-lg font-bold">
|
||||
Sorry, the page you are looking for does not exist.
|
||||
</h2>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default NotFound;
|
||||
21
archive/next-js-web-app/src/pages/500.tsx
Normal file
21
archive/next-js-web-app/src/pages/500.tsx
Normal file
@@ -0,0 +1,21 @@
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
|
||||
const ServerErrorPage: NextPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>500 Internal Server Error</title>
|
||||
<meta name="description" content="500 Internal Server Error" />
|
||||
</Head>
|
||||
<div className="mx-2 flex h-full flex-col items-center justify-center text-center lg:mx-0">
|
||||
<h1 className="text-2xl font-bold lg:text-4xl">500: Something Went Wrong</h1>
|
||||
<h2 className="text-lg font-bold">
|
||||
Please try again later or contact us if the problem persists.
|
||||
</h2>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ServerErrorPage;
|
||||
49
archive/next-js-web-app/src/pages/_app.tsx
Normal file
49
archive/next-js-web-app/src/pages/_app.tsx
Normal file
@@ -0,0 +1,49 @@
|
||||
import { AuthProvider } from '@/contexts/UserContext';
|
||||
|
||||
import '@/styles/globals.css';
|
||||
import type { AppProps } from 'next/app';
|
||||
import { useEffect } from 'react';
|
||||
import { themeChange } from 'theme-change';
|
||||
import { Analytics } from '@vercel/analytics/react';
|
||||
|
||||
import { Nunito_Sans } from 'next/font/google';
|
||||
import Head from 'next/head';
|
||||
import Layout from '@/components/ui/Layout';
|
||||
import CustomToast from '@/components/ui/CustomToast';
|
||||
|
||||
const font = Nunito_Sans({ subsets: ['latin-ext'] });
|
||||
|
||||
const App = ({ Component, pageProps }: AppProps) => {
|
||||
useEffect(() => {
|
||||
themeChange(false);
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<>
|
||||
<style jsx global>
|
||||
{`
|
||||
html {
|
||||
font-family: ${font.style.fontFamily};
|
||||
}
|
||||
`}
|
||||
</style>
|
||||
<Head>
|
||||
<meta
|
||||
name="viewport"
|
||||
content="width=device-width, initial-scale=1.0, maximum-scale=1.0"
|
||||
/>
|
||||
</Head>
|
||||
<AuthProvider>
|
||||
<Layout>
|
||||
<CustomToast>
|
||||
<Component {...pageProps} />
|
||||
</CustomToast>
|
||||
</Layout>
|
||||
</AuthProvider>
|
||||
|
||||
<Analytics />
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default App;
|
||||
32
archive/next-js-web-app/src/pages/_document.tsx
Normal file
32
archive/next-js-web-app/src/pages/_document.tsx
Normal file
@@ -0,0 +1,32 @@
|
||||
import { Html, Head, Main, NextScript } from 'next/document';
|
||||
|
||||
export default function Document() {
|
||||
return (
|
||||
<Html lang="en">
|
||||
<Head>
|
||||
<link
|
||||
rel="apple-touch-icon"
|
||||
sizes="180x180"
|
||||
href="favicon/apple-touch-icon.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="32x32"
|
||||
href="favicon/favicon-32x32.png"
|
||||
/>
|
||||
<link
|
||||
rel="icon"
|
||||
type="image/png"
|
||||
sizes="16x16"
|
||||
href="favicon/favicon-16x16.png"
|
||||
/>
|
||||
<link rel="manifest" href="favicon/site.webmanifest" />
|
||||
</Head>
|
||||
<body>
|
||||
<Main />
|
||||
<NextScript />
|
||||
</body>
|
||||
</Html>
|
||||
);
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { CommentRequest } from '@/controllers/comments/types';
|
||||
import {
|
||||
checkIfBeerCommentOwner,
|
||||
deleteBeerPostComment,
|
||||
editBeerPostComment,
|
||||
} from '@/controllers/comments/beer-comments';
|
||||
|
||||
const router = createRouter<
|
||||
CommentRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router
|
||||
.delete(
|
||||
validateRequest({
|
||||
querySchema: z.object({ postId: z.string().cuid(), commentId: z.string().cuid() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBeerCommentOwner,
|
||||
deleteBeerPostComment,
|
||||
)
|
||||
.put(
|
||||
validateRequest({
|
||||
querySchema: z.object({ postId: z.string().cuid(), commentId: z.string().cuid() }),
|
||||
bodySchema: CreateCommentValidationSchema,
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBeerCommentOwner,
|
||||
editBeerPostComment,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,39 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { NextApiResponse } from 'next';
|
||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||
import {
|
||||
createBeerPostComment,
|
||||
getAllBeerPostComments,
|
||||
} from '@/controllers/comments/beer-comments';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
const router = createRouter<
|
||||
// @TODO: Fix this any type
|
||||
any,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({
|
||||
bodySchema: CreateCommentValidationSchema,
|
||||
querySchema: z.object({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
createBeerPostComment,
|
||||
);
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getAllBeerPostComments,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,31 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { createRouter } from 'next-connect';
|
||||
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
|
||||
import { NextApiResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
|
||||
import { uploadMiddlewareMultiple } from '@/config/multer/uploadMiddleware';
|
||||
import { UploadImagesRequest } from '@/controllers/images/types';
|
||||
import { processBeerImageData } from '@/controllers/images/beer-images';
|
||||
|
||||
const router = createRouter<
|
||||
UploadImagesRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
getCurrentUser,
|
||||
// @ts-expect-error
|
||||
uploadMiddlewareMultiple,
|
||||
validateRequest({ bodySchema: ImageMetadataValidationSchema }),
|
||||
processBeerImageData,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
|
||||
export const config = { api: { bodyParser: false } };
|
||||
@@ -0,0 +1,44 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import { EditBeerPostRequest } from '@/controllers/posts/beer-posts/types';
|
||||
import {
|
||||
checkIfBeerPostOwner,
|
||||
editBeerPost,
|
||||
deleteBeerPost,
|
||||
} from '@/controllers/posts/beer-posts';
|
||||
|
||||
import EditBeerPostValidationSchema from '@/services/posts/beer-post/schema/EditBeerPostValidationSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
const router = createRouter<
|
||||
EditBeerPostRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router
|
||||
.put(
|
||||
validateRequest({
|
||||
bodySchema: EditBeerPostValidationSchema,
|
||||
querySchema: z.object({ postId: z.string() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBeerPostOwner,
|
||||
editBeerPost,
|
||||
)
|
||||
.delete(
|
||||
validateRequest({ querySchema: z.object({ postId: z.string() }) }),
|
||||
getCurrentUser,
|
||||
checkIfBeerPostOwner,
|
||||
deleteBeerPost,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,33 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import { NextApiResponse } from 'next';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import {
|
||||
sendBeerPostLikeRequest,
|
||||
getBeerPostLikeCount,
|
||||
} from '@/controllers/likes/beer-posts-likes';
|
||||
import { LikeRequest } from '@/controllers/likes/types';
|
||||
|
||||
const router = createRouter<
|
||||
LikeRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
sendBeerPostLikeRequest,
|
||||
);
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
getBeerPostLikeCount,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,24 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { checkIfBeerPostIsLiked } from '@/controllers/likes/beer-posts-likes';
|
||||
|
||||
const router = createRouter<
|
||||
UserExtendedNextApiRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
checkIfBeerPostIsLiked,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,25 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getBeerPostRecommendations } from '@/controllers/posts/beer-posts';
|
||||
import { GetBeerRecommendationsRequest } from '@/controllers/posts/beer-posts/types';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetBeerRecommendationsRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getBeerPostRecommendations,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
25
archive/next-js-web-app/src/pages/api/beers/create.ts
Normal file
25
archive/next-js-web-app/src/pages/api/beers/create.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { createRouter } from 'next-connect';
|
||||
|
||||
import CreateBeerPostValidationSchema from '@/services/posts/beer-post/schema/CreateBeerPostValidationSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { createBeerPost } from '@/controllers/posts/beer-posts';
|
||||
import { CreateBeerPostRequest } from '@/controllers/posts/beer-posts/types';
|
||||
|
||||
const router = createRouter<
|
||||
CreateBeerPostRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({ bodySchema: CreateBeerPostValidationSchema }),
|
||||
getCurrentUser,
|
||||
createBeerPost,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
25
archive/next-js-web-app/src/pages/api/beers/index.ts
Normal file
25
archive/next-js-web-app/src/pages/api/beers/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getBeerPosts } from '@/controllers/posts/beer-posts';
|
||||
import { GetAllBeerPostsRequest } from '@/controllers/posts/beer-posts/types';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetAllBeerPostsRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema,
|
||||
}),
|
||||
getBeerPosts,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
67
archive/next-js-web-app/src/pages/api/beers/search.ts
Normal file
67
archive/next-js-web-app/src/pages/api/beers/search.ts
Normal file
@@ -0,0 +1,67 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||
|
||||
const SearchSchema = z.object({
|
||||
search: z.string().min(1),
|
||||
});
|
||||
|
||||
interface SearchAPIRequest extends NextApiRequest {
|
||||
query: z.infer<typeof SearchSchema>;
|
||||
}
|
||||
|
||||
const search = async (req: SearchAPIRequest, res: NextApiResponse) => {
|
||||
const { search: query } = req.query;
|
||||
|
||||
const beers: z.infer<typeof BeerPostQueryResult>[] =
|
||||
await DBClient.instance.beerPost.findMany({
|
||||
select: {
|
||||
id: true,
|
||||
name: true,
|
||||
ibu: true,
|
||||
abv: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
description: true,
|
||||
postedBy: { select: { username: true, id: true } },
|
||||
brewery: { select: { name: true, id: true } },
|
||||
style: { select: { name: true, id: true, description: true } },
|
||||
beerImages: {
|
||||
select: {
|
||||
alt: true,
|
||||
path: true,
|
||||
caption: true,
|
||||
id: true,
|
||||
createdAt: true,
|
||||
updatedAt: true,
|
||||
},
|
||||
},
|
||||
},
|
||||
where: {
|
||||
OR: [
|
||||
{ name: { contains: query, mode: 'insensitive' } },
|
||||
{ description: { contains: query, mode: 'insensitive' } },
|
||||
{ brewery: { name: { contains: query, mode: 'insensitive' } } },
|
||||
{ style: { name: { contains: query, mode: 'insensitive' } } },
|
||||
],
|
||||
},
|
||||
});
|
||||
|
||||
res.status(200).json(beers);
|
||||
};
|
||||
|
||||
const router = createRouter<
|
||||
SearchAPIRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(validateRequest({}), search);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,29 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getAllBeersByBeerStyle } from '@/controllers/posts/beer-styles';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
interface GetAllBeersByBeerStyleRequest extends NextApiRequest {
|
||||
query: { page_size: string; page_num: string; id: string };
|
||||
}
|
||||
|
||||
const router = createRouter<
|
||||
GetAllBeersByBeerStyleRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getAllBeersByBeerStyle,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,42 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import {
|
||||
checkIfBeerStyleCommentOwner,
|
||||
deleteBeerStyleComment,
|
||||
editBeerStyleComment,
|
||||
} from '@/controllers/comments/beer-style-comments';
|
||||
import { CommentRequest } from '@/controllers/comments/types';
|
||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
CommentRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router
|
||||
.delete(
|
||||
validateRequest({
|
||||
querySchema: z.object({ postId: z.string().cuid(), commentId: z.string().cuid() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBeerStyleCommentOwner,
|
||||
deleteBeerStyleComment,
|
||||
)
|
||||
.put(
|
||||
validateRequest({
|
||||
querySchema: z.object({ postId: z.string().cuid(), commentId: z.string().cuid() }),
|
||||
bodySchema: CreateCommentValidationSchema,
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBeerStyleCommentOwner,
|
||||
editBeerStyleComment,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,36 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { NextApiResponse } from 'next';
|
||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||
import { createComment, getAll } from '@/controllers/comments/beer-style-comments';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
const router = createRouter<
|
||||
// I don't want to use any, but I can't figure out how to get the types to work
|
||||
any,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({
|
||||
bodySchema: CreateCommentValidationSchema,
|
||||
querySchema: z.object({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
createComment,
|
||||
);
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getAll,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,22 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getBeerStyle } from '@/controllers/posts/beer-styles';
|
||||
import { GetBeerStyleByIdRequest } from '@/controllers/posts/beer-styles/types';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetBeerStyleByIdRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
getBeerStyle,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,34 @@
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import { NextApiResponse } from 'next';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import {
|
||||
getBeerStyleLikeCountRequest,
|
||||
sendBeerStyleLikeRequest,
|
||||
} from '@/controllers/likes/beer-style-likes';
|
||||
import { LikeRequest } from '@/controllers/likes/types';
|
||||
|
||||
const router = createRouter<
|
||||
LikeRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
sendBeerStyleLikeRequest,
|
||||
);
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
getBeerStyleLikeCountRequest,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,23 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import { checkIfBeerStyleIsLiked } from '@/controllers/likes/beer-style-likes';
|
||||
|
||||
const router = createRouter<
|
||||
UserExtendedNextApiRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
checkIfBeerStyleIsLiked,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
24
archive/next-js-web-app/src/pages/api/beers/styles/create.ts
Normal file
24
archive/next-js-web-app/src/pages/api/beers/styles/create.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { createBeerStyle } from '@/controllers/posts/beer-styles';
|
||||
import { CreateBeerStyleRequest } from '@/controllers/posts/beer-styles/types';
|
||||
import CreateBeerStyleValidationSchema from '@/services/posts/beer-style-post/schema/CreateBeerStyleValidationSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
CreateBeerStyleRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({ bodySchema: CreateBeerStyleValidationSchema }),
|
||||
getCurrentUser,
|
||||
createBeerStyle,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
26
archive/next-js-web-app/src/pages/api/beers/styles/index.ts
Normal file
26
archive/next-js-web-app/src/pages/api/beers/styles/index.ts
Normal file
@@ -0,0 +1,26 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getBeerStyles } from '@/controllers/posts/beer-styles';
|
||||
import { GetAllPostsRequest } from '@/controllers/posts/types';
|
||||
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetAllPostsRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema,
|
||||
}),
|
||||
getBeerStyles,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,25 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getAllBeersByBrewery } from '@/controllers/posts/breweries';
|
||||
import { GetAllPostsByConnectedPostId } from '@/controllers/posts/types';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetAllPostsByConnectedPostId,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getAllBeersByBrewery,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,44 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import {
|
||||
checkIfBreweryCommentOwner,
|
||||
deleteBreweryPostComment,
|
||||
editBreweryPostComment,
|
||||
} from '@/controllers/comments/brewery-comments';
|
||||
import { CommentRequest } from '@/controllers/comments/types';
|
||||
|
||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
CommentRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router
|
||||
.delete(
|
||||
validateRequest({
|
||||
querySchema: z.object({ commentId: z.string().cuid(), postId: z.string().cuid() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBreweryCommentOwner,
|
||||
deleteBreweryPostComment,
|
||||
)
|
||||
.put(
|
||||
validateRequest({
|
||||
querySchema: z.object({ commentId: z.string().cuid(), postId: z.string().cuid() }),
|
||||
bodySchema: CreateCommentValidationSchema,
|
||||
}),
|
||||
getCurrentUser,
|
||||
checkIfBreweryCommentOwner,
|
||||
editBreweryPostComment,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,37 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { NextApiResponse } from 'next';
|
||||
|
||||
import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
|
||||
import { createComment, getAll } from '@/controllers/comments/brewery-comments';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
const router = createRouter<
|
||||
// I don't want to use any, but I can't figure out how to get the types to work
|
||||
any,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({
|
||||
bodySchema: CreateCommentValidationSchema,
|
||||
querySchema: z.object({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getCurrentUser,
|
||||
createComment,
|
||||
);
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ postId: z.string().cuid() }),
|
||||
}),
|
||||
getAll,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,31 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { createRouter } from 'next-connect';
|
||||
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
|
||||
import { NextApiResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
|
||||
import { uploadMiddlewareMultiple } from '@/config/multer/uploadMiddleware';
|
||||
import { UploadImagesRequest } from '@/controllers/images/types';
|
||||
import { processBreweryImageData } from '@/controllers/images/brewery-images';
|
||||
|
||||
const router = createRouter<
|
||||
UploadImagesRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
getCurrentUser,
|
||||
// @ts-expect-error
|
||||
uploadMiddlewareMultiple,
|
||||
validateRequest({ bodySchema: ImageMetadataValidationSchema }),
|
||||
processBreweryImageData,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
|
||||
export const config = { api: { bodyParser: false } };
|
||||
@@ -0,0 +1,25 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import {
|
||||
checkIfBreweryPostOwner,
|
||||
editBreweryPost,
|
||||
deleteBreweryPost,
|
||||
} from '@/controllers/posts/breweries';
|
||||
import { EditBreweryPostRequest } from '@/controllers/posts/breweries/types';
|
||||
|
||||
const router = createRouter<
|
||||
EditBreweryPostRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.put(getCurrentUser, checkIfBreweryPostOwner, editBreweryPost);
|
||||
router.delete(getCurrentUser, checkIfBreweryPostOwner, deleteBreweryPost);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,33 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import {
|
||||
sendBreweryPostLikeRequest,
|
||||
getBreweryPostLikeCount,
|
||||
} from '@/controllers/likes/brewery-post-likes';
|
||||
import { LikeRequest } from '@/controllers/likes/types';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
LikeRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
sendBreweryPostLikeRequest,
|
||||
);
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
getBreweryPostLikeCount,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,24 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getBreweryPostLikeStatus } from '@/controllers/likes/brewery-post-likes';
|
||||
import { LikeRequest } from '@/controllers/likes/types';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
LikeRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: z.object({ postId: z.string().cuid() }) }),
|
||||
getBreweryPostLikeStatus,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
25
archive/next-js-web-app/src/pages/api/breweries/create.ts
Normal file
25
archive/next-js-web-app/src/pages/api/breweries/create.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { createRouter } from 'next-connect';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import CreateBreweryPostSchema from '@/services/posts/brewery-post/schema/CreateBreweryPostSchema';
|
||||
|
||||
import { CreateBreweryPostRequest } from '@/controllers/posts/breweries/types';
|
||||
import { createBreweryPost } from '@/controllers/posts/breweries';
|
||||
|
||||
const router = createRouter<
|
||||
CreateBreweryPostRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({ bodySchema: CreateBreweryPostSchema }),
|
||||
getCurrentUser,
|
||||
createBreweryPost,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
23
archive/next-js-web-app/src/pages/api/breweries/index.ts
Normal file
23
archive/next-js-web-app/src/pages/api/breweries/index.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getBreweryPosts } from '@/controllers/posts/breweries';
|
||||
import { GetBreweryPostsRequest } from '@/controllers/posts/breweries/types';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetBreweryPostsRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: PaginatedQueryResponseSchema }),
|
||||
getBreweryPosts,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
25
archive/next-js-web-app/src/pages/api/breweries/map/index.ts
Normal file
25
archive/next-js-web-app/src/pages/api/breweries/map/index.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getMapBreweryPosts } from '@/controllers/posts/breweries';
|
||||
import { GetBreweryPostsRequest } from '@/controllers/posts/breweries/types';
|
||||
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetBreweryPostsRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: PaginatedQueryResponseSchema }),
|
||||
getMapBreweryPosts,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { followUser } from '@/controllers/users/profile';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
interface GetUserFollowInfoRequest extends UserExtendedNextApiRequest {
|
||||
query: { id: string };
|
||||
}
|
||||
|
||||
const router = createRouter<
|
||||
GetUserFollowInfoRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
||||
getCurrentUser,
|
||||
followUser,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,26 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getUserFollowers } from '@/controllers/users/profile';
|
||||
import { GetUserFollowInfoRequest } from '@/controllers/users/profile/types';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
GetUserFollowInfoRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ id: z.string().cuid() }),
|
||||
}),
|
||||
getUserFollowers,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,30 @@
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { getUsersFollowed } from '@/controllers/users/profile';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
interface GetUserFollowInfoRequest extends UserExtendedNextApiRequest {
|
||||
query: { id: string; page_size: string; page_num: string };
|
||||
}
|
||||
|
||||
const router = createRouter<
|
||||
GetUserFollowInfoRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ id: z.string().cuid() }),
|
||||
}),
|
||||
getUsersFollowed,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
41
archive/next-js-web-app/src/pages/api/users/[id]/index.ts
Normal file
41
archive/next-js-web-app/src/pages/api/users/[id]/index.ts
Normal file
@@ -0,0 +1,41 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { editUserInfo, deleteAccount } from '@/controllers/users/auth';
|
||||
import { checkIfUserCanEditUser } from '@/controllers/users/profile';
|
||||
import { EditUserRequest } from '@/controllers/users/profile/types';
|
||||
import EditUserSchema from '@/services/users/auth/schema/EditUserSchema';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
EditUserRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.put(
|
||||
getCurrentUser,
|
||||
validateRequest({
|
||||
bodySchema: EditUserSchema,
|
||||
querySchema: z.object({ id: z.string().cuid() }),
|
||||
}),
|
||||
checkIfUserCanEditUser,
|
||||
editUserInfo,
|
||||
);
|
||||
|
||||
router.delete(
|
||||
getCurrentUser,
|
||||
validateRequest({
|
||||
querySchema: z.object({ id: z.string().cuid() }),
|
||||
}),
|
||||
checkIfUserCanEditUser,
|
||||
deleteAccount,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
|
||||
import { checkIfUserIsFollowedBySessionUser } from '@/controllers/users/profile';
|
||||
import { GetUserFollowInfoRequest } from '@/controllers/users/profile/types';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
|
||||
const router = createRouter<
|
||||
GetUserFollowInfoRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ id: z.string().cuid() }) }),
|
||||
getCurrentUser,
|
||||
checkIfUserIsFollowedBySessionUser,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,25 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { GetAllPostsByConnectedPostId } from '@/controllers/posts/types';
|
||||
import { getBeerPostsByUserId } from '@/controllers/posts/beer-posts';
|
||||
|
||||
const router = createRouter<
|
||||
GetAllPostsByConnectedPostId,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ id: z.string().cuid() }),
|
||||
}),
|
||||
getBeerPostsByUserId,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,25 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import PaginatedQueryResponseSchema from '@/services/schema/PaginatedQueryResponseSchema';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { getBreweryPostsByUserId } from '@/controllers/posts/breweries';
|
||||
import { GetAllPostsByConnectedPostId } from '@/controllers/posts/types';
|
||||
|
||||
const router = createRouter<
|
||||
GetAllPostsByConnectedPostId,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({
|
||||
querySchema: PaginatedQueryResponseSchema.extend({ id: z.string().cuid() }),
|
||||
}),
|
||||
getBreweryPostsByUserId,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,27 @@
|
||||
import { singleUploadMiddleware } from '@/config/multer/uploadMiddleware';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { checkIfUserCanUpdateProfile, updateAvatar } from '@/controllers/users/profile';
|
||||
import { UpdateProfileRequest } from '@/controllers/users/profile/types';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
UpdateProfileRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.put(
|
||||
getCurrentUser,
|
||||
checkIfUserCanUpdateProfile,
|
||||
// @ts-expect-error
|
||||
singleUploadMiddleware,
|
||||
updateAvatar,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
export const config = { api: { bodyParser: false } };
|
||||
@@ -0,0 +1,25 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { checkIfUserCanUpdateProfile, updateProfile } from '@/controllers/users/profile';
|
||||
import { UpdateProfileRequest } from '@/controllers/users/profile/types';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
UpdateProfileRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.put(
|
||||
getCurrentUser,
|
||||
checkIfUserCanUpdateProfile,
|
||||
validateRequest({ bodySchema: z.object({ bio: z.string().max(1000) }) }),
|
||||
updateProfile,
|
||||
);
|
||||
|
||||
const handler = router.handler();
|
||||
|
||||
export default handler;
|
||||
23
archive/next-js-web-app/src/pages/api/users/check-email.ts
Normal file
23
archive/next-js-web-app/src/pages/api/users/check-email.ts
Normal file
@@ -0,0 +1,23 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { CheckEmailRequest } from '@/controllers/users/auth/types';
|
||||
import { checkEmail } from '@/controllers/users/auth';
|
||||
|
||||
const router = createRouter<
|
||||
CheckEmailRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ email: z.string().email() }) }),
|
||||
checkEmail,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
@@ -0,0 +1,24 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
|
||||
import { CheckUsernameRequest } from '@/controllers/users/auth/types';
|
||||
import { checkUsername } from '@/controllers/users/auth';
|
||||
|
||||
const router = createRouter<
|
||||
CheckUsernameRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
validateRequest({ querySchema: z.object({ username: z.string() }) }),
|
||||
checkUsername,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
27
archive/next-js-web-app/src/pages/api/users/confirm.ts
Normal file
27
archive/next-js-web-app/src/pages/api/users/confirm.ts
Normal file
@@ -0,0 +1,27 @@
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
|
||||
import { TokenValidationRequest } from '@/controllers/users/auth/types';
|
||||
import { confirmUser } from '@/controllers/users/auth';
|
||||
import TokenValidationSchema from '@/services/users/auth/schema/TokenValidationSchema';
|
||||
|
||||
const router = createRouter<
|
||||
TokenValidationRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(
|
||||
getCurrentUser,
|
||||
validateRequest({ querySchema: TokenValidationSchema }),
|
||||
confirmUser,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
|
||||
export default handler;
|
||||
18
archive/next-js-web-app/src/pages/api/users/current.ts
Normal file
18
archive/next-js-web-app/src/pages/api/users/current.ts
Normal file
@@ -0,0 +1,18 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import { sendCurrentUser } from '@/controllers/users/auth';
|
||||
|
||||
const router = createRouter<
|
||||
UserExtendedNextApiRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.get(getCurrentUser, sendCurrentUser);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
25
archive/next-js-web-app/src/pages/api/users/edit-password.ts
Normal file
25
archive/next-js-web-app/src/pages/api/users/edit-password.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { updatePassword } from '@/controllers/users/auth';
|
||||
import { UpdatePasswordRequest } from '@/controllers/users/auth/types';
|
||||
import { UpdatePasswordSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
const router = createRouter<
|
||||
UpdatePasswordRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.put(
|
||||
validateRequest({ bodySchema: UpdatePasswordSchema }),
|
||||
getCurrentUser,
|
||||
updatePassword,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,22 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { resetPassword } from '@/controllers/users/auth';
|
||||
import { ResetPasswordRequest } from '@/controllers/users/auth/types';
|
||||
|
||||
const router = createRouter<
|
||||
ResetPasswordRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({ bodySchema: z.object({ email: z.string().email() }) }),
|
||||
resetPassword,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
24
archive/next-js-web-app/src/pages/api/users/login.ts
Normal file
24
archive/next-js-web-app/src/pages/api/users/login.ts
Normal file
@@ -0,0 +1,24 @@
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { NextApiResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import { authenticateUser, loginUser } from '@/controllers/users/auth';
|
||||
import LoginValidationSchema from '@/services/users/auth/schema/LoginValidationSchema';
|
||||
|
||||
const router = createRouter<
|
||||
UserExtendedNextApiRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({ bodySchema: LoginValidationSchema }),
|
||||
authenticateUser,
|
||||
loginUser,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
16
archive/next-js-web-app/src/pages/api/users/logout.ts
Normal file
16
archive/next-js-web-app/src/pages/api/users/logout.ts
Normal file
@@ -0,0 +1,16 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiRequest, NextApiResponse } from 'next';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import { logoutUser } from '@/controllers/users/auth';
|
||||
|
||||
const router = createRouter<
|
||||
NextApiRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.all(logoutUser);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
25
archive/next-js-web-app/src/pages/api/users/register.ts
Normal file
25
archive/next-js-web-app/src/pages/api/users/register.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { NextApiResponse } from 'next';
|
||||
import { z } from 'zod';
|
||||
import { createRouter } from 'next-connect';
|
||||
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import validateRequest from '@/config/nextConnect/middleware/validateRequest';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { registerUser } from '@/controllers/users/auth';
|
||||
import { RegisterUserRequest } from '@/controllers/users/auth/types';
|
||||
import { CreateUserValidationSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||
|
||||
const router = createRouter<
|
||||
RegisterUserRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(
|
||||
validateRequest({
|
||||
bodySchema: CreateUserValidationSchema,
|
||||
}),
|
||||
registerUser,
|
||||
);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
@@ -0,0 +1,18 @@
|
||||
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
|
||||
import { UserExtendedNextApiRequest } from '@/config/auth/types';
|
||||
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
|
||||
import { NextApiResponse } from 'next';
|
||||
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
|
||||
import { createRouter } from 'next-connect';
|
||||
import { z } from 'zod';
|
||||
import { resendConfirmation } from '@/controllers/users/auth';
|
||||
|
||||
const router = createRouter<
|
||||
UserExtendedNextApiRequest,
|
||||
NextApiResponse<z.infer<typeof APIResponseValidationSchema>>
|
||||
>();
|
||||
|
||||
router.post(getCurrentUser, resendConfirmation);
|
||||
|
||||
const handler = router.handler(NextConnectOptions);
|
||||
export default handler;
|
||||
65
archive/next-js-web-app/src/pages/beers/[id]/edit.tsx
Normal file
65
archive/next-js-web-app/src/pages/beers/[id]/edit.tsx
Normal file
@@ -0,0 +1,65 @@
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import React from 'react';
|
||||
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||
import EditBeerPostForm from '@/components/EditBeerPostForm';
|
||||
import FormPageLayout from '@/components/ui/forms/FormPageLayout';
|
||||
import { BiBeer } from 'react-icons/bi';
|
||||
import { z } from 'zod';
|
||||
import { getBeerPostById } from '@/services/posts/beer-post';
|
||||
|
||||
interface EditPageProps {
|
||||
beerPost: z.infer<typeof BeerPostQueryResult>;
|
||||
}
|
||||
|
||||
const EditBeerPostPage: NextPage<EditPageProps> = ({ beerPost }) => {
|
||||
const pageTitle = `Edit \u201c${beerPost.name}\u201d`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{pageTitle}</title>
|
||||
<meta name="description" content={pageTitle} />
|
||||
</Head>
|
||||
|
||||
<FormPageLayout
|
||||
headingText={pageTitle}
|
||||
headingIcon={BiBeer}
|
||||
backLink={`/beers/${beerPost.id}`}
|
||||
backLinkText={`Back to "${beerPost.name}"`}
|
||||
>
|
||||
<EditBeerPostForm
|
||||
previousValues={{
|
||||
name: beerPost.name,
|
||||
abv: beerPost.abv,
|
||||
ibu: beerPost.ibu,
|
||||
description: beerPost.description,
|
||||
id: beerPost.id,
|
||||
}}
|
||||
/>
|
||||
</FormPageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditBeerPostPage;
|
||||
|
||||
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;
|
||||
|
||||
return isBeerPostOwner
|
||||
? { props: { beerPost: JSON.parse(JSON.stringify(beerPost)) } }
|
||||
: { redirect: { destination: `/beers/${beerPostId}`, permanent: false } };
|
||||
},
|
||||
);
|
||||
114
archive/next-js-web-app/src/pages/beers/[id]/index.tsx
Normal file
114
archive/next-js-web-app/src/pages/beers/[id]/index.tsx
Normal file
@@ -0,0 +1,114 @@
|
||||
import { NextPage, GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
|
||||
import BeerPostQueryResult from '@/services/posts/beer-post/schema/BeerPostQueryResult';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import 'react-responsive-carousel/lib/styles/carousel.min.css';
|
||||
import { Carousel } from 'react-responsive-carousel';
|
||||
import useMediaQuery from '@/hooks/utilities/useMediaQuery';
|
||||
import { Tab } from '@headlessui/react';
|
||||
import dynamic from 'next/dynamic';
|
||||
import { CldImage } from 'next-cloudinary';
|
||||
import { getBeerPostById } from '@/services/posts/beer-post';
|
||||
|
||||
const [BeerInfoHeader, BeerPostCommentsSection, BeerRecommendations] = [
|
||||
dynamic(() => import('@/components/BeerById/BeerInfoHeader')),
|
||||
dynamic(() => import('@/components/BeerById/BeerPostCommentsSection')),
|
||||
dynamic(() => import('@/components/BeerById/BeerRecommendations')),
|
||||
];
|
||||
|
||||
interface BeerPageProps {
|
||||
beerPost: z.infer<typeof BeerPostQueryResult>;
|
||||
}
|
||||
|
||||
const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost }) => {
|
||||
const isDesktop = useMediaQuery('(min-width: 1024px)');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{beerPost.name}</title>
|
||||
<meta name="description" content={beerPost.description} />
|
||||
</Head>
|
||||
<div className="mt-10">
|
||||
<Carousel
|
||||
className="w-full"
|
||||
useKeyboardArrows
|
||||
autoPlay
|
||||
interval={10000}
|
||||
infiniteLoop
|
||||
showThumbs={false}
|
||||
>
|
||||
{beerPost.beerImages.length
|
||||
? beerPost.beerImages.map((image, index) => (
|
||||
<div key={image.id} id={`image-${index}}`} className="w-full">
|
||||
<CldImage
|
||||
alt={image.alt}
|
||||
src={image.path}
|
||||
height={1080}
|
||||
crop="fill"
|
||||
width={1920}
|
||||
className="h-96 w-full object-cover lg:h-[42rem]"
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
: Array.from({ length: 1 }).map((_, i) => (
|
||||
<div className="h-96 lg:h-[42rem]" key={i} />
|
||||
))}
|
||||
</Carousel>
|
||||
|
||||
<main className="mb-12 mt-10 flex w-full items-center justify-center">
|
||||
<div className="w-11/12 space-y-3 xl:w-9/12 2xl:w-8/12">
|
||||
<BeerInfoHeader beerPost={beerPost} />
|
||||
|
||||
{isDesktop ? (
|
||||
<div className="mt-4 flex flex-row space-x-3 space-y-0">
|
||||
<div className="w-[60%]">
|
||||
<BeerPostCommentsSection beerPost={beerPost} />
|
||||
</div>
|
||||
<div className="w-[40%]">
|
||||
<BeerRecommendations beerPost={beerPost} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Tab.Group>
|
||||
<Tab.List className="tabs-boxed tabs grid grid-cols-2">
|
||||
<Tab className="tab uppercase ui-selected:tab-active">Comments</Tab>
|
||||
<Tab className="tab uppercase ui-selected:tab-active">Other Beers</Tab>
|
||||
</Tab.List>
|
||||
<Tab.Panels className="mt-2">
|
||||
<Tab.Panel>
|
||||
<BeerPostCommentsSection beerPost={beerPost} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<BeerRecommendations beerPost={beerPost} />
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<BeerPageProps> = async (context) => {
|
||||
const beerPost = await getBeerPostById({
|
||||
beerPostId: context.params?.id as string,
|
||||
});
|
||||
|
||||
if (!beerPost) {
|
||||
return { notFound: true };
|
||||
}
|
||||
|
||||
const props = {
|
||||
beerPost: JSON.parse(JSON.stringify(beerPost)),
|
||||
};
|
||||
|
||||
return { props };
|
||||
};
|
||||
|
||||
export default BeerByIdPage;
|
||||
101
archive/next-js-web-app/src/pages/beers/index.tsx
Normal file
101
archive/next-js-web-app/src/pages/beers/index.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import { NextPage } from 'next';
|
||||
import BeerCard from '@/components/BeerIndex/BeerCard';
|
||||
import Head from 'next/head';
|
||||
import { MutableRefObject, useRef } from 'react';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import Spinner from '@/components/ui/Spinner';
|
||||
import useBeerPosts from '@/hooks/data-fetching/beer-posts/useBeerPosts';
|
||||
import { FaArrowUp } from 'react-icons/fa';
|
||||
import LoadingCard from '@/components/ui/LoadingCard';
|
||||
|
||||
const BeerPage: NextPage = () => {
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
const { beerPosts, setSize, size, isLoading, isLoadingMore, isAtEnd } = useBeerPosts({
|
||||
pageSize: PAGE_SIZE,
|
||||
});
|
||||
|
||||
const { ref: lastBeerPostRef } = useInView({
|
||||
onChange: (visible) => {
|
||||
if (!visible || isAtEnd) return;
|
||||
setSize(size + 1);
|
||||
},
|
||||
});
|
||||
|
||||
const pageRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Beers | The Biergarten App</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Find beers made by breweries near you and around the world."
|
||||
/>
|
||||
</Head>
|
||||
<div className="mt-10 flex items-center justify-center bg-base-100" ref={pageRef}>
|
||||
<div className="my-10 flex w-10/12 flex-col space-y-4 lg:w-8/12 2xl:w-7/12">
|
||||
<header className="my-10 flex justify-between lg:flex-row">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold lg:text-6xl">The Biergarten App</h1>
|
||||
<h2 className="text-2xl font-bold lg:text-4xl">Beers</h2>
|
||||
</div>
|
||||
</header>
|
||||
<div className="grid gap-6 xl:grid-cols-2">
|
||||
{!!beerPosts.length && !isLoading && (
|
||||
<>
|
||||
{beerPosts.map((beerPost, i) => {
|
||||
return (
|
||||
<div
|
||||
key={beerPost.id}
|
||||
ref={beerPosts.length === i + 1 ? lastBeerPostRef : undefined}
|
||||
>
|
||||
<BeerCard post={beerPost} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<>
|
||||
{Array.from({ length: PAGE_SIZE }, (_, i) => (
|
||||
<LoadingCard key={i} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<div className="flex h-32 w-full items-center justify-center">
|
||||
<Spinner size="sm" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isAtEnd && !isLoading && (
|
||||
<div className="flex h-20 items-center justify-center text-center">
|
||||
<div
|
||||
className="tooltip tooltip-bottom"
|
||||
data-tip="Scroll back to top of page."
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-sm"
|
||||
aria-label="Scroll back to top of page."
|
||||
onClick={() => {
|
||||
pageRef.current?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<FaArrowUp />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BeerPage;
|
||||
76
archive/next-js-web-app/src/pages/beers/search.tsx
Normal file
76
archive/next-js-web-app/src/pages/beers/search.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import { NextPage } from 'next';
|
||||
|
||||
import { useRouter } from 'next/router';
|
||||
import BeerCard from '@/components/BeerIndex/BeerCard';
|
||||
import { ChangeEvent, useEffect, useState } from 'react';
|
||||
import Spinner from '@/components/ui/Spinner';
|
||||
|
||||
import debounce from 'lodash/debounce';
|
||||
import useBeerPostSearch from '@/hooks/data-fetching/beer-posts/useBeerPostSearch';
|
||||
import FormLabel from '@/components/ui/forms/FormLabel';
|
||||
|
||||
const DEBOUNCE_DELAY = 300;
|
||||
|
||||
const SearchPage: NextPage = () => {
|
||||
const router = useRouter();
|
||||
const querySearch = (router.query.search as string) || '';
|
||||
const [searchValue, setSearchValue] = useState(querySearch);
|
||||
const { searchResults, isLoading, searchError } = useBeerPostSearch(searchValue);
|
||||
|
||||
const debounceSearch = debounce((value: string) => {
|
||||
router.push({
|
||||
pathname: '/beers/search',
|
||||
query: { search: value },
|
||||
});
|
||||
}, DEBOUNCE_DELAY);
|
||||
|
||||
const onChange = (event: ChangeEvent<HTMLInputElement>) => {
|
||||
const { value } = event.target;
|
||||
setSearchValue(value);
|
||||
debounceSearch(value);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
debounce(() => {
|
||||
if (!querySearch || searchValue) {
|
||||
return;
|
||||
}
|
||||
setSearchValue(querySearch);
|
||||
}, DEBOUNCE_DELAY)();
|
||||
}, [querySearch, searchValue]);
|
||||
|
||||
const showSearchResults = !isLoading && searchResults && !searchError;
|
||||
|
||||
return (
|
||||
<div className="flex h-full w-full flex-col items-center justify-center">
|
||||
<div className="h-full w-full space-y-20">
|
||||
<div className="flex h-[50%] w-full items-center justify-center bg-base-200">
|
||||
<div className="w-8/12">
|
||||
<FormLabel htmlFor="search">What are you looking for?</FormLabel>
|
||||
<input
|
||||
type="text"
|
||||
id="search"
|
||||
className="input input-bordered w-full rounded-lg"
|
||||
onChange={onChange}
|
||||
value={searchValue}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center justify-center">
|
||||
{!showSearchResults ? (
|
||||
<Spinner size="lg" />
|
||||
) : (
|
||||
<div className="grid w-8/12 gap-4 md:grid-cols-2 lg:grid-cols-3">
|
||||
{searchResults.map((result) => {
|
||||
return <BeerCard key={result.id} post={result} />;
|
||||
})}
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default SearchPage;
|
||||
@@ -0,0 +1,77 @@
|
||||
import { NextPage, GetServerSideProps } from 'next';
|
||||
import Head from 'next/head';
|
||||
|
||||
import { z } from 'zod';
|
||||
|
||||
import useMediaQuery from '@/hooks/utilities/useMediaQuery';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
import BeerStyleHeader from '@/components/BeerStyleById/BeerStyleHeader';
|
||||
import BeerStyleQueryResult from '@/services/posts/beer-style-post/schema/BeerStyleQueryResult';
|
||||
import BeerStyleCommentSection from '@/components/BeerStyleById/BeerStyleCommentSection';
|
||||
import BeerStyleBeerSection from '@/components/BeerStyleById/BeerStyleBeerSection';
|
||||
import { getBeerStyleByIdService } from '@/services/posts/beer-style-post';
|
||||
|
||||
interface BeerStylePageProps {
|
||||
beerStyle: z.infer<typeof BeerStyleQueryResult>;
|
||||
}
|
||||
|
||||
const BeerStyleByIdPage: NextPage<BeerStylePageProps> = ({ beerStyle }) => {
|
||||
const isDesktop = useMediaQuery('(min-width: 1024px)');
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{beerStyle.name}</title>
|
||||
<meta name="description" content={beerStyle.description} />
|
||||
</Head>
|
||||
<>
|
||||
<main className="mb-12 mt-10 flex w-full items-center justify-center">
|
||||
<div className="mt-10 w-11/12 space-y-3 xl:w-9/12 2xl:w-8/12">
|
||||
<BeerStyleHeader beerStyle={beerStyle} />
|
||||
|
||||
{isDesktop ? (
|
||||
<div className="mt-4 flex flex-row space-x-3 space-y-0">
|
||||
<div className="w-[60%]">
|
||||
<BeerStyleCommentSection beerStyle={beerStyle} />
|
||||
</div>
|
||||
<div className="w-[40%]">
|
||||
<BeerStyleBeerSection beerStyle={beerStyle} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<Tab.Group>
|
||||
<Tab.List className="tabs-boxed tabs grid grid-cols-2">
|
||||
<Tab className="tab uppercase ui-selected:tab-active">Comments</Tab>
|
||||
<Tab className="tab uppercase ui-selected:tab-active">
|
||||
Beers in this Style
|
||||
</Tab>
|
||||
</Tab.List>
|
||||
<Tab.Panels className="mt-2">
|
||||
<Tab.Panel>
|
||||
<BeerStyleCommentSection beerStyle={beerStyle} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<BeerStyleBeerSection beerStyle={beerStyle} />
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
)}
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BeerStyleByIdPage;
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async ({ params }) => {
|
||||
const id = params!.id as string;
|
||||
const beerStyle = await getBeerStyleByIdService({ beerStyleId: id });
|
||||
if (!beerStyle) {
|
||||
return { notFound: true };
|
||||
}
|
||||
|
||||
return { props: { beerStyle: JSON.parse(JSON.stringify(beerStyle)) } };
|
||||
};
|
||||
101
archive/next-js-web-app/src/pages/beers/styles/index.tsx
Normal file
101
archive/next-js-web-app/src/pages/beers/styles/index.tsx
Normal file
@@ -0,0 +1,101 @@
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { MutableRefObject, useRef } from 'react';
|
||||
import { FaArrowUp } from 'react-icons/fa';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
|
||||
import BeerStyleCard from '@/components/BeerStyleIndex/BeerStyleCard';
|
||||
import SmLoadingCard from '@/components/ui/SmLoadingCard';
|
||||
import Spinner from '@/components/ui/Spinner';
|
||||
import useBeerStyles from '@/hooks/data-fetching/beer-styles/useBeerStyles';
|
||||
|
||||
const BeerStylePage: NextPage = () => {
|
||||
const PAGE_SIZE = 20;
|
||||
const pageRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
|
||||
|
||||
const { beerStyles, isLoading, isLoadingMore, isAtEnd, size, setSize } = useBeerStyles({
|
||||
pageSize: PAGE_SIZE,
|
||||
});
|
||||
|
||||
const { ref: lastBeerStyleRef } = useInView({
|
||||
onChange: (visible) => {
|
||||
if (!visible || isAtEnd) return;
|
||||
setSize(size + 1);
|
||||
},
|
||||
});
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Beer Styles | The Biergarten App</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Find beers made by breweries near you and around the world."
|
||||
/>
|
||||
</Head>
|
||||
<div className="mt-10 flex items-center justify-center bg-base-100" ref={pageRef}>
|
||||
<div className="my-10 flex w-10/12 flex-col space-y-4 lg:w-8/12 2xl:w-7/12">
|
||||
<header className="my-10 flex justify-between lg:flex-row">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold lg:text-6xl">The Biergarten App</h1>
|
||||
<h2 className="text-2xl font-bold lg:text-4xl">Styles</h2>
|
||||
</div>
|
||||
</header>
|
||||
<div className="grid gap-6 xl:grid-cols-2">
|
||||
{!!beerStyles.length && !isLoading && (
|
||||
<>
|
||||
{beerStyles.map((beerStyle, i) => {
|
||||
return (
|
||||
<div
|
||||
key={beerStyle.id}
|
||||
ref={beerStyles.length === i + 1 ? lastBeerStyleRef : undefined}
|
||||
>
|
||||
<BeerStyleCard beerStyle={beerStyle} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<>
|
||||
{Array.from({ length: PAGE_SIZE }, (_, i) => (
|
||||
<SmLoadingCard key={i} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<div className="flex h-32 w-full items-center justify-center">
|
||||
<Spinner size="sm" />
|
||||
</div>
|
||||
)}
|
||||
|
||||
{isAtEnd && !isLoading && (
|
||||
<div className="flex h-20 items-center justify-center text-center">
|
||||
<div
|
||||
className="tooltip tooltip-bottom"
|
||||
data-tip="Scroll back to top of page."
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-sm"
|
||||
aria-label="Scroll back to top of page."
|
||||
onClick={() => {
|
||||
pageRef.current?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<FaArrowUp />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BeerStylePage;
|
||||
@@ -0,0 +1,47 @@
|
||||
import CreateBeerPostForm from '@/components/CreateBeerPostForm';
|
||||
import FormPageLayout from '@/components/ui/forms/FormPageLayout';
|
||||
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import DBClient from '@/prisma/DBClient';
|
||||
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||
import { BeerStyle } from '@prisma/client';
|
||||
import { NextPage } from 'next';
|
||||
import { BiBeer } from 'react-icons/bi';
|
||||
import { z } from 'zod';
|
||||
import { getBreweryPostByIdService } from '@/services/posts/brewery-post';
|
||||
|
||||
interface CreateBeerPageProps {
|
||||
brewery: z.infer<typeof BreweryPostQueryResult>;
|
||||
styles: BeerStyle[];
|
||||
}
|
||||
|
||||
const CreateBeerPost: NextPage<CreateBeerPageProps> = ({ brewery, styles }) => {
|
||||
return (
|
||||
<FormPageLayout
|
||||
headingText="Create a new beer"
|
||||
headingIcon={BiBeer}
|
||||
backLink="/beers"
|
||||
backLinkText="Back to beers"
|
||||
>
|
||||
<CreateBeerPostForm brewery={brewery} styles={styles} />
|
||||
</FormPageLayout>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps = withPageAuthRequired<CreateBeerPageProps>(
|
||||
async (context) => {
|
||||
const id = context.params?.id as string;
|
||||
|
||||
const breweryPost = await getBreweryPostByIdService({ breweryPostId: id });
|
||||
const beerStyles = await DBClient.instance.beerStyle.findMany();
|
||||
|
||||
return {
|
||||
props: {
|
||||
brewery: JSON.parse(JSON.stringify(breweryPost)),
|
||||
styles: JSON.parse(JSON.stringify(beerStyles)),
|
||||
},
|
||||
};
|
||||
},
|
||||
);
|
||||
|
||||
export default CreateBeerPost;
|
||||
57
archive/next-js-web-app/src/pages/breweries/[id]/edit.tsx
Normal file
57
archive/next-js-web-app/src/pages/breweries/[id]/edit.tsx
Normal file
@@ -0,0 +1,57 @@
|
||||
import EditBreweryPostForm from '@/components/EditBreweryPostForm';
|
||||
import FormPageLayout from '@/components/ui/forms/FormPageLayout';
|
||||
import { getBreweryPostByIdService } from '@/services/posts/brewery-post';
|
||||
|
||||
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { BiBeer } from 'react-icons/bi';
|
||||
import { z } from 'zod';
|
||||
|
||||
interface EditPageProps {
|
||||
breweryPost: z.infer<typeof BreweryPostQueryResult>;
|
||||
}
|
||||
|
||||
const EditBreweryPostPage: NextPage<EditPageProps> = ({ breweryPost }) => {
|
||||
const pageTitle = `Edit \u201c${breweryPost.name}\u201d`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{pageTitle}</title>
|
||||
<meta name="description" content={pageTitle} />
|
||||
</Head>
|
||||
|
||||
<FormPageLayout
|
||||
headingText={pageTitle}
|
||||
headingIcon={BiBeer}
|
||||
backLink={`/breweries/${breweryPost.id}`}
|
||||
backLinkText={`Back to "${breweryPost.name}"`}
|
||||
>
|
||||
<EditBreweryPostForm breweryPost={breweryPost} />
|
||||
</FormPageLayout>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default EditBreweryPostPage;
|
||||
|
||||
export const getServerSideProps = withPageAuthRequired<EditPageProps>(
|
||||
async (context, session) => {
|
||||
const breweryPostId = context.params?.id as string;
|
||||
const breweryPost = await getBreweryPostByIdService({ breweryPostId });
|
||||
|
||||
const { id: userId } = session;
|
||||
|
||||
if (!breweryPost) {
|
||||
return { notFound: true };
|
||||
}
|
||||
|
||||
const isBreweryPostOwner = breweryPost.postedBy.id === userId;
|
||||
|
||||
return isBreweryPostOwner
|
||||
? { props: { breweryPost: JSON.parse(JSON.stringify(breweryPost)) } }
|
||||
: { redirect: { destination: `/breweries/${breweryPostId}`, permanent: false } };
|
||||
},
|
||||
);
|
||||
127
archive/next-js-web-app/src/pages/breweries/[id]/index.tsx
Normal file
127
archive/next-js-web-app/src/pages/breweries/[id]/index.tsx
Normal file
@@ -0,0 +1,127 @@
|
||||
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
|
||||
import { z } from 'zod';
|
||||
import Head from 'next/head';
|
||||
|
||||
import 'react-responsive-carousel/lib/styles/carousel.min.css'; // requires a loader
|
||||
import { Carousel } from 'react-responsive-carousel';
|
||||
import useMediaQuery from '@/hooks/utilities/useMediaQuery';
|
||||
import { Tab } from '@headlessui/react';
|
||||
|
||||
import dynamic from 'next/dynamic';
|
||||
import { MAPBOX_ACCESS_TOKEN } from '@/config/env';
|
||||
import { CldImage } from 'next-cloudinary';
|
||||
import { getBreweryPostByIdService } from '@/services/posts/brewery-post';
|
||||
|
||||
const [BreweryInfoHeader, BreweryBeersSection, BreweryCommentsSection, BreweryPostMap] = [
|
||||
dynamic(() => import('@/components/BreweryById/BreweryInfoHeader')),
|
||||
dynamic(() => import('@/components/BreweryById/BreweryBeerSection')),
|
||||
dynamic(() => import('@/components/BreweryById/BreweryCommentsSection')),
|
||||
dynamic(() => import('@/components/BreweryById/BreweryPostMap')),
|
||||
];
|
||||
|
||||
interface BreweryPageProps {
|
||||
breweryPost: z.infer<typeof BreweryPostQueryResult>;
|
||||
mapboxToken: string;
|
||||
}
|
||||
|
||||
const BreweryByIdPage: NextPage<BreweryPageProps> = ({ breweryPost, mapboxToken }) => {
|
||||
const [longitude, latitude] = breweryPost.location.coordinates;
|
||||
const isDesktop = useMediaQuery('(min-width: 1024px)');
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{breweryPost.name}</title>
|
||||
<meta name="description" content={breweryPost.description} />
|
||||
</Head>
|
||||
|
||||
<div className="mt-10">
|
||||
<Carousel
|
||||
className="w-full"
|
||||
useKeyboardArrows
|
||||
autoPlay
|
||||
interval={10000}
|
||||
infiniteLoop
|
||||
showThumbs={false}
|
||||
>
|
||||
{breweryPost.breweryImages.length
|
||||
? breweryPost.breweryImages.map((image, index) => (
|
||||
<div key={image.id} id={`image-${index}}`} className="w-full">
|
||||
<CldImage
|
||||
alt={image.alt}
|
||||
src={image.path}
|
||||
crop="fill"
|
||||
height={1080}
|
||||
width={1920}
|
||||
className="h-96 w-full object-cover lg:h-[42rem]"
|
||||
/>
|
||||
</div>
|
||||
))
|
||||
: Array.from({ length: 1 }).map((_, i) => (
|
||||
<div className="h-96 lg:h-[42rem]" key={i} />
|
||||
))}
|
||||
</Carousel>
|
||||
<div className="mb-12 mt-10 flex w-full items-center justify-center">
|
||||
<div className="w-11/12 space-y-3 xl:w-9/12 2xl:w-8/12">
|
||||
<BreweryInfoHeader breweryPost={breweryPost} />
|
||||
{isDesktop ? (
|
||||
<div className="mt-4 flex flex-row space-x-3 space-y-0">
|
||||
<div className="w-[60%]">
|
||||
<BreweryCommentsSection breweryPost={breweryPost} />
|
||||
</div>
|
||||
<div className="w-[40%] space-y-3">
|
||||
<BreweryPostMap
|
||||
coordinates={{ latitude, longitude }}
|
||||
token={mapboxToken}
|
||||
/>
|
||||
<BreweryBeersSection breweryPost={breweryPost} />
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<BreweryPostMap
|
||||
coordinates={{ latitude, longitude }}
|
||||
token={mapboxToken}
|
||||
/>
|
||||
<Tab.Group>
|
||||
<Tab.List className="tabs-boxed tabs grid grid-cols-2">
|
||||
<Tab className="tab-md tab w-1/2 uppercase ui-selected:tab-active">
|
||||
Comments
|
||||
</Tab>
|
||||
<Tab className="tab-md tab w-1/2 uppercase ui-selected:tab-active">
|
||||
Beers
|
||||
</Tab>
|
||||
</Tab.List>
|
||||
<Tab.Panels className="mt-2">
|
||||
<Tab.Panel>
|
||||
<BreweryCommentsSection breweryPost={breweryPost} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel>
|
||||
<BreweryBeersSection breweryPost={breweryPost} />
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<BreweryPageProps> = async (
|
||||
context,
|
||||
) => {
|
||||
const breweryPost = await getBreweryPostByIdService({
|
||||
breweryPostId: context.params?.id as string,
|
||||
});
|
||||
const mapboxToken = MAPBOX_ACCESS_TOKEN;
|
||||
|
||||
return !breweryPost
|
||||
? { notFound: true }
|
||||
: { props: { breweryPost: JSON.parse(JSON.stringify(breweryPost)), mapboxToken } };
|
||||
};
|
||||
|
||||
export default BreweryByIdPage;
|
||||
40
archive/next-js-web-app/src/pages/breweries/create.tsx
Normal file
40
archive/next-js-web-app/src/pages/breweries/create.tsx
Normal file
@@ -0,0 +1,40 @@
|
||||
import FormPageLayout from '@/components/ui/forms/FormPageLayout';
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
|
||||
import { FaBeer } from 'react-icons/fa';
|
||||
import CreateBreweryPostForm from '@/components/BreweryPost/CreateBreweryPostForm';
|
||||
import { MAPBOX_ACCESS_TOKEN } from '@/config/env';
|
||||
|
||||
interface CreateBreweryPageProps {
|
||||
mapboxAccessToken: string;
|
||||
}
|
||||
|
||||
const CreateBreweryPage: NextPage<CreateBreweryPageProps> = ({ mapboxAccessToken }) => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Create Brewery</title>
|
||||
</Head>
|
||||
<div className="flex w-full flex-col items-center justify-center">
|
||||
<div className="w-full">
|
||||
<FormPageLayout
|
||||
backLink="/breweries"
|
||||
backLinkText="Back to Breweries"
|
||||
headingText="Create Brewery"
|
||||
headingIcon={FaBeer}
|
||||
>
|
||||
<CreateBreweryPostForm mapboxAccessToken={mapboxAccessToken} />
|
||||
</FormPageLayout>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default CreateBreweryPage;
|
||||
|
||||
export const getServerSideProps = withPageAuthRequired<CreateBreweryPageProps>(
|
||||
async () => ({ props: { mapboxAccessToken: MAPBOX_ACCESS_TOKEN } }),
|
||||
);
|
||||
134
archive/next-js-web-app/src/pages/breweries/index.tsx
Normal file
134
archive/next-js-web-app/src/pages/breweries/index.tsx
Normal file
@@ -0,0 +1,134 @@
|
||||
import BreweryCard from '@/components/BreweryIndex/BreweryCard';
|
||||
import LoadingCard from '@/components/ui/LoadingCard';
|
||||
import Spinner from '@/components/ui/Spinner';
|
||||
import UserContext from '@/contexts/UserContext';
|
||||
import useBreweryPosts from '@/hooks/data-fetching/brewery-posts/useBreweryPosts';
|
||||
import BreweryPostQueryResult from '@/services/posts/brewery-post/schema/BreweryPostQueryResult';
|
||||
import { NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { useContext, MutableRefObject, useRef } from 'react';
|
||||
import Link from 'next/link';
|
||||
import { FaPlus, FaArrowUp, FaMap } from 'react-icons/fa';
|
||||
import { useInView } from 'react-intersection-observer';
|
||||
import { z } from 'zod';
|
||||
|
||||
interface BreweryPageProps {
|
||||
breweryPosts: z.infer<typeof BreweryPostQueryResult>[];
|
||||
}
|
||||
|
||||
const BreweryPage: NextPage<BreweryPageProps> = () => {
|
||||
const PAGE_SIZE = 20;
|
||||
|
||||
const { breweryPosts, setSize, size, isLoading, isLoadingMore, isAtEnd } =
|
||||
useBreweryPosts({
|
||||
pageSize: PAGE_SIZE,
|
||||
});
|
||||
|
||||
const { ref: lastBreweryPostRef } = useInView({
|
||||
onChange: (visible) => {
|
||||
if (!visible || isAtEnd) return;
|
||||
setSize(size + 1);
|
||||
},
|
||||
});
|
||||
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
const pageRef: MutableRefObject<HTMLDivElement | null> = useRef(null);
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Breweries</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Find breweries near you and around the world."
|
||||
/>
|
||||
</Head>
|
||||
<div className="mt-10 flex items-center justify-center bg-base-100" ref={pageRef}>
|
||||
<div className="my-10 flex w-10/12 flex-col space-y-4 lg:w-8/12 2xl:w-7/12">
|
||||
<header className="my-10 flex justify-between lg:flex-row">
|
||||
<div className="space-y-2">
|
||||
<div>
|
||||
<h1 className="text-4xl font-bold lg:text-6xl">The Biergarten App</h1>
|
||||
<h2 className="text-2xl font-bold lg:text-4xl">Breweries</h2>
|
||||
</div>
|
||||
</div>
|
||||
<div className="flex flex-col">
|
||||
{!!user && (
|
||||
<div
|
||||
className="tooltip tooltip-left"
|
||||
data-tip="Create a new brewery post"
|
||||
>
|
||||
<Link href="/breweries/create" className="btn btn-ghost btn-sm">
|
||||
<FaPlus className="text-lg" />
|
||||
</Link>
|
||||
</div>
|
||||
)}
|
||||
<div className="tooltip tooltip-left" data-tip="View map">
|
||||
<Link className="btn btn-ghost btn-sm" href="/breweries/map">
|
||||
<FaMap className="text-lg" />
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</header>
|
||||
<div className="grid gap-6 xl:grid-cols-2">
|
||||
{!!breweryPosts.length && !isLoading && (
|
||||
<>
|
||||
{breweryPosts.map((breweryPost) => {
|
||||
return (
|
||||
<div
|
||||
key={breweryPost.id}
|
||||
ref={
|
||||
breweryPosts[breweryPosts.length - 1] === breweryPost
|
||||
? lastBreweryPostRef
|
||||
: undefined
|
||||
}
|
||||
>
|
||||
<BreweryCard brewery={breweryPost} />
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
)}
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<>
|
||||
{Array.from({ length: PAGE_SIZE }, (_, i) => (
|
||||
<LoadingCard key={i} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
|
||||
{(isLoading || isLoadingMore) && (
|
||||
<div className="flex h-32 w-full items-center justify-center">
|
||||
<Spinner size="sm" />
|
||||
</div>
|
||||
)}
|
||||
{isAtEnd && !isLoading && (
|
||||
<div className="flex h-20 items-center justify-center text-center">
|
||||
<div
|
||||
className="tooltip tooltip-bottom"
|
||||
data-tip="Scroll back to top of page."
|
||||
>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-ghost btn-sm"
|
||||
aria-label="Scroll back to top of page."
|
||||
onClick={() => {
|
||||
pageRef.current?.scrollIntoView({
|
||||
behavior: 'smooth',
|
||||
block: 'start',
|
||||
});
|
||||
}}
|
||||
>
|
||||
<FaArrowUp />
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BreweryPage;
|
||||
132
archive/next-js-web-app/src/pages/breweries/map.tsx
Normal file
132
archive/next-js-web-app/src/pages/breweries/map.tsx
Normal file
@@ -0,0 +1,132 @@
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import { useEffect, useMemo, useState } from 'react';
|
||||
import Map, { Marker, Popup } from 'react-map-gl';
|
||||
import 'mapbox-gl/dist/mapbox-gl.css';
|
||||
|
||||
import LocationMarker from '@/components/ui/LocationMarker';
|
||||
import Link from 'next/link';
|
||||
import Head from 'next/head';
|
||||
import useGeolocation from '@/hooks/utilities/useGeolocation';
|
||||
import BreweryPostMapQueryResult from '@/services/posts/brewery-post/schema/BreweryPostMapQueryResult';
|
||||
import { z } from 'zod';
|
||||
import useBreweryMapPagePosts from '@/hooks/data-fetching/brewery-posts/useBreweryMapPagePosts';
|
||||
import ControlPanel from '@/components/ui/maps/ControlPanel';
|
||||
import { MAPBOX_ACCESS_TOKEN } from '@/config/env';
|
||||
|
||||
type MapStyles = Record<'light' | 'dark', `mapbox://styles/mapbox/${string}`>;
|
||||
|
||||
interface BreweryMapPageProps {
|
||||
token: string;
|
||||
}
|
||||
|
||||
const BreweryMapPage: NextPage<BreweryMapPageProps> = ({ token }) => {
|
||||
const [popupInfo, setPopupInfo] = useState<z.infer<
|
||||
typeof BreweryPostMapQueryResult
|
||||
> | null>(null);
|
||||
|
||||
const [theme, setTheme] = useState<'light' | 'dark'>('light');
|
||||
|
||||
useEffect(() => {
|
||||
setTheme(localStorage.getItem('theme') === 'dark' ? 'dark' : 'light');
|
||||
}, []);
|
||||
|
||||
const { breweries } = useBreweryMapPagePosts({ pageSize: 50 });
|
||||
|
||||
const mapStyles: MapStyles = {
|
||||
light: 'mapbox://styles/mapbox/light-v10',
|
||||
dark: 'mapbox://styles/mapbox/dark-v10',
|
||||
};
|
||||
|
||||
const pins = useMemo(
|
||||
() => (
|
||||
<>
|
||||
{breweries.map((brewery) => {
|
||||
const [longitude, latitude] = brewery.location.coordinates;
|
||||
|
||||
return (
|
||||
<Marker
|
||||
key={brewery.id}
|
||||
latitude={latitude}
|
||||
longitude={longitude}
|
||||
onClick={(e) => {
|
||||
e.originalEvent.stopPropagation();
|
||||
setPopupInfo(brewery);
|
||||
}}
|
||||
>
|
||||
<LocationMarker size="md" color="blue" />
|
||||
</Marker>
|
||||
);
|
||||
})}
|
||||
</>
|
||||
),
|
||||
[breweries],
|
||||
);
|
||||
|
||||
const { coords, error: geoError } = useGeolocation();
|
||||
|
||||
const userLocationPin = useMemo(
|
||||
() =>
|
||||
coords && !geoError ? (
|
||||
<Marker latitude={coords.latitude} longitude={coords.longitude}>
|
||||
<LocationMarker size="lg" color="red" />
|
||||
</Marker>
|
||||
) : null,
|
||||
[coords, geoError],
|
||||
);
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Brewery Map | The Biergarten App</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Find breweries near you and around the world."
|
||||
/>
|
||||
</Head>
|
||||
<div className="h-dvh">
|
||||
<Map
|
||||
// center the map on North America
|
||||
initialViewState={{ zoom: 3, latitude: 48.3544, longitude: -99.9981 }}
|
||||
style={{ width: '100%', height: '100%' }}
|
||||
mapStyle={mapStyles[theme]}
|
||||
mapboxAccessToken={token}
|
||||
scrollZoom
|
||||
>
|
||||
<ControlPanel />
|
||||
{pins}
|
||||
{userLocationPin}
|
||||
{popupInfo && (
|
||||
<Popup
|
||||
anchor="bottom"
|
||||
longitude={popupInfo.location.coordinates[0]}
|
||||
latitude={popupInfo.location.coordinates[1]}
|
||||
onClose={() => setPopupInfo(null)}
|
||||
>
|
||||
<div className="flex flex-col text-black">
|
||||
<Link
|
||||
className="link-hover link text-base font-bold"
|
||||
href={`/breweries/${popupInfo.id}`}
|
||||
>
|
||||
{popupInfo.name}
|
||||
</Link>
|
||||
<p className="text-base">
|
||||
{popupInfo.location.city}
|
||||
{popupInfo.location.stateOrProvince
|
||||
? `, ${popupInfo.location.stateOrProvince}`
|
||||
: ''}
|
||||
{popupInfo.location.country ? `, ${popupInfo.location.country}` : ''}
|
||||
</p>
|
||||
</div>
|
||||
</Popup>
|
||||
)}
|
||||
</Map>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default BreweryMapPage;
|
||||
|
||||
export const getServerSideProps: GetServerSideProps<BreweryMapPageProps> = async () => ({
|
||||
props: { token: MAPBOX_ACCESS_TOKEN },
|
||||
});
|
||||
60
archive/next-js-web-app/src/pages/index.tsx
Normal file
60
archive/next-js-web-app/src/pages/index.tsx
Normal file
@@ -0,0 +1,60 @@
|
||||
import { NextPage } from 'next';
|
||||
import { CldImage } from 'next-cloudinary';
|
||||
import Head from 'next/head';
|
||||
|
||||
const keywords = [
|
||||
'beer',
|
||||
'bier',
|
||||
'biergarten',
|
||||
'brewery',
|
||||
'brew',
|
||||
'drink',
|
||||
'alcohol',
|
||||
'brews',
|
||||
'breweries',
|
||||
'craft beer',
|
||||
'beer enthusiast',
|
||||
'beer tasting',
|
||||
'beer culture',
|
||||
'beer connoisseur',
|
||||
'beer reviews',
|
||||
'beer community',
|
||||
'beer events',
|
||||
'brewpubs',
|
||||
'beer aficionado',
|
||||
'beer types',
|
||||
'beer selection',
|
||||
'beer recommendations',
|
||||
'beer ratings',
|
||||
'beer pairing',
|
||||
'beer recipes',
|
||||
];
|
||||
|
||||
const description = `An app for beer lovers to share their favourite brews and breweries with like-minded people online.`;
|
||||
|
||||
const Home: NextPage = () => {
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>The Biergarten App</title>
|
||||
<meta name="description" content={description} />
|
||||
<meta name="keywords" content={keywords.join(', ')} />
|
||||
</Head>
|
||||
<div className="relative flex h-dvh w-full flex-col items-center justify-center bg-base-300">
|
||||
<CldImage
|
||||
src="https://res.cloudinary.com/dxie9b7na/image/upload/v1701056793/cloudinary-images/pexels-elevate-1267700_jrno3s.jpg"
|
||||
alt="Login Image"
|
||||
width={5000}
|
||||
height={5000}
|
||||
className="pointer-events-none absolute h-full w-full object-cover mix-blend-overlay"
|
||||
/>
|
||||
<div className="relative flex w-9/12 flex-col space-y-3 text-base-content">
|
||||
<h1 className="text-5xl font-extrabold lg:text-8xl">The Biergarten App</h1>
|
||||
<p className="font-bold lg:text-3xl">{description}</p>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default Home;
|
||||
54
archive/next-js-web-app/src/pages/login/index.tsx
Normal file
54
archive/next-js-web-app/src/pages/login/index.tsx
Normal file
@@ -0,0 +1,54 @@
|
||||
import { NextPage } from 'next';
|
||||
|
||||
import LoginForm from '@/components/Login/LoginForm';
|
||||
|
||||
import { FaUserCircle } from 'react-icons/fa';
|
||||
import Head from 'next/head';
|
||||
import Link from 'next/link';
|
||||
|
||||
import useRedirectWhenLoggedIn from '@/hooks/auth/useRedirectIfLoggedIn';
|
||||
import { CldImage } from 'next-cloudinary';
|
||||
|
||||
const LoginPage: NextPage = () => {
|
||||
useRedirectWhenLoggedIn();
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Login</title>
|
||||
<meta name="description" content="Login to your account" />
|
||||
</Head>
|
||||
|
||||
<div className="relative flex h-dvh w-full flex-col items-center justify-center bg-base-300">
|
||||
<CldImage
|
||||
src="https://res.cloudinary.com/dxie9b7na/image/upload/v1701056793/cloudinary-images/pexels-elevate-1267700_jrno3s.jpg"
|
||||
alt="Login Image"
|
||||
width={5000}
|
||||
height={5000}
|
||||
className="pointer-events-none absolute h-full w-full object-cover mix-blend-overlay"
|
||||
/>
|
||||
|
||||
<div className="relative flex w-9/12 flex-col items-center text-center text-base-content lg:w-6/12">
|
||||
<div className="mb-8 flex w-full flex-col items-center space-y-2 text-center">
|
||||
<FaUserCircle className="text-6xl text-primary-content" />
|
||||
<h1 className="text-6xl font-extrabold">Login</h1>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<LoginForm />
|
||||
</div>
|
||||
|
||||
<div className="mt-5 flex w-full flex-col space-y-1 text-center font-bold italic">
|
||||
<Link className="link-hover link text-lg" href="/register">
|
||||
Don't have an account?
|
||||
</Link>
|
||||
|
||||
<Link className="link-hover link text-lg" href="/users/forgot-password">
|
||||
Forgot your password?{' '}
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default LoginPage;
|
||||
43
archive/next-js-web-app/src/pages/register/index.tsx
Normal file
43
archive/next-js-web-app/src/pages/register/index.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import RegisterUserForm from '@/components/RegisterUserForm';
|
||||
|
||||
import useRedirectWhenLoggedIn from '@/hooks/auth/useRedirectIfLoggedIn';
|
||||
import { NextPage } from 'next';
|
||||
import { CldImage } from 'next-cloudinary';
|
||||
import Head from 'next/head';
|
||||
|
||||
import { FaUserCircle } from 'react-icons/fa';
|
||||
|
||||
const RegisterUserPage: NextPage = () => {
|
||||
useRedirectWhenLoggedIn();
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Register User</title>
|
||||
<meta name="description" content="Register a new user" />
|
||||
</Head>
|
||||
|
||||
<div className="relative flex min-h-dvh w-full flex-col items-center justify-center bg-base-300">
|
||||
<CldImage
|
||||
src="https://res.cloudinary.com/dxie9b7na/image/upload/v1701056793/cloudinary-images/pexels-elevate-1267700_jrno3s.jpg"
|
||||
alt="Login Image"
|
||||
width={5000}
|
||||
height={5000}
|
||||
className="pointer-events-none absolute h-full w-full object-cover mix-blend-overlay"
|
||||
/>
|
||||
|
||||
<div className="relative flex w-10/12 flex-col items-center pb-20 text-center text-base-content lg:w-6/12">
|
||||
<div className="mb-8 mt-20 flex w-full flex-col items-center space-y-2 text-center">
|
||||
<FaUserCircle className="text-6xl text-primary-content" />
|
||||
<h1 className="text-6xl font-extrabold">Register</h1>
|
||||
</div>
|
||||
<div className="w-full">
|
||||
<RegisterUserForm />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default RegisterUserPage;
|
||||
48
archive/next-js-web-app/src/pages/users/[id].tsx
Normal file
48
archive/next-js-web-app/src/pages/users/[id].tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import useMediaQuery from '@/hooks/utilities/useMediaQuery';
|
||||
|
||||
import GetUserSchema from '@/services/users/auth/schema/GetUserSchema';
|
||||
|
||||
import Head from 'next/head';
|
||||
import { FC } from 'react';
|
||||
import { z } from 'zod';
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import UserHeader from '@/components/UserPage/UserHeader';
|
||||
import { findUserByIdService } from '@/services/users/auth';
|
||||
|
||||
interface UserInfoPageProps {
|
||||
user: z.infer<typeof GetUserSchema>;
|
||||
}
|
||||
|
||||
const UserInfoPage: FC<UserInfoPageProps> = ({ user }) => {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const isDesktop = useMediaQuery('(min-width: 1024px)');
|
||||
const title = `${user.username} | The Biergarten App`;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>{title}</title>
|
||||
<meta name="description" content="User information" />
|
||||
</Head>
|
||||
<>
|
||||
<main className="mb-12 mt-16 flex w-full flex-col items-center justify-center">
|
||||
<div className="w-11/12 space-y-3 xl:w-9/12 2xl:w-8/12">
|
||||
<UserHeader user={user} />
|
||||
</div>
|
||||
</main>
|
||||
</>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default UserInfoPage;
|
||||
|
||||
export const getServerSideProps = withPageAuthRequired<UserInfoPageProps>(
|
||||
async (context) => {
|
||||
const { id } = context.params!;
|
||||
const user = await findUserByIdService({ userId: id as string });
|
||||
return user
|
||||
? { props: { user: JSON.parse(JSON.stringify(user)) } }
|
||||
: { notFound: true };
|
||||
},
|
||||
);
|
||||
181
archive/next-js-web-app/src/pages/users/account/edit-profile.tsx
Normal file
181
archive/next-js-web-app/src/pages/users/account/edit-profile.tsx
Normal file
@@ -0,0 +1,181 @@
|
||||
import { useContext, useEffect } from 'react';
|
||||
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import Head from 'next/head';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import { z } from 'zod';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
|
||||
import toast from 'react-hot-toast';
|
||||
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import createErrorToast from '@/util/createErrorToast';
|
||||
|
||||
import UserContext from '@/contexts/UserContext';
|
||||
|
||||
import UserAvatar from '@/components/Account/UserAvatar';
|
||||
import UpdateProfileForm from '@/components/Account/UpdateProfileForm';
|
||||
|
||||
import useGetUsersFollowedByUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowedByUser';
|
||||
import useGetUsersFollowingUser from '@/hooks/data-fetching/user-follows/useGetUsersFollowingUser';
|
||||
|
||||
import UpdateProfileSchema from '@/services/users/auth/schema/UpdateProfileSchema';
|
||||
import sendUpdateUserAvatarRequest from '@/requests/users/profile/sendUpdateUserAvatarRequest';
|
||||
import sendUpdateUserProfileRequest from '@/requests/users/profile/sendUpdateUserProfileRequest';
|
||||
import Spinner from '@/components/ui/Spinner';
|
||||
|
||||
const ProfilePage: NextPage = () => {
|
||||
const { user, mutate: mutateUser } = useContext(UserContext);
|
||||
|
||||
const {
|
||||
register,
|
||||
handleSubmit,
|
||||
setValue,
|
||||
formState: { errors, isSubmitting },
|
||||
watch,
|
||||
} = useForm<z.infer<typeof UpdateProfileSchema>>({
|
||||
resolver: zodResolver(UpdateProfileSchema),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!user || !user.bio) {
|
||||
return;
|
||||
}
|
||||
|
||||
setValue('bio', user.bio);
|
||||
}, [user, setValue]);
|
||||
|
||||
const router = useRouter();
|
||||
|
||||
const onSubmit: SubmitHandler<z.infer<typeof UpdateProfileSchema>> = async (data) => {
|
||||
try {
|
||||
const loadingToast = toast.loading('Updating profile...');
|
||||
if (!user) {
|
||||
throw new Error('User is not logged in.');
|
||||
}
|
||||
|
||||
if (data.userAvatar instanceof FileList && data.userAvatar.length === 1) {
|
||||
await sendUpdateUserAvatarRequest({
|
||||
userId: user.id,
|
||||
file: data.userAvatar[0],
|
||||
});
|
||||
}
|
||||
|
||||
await sendUpdateUserProfileRequest({
|
||||
userId: user.id,
|
||||
bio: data.bio,
|
||||
});
|
||||
|
||||
toast.remove(loadingToast);
|
||||
await mutateUser!();
|
||||
await router.push(`/users/${user.id}`);
|
||||
toast.success('Profile updated.');
|
||||
} catch (error) {
|
||||
createErrorToast(error);
|
||||
}
|
||||
};
|
||||
const { followingCount } = useGetUsersFollowedByUser({
|
||||
userId: user?.id,
|
||||
});
|
||||
|
||||
const { followerCount } = useGetUsersFollowingUser({
|
||||
userId: user?.id,
|
||||
});
|
||||
|
||||
const getUserAvatarPath = () => {
|
||||
const watchedInput = watch('userAvatar');
|
||||
|
||||
if (
|
||||
!(
|
||||
watchedInput instanceof FileList &&
|
||||
watchedInput.length === 1 &&
|
||||
watchedInput[0].type.startsWith('image/')
|
||||
)
|
||||
) {
|
||||
return '';
|
||||
}
|
||||
|
||||
const [avatar] = watchedInput;
|
||||
|
||||
return URL.createObjectURL(avatar);
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>The Biergarten App || Update Your Profile</title>
|
||||
<meta name="description" content="Update your user profile." />
|
||||
</Head>
|
||||
<div className="my-20 flex flex-col items-center justify-center">
|
||||
{user ? (
|
||||
<div className="w-10/12 lg:w-7/12">
|
||||
<div className="card">
|
||||
<div className="card-body">
|
||||
<div className="my-10 flex flex-col items-center justify-center">
|
||||
<div className="my-2 h-40 xl:my-5 xl:h-52">
|
||||
<UserAvatar
|
||||
user={{
|
||||
...user,
|
||||
userAvatar:
|
||||
// Render the user's avatar if they have one and then switch to the preview it's being updated.
|
||||
user.userAvatar && !getUserAvatarPath()
|
||||
? user.userAvatar
|
||||
: {
|
||||
id: 'preview',
|
||||
alt: 'User Avatar',
|
||||
caption: 'User Avatar',
|
||||
path: getUserAvatarPath(),
|
||||
createdAt: new Date(),
|
||||
updatedAt: new Date(),
|
||||
},
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div className="text-center">
|
||||
<h2 className="my-1 text-2xl font-bold xl:text-3xl">
|
||||
{user.username}
|
||||
</h2>
|
||||
|
||||
<div className="flex space-x-3 font-bold xl:text-lg">
|
||||
<span>{followingCount} Following</span>
|
||||
<span>{followerCount} Followers</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div>
|
||||
<p className="hyphens-auto text-center xl:text-lg">
|
||||
{watch('bio') ? (
|
||||
<span className="hyphens-manual">{watch('bio')}</span>
|
||||
) : (
|
||||
<span className="italic">Your bio will appear here.</span>
|
||||
)}
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<UpdateProfileForm
|
||||
handleSubmit={handleSubmit}
|
||||
onSubmit={onSubmit}
|
||||
errors={errors}
|
||||
isSubmitting={isSubmitting}
|
||||
register={register}
|
||||
user={user}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
) : (
|
||||
<div className="flex h-96 items-center justify-center">
|
||||
<Spinner />
|
||||
</div>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ProfilePage;
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = withPageAuthRequired();
|
||||
76
archive/next-js-web-app/src/pages/users/account/index.tsx
Normal file
76
archive/next-js-web-app/src/pages/users/account/index.tsx
Normal file
@@ -0,0 +1,76 @@
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import { NextPage } from 'next';
|
||||
|
||||
import { Tab } from '@headlessui/react';
|
||||
import Head from 'next/head';
|
||||
import AccountInfo from '@/components/Account/AccountInfo';
|
||||
import { useContext, useReducer } from 'react';
|
||||
import UserContext from '@/contexts/UserContext';
|
||||
import Security from '@/components/Account/Security';
|
||||
import DeleteAccount from '@/components/Account/DeleteAccount';
|
||||
import accountPageReducer from '@/reducers/accountPageReducer';
|
||||
import UserAvatar from '@/components/Account/UserAvatar';
|
||||
import UserPosts from '@/components/Account/UserPosts';
|
||||
import UpdateProfileLink from '@/components/Account/UpdateProfileLink';
|
||||
|
||||
const AccountPage: NextPage = () => {
|
||||
const { user } = useContext(UserContext);
|
||||
|
||||
const [pageState, dispatch] = useReducer(accountPageReducer, {
|
||||
accountInfoOpen: false,
|
||||
securityOpen: false,
|
||||
deleteAccountOpen: false,
|
||||
});
|
||||
|
||||
if (!user) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Your Account | The Biergarten App</title>
|
||||
<meta
|
||||
name="description"
|
||||
content="Your account page. Here you can view your account information, change your settings, and view your posts."
|
||||
/>
|
||||
</Head>
|
||||
<div className="mt-10 flex min-h-dvh flex-col items-center">
|
||||
<div className="m-12 flex w-11/12 flex-col items-center justify-center space-y-3 lg:w-8/12">
|
||||
<div className="flex flex-col items-center space-y-3">
|
||||
<div className="mb-1 h-28 w-28">
|
||||
<UserAvatar user={user} />
|
||||
</div>
|
||||
|
||||
<div className="flex flex-col items-center space-y-1">
|
||||
<p className="text-3xl font-bold">Hello, {user!.username}!</p>
|
||||
<p className="text-lg">Welcome to your account page.</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className="h-full w-full">
|
||||
<Tab.Group>
|
||||
<Tab.List className="tabs-boxed tabs grid grid-cols-2">
|
||||
<Tab className="tab uppercase ui-selected:tab-active">Account</Tab>
|
||||
<Tab className="tab uppercase ui-selected:tab-active">Your Posts</Tab>
|
||||
</Tab.List>
|
||||
<Tab.Panels>
|
||||
<Tab.Panel className="mt-3 h-full space-y-3">
|
||||
<UpdateProfileLink />
|
||||
<AccountInfo pageState={pageState} dispatch={dispatch} />
|
||||
<Security pageState={pageState} dispatch={dispatch} />
|
||||
<DeleteAccount pageState={pageState} dispatch={dispatch} />
|
||||
</Tab.Panel>
|
||||
<Tab.Panel className="h-full">
|
||||
<UserPosts />
|
||||
</Tab.Panel>
|
||||
</Tab.Panels>
|
||||
</Tab.Group>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default AccountPage;
|
||||
|
||||
export const getServerSideProps = withPageAuthRequired();
|
||||
73
archive/next-js-web-app/src/pages/users/confirm.tsx
Normal file
73
archive/next-js-web-app/src/pages/users/confirm.tsx
Normal file
@@ -0,0 +1,73 @@
|
||||
import useConfirmUser from '@/hooks/auth/useConfirmUser';
|
||||
import createErrorToast from '@/util/createErrorToast';
|
||||
import Head from 'next/head';
|
||||
|
||||
import { FC, useState } from 'react';
|
||||
import { toast } from 'react-hot-toast';
|
||||
|
||||
const ConfirmUserPage: FC = () => {
|
||||
const { needsToLogin, tokenInvalid } = useConfirmUser();
|
||||
|
||||
const [confirmationResent, setConfirmationResent] = useState(false);
|
||||
const onClick = async () => {
|
||||
const resentConfirmationLoadingToast = toast.loading(
|
||||
'Resending your confirmation email.',
|
||||
);
|
||||
try {
|
||||
const response = await fetch('/api/users/resend-confirmation', {
|
||||
method: 'POST',
|
||||
});
|
||||
if (!response.ok) {
|
||||
throw new Error('Something went wrong.');
|
||||
}
|
||||
|
||||
toast.remove(resentConfirmationLoadingToast);
|
||||
toast.success('Sent a new confirmation email.');
|
||||
|
||||
setConfirmationResent(true);
|
||||
} catch (err) {
|
||||
createErrorToast(err);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Confirm User | The Biergarten App</title>
|
||||
</Head>
|
||||
<div className="flex h-full flex-col items-center justify-center space-y-4">
|
||||
{needsToLogin && (
|
||||
<p className="text-center text-xl font-bold">
|
||||
Please login to confirm your account.
|
||||
</p>
|
||||
)}
|
||||
|
||||
{!needsToLogin && tokenInvalid && !confirmationResent && (
|
||||
<>
|
||||
<p className="text-center text-2xl font-bold">
|
||||
Your confirmation token is invalid or is expired.
|
||||
</p>
|
||||
<button
|
||||
className="btn btn-outline btn-sm normal-case"
|
||||
onClick={onClick}
|
||||
type="button"
|
||||
>
|
||||
Click here to request a new token.
|
||||
</button>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!needsToLogin && tokenInvalid && confirmationResent && (
|
||||
<>
|
||||
<p className="text-center text-2xl font-bold">
|
||||
Resent your confirmation link.
|
||||
</p>
|
||||
<p className="font-xl text-center">Please check your email.</p>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default ConfirmUserPage;
|
||||
47
archive/next-js-web-app/src/pages/users/current.tsx
Normal file
47
archive/next-js-web-app/src/pages/users/current.tsx
Normal file
@@ -0,0 +1,47 @@
|
||||
import Spinner from '@/components/ui/Spinner';
|
||||
import withPageAuthRequired from '@/util/withPageAuthRequired';
|
||||
import UserContext from '@/contexts/UserContext';
|
||||
|
||||
import { GetServerSideProps, NextPage } from 'next';
|
||||
import { useContext } from 'react';
|
||||
import useMediaQuery from '@/hooks/utilities/useMediaQuery';
|
||||
import Head from 'next/head';
|
||||
|
||||
const ProtectedPage: NextPage = () => {
|
||||
const { user, isLoading } = useContext(UserContext);
|
||||
|
||||
const currentTime = new Date().getHours();
|
||||
|
||||
const isMorning = currentTime >= 3 && currentTime < 12;
|
||||
const isAfternoon = currentTime >= 12 && currentTime < 18;
|
||||
const isEvening = currentTime >= 18 || currentTime < 3;
|
||||
|
||||
const isDesktop = useMediaQuery('(min-width: 768px)');
|
||||
return (
|
||||
<>
|
||||
<Head>
|
||||
<title>Hello! | The Biergarten App</title>
|
||||
</Head>
|
||||
<div className="h-dvh flex flex-col items-center justify-center space-y-3 bg-base-100 text-center">
|
||||
{isLoading && <Spinner size={isDesktop ? 'xl' : 'md'} />}
|
||||
{user && !isLoading && (
|
||||
<>
|
||||
<h1 className="text-2xl font-bold lg:text-7xl">
|
||||
Good {isMorning && 'morning'}
|
||||
{isAfternoon && 'afternoon'}
|
||||
{isEvening && 'evening'}
|
||||
{`, ${user?.firstName}!`}
|
||||
</h1>
|
||||
<h2 className="text-xl font-bold lg:text-4xl">
|
||||
Welcome to the Biergarten App!
|
||||
</h2>
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = withPageAuthRequired();
|
||||
|
||||
export default ProtectedPage;
|
||||
89
archive/next-js-web-app/src/pages/users/forgot-password.tsx
Normal file
89
archive/next-js-web-app/src/pages/users/forgot-password.tsx
Normal file
@@ -0,0 +1,89 @@
|
||||
import Button from '@/components/ui/forms/Button';
|
||||
import FormError from '@/components/ui/forms/FormError';
|
||||
import FormInfo from '@/components/ui/forms/FormInfo';
|
||||
import FormLabel from '@/components/ui/forms/FormLabel';
|
||||
import FormSegment from '@/components/ui/forms/FormSegment';
|
||||
import FormTextInput from '@/components/ui/forms/FormTextInput';
|
||||
import { BaseCreateUserSchema } from '@/services/users/auth/schema/CreateUserValidationSchemas';
|
||||
import createErrorToast from '@/util/createErrorToast';
|
||||
import { zodResolver } from '@hookform/resolvers/zod';
|
||||
import { NextPage } from 'next';
|
||||
|
||||
import { SubmitHandler, useForm } from 'react-hook-form';
|
||||
import toast from 'react-hot-toast';
|
||||
import { useRouter } from 'next/router';
|
||||
|
||||
import { FaUserCircle } from 'react-icons/fa';
|
||||
import { sendForgotPasswordRequest } from '@/requests/users/auth';
|
||||
|
||||
interface ForgotPasswordPageProps {}
|
||||
|
||||
const ForgotPasswordPage: NextPage<ForgotPasswordPageProps> = () => {
|
||||
const { register, handleSubmit, formState, reset } = useForm({
|
||||
resolver: zodResolver(BaseCreateUserSchema.pick({ email: true })),
|
||||
defaultValues: { email: '' },
|
||||
});
|
||||
|
||||
const router = useRouter();
|
||||
const { errors } = formState;
|
||||
|
||||
const onSubmit: SubmitHandler<{ email: string }> = async (data) => {
|
||||
try {
|
||||
const loadingToast = toast.loading('Sending reset link...');
|
||||
await sendForgotPasswordRequest({ email: data.email });
|
||||
reset();
|
||||
toast.dismiss(loadingToast);
|
||||
toast.success('Password reset link sent!');
|
||||
|
||||
router.push('/');
|
||||
} catch (error) {
|
||||
createErrorToast(error);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="flex h-full flex-col items-center">
|
||||
<div className="mt-64 flex w-10/12 flex-col space-y-3 text-center xl:w-6/12">
|
||||
<div className="space-y-1">
|
||||
<div className="my-2 flex flex-col items-center justify-center">
|
||||
<FaUserCircle className="text-3xl" />
|
||||
<h1 className="text-3xl font-bold">Forgot Your Password?</h1>
|
||||
</div>
|
||||
<p className="xl:text-lg">
|
||||
Enter your email address below, and we will send you a link to reset your
|
||||
password.
|
||||
</p>
|
||||
</div>
|
||||
<form
|
||||
onSubmit={handleSubmit(onSubmit)}
|
||||
className="form-control space-y-3"
|
||||
noValidate
|
||||
>
|
||||
<div>
|
||||
<FormInfo>
|
||||
<FormLabel htmlFor="email">Email</FormLabel>
|
||||
<FormError>{errors.email?.message}</FormError>
|
||||
</FormInfo>
|
||||
<FormSegment>
|
||||
<FormTextInput
|
||||
id="email"
|
||||
type="email"
|
||||
formValidationSchema={register('email')}
|
||||
disabled={formState.isSubmitting}
|
||||
error={!!errors.email}
|
||||
placeholder="Email"
|
||||
/>
|
||||
</FormSegment>
|
||||
</div>
|
||||
<div>
|
||||
<Button type="submit" isSubmitting={formState.isSubmitting}>
|
||||
Send Reset Link
|
||||
</Button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default ForgotPasswordPage;
|
||||
43
archive/next-js-web-app/src/pages/users/reset-password.tsx
Normal file
43
archive/next-js-web-app/src/pages/users/reset-password.tsx
Normal file
@@ -0,0 +1,43 @@
|
||||
import { setLoginSession } from '@/config/auth/session';
|
||||
import { verifyResetPasswordToken } from '@/config/jwt';
|
||||
import ServerError from '@/config/util/ServerError';
|
||||
import { findUserByIdService } from '@/services/users/auth';
|
||||
|
||||
import { GetServerSideProps, NextApiResponse, NextPage } from 'next';
|
||||
|
||||
const TokenExpiredPage: NextPage = () => {
|
||||
return (
|
||||
<div className="flex h-full items-center justify-center">
|
||||
<div className="text-center">
|
||||
<h1 className="text-4xl font-bold">Token Expired</h1>
|
||||
<p className="text-lg">
|
||||
Your link to reset your password has expired or is invalid.
|
||||
</p>
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TokenExpiredPage;
|
||||
|
||||
export const getServerSideProps: GetServerSideProps = async (context) => {
|
||||
try {
|
||||
const token = context.query.token as string | undefined;
|
||||
if (!token) {
|
||||
throw new ServerError('Token not provided', 400);
|
||||
}
|
||||
|
||||
const { id } = await verifyResetPasswordToken(token as string);
|
||||
|
||||
const user = await findUserByIdService({ userId: id as string });
|
||||
if (!user) {
|
||||
throw new ServerError('User not found', 404);
|
||||
}
|
||||
|
||||
await setLoginSession(context.res as NextApiResponse, user);
|
||||
|
||||
return { redirect: { destination: '/users/account', permanent: false } };
|
||||
} catch (error) {
|
||||
return { props: {} };
|
||||
}
|
||||
};
|
||||
Reference in New Issue
Block a user