diff --git a/apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts b/apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts new file mode 100644 index 000000000..70f27dc1b --- /dev/null +++ b/apps/web/src/app/(dashboard)/admin/leaderboard/fetch-leaderboard-fix.actions.ts @@ -0,0 +1,25 @@ +'use server'; + +import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-server-component-session'; +import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin'; +import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume-fix'; + +type SearchOptions = { + search: string; + page: number; + perPage: number; + sortBy: 'name' | 'createdAt' | 'signingVolume'; + sortOrder: 'asc' | 'desc'; +}; + +export async function search({ search, page, perPage, sortBy, sortOrder }: SearchOptions) { + const { user } = await getRequiredServerComponentSession(); + + if (!isAdmin(user)) { + throw new Error('Unauthorized'); + } + + const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder }); + + return results; +} diff --git a/apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx b/apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx index f5f880aa3..736971147 100644 --- a/apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx +++ b/apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx @@ -5,6 +5,7 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get- import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin'; import { LeaderboardTable } from './data-table-leaderboard'; +import { search as search2 } from './fetch-leaderboard-fix.actions'; import { search } from './fetch-leaderboard.actions'; type AdminLeaderboardProps = { @@ -40,6 +41,14 @@ export default async function Leaderboard({ searchParams = {} }: AdminLeaderboar sortOrder, }); + const { leaderboard: signingVolume2, totalPages: totalPagesFix2 } = await search2({ + search: searchString, + page, + perPage, + sortBy, + sortOrder, + }); + return (

@@ -54,6 +63,18 @@ export default async function Leaderboard({ searchParams = {} }: AdminLeaderboar sortBy={sortBy} sortOrder={sortOrder} /> + +

+ Signing Volume 2 +

+
); diff --git a/packages/lib/server-only/admin/get-signing-volume-fix.ts b/packages/lib/server-only/admin/get-signing-volume-fix.ts new file mode 100644 index 000000000..09be59e88 --- /dev/null +++ b/packages/lib/server-only/admin/get-signing-volume-fix.ts @@ -0,0 +1,148 @@ +import { prisma } from '@documenso/prisma'; +import { Prisma } from '@documenso/prisma/client'; + +export type SigningVolume = { + id: number; + name: string; + signingVolume: number; + createdAt: Date; + planId: string; +}; + +export type GetSigningVolumeOptions = { + search?: string; + page?: number; + perPage?: number; + sortBy?: 'name' | 'createdAt' | 'signingVolume'; + sortOrder?: 'asc' | 'desc'; +}; + +export async function getSigningVolume({ + search = '', + page = 1, + perPage = 10, + sortBy = 'signingVolume', + sortOrder = 'desc', +}: GetSigningVolumeOptions) { + const whereClause = Prisma.validator()({ + status: 'ACTIVE', + OR: [ + { + User: { + OR: [ + { name: { contains: search, mode: 'insensitive' } }, + { email: { contains: search, mode: 'insensitive' } }, + ], + }, + }, + { + team: { + name: { contains: search, mode: 'insensitive' }, + }, + }, + ], + }); + + const orderByClause = getOrderByClause({ sortBy, sortOrder }); + + const [subscriptions, totalCount] = await Promise.all([ + prisma.subscription.findMany({ + where: whereClause, + include: { + User: { + include: { + Document: { + where: { + status: 'COMPLETED', + deletedAt: null, + }, + }, + }, + }, + team: { + include: { + document: { + where: { + status: 'COMPLETED', + deletedAt: null, + }, + }, + }, + }, + }, + orderBy: orderByClause, + skip: Math.max(page - 1, 0) * perPage, + take: perPage, + }), + prisma.subscription.count({ + where: whereClause, + }), + ]); + + const leaderboardWithVolume: SigningVolume[] = subscriptions.map((subscription) => { + const name = + subscription.User?.name || subscription.team?.name || subscription.User?.email || 'Unknown'; + + const userSignedDocs = subscription.User?.Document?.length || 0; + const teamSignedDocs = subscription.team?.document?.length || 0; + + return { + id: subscription.id, + name, + signingVolume: userSignedDocs + teamSignedDocs, + createdAt: subscription.createdAt, + planId: subscription.planId, + }; + }); + + return { + leaderboard: leaderboardWithVolume, + totalPages: Math.ceil(totalCount / perPage), + }; +} + +function getOrderByClause(options: { + sortBy: string; + sortOrder: 'asc' | 'desc'; +}): Prisma.SubscriptionOrderByWithRelationInput | Prisma.SubscriptionOrderByWithRelationInput[] { + const { sortBy, sortOrder } = options; + + if (sortBy === 'name') { + return [ + { + User: { + name: sortOrder, + }, + }, + { + team: { + name: sortOrder, + }, + }, + ]; + } + + if (sortBy === 'createdAt') { + return { + createdAt: sortOrder, + }; + } + + // Default: sort by signing volume + return [ + { + User: { + Document: { + _count: sortOrder, + }, + }, + }, + { + team: { + document: { + _count: sortOrder, + }, + }, + }, + ]; +}