Merge pull request #40 from aaronpo97/create-beer-updates

Update: beer post form now connected to a specific brewery
This commit is contained in:
Aaron Po
2023-05-12 20:27:42 -04:00
committed by GitHub
8 changed files with 78 additions and 106 deletions

View File

@@ -3,7 +3,7 @@
"version": "0.1.0", "version": "0.1.0",
"private": true, "private": true,
"scripts": { "scripts": {
"dev": "next dev", "dev": "next dev --turbo",
"build": "next build", "build": "next build",
"start": "next start", "start": "next start",
"lint": "next lint", "lint": "next lint",

View File

@@ -4,6 +4,7 @@ import Link from 'next/link';
import { FC } from 'react'; import { FC } from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
import { z } from 'zod'; import { z } from 'zod';
import { FaPlus } from 'react-icons/fa';
import BeerRecommendationLoadingComponent from '../BeerById/BeerRecommendationLoadingComponent'; import BeerRecommendationLoadingComponent from '../BeerById/BeerRecommendationLoadingComponent';
interface BreweryCommentsSectionProps { interface BreweryCommentsSectionProps {
@@ -31,7 +32,21 @@ const BreweryBeersSection: FC<BreweryCommentsSectionProps> = ({ breweryPost }) =
<div className="card"> <div className="card">
<div className="card-body"> <div className="card-body">
<> <>
<h3 className="text-2xl font-bold">Brews</h3> <div className="my-2 flex flex-row items-center justify-between">
<div>
<h3 className="text-3xl font-bold">Brews</h3>
</div>
<div>
<Link
className={`btn-ghost btn-sm btn gap-2 rounded-2xl outline`}
href={`/breweries/${breweryPost.id}/beers/create`}
>
<FaPlus className="text-xl" />
Add Beer
</Link>
</div>
</div>
{!!beerPosts.length && ( {!!beerPosts.length && (
<div className="space-y-5"> <div className="space-y-5">
{beerPosts.map((beerPost, index) => { {beerPosts.map((beerPost, index) => {

View File

@@ -4,9 +4,11 @@ import useTimeDistance from '@/hooks/utilities/useTimeDistance';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { FC, useContext } from 'react'; import { FC, useContext } from 'react';
import { Link } from 'react-daisyui';
import { FaRegEdit } from 'react-icons/fa'; import { FaRegEdit } from 'react-icons/fa';
import { z } from 'zod'; import { z } from 'zod';
import Link from 'next/link';
import BreweryPostLikeButton from '../BreweryIndex/BreweryPostLikeButton'; import BreweryPostLikeButton from '../BreweryIndex/BreweryPostLikeButton';
interface BreweryInfoHeaderProps { interface BreweryInfoHeaderProps {
@@ -26,7 +28,8 @@ const BreweryInfoHeader: FC<BreweryInfoHeaderProps> = ({ breweryPost }) => {
<article className="card flex flex-col justify-center bg-base-300"> <article className="card flex flex-col justify-center bg-base-300">
<div className="card-body"> <div className="card-body">
<header className="flex justify-between"> <header className="flex justify-between">
<div className="space-y-2"> <div className="w-full space-y-2">
<div className="flex w-full flex-row justify-between">
<div> <div>
<h1 className="text-2xl font-bold lg:text-4xl">{breweryPost.name}</h1> <h1 className="text-2xl font-bold lg:text-4xl">{breweryPost.name}</h1>
<h2 className="text-lg font-semibold lg:text-2xl"> <h2 className="text-lg font-semibold lg:text-2xl">
@@ -36,6 +39,8 @@ const BreweryInfoHeader: FC<BreweryInfoHeaderProps> = ({ breweryPost }) => {
}`} }`}
</h2> </h2>
</div> </div>
</div>
<div> <div>
<h3 className="italic"> <h3 className="italic">
{' posted by '} {' posted by '}

View File

@@ -20,7 +20,7 @@ import FormTextArea from './ui/forms/FormTextArea';
import FormTextInput from './ui/forms/FormTextInput'; import FormTextInput from './ui/forms/FormTextInput';
interface BeerFormProps { interface BeerFormProps {
breweries: z.infer<typeof BreweryPostQueryResult>[]; brewery: z.infer<typeof BreweryPostQueryResult>;
types: BeerType[]; types: BeerType[];
} }
@@ -29,13 +29,14 @@ const CreateBeerPostWithImagesValidationSchema = CreateBeerPostValidationSchema.
); );
const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({ const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({
breweries = [],
types = [], types = [],
brewery,
}) => { }) => {
const { register, handleSubmit, formState } = useForm< const { register, handleSubmit, formState } = useForm<
z.infer<typeof CreateBeerPostWithImagesValidationSchema> z.infer<typeof CreateBeerPostWithImagesValidationSchema>
>({ >({
resolver: zodResolver(CreateBeerPostWithImagesValidationSchema), resolver: zodResolver(CreateBeerPostWithImagesValidationSchema),
defaultValues: { breweryId: brewery.id },
}); });
const { errors, isSubmitting } = formState; const { errors, isSubmitting } = formState;
@@ -48,23 +49,9 @@ const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({
return; return;
} }
const { breweryId, typeId, name, abv, ibu, description } = data;
try { try {
const response = await sendCreateBeerPostRequest({ const response = await sendCreateBeerPostRequest(data);
breweryId, await sendUploadBeerImagesRequest({ beerPost: response, images: data.images });
typeId,
name,
abv,
ibu,
description,
});
await sendUploadBeerImagesRequest({
beerPost: response,
images: data.images,
});
router.push(`/beers/${response.id}`); router.push(`/beers/${response.id}`);
} catch (e) { } catch (e) {
if (!(e instanceof Error)) { if (!(e instanceof Error)) {
@@ -93,28 +80,6 @@ const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({
/> />
</FormSegment> </FormSegment>
<div className="flex flex-wrap">
<div className="mb-2 w-full md:mb-0 md:w-1/2 md:pr-3">
<FormInfo>
<FormLabel htmlFor="breweryId">Brewery</FormLabel>
<FormError>{errors.breweryId?.message}</FormError>
</FormInfo>
<FormSegment>
<FormSelect
disabled={isSubmitting}
formRegister={register('breweryId')}
error={!!errors.breweryId}
id="breweryId"
options={breweries.map((brewery) => ({
value: brewery.id,
text: brewery.name,
}))}
placeholder="Brewery"
message="Pick a brewery"
/>
</FormSegment>
</div>
<div className="mb-2 w-full md:mb-0 md:w-1/2 md:pl-3">
<FormInfo> <FormInfo>
<FormLabel htmlFor="typeId">Type</FormLabel> <FormLabel htmlFor="typeId">Type</FormLabel>
<FormError>{errors.typeId?.message}</FormError> <FormError>{errors.typeId?.message}</FormError>
@@ -133,8 +98,6 @@ const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({
message="Pick a beer type" message="Pick a beer type"
/> />
</FormSegment> </FormSegment>
</div>
</div>
<div className="flex flex-wrap md:mb-3"> <div className="flex flex-wrap md:mb-3">
<div className="mb-2 w-full md:mb-0 md:w-1/2 md:pr-3"> <div className="mb-2 w-full md:mb-0 md:w-1/2 md:pr-3">
@@ -192,6 +155,7 @@ const CreateBeerPostForm: FunctionComponent<BeerFormProps> = ({
{...register('images')} {...register('images')}
multiple multiple
className="file-input-bordered file-input w-full" className="file-input-bordered file-input w-full"
disabled={isSubmitting}
/> />
</FormSegment> </FormSegment>

View File

@@ -34,8 +34,8 @@ const useNavbar = () => {
/** These pages are accessible to both authenticated and unauthenticated users. */ /** These pages are accessible to both authenticated and unauthenticated users. */
const otherPages: readonly Page[] = [ const otherPages: readonly Page[] = [
{ slug: '/beers', name: 'Beers' },
{ slug: '/breweries', name: 'Breweries' }, { slug: '/breweries', name: 'Breweries' },
{ slug: '/beers', name: 'Beers' },
]; ];
/** /**

View File

@@ -1,21 +1,14 @@
import { NextPage } from 'next'; import { NextPage } from 'next';
import BeerCard from '@/components/BeerIndex/BeerCard'; import BeerCard from '@/components/BeerIndex/BeerCard';
import Head from 'next/head'; import Head from 'next/head';
import Link from 'next/link'; import { MutableRefObject, useRef } from 'react';
import UserContext from '@/contexts/userContext';
import { MutableRefObject, useContext, useRef } from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
import Spinner from '@/components/ui/Spinner'; import Spinner from '@/components/ui/Spinner';
import useBeerPosts from '@/hooks/data-fetching/beer-posts/useBeerPosts'; import useBeerPosts from '@/hooks/data-fetching/beer-posts/useBeerPosts';
import { FaArrowUp, FaPlus } from 'react-icons/fa'; import { FaArrowUp } from 'react-icons/fa';
import LoadingCard from '@/components/ui/LoadingCard'; import LoadingCard from '@/components/ui/LoadingCard';
const BeerPage: NextPage = () => { const BeerPage: NextPage = () => {
const { user } = useContext(UserContext);
const PAGE_SIZE = 6; const PAGE_SIZE = 6;
const { beerPosts, setSize, size, isLoading, isLoadingMore, isAtEnd } = useBeerPosts({ const { beerPosts, setSize, size, isLoading, isLoadingMore, isAtEnd } = useBeerPosts({
@@ -47,16 +40,6 @@ const BeerPage: NextPage = () => {
<h1 className="text-4xl font-bold lg:text-6xl">The Biergarten App</h1> <h1 className="text-4xl font-bold lg:text-6xl">The Biergarten App</h1>
<h2 className="text-2xl font-bold lg:text-4xl">Beers</h2> <h2 className="text-2xl font-bold lg:text-4xl">Beers</h2>
</div> </div>
{!!user && (
<div
className="tooltip tooltip-left h-full"
data-tip="Create a new beer post"
>
<Link href="/beers/create" className="btn-ghost btn-sm btn">
<FaPlus />
</Link>
</div>
)}
</header> </header>
<div className="grid gap-6 xl:grid-cols-2"> <div className="grid gap-6 xl:grid-cols-2">
{!!beerPosts.length && !isLoading && ( {!!beerPosts.length && !isLoading && (

View File

@@ -3,19 +3,19 @@ import FormPageLayout from '@/components/ui/forms/FormPageLayout';
import withPageAuthRequired from '@/util/withPageAuthRequired'; import withPageAuthRequired from '@/util/withPageAuthRequired';
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import getAllBreweryPosts from '@/services/BreweryPost/getAllBreweryPosts';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult';
import { BeerType } from '@prisma/client'; import { BeerType } from '@prisma/client';
import { NextPage } from 'next'; import { NextPage } from 'next';
import { BiBeer } from 'react-icons/bi'; import { BiBeer } from 'react-icons/bi';
import { z } from 'zod'; import { z } from 'zod';
import getBreweryPostById from '@/services/BreweryPost/getBreweryPostById';
interface CreateBeerPageProps { interface CreateBeerPageProps {
breweries: z.infer<typeof BreweryPostQueryResult>[]; brewery: z.infer<typeof BreweryPostQueryResult>;
types: BeerType[]; types: BeerType[];
} }
const CreateBeerPost: NextPage<CreateBeerPageProps> = ({ breweries, types }) => { const CreateBeerPost: NextPage<CreateBeerPageProps> = ({ brewery, types }) => {
return ( return (
<FormPageLayout <FormPageLayout
headingText="Create a new beer" headingText="Create a new beer"
@@ -23,21 +23,26 @@ const CreateBeerPost: NextPage<CreateBeerPageProps> = ({ breweries, types }) =>
backLink="/beers" backLink="/beers"
backLinkText="Back to beers" backLinkText="Back to beers"
> >
<CreateBeerPostForm breweries={breweries} types={types} /> <CreateBeerPostForm brewery={brewery} types={types} />
</FormPageLayout> </FormPageLayout>
); );
}; };
export const getServerSideProps = withPageAuthRequired<CreateBeerPageProps>(async () => { export const getServerSideProps = withPageAuthRequired<CreateBeerPageProps>(
const breweryPosts = await getAllBreweryPosts(); async (context) => {
const id = context.params?.id as string;
const breweryPost = await getBreweryPostById(id);
const beerTypes = await DBClient.instance.beerType.findMany(); const beerTypes = await DBClient.instance.beerType.findMany();
return { return {
props: { props: {
breweries: JSON.parse(JSON.stringify(breweryPosts)), brewery: JSON.parse(JSON.stringify(breweryPost)),
types: JSON.parse(JSON.stringify(beerTypes)), types: JSON.parse(JSON.stringify(beerTypes)),
}, },
}; };
}); },
);
export default CreateBeerPost; export default CreateBeerPost;