diff --git a/citrus/app/(users)/connect-with-people/page.tsx b/citrus/app/(users)/connect-with-people/page.tsx new file mode 100644 index 0000000..79dc896 --- /dev/null +++ b/citrus/app/(users)/connect-with-people/page.tsx @@ -0,0 +1,5 @@ +import ConnectWithPeople from '@/components/ConnectWithPeople'; + +export default function Page() { + return ; +} diff --git a/citrus/app/api/users/route.ts b/citrus/app/api/users/route.ts index 15688de..3b3428e 100644 --- a/citrus/app/api/users/route.ts +++ b/citrus/app/api/users/route.ts @@ -2,6 +2,8 @@ import * as db from '@/lib/db' import { NextResponse } from 'next/server'; import '@/lib/patch' +import type { users } from '@prisma/client'; + const prisma = db.getClient(); const bcrypt = require('bcrypt'); @@ -22,6 +24,7 @@ schema * * @apiParam {String} [next_cursor] The cursor to use to get the next page of results * @apiParam {String} [prev_cursor] The cursor to use to get the previous page of results + * @apiParam {String} [search] The search query to use to filter results * @apiParam {Number} [limit=10] The maximum number of results to return * * @apiSuccess {String} next_cursor The cursor to use to get the next page of results @@ -65,15 +68,42 @@ schema const { searchParams } = new URL(request.url); const next_id = searchParams.get('next_cursor'); const prev_id = searchParams.get('prev_cursor'); + const search = searchParams.get('search'); const limit = Number(searchParams.get('limit')) || 10; - let where_clause: any = { - username: { - gt: next_id != null ? next_id : undefined, - lt: prev_id != null ? prev_id : undefined + let where_clause: any = {}; + + if (search) { + where_clause = { + ...where_clause, + username: { + contains: search + } + }; + } + + if (next_id && prev_id) { + return NextResponse.json({ error: "You must provide either a next_cursor or a prev_cursor, but not both" }, { status: 400 }); + } + + var cursor = undefined; + var take = limit; + var skip = 1; + if (next_id) { + cursor = { + username: next_id } - }; + } else if (prev_id) { + cursor = { + username: prev_id + } + take = -limit; + } else { + skip = 0; + } + var users: users[] = []; + let select = { username: true, email: true, @@ -83,32 +113,36 @@ schema experiences: true }; - if (next_id && prev_id) { - // If both cursors are provided, indicate an error - return NextResponse.json( - { error: "You must provide either a next_cursor or a prev_cursor, but not both" }, - { status: 400 }); + try { + users = await prisma.users.findMany({ + where: where_clause, + take: take, + select: select, + cursor: cursor, + skip: skip, + orderBy: { + username: 'asc' + }, + }); + + } + catch (e) { + return db.handleError(e); } - // Retrieve the users from the database - const res = await prisma.users.findMany({ - where: where_clause, - take: limit, - orderBy: { - username: 'desc' - }, - select: select - }); - const users = res; - // Set up the new cursors to return - const next_cursor = users.length != 0 ? users[users.length - 1].username : null; - const prev_cursor = users.length != 0 ? users[0].username : null; + var next_cursor = null; + var prev_cursor = null; + + if (users.length > 0) { + next_cursor = users[users.length - 1].username; + prev_cursor = users[0].username; + } return NextResponse.json({ - "next_cursor": next_cursor, - "prev_cursor": prev_cursor, - "limit": limit, - users + next_cursor: next_cursor, + prev_cursor: prev_cursor, + limit: limit, + users, }); } diff --git a/citrus/components/ConnectWithPeople.tsx b/citrus/components/ConnectWithPeople.tsx new file mode 100644 index 0000000..420daa8 --- /dev/null +++ b/citrus/components/ConnectWithPeople.tsx @@ -0,0 +1,111 @@ +"use client"; +import React, { useState, useEffect, useRef } from 'react'; +import { ReadonlyURLSearchParams, usePathname, useSearchParams } from 'next/navigation'; +import type { users } from '@prisma/client'; +import { useSession } from 'next-auth/react'; +import InfiniteScroll from 'react-infinite-scroll-component'; +import "react-datepicker/dist/react-datepicker.css"; +import "react-datepicker/dist/react-datepicker-cssmodules.css"; + +function buildAPISearchParams(searchParams: ReadonlyURLSearchParams, basePathName: string) { + var apiPathName = basePathName + const params = new URLSearchParams(Array.from(searchParams.entries())); + if (params.size > 0 && !apiPathName.includes('?')) { + apiPathName += '?' + params.toString(); + } else if (params.size > 0) { + apiPathName += '&' + params.toString(); + } + return apiPathName; +} + +export default function ConnectWithPeople() { + + const { data: session } = useSession(); + const searchParams = useSearchParams(); + + const [users, setUsers] = useState([]); + const [nextSearchName, setNextSearchName] = useState(searchParams.get('search') || ''); + const [nextCursor, setNextCursor] = useState(null); + + const basePathName = buildAPISearchParams(searchParams, "api/users"); + const [apiPathName, setApiPathName] = useState(basePathName); + + + useEffect(() => { + if (session?.user) { + fetch(apiPathName) + .then(res => res.json()) + .then(data => { + setUsers(users => [...users, ...data.users]); + setNextCursor(data.next_cursor); + }); + }; + }, [apiPathName]); + + + if (!session?.user) { + return ( +
+

You are not logged in.

+
+ ) + } + + const fetchMoreUsers = () => { + if (basePathName.includes('?')) { + setApiPathName(basePathName + '&next_cursor=' + nextCursor); + } else { + setApiPathName(basePathName + '?next_cursor=' + nextCursor); + } + }; + + + return ( +
+

Connect with People

+
+ setNextSearchName(e.target.value)} + className="mr-2 px-4 py-2 border border-gray-300 rounded-md text-black w-48" + //ref={searchUsernameRef} + /> + + + +
+ +
+ Loading...} + height={600} + endMessage={ +

+ End of users +

+ } + > + {users.map((user) => ( +
+
+

{user.username}

+

Email: {user.email}

+ {/* Render other user information as needed */} +
+ + + +
+ ))} +
+
+
+ ); +} \ No newline at end of file