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,
+ },
+ },
+ },
+ ];
+}