Feat/14410 move roles tab out of all roles (#14415)
# Move the roles tabs out of All Roles section #14410 **Description:** This is a **draft PR** opened for early feedback before final polishing. fixes(#14410) **Changes included:** - Moved the roles tabs out of the “All Roles” section as per the Figma design. - Added **search functionality** to the roles sections, following the Figma specification. - Updated layout and components to reflect the Figma design: [Figma link](https://www.figma.com/design/xt8O9mFeLl46C5InWwoMrN/Twenty?node-id=67064-202200&t=Z27CkXdNfG1vJMzG-11) **Notes for reviewers:** Please provide any **recommendations, feedback, or changes** before this is ready for final review. --- A **screen recording** demonstrating the functionality -> [Screencast from 2025-09-11 17-39-35.webm](https://github.com/user-attachments/assets/b1a0376e-8d12-414f-899e-83f35dd14842) This is my **first contribution**, so I opened a draft PR before polishing. Please recommend any changes and review if needed. --------- Co-authored-by: Abdul Rahman <ar5438376@gmail.com>
This commit is contained in:
co-authored by
Abdul Rahman
parent
e13db84f48
commit
469e7165fd
+32
-1
@@ -14,7 +14,10 @@ import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTab
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { H3Title } from 'twenty-ui/display';
|
||||
import { H3Title, IconUser, IconRobot, IconKey } from 'twenty-ui/display';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { TabList } from '@/ui/layout/tab-list/components/TabList';
|
||||
|
||||
export const SettingsRolesContainer = () => {
|
||||
const { t } = useLingui();
|
||||
@@ -25,6 +28,29 @@ export const SettingsRolesContainer = () => {
|
||||
);
|
||||
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
|
||||
const settingsRolesIsLoading = useRecoilValue(settingsRolesIsLoadingState);
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: ROLES_LIST_TABS.TABS_IDS.USER_ROLES,
|
||||
title: t`User Roles`,
|
||||
Icon: IconUser,
|
||||
},
|
||||
...(isAiEnabled
|
||||
? [
|
||||
{
|
||||
id: ROLES_LIST_TABS.TABS_IDS.AGENT_ROLES,
|
||||
title: t`Agent Roles`,
|
||||
Icon: IconRobot,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
id: ROLES_LIST_TABS.TABS_IDS.API_KEY_ROLES,
|
||||
title: t`API Key Roles`,
|
||||
Icon: IconKey,
|
||||
},
|
||||
];
|
||||
|
||||
if (settingsRolesIsLoading && !settingsAllRoles) {
|
||||
return null;
|
||||
@@ -42,6 +68,11 @@ export const SettingsRolesContainer = () => {
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<TabList
|
||||
tabs={tabs}
|
||||
className="tab-list"
|
||||
componentInstanceId={ROLES_LIST_TABS.COMPONENT_INSTANCE_ID}
|
||||
/>
|
||||
<SettingsRolesList />
|
||||
{activeTabId === ROLES_LIST_TABS.TABS_IDS.USER_ROLES && (
|
||||
<SettingsRoleDefaultRole roles={settingsAllRoles} />
|
||||
|
||||
@@ -6,25 +6,18 @@ import { SettingsRolesTableHeader } from '@/settings/roles/components/SettingsRo
|
||||
import { SettingsRolesTableRow } from '@/settings/roles/components/SettingsRolesTableRow';
|
||||
import { ROLES_LIST_TABS } from '@/settings/roles/constants/RolesListTabs';
|
||||
import { settingsAllRolesSelector } from '@/settings/roles/states/settingsAllRolesSelector';
|
||||
import { TabList } from '@/ui/layout/tab-list/components/TabList';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { SettingsPath } from 'twenty-shared/types';
|
||||
import {
|
||||
H2Title,
|
||||
IconKey,
|
||||
IconPlus,
|
||||
IconRobot,
|
||||
IconUser,
|
||||
} from 'twenty-ui/display';
|
||||
import { H2Title, IconPlus, IconSearch } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { sortByAscString } from '~/utils/array/sortByAscString';
|
||||
import { SettingsTextInput } from '@/ui/input/components/SettingsTextInput';
|
||||
import { useState } from 'react';
|
||||
|
||||
const StyledCreateRoleSection = styled(Section)`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@@ -43,6 +36,11 @@ const StyledNoRoles = styled(TableCell)`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
const StyledSearchInput = styled(SettingsTextInput)`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
width: 100%;
|
||||
`;
|
||||
|
||||
export const SettingsRolesList = () => {
|
||||
const navigateSettings = useNavigateSettings();
|
||||
const activeTabId = useRecoilComponentValue(
|
||||
@@ -50,60 +48,59 @@ export const SettingsRolesList = () => {
|
||||
ROLES_LIST_TABS.COMPONENT_INSTANCE_ID,
|
||||
);
|
||||
|
||||
const [searchTerm, setSearchTerm] = useState('');
|
||||
|
||||
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
|
||||
const isAiEnabled = useIsFeatureEnabled(FeatureFlagKey.IS_AI_ENABLED);
|
||||
|
||||
const sortedSettingsAllRoles = [...settingsAllRoles].sort((a, b) =>
|
||||
sortByAscString(a.label, b.label),
|
||||
);
|
||||
|
||||
const filteredRoles = sortedSettingsAllRoles.filter((role) => {
|
||||
let matchesTab = false;
|
||||
|
||||
switch (activeTabId) {
|
||||
case ROLES_LIST_TABS.TABS_IDS.USER_ROLES:
|
||||
return role.canBeAssignedToUsers;
|
||||
matchesTab = role.canBeAssignedToUsers;
|
||||
break;
|
||||
case ROLES_LIST_TABS.TABS_IDS.AGENT_ROLES:
|
||||
return role.canBeAssignedToAgents;
|
||||
matchesTab = role.canBeAssignedToAgents;
|
||||
break;
|
||||
case ROLES_LIST_TABS.TABS_IDS.API_KEY_ROLES:
|
||||
return role.canBeAssignedToApiKeys;
|
||||
matchesTab = role.canBeAssignedToApiKeys;
|
||||
break;
|
||||
default:
|
||||
return role.canBeAssignedToUsers;
|
||||
matchesTab = role.canBeAssignedToUsers;
|
||||
}
|
||||
|
||||
return (
|
||||
matchesTab && role.label?.toLowerCase().includes(searchTerm.toLowerCase())
|
||||
);
|
||||
});
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: ROLES_LIST_TABS.TABS_IDS.USER_ROLES,
|
||||
title: t`User Roles`,
|
||||
Icon: IconUser,
|
||||
},
|
||||
...(isAiEnabled
|
||||
? [
|
||||
{
|
||||
id: ROLES_LIST_TABS.TABS_IDS.AGENT_ROLES,
|
||||
title: t`Agent Roles`,
|
||||
Icon: IconRobot,
|
||||
},
|
||||
]
|
||||
: []),
|
||||
{
|
||||
id: ROLES_LIST_TABS.TABS_IDS.API_KEY_ROLES,
|
||||
title: t`API Key Roles`,
|
||||
Icon: IconKey,
|
||||
},
|
||||
];
|
||||
const tabDescriptions: Record<string, string> = {
|
||||
[ROLES_LIST_TABS.TABS_IDS.USER_ROLES]:
|
||||
t`Assign roles to specify each member's access permissions`,
|
||||
[ROLES_LIST_TABS.TABS_IDS.AGENT_ROLES]:
|
||||
t`Assign roles to specify each agent's access permissions`,
|
||||
[ROLES_LIST_TABS.TABS_IDS.API_KEY_ROLES]:
|
||||
t`Assign roles to specify each API key's access permissions`,
|
||||
};
|
||||
|
||||
const description = isAiEnabled
|
||||
? t`Manage roles and permissions for team members, agents, and API keys`
|
||||
: t`Manage roles and permissions for team members and API keys`;
|
||||
const description =
|
||||
(activeTabId && tabDescriptions[activeTabId]) ??
|
||||
t`Assign roles to specify each member's access permissions`;
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title title={t`All roles`} description={description} />
|
||||
|
||||
<TabList
|
||||
tabs={tabs}
|
||||
className="tab-list"
|
||||
componentInstanceId={ROLES_LIST_TABS.COMPONENT_INSTANCE_ID}
|
||||
<StyledSearchInput
|
||||
instanceId="settings-objects-search"
|
||||
LeftIcon={IconSearch}
|
||||
placeholder={t`Search a role...`}
|
||||
value={searchTerm}
|
||||
onChange={setSearchTerm}
|
||||
/>
|
||||
|
||||
<Table>
|
||||
|
||||
Reference in New Issue
Block a user