diff --git a/website/src/components/UsersCell.tsx b/website/src/components/UsersCell.tsx
index 3cf2f1e5..5354ee5c 100644
--- a/website/src/components/UsersCell.tsx
+++ b/website/src/components/UsersCell.tsx
@@ -1,4 +1,17 @@
-import { Table, TableCaption, TableContainer, Tbody, Td, Th, Thead, Tr } from "@chakra-ui/react";
+import {
+ Button,
+ Flex,
+ Spacer,
+ Stack,
+ Table,
+ TableCaption,
+ TableContainer,
+ Tbody,
+ Td,
+ Th,
+ Thead,
+ Tr,
+} from "@chakra-ui/react";
import Link from "next/link";
import { useState } from "react";
import fetcher from "src/lib/fetcher";
@@ -8,41 +21,60 @@ import useSWR from "swr";
* Fetches users from the users api route and then presents them in a simple Chakra table.
*/
const UsersCell = () => {
- // Fetch and save the users.
+ const [pageIndex, setPageIndex] = useState(0);
const [users, setUsers] = useState([]);
- const { isLoading } = useSWR("/api/admin/users", fetcher, {
+
+ // Fetch and save the users.
+ // This follows useSWR's recommendation for simple pagination:
+ // https://swr.vercel.app/docs/pagination#when-to-use-useswr
+ useSWR(`/api/admin/users?pageIndex=${pageIndex}`, fetcher, {
onSuccess: setUsers,
});
+ const toPreviousPage = () => {
+ setPageIndex(Math.max(0, pageIndex - 1));
+ };
+
+ const toNextPage = () => {
+ setPageIndex(pageIndex + 1);
+ };
+
// Present users in a naive table.
return (
-
-
- Users
-
-
- | Id |
- Email |
- Name |
- Role |
- Update |
-
-
-
- {users.map((user, index) => (
-
- | {user.id} |
- {user.email} |
- {user.name} |
- {user.role} |
-
- Manage
- |
+
+
+
+
+
+
+
+
+ Users
+
+
+ | Id |
+ Email |
+ Name |
+ Role |
+ Update |
- ))}
-
-
-
+
+
+ {users.map((user, index) => (
+
+ | {user.id} |
+ {user.email} |
+ {user.name} |
+ {user.role} |
+
+ Manage
+ |
+
+ ))}
+
+
+
+
);
};
diff --git a/website/src/lib/auth.ts b/website/src/lib/auth.ts
index 1a0387f9..5fa20f48 100644
--- a/website/src/lib/auth.ts
+++ b/website/src/lib/auth.ts
@@ -5,7 +5,7 @@ import { getToken } from "next-auth/jwt";
* Wraps any API Route handler and verifies that the user has the appropriate
* role before running the handler. Returns a 403 otherwise.
*/
-const withRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiResponse) => any) => {
+const withRole = (role: string, handler: (arg0: NextApiRequest, arg1: NextApiResponse) => void) => {
return async (req: NextApiRequest, res: NextApiResponse) => {
const token = await getToken({ req });
if (!token || token.role !== role) {
diff --git a/website/src/pages/admin/index.tsx b/website/src/pages/admin/index.tsx
index 705a188b..9cbea222 100644
--- a/website/src/pages/admin/index.tsx
+++ b/website/src/pages/admin/index.tsx
@@ -26,7 +26,7 @@ const AdminIndex = () => {
return;
}
router.push("/");
- }, [session, status]);
+ }, [router, session, status]);
return (
<>
diff --git a/website/src/pages/admin/manage_user/[id].tsx b/website/src/pages/admin/manage_user/[id].tsx
index ead55224..cdd4746e 100644
--- a/website/src/pages/admin/manage_user/[id].tsx
+++ b/website/src/pages/admin/manage_user/[id].tsx
@@ -1,4 +1,4 @@
-import { Box, Button, Container, Flex, FormControl, FormLabel, Input, Select, useToast } from "@chakra-ui/react";
+import { Button, Container, FormControl, FormLabel, Input, Select, useToast } from "@chakra-ui/react";
import { Field, Form, Formik } from "formik";
import Head from "next/head";
import { useRouter } from "next/router";
@@ -27,7 +27,7 @@ const ManageUser = ({ user }) => {
return;
}
router.push("/");
- }, [session, status]);
+ }, [router, session, status]);
// Trigger to let us update the user's role. Triggers a toast when complete.
const { trigger } = useSWRMutation("/api/admin/update_user", poster, {
diff --git a/website/src/pages/api/admin/update_user.ts b/website/src/pages/api/admin/update_user.ts
index d29fce7c..a717e3d8 100644
--- a/website/src/pages/api/admin/update_user.ts
+++ b/website/src/pages/api/admin/update_user.ts
@@ -1,4 +1,3 @@
-import { getToken } from "next-auth/jwt";
import withRole from "src/lib/auth";
import prisma from "src/lib/prismadb";
diff --git a/website/src/pages/api/admin/users.ts b/website/src/pages/api/admin/users.ts
index 1490522a..ea8d59d9 100644
--- a/website/src/pages/api/admin/users.ts
+++ b/website/src/pages/api/admin/users.ts
@@ -1,12 +1,21 @@
-import { getToken } from "next-auth/jwt";
import withRole from "src/lib/auth";
import prisma from "src/lib/prismadb";
+// The number of users to fetch in any request.
+const PAGE_SIZE = 20;
+
/**
* Returns a list of user results from the database when the requesting user is
* a logged in admin.
*/
const handler = withRole("admin", async (req, res) => {
+ // Figure out the pagination index and skip that number of users.
+ //
+ // Note: with Prisma this isn't the most efficient but it's the only possible
+ // option with cuid based User IDs.
+ const { pageIndex } = req.query;
+ const skip = parseInt(pageIndex as string) * PAGE_SIZE || 0;
+
// Fetch 20 users.
const users = await prisma.user.findMany({
select: {
@@ -15,7 +24,8 @@ const handler = withRole("admin", async (req, res) => {
name: true,
email: true,
},
- take: 20,
+ skip,
+ take: PAGE_SIZE,
});
res.status(200).json(users);