From 972846f5a8b53f22197864df38165d6b1983f313 Mon Sep 17 00:00:00 2001 From: Aaron William Po Date: Mon, 23 Jan 2023 20:13:25 -0500 Subject: [PATCH] scaffold create/edit beer form, scaffold beer page --- components/BeerForm.tsx | 192 ++++++++++++++++++ components/Layout.tsx | 2 +- components/ui/Button.tsx | 20 ++ components/ui/forms/FormError.tsx | 20 ++ components/ui/forms/FormInfo.tsx | 12 ++ components/ui/forms/FormLabel.tsx | 17 ++ components/ui/forms/FormSegment.tsx | 12 ++ components/ui/forms/FormSelect.tsx | 38 ++++ components/ui/forms/FormTextArea.tsx | 37 ++++ components/ui/forms/FormTextInput.tsx | 33 +++ next.config.js | 3 + package-lock.json | 55 +++++ package.json | 6 +- pages/beers/[id].tsx | 106 +++++++++- pages/beers/create.tsx | 32 +++ pages/beers/index.tsx | 20 +- pages/breweries/index.tsx | 4 +- prisma/schema.prisma | 6 +- prisma/seed/create/createNewBeerImages.ts | 32 +++ prisma/seed/create/createNewBeerPosts.ts | 2 +- prisma/seed/create/createNewBreweryImages.ts | 33 +++ prisma/seed/index.ts | 57 ++++-- services/BeerPost/getAllBeerPosts.ts | 18 +- services/BeerPost/getBeerPostById.ts | 18 +- .../BeerPost/types/BeerPostQueryResult.ts | 16 +- services/BreweryPost/getAllBreweryPosts.ts | 18 +- services/BreweryPost/getBreweryPostById.ts | 33 ++- .../types/BreweryPostQueryResult.ts | 2 +- tailwind.config.js | 2 +- 29 files changed, 776 insertions(+), 70 deletions(-) create mode 100644 components/BeerForm.tsx create mode 100644 components/ui/Button.tsx create mode 100644 components/ui/forms/FormError.tsx create mode 100644 components/ui/forms/FormInfo.tsx create mode 100644 components/ui/forms/FormLabel.tsx create mode 100644 components/ui/forms/FormSegment.tsx create mode 100644 components/ui/forms/FormSelect.tsx create mode 100644 components/ui/forms/FormTextArea.tsx create mode 100644 components/ui/forms/FormTextInput.tsx create mode 100644 pages/beers/create.tsx create mode 100644 prisma/seed/create/createNewBeerImages.ts create mode 100644 prisma/seed/create/createNewBreweryImages.ts diff --git a/components/BeerForm.tsx b/components/BeerForm.tsx new file mode 100644 index 0000000..aec4bcc --- /dev/null +++ b/components/BeerForm.tsx @@ -0,0 +1,192 @@ +import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; + +import { FunctionComponent } from 'react'; +import { SubmitHandler, useForm } from 'react-hook-form'; +// import { useNavigate } from 'react-router-dom'; +// import createBeerPost from '../api/beerPostRoutes/createBeerPost'; +// import getAllBreweryPosts from '../api/breweryPostRoutes/getAllBreweryPosts'; + +// import BreweryPostI from '../types/BreweryPostI'; +// import isValidUuid from '../util/isValidUuid'; +import Button from './ui/Button'; +import FormError from './ui/forms/FormError'; +import FormInfo from './ui/forms/FormInfo'; +import FormLabel from './ui/forms/FormLabel'; +import FormSegment from './ui/forms/FormSegment'; +import FormSelect from './ui/forms/FormSelect'; +import FormTextArea from './ui/forms/FormTextArea'; +import FormTextInput from './ui/forms/FormTextInput'; + +interface IFormInput { + name: string; + description: string; + type: string; + abv: number; + ibu: number; + breweryId: string; +} + +interface BeerFormProps { + type: 'edit' | 'create'; + // eslint-disable-next-line react/require-default-props + defaultValues?: IFormInput; + breweries?: BreweryPostQueryResult[]; +} +const BeerForm: FunctionComponent = ({ + type, + defaultValues, + breweries = [], +}) => { + const { + register, + handleSubmit, + formState: { errors }, + } = useForm({ + defaultValues: { + name: defaultValues?.name, + description: defaultValues?.description, + abv: defaultValues?.abv, + ibu: defaultValues?.ibu, + }, + }); + + // const navigate = useNavigate(); + + const nameValidationSchema = register('name', { + required: 'Beer name is required.', + }); + const breweryValidationSchema = register('breweryId', { + required: 'Brewery name is required.', + }); + const abvValidationSchema = register('abv', { + required: 'ABV is required.', + valueAsNumber: true, + max: { value: 50, message: 'ABV must be less than 50%.' }, + min: { + value: 0.1, + message: 'ABV must be greater than 0.1%', + }, + validate: (abv) => !Number.isNaN(abv) || 'ABV is invalid.', + }); + const ibuValidationSchema = register('ibu', { + required: 'IBU is required.', + min: { + value: 2, + message: 'IBU must be greater than 2.', + }, + valueAsNumber: true, + validate: (ibu) => !Number.isNaN(ibu) || 'IBU is invalid.', + }); + + const onSubmit: SubmitHandler = (data) => { + console.log(data); + }; + + return ( +
+ + Name + {errors.name?.message} + + + + + {type === 'create' && breweries.length && ( + <> + + Brewery + {errors.breweryId?.message} + + + ({ + value: brewery.id, + text: brewery.name, + }))} + placeholder="Brewery" + message="Pick a brewery" + /> + + + )} + +
+
+ + ABV + {errors.abv?.message} + + +
+
+ + IBU + {errors.ibu?.message} + + +
+
+ + + Description + {errors.description?.message} + + + + + + + Type + {errors.type?.message} + + + + + + +
+ ); +}; + +export default BeerForm; diff --git a/components/Layout.tsx b/components/Layout.tsx index 9a198ad..b6306b7 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -7,7 +7,7 @@ const Layout: FC<{ children: ReactNode }> = ({ children }) => {
-
{children}
+
{children}
); }; diff --git a/components/ui/Button.tsx b/components/ui/Button.tsx new file mode 100644 index 0000000..0f1db4e --- /dev/null +++ b/components/ui/Button.tsx @@ -0,0 +1,20 @@ +import { FunctionComponent } from 'react'; + +interface FormButtonProps { + children: string; + type: 'button' | 'submit' | 'reset'; + className?: string; +} + +const Button: FunctionComponent = ({ children, type, className }) => ( + // eslint-disable-next-line react/button-has-type + +); + +Button.defaultProps = { + className: '', +}; + +export default Button; diff --git a/components/ui/forms/FormError.tsx b/components/ui/forms/FormError.tsx new file mode 100644 index 0000000..5d20030 --- /dev/null +++ b/components/ui/forms/FormError.tsx @@ -0,0 +1,20 @@ +import { FunctionComponent } from 'react'; +// import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'; +// import { faTriangleExclamation } from '@fortawesome/free-solid-svg-icons'; + +/** + * Component for a styled form error message. + * + * @example + * Something went wrong!; + */ +const FormError: FunctionComponent<{ children: string | undefined }> = ({ children }) => + children ? ( +
+ {children} +
+ ) : null; +export default FormError; diff --git a/components/ui/forms/FormInfo.tsx b/components/ui/forms/FormInfo.tsx new file mode 100644 index 0000000..5baeaa3 --- /dev/null +++ b/components/ui/forms/FormInfo.tsx @@ -0,0 +1,12 @@ +import { FunctionComponent, ReactNode } from 'react'; + +/** A container for both the form error and form label. */ +interface FormInfoProps { + children: Array | ReactNode; +} + +const FormInfo: FunctionComponent = ({ children }) => ( +
{children}
+); + +export default FormInfo; diff --git a/components/ui/forms/FormLabel.tsx b/components/ui/forms/FormLabel.tsx new file mode 100644 index 0000000..977a38e --- /dev/null +++ b/components/ui/forms/FormLabel.tsx @@ -0,0 +1,17 @@ +import { FunctionComponent } from 'react'; + +interface FormLabelProps { + htmlFor: string; + children: string; +} + +const FormLabel: FunctionComponent = ({ htmlFor, children }) => ( + +); + +export default FormLabel; diff --git a/components/ui/forms/FormSegment.tsx b/components/ui/forms/FormSegment.tsx new file mode 100644 index 0000000..80496b1 --- /dev/null +++ b/components/ui/forms/FormSegment.tsx @@ -0,0 +1,12 @@ +import { FunctionComponent } from 'react'; + +/** A container for both the form error and form label. */ +interface FormInfoProps { + children: Array | JSX.Element; +} + +const FormSegment: FunctionComponent = ({ children }) => ( +
{children}
+); + +export default FormSegment; diff --git a/components/ui/forms/FormSelect.tsx b/components/ui/forms/FormSelect.tsx new file mode 100644 index 0000000..f74c402 --- /dev/null +++ b/components/ui/forms/FormSelect.tsx @@ -0,0 +1,38 @@ +import { FunctionComponent } from 'react'; +import { UseFormRegisterReturn } from 'react-hook-form'; + +interface FormSelectProps { + options: ReadonlyArray<{ value: string; text: string }>; + id: string; + formRegister: UseFormRegisterReturn; + error: boolean; + placeholder: string; + message: string; +} + +const FormSelect: FunctionComponent = ({ + options, + id, + error, + formRegister, + placeholder, + message, +}) => ( + +); + +export default FormSelect; diff --git a/components/ui/forms/FormTextArea.tsx b/components/ui/forms/FormTextArea.tsx new file mode 100644 index 0000000..f8f2c5c --- /dev/null +++ b/components/ui/forms/FormTextArea.tsx @@ -0,0 +1,37 @@ +import { FunctionComponent } from 'react'; +import { UseFormRegisterReturn } from 'react-hook-form'; + +interface FormTextAreaProps { + placeholder?: string; + formValidationSchema: UseFormRegisterReturn; + error: boolean; + id: string; + rows: number; + className?: string; +} + +const FormTextArea: FunctionComponent = ({ + placeholder = '', + formValidationSchema, + error, + id, + rows, + className, +}) => ( +