diff --git a/frontend/common/services/useGroup.ts b/frontend/common/services/useGroup.ts index 437767254dfd..525f7fd24169 100644 --- a/frontend/common/services/useGroup.ts +++ b/frontend/common/services/useGroup.ts @@ -3,7 +3,9 @@ import { Req } from 'common/types/requests' import { service } from 'common/service' export const groupService = service - .enhanceEndpoints({ addTagTypes: ['Group'] }) + .enhanceEndpoints({ + addTagTypes: ['Group', 'UserGroupPermission', 'GroupSummary'], + }) .injectEndpoints({ endpoints: (builder) => ({ createGroupAdmin: builder.mutation< @@ -18,7 +20,11 @@ export const groupService = service }), }), deleteGroup: builder.mutation({ - invalidatesTags: [{ id: 'LIST', type: 'Group' }], + invalidatesTags: [ + { id: 'LIST', type: 'Group' }, + { type: 'UserGroupPermission' }, + { type: 'GroupSummary' }, + ], query: (query: Req['deleteGroup']) => ({ body: query, method: 'DELETE', diff --git a/frontend/common/services/useUserGroupPermission.ts b/frontend/common/services/useUserGroupPermission.ts new file mode 100644 index 000000000000..29e245cd7f4f --- /dev/null +++ b/frontend/common/services/useUserGroupPermission.ts @@ -0,0 +1,47 @@ +import { Res } from 'common/types/responses' +import { Req } from 'common/types/requests' +import { service } from 'common/service' + +export const userGroupPermissionService = service + .enhanceEndpoints({ addTagTypes: ['UserGroupPermission'] }) + .injectEndpoints({ + endpoints: (builder) => ({ + getUserGroupPermission: builder.query< + Res['userGroupPermissions'], + Req['getUserGroupPermission'] + >({ + providesTags: (res) => [{ id: res?.id, type: 'UserGroupPermission' }], + query: (query: Req['getUserGroupPermission']) => ({ + url: `projects/${query.project_id}/user-group-permissions/`, + }), + }), + // END OF ENDPOINTS + }), + }) + +export async function getUserGroupPermission( + store: any, + data: Req['getUserGroupPermission'], + options?: Parameters< + typeof userGroupPermissionService.endpoints.getUserGroupPermission.initiate + >[1], +) { + return store.dispatch( + userGroupPermissionService.endpoints.getUserGroupPermission.initiate( + data, + options, + ), + ) +} +// END OF FUNCTION_EXPORTS + +export const { + useGetUserGroupPermissionQuery, + // END OF EXPORTS +} = userGroupPermissionService + +/* Usage examples: +const { data, isLoading } = useGetUserGroupPermissionQuery({ id: 2 }, {}) //get hook +const [createUserGroupPermission, { isLoading, data, isSuccess }] = useCreateUserGroupPermissionMutation() //create hook +userGroupPermissionService.endpoints.getUserGroupPermission.select({id: 2})(store.getState()) //access data from any function +*/ diff --git a/frontend/common/types/requests.ts b/frontend/common/types/requests.ts index e9df48617cad..9ab34e3dad31 100644 --- a/frontend/common/types/requests.ts +++ b/frontend/common/types/requests.ts @@ -256,5 +256,6 @@ export type Req = { id: string } getProject: { id: string } + getUserGroupPermission: { project_id: string } // END OF TYPES } diff --git a/frontend/common/types/responses.ts b/frontend/common/types/responses.ts index 512b3333fca1..42beae56fd81 100644 --- a/frontend/common/types/responses.ts +++ b/frontend/common/types/responses.ts @@ -486,5 +486,6 @@ export type Res = { flagsmithProjectImport: { id: string } featureImports: PagedResponse serversideEnvironmentKeys: APIKey[] + userGroupPermissions: GroupPermission[] // END OF TYPES } diff --git a/frontend/common/utils/utils.tsx b/frontend/common/utils/utils.tsx index 14fc9e24efe1..a6697d5fcbbc 100644 --- a/frontend/common/utils/utils.tsx +++ b/frontend/common/utils/utils.tsx @@ -18,6 +18,7 @@ import _ from 'lodash' import ErrorMessage from 'components/ErrorMessage' import WarningMessage from 'components/WarningMessage' import Constants from 'common/constants' +import Format from './format' const semver = require('semver') @@ -95,6 +96,7 @@ const Utils = Object.assign({}, require('./base/_utils'), { /> ) : null }, + escapeHtml(html: string) { const text = document.createTextNode(html) const p = document.createElement('p') @@ -255,6 +257,36 @@ const Utils = Object.assign({}, require('./base/_utils'), { getManageUserPermissionDescription() { return 'Manage Identities' }, + getPermissionList( + isAdmin: boolean, + permissions: string[] | undefined | null, + numberToTruncate = 3, + ): { + items: string[] + truncatedItems: string[] + } { + if (isAdmin) { + return { + items: [], + truncatedItems: ['Project Administrator'], + } + } + if (!permissions) return { items: [], truncatedItems: [] } + + const items = + permissions && permissions.length + ? permissions + .slice(0, numberToTruncate) + .map((item) => `${Format.enumeration.get(item)}`) + : [] + + return { + items, + truncatedItems: (permissions || []) + .slice(numberToTruncate) + .map((item) => `${Format.enumeration.get(item)}`), + } + }, getPlanName: (plan: string) => { if (plan && plan.includes('scale-up')) { return planNames.scaleUp diff --git a/frontend/web/components/EditPermissions.tsx b/frontend/web/components/EditPermissions.tsx index 6cb330b71740..bf9e4e1412ea 100644 --- a/frontend/web/components/EditPermissions.tsx +++ b/frontend/web/components/EditPermissions.tsx @@ -1032,6 +1032,7 @@ const EditPermissions: FC = (props) => { editGroupPermissions(group)} /> diff --git a/frontend/web/components/PermissionsSummaryList.tsx b/frontend/web/components/PermissionsSummaryList.tsx new file mode 100644 index 000000000000..7abb75b19c72 --- /dev/null +++ b/frontend/web/components/PermissionsSummaryList.tsx @@ -0,0 +1,42 @@ +import React, { FC } from 'react' +import Tooltip from './Tooltip' +import Utils from 'common/utils/utils' + +type PermissionsSummaryListType = { + isAdmin: boolean + permissions: string[] | null | undefined + numberToTruncate?: number +} + +const PermissionsSummaryList: FC = ({ + isAdmin, + numberToTruncate = 3, + permissions, +}) => { + const { items, truncatedItems } = Utils.getPermissionList( + isAdmin, + permissions, + numberToTruncate, + ) + + return ( +
+ {items.map((value: string, i: number) => ( +
+ {value} +
+ ))} + {!!truncatedItems.length && ( + +{truncatedItems.length} + } + > + {truncatedItems.join(', ')} + + )} +
+ ) +} + +export default PermissionsSummaryList diff --git a/frontend/web/components/UserGroupList.tsx b/frontend/web/components/UserGroupList.tsx index 03ddb536e5a6..6ab7e775f3e1 100644 --- a/frontend/web/components/UserGroupList.tsx +++ b/frontend/web/components/UserGroupList.tsx @@ -1,38 +1,49 @@ -import React, { FC, useState } from 'react' +import React, { FC, ReactNode, useState } from 'react' const CreateGroup = require('./modals/CreateGroup') import Button from './base/forms/Button' import AccountStore from 'common/stores/account-store' -import { UserGroup } from 'common/types/responses' -import { - useDeleteGroupMutation, - useGetGroupsQuery, -} from 'common/services/useGroup' +import { UserGroup, GroupPermission } from 'common/types/responses' +import { useDeleteGroupMutation } from 'common/services/useGroup' +import { useGetUserGroupPermissionQuery } from 'common/services/useUserGroupPermission' import PanelSearch from './PanelSearch' import { sortBy } from 'lodash' import InfoMessage from './InfoMessage' // we need this to make JSX compile import Icon from './Icon' import { useGetGroupSummariesQuery } from 'common/services/useGroupSummary' +import PermissionsSummaryList from './PermissionsSummaryList' const Panel = require('components/base/grid/Panel') type UserGroupsListType = { noTitle?: boolean orgId: string + projectId: string | boolean showRemove?: boolean onClick: (group: UserGroup) => void onEditPermissions?: (group: UserGroup) => void } -const UserGroupsList: FC = ({ - noTitle, +type UserGroupsRowType = { + id: string | number + index: number + name: string + permissionSummary?: ReactNode + group: UserGroup + orgId: string + showRemove?: boolean + onClick: (group: UserGroup) => void + onEditPermissions?: (group: UserGroup) => void +} +const UserGroupsRow: FC = ({ + group, + id, + index, + name, onClick, onEditPermissions, orgId, + permissionSummary, showRemove, }) => { - const [page, setPage] = useState(1) - const { data: userGroups, isLoading } = useGetGroupSummariesQuery({ - orgId: `${orgId}`, - }) const [deleteGroup] = useDeleteGroupMutation({}) const isAdmin = AccountStore.isAdmin() @@ -51,6 +62,112 @@ const UserGroupsList: FC = ({ yesText: 'Confirm', }) } + const _onClick = () => { + if (onClick) { + onClick(group) + } else { + openModal( + 'Edit Group', + , + 'side-modal', + ) + } + } + return ( + + +
{name}
+
+ {permissionSummary && {permissionSummary}} + + {onEditPermissions && isAdmin ? ( +
onEditPermissions(group)} + className='table-column' + > + +
+ ) : ( +
+ )} + {showRemove ? ( +
+ {isAdmin && ( + + )} +
+ ) : ( +
+ +
+ )} +
+ ) +} + +const UserGroupsList: FC = ({ + noTitle, + onClick, + onEditPermissions, + orgId, + projectId, + showRemove, +}) => { + const [page, setPage] = useState(1) + const { data: userGroups, isLoading } = useGetGroupSummariesQuery({ + orgId: `${orgId}`, + }) + const { data: userGroupsPermission, isLoading: userGroupPermissionLoading } = + useGetUserGroupPermissionQuery( + { + project_id: `${projectId}`, + }, + { + skip: !projectId, + }, + ) + + const mergeduserGroupsPermissionWithUserGroups = userGroupsPermission + ? [...userGroupsPermission] + : [] + + if (userGroupsPermission?.length > 0) { + userGroups?.forEach((group) => { + const existingPermissionIndex = + mergeduserGroupsPermissionWithUserGroups.findIndex( + (userGroupPermission) => userGroupPermission.group.id === group.id, + ) + if (existingPermissionIndex === -1) { + mergeduserGroupsPermissionWithUserGroups.push({ + admin: false, + group: group, + id: group.id, + permissions: [], + }) + } + }) + } return ( @@ -66,8 +183,12 @@ const UserGroupsList: FC = ({ title={noTitle ? '' : 'Groups'} className='no-pad' itemHeight={64} - items={sortBy(userGroups, 'name')} - paging={userGroups} + items={ + userGroupsPermission + ? sortBy(mergeduserGroupsPermissionWithUserGroups, 'group.name') + : sortBy(userGroups, 'name') + } + paging={mergeduserGroupsPermissionWithUserGroups || userGroups} nextPage={() => setPage(page + 1)} prevPage={() => setPage(page - 1)} goToPage={setPage} @@ -87,75 +208,49 @@ const UserGroupsList: FC = ({ ) } - renderRow={(group: UserGroup, index: number) => { - const { id, name, users } = group - const _onClick = () => { - if (onClick) { - onClick(group) - } else { - openModal( - 'Edit Group', - , - 'side-modal', - ) - } + renderRow={(group: UserGroup | GroupPermission, index: number) => { + if (userGroupsPermission) { + const { + admin, + group: userPermissionGroup, + permissions, + } = group as GroupPermission + return ( + + } + showRemove={showRemove} + /> + ) + } else { + const { id, name } = group as UserGroup + return ( + + ) } - return ( - - -
{name}
-
- - {onEditPermissions && isAdmin ? ( -
onEditPermissions(group)} - className='table-column' - > - -
- ) : ( -
- )} - {showRemove ? ( -
- {isAdmin && ( - - )} -
- ) : ( -
- -
- )} -
- ) }} - isLoading={isLoading} + isLoading={isLoading || userGroupPermissionLoading} renderNoResults={