mirror of
https://github.com/documenso/documenso.git
synced 2025-11-15 17:21:41 +10:00
fix: sort table
This commit is contained in:
@ -25,6 +25,8 @@ type LeaderboardTableProps = {
|
|||||||
totalPages: number;
|
totalPages: number;
|
||||||
perPage: number;
|
perPage: number;
|
||||||
page: number;
|
page: number;
|
||||||
|
sortBy: 'name' | 'createdAt' | 'signingVolume';
|
||||||
|
sortOrder: 'asc' | 'desc';
|
||||||
};
|
};
|
||||||
|
|
||||||
export const LeaderboardTable = ({
|
export const LeaderboardTable = ({
|
||||||
@ -32,6 +34,8 @@ export const LeaderboardTable = ({
|
|||||||
totalPages,
|
totalPages,
|
||||||
perPage,
|
perPage,
|
||||||
page,
|
page,
|
||||||
|
sortBy,
|
||||||
|
sortOrder,
|
||||||
}: LeaderboardTableProps) => {
|
}: LeaderboardTableProps) => {
|
||||||
const { _ } = useLingui();
|
const { _ } = useLingui();
|
||||||
|
|
||||||
@ -49,10 +53,10 @@ export const LeaderboardTable = ({
|
|||||||
size: 10,
|
size: 10,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: ({ column }) => (
|
header: () => (
|
||||||
<div
|
<div
|
||||||
className="flex cursor-pointer items-center"
|
className="flex cursor-pointer items-center"
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
onClick={() => handleColumnSort('name', sortOrder)}
|
||||||
>
|
>
|
||||||
{_(msg`Name`)}
|
{_(msg`Name`)}
|
||||||
<CaretSortIcon className="ml-2 h-4 w-4" />
|
<CaretSortIcon className="ml-2 h-4 w-4" />
|
||||||
@ -63,10 +67,10 @@ export const LeaderboardTable = ({
|
|||||||
size: 250,
|
size: 250,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: ({ column }) => (
|
header: () => (
|
||||||
<div
|
<div
|
||||||
className="flex cursor-pointer items-center"
|
className="flex cursor-pointer items-center"
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
onClick={() => handleColumnSort('signingVolume', sortOrder)}
|
||||||
>
|
>
|
||||||
{_(msg`Signing Volume`)}
|
{_(msg`Signing Volume`)}
|
||||||
<CaretSortIcon className="ml-2 h-4 w-4" />
|
<CaretSortIcon className="ml-2 h-4 w-4" />
|
||||||
@ -76,11 +80,11 @@ export const LeaderboardTable = ({
|
|||||||
cell: ({ row }) => <div>{Number(row.getValue('signingVolume'))}</div>,
|
cell: ({ row }) => <div>{Number(row.getValue('signingVolume'))}</div>,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
header: ({ column }) => {
|
header: () => {
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className="flex cursor-pointer items-center"
|
className="flex cursor-pointer items-center"
|
||||||
onClick={() => column.toggleSorting(column.getIsSorted() === 'asc')}
|
onClick={() => handleColumnSort('createdAt', sortOrder)}
|
||||||
>
|
>
|
||||||
{_(msg`Created`)}
|
{_(msg`Created`)}
|
||||||
<CaretSortIcon className="ml-2 h-4 w-4" />
|
<CaretSortIcon className="ml-2 h-4 w-4" />
|
||||||
@ -91,7 +95,7 @@ export const LeaderboardTable = ({
|
|||||||
cell: ({ row }) => <div>{row.original.createdAt.toLocaleDateString()}</div>,
|
cell: ({ row }) => <div>{row.original.createdAt.toLocaleDateString()}</div>,
|
||||||
},
|
},
|
||||||
] satisfies DataTableColumnDef<SigningVolume>[];
|
] satisfies DataTableColumnDef<SigningVolume>[];
|
||||||
}, []);
|
}, [sortOrder]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
startTransition(() => {
|
startTransition(() => {
|
||||||
@ -117,6 +121,18 @@ export const LeaderboardTable = ({
|
|||||||
setSearchString(e.target.value);
|
setSearchString(e.target.value);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleColumnSort = (
|
||||||
|
column: 'name' | 'createdAt' | 'signingVolume',
|
||||||
|
sortOrder: 'asc' | 'desc',
|
||||||
|
) => {
|
||||||
|
startTransition(() => {
|
||||||
|
updateSearchParams({
|
||||||
|
sortBy: sortBy === column,
|
||||||
|
sortOrder: sortOrder === 'asc' ? 'desc' : 'asc',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="relative">
|
<div className="relative">
|
||||||
<Input
|
<Input
|
||||||
|
|||||||
@ -4,14 +4,22 @@ import { getRequiredServerComponentSession } from '@documenso/lib/next-auth/get-
|
|||||||
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
import { isAdmin } from '@documenso/lib/next-auth/guards/is-admin';
|
||||||
import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume';
|
import { getSigningVolume } from '@documenso/lib/server-only/admin/get-signing-volume';
|
||||||
|
|
||||||
export async function search(search: string, page: number, perPage: number) {
|
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();
|
const { user } = await getRequiredServerComponentSession();
|
||||||
|
|
||||||
if (!isAdmin(user)) {
|
if (!isAdmin(user)) {
|
||||||
throw new Error('Unauthorized');
|
throw new Error('Unauthorized');
|
||||||
}
|
}
|
||||||
|
|
||||||
const results = await getSigningVolume({ search, page, perPage });
|
const results = await getSigningVolume({ search, page, perPage, sortBy, sortOrder });
|
||||||
|
|
||||||
return results;
|
return results;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -12,6 +12,8 @@ type AdminLeaderboardProps = {
|
|||||||
search?: string;
|
search?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
|
sortBy?: 'name' | 'createdAt' | 'signingVolume';
|
||||||
|
sortOrder?: 'asc' | 'desc';
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -27,9 +29,16 @@ export default async function Leaderboard({ searchParams = {} }: AdminLeaderboar
|
|||||||
const page = Number(searchParams.page) || 1;
|
const page = Number(searchParams.page) || 1;
|
||||||
const perPage = Number(searchParams.perPage) || 10;
|
const perPage = Number(searchParams.perPage) || 10;
|
||||||
const searchString = searchParams.search || '';
|
const searchString = searchParams.search || '';
|
||||||
|
const sortBy = searchParams.sortBy || 'signingVolume';
|
||||||
|
const sortOrder = searchParams.sortOrder || 'desc';
|
||||||
|
|
||||||
// todo: change the name
|
const { leaderboard: signingVolume, totalPages } = await search({
|
||||||
const { leaderboard: signingVolume, totalPages } = await search(searchString, page, perPage);
|
search: searchString,
|
||||||
|
page,
|
||||||
|
perPage,
|
||||||
|
sortBy,
|
||||||
|
sortOrder,
|
||||||
|
});
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
@ -42,6 +51,8 @@ export default async function Leaderboard({ searchParams = {} }: AdminLeaderboar
|
|||||||
totalPages={totalPages}
|
totalPages={totalPages}
|
||||||
page={page}
|
page={page}
|
||||||
perPage={perPage}
|
perPage={perPage}
|
||||||
|
sortBy={sortBy}
|
||||||
|
sortOrder={sortOrder}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|||||||
@ -12,12 +12,16 @@ export type GetSigningVolumeOptions = {
|
|||||||
search?: string;
|
search?: string;
|
||||||
page?: number;
|
page?: number;
|
||||||
perPage?: number;
|
perPage?: number;
|
||||||
|
sortBy?: 'name' | 'createdAt' | 'signingVolume';
|
||||||
|
sortOrder?: 'asc' | 'desc';
|
||||||
};
|
};
|
||||||
|
|
||||||
export async function getSigningVolume({
|
export async function getSigningVolume({
|
||||||
search = '',
|
search = '',
|
||||||
page = 1,
|
page = 1,
|
||||||
perPage = 10,
|
perPage = 10,
|
||||||
|
sortBy = 'signingVolume',
|
||||||
|
sortOrder = 'desc',
|
||||||
}: GetSigningVolumeOptions) {
|
}: GetSigningVolumeOptions) {
|
||||||
const whereClause = Prisma.validator<Prisma.SubscriptionWhereInput>()({
|
const whereClause = Prisma.validator<Prisma.SubscriptionWhereInput>()({
|
||||||
status: 'ACTIVE',
|
status: 'ACTIVE',
|
||||||
@ -38,6 +42,8 @@ export async function getSigningVolume({
|
|||||||
],
|
],
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const orderBy = getOrderByClause({ sortBy, sortOrder });
|
||||||
|
|
||||||
const [subscriptions, totalCount] = await Promise.all([
|
const [subscriptions, totalCount] = await Promise.all([
|
||||||
prisma.subscription.findMany({
|
prisma.subscription.findMany({
|
||||||
where: whereClause,
|
where: whereClause,
|
||||||
@ -76,15 +82,7 @@ export async function getSigningVolume({
|
|||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
orderBy: [
|
orderBy,
|
||||||
{
|
|
||||||
User: {
|
|
||||||
Document: {
|
|
||||||
_count: 'desc',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
],
|
|
||||||
skip: Math.max(page - 1, 0) * perPage,
|
skip: Math.max(page - 1, 0) * perPage,
|
||||||
take: perPage,
|
take: perPage,
|
||||||
}),
|
}),
|
||||||
@ -112,3 +110,34 @@ export async function getSigningVolume({
|
|||||||
totalPages: Math.ceil(totalCount / perPage),
|
totalPages: Math.ceil(totalCount / perPage),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type GetOrderByClauseOptions = {
|
||||||
|
sortBy: string;
|
||||||
|
sortOrder: 'asc' | 'desc';
|
||||||
|
};
|
||||||
|
|
||||||
|
const getOrderByClause = ({ sortBy, sortOrder }: GetOrderByClauseOptions) => {
|
||||||
|
switch (sortBy) {
|
||||||
|
case 'name':
|
||||||
|
return [{ User: { name: sortOrder } }, { team: { name: sortOrder } }];
|
||||||
|
case 'createdAt':
|
||||||
|
return { createdAt: sortOrder };
|
||||||
|
default:
|
||||||
|
return [
|
||||||
|
{
|
||||||
|
User: {
|
||||||
|
Document: {
|
||||||
|
_count: sortOrder,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
team: {
|
||||||
|
document: {
|
||||||
|
_count: sortOrder,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|||||||
@ -961,7 +961,7 @@ msgid "Create your account and start using state-of-the-art document signing. Op
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
|
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:85
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:89
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
|
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
|
||||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
|
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
|
||||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
|
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
|
||||||
@ -1989,7 +1989,7 @@ msgid "My templates"
|
|||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
|
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:57
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:61
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
|
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
|
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
|
||||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
|
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
|
||||||
@ -2594,7 +2594,7 @@ msgstr ""
|
|||||||
msgid "Search by document title"
|
msgid "Search by document title"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:125
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:141
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
|
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
|
||||||
msgid "Search by name or email"
|
msgid "Search by name or email"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
@ -2816,8 +2816,8 @@ msgstr ""
|
|||||||
msgid "Signing up..."
|
msgid "Signing up..."
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:71
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:75
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:37
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:46
|
||||||
msgid "Signing Volume"
|
msgid "Signing Volume"
|
||||||
msgstr ""
|
msgstr ""
|
||||||
|
|
||||||
|
|||||||
@ -956,7 +956,7 @@ msgid "Create your account and start using state-of-the-art document signing. Op
|
|||||||
msgstr "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
msgstr "Create your account and start using state-of-the-art document signing. Open and beautiful signing is within your grasp."
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
|
#: apps/web/src/app/(dashboard)/admin/documents/document-results.tsx:62
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:85
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:89
|
||||||
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
|
#: apps/web/src/app/(dashboard)/documents/[id]/document-page-view-information.tsx:35
|
||||||
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
|
#: apps/web/src/app/(dashboard)/documents/data-table.tsx:54
|
||||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
|
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table.tsx:65
|
||||||
@ -2007,7 +2007,7 @@ msgid "My templates"
|
|||||||
msgstr "My templates"
|
msgstr "My templates"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
|
#: apps/web/src/app/(dashboard)/admin/documents/[id]/recipient-item.tsx:148
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:57
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:61
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
|
#: apps/web/src/app/(dashboard)/admin/users/[id]/page.tsx:99
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
|
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:66
|
||||||
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
|
#: apps/web/src/app/(dashboard)/settings/security/passkeys/user-passkeys-data-table-actions.tsx:144
|
||||||
@ -2612,7 +2612,7 @@ msgstr "Search"
|
|||||||
msgid "Search by document title"
|
msgid "Search by document title"
|
||||||
msgstr "Search by document title"
|
msgstr "Search by document title"
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:125
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:141
|
||||||
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
|
#: apps/web/src/app/(dashboard)/admin/users/data-table-users.tsx:144
|
||||||
msgid "Search by name or email"
|
msgid "Search by name or email"
|
||||||
msgstr "Search by name or email"
|
msgstr "Search by name or email"
|
||||||
@ -2838,8 +2838,8 @@ msgstr "Signing in..."
|
|||||||
msgid "Signing up..."
|
msgid "Signing up..."
|
||||||
msgstr "Signing up..."
|
msgstr "Signing up..."
|
||||||
|
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:71
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/data-table-leaderboard.tsx:75
|
||||||
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:37
|
#: apps/web/src/app/(dashboard)/admin/leaderboard/page.tsx:46
|
||||||
msgid "Signing Volume"
|
msgid "Signing Volume"
|
||||||
msgstr "Signing Volume"
|
msgstr "Signing Volume"
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user