[Permissions][FE] Design followup 5 (#12793)

## Context
- We now display workspace member full name and email in role assignment
picker
- Replaced Forbidden by "Not shared" with lock icon
- Fix Disabled for Danger accent
- Fix avatar URL

<img width="575" alt="Screenshot 2025-06-23 at 16 38 56"
src="https://github.com/user-attachments/assets/08430bfe-29c4-4ac4-821c-9062dfad7150"
/>
<img width="756" alt="Screenshot 2025-06-23 at 16 21 36"
src="https://github.com/user-attachments/assets/c19f31bd-fe9d-415d-aa55-62fa3e228c49"
/>
<img width="373" alt="Screenshot 2025-06-23 at 17 13 08"
src="https://github.com/user-attachments/assets/e2f7878c-7c5a-40b4-a482-8e99292257c3"
/>
<img width="342" alt="Screenshot 2025-06-23 at 17 37 49"
src="https://github.com/user-attachments/assets/04169e85-14dd-4aed-bd71-7aefd601a894"
/>
<img width="434" alt="Screenshot 2025-06-23 at 17 37 35"
src="https://github.com/user-attachments/assets/7caf0967-c4dd-4d0f-90c8-259a85182b19"
/>
This commit is contained in:
Weiko
2025-06-23 19:15:58 +02:00
committed by GitHub
parent f05da75bb5
commit 85c50f149d
16 changed files with 70 additions and 45 deletions

View File

@ -30,6 +30,7 @@ const mockWorkspaceMember = {
lastName: 'Doe',
},
colorScheme: 'Light' as const,
userEmail: 'userEmail',
};
const mockWorkspace = {
@ -200,6 +201,7 @@ describe('ApolloFactory', () => {
lastName: 'Doe',
},
colorScheme: 'Light' as const,
userEmail: 'userEmail',
};
apolloFactory.updateWorkspaceMember(newWorkspaceMember);

View File

@ -3,7 +3,7 @@ import { createState } from 'twenty-ui/utilities';
export type CurrentWorkspaceMember = Omit<
WorkspaceMember,
'createdAt' | 'updatedAt' | 'userId' | 'userEmail' | '__typename'
'createdAt' | 'updatedAt' | 'userId' | '__typename'
>;
export const currentWorkspaceMemberState =

View File

@ -1012,4 +1012,5 @@ export const mockWorkspaceMember = {
createdAt: '',
updatedAt: '',
userId: '1',
userEmail: 'userEmail',
};

View File

@ -43,6 +43,7 @@ describe('useFindManyRecords', () => {
name: { firstName: 'John', lastName: 'Connor' },
locale: 'en',
colorScheme: 'Light',
userEmail: 'userEmail',
});
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);

View File

