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 - - - - - - - - - - - {users.map((user, index) => ( - - - - - - + + + + + + + +
IdEmailNameRoleUpdate
{user.id}{user.email}{user.name}{user.role} - Manage -
+ Users + + + + + + + - ))} - -
IdEmailNameRoleUpdate
-
+ + + {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);