Add Import CSV and Export CSV Permissions (#13421)

Co-authored-by: Félix Malfait <felix.malfait@gmail.com>
Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
Abdul Rahman
2025-07-25 11:37:38 +05:30
committed by GitHub
parent f411bd1b0e
commit 4b95de6775
28 changed files with 118 additions and 71 deletions
@@ -1,4 +1,4 @@
import { useHasSettingsPermission } from '@/settings/roles/hooks/useHasSettingsPermission';
import { useHasPermissionFlag } from '@/settings/roles/hooks/useHasPermissionFlag';
import { SettingsPath } from '@/types/SettingsPath';
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
import { ReactNode } from 'react';
@@ -17,7 +17,7 @@ export const SettingsProtectedRouteWrapper = ({
settingsPermission,
requiredFeatureFlag,
}: SettingsProtectedRouteWrapperProps) => {
const hasPermission = useHasSettingsPermission(settingsPermission);
const hasPermission = useHasPermissionFlag(settingsPermission);
const requiredFeatureFlagEnabled = useIsFeatureEnabled(
requiredFeatureFlag || null,
);
@@ -13,7 +13,7 @@ import {
import { currentUserState } from '@/auth/states/currentUserState';
import { billingState } from '@/client-config/states/billingState';
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
import { useSettingsPermissionMap } from '@/settings/roles/hooks/useSettingsPermissionMap';
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
import { SnackBarComponentInstanceContextProvider } from '@/ui/feedback/snack-bar-manager/scopes/SnackBarComponentInstanceContextProvider';
const mockCurrentUser = {
@@ -53,13 +53,13 @@ const Wrapper = ({ children }: { children: ReactNode }) => (
</MockedProvider>
);
jest.mock('@/settings/roles/hooks/useSettingsPermissionMap', () => ({
useSettingsPermissionMap: jest.fn(),
jest.mock('@/settings/roles/hooks/usePermissionFlagMap', () => ({
usePermissionFlagMap: jest.fn(),
}));
describe('useSettingsNavigationItems', () => {
it('should hide workspace settings when no permissions', () => {
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({
(usePermissionFlagMap as jest.Mock).mockImplementation(() => ({
[PermissionFlagType.WORKSPACE]: false,
[PermissionFlagType.WORKSPACE_MEMBERS]: false,
[PermissionFlagType.DATA_MODEL]: false,
@@ -80,7 +80,7 @@ describe('useSettingsNavigationItems', () => {
});
it('should show workspace settings when has permissions', () => {
(useSettingsPermissionMap as jest.Mock).mockImplementation(() => ({
(usePermissionFlagMap as jest.Mock).mockImplementation(() => ({
[PermissionFlagType.WORKSPACE]: true,
[PermissionFlagType.WORKSPACE_MEMBERS]: true,
[PermissionFlagType.DATA_MODEL]: true,
@@ -4,7 +4,7 @@ import { useAuth } from '@/auth/hooks/useAuth';
import { currentUserState } from '@/auth/states/currentUserState';
import { billingState } from '@/client-config/states/billingState';
import { labPublicFeatureFlagsState } from '@/client-config/states/labPublicFeatureFlagsState';
import { useSettingsPermissionMap } from '@/settings/roles/hooks/useSettingsPermissionMap';
import { usePermissionFlagMap } from '@/settings/roles/hooks/usePermissionFlagMap';
import { NavigationDrawerItemIndentationLevel } from '@/ui/navigation/navigation-drawer/components/NavigationDrawerItem';
import { t } from '@lingui/core/macro';
import { useRecoilValue } from 'recoil';
@@ -63,7 +63,7 @@ const useSettingsNavigationItems = (): SettingsNavigationSection[] => {
false;
const labPublicFeatureFlags = useRecoilValue(labPublicFeatureFlagsState);
const permissionMap = useSettingsPermissionMap();
const permissionMap = usePermissionFlagMap();
return [
{
label: t`User`,
@@ -4,9 +4,7 @@ import { useRecoilValue } from 'recoil';
import { WorkspaceActivationStatus } from 'twenty-shared/workspace';
import { PermissionFlagType } from '~/generated/graphql';
export const useHasSettingsPermission = (
permissionFlag?: PermissionFlagType,
) => {
export const useHasPermissionFlag = (permissionFlag?: PermissionFlagType) => {
const currentWorkspace = useRecoilValue(currentWorkspaceState);
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
@@ -22,7 +20,7 @@ export const useHasSettingsPermission = (
return true;
}
const currentUserWorkspaceSetting = currentUserWorkspace?.settingsPermissions;
const currentUserWorkspaceSetting = currentUserWorkspace?.permissionFlags;
if (!currentUserWorkspaceSetting) {
return false;
@@ -3,14 +3,11 @@ import { useRecoilValue } from 'recoil';
import { PermissionFlagType } from '~/generated/graphql';
import { buildRecordFromKeysWithSameValue } from '~/utils/array/buildRecordFromKeysWithSameValue';
export const useSettingsPermissionMap = (): Record<
PermissionFlagType,
boolean
> => {
export const usePermissionFlagMap = (): Record<PermissionFlagType, boolean> => {
const currentUserWorkspace = useRecoilValue(currentUserWorkspaceState);
const currentUserWorkspaceSettingsPermissions =
currentUserWorkspace?.settingsPermissions;
currentUserWorkspace?.permissionFlags;
const initialPermissions = buildRecordFromKeysWithSameValue(
Object.values(PermissionFlagType),
@@ -7,7 +7,13 @@ import styled from '@emotion/styled';
import { t } from '@lingui/core/macro';
import { useRecoilState } from 'recoil';
import { H2Title, IconMail, IconTool } from 'twenty-ui/display';
import {
H2Title,
IconFileExport,
IconFileImport,
IconMail,
IconTool,
} from 'twenty-ui/display';
import { AnimatedExpandableContainer, Card, Section } from 'twenty-ui/layout';
import { PermissionFlagType } from '~/generated-metadata/graphql';
@@ -45,19 +51,30 @@ export const SettingsRolePermissionsToolSection = ({
Icon: IconMail,
isToolPermission: true,
},
{
key: PermissionFlagType.IMPORT_CSV,
name: t`Import CSV`,
description: t`Allow importing data from CSV files`,
Icon: IconFileImport,
isToolPermission: true,
},
{
key: PermissionFlagType.EXPORT_CSV,
name: t`Export CSV`,
description: t`Allow exporting data to CSV files`,
Icon: IconFileExport,
isToolPermission: true,
},
];
return (
<Section>
<H2Title
title={t`Action Permissions`}
description={t`Permissions for performing automated actions.`}
/>
<H2Title title={t`Actions`} description={t`Actions permissions`} />
<StyledCard rounded>
<SettingsOptionCardContentToggle
Icon={IconTool}
title={t`All Actions Access`}
description={t`Grants permission to perform all available actions without restriction.`}
description={t`Grants permission to perform all available actions without restriction`}
checked={settingsDraftRole.canAccessAllTools}
disabled={!isEditable}
onChange={() => {