Merge pull request #47 from aaronpo97/dev

Dev updates
This commit is contained in:
Aaron Po
2023-07-07 23:27:12 -04:00
committed by GitHub
70 changed files with 575 additions and 344 deletions

View File

@@ -1,9 +1,9 @@
import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments'; import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments';
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import { FC, useState } from 'react'; import { FC, useState } from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
import { z } from 'zod'; import { z } from 'zod';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import CommentContentBody from './CommentContentBody'; import CommentContentBody from './CommentContentBody';
import EditCommentBody from './EditCommentBody'; import EditCommentBody from './EditCommentBody';

View File

@@ -1,7 +1,7 @@
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import { Dispatch, SetStateAction, FC, useContext } from 'react'; import { Dispatch, SetStateAction, FC, useContext } from 'react';
import { FaEllipsisH } from 'react-icons/fa'; import { FaEllipsisH } from 'react-icons/fa';
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import { z } from 'zod'; import { z } from 'zod';
interface CommentCardDropdownProps { interface CommentCardDropdownProps {

View File

@@ -4,7 +4,7 @@ import { format } from 'date-fns';
import { Dispatch, FC, SetStateAction, useContext } from 'react'; import { Dispatch, FC, SetStateAction, useContext } from 'react';
import { Rating } from 'react-daisyui'; import { Rating } from 'react-daisyui';
import Link from 'next/link'; import Link from 'next/link';
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import { z } from 'zod'; import { z } from 'zod';
import CommentCardDropdown from './CommentCardDropdown'; import CommentCardDropdown from './CommentCardDropdown';

View File

@@ -4,8 +4,8 @@ import { Rating } from 'react-daisyui';
import { useForm, SubmitHandler } from 'react-hook-form'; import { useForm, SubmitHandler } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments'; import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments';
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments'; import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import createErrorToast from '@/util/createErrorToast'; import createErrorToast from '@/util/createErrorToast';

View File

@@ -1,6 +1,6 @@
import sendCreateBeerCommentRequest from '@/requests/BeerComment/sendCreateBeerCommentRequest'; import sendCreateBeerCommentRequest from '@/requests/BeerComment/sendCreateBeerCommentRequest';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { FunctionComponent } from 'react'; import { FunctionComponent } from 'react';
@@ -8,13 +8,13 @@ import { useForm, SubmitHandler } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments'; import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';
import createErrorToast from '@/util/createErrorToast'; import createErrorToast from '@/util/createErrorToast';
import CommentForm from '../ui/CommentForm'; import CommentForm from '../ui/CommentForm';
interface BeerCommentFormProps { interface BeerCommentFormProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
mutate: ReturnType<typeof useBeerPostComments>['mutate']; mutate: ReturnType<typeof useBeerPostComments>['mutate'];
} }

View File

@@ -4,14 +4,14 @@ import { FC, useContext } from 'react';
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import { FaRegEdit } from 'react-icons/fa'; import { FaRegEdit } from 'react-icons/fa';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount'; import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount';
import useTimeDistance from '@/hooks/utilities/useTimeDistance'; import useTimeDistance from '@/hooks/utilities/useTimeDistance';
import BeerPostLikeButton from './BeerPostLikeButton'; import BeerPostLikeButton from './BeerPostLikeButton';
interface BeerInfoHeaderProps { interface BeerInfoHeaderProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
} }
const BeerInfoHeader: FC<BeerInfoHeaderProps> = ({ beerPost }) => { const BeerInfoHeader: FC<BeerInfoHeaderProps> = ({ beerPost }) => {

View File

@@ -1,19 +1,19 @@
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { FC, MutableRefObject, useContext, useRef } from 'react'; import { FC, MutableRefObject, useContext, useRef } from 'react';
import { z } from 'zod'; import { z } from 'zod';
import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments'; import useBeerPostComments from '@/hooks/data-fetching/beer-comments/useBeerPostComments';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import BeerCommentForm from './BeerCommentForm'; import BeerCommentForm from './BeerCommentForm';
import LoadingComponent from './LoadingComponent'; import LoadingComponent from './LoadingComponent';
import CommentsComponent from '../ui/CommentsComponent'; import CommentsComponent from '../ui/CommentsComponent';
interface BeerPostCommentsSectionProps { interface BeerPostCommentsSectionProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
} }
const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost }) => { const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost }) => {

View File

@@ -3,12 +3,12 @@ import { FC, MutableRefObject, useRef } from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
import { z } from 'zod'; import { z } from 'zod';
import useBeerRecommendations from '@/hooks/data-fetching/beer-posts/useBeerRecommendations'; import useBeerRecommendations from '@/hooks/data-fetching/beer-posts/useBeerRecommendations';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import debounce from 'lodash/debounce'; import debounce from 'lodash/debounce';
import BeerRecommendationLoadingComponent from './BeerRecommendationLoadingComponent'; import BeerRecommendationLoadingComponent from './BeerRecommendationLoadingComponent';
const BeerRecommendationsSection: FC<{ const BeerRecommendationsSection: FC<{
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
}> = ({ beerPost }) => { }> = ({ beerPost }) => {
const PAGE_SIZE = 10; const PAGE_SIZE = 10;

View File

@@ -1,13 +1,13 @@
import Link from 'next/link'; import Link from 'next/link';
import { FC, useContext } from 'react'; import { FC, useContext } from 'react';
import Image from 'next/image'; import Image from 'next/image';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount'; import useGetBeerPostLikeCount from '@/hooks/data-fetching/beer-likes/useBeerPostLikeCount';
import BeerPostLikeButton from '../BeerById/BeerPostLikeButton'; import BeerPostLikeButton from '../BeerById/BeerPostLikeButton';
const BeerCard: FC<{ post: z.infer<typeof beerPostQueryResult> }> = ({ post }) => { const BeerCard: FC<{ post: z.infer<typeof BeerPostQueryResult> }> = ({ post }) => {
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
const { mutate, likeCount, isLoading } = useGetBeerPostLikeCount(post.id); const { mutate, likeCount, isLoading } = useGetBeerPostLikeCount(post.id);

View File

@@ -1,5 +1,5 @@
import UseBeerPostsByBrewery from '@/hooks/data-fetching/beer-posts/useBeerPostsByBrewery'; import UseBeerPostsByBrewery from '@/hooks/data-fetching/beer-posts/useBeerPostsByBrewery';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import Link from 'next/link'; import Link from 'next/link';
import { FC, MutableRefObject, useContext, useRef } from 'react'; import { FC, MutableRefObject, useContext, useRef } from 'react';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';

View File

@@ -1,6 +1,6 @@
import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments'; import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import { zodResolver } from '@hookform/resolvers/zod'; import { zodResolver } from '@hookform/resolvers/zod';
import { FC } from 'react'; import { FC } from 'react';
import { useForm, SubmitHandler } from 'react-hook-form'; import { useForm, SubmitHandler } from 'react-hook-form';

View File

@@ -1,8 +1,8 @@
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { FC, MutableRefObject, useContext, useRef } from 'react'; import { FC, MutableRefObject, useContext, useRef } from 'react';
import { z } from 'zod'; import { z } from 'zod';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments'; import useBreweryPostComments from '@/hooks/data-fetching/brewery-comments/useBreweryPostComments';
import LoadingComponent from '../BeerById/LoadingComponent'; import LoadingComponent from '../BeerById/LoadingComponent';

View File

@@ -1,7 +1,7 @@
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount'; import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount';
import useTimeDistance from '@/hooks/utilities/useTimeDistance'; import useTimeDistance from '@/hooks/utilities/useTimeDistance';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { format } from 'date-fns'; import { format } from 'date-fns';
import { FC, useContext } from 'react'; import { FC, useContext } from 'react';

View File

@@ -1,6 +1,6 @@
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount'; import useGetBreweryPostLikeCount from '@/hooks/data-fetching/brewery-likes/useGetBreweryPostLikeCount';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { FC, useContext } from 'react'; import { FC, useContext } from 'react';
import Link from 'next/link'; import Link from 'next/link';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -0,0 +1,284 @@
import sendUploadBreweryImagesRequest from '@/requests/BreweryImage/sendUploadBreweryImageRequest';
import sendCreateBreweryPostRequest from '@/requests/BreweryPost/sendCreateBreweryPostRequest';
import CreateBreweryPostSchema from '@/services/BreweryPost/schema/CreateBreweryPostSchema';
import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
import createErrorToast from '@/util/createErrorToast';
import { Tab } from '@headlessui/react';
import { zodResolver } from '@hookform/resolvers/zod';
import { AddressAutofillRetrieveResponse } from '@mapbox/search-js-core';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import { FC, Fragment } from 'react';
import {
useForm,
SubmitHandler,
FieldError,
UseFormRegister,
FieldErrors,
UseFormSetValue,
} from 'react-hook-form';
import toast from 'react-hot-toast';
import { z } from 'zod';
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 FormTextArea from '../ui/forms/FormTextArea';
import FormTextInput from '../ui/forms/FormTextInput';
import Button from '../ui/forms/Button';
const AddressAutofill = dynamic(
// @ts-ignore
() => import('@mapbox/search-js-react').then((mod) => mod.AddressAutofill),
{ ssr: false },
);
const CreateBreweryPostWithImagesSchema = CreateBreweryPostSchema.merge(
UploadImageValidationSchema,
);
const InfoSection: FC<{
register: UseFormRegister<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
errors: FieldErrors<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
isSubmitting: boolean;
}> = ({ register, errors, isSubmitting }) => {
return (
<>
<FormInfo>
<FormLabel htmlFor="name">Name</FormLabel>
<FormError>{errors.name?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Lorem Ipsum Brewing Company"
formValidationSchema={register('name')}
error={!!errors.name}
type="text"
id="name"
disabled={isSubmitting}
/>
</FormSegment>
<FormInfo>
<FormLabel htmlFor="description">Description</FormLabel>
<FormError>{errors.description?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextArea
placeholder="We make beer, and we make it good."
formValidationSchema={register('description')}
error={!!errors.description}
rows={4}
id="description"
disabled={isSubmitting}
/>
</FormSegment>
<FormInfo>
<FormLabel htmlFor="dateEstablished">Date Established</FormLabel>
<FormError>{errors.dateEstablished?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="2021-01-01"
formValidationSchema={register('dateEstablished')}
error={!!errors.dateEstablished}
type="date"
id="dateEstablished"
disabled={isSubmitting}
/>
</FormSegment>
<FormInfo>
<FormLabel htmlFor="images">Images</FormLabel>
<FormError>{(errors.images as FieldError | undefined)?.message}</FormError>
</FormInfo>
<FormSegment>
<input
type="file"
{...register('images')}
multiple
className="file-input-bordered file-input w-full"
disabled={isSubmitting}
/>
</FormSegment>
</>
);
};
const LocationSection: FC<{
register: UseFormRegister<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
errors: FieldErrors<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
isSubmitting: boolean;
setValue: UseFormSetValue<z.infer<typeof CreateBreweryPostWithImagesSchema>>;
}> = ({ register, errors, isSubmitting, setValue }) => {
const onAutoCompleteChange = (address: string) => {
setValue('address', address);
};
const onAutoCompleteRetrieve = (address: AddressAutofillRetrieveResponse) => {
const { country, region, place } = address.features[0].properties as unknown as {
country?: string;
region?: string;
place?: string;
};
setValue('country', country);
setValue('region', region);
setValue('city', place!);
};
return (
<>
<FormInfo>
<FormLabel htmlFor="address">Address</FormLabel>
<FormError>{errors.address?.message}</FormError>
</FormInfo>
<FormSegment>
<AddressAutofill
accessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN!}
onRetrieve={onAutoCompleteRetrieve}
onChange={onAutoCompleteChange}
>
<input
id="address"
type="text"
placeholder="1234 Main St"
className={`input-bordered input w-full appearance-none rounded-lg transition ease-in-out ${
errors.address?.message ? 'input-error' : ''
}`}
{...register('address')}
disabled={isSubmitting}
/>
</AddressAutofill>
</FormSegment>
<div className="flex space-x-3">
<div className="w-1/2">
<FormInfo>
<FormLabel htmlFor="city">City</FormLabel>
<FormError>{errors.city?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Toronto"
formValidationSchema={register('city')}
error={!!errors.city}
type="text"
id="city"
disabled={isSubmitting}
/>
</FormSegment>
</div>
<div className="w-1/2">
<FormInfo>
<FormLabel htmlFor="region">Region</FormLabel>
<FormError>{errors.region?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Ontario"
formValidationSchema={register('region')}
error={!!errors.region}
type="text"
id="region"
disabled={isSubmitting}
/>
</FormSegment>
</div>
</div>
<FormInfo>
<FormLabel htmlFor="country">Country</FormLabel>
<FormError>{errors.country?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Canada"
formValidationSchema={register('country')}
error={!!errors.country}
type="text"
id="country"
disabled={isSubmitting}
/>
</FormSegment>
</>
);
};
const CreateBreweryPostForm: FC = () => {
const {
register,
handleSubmit,
reset,
setValue,
formState: { errors, isSubmitting },
} = useForm<z.infer<typeof CreateBreweryPostWithImagesSchema>>({
resolver: zodResolver(CreateBreweryPostWithImagesSchema),
});
const router = useRouter();
const onSubmit: SubmitHandler<
z.infer<typeof CreateBreweryPostWithImagesSchema>
> = async (data) => {
const loadingToast = toast.loading('Creating brewery...');
try {
if (!(data.images instanceof FileList)) {
return;
}
const breweryPost = await sendCreateBreweryPostRequest(data);
await sendUploadBreweryImagesRequest({ breweryPost, images: data.images });
await router.push(`/breweries/${breweryPost.id}`);
toast.remove(loadingToast);
toast.success('Created brewery.');
} catch (error) {
toast.remove(loadingToast);
createErrorToast(error);
reset();
}
};
return (
<form
onSubmit={handleSubmit(onSubmit, (error) => {
const fieldErrors = Object.keys(error).length;
toast.error(`Form submission failed.`);
toast.error(`You have ${fieldErrors} errors in your form.`);
})}
className="form-control"
autoComplete="off"
>
<Tab.Group as={Fragment}>
<Tab.List className="tabs tabs-boxed items-center justify-center rounded-2xl">
<Tab className="tab tab-md w-1/2 uppercase ui-selected:tab-active">
Information
</Tab>
<Tab className="tab tab-md w-1/2 uppercase ui-selected:tab-active">
Location
</Tab>
</Tab.List>
<Tab.Panels className="mt-4">
<Tab.Panel>
<InfoSection
register={register}
errors={errors}
isSubmitting={isSubmitting}
/>
</Tab.Panel>
<Tab.Panel>
<LocationSection
setValue={setValue}
register={register}
errors={errors}
isSubmitting={isSubmitting}
/>
</Tab.Panel>
</Tab.Panels>
</Tab.Group>
<div className="mt-8">
<Button type="submit" isSubmitting={isSubmitting}>
Create Brewery Post
</Button>
</div>
</form>
);
};
export default CreateBreweryPostForm;

View File

@@ -4,10 +4,10 @@ import router from 'next/router';
import { FunctionComponent } from 'react'; import { FunctionComponent } from 'react';
import { useForm, SubmitHandler, FieldError } from 'react-hook-form'; import { useForm, SubmitHandler, FieldError } from 'react-hook-form';
import { z } from 'zod'; import { z } from 'zod';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import CreateBeerPostValidationSchema from '@/services/BeerPost/schema/CreateBeerPostValidationSchema'; import CreateBeerPostValidationSchema from '@/services/BeerPost/schema/CreateBeerPostValidationSchema';
import sendCreateBeerPostRequest from '@/requests/BeerPost/sendCreateBeerPostRequest'; import sendCreateBeerPostRequest from '@/requests/BeerPost/sendCreateBeerPostRequest';
import UploadImageValidationSchema from '@/services/types/ImageSchema/UploadImageValidationSchema'; import UploadImageValidationSchema from '@/services/schema/ImageSchema/UploadImageValidationSchema';
import sendUploadBeerImagesRequest from '@/requests/BeerImage/sendUploadBeerImageRequest'; import sendUploadBeerImagesRequest from '@/requests/BeerImage/sendUploadBeerImageRequest';
import toast from 'react-hot-toast'; import toast from 'react-hot-toast';

View File

@@ -1,4 +1,4 @@
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod'; import { z } from 'zod';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';

View File

@@ -1,4 +1,4 @@
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import useSWR from 'swr'; import useSWR from 'swr';
import { z } from 'zod'; import { z } from 'zod';
/** /**
@@ -23,7 +23,7 @@ const useBeerPostSearch = (query: string | undefined) => {
} }
const json = await response.json(); const json = await response.json();
const result = z.array(beerPostQueryResult).parse(json); const result = z.array(BeerPostQueryResult).parse(json);
return result; return result;
}, },

View File

@@ -1,4 +1,4 @@
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';
import { z } from 'zod'; import { z } from 'zod';
@@ -34,7 +34,7 @@ const useBeerPosts = ({ pageSize }: { pageSize: number }) => {
throw new Error('API response validation failed'); throw new Error('API response validation failed');
} }
const parsedPayload = z.array(beerPostQueryResult).safeParse(parsed.data.payload); const parsedPayload = z.array(BeerPostQueryResult).safeParse(parsed.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {
throw new Error('API response validation failed'); throw new Error('API response validation failed');
} }

View File

@@ -1,4 +1,4 @@
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';
import { z } from 'zod'; import { z } from 'zod';
@@ -40,7 +40,7 @@ const UseBeerPostsByBrewery = ({ pageSize, breweryId }: UseBeerPostsByBreweryPar
throw new Error('API response validation failed'); throw new Error('API response validation failed');
} }
const parsedPayload = z.array(beerPostQueryResult).safeParse(parsed.data.payload); const parsedPayload = z.array(BeerPostQueryResult).safeParse(parsed.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {
throw new Error('API response validation failed'); throw new Error('API response validation failed');
} }

View File

@@ -1,11 +1,11 @@
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';
import { z } from 'zod'; import { z } from 'zod';
interface UseBeerRecommendationsParams { interface UseBeerRecommendationsParams {
pageSize: number; pageSize: number;
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
} }
/** /**
@@ -40,7 +40,7 @@ const UseBeerPostsByBrewery = ({ pageSize, beerPost }: UseBeerRecommendationsPar
throw new Error('API response validation failed'); throw new Error('API response validation failed');
} }
const parsedPayload = z.array(beerPostQueryResult).safeParse(parsed.data.payload); const parsedPayload = z.array(BeerPostQueryResult).safeParse(parsed.data.payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {
throw new Error('API response validation failed'); throw new Error('API response validation failed');
} }

View File

@@ -1,4 +1,4 @@
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod'; import { z } from 'zod';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';

View File

@@ -1,4 +1,4 @@
import BreweryPostMapQueryResult from '@/services/BreweryPost/types/BreweryPostMapQueryResult'; import BreweryPostMapQueryResult from '@/services/BreweryPost/schema/BreweryPostMapQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -1,4 +1,4 @@
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import useSWRInfinite from 'swr/infinite'; import useSWRInfinite from 'swr/infinite';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -5,7 +5,7 @@ import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import ServerError from '@/config/util/ServerError'; import ServerError from '@/config/util/ServerError';
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import findBeerCommentById from '@/services/BeerComment/findBeerCommentById'; import findBeerCommentById from '@/services/BeerComment/findBeerCommentById';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import editBeerCommentById from '@/services/BeerComment/editBeerCommentById'; import editBeerCommentById from '@/services/BeerComment/editBeerCommentById';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';

View File

@@ -10,8 +10,8 @@ import { createRouter } from 'next-connect';
import { z } from 'zod'; import { z } from 'zod';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser'; import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
interface CreateCommentRequest extends UserExtendedNextApiRequest { interface CreateCommentRequest extends UserExtendedNextApiRequest {
body: z.infer<typeof CreateCommentValidationSchema>; body: z.infer<typeof CreateCommentValidationSchema>;

View File

@@ -13,7 +13,7 @@ import { z } from 'zod';
import ServerError from '@/config/util/ServerError'; import ServerError from '@/config/util/ServerError';
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import addBeerImageToDB from '@/services/BeerImage/addBeerImageToDB'; import addBeerImageToDB from '@/services/BeerImage/addBeerImageToDB';
import ImageMetadataValidationSchema from '@/services/types/ImageSchema/ImageMetadataValidationSchema'; import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
const { storage } = cloudinaryConfig; const { storage } = cloudinaryConfig;

View File

@@ -5,7 +5,7 @@ import { NextApiRequest, NextApiResponse } from 'next';
import { createRouter } from 'next-connect'; import { createRouter } from 'next-connect';
import { z } from 'zod'; import { z } from 'zod';
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
const SearchSchema = z.object({ const SearchSchema = z.object({
search: z.string().min(1), search: z.string().min(1),
@@ -18,7 +18,7 @@ interface SearchAPIRequest extends NextApiRequest {
const search = async (req: SearchAPIRequest, res: NextApiResponse) => { const search = async (req: SearchAPIRequest, res: NextApiResponse) => {
const { search: query } = req.query; const { search: query } = req.query;
const beers: z.infer<typeof beerPostQueryResult>[] = const beers: z.infer<typeof BeerPostQueryResult>[] =
await DBClient.instance.beerPost.findMany({ await DBClient.instance.beerPost.findMany({
select: { select: {
id: true, id: true,

View File

@@ -1,7 +1,7 @@
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions'; import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiRequest, NextApiResponse } from 'next'; import { NextApiRequest, NextApiResponse } from 'next';
import { createRouter } from 'next-connect'; import { createRouter } from 'next-connect';
@@ -18,7 +18,7 @@ const getAllBeersByBrewery = async (
// eslint-disable-next-line @typescript-eslint/naming-convention // eslint-disable-next-line @typescript-eslint/naming-convention
const { page_size, page_num, id } = req.query; const { page_size, page_num, id } = req.query;
const beers: z.infer<typeof beerPostQueryResult>[] = const beers: z.infer<typeof BeerPostQueryResult>[] =
await DBClient.instance.beerPost.findMany({ await DBClient.instance.beerPost.findMany({
where: { breweryId: id }, where: { breweryId: id },
take: parseInt(page_size, 10), take: parseInt(page_size, 10),

View File

@@ -13,9 +13,9 @@ import { z } from 'zod';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser'; import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import getAllBreweryComments from '@/services/BreweryComment/getAllBreweryComments'; import getAllBreweryComments from '@/services/BreweryComment/getAllBreweryComments';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import createNewBreweryComment from '@/services/BreweryComment/createNewBreweryComment'; import createNewBreweryComment from '@/services/BreweryComment/createNewBreweryComment';
interface CreateCommentRequest extends UserExtendedNextApiRequest { interface CreateCommentRequest extends UserExtendedNextApiRequest {

View File

@@ -12,7 +12,7 @@ import { NextApiResponse } from 'next';
import { z } from 'zod'; import { z } from 'zod';
import ServerError from '@/config/util/ServerError'; import ServerError from '@/config/util/ServerError';
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import ImageMetadataValidationSchema from '@/services/types/ImageSchema/ImageMetadataValidationSchema'; import ImageMetadataValidationSchema from '@/services/schema/ImageSchema/ImageMetadataValidationSchema';
import addBreweryImageToDB from '@/services/BreweryImage/addBreweryImageToDB'; import addBreweryImageToDB from '@/services/BreweryImage/addBreweryImageToDB';
const { storage } = cloudinaryConfig; const { storage } = cloudinaryConfig;

View File

@@ -0,0 +1,91 @@
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import { UserExtendedNextApiRequest } from '@/config/auth/types';
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next';
import { createRouter, NextHandler } from 'next-connect';
import { z } from 'zod';
import ServerError from '@/config/util/ServerError';
import DBClient from '@/prisma/DBClient';
import getBreweryPostById from '@/services/BreweryPost/getBreweryPostById';
import EditBreweryPostValidationSchema from '@/services/BreweryPost/schema/EditBreweryPostValidationSchema';
interface BreweryPostRequest extends UserExtendedNextApiRequest {
query: { id: string };
}
interface EditBreweryPostRequest extends BreweryPostRequest {
body: z.infer<typeof EditBreweryPostValidationSchema>;
}
const checkIfBreweryPostOwner = async (
req: BreweryPostRequest,
res: NextApiResponse,
next: NextHandler,
) => {
const user = req.user!;
const { id } = req.query;
const breweryPost = await getBreweryPostById(id);
if (!breweryPost) {
throw new ServerError('Brewery post not found', 404);
}
if (breweryPost.postedBy.id !== user.id) {
throw new ServerError('You are not the owner of this brewery post', 403);
}
next();
};
const editBreweryPost = async (
req: EditBreweryPostRequest,
res: NextApiResponse<z.infer<typeof APIResponseValidationSchema>>,
) => {
const {
body,
query: { id },
} = req;
await DBClient.instance.breweryPost.update({
where: { id },
data: body,
});
res.status(200).json({
message: 'Brewery post updated successfully',
success: true,
statusCode: 200,
});
};
const deleteBreweryPost = async (req: BreweryPostRequest, res: NextApiResponse) => {
const {
query: { id },
} = req;
const deleted = await DBClient.instance.beerPost.delete({
where: { id },
});
if (!deleted) {
throw new ServerError('Brewery post not found', 404);
}
res.status(200).json({
message: 'Brewery post deleted successfully',
success: true,
statusCode: 200,
});
};
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;

View File

@@ -6,7 +6,7 @@ import { NextApiResponse } from 'next';
import { z } from 'zod'; import { z } from 'zod';
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions'; import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser'; import getCurrentUser from '@/config/nextConnect/middleware/getCurrentUser';
import CreateBreweryPostSchema from '@/services/BreweryPost/types/CreateBreweryPostSchema'; import CreateBreweryPostSchema from '@/services/BreweryPost/schema/CreateBreweryPostSchema';
import createNewBreweryPost from '@/services/BreweryPost/createNewBreweryPost'; import createNewBreweryPost from '@/services/BreweryPost/createNewBreweryPost';
import geocode from '@/config/mapbox/geocoder'; import geocode from '@/config/mapbox/geocoder';
import ServerError from '@/config/util/ServerError'; import ServerError from '@/config/util/ServerError';

View File

@@ -1,6 +1,6 @@
import validateRequest from '@/config/nextConnect/middleware/validateRequest'; import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import BreweryPostMapQueryResult from '@/services/BreweryPost/types/BreweryPostMapQueryResult'; import BreweryPostMapQueryResult from '@/services/BreweryPost/schema/BreweryPostMapQueryResult';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiRequest, NextApiResponse } from 'next'; import { NextApiRequest, NextApiResponse } from 'next';

View File

@@ -4,7 +4,7 @@ import validateRequest from '@/config/nextConnect/middleware/validateRequest';
import NextConnectOptions from '@/config/nextConnect/NextConnectOptions'; import NextConnectOptions from '@/config/nextConnect/NextConnectOptions';
import ServerError from '@/config/util/ServerError'; import ServerError from '@/config/util/ServerError';
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { NextApiResponse } from 'next'; import { NextApiResponse } from 'next';

View File

@@ -4,17 +4,17 @@ import React from 'react';
import withPageAuthRequired from '@/util/withPageAuthRequired'; import withPageAuthRequired from '@/util/withPageAuthRequired';
import getBeerPostById from '@/services/BeerPost/getBeerPostById'; import getBeerPostById from '@/services/BeerPost/getBeerPostById';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import EditBeerPostForm from '@/components/EditBeerPostForm'; import EditBeerPostForm from '@/components/EditBeerPostForm';
import FormPageLayout from '@/components/ui/forms/FormPageLayout'; import FormPageLayout from '@/components/ui/forms/FormPageLayout';
import { BiBeer } from 'react-icons/bi'; import { BiBeer } from 'react-icons/bi';
import { z } from 'zod'; import { z } from 'zod';
interface EditPageProps { interface EditPageProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
} }
const EditPage: NextPage<EditPageProps> = ({ beerPost }) => { const EditBeerPostPage: NextPage<EditPageProps> = ({ beerPost }) => {
const pageTitle = `Edit \u201c${beerPost.name}\u201d`; const pageTitle = `Edit \u201c${beerPost.name}\u201d`;
return ( return (
@@ -44,7 +44,7 @@ const EditPage: NextPage<EditPageProps> = ({ beerPost }) => {
); );
}; };
export default EditPage; export default EditBeerPostPage;
export const getServerSideProps = withPageAuthRequired<EditPageProps>( export const getServerSideProps = withPageAuthRequired<EditPageProps>(
async (context, session) => { async (context, session) => {

View File

@@ -4,7 +4,7 @@ import Image from 'next/image';
import getBeerPostById from '@/services/BeerPost/getBeerPostById'; import getBeerPostById from '@/services/BeerPost/getBeerPostById';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
@@ -21,7 +21,7 @@ const [BeerInfoHeader, BeerPostCommentsSection, BeerRecommendations] = [
]; ];
interface BeerPageProps { interface BeerPageProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
} }
const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost }) => { const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost }) => {

View File

@@ -3,7 +3,7 @@ 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 BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/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';

View File

@@ -0,0 +1,55 @@
import FormPageLayout from '@/components/ui/forms/FormPageLayout';
import getBreweryPostById from '@/services/BreweryPost/getBreweryPostById';
import BreweryPostQueryResult from '@/services/BreweryPost/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}"`}
>
<></>
</FormPageLayout>
</>
);
};
export default EditBreweryPostPage;
export const getServerSideProps = withPageAuthRequired<EditPageProps>(
async (context, session) => {
const breweryPostId = context.params?.id as string;
const breweryPost = await getBreweryPostById(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 } };
},
);

View File

@@ -1,5 +1,5 @@
import getBreweryPostById from '@/services/BreweryPost/getBreweryPostById'; import getBreweryPostById from '@/services/BreweryPost/getBreweryPostById';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { GetServerSideProps, NextPage } from 'next'; import { GetServerSideProps, NextPage } from 'next';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -1,115 +1,12 @@
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 FormPageLayout from '@/components/ui/forms/FormPageLayout'; import FormPageLayout from '@/components/ui/forms/FormPageLayout';
import FormSegment from '@/components/ui/forms/FormSegment';
import FormTextArea from '@/components/ui/forms/FormTextArea';
import FormTextInput from '@/components/ui/forms/FormTextInput';
import createErrorToast from '@/util/createErrorToast';
import withPageAuthRequired from '@/util/withPageAuthRequired'; import withPageAuthRequired from '@/util/withPageAuthRequired';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { zodResolver } from '@hookform/resolvers/zod';
import type { AddressAutofillRetrieveResponse } from '@mapbox/search-js-core';
import { GetServerSideProps, NextPage } from 'next'; import { GetServerSideProps, NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import { FieldError, SubmitHandler, useForm } from 'react-hook-form';
import toast from 'react-hot-toast';
import { FaBeer } from 'react-icons/fa'; import { FaBeer } from 'react-icons/fa';
import { z } from 'zod'; import CreateBreweryPostForm from '@/components/BreweryPost/CreateBreweryPostForm';
import dynamic from 'next/dynamic';
import { useRouter } from 'next/router';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult';
import CreateBreweryPostSchema from '@/services/BreweryPost/types/CreateBreweryPostSchema';
import UploadImageValidationSchema from '@/services/types/ImageSchema/UploadImageValidationSchema';
import sendUploadBreweryImagesRequest from '@/requests/BreweryImage/sendUploadBreweryImageRequest';
const AddressAutofill = dynamic(
() => import('@mapbox/search-js-react').then((mod) => mod.AddressAutofill),
{ ssr: false },
);
const CreateBreweryPostWithImagesSchema = CreateBreweryPostSchema.merge(
UploadImageValidationSchema,
);
const sendCreateBreweryPostRequest = async (
data: z.infer<typeof CreateBreweryPostSchema>,
) => {
const response = await fetch('/api/breweries/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('API response parsing failed');
}
const parsedPayload = BreweryPostQueryResult.safeParse(parsed.data.payload);
if (!parsedPayload.success) {
throw new Error('API response payload parsing failed');
}
return parsedPayload.data;
};
const CreateBreweryPage: NextPage = () => { const CreateBreweryPage: NextPage = () => {
const {
register,
handleSubmit,
reset,
setValue,
formState: { errors, isSubmitting },
} = useForm<z.infer<typeof CreateBreweryPostWithImagesSchema>>({
resolver: zodResolver(CreateBreweryPostWithImagesSchema),
});
const router = useRouter();
const onSubmit: SubmitHandler<
z.infer<typeof CreateBreweryPostWithImagesSchema>
> = async (data) => {
const loadingToast = toast.loading('Creating brewery...');
try {
if (!(data.images instanceof FileList)) {
return;
}
const breweryPost = await sendCreateBreweryPostRequest(data);
await sendUploadBreweryImagesRequest({ breweryPost, images: data.images });
await router.push(`/breweries/${breweryPost.id}`);
toast.remove(loadingToast);
toast.success('Created brewery.');
} catch (error) {
toast.remove(loadingToast);
reset();
createErrorToast(error);
}
};
const onAutoCompleteChange = (address: string) => {
setValue('address', address);
};
const onAutoCompleteRetrieve = (address: AddressAutofillRetrieveResponse) => {
const { country, region, place } = address.features[0].properties as unknown as {
country?: string;
region?: string;
place?: string;
};
setValue('country', country);
setValue('region', region);
setValue('city', place!);
};
return ( return (
<> <>
<Head> <Head>
@@ -123,150 +20,7 @@ const CreateBreweryPage: NextPage = () => {
headingText="Create Brewery" headingText="Create Brewery"
headingIcon={FaBeer} headingIcon={FaBeer}
> >
<form <CreateBreweryPostForm />
onSubmit={handleSubmit(onSubmit)}
className="form-control"
autoComplete="off"
>
<div>
<FormInfo>
<FormLabel htmlFor="name">Name</FormLabel>
<FormError>{errors.name?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Lorem Ipsum Brewing Company"
formValidationSchema={register('name')}
error={!!errors.name}
type="text"
id="name"
disabled={isSubmitting}
/>
</FormSegment>
<FormInfo>
<FormLabel htmlFor="description">Description</FormLabel>
<FormError>{errors.description?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextArea
placeholder="We make beer, and we make it good."
formValidationSchema={register('description')}
error={!!errors.description}
rows={4}
id="description"
disabled={isSubmitting}
/>
</FormSegment>
<FormInfo>
<FormLabel htmlFor="dateEstablished">Date Established</FormLabel>
<FormError>{errors.dateEstablished?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="2021-01-01"
formValidationSchema={register('dateEstablished')}
error={!!errors.dateEstablished}
type="date"
id="dateEstablished"
disabled={isSubmitting}
/>
</FormSegment>
<FormInfo>
<FormLabel htmlFor="images">Images</FormLabel>
<FormError>
{(errors.images as FieldError | undefined)?.message}
</FormError>
</FormInfo>
<FormSegment>
<input
type="file"
{...register('images')}
multiple
className="file-input-bordered file-input w-full"
disabled={isSubmitting}
/>
</FormSegment>
</div>
<div>
<FormInfo>
<FormLabel htmlFor="address">Address</FormLabel>
<FormError>{errors.address?.message}</FormError>
</FormInfo>
<FormSegment>
<AddressAutofill
accessToken={process.env.NEXT_PUBLIC_MAPBOX_ACCESS_TOKEN!}
onRetrieve={onAutoCompleteRetrieve}
onChange={onAutoCompleteChange}
>
<input
id="address"
type="text"
placeholder="1234 Main St"
className={`input-bordered input w-full appearance-none rounded-lg transition ease-in-out ${
errors.address?.message ? 'input-error' : ''
}`}
{...register('address')}
disabled={isSubmitting}
/>
</AddressAutofill>
</FormSegment>
<div className="flex space-x-3">
<div className="w-1/2">
<FormInfo>
<FormLabel htmlFor="city">City</FormLabel>
<FormError>{errors.city?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Toronto"
formValidationSchema={register('city')}
error={!!errors.city}
type="text"
id="city"
disabled={isSubmitting}
/>
</FormSegment>
</div>
<div className="w-1/2">
<FormInfo>
<FormLabel htmlFor="region">Region</FormLabel>
<FormError>{errors.region?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Ontario"
formValidationSchema={register('region')}
error={!!errors.region}
type="text"
id="region"
disabled={isSubmitting}
/>
</FormSegment>
</div>
</div>
<FormInfo>
<FormLabel htmlFor="country">Country</FormLabel>
<FormError>{errors.country?.message}</FormError>
</FormInfo>
<FormSegment>
<FormTextInput
placeholder="Canada"
formValidationSchema={register('country')}
error={!!errors.country}
type="text"
id="country"
disabled={isSubmitting}
/>
</FormSegment>
</div>
<div className="mt-4">
<Button type="submit" isSubmitting={isSubmitting}>
Create Brewery Post
</Button>
</div>
</form>
</FormPageLayout> </FormPageLayout>
</div> </div>
</div> </div>

View File

@@ -3,7 +3,7 @@ import LoadingCard from '@/components/ui/LoadingCard';
import Spinner from '@/components/ui/Spinner'; import Spinner from '@/components/ui/Spinner';
import UserContext from '@/contexts/UserContext'; import UserContext from '@/contexts/UserContext';
import useBreweryPosts from '@/hooks/data-fetching/brewery-posts/useBreweryPosts'; import useBreweryPosts from '@/hooks/data-fetching/brewery-posts/useBreweryPosts';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { NextPage } from 'next'; import { NextPage } from 'next';
import Head from 'next/head'; import Head from 'next/head';
import { useContext, MutableRefObject, useRef } from 'react'; import { useContext, MutableRefObject, useRef } from 'react';

View File

@@ -7,7 +7,7 @@ import LocationMarker from '@/components/ui/LocationMarker';
import Link from 'next/link'; import Link from 'next/link';
import Head from 'next/head'; import Head from 'next/head';
import useGeolocation from '@/hooks/utilities/useGeolocation'; import useGeolocation from '@/hooks/utilities/useGeolocation';
import BreweryPostMapQueryResult from '@/services/BreweryPost/types/BreweryPostMapQueryResult'; import BreweryPostMapQueryResult from '@/services/BreweryPost/schema/BreweryPostMapQueryResult';
import { z } from 'zod'; import { z } from 'zod';
import useBreweryMapPagePosts from '@/hooks/data-fetching/brewery-posts/useBreweryMapPagePosts'; import useBreweryMapPagePosts from '@/hooks/data-fetching/brewery-posts/useBreweryMapPagePosts';
import ControlPanel from '@/components/ui/maps/ControlPanel'; import ControlPanel from '@/components/ui/maps/ControlPanel';

View File

@@ -1,5 +1,5 @@
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -1,8 +1,8 @@
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
interface SendUploadBeerImagesRequestArgs { interface SendUploadBeerImagesRequestArgs {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof BeerPostQueryResult>;
images: FileList; images: FileList;
} }

View File

@@ -1,4 +1,4 @@
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import CreateBeerPostValidationSchema from '@/services/BeerPost/schema/CreateBeerPostValidationSchema'; import CreateBeerPostValidationSchema from '@/services/BeerPost/schema/CreateBeerPostValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod'; import { z } from 'zod';
@@ -24,7 +24,7 @@ const sendCreateBeerPostRequest = async (
throw new Error(message); throw new Error(message);
} }
const parsedPayload = beerPostQueryResult.safeParse(payload); const parsedPayload = BeerPostQueryResult.safeParse(payload);
if (!parsedPayload.success) { if (!parsedPayload.success) {
throw new Error('Invalid API response payload'); throw new Error('Invalid API response payload');

View File

@@ -1,5 +1,5 @@
import CommentQueryResult from '@/services/types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '@/services/schema/CommentSchema/CommentQueryResult';
import CreateCommentValidationSchema from '@/services/types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '@/services/schema/CommentSchema/CreateCommentValidationSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema'; import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -1,4 +1,4 @@
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
interface SendUploadBeerImagesRequestArgs { interface SendUploadBeerImagesRequestArgs {

View File

@@ -0,0 +1,34 @@
import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import CreateBreweryPostSchema from '@/services/BreweryPost/schema/CreateBreweryPostSchema';
import APIResponseValidationSchema from '@/validation/APIResponseValidationSchema';
import { z } from 'zod';
const sendCreateBreweryPostRequest = async (
data: z.infer<typeof CreateBreweryPostSchema>,
) => {
const response = await fetch('/api/breweries/create', {
method: 'POST',
headers: { 'Content-Type': 'application/json' },
body: JSON.stringify(data),
});
if (!response.ok) {
throw new Error(response.statusText);
}
const json = await response.json();
const parsed = APIResponseValidationSchema.safeParse(json);
if (!parsed.success) {
throw new Error('API response parsing failed');
}
const parsedPayload = BreweryPostQueryResult.safeParse(parsed.data.payload);
if (!parsedPayload.success) {
throw new Error('API response payload parsing failed');
}
return parsedPayload.data;
};
export default sendCreateBreweryPostRequest;

View File

@@ -1,6 +1,6 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import { z } from 'zod'; import { z } from 'zod';
import CreateCommentValidationSchema from '../types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '../schema/CommentSchema/CreateCommentValidationSchema';
const CreateNewBeerCommentServiceSchema = CreateCommentValidationSchema.extend({ const CreateNewBeerCommentServiceSchema = CreateCommentValidationSchema.extend({
userId: z.string().cuid(), userId: z.string().cuid(),

View File

@@ -1,10 +1,10 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
import CommentQueryResult from '../types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '../schema/CommentSchema/CommentQueryResult';
const getAllBeerComments = async ( const getAllBeerComments = async (
{ id }: Pick<z.infer<typeof beerPostQueryResult>, 'id'>, { id }: Pick<z.infer<typeof BeerPostQueryResult>, 'id'>,
{ pageSize, pageNum = 0 }: { pageSize: number; pageNum?: number }, { pageSize, pageNum = 0 }: { pageSize: number; pageNum?: number },
) => { ) => {
const skip = (pageNum - 1) * pageSize; const skip = (pageNum - 1) * pageSize;

View File

@@ -1,7 +1,7 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import { BeerImage } from '@prisma/client'; import { BeerImage } from '@prisma/client';
import { z } from 'zod'; import { z } from 'zod';
import ImageMetadataValidationSchema from '../types/ImageSchema/ImageMetadataValidationSchema'; import ImageMetadataValidationSchema from '../schema/ImageSchema/ImageMetadataValidationSchema';
interface ProcessImageDataArgs { interface ProcessImageDataArgs {
files: Express.Multer.File[]; files: Express.Multer.File[];

View File

@@ -1,6 +1,6 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import { z } from 'zod'; import { z } from 'zod';
import beerPostQueryResult from './schema/BeerPostQueryResult'; import BeerPostQueryResult from './schema/BeerPostQueryResult';
import CreateBeerPostValidationSchema from './schema/CreateBeerPostValidationSchema'; import CreateBeerPostValidationSchema from './schema/CreateBeerPostValidationSchema';
const CreateBeerPostWithUserSchema = CreateBeerPostValidationSchema.extend({ const CreateBeerPostWithUserSchema = CreateBeerPostValidationSchema.extend({
@@ -16,7 +16,7 @@ const createNewBeerPost = async ({
breweryId, breweryId,
userId, userId,
}: z.infer<typeof CreateBeerPostWithUserSchema>) => { }: z.infer<typeof CreateBeerPostWithUserSchema>) => {
const newBeerPost: z.infer<typeof beerPostQueryResult> = const newBeerPost: z.infer<typeof BeerPostQueryResult> =
await DBClient.instance.beerPost.create({ await DBClient.instance.beerPost.create({
data: { data: {
name, name,

View File

@@ -1,5 +1,5 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
const prisma = DBClient.instance; const prisma = DBClient.instance;
@@ -7,7 +7,7 @@ const prisma = DBClient.instance;
const getAllBeerPosts = async (pageNum: number, pageSize: number) => { const getAllBeerPosts = async (pageNum: number, pageSize: number) => {
const skip = (pageNum - 1) * pageSize; const skip = (pageNum - 1) * pageSize;
const beerPosts: z.infer<typeof beerPostQueryResult>[] = await prisma.beerPost.findMany( const beerPosts: z.infer<typeof BeerPostQueryResult>[] = await prisma.beerPost.findMany(
{ {
select: { select: {
id: true, id: true,

View File

@@ -1,11 +1,11 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
const prisma = DBClient.instance; const prisma = DBClient.instance;
const getBeerPostById = async (id: string) => { const getBeerPostById = async (id: string) => {
const beerPost: z.infer<typeof beerPostQueryResult> | null = const beerPost: z.infer<typeof BeerPostQueryResult> | null =
await prisma.beerPost.findFirst({ await prisma.beerPost.findFirst({
select: { select: {
id: true, id: true,

View File

@@ -1,6 +1,6 @@
import { z } from 'zod'; import { z } from 'zod';
const beerPostQueryResult = z.object({ const BeerPostQueryResult = z.object({
id: z.string(), id: z.string(),
name: z.string(), name: z.string(),
brewery: z.object({ id: z.string(), name: z.string() }), brewery: z.object({ id: z.string(), name: z.string() }),
@@ -15,4 +15,4 @@ const beerPostQueryResult = z.object({
createdAt: z.coerce.date(), createdAt: z.coerce.date(),
}); });
export default beerPostQueryResult; export default BeerPostQueryResult;

View File

@@ -1,6 +1,6 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import { z } from 'zod'; import { z } from 'zod';
import CreateCommentValidationSchema from '../types/CommentSchema/CreateCommentValidationSchema'; import CreateCommentValidationSchema from '../schema/CommentSchema/CreateCommentValidationSchema';
const CreateNewBreweryCommentServiceSchema = CreateCommentValidationSchema.extend({ const CreateNewBreweryCommentServiceSchema = CreateCommentValidationSchema.extend({
userId: z.string().cuid(), userId: z.string().cuid(),

View File

@@ -1,10 +1,10 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import BeerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
import CommentQueryResult from '../types/CommentSchema/CommentQueryResult'; import CommentQueryResult from '../schema/CommentSchema/CommentQueryResult';
const getAllBreweryComments = async ( const getAllBreweryComments = async (
{ id }: Pick<z.infer<typeof beerPostQueryResult>, 'id'>, { id }: Pick<z.infer<typeof BeerPostQueryResult>, 'id'>,
{ pageSize, pageNum = 0 }: { pageSize: number; pageNum?: number }, { pageSize, pageNum = 0 }: { pageSize: number; pageNum?: number },
) => { ) => {
const skip = (pageNum - 1) * pageSize; const skip = (pageNum - 1) * pageSize;

View File

@@ -1,7 +1,7 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import { BreweryImage } from '@prisma/client'; import { BreweryImage } from '@prisma/client';
import { z } from 'zod'; import { z } from 'zod';
import ImageMetadataValidationSchema from '../types/ImageSchema/ImageMetadataValidationSchema'; import ImageMetadataValidationSchema from '../schema/ImageSchema/ImageMetadataValidationSchema';
interface ProcessImageDataArgs { interface ProcessImageDataArgs {
files: Express.Multer.File[]; files: Express.Multer.File[];

View File

@@ -1,7 +1,7 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import { z } from 'zod'; import { z } from 'zod';
import CreateBreweryPostSchema from './types/CreateBreweryPostSchema'; import CreateBreweryPostSchema from './schema/CreateBreweryPostSchema';
import BreweryPostQueryResult from './types/BreweryPostQueryResult'; import BreweryPostQueryResult from './schema/BreweryPostQueryResult';
const CreateNewBreweryPostWithUserAndLocationSchema = CreateBreweryPostSchema.omit({ const CreateNewBreweryPostWithUserAndLocationSchema = CreateBreweryPostSchema.omit({
address: true, address: true,

View File

@@ -1,5 +1,5 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';

View File

@@ -1,5 +1,5 @@
import DBClient from '@/prisma/DBClient'; import DBClient from '@/prisma/DBClient';
import BreweryPostQueryResult from '@/services/BreweryPost/types/BreweryPostQueryResult'; import BreweryPostQueryResult from '@/services/BreweryPost/schema/BreweryPostQueryResult';
import { z } from 'zod'; import { z } from 'zod';
const prisma = DBClient.instance; const prisma = DBClient.instance;

View File

@@ -0,0 +1,13 @@
import { z } from 'zod';
import CreateBreweryPostSchema from './CreateBreweryPostSchema';
const EditBreweryPostValidationSchema = CreateBreweryPostSchema.extend({
id: z.string().cuid(),
}).omit({
address: true,
city: true,
region: true,
country: true,
});
export default EditBreweryPostValidationSchema;