Add role edit page container (#10037)
## Context This PR adds a new SettingsRoleEdit page, the existing roles page now redirects to the role edition page when clicking on it. For now, we can't edit anything. Next step is to allow role assignment in the corresponding tab. <img width="941" alt="Screenshot 2025-02-05 at 17 16 14" src="https://github.com/user-attachments/assets/ca46de15-6237-4de6-88e1-2384a09d4a27" />
This commit is contained in:
@ -265,6 +265,12 @@ const SettingsRoles = lazy(() =>
|
||||
})),
|
||||
);
|
||||
|
||||
const SettingsRoleEdit = lazy(() =>
|
||||
import('~/pages/settings/roles/SettingsRoleEdit').then((module) => ({
|
||||
default: module.SettingsRoleEdit,
|
||||
})),
|
||||
);
|
||||
|
||||
type SettingsRoutesProps = {
|
||||
isBillingEnabled?: boolean;
|
||||
isFunctionSettingsEnabled?: boolean;
|
||||
@ -311,6 +317,7 @@ export const SettingsRoutes = ({
|
||||
/>
|
||||
<Route path={SettingsPath.NewObject} element={<SettingsNewObject />} />
|
||||
<Route path={SettingsPath.Roles} element={<SettingsRoles />} />
|
||||
<Route path={SettingsPath.RoleDetail} element={<SettingsRoleEdit />} />
|
||||
<Route path={SettingsPath.Developers} element={<SettingsDevelopers />} />
|
||||
<Route
|
||||
path={SettingsPath.DevelopersNewApiKey}
|
||||
|
||||
@ -37,4 +37,5 @@ export enum SettingsPath {
|
||||
FeatureFlags = 'admin-panel/feature-flags',
|
||||
Lab = 'lab',
|
||||
Roles = 'roles',
|
||||
RoleDetail = 'roles/:roleId',
|
||||
}
|
||||
|
||||
@ -0,0 +1,123 @@
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useEffect } from 'react';
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { H3Title, IconLockOpen, IconUser, IconUserPlus } from 'twenty-ui';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { TabList } from '@/ui/layout/tab/components/TabList';
|
||||
import { useTabList } from '@/ui/layout/tab/hooks/useTabList';
|
||||
import { useGetRolesQuery } from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { RolePermissions } from '~/pages/settings/roles/components/RolePermissions';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { RoleAssignment } from './components/RoleAssignment';
|
||||
|
||||
const StyledContentContainer = styled.div`
|
||||
flex: 1;
|
||||
width: 100%;
|
||||
padding-left: 0;
|
||||
`;
|
||||
|
||||
const StyledTitleContainer = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledIconUser = styled(IconUser)`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
`;
|
||||
|
||||
export const SETTINGS_ROLE_DETAIL_TABS = {
|
||||
COMPONENT_INSTANCE_ID: 'settings-role-detail-tabs',
|
||||
TABS_IDS: {
|
||||
ASSIGNMENT: 'assignment',
|
||||
PERMISSIONS: 'permissions',
|
||||
},
|
||||
} as const;
|
||||
|
||||
export const SettingsRoleEdit = () => {
|
||||
const { roleId = '' } = useParams();
|
||||
const { data: rolesData, loading: rolesLoading } = useGetRolesQuery();
|
||||
const navigateSettings = useNavigateSettings();
|
||||
|
||||
const role = rolesData?.getRoles.find((r) => r.id === roleId);
|
||||
|
||||
const { activeTabId } = useTabList(
|
||||
SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID,
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
if (!rolesLoading && !role) {
|
||||
navigateSettings(SettingsPath.Roles);
|
||||
}
|
||||
}, [role, navigateSettings, rolesLoading]);
|
||||
|
||||
if (!role) return null;
|
||||
|
||||
const tabs = [
|
||||
{
|
||||
id: SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT,
|
||||
title: t`Assignment`,
|
||||
Icon: IconUserPlus,
|
||||
hide: false,
|
||||
},
|
||||
{
|
||||
id: SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.PERMISSIONS,
|
||||
title: t`Permissions`,
|
||||
Icon: IconLockOpen,
|
||||
hide: false,
|
||||
},
|
||||
];
|
||||
|
||||
const renderActiveTabContent = () => {
|
||||
switch (activeTabId) {
|
||||
case SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.ASSIGNMENT:
|
||||
return <RoleAssignment role={role} />;
|
||||
case SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.PERMISSIONS:
|
||||
return <RolePermissions role={role} />;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<>
|
||||
<SubMenuTopBarContainer
|
||||
title={
|
||||
<StyledTitleContainer>
|
||||
<StyledIconUser size={16} />
|
||||
<H3Title title={role.label} />
|
||||
</StyledTitleContainer>
|
||||
}
|
||||
links={[
|
||||
{
|
||||
children: 'Workspace',
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{
|
||||
children: 'Roles',
|
||||
href: getSettingsPath(SettingsPath.Roles),
|
||||
},
|
||||
{
|
||||
children: role.label,
|
||||
},
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<TabList
|
||||
tabListInstanceId={SETTINGS_ROLE_DETAIL_TABS.COMPONENT_INSTANCE_ID}
|
||||
tabs={tabs}
|
||||
className="tab-list"
|
||||
/>
|
||||
<StyledContentContainer>
|
||||
{renderActiveTabContent()}
|
||||
</StyledContentContainer>
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
</>
|
||||
);
|
||||
};
|
||||
@ -23,6 +23,7 @@ import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import { FeatureFlagKey, useGetRolesQuery } from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
const StyledTable = styled(Table)`
|
||||
@ -89,6 +90,7 @@ export const SettingsRoles = () => {
|
||||
FeatureFlagKey.IsPermissionsEnabled,
|
||||
);
|
||||
const theme = useTheme();
|
||||
const navigateSettings = useNavigateSettings();
|
||||
|
||||
const { data: rolesData, loading: isRolesLoading } = useGetRolesQuery();
|
||||
|
||||
@ -96,6 +98,10 @@ export const SettingsRoles = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
const handleRoleClick = (roleId: string) => {
|
||||
navigateSettings(SettingsPath.RoleDetail, { roleId });
|
||||
};
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={t`Roles`}
|
||||
@ -127,7 +133,10 @@ export const SettingsRoles = () => {
|
||||
</StyledTableHeaderRow>
|
||||
{!isRolesLoading &&
|
||||
rolesData?.getRoles.map((role) => (
|
||||
<StyledTableRow key={role.id}>
|
||||
<StyledTableRow
|
||||
key={role.id}
|
||||
onClick={() => handleRoleClick(role.id)}
|
||||
>
|
||||
<TableCell>
|
||||
<StyledNameCell>
|
||||
<IconUser size={theme.icon.size.md} />
|
||||
|
||||
@ -0,0 +1,19 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title, Section } from 'twenty-ui';
|
||||
import { Role } from '~/generated-metadata/graphql';
|
||||
|
||||
type RoleAssignmentProps = {
|
||||
role: Pick<Role, 'id' | 'label' | 'canUpdateAllSettings'>;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
export const RoleAssignment = ({ role }: RoleAssignmentProps) => {
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Assigned members`}
|
||||
description={t`This Role is assigned to these workspace member.`}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@ -0,0 +1,19 @@
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { H2Title, Section } from 'twenty-ui';
|
||||
import { Role } from '~/generated-metadata/graphql';
|
||||
|
||||
type RolePermissionsProps = {
|
||||
role: Pick<Role, 'id' | 'label' | 'canUpdateAllSettings'>;
|
||||
};
|
||||
|
||||
// eslint-disable-next-line unused-imports/no-unused-vars
|
||||
export const RolePermissions = ({ role }: RolePermissionsProps) => {
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Permissions`}
|
||||
description={t`This Role has the following permissions.`}
|
||||
/>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
Reference in New Issue
Block a user