Skip to content

Commit

Permalink
Feature/cit 18 view users (#39)
Browse files Browse the repository at this point in the history
* [CIT-18] Overwrite from scratch

* CIT-18: fix user search api route, fix infinite scroll, fix search

* Trigger build

* CIT-18: clean up

---------

Co-authored-by: Ishaan <[email protected]>
Co-authored-by: Daniel <[email protected]>
  • Loading branch information
3 people authored Aug 4, 2023
1 parent 9b15bb0 commit 676fcd2
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 27 deletions.
5 changes: 5 additions & 0 deletions citrus/app/(users)/connect-with-people/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import ConnectWithPeople from '@/components/ConnectWithPeople';

export default function Page() {
return <ConnectWithPeople />;
}
88 changes: 61 additions & 27 deletions citrus/app/api/users/route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -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');
Expand All @@ -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
Expand Down Expand Up @@ -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,
Expand All @@ -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,
});
}

Expand Down
111 changes: 111 additions & 0 deletions citrus/components/ConnectWithPeople.tsx
Original file line number Diff line number Diff line change
@@ -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<users[]>([]);
const [nextSearchName, setNextSearchName] = useState(searchParams.get('search') || '');
const [nextCursor, setNextCursor] = useState<string | null>(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 (
<div className="w-9/12 m-auto">
<h1 className="text-5xl font-bold mb-4">You are not logged in.</h1>
</div>
)
}

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


return (
<div id="contents" className="w-9/12 mx-auto my-auto">
<h1 className="text-5xl font-bold mb-4">Connect with People</h1>
<div className="my-5">
<input
type="text"
placeholder="Search by username"
value={nextSearchName}
onChange={(e) => setNextSearchName(e.target.value)}
className="mr-2 px-4 py-2 border border-gray-300 rounded-md text-black w-48"
//ref={searchUsernameRef}
/>
<a href={`/connect-with-people?search=${nextSearchName}`}>
<button className='mr-2 px-4 py-2 bg-green-600 text-white rounded'>
Search
</button>
</a>
</div>

<div id="people" className="my-5">
<InfiniteScroll
dataLength={users.length}
next={fetchMoreUsers}
hasMore={nextCursor !== null}
loader={<h4>Loading...</h4>}
height={600}
endMessage={
<p style={{ textAlign: 'center' }}>
<b>End of users</b>
</p>
}
>
{users.map((user) => (
<div key={user.username} className="bg-green-600 text-white p-4 mb-4 rounded-md border border-white flex items-center justify-between">
<div className="flex-1">
<h3>{user.username}</h3>
<p>Email: {user.email}</p>
{/* Render other user information as needed */}
</div>
<a href={`/profile/${user.username}`}>
<button className="px-4 py-2 bg-black text-white rounded">View Profile</button>
</a>
</div>
))}
</InfiniteScroll>
</div>
</div>
);
}

1 comment on commit 676fcd2

@vercel
Copy link

@vercel vercel bot commented on 676fcd2 Aug 4, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@aliasana is attempting to deploy a commit to a Personal Account on Vercel that is not owned by them.

In order for the commit to be deployed, @aliasana must be granted access to the connected Vercel project.

If you're the owner of the Personal Account, transfer the project to a Vercel Team and start collaborating, or learn more.

Please sign in to comment.