From 36d148d5e5c47747812a85c036994d7e3ed8f32e Mon Sep 17 00:00:00 2001 From: Weiko Date: Wed, 5 Feb 2025 14:22:00 +0100 Subject: [PATCH] Fetch roles in roles settings page (#10001) ## Context Following the addition of the new Roles page, we are now fetching roles from the DB thanks to this PR #9955 ## Test Screenshot 2025-02-04 at 14 46 21 --------- Co-authored-by: Lucas Bordeau --- .../src/generated-metadata/graphql.ts | 6 +- .../twenty-front/src/generated/graphql.tsx | 52 ++++- .../roles/graphql/queries/getRolesQuery.ts | 18 ++ .../pages/settings/roles/SettingsRoles.tsx | 190 ++++++++++++++---- .../engine/core-modules/core-engine.module.ts | 2 + .../permissions/permissions.service.ts | 2 +- .../metadata-modules/role/dtos/role.dto.ts | 2 +- 7 files changed, 221 insertions(+), 51 deletions(-) create mode 100644 packages/twenty-front/src/modules/settings/roles/graphql/queries/getRolesQuery.ts diff --git a/packages/twenty-front/src/generated-metadata/graphql.ts b/packages/twenty-front/src/generated-metadata/graphql.ts index 9dc6134b0..68eb319d0 100644 --- a/packages/twenty-front/src/generated-metadata/graphql.ts +++ b/packages/twenty-front/src/generated-metadata/graphql.ts @@ -1386,7 +1386,7 @@ export type Query = { getPostgresCredentials?: Maybe; getProductPrices: BillingProductPricesOutput; getPublicWorkspaceDataBySubdomain: PublicWorkspaceDataOutput; - getRoles: Array; + getRoles: Array; getServerlessFunctionSourceCode?: Maybe; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; @@ -1653,8 +1653,8 @@ export type ResendEmailVerificationTokenOutput = { success: Scalars['Boolean']['output']; }; -export type RoleDto = { - __typename?: 'RoleDTO'; +export type Role = { + __typename?: 'Role'; canUpdateAllSettings: Scalars['Boolean']['output']; description?: Maybe; id: Scalars['String']['output']; diff --git a/packages/twenty-front/src/generated/graphql.tsx b/packages/twenty-front/src/generated/graphql.tsx index 7c78f0be3..33e2a6d22 100644 --- a/packages/twenty-front/src/generated/graphql.tsx +++ b/packages/twenty-front/src/generated/graphql.tsx @@ -1250,7 +1250,7 @@ export type Query = { getPostgresCredentials?: Maybe; getProductPrices: BillingProductPricesOutput; getPublicWorkspaceDataBySubdomain: PublicWorkspaceDataOutput; - getRoles: Array; + getRoles: Array; getServerlessFunctionSourceCode?: Maybe; getTimelineCalendarEventsFromCompanyId: TimelineCalendarEventsWithTotal; getTimelineCalendarEventsFromPersonId: TimelineCalendarEventsWithTotal; @@ -1449,8 +1449,8 @@ export type ResendEmailVerificationTokenOutput = { success: Scalars['Boolean']; }; -export type RoleDto = { - __typename?: 'RoleDTO'; +export type Role = { + __typename?: 'Role'; canUpdateAllSettings: Scalars['Boolean']; description?: Maybe; id: Scalars['String']; @@ -2242,6 +2242,11 @@ export type UpdateLabPublicFeatureFlagMutationVariables = Exact<{ export type UpdateLabPublicFeatureFlagMutation = { __typename?: 'Mutation', updateLabPublicFeatureFlag: { __typename?: 'FeatureFlag', id: any, key: FeatureFlagKey, value: boolean } }; +export type GetRolesQueryVariables = Exact<{ [key: string]: never; }>; + + +export type GetRolesQuery = { __typename?: 'Query', getRoles: Array<{ __typename?: 'Role', id: string, label: string, description?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, workspaceMembers: Array<{ __typename?: 'WorkspaceMember', id: any, colorScheme: string, avatarUrl?: string | null, locale?: string | null, timeZone?: string | null, dateFormat?: WorkspaceMemberDateFormatEnum | null, timeFormat?: WorkspaceMemberTimeFormatEnum | null, name: { __typename?: 'FullName', firstName: string, lastName: string } }> }> }; + export type CreateOidcIdentityProviderMutationVariables = Exact<{ input: SetupOidcSsoInput; }>; @@ -3936,6 +3941,47 @@ export function useUpdateLabPublicFeatureFlagMutation(baseOptions?: Apollo.Mutat export type UpdateLabPublicFeatureFlagMutationHookResult = ReturnType; export type UpdateLabPublicFeatureFlagMutationResult = Apollo.MutationResult; export type UpdateLabPublicFeatureFlagMutationOptions = Apollo.BaseMutationOptions; +export const GetRolesDocument = gql` + query GetRoles { + getRoles { + id + label + description + canUpdateAllSettings + isEditable + workspaceMembers { + ...WorkspaceMemberQueryFragment + } + } +} + ${WorkspaceMemberQueryFragmentFragmentDoc}`; + +/** + * __useGetRolesQuery__ + * + * To run a query within a React component, call `useGetRolesQuery` and pass it any options that fit your needs. + * When your component renders, `useGetRolesQuery` returns an object from Apollo Client that contains loading, error, and data properties + * you can use to render your UI. + * + * @param baseOptions options that will be passed into the query, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options; + * + * @example + * const { data, loading, error } = useGetRolesQuery({ + * variables: { + * }, + * }); + */ +export function useGetRolesQuery(baseOptions?: Apollo.QueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useQuery(GetRolesDocument, options); + } +export function useGetRolesLazyQuery(baseOptions?: Apollo.LazyQueryHookOptions) { + const options = {...defaultOptions, ...baseOptions} + return Apollo.useLazyQuery(GetRolesDocument, options); + } +export type GetRolesQueryHookResult = ReturnType; +export type GetRolesLazyQueryHookResult = ReturnType; +export type GetRolesQueryResult = Apollo.QueryResult; export const CreateOidcIdentityProviderDocument = gql` mutation CreateOIDCIdentityProvider($input: SetupOIDCSsoInput!) { createOIDCIdentityProvider(input: $input) { diff --git a/packages/twenty-front/src/modules/settings/roles/graphql/queries/getRolesQuery.ts b/packages/twenty-front/src/modules/settings/roles/graphql/queries/getRolesQuery.ts new file mode 100644 index 000000000..9799fc81e --- /dev/null +++ b/packages/twenty-front/src/modules/settings/roles/graphql/queries/getRolesQuery.ts @@ -0,0 +1,18 @@ +import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment'; +import { gql } from '@apollo/client'; + +export const GET_ROLES = gql` + ${WORKSPACE_MEMBER_QUERY_FRAGMENT} + query GetRoles { + getRoles { + id + label + description + canUpdateAllSettings + isEditable + workspaceMembers { + ...WorkspaceMemberQueryFragment + } + } + } +`; diff --git a/packages/twenty-front/src/pages/settings/roles/SettingsRoles.tsx b/packages/twenty-front/src/pages/settings/roles/SettingsRoles.tsx index f9fafcbd8..775c26e16 100644 --- a/packages/twenty-front/src/pages/settings/roles/SettingsRoles.tsx +++ b/packages/twenty-front/src/pages/settings/roles/SettingsRoles.tsx @@ -1,20 +1,86 @@ import styled from '@emotion/styled'; import { Trans, useLingui } from '@lingui/react/macro'; -import { Button, H2Title, IconPlus, Section } from 'twenty-ui'; +import { + AppTooltip, + Avatar, + Button, + H2Title, + IconChevronRight, + IconLock, + IconPlus, + IconUser, + Section, + TooltipDelay, +} from 'twenty-ui'; import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer'; import { SettingsPath } from '@/types/SettingsPath'; import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer'; import { Table } from '@/ui/layout/table/components/Table'; +import { TableCell } from '@/ui/layout/table/components/TableCell'; import { TableHeader } from '@/ui/layout/table/components/TableHeader'; +import { TableRow } from '@/ui/layout/table/components/TableRow'; import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled'; -import { FeatureFlagKey } from '~/generated/graphql'; +import { useTheme } from '@emotion/react'; +import { FeatureFlagKey, useGetRolesQuery } from '~/generated/graphql'; import { getSettingsPath } from '~/utils/navigation/getSettingsPath'; -const StyledRoleTableRow = styled.div` +const StyledTable = styled(Table)` + margin-top: ${({ theme }) => theme.spacing(0.5)}; +`; + +const StyledTableRow = styled(TableRow)` + &:hover { + background: ${({ theme }) => theme.background.transparent.light}; + cursor: pointer; + } +`; + +const StyledNameCell = styled.div` align-items: center; - display: grid; - grid-template-columns: 1fr 1fr; + display: flex; + gap: ${({ theme }) => theme.spacing(2)}; +`; + +const StyledAssignedCell = styled.div` + align-items: center; + display: flex; + gap: ${({ theme }) => theme.spacing(1)}; +`; + +const StyledAvatarGroup = styled.div` + align-items: center; + display: flex; + margin-right: ${({ theme }) => theme.spacing(1)}; + + > * { + border: 2px solid ${({ theme }) => theme.background.primary}; + margin-left: -8px; + + &:first-of-type { + margin-left: 0; + } + } +`; + +const StyledTableHeaderRow = styled(Table)` + margin-bottom: ${({ theme }) => theme.spacing(1.5)}; +`; + +const StyledBottomSection = styled(Section)` + border-top: 1px solid ${({ theme }) => theme.border.color.light}; + margin-top: ${({ theme }) => theme.spacing(2)}; + padding-top: ${({ theme }) => theme.spacing(4)}; + display: flex; + justify-content: flex-end; +`; + +const StyledIconChevronRight = styled(IconChevronRight)` + color: ${({ theme }) => theme.font.color.tertiary}; +`; + +const StyledAvatarContainer = styled.div` + border: 0px; `; export const SettingsRoles = () => { @@ -22,22 +88,9 @@ export const SettingsRoles = () => { const isPermissionsEnabled = useIsFeatureEnabled( FeatureFlagKey.IsPermissionsEnabled, ); + const theme = useTheme(); - const GET_SETTINGS_ROLE_TABLE_METADATA = { - tableId: 'settingsRole', - fields: [ - { - fieldName: 'name', - fieldLabel: t`Name`, - align: 'left' as const, - }, - { - fieldName: 'assignedTo', - fieldLabel: t`Assigned to`, - align: 'left' as const, - }, - ], - }; + const { data: rolesData, loading: isRolesLoading } = useGetRolesQuery(); if (!isPermissionsEnabled) { return null; @@ -46,14 +99,6 @@ export const SettingsRoles = () => { return ( - } links={[ { children: Workspace, @@ -68,21 +113,80 @@ export const SettingsRoles = () => { title={t`All roles`} description={t`Assign roles to specify each member's access permissions`} /> - - - - {GET_SETTINGS_ROLE_TABLE_METADATA.fields.map( - (settingsRoleTableMetadataField) => ( - - {settingsRoleTableMetadataField.fieldLabel} - - ), - )} - -
+ + + + + Name + + + Assigned to + + + + + {!isRolesLoading && + rolesData?.getRoles.map((role) => ( + + + + + {role.label} + {!role.isEditable && ( + + )} + + + + + + {role.workspaceMembers + .slice(0, 5) + .map((workspaceMember) => ( + <> + + + + + + ))} + + {role.workspaceMembers.length} + + + + + + + ))} + + +