From 6a62091d651189f8c4aabdffbac1ffb8fad319d8 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Wed, 22 Dec 2021 17:30:59 +0000 Subject: [PATCH 1/3] Initial POC --- .../components/Dashboard/JAdmin/JAdmin.tsx | 107 ++++++++++++++++++ .../web/components/Dashboard/JAdmin/index.ts | 1 + packages/web/generated/graphql.tsx | 16 +-- packages/web/graphql/user/users.graphql | 9 +- packages/web/pages/dashboard/jadmin.tsx | 28 +++++ packages/web/pages/dashboard/my-feed.tsx | 2 +- 6 files changed, 141 insertions(+), 22 deletions(-) create mode 100644 packages/web/components/Dashboard/JAdmin/JAdmin.tsx create mode 100644 packages/web/components/Dashboard/JAdmin/index.ts create mode 100644 packages/web/pages/dashboard/jadmin.tsx diff --git a/packages/web/components/Dashboard/JAdmin/JAdmin.tsx b/packages/web/components/Dashboard/JAdmin/JAdmin.tsx new file mode 100644 index 000000000..7327934ab --- /dev/null +++ b/packages/web/components/Dashboard/JAdmin/JAdmin.tsx @@ -0,0 +1,107 @@ +import React from 'react' +import theme from '@/theme' +import { useUsersQuery, UserWithLanguagesFragmentFragment as UserType } from '@/generated/graphql' +import Button, { ButtonVariant } from '@/components/Button' + +type UserInfoProps = { + user: UserType +} + +const UserInfo: React.FC = ({ user }) => { + return ( + + {user.handle} + {user.name} + {user.email} + {user.userRole} + + + + + + ) +} + +const JAdmin = () => { + const { data } = useUsersQuery() + + return ( +
+
+

Manage Users

+ + + + + + + + + + + + {data?.users.map((user) => ( + + ))} + +
HandleNameEmailPermissions⬇︎
+
+ +
+ ) +} + +export default JAdmin diff --git a/packages/web/components/Dashboard/JAdmin/index.ts b/packages/web/components/Dashboard/JAdmin/index.ts new file mode 100644 index 000000000..c4791ff25 --- /dev/null +++ b/packages/web/components/Dashboard/JAdmin/index.ts @@ -0,0 +1 @@ +export { default } from './JAdmin' diff --git a/packages/web/generated/graphql.tsx b/packages/web/generated/graphql.tsx index d6a0224f3..b6002e80d 100644 --- a/packages/web/generated/graphql.tsx +++ b/packages/web/generated/graphql.tsx @@ -1551,11 +1551,7 @@ export type UserStatsQuery = { __typename?: 'Query' } & { export type UsersQueryVariables = Exact<{ [key: string]: never }> export type UsersQuery = { __typename?: 'Query' } & { - users: Array< - { __typename?: 'User' } & Pick & { - posts: Array<{ __typename?: 'Post' } & Pick> - } - > + users: Array<{ __typename?: 'User' } & UserWithLanguagesFragmentFragment> } export const UserFragmentFragmentDoc = gql` @@ -5005,16 +5001,10 @@ export type UserStatsQueryResult = ApolloReactCommon.QueryResult< export const UsersDocument = gql` query users { users { - id - name - email - posts { - id - title - body - } + ...UserWithLanguagesFragment } } + ${UserWithLanguagesFragmentFragmentDoc} ` /** diff --git a/packages/web/graphql/user/users.graphql b/packages/web/graphql/user/users.graphql index 157e03f41..1bc4c2a0d 100644 --- a/packages/web/graphql/user/users.graphql +++ b/packages/web/graphql/user/users.graphql @@ -1,12 +1,5 @@ query users { users { - id - name - email - posts { - id - title - body - } + ...UserWithLanguagesFragment } } diff --git a/packages/web/pages/dashboard/jadmin.tsx b/packages/web/pages/dashboard/jadmin.tsx new file mode 100644 index 000000000..cb7d446c4 --- /dev/null +++ b/packages/web/pages/dashboard/jadmin.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { NextPage } from 'next' +import AuthGate from '@/components/AuthGate' +import JAdmin from '@/components/Dashboard/JAdmin' +import { withApollo } from '@/lib/apollo' +import DashboardLayout from '@/components/Layouts/DashboardLayout' + +type InitialProps = { + namespacesRequired: string[] +} + +const JAdminPage: NextPage = () => { + return ( + <> + + + + + ) +} + +JAdminPage.getInitialProps = async (ctx) => { + return { + namespacesRequired: ['common'], + } +} + +export default withApollo(JAdminPage) diff --git a/packages/web/pages/dashboard/my-feed.tsx b/packages/web/pages/dashboard/my-feed.tsx index 2230d4239..1514a4f3d 100644 --- a/packages/web/pages/dashboard/my-feed.tsx +++ b/packages/web/pages/dashboard/my-feed.tsx @@ -9,7 +9,7 @@ import AuthGate from '@/components/AuthGate' import WelcomeModal from '@/components/Modals/WelcomeModal' import { InitialSearchFilters } from '@/components/Dashboard/MyFeed' -interface InitialProps { +type InitialProps = { namespacesRequired: string[] initialSearchFilters: InitialSearchFilters | null } From 07921eaa9c3f7c156b1f4e7d70cae22259e51355 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Wed, 22 Dec 2021 17:32:08 +0000 Subject: [PATCH 2/3] Add user names to seed data --- packages/web/seed.sql | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/packages/web/seed.sql b/packages/web/seed.sql index 3629f4043..c7a34505c 100644 --- a/packages/web/seed.sql +++ b/packages/web/seed.sql @@ -2310,17 +2310,17 @@ COPY public."TopicTranslation" (id, "uiLanguage", name, "topicId") FROM stdin; -- COPY public."User" (id, name, email, handle, "userRole", bio, "createdAt", "updatedAt", "profileImage", city, country, "stripeCustomerId", "moosendSubscriberId", "lastFourCardNumbers", "cardBrand", "isStudent") FROM stdin; -2 \N jon@arryn.net HandyMan MODERATOR \N 2021-07-28 21:29:23.535 2021-07-28 21:29:24.175 \N \N \N \N 9b525bfd-a224-4816-b406-209e1f33f216 \N \N f -4 \N gold@gold.gold TheLannyster USER \N 2021-07-28 23:21:51.833 2021-11-19 14:24:42.16 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/d4876a80-b5bf-4401-994a-e6ffaad1b9c3-large \N \N \N 45b510f8-e131-49c5-97f8-c3b2831ab243 \N \N f -3 \N j@n.com jsno USER \N 2021-07-28 21:31:53.623 2021-11-19 14:29:24.769 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/6758b20c-6d0b-44b5-83b5-2f59f8368305-large \N \N cus_JwCmGeU4Hqiwmn a010a2ec-6266-42c0-9a54-ce4ee81060cb NONE visa f -5 \N s@ndor.com the_bloody_hound USER \N 2021-11-19 14:30:10.529 2021-11-19 14:34:06.992 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/3b0de0b8-d0a2-4dd9-b20a-356cc5d43641-large \N \N \N \N \N \N f -1 \N robert@baratheon.net RobertBaratheon ADMIN \N 2021-07-28 21:25:42.74 2021-11-19 14:54:49.584 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/fe19cc0a-8661-4a0c-9005-413f1981491b-large \N \N cus_JwCnTV2oSgERHO 0b6fe694-9298-4aef-b7af-895329b60ed9 NONE visa f -6 \N petyr@baelish.co.uk little_finger USER \N 2021-11-19 14:56:34.037 2021-11-19 14:59:11.785 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/5d914d2b-44f4-43df-bb5f-9a772b7b43dd-large \N \N \N \N \N \N f -7 arya@harvard.edu faceless_one USER Now you see me... Now you don't. 2021-11-19 15:02:26.403 2021-11-19 15:05:04.285 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/ab7f791a-309e-4367-b10e-ca047cc5c30d-large Winterfell \N \N \N \N f -8 ned@stark.coffee true_stark USER The Man Who Passes The Sentence Should Swing The Sword. Oh and, for God's sake, winter is coming! 2021-11-19 15:11:09.472 2021-11-19 15:14:01.098 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/12d2f0ec-02f3-4a80-be21-e5f11f9593b9-large Winterfell \N \N \N \N f -9 tyrion@stark.com smartest_man_in_westeros USER I really am the smartest man in Westeros, and also the most modest! 2021-11-19 15:19:48.545 2021-11-19 15:22:27.069 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/482cfb85-907e-431b-a01f-0fb429b2f5ab-large Casterly Rock \N \N \N \N f -10 \N drogon@fire.com drogon USER \N 2021-11-19 18:55:34.072 2021-11-19 19:00:29.249 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/14ed7bec-9016-4c14-9b65-edce13bdf9f1-large \N \N \N \N \N \N f -11 \N rhaegal@fire.com rhaegal_1991 USER \N 2021-11-19 19:09:38.651 2021-11-19 19:10:01.511 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/e45bd6bc-a92a-410e-b02c-aa3fc9699fab-large \N \N \N \N \N \N f +4 Tywin Lannister gold@gold.gold TheLannyster USER \N 2021-07-28 23:21:51.833 2021-12-22 17:13:58.482 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/d4876a80-b5bf-4401-994a-e6ffaad1b9c3-large \N 45b510f8-e131-49c5-97f8-c3b2831ab243 \N \N f +1 Robert Baratheon robert@baratheon.net RobertBaratheon ADMIN \N 2021-07-28 21:25:42.74 2021-12-22 17:14:48.908 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/fe19cc0a-8661-4a0c-9005-413f1981491b-large cus_JwCnTV2oSgERHO 0b6fe694-9298-4aef-b7af-895329b60ed9 NONE visa f +2 Jon Arryn jon@arryn.net HandyMan MODERATOR \N 2021-07-28 21:29:23.535 2021-12-22 17:15:27.68 \N \N 9b525bfd-a224-4816-b406-209e1f33f216 \N \N f +5 Sandor Clegane s@ndor.com the_bloody_hound USER \N 2021-11-19 14:30:10.529 2021-12-22 17:16:24.538 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/3b0de0b8-d0a2-4dd9-b20a-356cc5d43641-large \N \N \N \N f +6 Petyr Baelish petyr@baelish.co.uk little_finger USER \N 2021-11-19 14:56:34.037 2021-12-22 17:16:53.677 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/5d914d2b-44f4-43df-bb5f-9a772b7b43dd-large \N \N \N \N f +7 Arya Stark arya@harvard.edu faceless_one USER Now you see me... Now you don't. 2021-11-19 15:02:26.403 2021-12-22 17:17:20.797 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/ab7f791a-309e-4367-b10e-ca047cc5c30d-large Winterfell \N \N \N \N t +8 Ned Stark ned@stark.coffee true_stark USER The Man Who Passes The Sentence Should Swing The Sword. Oh and, for God's sake, winter is coming! 2021-11-19 15:11:09.472 2021-12-22 17:17:39.673 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/12d2f0ec-02f3-4a80-be21-e5f11f9593b9-large Winterfell \N \N \N \N f +9 Tyrion Lannister tyrion@stark.com smartest_man_in_westeros USER I really am the smartest man in Westeros, and also the most modest! 2021-11-19 15:19:48.545 2021-12-22 17:17:59.321 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/482cfb85-907e-431b-a01f-0fb429b2f5ab-large Casterly Rock \N \N \N \N f +10 Drogon drogon@fire.com drogon USER \N 2021-11-19 18:55:34.072 2021-12-22 17:18:20.029 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/14ed7bec-9016-4c14-9b65-edce13bdf9f1-large \N \N \N \N f +11 Rhaegal rhaegal@fire.com rhaegal_1991 USER \N 2021-11-19 19:09:38.651 2021-12-22 17:18:41.607 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/e45bd6bc-a92a-410e-b02c-aa3fc9699fab-large \N \N \N \N f +3 Jon Snow j@n.com jsno ADMIN \N 2021-07-28 21:31:53.623 2021-12-22 17:12:37.75 https://d1tjf7rcyn4hkr.cloudfront.net/avatar-image/6758b20c-6d0b-44b5-83b5-2f59f8368305-large cus_JwCmGeU4Hqiwmn a010a2ec-6266-42c0-9a54-ce4ee81060cb NONE visa f \. From a07c478ef5e2b3ecfe17671758c7892aaf2e0a93 Mon Sep 17 00:00:00 2001 From: Robin MacPherson Date: Thu, 23 Dec 2021 13:53:52 +0000 Subject: [PATCH 3/3] Build out page for managing user posts --- .../components/Dashboard/JAdmin/JAdmin.tsx | 11 ++ .../Dashboard/JAdmin/JAdminPosts.tsx | 130 ++++++++++++++++++ .../{jadmin.tsx => jadmin/index.tsx} | 4 +- packages/web/pages/dashboard/jadmin/posts.tsx | 28 ++++ packages/web/resolvers/user.ts | 25 ++-- 5 files changed, 187 insertions(+), 11 deletions(-) create mode 100644 packages/web/components/Dashboard/JAdmin/JAdminPosts.tsx rename packages/web/pages/dashboard/{jadmin.tsx => jadmin/index.tsx} (85%) create mode 100644 packages/web/pages/dashboard/jadmin/posts.tsx diff --git a/packages/web/components/Dashboard/JAdmin/JAdmin.tsx b/packages/web/components/Dashboard/JAdmin/JAdmin.tsx index 7327934ab..702285a81 100644 --- a/packages/web/components/Dashboard/JAdmin/JAdmin.tsx +++ b/packages/web/components/Dashboard/JAdmin/JAdmin.tsx @@ -1,4 +1,5 @@ import React from 'react' +import Link from 'next/link' import theme from '@/theme' import { useUsersQuery, UserWithLanguagesFragmentFragment as UserType } from '@/generated/graphql' import Button, { ButtonVariant } from '@/components/Button' @@ -10,6 +11,11 @@ type UserInfoProps = { const UserInfo: React.FC = ({ user }) => { return ( + + + {user.id} + + {user.handle} {user.name} {user.email} @@ -35,6 +41,10 @@ const UserInfo: React.FC = ({ user }) => { td:last-child :global(.user-action-btn) { width: 100%; } + + .id-col { + text-align: center; + } `} ) @@ -50,6 +60,7 @@ const JAdmin = () => { + diff --git a/packages/web/components/Dashboard/JAdmin/JAdminPosts.tsx b/packages/web/components/Dashboard/JAdmin/JAdminPosts.tsx new file mode 100644 index 000000000..d11546468 --- /dev/null +++ b/packages/web/components/Dashboard/JAdmin/JAdminPosts.tsx @@ -0,0 +1,130 @@ +import React from 'react' +import theme from '@/theme' +import { + PostCardFragmentFragment as PostType, + usePostsQuery, + PostStatus, + useUserByIdQuery, +} from '@/generated/graphql' +import Button, { ButtonVariant } from '@/components/Button' +import { useRouter } from 'next/router' + +type PostInfoProps = { + post: PostType +} + +const PostInfo: React.FC = ({ post }) => { + return ( + + + + + + + + ) +} + +const JAdminPosts = () => { + const router = useRouter() + const authorIdParam = router.query.id as string + const authorId = parseInt(authorIdParam, 10) + + const { data: authorData } = useUserByIdQuery({ + variables: { + id: authorId, + }, + }) + + const { data } = usePostsQuery({ + variables: { + first: 10, + skip: 0, + authorId, + status: PostStatus.Published, + }, + }) + + return ( +
+
+

Manage Posts By: {authorData?.userById.handle}

+
ID Handle Name Email
{post.id}{post.title}{post.createdAt} + +
+ + + + + + + + + + {data?.posts && data.posts.posts.map((post) => )} + +
IDTitleCreated At⬇︎
+ + + + ) +} + +export default JAdminPosts diff --git a/packages/web/pages/dashboard/jadmin.tsx b/packages/web/pages/dashboard/jadmin/index.tsx similarity index 85% rename from packages/web/pages/dashboard/jadmin.tsx rename to packages/web/pages/dashboard/jadmin/index.tsx index cb7d446c4..6f71b3ab0 100644 --- a/packages/web/pages/dashboard/jadmin.tsx +++ b/packages/web/pages/dashboard/jadmin/index.tsx @@ -1,6 +1,6 @@ import React from 'react' import { NextPage } from 'next' -import AuthGate from '@/components/AuthGate' +// import AuthGate from '@/components/AuthGate' import JAdmin from '@/components/Dashboard/JAdmin' import { withApollo } from '@/lib/apollo' import DashboardLayout from '@/components/Layouts/DashboardLayout' @@ -19,7 +19,7 @@ const JAdminPage: NextPage = () => { ) } -JAdminPage.getInitialProps = async (ctx) => { +JAdminPage.getInitialProps = async () => { return { namespacesRequired: ['common'], } diff --git a/packages/web/pages/dashboard/jadmin/posts.tsx b/packages/web/pages/dashboard/jadmin/posts.tsx new file mode 100644 index 000000000..a32166108 --- /dev/null +++ b/packages/web/pages/dashboard/jadmin/posts.tsx @@ -0,0 +1,28 @@ +import React from 'react' +import { NextPage } from 'next' +// import AuthGate from '@/components/AuthGate' +import JAdminPosts from '@/components/Dashboard/JAdmin/JAdminPosts' +import { withApollo } from '@/lib/apollo' +import DashboardLayout from '@/components/Layouts/DashboardLayout' + +type InitialProps = { + namespacesRequired: string[] +} + +const JAdminPostsPage: NextPage = () => { + return ( + <> + + + + + ) +} + +JAdminPostsPage.getInitialProps = async () => { + return { + namespacesRequired: ['common'], + } +} + +export default withApollo(JAdminPostsPage) diff --git a/packages/web/resolvers/user.ts b/packages/web/resolvers/user.ts index e3ba5ad4b..eaad50f25 100644 --- a/packages/web/resolvers/user.ts +++ b/packages/web/resolvers/user.ts @@ -12,6 +12,7 @@ import { PostStatus, EmailVerificationStatus, InAppNotificationType, + UserRole, } from '@journaly/j-db-client' import { NotAuthorizedError, UserInputError } from './errors' @@ -41,10 +42,19 @@ const User = objectType({ t.model.name() t.string('email', { nullable: true, - resolve(parent, _args, ctx) { + async resolve(parent, _args, ctx) { const { userId } = ctx.request - if (userId && userId === parent.id) { + const user = await ctx.db.user.findUnique({ + where: { + id: userId, + }, + }) + + if ( + (userId && userId === parent.id) || + user?.userRole === (UserRole.ADMIN || UserRole.MODERATOR) + ) { return parent.email } @@ -147,15 +157,12 @@ const User = objectType({ return ctx.db.inAppNotification.findMany({ where: { - userId: userId + userId: userId, }, take: 99, - orderBy: [ - { readStatus: 'desc' }, - { bumpedAt: 'desc' }, - ] + orderBy: [{ readStatus: 'desc' }, { bumpedAt: 'desc' }], }) - } + }, }) t.list.field('activityGraphData', { type: 'DatedActivityCount', @@ -209,7 +216,7 @@ const User = objectType({ ON post_activity.date = post_comment_activity.date ; ` - return stats as any || [] + return (stats as any) || [] }, }) },