From 4e1887472da2bb66243416e075082f5f537a59fa Mon Sep 17 00:00:00 2001 From: Joshen Lim Date: Wed, 12 Jul 2023 11:54:01 +0800 Subject: [PATCH] Small refactors after testing org team settings --- .../Organization/TeamSettings/MembersView.tsx | 447 +++++++++--------- .../TeamSettings/TeamSettings.tsx | 5 +- .../organization-detail-query.ts | 4 +- ...anization-member-invite-delete-mutation.ts | 10 +- .../organizations/organization-roles-query.ts | 4 +- studio/types/base.ts | 4 +- 6 files changed, 246 insertions(+), 228 deletions(-) diff --git a/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx b/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx index d6002c16996..f4333c9e7fd 100644 --- a/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx +++ b/studio/components/interfaces/Organization/TeamSettings/MembersView.tsx @@ -5,7 +5,8 @@ import { Fragment, useState } from 'react' import { useParams } from 'common/hooks' import Table from 'components/to-be-cleaned/Table' -import ShimmeringLoader from 'components/ui/ShimmeringLoader' +import AlertError from 'components/ui/AlertError' +import { GenericSkeletonLoader } from 'components/ui/ShimmeringLoader' import { useOrganizationDetailQuery } from 'data/organizations/organization-detail-query' import { useOrganizationMemberUpdateMutation } from 'data/organizations/organization-member-update-mutation' import { useOrganizationRolesQuery } from 'data/organizations/organization-roles-query' @@ -35,8 +36,19 @@ const MembersView = ({ searchString }: MembersViewProps) => { const { profile } = useProfile() const { data: permissions } = usePermissionsQuery() - const { data: detailData, isLoading: isLoadingOrgDetails } = useOrganizationDetailQuery({ slug }) - const { data: rolesData, isLoading: isLoadingRoles } = useOrganizationRolesQuery({ slug }) + const { + data: detailData, + error: detailError, + isLoading: isLoadingOrgDetails, + isError: isErrorOrgDetails, + isSuccess: isSuccessOrgDetails, + } = useOrganizationDetailQuery({ slug }) + const { + data: rolesData, + error: rolesError, + isLoading: isLoadingRoles, + isError: isErrorRoles, + } = useOrganizationRolesQuery({ slug }) const { mutateAsync: updateOrganizationMember, isLoading } = useOrganizationMemberUpdateMutation({ onSuccess() { ui.setNotification({ @@ -44,10 +56,12 @@ const MembersView = ({ searchString }: MembersViewProps) => { message: `Successfully updated role for ${getUserDisplayName(selectedMember)}`, }) }, - onError() { + onError(error) { ui.setNotification({ category: 'error', - message: `Failed to update role for ${getUserDisplayName(selectedMember)}`, + message: `Failed to update role for ${getUserDisplayName(selectedMember)}: ${ + error.message + }`, }) }, }) @@ -94,230 +108,233 @@ const MembersView = ({ searchString }: MembersViewProps) => { setUserRoleChangeModalVisible(false) } - if (isLoadingOrgDetails) { - return ( -
- - - -
- ) - } - return ( <> -
- - User, - , - - Role - - , - , - ]} - body={[ - ...filteredMembers.map((x: Member, i: number) => { - const [memberRoleId] = x.role_ids ?? [] - const role = (roles || []).find((role) => role.id === memberRoleId) - const memberIsUser = x.primary_email == profile?.primary_email - const memberIsPendingInvite = !!x.invited_id - const canRemoveRole = rolesRemovable.includes(memberRoleId) - const disableRoleEdit = !canRemoveRole || memberIsUser || memberIsPendingInvite - const isEmailUser = x.username === x.primary_email + {isLoadingOrgDetails && } - const validateSelectedRoleToChange = (roleId: any) => { - if (!role || role.id === roleId) return + {isErrorOrgDetails && ( + + )} - const selectedRole = (roles || []).find((role) => role.id === roleId) - const canAddRole = rolesAddable.includes(selectedRole?.id ?? -1) + {isErrorRoles && ( + + )} - if (!canAddRole) { - return ui.setNotification({ - category: 'error', - duration: 4000, - message: `You do not have permission to update this team member to ${ - selectedRole!.name - }`, - }) + {isSuccessOrgDetails && ( +
+ +
User, + , + + Role + + , + , + ]} + body={[ + ...filteredMembers.map((x: Member, i: number) => { + const [memberRoleId] = x.role_ids ?? [] + const role = (roles || []).find((role) => role.id === memberRoleId) + const memberIsUser = x.primary_email == profile?.primary_email + const memberIsPendingInvite = !!x.invited_id + const canRemoveRole = rolesRemovable.includes(memberRoleId) + const disableRoleEdit = !canRemoveRole || memberIsUser || memberIsPendingInvite + const isEmailUser = x.username === x.primary_email + + const validateSelectedRoleToChange = (roleId: any) => { + if (!role || role.id === roleId) return + + const selectedRole = (roles || []).find((role) => role.id === roleId) + const canAddRole = rolesAddable.includes(selectedRole?.id ?? -1) + + if (!canAddRole) { + return ui.setNotification({ + category: 'error', + duration: 4000, + message: `You do not have permission to update this team member to ${ + selectedRole!.name + }`, + }) + } + + setUserRoleChangeModalVisible(true) + setSelectedMember({ ...x, oldRoleId: role.id, newRoleId: roleId }) } - setUserRoleChangeModalVisible(true) - setSelectedMember({ ...x, oldRoleId: role.id, newRoleId: roleId }) - } - - return ( - - - -
-
- {x.invited_id ? ( - - - - ) : isEmailUser ? ( -
- -
- ) : ( - - )} + return ( + + + +
+
+ {x.invited_id ? ( + + + + ) : isEmailUser ? ( +
+ +
+ ) : ( + + )} +
+
+

{getUserDisplayName(x)}

+ {x.invited_id === undefined && ( +

{x.primary_email}

+ )} +
-
-

{getUserDisplayName(x)}

- {x.invited_id === undefined && ( -

{x.primary_email}

- )} -
-
- + - - {x.invited_id && x.invited_at && ( - - {isInviteExpired(x.invited_at) ? 'Expired' : 'Invited'} - - )} - + + {x.invited_id && x.invited_at && ( + + {isInviteExpired(x.invited_at) ? 'Expired' : 'Invited'} + + )} + - - {isLoadingRoles ? ( -
- -
- ) : role !== undefined ? ( - - - - {roles.map((r: any) => ( - - {r.name} - - ))} - - - {memberIsPendingInvite ? ( - - - -
- - Role can only be changed after the user has accepted the - invite - -
-
-
- ) : !memberIsUser && !canRemoveRole ? ( - - - -
- - You need additional permissions to manage this team member - -
-
-
- ) : ( - <> - )} -
- ) : ( -
-

Invalid role

+ + {isLoadingRoles ? ( +
+ +
+ ) : role !== undefined ? ( - - + + + {roles.map((r: any) => ( + + {r.name} + + ))} + - - - -
- - This user has an invalid role, please reach out to us via - support - -
-
-
+ {memberIsPendingInvite ? ( + + + +
+ + Role can only be changed after the user has accepted the + invite + +
+
+
+ ) : !memberIsUser && !canRemoveRole ? ( + + + +
+ + You need additional permissions to manage this team member + +
+
+
+ ) : ( + <> + )}
+ ) : ( +
+

Invalid role

+ + + + + + + +
+ + This user has an invalid role, please reach out to us via + support + +
+
+
+
+
+ )} +
+ + {!memberIsUser && } + + + + ) + }), + ...(searchString.length > 0 && filteredMembers.length === 0 + ? [ + + +
+ +

+ No users matched the search query "{searchString}" +

- )} -
- - {!memberIsUser && } - -
- - ) - }), - ...(searchString.length > 0 && filteredMembers.length === 0 - ? [ - - -
- -

- No users matched the search query "{searchString}" -

-
-
-
, - ] - : []), - - -

- {searchString ? `${filteredMembers.length} of ` : ''} - {members.length || '0'} {members.length == 1 ? 'user' : 'users'} -

-
-
, - ]} - /> - -
+
+ , + ] + : []), + + +

+ {searchString ? `${filteredMembers.length} of ` : ''} + {members.length || '0'} {members.length == 1 ? 'user' : 'users'} +

+
+
, + ]} + /> + +
+ )} { @@ -133,7 +134,9 @@ const TeamSettings = () => { - + + + ) diff --git a/studio/data/organizations/organization-detail-query.ts b/studio/data/organizations/organization-detail-query.ts index 34c46d9e306..f07aa19e74d 100644 --- a/studio/data/organizations/organization-detail-query.ts +++ b/studio/data/organizations/organization-detail-query.ts @@ -2,7 +2,7 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query import { get } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' -import { Member } from 'types' +import { Member, ResponseError } from 'types' import { organizationKeys } from './keys' export type OrganizationDetailVariables = { @@ -56,7 +56,7 @@ export async function getOrganizationDetail( } export type OrganizationDetailData = Awaited> -export type OrganizationDetailError = unknown +export type OrganizationDetailError = ResponseError export const useOrganizationDetailQuery = ( { slug }: OrganizationDetailVariables, diff --git a/studio/data/organizations/organization-member-invite-delete-mutation.ts b/studio/data/organizations/organization-member-invite-delete-mutation.ts index 0ab1efd7bc0..374f3146d50 100644 --- a/studio/data/organizations/organization-member-invite-delete-mutation.ts +++ b/studio/data/organizations/organization-member-invite-delete-mutation.ts @@ -1,9 +1,10 @@ import { useMutation, UseMutationOptions, useQueryClient } from '@tanstack/react-query' +import { toast } from 'react-hot-toast' + import { delete_ } from 'lib/common/fetch' import { API_URL } from 'lib/constants' -import { organizationKeys } from './keys' -import { toast } from 'react-hot-toast' import { ResponseError } from 'types' +import { organizationKeys } from './keys' export type OrganizationMemberInviteDeleteVariables = { slug: string @@ -18,10 +19,7 @@ export async function deleteOrganizationMemberInvite({ `${API_URL}/organizations/${slug}/members/invite?invited_id=${invitedId}`, {} ) - if (response.error) { - throw response.error - } - + if (response.error) throw response.error return response } diff --git a/studio/data/organizations/organization-roles-query.ts b/studio/data/organizations/organization-roles-query.ts index 67618853e85..699eef8cd89 100644 --- a/studio/data/organizations/organization-roles-query.ts +++ b/studio/data/organizations/organization-roles-query.ts @@ -2,7 +2,7 @@ import { useQuery, useQueryClient, UseQueryOptions } from '@tanstack/react-query import { get } from 'lib/common/fetch' import { API_URL } from 'lib/constants' import { useCallback } from 'react' -import { Role } from 'types' +import { ResponseError, Role } from 'types' import { organizationKeys } from './keys' export type OrganizationRolesVariables = { @@ -32,7 +32,7 @@ export async function getOrganizationRoles( } export type OrganizationRolesData = Awaited> -export type OrganizationRolesError = unknown +export type OrganizationRolesError = ResponseError export const useOrganizationRolesQuery = ( { slug }: OrganizationRolesVariables, diff --git a/studio/types/base.ts b/studio/types/base.ts index 53bef164213..96d370f8242 100644 --- a/studio/types/base.ts +++ b/studio/types/base.ts @@ -89,7 +89,7 @@ export type SupaResponse = T & ResponseFailure export type SupaResponseV2 = T | ResponseFailure export interface ResponseError { - code: number + code?: number message: string - requestId: string + requestId?: string }