add settings permissions update (#11377)
Fixes https://github.com/twentyhq/core-team-issues/issues/710
This commit is contained in:
@ -1,3 +1,4 @@
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
||||
import { settingsRoleIdsState } from '@/settings/roles/states/settingsRoleIdsState';
|
||||
import { settingsRolesIsLoadingState } from '@/settings/roles/states/settingsRolesIsLoadingState';
|
||||
@ -21,6 +22,7 @@ export const SettingsRolesQueryEffect = () => {
|
||||
const roleIds = roles.map((role) => role.id);
|
||||
set(settingsRoleIdsState, roleIds);
|
||||
roles.forEach((role) => {
|
||||
set(settingsDraftRoleFamilyState(role.id), role);
|
||||
set(settingsPersistedRoleFamilyState(role.id), role);
|
||||
});
|
||||
},
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const SETTING_PERMISSION_FRAGMENT = gql`
|
||||
fragment SettingPermissionFragment on SettingPermission {
|
||||
id
|
||||
setting
|
||||
roleId
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,15 @@
|
||||
import { SETTING_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/settingPermissionFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPSERT_SETTING_PERMISSIONS = gql`
|
||||
${SETTING_PERMISSION_FRAGMENT}
|
||||
mutation UpsertSettingPermissions(
|
||||
$upsertSettingPermissionsInput: UpsertSettingPermissionsInput!
|
||||
) {
|
||||
upsertSettingPermissions(
|
||||
upsertSettingPermissionsInput: $upsertSettingPermissionsInput
|
||||
) {
|
||||
...SettingPermissionFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,16 +1,21 @@
|
||||
import { ROLE_FRAGMENT } from '@/settings/roles/graphql/fragments/roleFragment';
|
||||
import { SETTING_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/settingPermissionFragment';
|
||||
import { WORKSPACE_MEMBER_QUERY_FRAGMENT } from '@/workspace-member/graphql/fragments/workspaceMemberQueryFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const GET_ROLES = gql`
|
||||
${WORKSPACE_MEMBER_QUERY_FRAGMENT}
|
||||
${ROLE_FRAGMENT}
|
||||
${SETTING_PERMISSION_FRAGMENT}
|
||||
query GetRoles {
|
||||
getRoles {
|
||||
...RoleFragment
|
||||
workspaceMembers {
|
||||
...WorkspaceMemberQueryFragment
|
||||
}
|
||||
settingPermissions {
|
||||
...SettingPermissionFragment
|
||||
}
|
||||
}
|
||||
}
|
||||
`;
|
||||
|
||||
@ -1,3 +1,4 @@
|
||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||
import { SettingsRolePermissionsObjectsTableHeader } from '@/settings/roles/role-permissions/components/SettingsRolePermissionsObjectsTableHeader';
|
||||
import { SettingsRolePermissionsObjectsTableRow } from '@/settings/roles/role-permissions/components/SettingsRolePermissionsObjectsTableRow';
|
||||
import { SettingsRolePermissionsSettingsTableHeader } from '@/settings/roles/role-permissions/components/SettingsRolePermissionsSettingsTableHeader';
|
||||
@ -5,10 +6,10 @@ import { SettingsRolePermissionsSettingsTableRow } from '@/settings/roles/role-p
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { SettingsRolePermissionsObjectPermission } from '@/settings/roles/types/SettingsRolePermissionsObjectPermission';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/types/SettingsRolePermissionsSettingPermission';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { SettingPermissionType } from '~/generated-metadata/graphql';
|
||||
import {
|
||||
H2Title,
|
||||
IconCode,
|
||||
@ -23,7 +24,11 @@ import {
|
||||
IconTrashX,
|
||||
IconUsers,
|
||||
} from 'twenty-ui/display';
|
||||
import { Section } from 'twenty-ui/layout';
|
||||
import { Card, Section } from 'twenty-ui/layout';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
SettingPermissionType,
|
||||
} from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledRolePermissionsContainer = styled.div`
|
||||
display: flex;
|
||||
@ -40,6 +45,10 @@ const StyledTableRows = styled.div`
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
type SettingsRolePermissionsProps = {
|
||||
roleId: string;
|
||||
isEditable: boolean;
|
||||
@ -110,53 +119,50 @@ export const SettingsRolePermissions = ({
|
||||
key: SettingPermissionType.API_KEYS_AND_WEBHOOKS,
|
||||
name: t`API Keys & Webhooks`,
|
||||
description: t`Manage API keys and webhooks`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconCode,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.WORKSPACE,
|
||||
name: t`Workspace`,
|
||||
description: t`Set global workspace preferences`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconSettings,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.WORKSPACE_MEMBERS,
|
||||
name: t`Users`,
|
||||
description: t`Add or remove users`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconUsers,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.ROLES,
|
||||
name: t`Roles`,
|
||||
description: t`Define user roles and access levels`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconLockOpen,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.DATA_MODEL,
|
||||
name: t`Data Model`,
|
||||
description: t`Edit CRM data structure and fields`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconHierarchy,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.ADMIN_PANEL,
|
||||
name: t`Admin Panel`,
|
||||
description: t`Admin settings and system tools`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconServer,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.SECURITY,
|
||||
name: t`Security`,
|
||||
description: t`Manage security policies`,
|
||||
value: settingsDraftRole.canUpdateAllSettings,
|
||||
Icon: IconKey,
|
||||
},
|
||||
];
|
||||
|
||||
const isPermissionsV2Enabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||
);
|
||||
|
||||
return (
|
||||
<StyledRolePermissionsContainer>
|
||||
<Section>
|
||||
@ -183,15 +189,32 @@ export const SettingsRolePermissions = ({
|
||||
</Section>
|
||||
<Section>
|
||||
<H2Title title={t`Settings`} description={t`Settings permissions`} />
|
||||
{isPermissionsV2Enabled && (
|
||||
<StyledCard rounded>
|
||||
<SettingsOptionCardContentToggle
|
||||
Icon={IconSettings}
|
||||
title={t`Settings All Access`}
|
||||
description={t`Ability to edit all settings`}
|
||||
checked={settingsDraftRole.canUpdateAllSettings}
|
||||
disabled={!isEditable}
|
||||
onChange={() => {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
canUpdateAllSettings: !settingsDraftRole.canUpdateAllSettings,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
)}
|
||||
<StyledTable>
|
||||
<SettingsRolePermissionsSettingsTableHeader
|
||||
allPermissions={settingsDraftRole.canUpdateAllSettings}
|
||||
/>
|
||||
<SettingsRolePermissionsSettingsTableHeader />
|
||||
<StyledTableRows>
|
||||
{settingsPermissionsConfig.map((permission) => (
|
||||
<SettingsRolePermissionsSettingsTableRow
|
||||
key={permission.key}
|
||||
roleId={roleId}
|
||||
permission={permission}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
))}
|
||||
</StyledTableRows>
|
||||
|
||||
@ -1,42 +1,11 @@
|
||||
import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Checkbox } from 'twenty-ui/input';
|
||||
|
||||
const StyledNameHeader = styled(TableHeader)`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const StyledTypeHeader = styled(TableHeader)`
|
||||
flex: 1;
|
||||
`;
|
||||
|
||||
const StyledActionsHeader = styled(TableHeader)`
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-right: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
type SettingsRolePermissionsSettingsTableHeaderProps = {
|
||||
allPermissions: boolean;
|
||||
onToggleAll?: () => void;
|
||||
};
|
||||
|
||||
export const SettingsRolePermissionsSettingsTableHeader = ({
|
||||
allPermissions,
|
||||
onToggleAll,
|
||||
}: SettingsRolePermissionsSettingsTableHeaderProps) => (
|
||||
export const SettingsRolePermissionsSettingsTableHeader = () => (
|
||||
<TableRow gridAutoColumns="3fr 4fr 24px">
|
||||
<StyledNameHeader>{t`Name`}</StyledNameHeader>
|
||||
<StyledTypeHeader>{t`Description`}</StyledTypeHeader>
|
||||
<StyledActionsHeader aria-label={t`Actions`}>
|
||||
<Checkbox
|
||||
checked={allPermissions}
|
||||
disabled={!onToggleAll}
|
||||
onChange={onToggleAll}
|
||||
/>
|
||||
</StyledActionsHeader>
|
||||
<TableHeader>{t`Name`}</TableHeader>
|
||||
<TableHeader>{t`Description`}</TableHeader>
|
||||
<TableHeader></TableHeader>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@ -1,9 +1,14 @@
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/types/SettingsRolePermissionsSettingPermission';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useTheme } from '@emotion/react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { Checkbox } from 'twenty-ui/input';
|
||||
import { v4 } from 'uuid';
|
||||
import { FeatureFlagKey } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledName = styled.span`
|
||||
color: ${({ theme }) => theme.font.color.primary};
|
||||
@ -34,13 +39,54 @@ const StyledIconContainer = styled.div`
|
||||
`;
|
||||
|
||||
type SettingsRolePermissionsSettingsTableRowProps = {
|
||||
roleId: string;
|
||||
permission: SettingsRolePermissionsSettingPermission;
|
||||
isEditable: boolean;
|
||||
};
|
||||
|
||||
export const SettingsRolePermissionsSettingsTableRow = ({
|
||||
roleId,
|
||||
permission,
|
||||
isEditable,
|
||||
}: SettingsRolePermissionsSettingsTableRowProps) => {
|
||||
const theme = useTheme();
|
||||
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
const isPermissionsV2Enabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||
);
|
||||
const canUpdateAllSettings = settingsDraftRole.canUpdateAllSettings;
|
||||
|
||||
const isSettingPermissionEnabled =
|
||||
settingsDraftRole.settingPermissions?.some(
|
||||
(settingPermission) => settingPermission.setting === permission.key,
|
||||
) ?? false;
|
||||
|
||||
const handleChange = (value: boolean) => {
|
||||
const currentPermissions = settingsDraftRole.settingPermissions ?? [];
|
||||
|
||||
if (value === true) {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
settingPermissions: [
|
||||
...currentPermissions,
|
||||
{
|
||||
id: v4(),
|
||||
setting: permission.key,
|
||||
roleId,
|
||||
},
|
||||
],
|
||||
});
|
||||
} else {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
settingPermissions: currentPermissions.filter(
|
||||
(p) => p.setting !== permission.key,
|
||||
),
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<TableRow key={permission.key} gridAutoColumns="3fr 4fr 24px">
|
||||
@ -58,7 +104,13 @@ export const SettingsRolePermissionsSettingsTableRow = ({
|
||||
<StyledDescription>{permission.description}</StyledDescription>
|
||||
</StyledPermissionCell>
|
||||
<StyledCheckboxCell>
|
||||
<Checkbox checked={permission.value} disabled />
|
||||
<Checkbox
|
||||
checked={isSettingPermissionEnabled || canUpdateAllSettings}
|
||||
disabled={
|
||||
!isEditable || canUpdateAllSettings || !isPermissionsV2Enabled
|
||||
}
|
||||
onChange={(event) => handleChange(event.target.checked)}
|
||||
/>
|
||||
</StyledCheckboxCell>
|
||||
</TableRow>
|
||||
);
|
||||
|
||||
@ -1,4 +1,5 @@
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { GET_ROLES } from '@/settings/roles/graphql/queries/getRolesQuery';
|
||||
import { useUpdateWorkspaceMemberRole } from '@/settings/roles/hooks/useUpdateWorkspaceMemberRole';
|
||||
import { SettingsRoleAssignment } from '@/settings/roles/role-assignment/components/SettingsRoleAssignment';
|
||||
import { SettingsRolePermissions } from '@/settings/roles/role-permissions/components/SettingsRolePermissions';
|
||||
@ -14,26 +15,41 @@ import { TabList } from '@/ui/layout/tab/components/TabList';
|
||||
import { activeTabIdComponentState } from '@/ui/layout/tab/states/activeTabIdComponentState';
|
||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { getOperationName } from '@apollo/client/utilities';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { IconLockOpen, IconSettings, IconUserPlus } from 'twenty-ui/display';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { v4 } from 'uuid';
|
||||
import {
|
||||
FeatureFlagKey,
|
||||
Role,
|
||||
useCreateOneRoleMutation,
|
||||
useUpdateOneRoleMutation,
|
||||
useUpsertSettingPermissionsMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getDirtyFields } from '~/utils/getDirtyFields';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
import { Button } from 'twenty-ui/input';
|
||||
import { IconLockOpen, IconSettings, IconUserPlus } from 'twenty-ui/display';
|
||||
|
||||
type SettingsRoleProps = {
|
||||
roleId: string;
|
||||
isCreateMode: boolean;
|
||||
};
|
||||
|
||||
const ROLE_BASIC_KEYS: Array<keyof Role> = [
|
||||
'label',
|
||||
'description',
|
||||
'icon',
|
||||
'canUpdateAllSettings',
|
||||
'canReadAllObjectRecords',
|
||||
'canUpdateAllObjectRecords',
|
||||
'canSoftDeleteAllObjectRecords',
|
||||
'canDestroyAllObjectRecords',
|
||||
];
|
||||
|
||||
export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
const activeTabId = useRecoilComponentValueV2(
|
||||
activeTabIdComponentState,
|
||||
@ -48,6 +64,11 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
|
||||
const [createRole] = useCreateOneRoleMutation();
|
||||
const [updateRole] = useUpdateOneRoleMutation();
|
||||
const [upsertSettingPermissions] = useUpsertSettingPermissionsMutation();
|
||||
|
||||
const { addWorkspaceMembersToRole } = useUpdateWorkspaceMemberRole(roleId);
|
||||
|
||||
const settingsRolesIsLoading = useRecoilValue(settingsRolesIsLoadingState);
|
||||
|
||||
const settingsDraftRole = useRecoilValue(
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
@ -57,10 +78,6 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
settingsPersistedRoleFamilyState(roleId),
|
||||
);
|
||||
|
||||
const { addWorkspaceMembersToRole } = useUpdateWorkspaceMemberRole(roleId);
|
||||
|
||||
const settingsRolesIsLoading = useRecoilValue(settingsRolesIsLoadingState);
|
||||
|
||||
if (!isDefined(settingsRolesIsLoading)) {
|
||||
return <></>;
|
||||
}
|
||||
@ -87,7 +104,12 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
|
||||
const isDirty = !isDeeplyEqual(settingsDraftRole, settingsPersistedRole);
|
||||
|
||||
const handleSave = () => {
|
||||
const handleSave = async () => {
|
||||
const dirtyFields = getDirtyFields(
|
||||
settingsDraftRole,
|
||||
settingsPersistedRole,
|
||||
);
|
||||
|
||||
if (isCreateMode) {
|
||||
const roleId = v4();
|
||||
|
||||
@ -116,33 +138,63 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
),
|
||||
});
|
||||
|
||||
await upsertSettingPermissions({
|
||||
variables: {
|
||||
upsertSettingPermissionsInput: {
|
||||
roleId: data.createOneRole.id,
|
||||
settingPermissionKeys:
|
||||
settingsDraftRole.settingPermissions?.map(
|
||||
(settingPermission) => settingPermission.setting,
|
||||
) ?? [],
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
||||
});
|
||||
|
||||
navigateSettings(SettingsPath.RoleDetail, {
|
||||
roleId: data.createOneRole.id,
|
||||
});
|
||||
},
|
||||
});
|
||||
} else {
|
||||
updateRole({
|
||||
variables: {
|
||||
updateRoleInput: {
|
||||
id: roleId,
|
||||
update: {
|
||||
label: settingsDraftRole.label,
|
||||
description: settingsDraftRole.description,
|
||||
icon: settingsDraftRole.icon,
|
||||
canUpdateAllSettings: settingsDraftRole.canUpdateAllSettings,
|
||||
canReadAllObjectRecords:
|
||||
settingsDraftRole.canReadAllObjectRecords,
|
||||
canUpdateAllObjectRecords:
|
||||
settingsDraftRole.canUpdateAllObjectRecords,
|
||||
canSoftDeleteAllObjectRecords:
|
||||
settingsDraftRole.canSoftDeleteAllObjectRecords,
|
||||
canDestroyAllObjectRecords:
|
||||
settingsDraftRole.canDestroyAllObjectRecords,
|
||||
if (ROLE_BASIC_KEYS.some((key) => key in dirtyFields)) {
|
||||
await updateRole({
|
||||
variables: {
|
||||
updateRoleInput: {
|
||||
id: roleId,
|
||||
update: {
|
||||
label: settingsDraftRole.label,
|
||||
description: settingsDraftRole.description,
|
||||
icon: settingsDraftRole.icon,
|
||||
canUpdateAllSettings: settingsDraftRole.canUpdateAllSettings,
|
||||
canReadAllObjectRecords:
|
||||
settingsDraftRole.canReadAllObjectRecords,
|
||||
canUpdateAllObjectRecords:
|
||||
settingsDraftRole.canUpdateAllObjectRecords,
|
||||
canSoftDeleteAllObjectRecords:
|
||||
settingsDraftRole.canSoftDeleteAllObjectRecords,
|
||||
canDestroyAllObjectRecords:
|
||||
settingsDraftRole.canDestroyAllObjectRecords,
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
if (isDefined(dirtyFields.settingPermissions)) {
|
||||
await upsertSettingPermissions({
|
||||
variables: {
|
||||
upsertSettingPermissionsInput: {
|
||||
roleId: roleId,
|
||||
settingPermissionKeys:
|
||||
settingsDraftRole.settingPermissions?.map(
|
||||
(settingPermission) => settingPermission.setting,
|
||||
) ?? [],
|
||||
},
|
||||
},
|
||||
refetchQueries: [getOperationName(GET_ROLES) ?? ''],
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
@ -15,5 +15,6 @@ export const settingsDraftRoleFamilyState = createFamilyState<Role, string>({
|
||||
canUpdateAllSettings: false,
|
||||
isEditable: false,
|
||||
workspaceMembers: [],
|
||||
settingPermissions: [],
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { IconComponent } from 'twenty-ui/display';
|
||||
import { SettingPermissionType } from '~/generated-metadata/graphql';
|
||||
|
||||
export type SettingsRolePermissionsSettingPermission = {
|
||||
key: string;
|
||||
key: SettingPermissionType;
|
||||
name: string;
|
||||
description: string;
|
||||
value: boolean;
|
||||
Icon: IconComponent;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user