Add delete role action (#12691)
## Context Add delete role action, the backend takes care of most of the operations (can't delete a default role, can't delete the admin role, re-assign existing members to default role...) <img width="592" alt="Screenshot 2025-06-17 at 20 24 21" src="https://github.com/user-attachments/assets/3f01f12c-d8a4-466c-b4c7-9674f597a7a8" /> <img width="567" alt="Screenshot 2025-06-17 at 20 24 24" src="https://github.com/user-attachments/assets/8aceaf6c-3082-4ca6-a4dd-9767fc186923" />
This commit is contained in:
@ -2862,6 +2862,13 @@ export type CreateOneRoleMutationVariables = Exact<{
|
|||||||
|
|
||||||
export type CreateOneRoleMutation = { __typename?: 'Mutation', createOneRole: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } };
|
export type CreateOneRoleMutation = { __typename?: 'Mutation', createOneRole: { __typename?: 'Role', id: string, label: string, description?: string | null, icon?: string | null, canUpdateAllSettings: boolean, isEditable: boolean, canReadAllObjectRecords: boolean, canUpdateAllObjectRecords: boolean, canSoftDeleteAllObjectRecords: boolean, canDestroyAllObjectRecords: boolean } };
|
||||||
|
|
||||||
|
export type DeleteOneRoleMutationVariables = Exact<{
|
||||||
|
roleId: Scalars['String'];
|
||||||
|
}>;
|
||||||
|
|
||||||
|
|
||||||
|
export type DeleteOneRoleMutation = { __typename?: 'Mutation', deleteOneRole: string };
|
||||||
|
|
||||||
export type UpdateOneRoleMutationVariables = Exact<{
|
export type UpdateOneRoleMutationVariables = Exact<{
|
||||||
updateRoleInput: UpdateRoleInput;
|
updateRoleInput: UpdateRoleInput;
|
||||||
}>;
|
}>;
|
||||||
@ -5389,6 +5396,37 @@ export function useCreateOneRoleMutation(baseOptions?: Apollo.MutationHookOption
|
|||||||
export type CreateOneRoleMutationHookResult = ReturnType<typeof useCreateOneRoleMutation>;
|
export type CreateOneRoleMutationHookResult = ReturnType<typeof useCreateOneRoleMutation>;
|
||||||
export type CreateOneRoleMutationResult = Apollo.MutationResult<CreateOneRoleMutation>;
|
export type CreateOneRoleMutationResult = Apollo.MutationResult<CreateOneRoleMutation>;
|
||||||
export type CreateOneRoleMutationOptions = Apollo.BaseMutationOptions<CreateOneRoleMutation, CreateOneRoleMutationVariables>;
|
export type CreateOneRoleMutationOptions = Apollo.BaseMutationOptions<CreateOneRoleMutation, CreateOneRoleMutationVariables>;
|
||||||
|
export const DeleteOneRoleDocument = gql`
|
||||||
|
mutation DeleteOneRole($roleId: String!) {
|
||||||
|
deleteOneRole(roleId: $roleId)
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
export type DeleteOneRoleMutationFn = Apollo.MutationFunction<DeleteOneRoleMutation, DeleteOneRoleMutationVariables>;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* __useDeleteOneRoleMutation__
|
||||||
|
*
|
||||||
|
* To run a mutation, you first call `useDeleteOneRoleMutation` within a React component and pass it any options that fit your needs.
|
||||||
|
* When your component renders, `useDeleteOneRoleMutation` returns a tuple that includes:
|
||||||
|
* - A mutate function that you can call at any time to execute the mutation
|
||||||
|
* - An object with fields that represent the current status of the mutation's execution
|
||||||
|
*
|
||||||
|
* @param baseOptions options that will be passed into the mutation, supported options are listed on: https://www.apollographql.com/docs/react/api/react-hooks/#options-2;
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const [deleteOneRoleMutation, { data, loading, error }] = useDeleteOneRoleMutation({
|
||||||
|
* variables: {
|
||||||
|
* roleId: // value for 'roleId'
|
||||||
|
* },
|
||||||
|
* });
|
||||||
|
*/
|
||||||
|
export function useDeleteOneRoleMutation(baseOptions?: Apollo.MutationHookOptions<DeleteOneRoleMutation, DeleteOneRoleMutationVariables>) {
|
||||||
|
const options = {...defaultOptions, ...baseOptions}
|
||||||
|
return Apollo.useMutation<DeleteOneRoleMutation, DeleteOneRoleMutationVariables>(DeleteOneRoleDocument, options);
|
||||||
|
}
|
||||||
|
export type DeleteOneRoleMutationHookResult = ReturnType<typeof useDeleteOneRoleMutation>;
|
||||||
|
export type DeleteOneRoleMutationResult = Apollo.MutationResult<DeleteOneRoleMutation>;
|
||||||
|
export type DeleteOneRoleMutationOptions = Apollo.BaseMutationOptions<DeleteOneRoleMutation, DeleteOneRoleMutationVariables>;
|
||||||
export const UpdateOneRoleDocument = gql`
|
export const UpdateOneRoleDocument = gql`
|
||||||
mutation UpdateOneRole($updateRoleInput: UpdateRoleInput!) {
|
mutation UpdateOneRole($updateRoleInput: UpdateRoleInput!) {
|
||||||
updateOneRole(updateRoleInput: $updateRoleInput) {
|
updateOneRole(updateRoleInput: $updateRoleInput) {
|
||||||
|
|||||||
@ -0,0 +1,7 @@
|
|||||||
|
import { gql } from '@apollo/client';
|
||||||
|
|
||||||
|
export const DELETE_ROLE = gql`
|
||||||
|
mutation DeleteOneRole($roleId: String!) {
|
||||||
|
deleteOneRole(roleId: $roleId)
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -14,6 +14,7 @@ import {
|
|||||||
IconKey,
|
IconKey,
|
||||||
IconLockOpen,
|
IconLockOpen,
|
||||||
IconSettings,
|
IconSettings,
|
||||||
|
IconSettingsAutomation,
|
||||||
IconUsers,
|
IconUsers,
|
||||||
} from 'twenty-ui/display';
|
} from 'twenty-ui/display';
|
||||||
import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout';
|
import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout';
|
||||||
@ -90,6 +91,12 @@ export const SettingsRolePermissionsSettingsSection = ({
|
|||||||
description: t`Manage security policies`,
|
description: t`Manage security policies`,
|
||||||
Icon: IconKey,
|
Icon: IconKey,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
key: SettingPermissionType.WORKFLOWS,
|
||||||
|
name: t`Workflows`,
|
||||||
|
description: t`Manage workflows`,
|
||||||
|
Icon: IconSettingsAutomation,
|
||||||
|
},
|
||||||
];
|
];
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
|||||||
@ -1,11 +1,16 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { t } from '@lingui/core/macro';
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { useRecoilState } from 'recoil';
|
||||||
|
|
||||||
|
import { ROLE_SETTINGS_DELETE_ROLE_CONFIRMATION_MODAL_ID } from '@/settings/roles/role-settings/components/constants/RoleSettingsDeleteRoleConfirmationModalId';
|
||||||
|
import { SettingsRoleSettingsDeleteRoleConfirmationModal } from '@/settings/roles/role-settings/components/SettingsRoleSettingsDeleteRoleConfirmationModal';
|
||||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
import { IconPicker } from '@/ui/input/components/IconPicker';
|
import { IconPicker } from '@/ui/input/components/IconPicker';
|
||||||
import { TextArea } from '@/ui/input/components/TextArea';
|
import { TextArea } from '@/ui/input/components/TextArea';
|
||||||
import { TextInput } from '@/ui/input/components/TextInput';
|
import { TextInput } from '@/ui/input/components/TextInput';
|
||||||
import { useRecoilState } from 'recoil';
|
import { useModal } from '@/ui/layout/modal/hooks/useModal';
|
||||||
|
import { H2Title } from 'twenty-ui/display';
|
||||||
|
import { Button } from 'twenty-ui/input';
|
||||||
import { Section } from 'twenty-ui/layout';
|
import { Section } from 'twenty-ui/layout';
|
||||||
|
|
||||||
const StyledInputsContainer = styled.div`
|
const StyledInputsContainer = styled.div`
|
||||||
@ -23,57 +28,85 @@ const StyledInputContainer = styled.div`
|
|||||||
type SettingsRoleSettingsProps = {
|
type SettingsRoleSettingsProps = {
|
||||||
roleId: string;
|
roleId: string;
|
||||||
isEditable: boolean;
|
isEditable: boolean;
|
||||||
|
isCreateMode: boolean;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const SettingsRoleSettings = ({
|
export const SettingsRoleSettings = ({
|
||||||
roleId,
|
roleId,
|
||||||
isEditable,
|
isEditable,
|
||||||
|
isCreateMode,
|
||||||
}: SettingsRoleSettingsProps) => {
|
}: SettingsRoleSettingsProps) => {
|
||||||
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
||||||
settingsDraftRoleFamilyState(roleId),
|
settingsDraftRoleFamilyState(roleId),
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const { openModal } = useModal();
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Section>
|
<>
|
||||||
<StyledInputsContainer>
|
<Section>
|
||||||
<StyledInputContainer>
|
<StyledInputsContainer>
|
||||||
<IconPicker
|
<StyledInputContainer>
|
||||||
selectedIconKey={settingsDraftRole.icon ?? 'IconUser'}
|
<IconPicker
|
||||||
dropdownId="role-settings-icon-picker"
|
selectedIconKey={settingsDraftRole.icon ?? 'IconUser'}
|
||||||
onChange={({ iconKey }: { iconKey: string }) => {
|
dropdownId="role-settings-icon-picker"
|
||||||
|
onChange={({ iconKey }: { iconKey: string }) => {
|
||||||
|
setSettingsDraftRole({
|
||||||
|
...settingsDraftRole,
|
||||||
|
icon: iconKey,
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
disabled={!isEditable}
|
||||||
|
/>
|
||||||
|
</StyledInputContainer>
|
||||||
|
<TextInput
|
||||||
|
value={settingsDraftRole.label}
|
||||||
|
fullWidth
|
||||||
|
onChange={(value: string) => {
|
||||||
setSettingsDraftRole({
|
setSettingsDraftRole({
|
||||||
...settingsDraftRole,
|
...settingsDraftRole,
|
||||||
icon: iconKey,
|
label: value,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
|
placeholder={t`Role name`}
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
/>
|
/>
|
||||||
</StyledInputContainer>
|
</StyledInputsContainer>
|
||||||
<TextInput
|
<TextArea
|
||||||
value={settingsDraftRole.label}
|
minRows={4}
|
||||||
fullWidth
|
placeholder={t`Write a description`}
|
||||||
|
value={settingsDraftRole.description || ''}
|
||||||
onChange={(value: string) => {
|
onChange={(value: string) => {
|
||||||
setSettingsDraftRole({
|
setSettingsDraftRole({
|
||||||
...settingsDraftRole,
|
...settingsDraftRole,
|
||||||
label: value,
|
description: value,
|
||||||
});
|
});
|
||||||
}}
|
}}
|
||||||
placeholder={t`Role name`}
|
|
||||||
disabled={!isEditable}
|
disabled={!isEditable}
|
||||||
/>
|
/>
|
||||||
</StyledInputsContainer>
|
</Section>
|
||||||
<TextArea
|
|
||||||
minRows={4}
|
{!isCreateMode && (
|
||||||
placeholder={t`Write a description`}
|
<>
|
||||||
value={settingsDraftRole.description || ''}
|
<Section>
|
||||||
onChange={(value: string) => {
|
<H2Title
|
||||||
setSettingsDraftRole({
|
title={t`Danger zone`}
|
||||||
...settingsDraftRole,
|
description={t`Delete this role and assign a new role to its members`}
|
||||||
description: value,
|
/>
|
||||||
});
|
<Button
|
||||||
}}
|
title={t`Delete role`}
|
||||||
disabled={!isEditable}
|
size="small"
|
||||||
/>
|
variant="secondary"
|
||||||
</Section>
|
accent="danger"
|
||||||
|
onClick={() => {
|
||||||
|
openModal(ROLE_SETTINGS_DELETE_ROLE_CONFIRMATION_MODAL_ID);
|
||||||
|
}}
|
||||||
|
disabled={!isEditable}
|
||||||
|
/>
|
||||||
|
</Section>
|
||||||
|
<SettingsRoleSettingsDeleteRoleConfirmationModal roleId={roleId} />
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|||||||
@ -0,0 +1,41 @@
|
|||||||
|
import { ROLE_SETTINGS_DELETE_ROLE_CONFIRMATION_MODAL_ID } from '@/settings/roles/role-settings/components/constants/RoleSettingsDeleteRoleConfirmationModalId';
|
||||||
|
import { SettingsRoleSettingsDeleteRoleConfirmationModalSubtitle } from '@/settings/roles/role-settings/components/SettingsRoleSettingsDeleteRoleConfirmationModalSubtitle';
|
||||||
|
import { SettingsPath } from '@/types/SettingsPath';
|
||||||
|
import { ConfirmationModal } from '@/ui/layout/modal/components/ConfirmationModal';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { useDeleteOneRoleMutation } from '~/generated/graphql';
|
||||||
|
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||||
|
|
||||||
|
type SettingsRoleSettingsDeleteRoleConfirmationModalProps = {
|
||||||
|
roleId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsRoleSettingsDeleteRoleConfirmationModal = ({
|
||||||
|
roleId,
|
||||||
|
}: SettingsRoleSettingsDeleteRoleConfirmationModalProps) => {
|
||||||
|
const [deleteRole] = useDeleteOneRoleMutation();
|
||||||
|
|
||||||
|
const navigateSettings = useNavigateSettings();
|
||||||
|
|
||||||
|
const handleConfirmClick = async () => {
|
||||||
|
await deleteRole({
|
||||||
|
variables: { roleId },
|
||||||
|
});
|
||||||
|
navigateSettings(SettingsPath.Roles);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ConfirmationModal
|
||||||
|
modalId={ROLE_SETTINGS_DELETE_ROLE_CONFIRMATION_MODAL_ID}
|
||||||
|
title={t`Delete Role Permanently`}
|
||||||
|
subtitle={
|
||||||
|
<SettingsRoleSettingsDeleteRoleConfirmationModalSubtitle
|
||||||
|
roleId={roleId}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
onConfirmClick={handleConfirmClick}
|
||||||
|
confirmButtonText={t`Confirm`}
|
||||||
|
confirmButtonAccent="danger"
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,20 @@
|
|||||||
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
|
import { useRecoilValue } from 'recoil';
|
||||||
|
|
||||||
|
type SettingsRoleSettingsDeleteRoleConfirmationModalSubtitleProps = {
|
||||||
|
roleId: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export const SettingsRoleSettingsDeleteRoleConfirmationModalSubtitle = ({
|
||||||
|
roleId,
|
||||||
|
}: SettingsRoleSettingsDeleteRoleConfirmationModalSubtitleProps) => {
|
||||||
|
const settingsDraftRole = useRecoilValue(
|
||||||
|
settingsDraftRoleFamilyState(roleId),
|
||||||
|
);
|
||||||
|
const roleName = settingsDraftRole.label;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<>{t`Confirm deletion of ${roleName} role? This cannot be undone. All members will be reassigned to the default role.`}</>
|
||||||
|
);
|
||||||
|
};
|
||||||
@ -0,0 +1,2 @@
|
|||||||
|
export const ROLE_SETTINGS_DELETE_ROLE_CONFIRMATION_MODAL_ID =
|
||||||
|
'role-settings-delete-role-confirmation-modal';
|
||||||
@ -3,10 +3,10 @@ import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDr
|
|||||||
import { Meta, StoryObj } from '@storybook/react';
|
import { Meta, StoryObj } from '@storybook/react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
import { isDefined } from 'twenty-shared/utils';
|
import { isDefined } from 'twenty-shared/utils';
|
||||||
|
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
||||||
import { PENDING_ROLE_ID } from '~/pages/settings/roles/SettingsRoleCreate';
|
import { PENDING_ROLE_ID } from '~/pages/settings/roles/SettingsRoleCreate';
|
||||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||||
import { getRolesMock } from '~/testing/mock-data/roles';
|
import { getRolesMock } from '~/testing/mock-data/roles';
|
||||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui/testing';
|
|
||||||
|
|
||||||
const SettingsRoleSettingsWrapper = (
|
const SettingsRoleSettingsWrapper = (
|
||||||
args: React.ComponentProps<typeof SettingsRoleSettings>,
|
args: React.ComponentProps<typeof SettingsRoleSettings>,
|
||||||
@ -22,7 +22,11 @@ const SettingsRoleSettingsWrapper = (
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<SettingsRoleSettings roleId={args.roleId} isEditable={args.isEditable} />
|
<SettingsRoleSettings
|
||||||
|
roleId={args.roleId}
|
||||||
|
isEditable={args.isEditable}
|
||||||
|
isCreateMode={args.isCreateMode}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -39,6 +43,7 @@ export const Default: Story = {
|
|||||||
args: {
|
args: {
|
||||||
roleId: '1',
|
roleId: '1',
|
||||||
isEditable: true,
|
isEditable: true,
|
||||||
|
isCreateMode: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
@ -46,11 +51,22 @@ export const ReadOnly: Story = {
|
|||||||
args: {
|
args: {
|
||||||
roleId: '1',
|
roleId: '1',
|
||||||
isEditable: false,
|
isEditable: false,
|
||||||
|
isCreateMode: false,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
export const PendingRole: Story = {
|
export const PendingRole: Story = {
|
||||||
args: {
|
args: {
|
||||||
roleId: PENDING_ROLE_ID,
|
roleId: PENDING_ROLE_ID,
|
||||||
|
isEditable: true,
|
||||||
|
isCreateMode: false,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const CreateMode: Story = {
|
||||||
|
args: {
|
||||||
|
roleId: '1',
|
||||||
|
isEditable: true,
|
||||||
|
isCreateMode: true,
|
||||||
},
|
},
|
||||||
};
|
};
|
||||||
|
|||||||
@ -205,6 +205,21 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
} else {
|
} else {
|
||||||
|
if (isDefined(dirtyFields.settingPermissions)) {
|
||||||
|
await upsertSettingPermissions({
|
||||||
|
variables: {
|
||||||
|
upsertSettingPermissionsInput: {
|
||||||
|
roleId: roleId,
|
||||||
|
settingPermissionKeys:
|
||||||
|
settingsDraftRole.settingPermissions?.map(
|
||||||
|
(settingPermission) => settingPermission.setting,
|
||||||
|
) ?? [],
|
||||||
|
},
|
||||||
|
},
|
||||||
|
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (ROLE_BASIC_KEYS.some((key) => key in dirtyFields)) {
|
if (ROLE_BASIC_KEYS.some((key) => key in dirtyFields)) {
|
||||||
await updateRole({
|
await updateRole({
|
||||||
variables: {
|
variables: {
|
||||||
@ -229,21 +244,6 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
if (isDefined(dirtyFields.settingPermissions)) {
|
|
||||||
await upsertSettingPermissions({
|
|
||||||
variables: {
|
|
||||||
upsertSettingPermissionsInput: {
|
|
||||||
roleId: roleId,
|
|
||||||
settingPermissionKeys:
|
|
||||||
settingsDraftRole.settingPermissions?.map(
|
|
||||||
(settingPermission) => settingPermission.setting,
|
|
||||||
) ?? [],
|
|
||||||
},
|
|
||||||
},
|
|
||||||
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
if (isDefined(dirtyFields.objectPermissions)) {
|
if (isDefined(dirtyFields.objectPermissions)) {
|
||||||
await upsertObjectPermissions({
|
await upsertObjectPermissions({
|
||||||
variables: {
|
variables: {
|
||||||
@ -310,7 +310,11 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
|||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
{activeTabId === SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.SETTINGS && (
|
{activeTabId === SETTINGS_ROLE_DETAIL_TABS.TABS_IDS.SETTINGS && (
|
||||||
<SettingsRoleSettings roleId={roleId} isEditable={isRoleEditable} />
|
<SettingsRoleSettings
|
||||||
|
roleId={roleId}
|
||||||
|
isEditable={isRoleEditable}
|
||||||
|
isCreateMode={isCreateMode}
|
||||||
|
/>
|
||||||
)}
|
)}
|
||||||
</SettingsPageContainer>
|
</SettingsPageContainer>
|
||||||
</SubMenuTopBarContainer>
|
</SubMenuTopBarContainer>
|
||||||
|
|||||||
@ -2,6 +2,7 @@ import { SETTINGS_ROLE_DETAIL_TABS } from '@/settings/roles/role/constants/Setti
|
|||||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||||
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
import { activeTabIdComponentState } from '@/ui/layout/tab-list/states/activeTabIdComponentState';
|
||||||
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
import { t } from '@lingui/core/macro';
|
||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { useSetRecoilState } from 'recoil';
|
import { useSetRecoilState } from 'recoil';
|
||||||
|
|
||||||
@ -31,7 +32,7 @@ export const SettingsRoleCreateEffect = ({
|
|||||||
|
|
||||||
const newRole = {
|
const newRole = {
|
||||||
id: roleId,
|
id: roleId,
|
||||||
label: '',
|
label: t`Role name`,
|
||||||
description: '',
|
description: '',
|
||||||
icon: 'IconUser',
|
icon: 'IconUser',
|
||||||
canUpdateAllSettings: true,
|
canUpdateAllSettings: true,
|
||||||
|
|||||||
@ -4,11 +4,14 @@ import { TypeOrmModule } from '@nestjs/typeorm';
|
|||||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
|
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
|
||||||
import { SettingPermissionService } from 'src/engine/metadata-modules/setting-permission/setting-permission.service';
|
import { SettingPermissionService } from 'src/engine/metadata-modules/setting-permission/setting-permission.service';
|
||||||
|
import { WorkspacePermissionsCacheModule } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.module';
|
||||||
|
|
||||||
@Module({
|
@Module({
|
||||||
imports: [
|
imports: [
|
||||||
TypeOrmModule.forFeature([SettingPermissionEntity, RoleEntity], 'core'),
|
TypeOrmModule.forFeature([SettingPermissionEntity, RoleEntity], 'core'),
|
||||||
|
WorkspacePermissionsCacheModule,
|
||||||
],
|
],
|
||||||
|
|
||||||
providers: [SettingPermissionService],
|
providers: [SettingPermissionService],
|
||||||
exports: [SettingPermissionService],
|
exports: [SettingPermissionService],
|
||||||
})
|
})
|
||||||
|
|||||||
@ -12,6 +12,7 @@ import {
|
|||||||
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
import { RoleEntity } from 'src/engine/metadata-modules/role/role.entity';
|
||||||
import { UpsertSettingPermissionsInput } from 'src/engine/metadata-modules/setting-permission/dtos/upsert-setting-permission-input';
|
import { UpsertSettingPermissionsInput } from 'src/engine/metadata-modules/setting-permission/dtos/upsert-setting-permission-input';
|
||||||
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
|
import { SettingPermissionEntity } from 'src/engine/metadata-modules/setting-permission/setting-permission.entity';
|
||||||
|
import { WorkspacePermissionsCacheService } from 'src/engine/metadata-modules/workspace-permissions-cache/workspace-permissions-cache.service';
|
||||||
|
|
||||||
export class SettingPermissionService {
|
export class SettingPermissionService {
|
||||||
constructor(
|
constructor(
|
||||||
@ -21,6 +22,7 @@ export class SettingPermissionService {
|
|||||||
private readonly roleRepository: Repository<RoleEntity>,
|
private readonly roleRepository: Repository<RoleEntity>,
|
||||||
@InjectDataSource('core')
|
@InjectDataSource('core')
|
||||||
private readonly coreDataSource: DataSource,
|
private readonly coreDataSource: DataSource,
|
||||||
|
private readonly workspacePermissionsCacheService: WorkspacePermissionsCacheService,
|
||||||
) {}
|
) {}
|
||||||
|
|
||||||
public async upsertSettingPermissions({
|
public async upsertSettingPermissions({
|
||||||
@ -115,6 +117,14 @@ export class SettingPermissionService {
|
|||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
await queryRunner.release();
|
await queryRunner.release();
|
||||||
|
|
||||||
|
await this.workspacePermissionsCacheService.recomputeRolesPermissionsCache(
|
||||||
|
{
|
||||||
|
workspaceId,
|
||||||
|
roleIds: [input.roleId],
|
||||||
|
ignoreLock: true,
|
||||||
|
},
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user