Decouple Send Email node from workflows (#13322)
- Renamed `WorkflowActionAdapter` to `ToolExecutorWorkflowAction` - Renamed `settingPermission` table to `permissionFlag` and `setting` column to `flag` - Decoupled the send email logic from workflows to tools - Add new `Tools Permission` section in FE --------- Co-authored-by: Félix Malfait <felix.malfait@gmail.com> Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
@ -4,7 +4,7 @@ import { Route, Routes } from 'react-router-dom';
|
||||
import { SettingsProtectedRouteWrapper } from '@/settings/components/SettingsProtectedRouteWrapper';
|
||||
import { SettingsSkeletonLoader } from '@/settings/components/SettingsSkeletonLoader';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { SettingPermissionType } from '~/generated/graphql';
|
||||
import { PermissionFlagType } from '~/generated/graphql';
|
||||
|
||||
const SettingsApiKeys = lazy(() =>
|
||||
import('~/pages/settings/developers/api-keys/SettingsApiKeys').then(
|
||||
@ -405,7 +405,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.WORKSPACE}
|
||||
settingsPermission={PermissionFlagType.WORKSPACE}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@ -416,7 +416,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.WORKSPACE_MEMBERS}
|
||||
settingsPermission={PermissionFlagType.WORKSPACE_MEMBERS}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@ -428,7 +428,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.DATA_MODEL}
|
||||
settingsPermission={PermissionFlagType.DATA_MODEL}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@ -458,7 +458,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.ROLES}
|
||||
settingsPermission={PermissionFlagType.ROLES}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@ -480,7 +480,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.API_KEYS_AND_WEBHOOKS}
|
||||
settingsPermission={PermissionFlagType.API_KEYS_AND_WEBHOOKS}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@ -555,7 +555,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.SECURITY}
|
||||
settingsPermission={PermissionFlagType.SECURITY}
|
||||
/>
|
||||
}
|
||||
>
|
||||
@ -587,7 +587,7 @@ export const SettingsRoutes = ({
|
||||
<Route
|
||||
element={
|
||||
<SettingsProtectedRouteWrapper
|
||||
settingsPermission={SettingPermissionType.WORKSPACE}
|
||||
settingsPermission={PermissionFlagType.WORKSPACE}
|
||||
/>
|
||||
}
|
||||
>
|
||||
|
||||
@ -5,7 +5,7 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
SettingPermissionType,
|
||||
PermissionFlagType,
|
||||
useBillingPortalSessionQuery,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
@ -20,7 +20,7 @@ export const InformationBannerBillingSubscriptionPaused = () => {
|
||||
});
|
||||
|
||||
const {
|
||||
[SettingPermissionType.WORKSPACE]: hasPermissionToUpdateBillingDetails,
|
||||
[PermissionFlagType.WORKSPACE]: hasPermissionToUpdateBillingDetails,
|
||||
} = useSettingsPermissionMap();
|
||||
|
||||
const openBillingPortal = () => {
|
||||
|
||||
@ -2,13 +2,13 @@ import { useEndSubscriptionTrialPeriod } from '@/billing/hooks/useEndSubscriptio
|
||||
import { InformationBanner } from '@/information-banner/components/InformationBanner';
|
||||
import { useSettingsPermissionMap } from '@/settings/roles/hooks/useSettingsPermissionMap';
|
||||
import { useLingui } from '@lingui/react/macro';
|
||||
import { SettingPermissionType } from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
export const InformationBannerEndTrialPeriod = () => {
|
||||
const { endTrialPeriod, isLoading } = useEndSubscriptionTrialPeriod();
|
||||
const { t } = useLingui();
|
||||
|
||||
const { [SettingPermissionType.WORKSPACE]: hasPermissionToEndTrialPeriod } =
|
||||
const { [PermissionFlagType.WORKSPACE]: hasPermissionToEndTrialPeriod } =
|
||||
useSettingsPermissionMap();
|
||||
|
||||
return (
|
||||
|
||||
@ -5,7 +5,7 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import {
|
||||
SettingPermissionType,
|
||||
PermissionFlagType,
|
||||
useBillingPortalSessionQuery,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
@ -20,7 +20,7 @@ export const InformationBannerFailPaymentInfo = () => {
|
||||
});
|
||||
|
||||
const {
|
||||
[SettingPermissionType.WORKSPACE]: hasPermissionToUpdateBillingDetails,
|
||||
[PermissionFlagType.WORKSPACE]: hasPermissionToUpdateBillingDetails,
|
||||
} = useSettingsPermissionMap();
|
||||
|
||||
const openBillingPortal = () => {
|
||||
|
||||
@ -4,7 +4,7 @@ import { InformationBanner } from '@/information-banner/components/InformationBa
|
||||
import { useSettingsPermissionMap } from '@/settings/roles/hooks/useSettingsPermissionMap';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { SettingPermissionType } from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
export const InformationBannerNoBillingSubscription = () => {
|
||||
@ -15,7 +15,7 @@ export const InformationBannerNoBillingSubscription = () => {
|
||||
successUrlPath: getSettingsPath(SettingsPath.Billing),
|
||||
});
|
||||
|
||||
const { [SettingPermissionType.WORKSPACE]: hasPermissionToSubscribe } =
|
||||
const { [PermissionFlagType.WORKSPACE]: hasPermissionToSubscribe } =
|
||||
useSettingsPermissionMap();
|
||||
|
||||
return (
|
||||
|
||||
@ -24,7 +24,7 @@ import {
|
||||
IconEyeOff,
|
||||
IconSettings,
|
||||
} from 'twenty-ui/display';
|
||||
import { SettingPermissionType } from '~/generated/graphql';
|
||||
import { PermissionFlagType } from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
|
||||
type UseRecordGroupActionsParams = {
|
||||
@ -91,7 +91,7 @@ export const useRecordGroupActions = ({
|
||||
]);
|
||||
|
||||
const hasAccessToDataModelSettings = useHasSettingsPermission(
|
||||
SettingPermissionType.DATA_MODEL,
|
||||
PermissionFlagType.DATA_MODEL,
|
||||
);
|
||||
const currentIndex = visibleRecordGroupIds.findIndex(
|
||||
(id) => id === recordGroupDefinition.id,
|
||||
|
||||
@ -3,12 +3,12 @@ import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { ReactNode } from 'react';
|
||||
import { Navigate, Outlet } from 'react-router-dom';
|
||||
import { FeatureFlagKey, SettingPermissionType } from '~/generated/graphql';
|
||||
import { FeatureFlagKey, PermissionFlagType } from '~/generated/graphql';
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
type SettingsProtectedRouteWrapperProps = {
|
||||
children?: ReactNode;
|
||||
settingsPermission?: SettingPermissionType;
|
||||
settingsPermission?: PermissionFlagType;
|
||||
requiredFeatureFlag?: FeatureFlagKey;
|
||||
};
|
||||
|
||||
|
||||
@ -7,7 +7,7 @@ import { MutableSnapshot, RecoilRoot } from 'recoil';
|
||||
import {
|
||||
Billing,
|
||||
OnboardingStatus,
|
||||
SettingPermissionType,
|
||||
PermissionFlagType,
|
||||
} from '~/generated/graphql';
|
||||
|
||||
import { currentUserState } from '@/auth/states/currentUserState';
|
||||
@ -60,12 +60,12 @@ jest.mock('@/settings/roles/hooks/useSettingsPermissionMap', () => ({
|
||||
describe('useSettingsNavigationItems', () => {
|
||||
it('should hide workspace settings when no permissions', () => {
|
||||
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({
|
||||
[SettingPermissionType.WORKSPACE]: false,
|
||||
[SettingPermissionType.WORKSPACE_MEMBERS]: false,
|
||||
[SettingPermissionType.DATA_MODEL]: false,
|
||||
[SettingPermissionType.API_KEYS_AND_WEBHOOKS]: false,
|
||||
[SettingPermissionType.ROLES]: false,
|
||||
[SettingPermissionType.SECURITY]: false,
|
||||
[PermissionFlagType.WORKSPACE]: false,
|
||||
[PermissionFlagType.WORKSPACE_MEMBERS]: false,
|
||||
[PermissionFlagType.DATA_MODEL]: false,
|
||||
[PermissionFlagType.API_KEYS_AND_WEBHOOKS]: false,
|
||||
[PermissionFlagType.ROLES]: false,
|
||||
[PermissionFlagType.SECURITY]: false,
|
||||
}));
|
||||
|
||||
const { result } = renderHook(() => useSettingsNavigationItems(), {
|
||||
@ -81,12 +81,12 @@ describe('useSettingsNavigationItems', () => {
|
||||
|
||||
it('should show workspace settings when has permissions', () => {
|
||||
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({
|
||||
[SettingPermissionType.WORKSPACE]: true,
|
||||
[SettingPermissionType.WORKSPACE_MEMBERS]: true,
|
||||
[SettingPermissionType.DATA_MODEL]: true,
|
||||
[SettingPermissionType.API_KEYS_AND_WEBHOOKS]: true,
|
||||
[SettingPermissionType.ROLES]: true,
|
||||
[SettingPermissionType.SECURITY]: true,
|
||||
[PermissionFlagType.WORKSPACE]: true,
|
||||
[PermissionFlagType.WORKSPACE_MEMBERS]: true,
|
||||
[PermissionFlagType.DATA_MODEL]: true,
|
||||
[PermissionFlagType.API_KEYS_AND_WEBHOOKS]: true,
|
||||
[PermissionFlagType.ROLES]: true,
|
||||
[PermissionFlagType.SECURITY]: true,
|
||||
}));
|
||||
|
||||
const { result } = renderHook(() => useSettingsNavigationItems(), {
|
||||
|
||||
@ -30,7 +30,7 @@ import {
|
||||
IconUsers,
|
||||
IconWebhook,
|
||||
} from 'twenty-ui/display';
|
||||
import { SettingPermissionType } from '~/generated/graphql';
|
||||
import { PermissionFlagType } from '~/generated/graphql';
|
||||
|
||||
export type SettingsNavigationSection = {
|
||||
label: string;
|
||||
@ -107,46 +107,45 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
||||
label: t`General`,
|
||||
path: SettingsPath.Workspace,
|
||||
Icon: IconSettings,
|
||||
isHidden: !permissionMap[SettingPermissionType.WORKSPACE],
|
||||
isHidden: !permissionMap[PermissionFlagType.WORKSPACE],
|
||||
},
|
||||
{
|
||||
label: t`Members`,
|
||||
path: SettingsPath.WorkspaceMembersPage,
|
||||
Icon: IconUsers,
|
||||
isHidden: !permissionMap[SettingPermissionType.WORKSPACE_MEMBERS],
|
||||
isHidden: !permissionMap[PermissionFlagType.WORKSPACE_MEMBERS],
|
||||
},
|
||||
{
|
||||
label: t`Roles`,
|
||||
path: SettingsPath.Roles,
|
||||
Icon: IconLock,
|
||||
isHidden: !permissionMap[SettingPermissionType.ROLES],
|
||||
isHidden: !permissionMap[PermissionFlagType.ROLES],
|
||||
},
|
||||
{
|
||||
label: t`Billing`,
|
||||
path: SettingsPath.Billing,
|
||||
Icon: IconCurrencyDollar,
|
||||
isHidden:
|
||||
!isBillingEnabled ||
|
||||
!permissionMap[SettingPermissionType.WORKSPACE],
|
||||
!isBillingEnabled || !permissionMap[PermissionFlagType.WORKSPACE],
|
||||
},
|
||||
{
|
||||
label: t`Data model`,
|
||||
path: SettingsPath.Objects,
|
||||
Icon: IconHierarchy2,
|
||||
isHidden: !permissionMap[SettingPermissionType.DATA_MODEL],
|
||||
isHidden: !permissionMap[PermissionFlagType.DATA_MODEL],
|
||||
},
|
||||
{
|
||||
label: t`Integrations`,
|
||||
path: SettingsPath.Integrations,
|
||||
Icon: IconApps,
|
||||
isHidden: !permissionMap[SettingPermissionType.API_KEYS_AND_WEBHOOKS],
|
||||
isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS],
|
||||
},
|
||||
{
|
||||
label: t`Security`,
|
||||
path: SettingsPath.Security,
|
||||
Icon: IconKey,
|
||||
isAdvanced: true,
|
||||
isHidden: !permissionMap[SettingPermissionType.SECURITY],
|
||||
isHidden: !permissionMap[PermissionFlagType.SECURITY],
|
||||
},
|
||||
],
|
||||
},
|
||||
@ -159,14 +158,14 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
||||
path: SettingsPath.APIs,
|
||||
Icon: IconApi,
|
||||
isAdvanced: true,
|
||||
isHidden: !permissionMap[SettingPermissionType.API_KEYS_AND_WEBHOOKS],
|
||||
isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS],
|
||||
},
|
||||
{
|
||||
label: t`Webhooks`,
|
||||
path: SettingsPath.Webhooks,
|
||||
Icon: IconWebhook,
|
||||
isAdvanced: true,
|
||||
isHidden: !permissionMap[SettingPermissionType.API_KEYS_AND_WEBHOOKS],
|
||||
isHidden: !permissionMap[PermissionFlagType.API_KEYS_AND_WEBHOOKS],
|
||||
},
|
||||
{
|
||||
label: t`Functions`,
|
||||
@ -192,7 +191,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
|
||||
Icon: IconFlask,
|
||||
isHidden:
|
||||
!labPublicFeatureFlags.length ||
|
||||
!permissionMap[SettingPermissionType.WORKSPACE],
|
||||
!permissionMap[PermissionFlagType.WORKSPACE],
|
||||
},
|
||||
{
|
||||
label: t`Releases`,
|
||||
|
||||
@ -0,0 +1,9 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const PERMISSION_FLAG_FRAGMENT = gql`
|
||||
fragment PermissionFlagFragment on PermissionFlag {
|
||||
id
|
||||
flag
|
||||
roleId
|
||||
}
|
||||
`;
|
||||
@ -7,6 +7,7 @@ export const ROLE_FRAGMENT = gql`
|
||||
description
|
||||
icon
|
||||
canUpdateAllSettings
|
||||
canAccessAllTools
|
||||
isEditable
|
||||
canReadAllObjectRecords
|
||||
canUpdateAllObjectRecords
|
||||
|
||||
@ -1,9 +0,0 @@
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const SETTING_PERMISSION_FRAGMENT = gql`
|
||||
fragment SettingPermissionFragment on SettingPermission {
|
||||
id
|
||||
setting
|
||||
roleId
|
||||
}
|
||||
`;
|
||||
@ -0,0 +1,15 @@
|
||||
import { PERMISSION_FLAG_FRAGMENT } from '@/settings/roles/graphql/fragments/permissionFlagFragment';
|
||||
import { gql } from '@apollo/client';
|
||||
|
||||
export const UPSERT_PERMISSION_FLAGS = gql`
|
||||
${PERMISSION_FLAG_FRAGMENT}
|
||||
mutation UpsertPermissionFlags(
|
||||
$upsertPermissionFlagsInput: UpsertPermissionFlagsInput!
|
||||
) {
|
||||
upsertPermissionFlags(
|
||||
upsertPermissionFlagsInput: $upsertPermissionFlagsInput
|
||||
) {
|
||||
...PermissionFlagFragment
|
||||
}
|
||||
}
|
||||
`;
|
||||
@ -1,15 +0,0 @@
|
||||
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,13 +1,13 @@
|
||||
import { OBJECT_PERMISSION_FRAGMENT } from '@/settings/roles/graphql/fragments/objectPermissionFragment';
|
||||
import { PERMISSION_FLAG_FRAGMENT } from '@/settings/roles/graphql/fragments/permissionFlagFragment';
|
||||
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}
|
||||
${PERMISSION_FLAG_FRAGMENT}
|
||||
${OBJECT_PERMISSION_FRAGMENT}
|
||||
query GetRoles {
|
||||
getRoles {
|
||||
@ -15,8 +15,8 @@ export const GET_ROLES = gql`
|
||||
workspaceMembers {
|
||||
...WorkspaceMemberQueryFragment
|
||||
}
|
||||
settingPermissions {
|
||||
...SettingPermissionFragment
|
||||
permissionFlags {
|
||||
...PermissionFlagFragment
|
||||
}
|
||||
objectPermissions {
|
||||
...ObjectPermissionFragment
|
||||
|
||||
@ -2,20 +2,20 @@ import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceSta
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
|
||||
import { SettingPermissionType } from '~/generated/graphql';
|
||||
import { PermissionFlagType } from '~/generated/graphql';
|
||||
|
||||
export const useHasSettingsPermission = (
|
||||
settingsPermission?: SettingPermissionType,
|
||||
permissionFlag?: PermissionFlagType,
|
||||
) => {
|
||||
const currentWorkspace = useRecoilValue(currentWorkspaceState);
|
||||
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
|
||||
|
||||
if (!settingsPermission) {
|
||||
if (!permissionFlag) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (
|
||||
settingsPermission === SettingPermissionType.WORKSPACE &&
|
||||
permissionFlag === PermissionFlagType.WORKSPACE &&
|
||||
currentWorkspace?.activationStatus ===
|
||||
WorkspaceActivationStatus.PENDING_CREATION
|
||||
) {
|
||||
@ -28,5 +28,5 @@ export const useHasSettingsPermission = (
|
||||
return false;
|
||||
}
|
||||
|
||||
return currentUserWorkspaceSetting.includes(settingsPermission);
|
||||
return currentUserWorkspaceSetting.includes(permissionFlag);
|
||||
};
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
import { currentUserWorkspaceState } from '@/auth/states/currentUserWorkspaceState';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { SettingPermissionType } from '~/generated/graphql';
|
||||
import { PermissionFlagType } from '~/generated/graphql';
|
||||
import { buildRecordFromKeysWithSameValue } from '~/utils/array/buildRecordFromKeysWithSameValue';
|
||||
|
||||
export const useSettingsPermissionMap = (): Record<
|
||||
SettingPermissionType,
|
||||
PermissionFlagType,
|
||||
boolean
|
||||
> => {
|
||||
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
|
||||
@ -13,7 +13,7 @@ export const useSettingsPermissionMap = (): Record<
|
||||
currentUserWorkspace?.settingsPermissions;
|
||||
|
||||
const initialPermissions = buildRecordFromKeysWithSameValue(
|
||||
Object.values(SettingPermissionType),
|
||||
Object.values(PermissionFlagType),
|
||||
false,
|
||||
);
|
||||
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
import { SettingsRolePermissionsObjectLevelSection } from '@/settings/roles/role-permissions/object-level-permissions/components/SettingsRolePermissionsObjectLevelSection';
|
||||
import { SettingsRolePermissionsObjectsSection } from '@/settings/roles/role-permissions/objects-permissions/components/SettingsRolePermissionsObjectsSection';
|
||||
import { SettingsRolePermissionsSettingsSection } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsSection';
|
||||
import { SettingsRolePermissionsSettingsSection } from '@/settings/roles/role-permissions/permission-flags/components/SettingsRolePermissionsSettingsSection';
|
||||
import { SettingsRolePermissionsToolSection } from '@/settings/roles/role-permissions/permission-flags/components/SettingsRolePermissionsToolSection';
|
||||
import styled from '@emotion/styled';
|
||||
|
||||
const StyledRolePermissionsContainer = styled.div`
|
||||
@ -32,6 +33,10 @@ export const SettingsRolePermissions = ({
|
||||
roleId={roleId}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
<SettingsRolePermissionsToolSection
|
||||
roleId={roleId}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
</StyledRolePermissionsContainer>
|
||||
);
|
||||
};
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||
import { SettingsRolePermissionsSettingsTableHeader } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableHeader';
|
||||
import { SettingsRolePermissionsSettingsTableRow } from '@/settings/roles/role-permissions/settings-permissions/components/SettingsRolePermissionsSettingsTableRow';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/settings-permissions/types/SettingsRolePermissionsSettingPermission';
|
||||
import { SettingsRolePermissionsSettingsTableHeader } from '@/settings/roles/role-permissions/permission-flags/components/SettingsRolePermissionsSettingsTableHeader';
|
||||
import { SettingsRolePermissionsSettingsTableRow } from '@/settings/roles/role-permissions/permission-flags/components/SettingsRolePermissionsSettingsTableRow';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
@ -17,7 +17,7 @@ import {
|
||||
IconUsers,
|
||||
} from 'twenty-ui/display';
|
||||
import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout';
|
||||
import { SettingPermissionType } from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledTable = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
@ -48,43 +48,43 @@ export const SettingsRolePermissionsSettingsSection = ({
|
||||
const settingsPermissionsConfig: SettingsRolePermissionsSettingPermission[] =
|
||||
[
|
||||
{
|
||||
key: SettingPermissionType.API_KEYS_AND_WEBHOOKS,
|
||||
key: PermissionFlagType.API_KEYS_AND_WEBHOOKS,
|
||||
name: t`API Keys & Webhooks`,
|
||||
description: t`Manage API keys and webhooks`,
|
||||
Icon: IconCode,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.WORKSPACE,
|
||||
key: PermissionFlagType.WORKSPACE,
|
||||
name: t`Workspace`,
|
||||
description: t`Set global workspace preferences`,
|
||||
Icon: IconSettings,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.WORKSPACE_MEMBERS,
|
||||
key: PermissionFlagType.WORKSPACE_MEMBERS,
|
||||
name: t`Users`,
|
||||
description: t`Add or remove users`,
|
||||
Icon: IconUsers,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.ROLES,
|
||||
key: PermissionFlagType.ROLES,
|
||||
name: t`Roles`,
|
||||
description: t`Define user roles and access levels`,
|
||||
Icon: IconLockOpen,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.DATA_MODEL,
|
||||
key: PermissionFlagType.DATA_MODEL,
|
||||
name: t`Data Model`,
|
||||
description: t`Edit CRM data structure and fields`,
|
||||
Icon: IconHierarchy,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.SECURITY,
|
||||
key: PermissionFlagType.SECURITY,
|
||||
name: t`Security`,
|
||||
description: t`Manage security policies`,
|
||||
Icon: IconKey,
|
||||
},
|
||||
{
|
||||
key: SettingPermissionType.WORKFLOWS,
|
||||
key: PermissionFlagType.WORKFLOWS,
|
||||
name: t`Workflows`,
|
||||
description: t`Manage workflows`,
|
||||
Icon: IconSettingsAutomation,
|
||||
@ -4,7 +4,7 @@ import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { Checkbox } from 'twenty-ui/input';
|
||||
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/settings-permissions/types/SettingsRolePermissionsSettingPermission';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { v4 } from 'uuid';
|
||||
@ -32,15 +32,15 @@ export const SettingsRolePermissionsSettingsTableHeader = ({
|
||||
);
|
||||
const allSettingsPermissionsEnabled = settingsPermissionsConfig.every(
|
||||
(permission) =>
|
||||
settingsDraftRole.settingPermissions?.some(
|
||||
(settingPermission) => settingPermission.setting === permission.key,
|
||||
settingsDraftRole.permissionFlags?.some(
|
||||
(permissionFlag) => permissionFlag.flag === permission.key,
|
||||
),
|
||||
);
|
||||
|
||||
const someSettingsPermissionsEnabled = settingsPermissionsConfig.some(
|
||||
(permission) =>
|
||||
settingsDraftRole.settingPermissions?.some(
|
||||
(settingPermission) => settingPermission.setting === permission.key,
|
||||
settingsDraftRole.permissionFlags?.some(
|
||||
(permissionFlag) => permissionFlag.flag === permission.key,
|
||||
),
|
||||
);
|
||||
|
||||
@ -61,10 +61,10 @@ export const SettingsRolePermissionsSettingsTableHeader = ({
|
||||
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
settingPermissions: newValue
|
||||
permissionFlags: newValue
|
||||
? settingsPermissionsConfig.map((permission) => ({
|
||||
id: v4(),
|
||||
setting: permission.key,
|
||||
flag: permission.key,
|
||||
roleId,
|
||||
}))
|
||||
: [],
|
||||
@ -1,4 +1,4 @@
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/settings-permissions/types/SettingsRolePermissionsSettingPermission';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
@ -44,6 +44,7 @@ type SettingsRolePermissionsSettingsTableRowProps = {
|
||||
roleId: string;
|
||||
permission: SettingsRolePermissionsSettingPermission;
|
||||
isEditable: boolean;
|
||||
isToolPermission?: boolean;
|
||||
};
|
||||
|
||||
export const SettingsRolePermissionsSettingsTableRow = ({
|
||||
@ -55,27 +56,35 @@ export const SettingsRolePermissionsSettingsTableRow = ({
|
||||
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
const canUpdateAllSettings = settingsDraftRole.canUpdateAllSettings;
|
||||
|
||||
const isSettingPermissionEnabled =
|
||||
settingsDraftRole.settingPermissions?.some(
|
||||
(settingPermission) => settingPermission.setting === permission.key,
|
||||
const isPermissionEnabled =
|
||||
settingsDraftRole.permissionFlags?.some(
|
||||
(permissionFlag) => permissionFlag.flag === permission.key,
|
||||
) ?? false;
|
||||
|
||||
const isChecked = isSettingPermissionEnabled || canUpdateAllSettings;
|
||||
const isDisabled = !isEditable || canUpdateAllSettings;
|
||||
const isAllSettingsOverride =
|
||||
!permission.isToolPermission &&
|
||||
settingsDraftRole.canUpdateAllSettings === true;
|
||||
const isAllToolsOverride =
|
||||
permission.isToolPermission && settingsDraftRole.canAccessAllTools === true;
|
||||
const isChecked = Boolean(
|
||||
isPermissionEnabled || isAllSettingsOverride || isAllToolsOverride,
|
||||
);
|
||||
const isDisabled = Boolean(
|
||||
!isEditable || isAllSettingsOverride || isAllToolsOverride,
|
||||
);
|
||||
|
||||
const handleChange = (value: boolean) => {
|
||||
const currentPermissions = settingsDraftRole.settingPermissions ?? [];
|
||||
const currentPermissions = settingsDraftRole.permissionFlags ?? [];
|
||||
|
||||
if (value === true) {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
settingPermissions: [
|
||||
permissionFlags: [
|
||||
...currentPermissions,
|
||||
{
|
||||
id: v4(),
|
||||
setting: permission.key,
|
||||
flag: permission.key,
|
||||
roleId,
|
||||
},
|
||||
],
|
||||
@ -83,8 +92,8 @@ export const SettingsRolePermissionsSettingsTableRow = ({
|
||||
} else {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
settingPermissions: currentPermissions.filter(
|
||||
(p) => p.setting !== permission.key,
|
||||
permissionFlags: currentPermissions.filter(
|
||||
(p) => p.flag !== permission.key,
|
||||
),
|
||||
});
|
||||
}
|
||||
@ -0,0 +1,101 @@
|
||||
import { SettingsOptionCardContentToggle } from '@/settings/components/SettingsOptions/SettingsOptionCardContentToggle';
|
||||
import { SettingsRolePermissionsSettingsTableHeader } from '@/settings/roles/role-permissions/permission-flags/components/SettingsRolePermissionsSettingsTableHeader';
|
||||
import { SettingsRolePermissionsSettingsTableRow } from '@/settings/roles/role-permissions/permission-flags/components/SettingsRolePermissionsSettingsTableRow';
|
||||
import { SettingsRolePermissionsSettingPermission } from '@/settings/roles/role-permissions/permission-flags/types/SettingsRolePermissionsSettingPermission';
|
||||
import { settingsDraftRoleFamilyState } from '@/settings/roles/states/settingsDraftRoleFamilyState';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilState } from 'recoil';
|
||||
|
||||
import { H2Title, IconMail, IconTool } from 'twenty-ui/display';
|
||||
import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledTable = styled.div`
|
||||
border-bottom: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
`;
|
||||
|
||||
const StyledTableRows = styled.div`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledCard = styled(Card)`
|
||||
margin-bottom: ${({ theme }) => theme.spacing(4)};
|
||||
`;
|
||||
|
||||
type SettingsRolePermissionsToolSectionProps = {
|
||||
roleId: string;
|
||||
isEditable: boolean;
|
||||
};
|
||||
|
||||
export const SettingsRolePermissionsToolSection = ({
|
||||
roleId,
|
||||
isEditable,
|
||||
}: SettingsRolePermissionsToolSectionProps) => {
|
||||
const [settingsDraftRole, setSettingsDraftRole] = useRecoilState(
|
||||
settingsDraftRoleFamilyState(roleId),
|
||||
);
|
||||
|
||||
const toolPermissionsConfig: SettingsRolePermissionsSettingPermission[] = [
|
||||
{
|
||||
key: PermissionFlagType.SEND_EMAIL_TOOL,
|
||||
name: t`Send Email`,
|
||||
description: t`Allow sending emails using connected accounts`,
|
||||
Icon: IconMail,
|
||||
isToolPermission: true,
|
||||
},
|
||||
];
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`Action Permissions`}
|
||||
description={t`Permissions for performing automated actions.`}
|
||||
/>
|
||||
<StyledCard rounded>
|
||||
<SettingsOptionCardContentToggle
|
||||
Icon={IconTool}
|
||||
title={t`All Actions Access`}
|
||||
description={t`Grants permission to perform all available actions without restriction.`}
|
||||
checked={settingsDraftRole.canAccessAllTools}
|
||||
disabled={!isEditable}
|
||||
onChange={() => {
|
||||
setSettingsDraftRole({
|
||||
...settingsDraftRole,
|
||||
canAccessAllTools: !settingsDraftRole.canAccessAllTools,
|
||||
});
|
||||
}}
|
||||
/>
|
||||
</StyledCard>
|
||||
<AnimatedExpandableContainer
|
||||
isExpanded={!settingsDraftRole.canAccessAllTools}
|
||||
dimension="height"
|
||||
animationDurations={{
|
||||
opacity: 0.2,
|
||||
size: 0.4,
|
||||
}}
|
||||
mode="scroll-height"
|
||||
containAnimation={false}
|
||||
>
|
||||
<StyledTable>
|
||||
<SettingsRolePermissionsSettingsTableHeader
|
||||
roleId={roleId}
|
||||
settingsPermissionsConfig={toolPermissionsConfig}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
<StyledTableRows>
|
||||
{toolPermissionsConfig.map((permission) => (
|
||||
<SettingsRolePermissionsSettingsTableRow
|
||||
key={permission.key}
|
||||
roleId={roleId}
|
||||
permission={permission}
|
||||
isEditable={isEditable}
|
||||
/>
|
||||
))}
|
||||
</StyledTableRows>
|
||||
</StyledTable>
|
||||
</AnimatedExpandableContainer>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
@ -1,9 +1,10 @@
|
||||
import { IconComponent } from 'twenty-ui/display';
|
||||
import { SettingPermissionType } from '~/generated-metadata/graphql';
|
||||
import { PermissionFlagType } from '~/generated-metadata/graphql';
|
||||
|
||||
export type SettingsRolePermissionsSettingPermission = {
|
||||
key: SettingPermissionType;
|
||||
key: PermissionFlagType;
|
||||
name: string;
|
||||
description: string;
|
||||
Icon: IconComponent;
|
||||
isToolPermission?: boolean;
|
||||
};
|
||||
@ -28,7 +28,7 @@ import {
|
||||
useCreateOneRoleMutation,
|
||||
useUpdateOneRoleMutation,
|
||||
useUpsertObjectPermissionsMutation,
|
||||
useUpsertSettingPermissionsMutation,
|
||||
useUpsertPermissionFlagsMutation,
|
||||
} from '~/generated-metadata/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
import { getDirtyFields } from '~/utils/getDirtyFields';
|
||||
@ -45,6 +45,7 @@ const ROLE_BASIC_KEYS: Array<keyof Role> = [
|
||||
'description',
|
||||
'icon',
|
||||
'canUpdateAllSettings',
|
||||
'canAccessAllTools',
|
||||
'canReadAllObjectRecords',
|
||||
'canUpdateAllObjectRecords',
|
||||
'canSoftDeleteAllObjectRecords',
|
||||
@ -61,7 +62,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
|
||||
const [createRole] = useCreateOneRoleMutation();
|
||||
const [updateRole] = useUpdateOneRoleMutation();
|
||||
const [upsertSettingPermissions] = useUpsertSettingPermissionsMutation();
|
||||
const [upsertPermissionFlags] = useUpsertPermissionFlagsMutation();
|
||||
const [upsertObjectPermissions] = useUpsertObjectPermissionsMutation();
|
||||
|
||||
const [isSaving, setIsSaving] = useState(false);
|
||||
@ -144,6 +145,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
description: settingsDraftRole.description,
|
||||
icon: settingsDraftRole.icon,
|
||||
canUpdateAllSettings: settingsDraftRole.canUpdateAllSettings,
|
||||
canAccessAllTools: settingsDraftRole.canAccessAllTools,
|
||||
canReadAllObjectRecords:
|
||||
settingsDraftRole.canReadAllObjectRecords,
|
||||
canUpdateAllObjectRecords:
|
||||
@ -161,14 +163,14 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
return;
|
||||
}
|
||||
|
||||
if (isDefined(dirtyFields.settingPermissions)) {
|
||||
await upsertSettingPermissions({
|
||||
if (isDefined(dirtyFields.permissionFlags)) {
|
||||
await upsertPermissionFlags({
|
||||
variables: {
|
||||
upsertSettingPermissionsInput: {
|
||||
upsertPermissionFlagsInput: {
|
||||
roleId: data.createOneRole.id,
|
||||
settingPermissionKeys:
|
||||
settingsDraftRole.settingPermissions?.map(
|
||||
(settingPermission) => settingPermission.setting,
|
||||
permissionFlagKeys:
|
||||
settingsDraftRole.permissionFlags?.map(
|
||||
(permissionFlag) => permissionFlag.flag,
|
||||
) ?? [],
|
||||
},
|
||||
},
|
||||
@ -214,14 +216,14 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
roleId: data.createOneRole.id,
|
||||
});
|
||||
} else {
|
||||
if (isDefined(dirtyFields.settingPermissions)) {
|
||||
await upsertSettingPermissions({
|
||||
if (isDefined(dirtyFields.permissionFlags)) {
|
||||
await upsertPermissionFlags({
|
||||
variables: {
|
||||
upsertSettingPermissionsInput: {
|
||||
upsertPermissionFlagsInput: {
|
||||
roleId: roleId,
|
||||
settingPermissionKeys:
|
||||
settingsDraftRole.settingPermissions?.map(
|
||||
(settingPermission) => settingPermission.setting,
|
||||
permissionFlagKeys:
|
||||
settingsDraftRole.permissionFlags?.map(
|
||||
(permissionFlag) => permissionFlag.flag,
|
||||
) ?? [],
|
||||
},
|
||||
},
|
||||
@ -239,6 +241,7 @@ export const SettingsRole = ({ roleId, isCreateMode }: SettingsRoleProps) => {
|
||||
description: settingsDraftRole.description,
|
||||
icon: settingsDraftRole.icon,
|
||||
canUpdateAllSettings: settingsDraftRole.canUpdateAllSettings,
|
||||
canAccessAllTools: settingsDraftRole.canAccessAllTools,
|
||||
canReadAllObjectRecords:
|
||||
settingsDraftRole.canReadAllObjectRecords,
|
||||
canUpdateAllObjectRecords:
|
||||
|
||||
@ -42,6 +42,7 @@ export const SettingsRoleCreateEffect = ({
|
||||
description: '',
|
||||
icon: 'IconUser',
|
||||
canUpdateAllSettings: true,
|
||||
canAccessAllTools: true,
|
||||
canReadAllObjectRecords: true,
|
||||
canUpdateAllObjectRecords: true,
|
||||
canSoftDeleteAllObjectRecords: true,
|
||||
|
||||
@ -13,9 +13,10 @@ export const settingsDraftRoleFamilyState = createFamilyState<Role, string>({
|
||||
canSoftDeleteAllObjectRecords: false,
|
||||
canUpdateAllObjectRecords: false,
|
||||
canUpdateAllSettings: false,
|
||||
canAccessAllTools: false,
|
||||
isEditable: false,
|
||||
workspaceMembers: [],
|
||||
settingPermissions: [],
|
||||
permissionFlags: [],
|
||||
objectPermissions: [],
|
||||
},
|
||||
});
|
||||
|
||||
Reference in New Issue
Block a user