From 469e7165fd44c0eb2a1cbc66c7bbad1be4545fe2 Mon Sep 17 00:00:00 2001 From: Saurav Jain Date: Mon, 15 Sep 2025 00:43:52 +0530 Subject: [PATCH] Feat/14410 move roles tab out of all roles (#14415) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit # 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 --- .../components/SettingsRolesContainer.tsx | 33 +++++++- .../roles/components/SettingsRolesList.tsx | 83 +++++++++---------- 2 files changed, 72 insertions(+), 44 deletions(-) diff --git a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx index 8cef5dca32c..ba2e1396943 100644 --- a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx +++ b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesContainer.tsx @@ -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 = () => { ]} > + {activeTabId === ROLES_LIST_TABS.TABS_IDS.USER_ROLES && ( diff --git a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesList.tsx b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesList.tsx index 7c72d597e4c..3c494b3ff2f 100644 --- a/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesList.tsx +++ b/packages/twenty-front/src/modules/settings/roles/components/SettingsRolesList.tsx @@ -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 = { + [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 (
-