From a1e751407f2cec632b6d42c961bd1679bb7c205c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Filip=20Sodi=C4=87?= Date: Fri, 21 Feb 2025 14:07:15 +0100 Subject: [PATCH 1/2] Refactor UserTable and getPaginatedUsers query --- template/app/schema.prisma | 2 +- .../src/admin/dashboards/users/UsersTable.tsx | 115 ++++++++++-------- template/app/src/user/operations.ts | 90 ++++++-------- 3 files changed, 99 insertions(+), 108 deletions(-) diff --git a/template/app/schema.prisma b/template/app/schema.prisma index 7d2d71d9..1da9b4a4 100644 --- a/template/app/schema.prisma +++ b/template/app/schema.prisma @@ -110,4 +110,4 @@ model ContactFormMessage { content String isRead Boolean @default(false) repliedAt DateTime? -} \ No newline at end of file +} diff --git a/template/app/src/admin/dashboards/users/UsersTable.tsx b/template/app/src/admin/dashboards/users/UsersTable.tsx index f94b1459..3b943765 100644 --- a/template/app/src/admin/dashboards/users/UsersTable.tsx +++ b/template/app/src/admin/dashboards/users/UsersTable.tsx @@ -12,25 +12,28 @@ const AdminSwitch = ({ id, isAdmin }: Pick) => { }; const UsersTable = () => { - const [skip, setskip] = useState(0); - const [page, setPage] = useState(1); - const [email, setEmail] = useState(undefined); + const [currentPage, setCurrentPage] = useState(1); + const [emailFilter, setEmailFilter] = useState(''); const [isAdminFilter, setIsAdminFilter] = useState(undefined); - const [statusOptions, setStatusOptions] = useState([]); + const [subscriptionStatusFilter, setSubcriptionStatusFilter] = useState([]); + + const skipPages = currentPage - 1; + const { data, isLoading } = useQuery(getPaginatedUsers, { - skip, - emailContains: email, - isAdmin: isAdminFilter, - subscriptionStatus: statusOptions?.length > 0 ? statusOptions : undefined, + skipPages, + filter: { + ...(emailFilter && { emailContains: emailFilter }), + ...(isAdminFilter !== undefined && { isAdmin: isAdminFilter }), + ...(subscriptionStatusFilter?.length > 0 && { subscriptionStatusIn: subscriptionStatusFilter }), + }, }); - useEffect(() => { - setPage(1); - }, [email, statusOptions]); - - useEffect(() => { - setskip((page - 1) * 10); - }, [page]); + useEffect( + function backToPageOne() { + setCurrentPage(1); + }, + [emailFilter, subscriptionStatusFilter, isAdminFilter] + ); return (
@@ -47,7 +50,7 @@ const UsersTable = () => { id='email-filter' placeholder='dude@example.com' onChange={(e) => { - setEmail(e.currentTarget.value); + setEmailFilter(e.currentTarget.value); }} className='rounded border border-stroke py-2 px-5 bg-white outline-none transition focus:border-primary active:border-primary disabled:cursor-default disabled:bg-whiter dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary' /> @@ -56,8 +59,8 @@ const UsersTable = () => {
- {!!statusOptions && statusOptions.length > 0 ? ( - statusOptions.map((opt) => ( + {!!subscriptionStatusFilter && subscriptionStatusFilter.length > 0 ? ( + subscriptionStatusFilter.map((opt) => ( { { e.stopPropagation(); - setStatusOptions((prevValue) => { + setSubcriptionStatusFilter((prevValue) => { return prevValue?.filter((val) => val !== opt); }); }} className='z-30 cursor-pointer pl-2 hover:text-danger' > - - - + )) @@ -98,7 +88,7 @@ const UsersTable = () => { - - - - - +
@@ -157,6 +132,7 @@ const UsersTable = () => { }} className='relative z-20 w-full appearance-none rounded border border-stroke bg-white p-2 pl-4 pr-8 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input' > + {/*Why no svg here*/} @@ -168,11 +144,14 @@ const UsersTable = () => { page { - setPage(parseInt(e.currentTarget.value)); + const value = parseInt(e.currentTarget.value); + if (data?.totalPages && value <= data?.totalPages && value > 0) { + setCurrentPage(value); + } }} className='rounded-md border-1 border-stroke bg-transparent px-4 font-medium outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input dark:focus:border-primary' /> @@ -238,4 +217,32 @@ const UsersTable = () => { ); }; +function ChevronDownIcon() { + return ( + + + + + + ); +} + +function XIcon() { + return ( + + + + ); +} + export default UsersTable; diff --git a/template/app/src/user/operations.ts b/template/app/src/user/operations.ts index a29ba9d0..622a57b5 100644 --- a/template/app/src/user/operations.ts +++ b/template/app/src/user/operations.ts @@ -1,7 +1,8 @@ import { type UpdateIsUserAdminById, type GetPaginatedUsers } from 'wasp/server/operations'; import { type User } from 'wasp/entities'; -import { HttpError } from 'wasp/server'; +import { HttpError, prisma } from 'wasp/server'; import { type SubscriptionStatus } from '../payment/plans'; +import { type Prisma } from '@prisma/client'; export const updateIsUserAdminById: UpdateIsUserAdminById, User> = async ( { id, isAdmin }, @@ -22,11 +23,12 @@ export const updateIsUserAdminById: UpdateIsUserAdminById { - if (!context.user?.isAdmin) { - throw new HttpError(401); + if (!context.user) { + throw new HttpError(401, 'Only authenticated users are allowed to perform this operation'); } - const allSubscriptionStatusOptions = args.subscriptionStatus as Array | undefined; - const hasNotSubscribed = allSubscriptionStatusOptions?.find((status) => status === null); - const subscriptionStatusStrings = allSubscriptionStatusOptions?.filter((status) => status !== null) as - | string[] - | undefined; + if (!context.user.isAdmin) { + throw new HttpError(403, 'Only admins are allowed to perform this operation'); + } + + const { + skipPages, + filter: { subscriptionStatusIn: subscriptionStatus, emailContains, isAdmin }, + } = args; + const includeUnsubscribedUsers = !!subscriptionStatus?.some((status) => status === null); + const desiredSubscriptionStatuses = subscriptionStatus?.filter((status) => status !== null); - const queryResults = await context.entities.User.findMany({ - skip: args.skip, - take: 10, + const pageSize = 10; + + const userPageQuery: Prisma.UserFindManyArgs = { + skip: skipPages * pageSize, + take: pageSize, where: { AND: [ { email: { - contains: args.emailContains || undefined, + contains: emailContains, mode: 'insensitive', }, - isAdmin: args.isAdmin, + isAdmin, }, { OR: [ { subscriptionStatus: { - in: subscriptionStatusStrings, + in: desiredSubscriptionStatuses, }, }, { - subscriptionStatus: { - equals: hasNotSubscribed, - }, + subscriptionStatus: includeUnsubscribedUsers ? null : undefined, }, ], }, @@ -88,41 +95,18 @@ export const getPaginatedUsers: GetPaginatedUsers Date: Fri, 21 Feb 2025 14:33:27 +0100 Subject: [PATCH 2/2] Remove reminder comment --- template/app/src/admin/dashboards/users/UsersTable.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/template/app/src/admin/dashboards/users/UsersTable.tsx b/template/app/src/admin/dashboards/users/UsersTable.tsx index 3b943765..4d1f4a8f 100644 --- a/template/app/src/admin/dashboards/users/UsersTable.tsx +++ b/template/app/src/admin/dashboards/users/UsersTable.tsx @@ -132,7 +132,6 @@ const UsersTable = () => { }} className='relative z-20 w-full appearance-none rounded border border-stroke bg-white p-2 pl-4 pr-8 outline-none transition focus:border-primary active:border-primary dark:border-form-strokedark dark:bg-form-input' > - {/*Why no svg here*/}