Refactor beer comment section and incorporate image carousel

Beer comment section now uses a ternary expression for isLoading. Image carousel implemented in beer by id page.
This commit is contained in:
Aaron William Po
2023-04-11 20:23:55 -04:00
parent 6c8a510d80
commit ea3e8a0023
7 changed files with 563 additions and 82 deletions

View File

@@ -8,36 +8,23 @@ import { z } from 'zod';
import useBeerPostComments from '@/hooks/useBeerPostComments'; import useBeerPostComments from '@/hooks/useBeerPostComments';
import { useRouter } from 'next/router'; import { useRouter } from 'next/router';
import { useInView } from 'react-intersection-observer'; import { useInView } from 'react-intersection-observer';
import { FaArrowUp } from 'react-icons/fa';
import BeerCommentForm from './BeerCommentForm'; import BeerCommentForm from './BeerCommentForm';
import CommentCardBody from './CommentCardBody'; import CommentCardBody from './CommentCardBody';
import NoCommentsCard from './NoCommentsCard'; import NoCommentsCard from './NoCommentsCard';
import CommentLoadingCardBody from './CommentLoadingCardBody'; import LoadingComponent from './LoadingComponent';
import Spinner from '../ui/Spinner';
interface BeerPostCommentsSectionProps { interface BeerPostCommentsSectionProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof beerPostQueryResult>;
} }
const LoadingComponent: FC<{ length: number }> = ({ length }) => {
return (
<>
{Array.from({ length }).map((_, i) => (
<CommentLoadingCardBody key={i} />
))}
<div className="p-1">
<Spinner size="sm" />
</div>
</>
);
};
const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost }) => { const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost }) => {
const { user } = useContext(UserContext); const { user } = useContext(UserContext);
const router = useRouter(); const router = useRouter();
const { id } = beerPost; const { id } = beerPost;
const pageNum = parseInt(router.query.comments_page as string, 10) || 1; const pageNum = parseInt(router.query.comments_page as string, 10) || 1;
const PAGE_SIZE = 6; const PAGE_SIZE = 4;
const { comments, isLoading, mutate, setSize, size, isLoadingMore, isAtEnd } = const { comments, isLoading, mutate, setSize, size, isLoadingMore, isAtEnd } =
useBeerPostComments({ useBeerPostComments({
@@ -46,8 +33,11 @@ const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost })
pageSize: PAGE_SIZE, pageSize: PAGE_SIZE,
}); });
const { ref } = useInView({ const { ref: lastCommentRef } = useInView({
delay: 3000, /**
* When the last comment comes into view, call setSize from useBeerPostComments to
* load more comments.
*/
onChange: (visible) => { onChange: (visible) => {
if (!visible || isAtEnd) return; if (!visible || isAtEnd) return;
setSize(size + 1); setSize(size + 1);
@@ -58,7 +48,7 @@ const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost })
return ( return (
<div className="w-full space-y-3 md:w-[60%]"> <div className="w-full space-y-3 md:w-[60%]">
<div className="card h-96 bg-base-300"> <div className="card h-96 bg-base-300">
<div className="card-body h-full"> <div className="card-body h-full" ref={sectionRef}>
{user ? ( {user ? (
<BeerCommentForm beerPost={beerPost} mutate={mutate} /> <BeerCommentForm beerPost={beerPost} mutate={mutate} />
) : ( ) : (
@@ -69,47 +59,80 @@ const BeerPostCommentsSection: FC<BeerPostCommentsSectionProps> = ({ beerPost })
</div> </div>
</div> </div>
{comments && !!comments.length && !isLoading && ( {
<div className="card bg-base-300 pb-6" ref={sectionRef}> /**
{comments.map((comment, index) => { * If the comments are loading, show a loading component. Otherwise, show the
const isLastComment = index === comments.length - 1; * comments.
*/
isLoading ? (
<div className="card bg-base-300 pb-6">
<LoadingComponent length={PAGE_SIZE} />
</div>
) : (
<>
{!!comments.length && (
<div className="card bg-base-300 pb-6">
{comments.map((comment, index) => {
const isLastComment = index === comments.length - 1;
return ( /**
<div ref={isLastComment ? ref : undefined} key={comment.id}> * Attach a ref to the last comment in the list. When it comes into
<CommentCardBody comment={comment} mutate={mutate} /> * view, the component will call setSize to load more comments.
*/
return (
<div
ref={isLastComment ? lastCommentRef : undefined}
key={comment.id}
>
<CommentCardBody comment={comment} mutate={mutate} />
</div>
);
})}
{
/**
* If there are more comments to load, show a loading component with a
* skeleton loader and a loading spinner.
*/
!!isLoadingMore && (
<LoadingComponent length={Math.floor(PAGE_SIZE / 2)} />
)
}
{
/**
* If the user has scrolled to the end of the comments, show a button
* that will scroll them back to the top of the comments section.
*/
!!isAtEnd && (
<div className="flex h-20 items-center justify-center text-center">
<div
className="tooltip tooltip-bottom"
data-tip="Scroll back to top of comments."
>
<button
type="button"
className="btn-ghost btn-sm btn"
aria-label="Scroll back to top of comments"
onClick={() => {
sectionRef.current?.scrollIntoView({
behavior: 'smooth',
});
}}
>
<FaArrowUp />
</button>
</div>
</div>
)
}
</div> </div>
); )}
})}
{!!isLoadingMore && ( {!comments.length && <NoCommentsCard />}
<div> </>
<LoadingComponent length={Math.floor(PAGE_SIZE / 2)} /> )
</div> }
)}
{isAtEnd && (
<div className="flex h-10 items-center justify-center text-center">
<button
className="btn-ghost btn-sm btn"
type="button"
onClick={() => {
sectionRef.current!.scrollIntoView({ behavior: 'smooth' });
}}
>
Scroll to top of comments
</button>
</div>
)}
</div>
)}
{!comments?.length && !isLoading && <NoCommentsCard />}
{isLoading && (
<div className="card bg-base-300 pb-6">
<LoadingComponent length={PAGE_SIZE} />
</div>
)}
</div> </div>
); );
}; };

View File

@@ -0,0 +1,22 @@
import { FC } from 'react';
import Spinner from '../ui/Spinner';
import CommentLoadingCardBody from './CommentLoadingCardBody';
interface LoadingComponentProps {
length: number;
}
const LoadingComponent: FC<LoadingComponentProps> = ({ length }) => {
return (
<>
{Array.from({ length }).map((_, i) => (
<CommentLoadingCardBody key={i} />
))}
<div className="p-1">
<Spinner size="sm" />
</div>
</>
);
};
export default LoadingComponent;

View File

@@ -7,17 +7,22 @@ interface SpinnerProps {
const Spinner: FC<SpinnerProps> = ({ size = 'md' }) => { const Spinner: FC<SpinnerProps> = ({ size = 'md' }) => {
const spinnerWidths: Record<NonNullable<SpinnerProps['size']>, `w-[${number}px]`> = { const spinnerWidths: Record<NonNullable<SpinnerProps['size']>, `w-[${number}px]`> = {
xs: 'w-[45px]', xs: 'w-[45px]',
sm: 'w-[60px]', sm: 'w-[90px]',
md: 'w-[100px]', md: 'w-[135px]',
lg: 'w-[150px]', lg: 'w-[180px]',
xl: 'w-[200px]', xl: 'w-[225px]',
}; };
return ( return (
<div role="status" className="flex flex-col items-center justify-center rounded-3xl"> <div
role="alert"
aria-busy="true"
aria-live="polite"
className="flex flex-col items-center justify-center rounded-3xl text-primary"
>
<svg <svg
aria-hidden="true" aria-hidden="true"
className={`${spinnerWidths[size]} animate-spin fill-secondary text-primary`} className={`${spinnerWidths[size]} animate-spin fill-base-content`}
viewBox="0 0 100 101" viewBox="0 0 100 101"
fill="none" fill="none"
xmlns="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg"

419
package-lock.json generated
View File

@@ -36,6 +36,7 @@
"react-hook-form": "^7.43.9", "react-hook-form": "^7.43.9",
"react-icons": "^4.8.0", "react-icons": "^4.8.0",
"react-intersection-observer": "^9.4.3", "react-intersection-observer": "^9.4.3",
"react-responsive-carousel": "^3.2.23",
"sparkpost": "^2.1.4", "sparkpost": "^2.1.4",
"swr": "^2.1.2", "swr": "^2.1.2",
"zod": "^3.21.4" "zod": "^3.21.4"
@@ -52,6 +53,7 @@
"@types/react": "^18.0.33", "@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@types/sparkpost": "^2.1.5", "@types/sparkpost": "^2.1.5",
"@vercel/fetch": "^6.2.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"daisyui": "^2.51.5", "daisyui": "^2.51.5",
"dotenv-cli": "^7.1.0", "dotenv-cli": "^7.1.0",
@@ -1558,6 +1560,15 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"devOptional": true "devOptional": true
}, },
"node_modules/@types/async-retry": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.4.5.tgz",
"integrity": "sha512-YrdjSD+yQv7h6d5Ip+PMxh3H6ZxKyQk0Ts+PvaNRInxneG9PFVZjFg77ILAN+N6qYf7g4giSJ1l+ZjQ1zeegvA==",
"dev": true,
"dependencies": {
"@types/retry": "*"
}
},
"node_modules/@types/body-parser": { "node_modules/@types/body-parser": {
"version": "1.19.2", "version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@@ -1655,6 +1666,12 @@
"integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==", "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==",
"dev": true "dev": true
}, },
"node_modules/@types/lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw==",
"dev": true
},
"node_modules/@types/mdast": { "node_modules/@types/mdast": {
"version": "3.0.11", "version": "3.0.11",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz",
@@ -1691,6 +1708,30 @@
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"devOptional": true "devOptional": true
}, },
"node_modules/@types/node-fetch": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz",
"integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==",
"dev": true,
"dependencies": {
"@types/node": "*",
"form-data": "^3.0.0"
}
},
"node_modules/@types/node-fetch/node_modules/form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"dependencies": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/@types/normalize-package-data": { "node_modules/@types/normalize-package-data": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
@@ -1776,6 +1817,12 @@
"form-data": "^2.5.0" "form-data": "^2.5.0"
} }
}, },
"node_modules/@types/retry": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
"integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
"dev": true
},
"node_modules/@types/scheduler": { "node_modules/@types/scheduler": {
"version": "0.16.3", "version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@@ -2012,6 +2059,107 @@
"url": "https://opencollective.com/typescript-eslint" "url": "https://opencollective.com/typescript-eslint"
} }
}, },
"node_modules/@vercel/fetch": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@vercel/fetch/-/fetch-6.2.0.tgz",
"integrity": "sha512-MU+Mzh06NIAXxwdnyHmBFg+/lTKBbzDkCSNhAwWTFJ4rHuBc4pHc8E6XP+qnwqaWugjOBQgFfQCGDLnV820c9A==",
"dev": true,
"dependencies": {
"@types/async-retry": "^1.4.3",
"@vercel/fetch-cached-dns": "^2.0.2",
"@vercel/fetch-retry": "^5.0.3",
"agentkeepalive": "^4.2.1",
"debug": "^4.3.3"
},
"peerDependencies": {
"@types/node-fetch": "^2.6.1",
"node-fetch": "^2.6.7"
}
},
"node_modules/@vercel/fetch-cached-dns": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@vercel/fetch-cached-dns/-/fetch-cached-dns-2.1.2.tgz",
"integrity": "sha512-8JHBmYuXYGlyRupRgnyRML84NL1SBi3lVedYhPaWb+6HwMsYF5/PXfQ9M9GPrXsZ8bqKpLQH9tQcVpZmAUJzeQ==",
"dev": true,
"dependencies": {
"@types/node-fetch": "^2.6.1",
"@zeit/dns-cached-resolve": "^2.1.2"
},
"peerDependencies": {
"node-fetch": "^2.6.1"
}
},
"node_modules/@vercel/fetch-retry": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@vercel/fetch-retry/-/fetch-retry-5.1.3.tgz",
"integrity": "sha512-UIbFc4VsEZHOr6dWuE+kxY4NxnOLXFMCWm0fSKRRHUEtrIzaJLzHpWk2QskCXTSzFgFvhkLAvSrBK2XZg7NSzg==",
"dev": true,
"dependencies": {
"async-retry": "^1.3.3",
"debug": "^4.3.3"
},
"peerDependencies": {
"node-fetch": "^2.6.7"
}
},
"node_modules/@zeit/dns-cached-resolve": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.2.tgz",
"integrity": "sha512-A/5gbBskKPETTBqHwvlaW1Ri2orO62yqoFoXdxna1SQ7A/lXjpWgpJ1wdY3IQEcz5LydpS4sJ8SzI2gFyyLEhg==",
"dev": true,
"dependencies": {
"@types/async-retry": "1.2.1",
"@types/lru-cache": "4.1.1",
"@types/node": "10.12.18",
"async-retry": "1.2.3",
"lru-cache": "5.1.1"
}
},
"node_modules/@zeit/dns-cached-resolve/node_modules/@types/async-retry": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.2.1.tgz",
"integrity": "sha512-yMQ6CVgICWtyFNBqJT3zqOc+TnqqEPLo4nKJNPFwcialiylil38Ie6q1ENeFTjvaLOkVim9K5LisHgAKJWidGQ==",
"dev": true
},
"node_modules/@zeit/dns-cached-resolve/node_modules/@types/node": {
"version": "10.12.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
"dev": true
},
"node_modules/@zeit/dns-cached-resolve/node_modules/async-retry": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz",
"integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==",
"dev": true,
"dependencies": {
"retry": "0.12.0"
}
},
"node_modules/@zeit/dns-cached-resolve/node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"dependencies": {
"yallist": "^3.0.2"
}
},
"node_modules/@zeit/dns-cached-resolve/node_modules/retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true,
"engines": {
"node": ">= 4"
}
},
"node_modules/@zeit/dns-cached-resolve/node_modules/yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
},
"node_modules/abbrev": { "node_modules/abbrev": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -2098,6 +2246,20 @@
"node": ">= 6.0.0" "node": ">= 6.0.0"
} }
}, },
"node_modules/agentkeepalive": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz",
"integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==",
"dev": true,
"dependencies": {
"debug": "^4.1.0",
"depd": "^2.0.0",
"humanize-ms": "^1.2.1"
},
"engines": {
"node": ">= 8.0.0"
}
},
"node_modules/ajv": { "node_modules/ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -2334,6 +2496,15 @@
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"dev": true "dev": true
}, },
"node_modules/async-retry": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
"integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
"dev": true,
"dependencies": {
"retry": "0.13.1"
}
},
"node_modules/asynckit": { "node_modules/asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -2753,6 +2924,11 @@
"node": ">=10" "node": ">=10"
} }
}, },
"node_modules/classnames": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
"node_modules/cli-cursor": { "node_modules/cli-cursor": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -3206,7 +3382,7 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"optional": true, "devOptional": true,
"engines": { "engines": {
"node": ">= 0.8" "node": ">= 0.8"
} }
@@ -5258,6 +5434,15 @@
"node": ">=10.17.0" "node": ">=10.17.0"
} }
}, },
"node_modules/humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"dev": true,
"dependencies": {
"ms": "^2.0.0"
}
},
"node_modules/iconv-lite": { "node_modules/iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -8013,7 +8198,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"dependencies": { "dependencies": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -8205,6 +8389,17 @@
"react": "^18.2.0" "react": "^18.2.0"
} }
}, },
"node_modules/react-easy-swipe": {
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/react-easy-swipe/-/react-easy-swipe-0.0.21.tgz",
"integrity": "sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg==",
"dependencies": {
"prop-types": "^15.5.8"
},
"engines": {
"node": ">= 6"
}
},
"node_modules/react-email": { "node_modules/react-email": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/react-email/-/react-email-1.9.0.tgz", "resolved": "https://registry.npmjs.org/react-email/-/react-email-1.9.0.tgz",
@@ -8353,14 +8548,23 @@
"node_modules/react-is": { "node_modules/react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"dev": true
}, },
"node_modules/react-property": { "node_modules/react-property": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz",
"integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==" "integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw=="
}, },
"node_modules/react-responsive-carousel": {
"version": "3.2.23",
"resolved": "https://registry.npmjs.org/react-responsive-carousel/-/react-responsive-carousel-3.2.23.tgz",
"integrity": "sha512-pqJLsBaKHWJhw/ItODgbVoziR2z4lpcJg+YwmRlSk4rKH32VE633mAtZZ9kDXjy4wFO+pgUZmDKPsPe1fPmHCg==",
"dependencies": {
"classnames": "^2.2.5",
"prop-types": "^15.5.8",
"react-easy-swipe": "^0.0.21"
}
},
"node_modules/read-cache": { "node_modules/read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -8602,6 +8806,15 @@
"node": ">=8" "node": ">=8"
} }
}, },
"node_modules/retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"dev": true,
"engines": {
"node": ">= 4"
}
},
"node_modules/reusify": { "node_modules/reusify": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",
@@ -11109,6 +11322,15 @@
"integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==", "integrity": "sha512-yOlFc+7UtL/89t2ZhjPvvB/DeAr3r+Dq58IgzsFkOAvVC6NMJXmCGjbptdXdR9qsX7pKcTL+s87FtYREi2dEEQ==",
"devOptional": true "devOptional": true
}, },
"@types/async-retry": {
"version": "1.4.5",
"resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.4.5.tgz",
"integrity": "sha512-YrdjSD+yQv7h6d5Ip+PMxh3H6ZxKyQk0Ts+PvaNRInxneG9PFVZjFg77ILAN+N6qYf7g4giSJ1l+ZjQ1zeegvA==",
"dev": true,
"requires": {
"@types/retry": "*"
}
},
"@types/body-parser": { "@types/body-parser": {
"version": "1.19.2", "version": "1.19.2",
"resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz", "resolved": "https://registry.npmjs.org/@types/body-parser/-/body-parser-1.19.2.tgz",
@@ -11206,6 +11428,12 @@
"integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==", "integrity": "sha512-km+Vyn3BYm5ytMO13k9KTp27O75rbQ0NFw+U//g+PX7VZyjCioXaRFisqSIJRECljcTv73G3i6BpglNGHgUQ5A==",
"dev": true "dev": true
}, },
"@types/lru-cache": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@types/lru-cache/-/lru-cache-4.1.1.tgz",
"integrity": "sha512-8mNEUG6diOrI6pMqOHrHPDBB1JsrpedeMK9AWGzVCQ7StRRribiT9BRvUmF8aUws9iBbVlgVekOT5Sgzc1MTKw==",
"dev": true
},
"@types/mdast": { "@types/mdast": {
"version": "3.0.11", "version": "3.0.11",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz", "resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-3.0.11.tgz",
@@ -11242,6 +11470,29 @@
"integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==", "integrity": "sha512-E5Kwq2n4SbMzQOn6wnmBjuK9ouqlURrcZDVfbo9ftDDTFt3nk7ZKK4GMOzoYgnpQJKcxwQw+lGaBvvlMo0qN/Q==",
"devOptional": true "devOptional": true
}, },
"@types/node-fetch": {
"version": "2.6.3",
"resolved": "https://registry.npmjs.org/@types/node-fetch/-/node-fetch-2.6.3.tgz",
"integrity": "sha512-ETTL1mOEdq/sxUtgtOhKjyB2Irra4cjxksvcMUR5Zr4n+PxVhsCD9WS46oPbHL3et9Zde7CNRr+WUNlcHvsX+w==",
"dev": true,
"requires": {
"@types/node": "*",
"form-data": "^3.0.0"
},
"dependencies": {
"form-data": {
"version": "3.0.1",
"resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz",
"integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==",
"dev": true,
"requires": {
"asynckit": "^0.4.0",
"combined-stream": "^1.0.8",
"mime-types": "^2.1.12"
}
}
}
},
"@types/normalize-package-data": { "@types/normalize-package-data": {
"version": "2.4.1", "version": "2.4.1",
"resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz", "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.1.tgz",
@@ -11327,6 +11578,12 @@
"form-data": "^2.5.0" "form-data": "^2.5.0"
} }
}, },
"@types/retry": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
"integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow==",
"dev": true
},
"@types/scheduler": { "@types/scheduler": {
"version": "0.16.3", "version": "0.16.3",
"resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz", "resolved": "https://registry.npmjs.org/@types/scheduler/-/scheduler-0.16.3.tgz",
@@ -11474,6 +11731,96 @@
"eslint-visitor-keys": "^3.3.0" "eslint-visitor-keys": "^3.3.0"
} }
}, },
"@vercel/fetch": {
"version": "6.2.0",
"resolved": "https://registry.npmjs.org/@vercel/fetch/-/fetch-6.2.0.tgz",
"integrity": "sha512-MU+Mzh06NIAXxwdnyHmBFg+/lTKBbzDkCSNhAwWTFJ4rHuBc4pHc8E6XP+qnwqaWugjOBQgFfQCGDLnV820c9A==",
"dev": true,
"requires": {
"@types/async-retry": "^1.4.3",
"@vercel/fetch-cached-dns": "^2.0.2",
"@vercel/fetch-retry": "^5.0.3",
"agentkeepalive": "^4.2.1",
"debug": "^4.3.3"
}
},
"@vercel/fetch-cached-dns": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@vercel/fetch-cached-dns/-/fetch-cached-dns-2.1.2.tgz",
"integrity": "sha512-8JHBmYuXYGlyRupRgnyRML84NL1SBi3lVedYhPaWb+6HwMsYF5/PXfQ9M9GPrXsZ8bqKpLQH9tQcVpZmAUJzeQ==",
"dev": true,
"requires": {
"@types/node-fetch": "^2.6.1",
"@zeit/dns-cached-resolve": "^2.1.2"
}
},
"@vercel/fetch-retry": {
"version": "5.1.3",
"resolved": "https://registry.npmjs.org/@vercel/fetch-retry/-/fetch-retry-5.1.3.tgz",
"integrity": "sha512-UIbFc4VsEZHOr6dWuE+kxY4NxnOLXFMCWm0fSKRRHUEtrIzaJLzHpWk2QskCXTSzFgFvhkLAvSrBK2XZg7NSzg==",
"dev": true,
"requires": {
"async-retry": "^1.3.3",
"debug": "^4.3.3"
}
},
"@zeit/dns-cached-resolve": {
"version": "2.1.2",
"resolved": "https://registry.npmjs.org/@zeit/dns-cached-resolve/-/dns-cached-resolve-2.1.2.tgz",
"integrity": "sha512-A/5gbBskKPETTBqHwvlaW1Ri2orO62yqoFoXdxna1SQ7A/lXjpWgpJ1wdY3IQEcz5LydpS4sJ8SzI2gFyyLEhg==",
"dev": true,
"requires": {
"@types/async-retry": "1.2.1",
"@types/lru-cache": "4.1.1",
"@types/node": "10.12.18",
"async-retry": "1.2.3",
"lru-cache": "5.1.1"
},
"dependencies": {
"@types/async-retry": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@types/async-retry/-/async-retry-1.2.1.tgz",
"integrity": "sha512-yMQ6CVgICWtyFNBqJT3zqOc+TnqqEPLo4nKJNPFwcialiylil38Ie6q1ENeFTjvaLOkVim9K5LisHgAKJWidGQ==",
"dev": true
},
"@types/node": {
"version": "10.12.18",
"resolved": "https://registry.npmjs.org/@types/node/-/node-10.12.18.tgz",
"integrity": "sha512-fh+pAqt4xRzPfqA6eh3Z2y6fyZavRIumvjhaCL753+TVkGKGhpPeyrJG2JftD0T9q4GF00KjefsQ+PQNDdWQaQ==",
"dev": true
},
"async-retry": {
"version": "1.2.3",
"resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.2.3.tgz",
"integrity": "sha512-tfDb02Th6CE6pJUF2gjW5ZVjsgwlucVXOEQMvEX9JgSJMs9gAX+Nz3xRuJBKuUYjTSYORqvDBORdAQ3LU59g7Q==",
"dev": true,
"requires": {
"retry": "0.12.0"
}
},
"lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
"integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==",
"dev": true,
"requires": {
"yallist": "^3.0.2"
}
},
"retry": {
"version": "0.12.0",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.12.0.tgz",
"integrity": "sha512-9LkiTwjUh6rT555DtE9rTX+BKByPfrMzEAtnlEtdEwr3Nkffwiihqe2bWADg+OQRjt9gl6ICdmB/ZFDCGAtSow==",
"dev": true
},
"yallist": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz",
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
"dev": true
}
}
},
"abbrev": { "abbrev": {
"version": "1.1.1", "version": "1.1.1",
"resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz", "resolved": "https://registry.npmjs.org/abbrev/-/abbrev-1.1.1.tgz",
@@ -11536,6 +11883,17 @@
"debug": "4" "debug": "4"
} }
}, },
"agentkeepalive": {
"version": "4.3.0",
"resolved": "https://registry.npmjs.org/agentkeepalive/-/agentkeepalive-4.3.0.tgz",
"integrity": "sha512-7Epl1Blf4Sy37j4v9f9FjICCh4+KAQOyXgHEwlyBiAQLbhKdq/i2QQU3amQalS/wPhdPzDXPL5DMR5bkn+YeWg==",
"dev": true,
"requires": {
"debug": "^4.1.0",
"depd": "^2.0.0",
"humanize-ms": "^1.2.1"
}
},
"ajv": { "ajv": {
"version": "6.12.6", "version": "6.12.6",
"resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz",
@@ -11716,6 +12074,15 @@
"integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==", "integrity": "sha512-eBvWn1lvIApYMhzQMsu9ciLfkBY499mFZlNqG+/9WR7PVlroQw0vG30cOQQbaKz3sCEc44TAOu2ykzqXSNnwag==",
"dev": true "dev": true
}, },
"async-retry": {
"version": "1.3.3",
"resolved": "https://registry.npmjs.org/async-retry/-/async-retry-1.3.3.tgz",
"integrity": "sha512-wfr/jstw9xNi/0teMHrRW7dsz3Lt5ARhYNZ2ewpadnhaIp5mbALhOAP+EAdsC7t4Z6wqsDVv9+W6gm1Dk9mEyw==",
"dev": true,
"requires": {
"retry": "0.13.1"
}
},
"asynckit": { "asynckit": {
"version": "0.4.0", "version": "0.4.0",
"resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz",
@@ -11982,6 +12349,11 @@
"resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz", "resolved": "https://registry.npmjs.org/chownr/-/chownr-2.0.0.tgz",
"integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==" "integrity": "sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ=="
}, },
"classnames": {
"version": "2.3.2",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.3.2.tgz",
"integrity": "sha512-CSbhY4cFEJRe6/GQzIk5qXZ4Jeg5pcsP7b5peFSDpffpe1cqjASH/n9UTjBwOp6XpMSTwQ8Za2K5V02ueA7Tmw=="
},
"cli-cursor": { "cli-cursor": {
"version": "3.1.0", "version": "3.1.0",
"resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz", "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-3.1.0.tgz",
@@ -12318,7 +12690,7 @@
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz", "resolved": "https://registry.npmjs.org/depd/-/depd-2.0.0.tgz",
"integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==", "integrity": "sha512-g7nH6P6dyDioJogAAGprGpCtVImJhpPk/roCzdb3fIh61/s/nPsfR6onyMwkCAR/OlC3yBC0lESvUoQEAssIrw==",
"optional": true "devOptional": true
}, },
"deprecation": { "deprecation": {
"version": "2.3.1", "version": "2.3.1",
@@ -13878,6 +14250,15 @@
"resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz",
"integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==" "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw=="
}, },
"humanize-ms": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/humanize-ms/-/humanize-ms-1.2.1.tgz",
"integrity": "sha512-Fl70vYtsAFb/C06PTS9dZBo7ihau+Tu/DNCk/OyHhea07S+aeMWpFFkUaXRa8fI+ScZbEI8dfSxwY7gxZ9SAVQ==",
"dev": true,
"requires": {
"ms": "^2.0.0"
}
},
"iconv-lite": { "iconv-lite": {
"version": "0.4.24", "version": "0.4.24",
"resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz", "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.4.24.tgz",
@@ -15666,7 +16047,6 @@
"version": "15.8.1", "version": "15.8.1",
"resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz", "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.8.1.tgz",
"integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==", "integrity": "sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg==",
"dev": true,
"requires": { "requires": {
"loose-envify": "^1.4.0", "loose-envify": "^1.4.0",
"object-assign": "^4.1.1", "object-assign": "^4.1.1",
@@ -15810,6 +16190,14 @@
"scheduler": "^0.23.0" "scheduler": "^0.23.0"
} }
}, },
"react-easy-swipe": {
"version": "0.0.21",
"resolved": "https://registry.npmjs.org/react-easy-swipe/-/react-easy-swipe-0.0.21.tgz",
"integrity": "sha512-OeR2jAxdoqUMHIn/nS9fgreI5hSpgGoL5ezdal4+oO7YSSgJR8ga+PkYGJrSrJ9MKlPcQjMQXnketrD7WNmNsg==",
"requires": {
"prop-types": "^15.5.8"
}
},
"react-email": { "react-email": {
"version": "1.9.0", "version": "1.9.0",
"resolved": "https://registry.npmjs.org/react-email/-/react-email-1.9.0.tgz", "resolved": "https://registry.npmjs.org/react-email/-/react-email-1.9.0.tgz",
@@ -15919,14 +16307,23 @@
"react-is": { "react-is": {
"version": "16.13.1", "version": "16.13.1",
"resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz",
"integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==", "integrity": "sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ=="
"dev": true
}, },
"react-property": { "react-property": {
"version": "2.0.0", "version": "2.0.0",
"resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz", "resolved": "https://registry.npmjs.org/react-property/-/react-property-2.0.0.tgz",
"integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw==" "integrity": "sha512-kzmNjIgU32mO4mmH5+iUyrqlpFQhF8K2k7eZ4fdLSOPFrD1XgEuSBv9LDEgxRXTMBqMd8ppT0x6TIzqE5pdGdw=="
}, },
"react-responsive-carousel": {
"version": "3.2.23",
"resolved": "https://registry.npmjs.org/react-responsive-carousel/-/react-responsive-carousel-3.2.23.tgz",
"integrity": "sha512-pqJLsBaKHWJhw/ItODgbVoziR2z4lpcJg+YwmRlSk4rKH32VE633mAtZZ9kDXjy4wFO+pgUZmDKPsPe1fPmHCg==",
"requires": {
"classnames": "^2.2.5",
"prop-types": "^15.5.8",
"react-easy-swipe": "^0.0.21"
}
},
"read-cache": { "read-cache": {
"version": "1.0.0", "version": "1.0.0",
"resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz", "resolved": "https://registry.npmjs.org/read-cache/-/read-cache-1.0.0.tgz",
@@ -16114,6 +16511,12 @@
"signal-exit": "^3.0.2" "signal-exit": "^3.0.2"
} }
}, },
"retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"dev": true
},
"reusify": { "reusify": {
"version": "1.0.4", "version": "1.0.4",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz",

View File

@@ -39,6 +39,7 @@
"react-hook-form": "^7.43.9", "react-hook-form": "^7.43.9",
"react-icons": "^4.8.0", "react-icons": "^4.8.0",
"react-intersection-observer": "^9.4.3", "react-intersection-observer": "^9.4.3",
"react-responsive-carousel": "^3.2.23",
"sparkpost": "^2.1.4", "sparkpost": "^2.1.4",
"swr": "^2.1.2", "swr": "^2.1.2",
"zod": "^3.21.4" "zod": "^3.21.4"
@@ -55,6 +56,7 @@
"@types/react": "^18.0.33", "@types/react": "^18.0.33",
"@types/react-dom": "^18.0.11", "@types/react-dom": "^18.0.11",
"@types/sparkpost": "^2.1.5", "@types/sparkpost": "^2.1.5",
"@vercel/fetch": "^6.2.0",
"autoprefixer": "^10.4.14", "autoprefixer": "^10.4.14",
"daisyui": "^2.51.5", "daisyui": "^2.51.5",
"dotenv-cli": "^7.1.0", "dotenv-cli": "^7.1.0",

View File

@@ -15,6 +15,9 @@ import { BeerPost } from '@prisma/client';
import { z } from 'zod'; import { z } from 'zod';
import 'react-responsive-carousel/lib/styles/carousel.min.css'; // requires a loader
import { Carousel } from 'react-responsive-carousel';
interface BeerPageProps { interface BeerPageProps {
beerPost: z.infer<typeof beerPostQueryResult>; beerPost: z.infer<typeof beerPostQueryResult>;
beerRecommendations: (BeerPost & { beerRecommendations: (BeerPost & {
@@ -32,17 +35,28 @@ const BeerByIdPage: NextPage<BeerPageProps> = ({ beerPost, beerRecommendations }
</Head> </Head>
<Layout> <Layout>
<div> <div>
{beerPost.beerImages[0] && ( <Carousel
<Image className="w-full"
alt={beerPost.beerImages[0].alt} useKeyboardArrows
src={beerPost.beerImages[0].path} autoPlay
height={1080} interval={10000}
width={1920} infiniteLoop
className="h-[42rem] w-full object-cover" showThumbs={false}
/> >
)} {beerPost.beerImages.map((image, index) => (
<div key={image.id} id={`image-${index}}`} className="w-full">
<Image
alt={image.alt}
src={image.path}
height={1080}
width={1920}
className="h-[42rem] w-full object-cover"
/>
</div>
))}
</Carousel>
<div className="my-12 flex w-full items-center justify-center "> <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"> <div className="w-11/12 space-y-3 xl:w-9/12">
<BeerInfoHeader beerPost={beerPost} /> <BeerInfoHeader beerPost={beerPost} />
<div className="mt-4 flex flex-col space-y-3 md:flex-row md:space-x-3 md:space-y-0"> <div className="mt-4 flex flex-col space-y-3 md:flex-row md:space-x-3 md:space-y-0">

View File

@@ -9,6 +9,9 @@ import BeerCard from '@/components/BeerIndex/BeerCard';
import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult'; import beerPostQueryResult from '@/services/BeerPost/schema/BeerPostQueryResult';
import Head from 'next/head'; import Head from 'next/head';
import { z } from 'zod'; import { z } from 'zod';
import Link from 'next/link';
import UserContext from '@/contexts/userContext';
import { useContext } from 'react';
interface BeerPageProps { interface BeerPageProps {
initialBeerPosts: z.infer<typeof beerPostQueryResult>[]; initialBeerPosts: z.infer<typeof beerPostQueryResult>[];
@@ -19,6 +22,8 @@ const BeerPage: NextPage<BeerPageProps> = ({ initialBeerPosts, pageCount }) => {
const router = useRouter(); const router = useRouter();
const { query } = router; const { query } = router;
const { user } = useContext(UserContext);
const pageNum = parseInt(query.page_num as string, 10) || 1; const pageNum = parseInt(query.page_num as string, 10) || 1;
return ( return (
<Layout> <Layout>
@@ -28,13 +33,20 @@ const BeerPage: NextPage<BeerPageProps> = ({ initialBeerPosts, pageCount }) => {
</Head> </Head>
<div className="flex items-center justify-center bg-base-100"> <div className="flex items-center justify-center bg-base-100">
<div className="my-10 flex w-10/12 flex-col space-y-4"> <div className="my-10 flex w-10/12 flex-col space-y-4">
<header className="my-10"> <header className="my-10 flex justify-between">
<div className="space-y-2"> <div className="space-y-2">
<h1 className="text-6xl font-bold">The Biergarten Index</h1> <h1 className="text-6xl font-bold">The Biergarten Index</h1>
<h2 className="text-2xl font-bold"> <h2 className="text-2xl font-bold">
Page {pageNum} of {pageCount} Page {pageNum} of {pageCount}
</h2> </h2>
</div> </div>
{!!user && (
<div>
<Link href="/beers/create" className="btn-primary btn">
Create a new beer post
</Link>
</div>
)}
</header> </header>
<div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3"> <div className="grid gap-5 md:grid-cols-2 xl:grid-cols-3">
{initialBeerPosts.map((post) => { {initialBeerPosts.map((post) => {