@ -1,19 +1,20 @@
import { Theme, useTheme } from '@emotion/react';
import styled from '@emotion/styled';
import { Trans } from '@lingui/react/macro';
import { IconLock } from 'twenty-ui/display';
const StyledContainer = styled.div<{ theme: Theme }>`
align-items: center;
display: flex;
display: inline-flex;
background: ${({ theme }) => theme.background.transparent.lighter};
background: ${({ theme }) => theme.background.transparent.light};
color: ${({ theme }) => theme.font.color.tertiary};
font-weight: ${({ theme }) => theme.font.weight.regular};
font-size: ${({ theme }) => theme.font.size.sm};
padding: ${({ theme }) => theme.spacing(1, 2)};
font-size: ${({ theme }) => theme.font.size.md};
padding: ${({ theme }) => theme.spacing(1)};
gap: ${({ theme }) => theme.spacing(1)};
border-radius: 4px;
border: 1px solid ${({ theme }) => theme.border.color.light};
`;
export const ForbiddenFieldDisplay = () => {
@ -21,7 +22,8 @@ export const ForbiddenFieldDisplay = () => {
return (
<StyledContainer theme={theme}>
<Trans>Forbidden</Trans>
<IconLock size={theme.icon.size.sm} />
<Trans>Not shared</Trans>
</StyledContainer>
);
};

View File

@ -229,6 +229,7 @@ describe('buildValueFromFilter', () => {
dateFormat: null,
timeFormat: null,
timeZone: null,
userEmail: 'userEmail',
};
const testCases = [

View File

@ -74,6 +74,7 @@ describe('useFilteredSearchRecordQuery', () => {
name: { firstName: 'John', lastName: 'Connor' },
locale: 'en',
colorScheme: 'Light',
userEmail: 'userEmail',
});
const setMetadataItems = useSetRecoilState(objectMetadataItemsState);

View File

@ -6,6 +6,7 @@ import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useLingui } from '@lingui/react/macro';
import { ChangeEvent, useState } from 'react';
@ -38,7 +39,7 @@ export const SettingsRoleAssignmentWorkspaceMemberPickerDropdown = ({
const { t } = useLingui();
return (
<DropdownContent>
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuSearchInput
value={searchFilter}
onChange={handleSearchFilterChange}

View File

@ -39,20 +39,25 @@ export const SettingsRoleAssignmentWorkspaceMemberPickerDropdownContent = ({
return (
<>
{enrichedWorkspaceMembers.map((workspaceMember) => (
<MenuItemAvatar
key={workspaceMember.id}
onClick={() => onSelect(workspaceMember)}
avatar={{
type: 'rounded',
size: 'md',
placeholder: workspaceMember?.name.firstName ?? '',
placeholderColorSeed: workspaceMember?.id,
avatarUrl: workspaceMember?.avatarUrl,
}}
text={workspaceMember?.name.firstName ?? ''}
/>
))}
{enrichedWorkspaceMembers.map((workspaceMember) => {
const workspaceMemberFullName = `${workspaceMember?.name.firstName ?? ''} ${workspaceMember?.name.lastName ?? ''}`;
return (
<MenuItemAvatar
key={workspaceMember.id}
onClick={() => onSelect(workspaceMember)}
avatar={{
type: 'rounded',
size: 'md',
placeholder: workspaceMemberFullName,
placeholderColorSeed: workspaceMember.id,
avatarUrl: workspaceMember.avatarUrl,
}}
text={workspaceMemberFullName}
contextualText={workspaceMember.userEmail}
/>
);
})}
</>
);
};

View File

@ -10,6 +10,7 @@ import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/Drop
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSectionLabel } from '@/ui/layout/dropdown/components/DropdownMenuSectionLabel';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { ScrollWrapper } from '@/ui/utilities/scroll/components/ScrollWrapper';
import styled from '@emotion/styled';
import { useLingui } from '@lingui/react/macro';
@ -77,7 +78,7 @@ export const MatchColumnSelectFieldSelectDropdownContent = ({
const { t } = useLingui();
return (
<DropdownContent widthInPixels={320}>
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent

View File

@ -10,6 +10,7 @@ import { DropdownMenuHeaderLeftComponent } from '@/ui/layout/dropdown/components
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
import { DropdownMenuSearchInput } from '@/ui/layout/dropdown/components/DropdownMenuSearchInput';
import { DropdownMenuSeparator } from '@/ui/layout/dropdown/components/DropdownMenuSeparator';
import { GenericDropdownContentWidth } from '@/ui/layout/dropdown/constants/GenericDropdownContentWidth';
import { useState } from 'react';
import { isDefined } from 'twenty-shared/utils';
import {
@ -78,7 +79,7 @@ export const MatchColumnSelectSubFieldSelectDropdownContent = ({
);
return (
<DropdownContent widthInPixels={320}>
<DropdownContent widthInPixels={GenericDropdownContentWidth.ExtraLarge}>
<DropdownMenuHeader
StartComponent={
<DropdownMenuHeaderLeftComponent

View File

@ -15,7 +15,7 @@ jest.mock('@/object-record/hooks/useUpdateOneRecord', () => ({
const workspaceMember: Omit<
WorkspaceMember,
'createdAt' | 'updatedAt' | 'userId' | 'userEmail'
'createdAt' | 'updatedAt' | 'userId'
> = {
__typename: 'WorkspaceMember',
id: 'id',
@ -25,6 +25,7 @@ const workspaceMember: Omit<
},
locale: 'en',
colorScheme: 'System',
userEmail: 'userEmail',
};
describe('useColorScheme', () => {

View File

@ -45,6 +45,7 @@ export const mockCurrentWorkspaceMembers: CurrentWorkspaceMember[] =
dateFormat,
timeFormat,
timeZone,
userEmail,
}) => ({
id,
locale,
@ -54,5 +55,6 @@ export const mockCurrentWorkspaceMembers: CurrentWorkspaceMember[] =
dateFormat,
timeFormat,
timeZone,
userEmail,
}),
);