From 32b41386e5180892817db9985e77bbf655c5b812 Mon Sep 17 00:00:00 2001 From: pit Date: Thu, 21 Sep 2023 12:43:36 +0100 Subject: [PATCH] feat: admin ui for managing instance --- apps/web/src/app/(dashboard)/admin/nav.tsx | 8 +- .../app/(dashboard)/admin/users/[id]/page.tsx | 8 ++ .../admin/users/data-table-users.tsx | 135 ++++++++++++++++++ .../src/app/(dashboard)/admin/users/page.tsx | 35 +++++ .../lib/server-only/user/get-all-users.ts | 37 +++++ 5 files changed, 220 insertions(+), 3 deletions(-) create mode 100644 apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx create mode 100644 apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx create mode 100644 apps/web/src/app/(dashboard)/admin/users/page.tsx create mode 100644 packages/lib/server-only/user/get-all-users.ts diff --git a/apps/web/src/app/(dashboard)/admin/nav.tsx b/apps/web/src/app/(dashboard)/admin/nav.tsx index 3b87a9b13..0b59335bf 100644 --- a/apps/web/src/app/(dashboard)/admin/nav.tsx +++ b/apps/web/src/app/(dashboard)/admin/nav.tsx @@ -37,10 +37,12 @@ export const AdminNav = ({ className, ...props }: AdminNavProps) => { 'justify-start md:w-full', pathname?.startsWith('/admin/users') && 'bg-secondary', )} - disabled + asChild > - - Users (Coming Soon) + + + Users + ); diff --git a/apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx b/apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx new file mode 100644 index 000000000..3b8cfa287 --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx @@ -0,0 +1,8 @@ +export default function UserPage() { + return ( +
+

Hey

+

Ho

+
+ ); +} diff --git a/apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx b/apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx new file mode 100644 index 000000000..890d5cd48 --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx @@ -0,0 +1,135 @@ +'use client'; + +import { useTransition } from 'react'; + +import Link from 'next/link'; + +import { Edit, Loader } from 'lucide-react'; + +import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; +import { Role } from '@documenso/prisma/client'; +import { Button } from '@documenso/ui/primitives/button'; +import { DataTable } from '@documenso/ui/primitives/data-table'; +import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; + +interface User { + id: number; + name: string | null; + email: string; + roles: Role[]; + Subscription: Subscription[]; +} + +interface Subscription { + id: number; + status: string; + planId: string | null; + priceId: string | null; + createdAt: Date | null; + periodEnd: Date | null; +} + +type UsersDataTableProps = { + users: User[]; + perPage: number; + page: number; + totalPages: number; +}; + +export const UsersDataTable = ({ users, perPage, page, totalPages }: UsersDataTableProps) => { + const [isPending, startTransition] = useTransition(); + const updateSearchParams = useUpdateSearchParams(); + console.log(users); + + const onPaginationChange = (page: number, perPage: number) => { + startTransition(() => { + updateSearchParams({ + page, + perPage, + }); + }); + }; + + return ( +
+
{row.original.id}
, + }, + { + header: 'Name', + accessorKey: 'name', + cell: ({ row }) =>
{row.original.name}
, + }, + { + header: 'Email', + accessorKey: 'email', + cell: ({ row }) =>
{row.original.email}
, + }, + { + header: 'Roles', + accessorKey: 'roles', + cell: ({ row }) => { + return ( + <> + {row.original.roles.map((role: string, idx: number) => { + return ( + + {role} {} + + ); + })} + + ); + }, + }, + { + header: 'Subscription status', + accessorKey: 'subscription', + cell: ({ row }) => { + return ( + <> + {row.original.Subscription.map((subscription: Subscription, idx: number) => { + return {subscription.status}; + })} + + ); + }, + }, + { + header: 'Edit', + accessorKey: 'edit', + cell: ({ row }) => { + return ( +
+ +
+ ); + }, + }, + ]} + data={users} + perPage={perPage} + currentPage={page} + totalPages={totalPages} + onPaginationChange={onPaginationChange} + > + {(table) => } +
+ + {isPending && ( +
+ +
+ )} +
+ ); +}; diff --git a/apps/web/src/app/(dashboard)/admin/users/page.tsx b/apps/web/src/app/(dashboard)/admin/users/page.tsx new file mode 100644 index 000000000..7f8b9af47 --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/users/page.tsx @@ -0,0 +1,35 @@ +import { findUsers } from '@documenso/lib/server-only/user/get-all-users'; + +/* +1. retrieve all users from the db +2. display them in a table +*/ +import { UsersDataTable } from './data-table-users'; + +type AdminManageUsersProps = { + searchParams?: { + page?: number; + perPage?: number; + }; +}; + +export default async function AdminManageUsers({ searchParams = {} }: AdminManageUsersProps) { + const page = Number(searchParams.page) || 1; + const perPage = Number(searchParams.perPage) || 10; + + const results = await findUsers({ page, perPage }); + + return ( +
+

Manage users

+
+ +
+
+ ); +} diff --git a/packages/lib/server-only/user/get-all-users.ts b/packages/lib/server-only/user/get-all-users.ts new file mode 100644 index 000000000..157a75d4a --- /dev/null +++ b/packages/lib/server-only/user/get-all-users.ts @@ -0,0 +1,37 @@ +import { prisma } from '@documenso/prisma'; + +type getAllUsersProps = { + page: number; + perPage: number; +}; + +export const findUsers = async ({ page = 1, perPage = 10 }: getAllUsersProps) => { + const [users, count] = await Promise.all([ + await prisma.user.findMany({ + select: { + id: true, + name: true, + email: true, + roles: true, + Subscription: { + select: { + id: true, + status: true, + planId: true, + priceId: true, + createdAt: true, + periodEnd: true, + }, + }, + }, + skip: Math.max(page - 1, 0) * perPage, + take: perPage, + }), + await prisma.user.count(), + ]); + + return { + users, + totalPages: Math.ceil(count / perPage), + }; +};