diff --git a/apps/web/src/app/(dashboard)/admin/documents/data-table.tsx b/apps/web/src/app/(dashboard)/admin/documents/data-table.tsx index b1ab92e42..1d121742a 100644 --- a/apps/web/src/app/(dashboard)/admin/documents/data-table.tsx +++ b/apps/web/src/app/(dashboard)/admin/documents/data-table.tsx @@ -10,10 +10,10 @@ import { useSession } from 'next-auth/react'; import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; import { FindResultSet } from '@documenso/lib/types/find-result-set'; import { Document, Recipient, User } from '@documenso/prisma/client'; +import { Avatar, AvatarFallback } from '@documenso/ui/primitives/avatar'; import { DataTable } from '@documenso/ui/primitives/data-table'; import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; -import { StackAvatarsWithTooltip } from '~/components/(dashboard)/avatar/stack-avatars-with-tooltip'; import { DocumentStatus } from '~/components/formatter/document-status'; import { LocaleDate } from '~/components/formatter/locale-date'; @@ -68,15 +68,11 @@ export const DocumentsDataTable = ({ results }: DocumentsDataTableProps) => { cell: ({ row }) => { return ( - + + + {row.original.User.name} + + ); }, diff --git a/apps/web/src/app/(dashboard)/admin/documents/page.tsx b/apps/web/src/app/(dashboard)/admin/documents/page.tsx index d62d82ada..6b5a0761c 100644 --- a/apps/web/src/app/(dashboard)/admin/documents/page.tsx +++ b/apps/web/src/app/(dashboard)/admin/documents/page.tsx @@ -11,12 +11,10 @@ export type DocumentsPageProps = { }; export default async function Documents({ searchParams = {} }: DocumentsPageProps) { - const user = await getRequiredServerComponentSession(); const page = Number(searchParams.page) || 1; const perPage = Number(searchParams.perPage) || 20; const results = await findDocuments({ - userId: user.id, orderBy: { column: 'createdAt', direction: 'desc', 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 index 0329b6a17..a598fc605 100644 --- a/apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx +++ b/apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx @@ -7,7 +7,7 @@ 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 { Document, Role, Subscription } 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'; @@ -17,18 +17,16 @@ interface User { name: string | null; email: string; roles: Role[]; - Subscription: Subscription[]; - Document: Document[]; + Subscription: SubscriptionLite[]; + Document: DocumentLite[]; } -interface Subscription { - id: number; - status: string; - planId: string | null; - priceId: string | null; - createdAt: Date | null; - periodEnd: Date | null; -} +type SubscriptionLite = Pick< + Subscription, + 'id' | 'status' | 'planId' | 'priceId' | 'createdAt' | 'periodEnd' +>; + +type DocumentLite = Pick; type UsersDataTableProps = { users: User[]; @@ -37,10 +35,6 @@ type UsersDataTableProps = { totalPages: number; }; -type Document = { - id: number; -}; - export const UsersDataTable = ({ users, perPage, page, totalPages }: UsersDataTableProps) => { const [isPending, startTransition] = useTransition(); const updateSearchParams = useUpdateSearchParams(); @@ -97,7 +91,7 @@ export const UsersDataTable = ({ users, perPage, page, totalPages }: UsersDataTa if (row.original.Subscription && row.original.Subscription.length > 0) { return ( <> - {row.original.Subscription.map((subscription: Subscription, i: number) => { + {row.original.Subscription.map((subscription: SubscriptionLite, i: number) => { return {subscription.status}; })} diff --git a/apps/web/src/app/(dashboard)/admin/users/page.tsx b/apps/web/src/app/(dashboard)/admin/users/page.tsx index 7f8b9af47..67dc5563b 100644 --- a/apps/web/src/app/(dashboard)/admin/users/page.tsx +++ b/apps/web/src/app/(dashboard)/admin/users/page.tsx @@ -1,10 +1,6 @@ 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'; +import { Users } from './users'; type AdminManageUsersProps = { searchParams?: { @@ -13,23 +9,22 @@ type AdminManageUsersProps = { }; }; -export default async function AdminManageUsers({ searchParams = {} }: AdminManageUsersProps) { +export default function AdminManageUsers({ searchParams = {} }: AdminManageUsersProps) { const page = Number(searchParams.page) || 1; const perPage = Number(searchParams.perPage) || 10; - const results = await findUsers({ page, perPage }); + async function search(search: string) { + 'use server'; + + const results = await findUsers({ username: search, email: search, page, perPage }); + + return results; + } return (

Manage users

-
- -
+
); } diff --git a/apps/web/src/app/(dashboard)/admin/users/users.tsx b/apps/web/src/app/(dashboard)/admin/users/users.tsx new file mode 100644 index 000000000..e44772dbb --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/users/users.tsx @@ -0,0 +1,78 @@ +'use client'; + +import { useEffect, useState } from 'react'; + +import { Document, Role, Subscription } from '@documenso/prisma/client'; +import { Button } from '@documenso/ui/primitives/button'; +import { Input } from '@documenso/ui/primitives/input'; + +import { UsersDataTable } from './data-table-users'; + +export type SubscriptionLite = Pick< + Subscription, + 'id' | 'status' | 'planId' | 'priceId' | 'createdAt' | 'periodEnd' +>; +export type DocumentLite = Pick; + +export type User = { + id: number; + name: string | null; + email: string; + roles: Role[]; + Subscription: SubscriptionLite[]; + Document: DocumentLite[]; +}; + +export type UsersProps = { + search: (search: string) => Promise<{ users: User[]; totalPages: number }>; + perPage: number; + page: number; +}; + +export const Users = ({ search, perPage, page }: UsersProps) => { + const [data, setData] = useState([]); + const [totalPages, setTotalPages] = useState(0); + + const [searchString, setSearchString] = useState(''); + + useEffect(() => { + const fetchData = async () => { + try { + const result = await search(searchString); + setData(result.users); + setTotalPages(result.totalPages); + } catch (err) { + throw new Error(err); + } + }; + + fetchData(); + }, [searchString, search]); + + const onSubmit = async (e: React.FormEvent) => { + e.preventDefault(); + const result = await search(searchString); + setData(result.users); + }; + + const handleChange = (e: React.ChangeEvent) => { + setSearchString(e.target.value); + }; + + return ( + <> +
+ + +
+
+ +
+ + ); +}; diff --git a/apps/web/src/components/(dashboard)/admin/generic-data-table.tsx b/apps/web/src/components/(dashboard)/admin/generic-data-table.tsx new file mode 100644 index 000000000..1dd3e01d3 --- /dev/null +++ b/apps/web/src/components/(dashboard)/admin/generic-data-table.tsx @@ -0,0 +1,57 @@ +import { useTransition } from 'react'; + +import { ColumnDef } from '@tanstack/react-table'; +import { Loader } from 'lucide-react'; + +import { useUpdateSearchParams } from '@documenso/lib/client-only/hooks/use-update-search-params'; +import { DataTable } from '@documenso/ui/primitives/data-table'; +import { DataTablePagination } from '@documenso/ui/primitives/data-table-pagination'; + +type GenericDataTableProps = { + columns: ColumnDef[]; + data: TData[]; + perPage: number; + currentPage: number; + totalPages: number; +}; + +export function GenericDataTable({ + columns, + data, + perPage, + currentPage, + totalPages, +}: GenericDataTableProps) { + const [isPending, startTransition] = useTransition(); + const updateSearchParams = useUpdateSearchParams(); + + const onPaginationChange = (page: number, perPage: number) => { + startTransition(() => { + updateSearchParams({ + page: page.toString(), + perPage: perPage.toString(), + }); + }); + }; + + return ( +
+ + {(table) => } + + + {isPending && ( +
+ +
+ )} +
+ ); +} diff --git a/packages/lib/server-only/user/get-all-users.ts b/packages/lib/server-only/user/get-all-users.ts index 35e165260..babcc7ba1 100644 --- a/packages/lib/server-only/user/get-all-users.ts +++ b/packages/lib/server-only/user/get-all-users.ts @@ -1,11 +1,37 @@ +import { Prisma } from '@prisma/client'; + import { prisma } from '@documenso/prisma'; type getAllUsersProps = { + username: string; + email: string; page: number; perPage: number; }; -export const findUsers = async ({ page = 1, perPage = 10 }: getAllUsersProps) => { +export const findUsers = async ({ + username = '', + email = '', + page = 1, + perPage = 10, +}: getAllUsersProps) => { + const whereClause = Prisma.validator()({ + OR: [ + { + name: { + contains: username, + mode: 'insensitive', + }, + }, + { + email: { + contains: email, + mode: 'insensitive', + }, + }, + ], + }); + const [users, count] = await Promise.all([ await prisma.user.findMany({ select: { @@ -29,10 +55,13 @@ export const findUsers = async ({ page = 1, perPage = 10 }: getAllUsersProps) => }, }, }, + where: whereClause, skip: Math.max(page - 1, 0) * perPage, take: perPage, }), - await prisma.user.count(), + await prisma.user.count({ + where: whereClause, + }), ]); return {