diff --git a/package-lock.json b/package-lock.json index 8c70b39..70873c5 100644 --- a/package-lock.json +++ b/package-lock.json @@ -38,17 +38,6 @@ "zod": "^3.22.4" }, "devDependencies": { - "@commitlint/cli": "^18.6.1", - "@commitlint/config-conventional": "^16.2.4", - "@tailwindcss/typography": "^0.5.15", - "@types/eslint": "^8.56.12", - "@types/jest": "^29.5.12", - "@types/node": "^18.19.50", - "@types/react": "^18.3.5", - "@types/react-dom": "^18.3.0", - "@typescript-eslint/eslint-plugin": "^6.21.0", - "@typescript-eslint/parser": "^6.21.0", - "autoprefixer": "^10.4.20", "@commitlint/cli": "^18.4.3", "@commitlint/config-conventional": "^16.2.1", "@tailwindcss/typography": "^0.5.9", @@ -64,12 +53,6 @@ "cz-conventional-changelog": "^3.3.0", "dotenv-cli": "^7.4.2", "drizzle-kit": "^0.19.13", - "eslint": "^8.57.1", - "eslint-config-next": "^13.5.6", - "eslint-config-prettier": "^8.10.0", - "eslint-plugin-jsx-a11y": "^6.10.0", - "eslint-plugin-react": "^7.35.2", - "eslint-plugin-react-hooks": "^4.6.2", "eslint": "^8.47.0", "eslint-config-next": "^14.2.1", "eslint-config-prettier": "^8.5.0", @@ -1437,10 +1420,12 @@ } }, "node_modules/@humanwhocodes/object-schema": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", - "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", - "dev": true + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@isaacs/cliui": { "version": "8.0.2", @@ -3574,6 +3559,13 @@ "url": "https://opencollective.com/typescript-eslint" } }, + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true, + "license": "ISC" + }, "node_modules/acorn": { "version": "8.10.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.10.0.tgz", @@ -8378,6 +8370,49 @@ "@pkgjs/parseargs": "^0.11.0" } }, + "node_modules/jake": { + "version": "10.9.2", + "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", + "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "async": "^3.2.3", + "chalk": "^4.0.2", + "filelist": "^1.0.4", + "minimatch": "^3.1.2" + }, + "bin": { + "jake": "bin/cli.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/jake/node_modules/brace-expansion": { + "version": "1.1.11", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", + "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/jake/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", @@ -11579,11 +11614,6 @@ "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", "devOptional": true }, - "node_modules/save-dev": { - "version": "0.0.1-security", - "resolved": "https://registry.npmjs.org/save-dev/-/save-dev-0.0.1-security.tgz", - "integrity": "sha512-k6knZTDNK8PKKbIqnvxiOveJinuw2LcQjqDoaorZWP9M5AR2EPsnpDeSbeoZZ0pHr5ze1uoaKdK8NBGQrJ34Uw==" - }, "node_modules/scheduler": { "version": "0.23.0", "resolved": "https://registry.npmjs.org/scheduler/-/scheduler-0.23.0.tgz", @@ -11612,6 +11642,24 @@ "optional": true, "peer": true }, + "node_modules/set-function-length": { + "version": "1.2.2", + "resolved": "https://registry.npmjs.org/set-function-length/-/set-function-length-1.2.2.tgz", + "integrity": "sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==", + "dev": true, + "license": "MIT", + "dependencies": { + "define-data-property": "^1.1.4", + "es-errors": "^1.3.0", + "function-bind": "^1.1.2", + "get-intrinsic": "^1.2.4", + "gopd": "^1.0.1", + "has-property-descriptors": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/set-function-name": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/set-function-name/-/set-function-name-2.0.2.tgz", diff --git a/src/app/page.tsx b/src/app/page.tsx index ddab1f9..9753151 100644 --- a/src/app/page.tsx +++ b/src/app/page.tsx @@ -33,7 +33,7 @@ const Home = async (props: Params) => { - + ); diff --git a/src/components/InfiniteScrollGrid.tsx b/src/components/InfiniteScrollGrid.tsx new file mode 100644 index 0000000..6883567 --- /dev/null +++ b/src/components/InfiniteScrollGrid.tsx @@ -0,0 +1,78 @@ +'use client'; +import { api } from '@src/trpc/react'; +import { type Session } from 'next-auth'; +import { useEffect, useRef } from 'react'; +import { type SelectClub } from '@src/server/db/models'; +import ClubCard, { ClubCardSkeleton } from './club/ClubCard'; + +type Props = { + session: Session | null; + tag?: string; +}; + +export default function InfiniteScrollGrid({ session, tag }: Props) { + const { data, isLoading, isFetchingNextPage, hasNextPage, fetchNextPage } = + api.club.all.useInfiniteQuery( + { tag, limit: 9 }, + { + getNextPageParam: (lastPage) => + lastPage.clubs.length < 9 ? undefined : lastPage.cursor, + initialCursor: 9, + }, + ); + + const observer = useRef(); + const lastOrgElementRef = useRef(null); + + useEffect(() => { + if (isLoading || isFetchingNextPage) return; + if (observer.current) observer.current.disconnect(); + + observer.current = new IntersectionObserver((entries) => { + if (!entries[0]) return; + if (entries[0].isIntersecting) { + void fetchNextPage(); + } + }); + + if (lastOrgElementRef.current) { + observer.current.observe(lastOrgElementRef.current); + } + + return () => { + if (observer.current) observer.current.disconnect(); + }; + }, [isLoading, isFetchingNextPage, hasNextPage, fetchNextPage]); + + return ( + <> + {data && !isLoading + ? data.pages.map((page: { clubs: SelectClub[] }, index: number) => + page.clubs.map((club, clubIndex) => { + const isLastElement = + index === data.pages.length - 1 && + clubIndex === page.clubs.length - 1; + return ( +
+ +
+ ); + }), + ) + : Array.from({ length: 4 }, (_, index) => ( + + ))} + {isFetchingNextPage && + Array.from({ length: 4 }, (_, index) => ( + + ))} + + ); +} diff --git a/src/components/OrgDirectoryCarousel.tsx b/src/components/OrgDirectoryCarousel.tsx index 434750d..20eae73 100644 --- a/src/components/OrgDirectoryCarousel.tsx +++ b/src/components/OrgDirectoryCarousel.tsx @@ -2,10 +2,10 @@ import { useRef, type FC } from 'react'; import React, { useState } from 'react'; -import DirectoryOrgs from './DirectoryOrgs'; import { type Session } from 'next-auth'; import { type SelectClub } from '@src/server/db/models'; import { LeftArrowIcon, RightArrowIcon } from '@src/icons/Icons'; +import ClubCard from './club/ClubCard'; type Props = { clubs: SelectClub[], @@ -54,7 +54,7 @@ const OrgDirectoryCarousel: FC = ({ clubs, session }) => { > {clubs.map((club) => (
- +
))}