add role update (#11217)
## Context This PR introduces the new Create and Edit role components, behind the PERMISSIONS_ENABLED_V2 feature flag.
This commit is contained in:
@@ -1,49 +0,0 @@
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { RolesTableHeader } from '@/settings/roles/components/RolesTableHeader';
|
||||
import { RolesTableRow } from '@/settings/roles/components/RolesTableRow';
|
||||
import { Button, H2Title, IconPlus, Section } from 'twenty-ui';
|
||||
import { Role } from '~/generated-metadata/graphql';
|
||||
|
||||
const StyledCreateRoleSection = styled(Section)`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledTableRows = styled.div`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
export const Roles = ({ roles }: { roles: Role[] }) => {
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`All roles`}
|
||||
description={t`Assign roles to specify each member's access permissions`}
|
||||
/>
|
||||
<Table>
|
||||
<RolesTableHeader />
|
||||
<StyledTableRows>
|
||||
{roles.map((role) => (
|
||||
<RolesTableRow key={role.id} role={role} />
|
||||
))}
|
||||
</StyledTableRows>
|
||||
</Table>
|
||||
<StyledCreateRoleSection>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={t`Create Role`}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
soon
|
||||
/>
|
||||
</StyledCreateRoleSection>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
import { H3Title } from 'twenty-ui';
|
||||
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
|
||||
import { getSettingsPath } from '~/utils/navigation/getSettingsPath';
|
||||
|
||||
import { SettingsPageContainer } from '@/settings/components/SettingsPageContainer';
|
||||
import { SettingsRoleDefaultRole } from '@/settings/roles/components/SettingsRolesDefaultRole';
|
||||
|
||||
import { SettingsRolesList } from '@/settings/roles/components/SettingsRolesList';
|
||||
import { settingsAllRolesSelector } from '@/settings/roles/states/settingsAllRolesSelector';
|
||||
import { settingsRolesIsLoadingState } from '@/settings/roles/states/settingsRolesIsLoadingState';
|
||||
import { SubMenuTopBarContainer } from '@/ui/layout/page/components/SubMenuTopBarContainer';
|
||||
import { Trans, useLingui } from '@lingui/react/macro';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
export const SettingsRolesContainer = () => {
|
||||
const { t } = useLingui();
|
||||
|
||||
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
|
||||
const settingsRolesIsLoading = useRecoilValue(settingsRolesIsLoadingState);
|
||||
|
||||
if (settingsRolesIsLoading) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<SubMenuTopBarContainer
|
||||
title={<H3Title title={t`Roles`} />}
|
||||
links={[
|
||||
{
|
||||
children: <Trans>Workspace</Trans>,
|
||||
href: getSettingsPath(SettingsPath.Workspace),
|
||||
},
|
||||
{ children: <Trans>Roles</Trans> },
|
||||
]}
|
||||
>
|
||||
<SettingsPageContainer>
|
||||
<SettingsRolesList />
|
||||
<SettingsRoleDefaultRole roles={settingsAllRoles} />
|
||||
</SettingsPageContainer>
|
||||
</SubMenuTopBarContainer>
|
||||
);
|
||||
};
|
||||
+2
-2
@@ -6,15 +6,15 @@ import { SettingsOptionCardContentSelect } from '@/settings/components/SettingsO
|
||||
import { Select } from '@/ui/input/components/Select';
|
||||
import { t } from '@lingui/core/macro';
|
||||
import { useRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Card, H2Title, IconUserPin, Section } from 'twenty-ui';
|
||||
import {
|
||||
Role,
|
||||
UpdateWorkspaceMutation,
|
||||
useUpdateWorkspaceMutation,
|
||||
} from '~/generated/graphql';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
|
||||
export const RolesDefaultRole = ({ roles }: { roles: Role[] }) => {
|
||||
export const SettingsRoleDefaultRole = ({ roles }: { roles: Role[] }) => {
|
||||
const [updateWorkspace] = useUpdateWorkspaceMutation();
|
||||
|
||||
const [currentWorkspace, setCurrentWorkspace] = useRecoilState(
|
||||
@@ -0,0 +1,72 @@
|
||||
import { Table } from '@/ui/layout/table/components/Table';
|
||||
import styled from '@emotion/styled';
|
||||
import { t } from '@lingui/core/macro';
|
||||
|
||||
import { SettingsRolesTableHeader } from '@/settings/roles/components/SettingsRolesTableHeader';
|
||||
import { SettingsRolesTableRow } from '@/settings/roles/components/SettingsRolesTableRow';
|
||||
import { settingsAllRolesSelector } from '@/settings/roles/states/settingsAllRolesSelector';
|
||||
import { SettingsPath } from '@/types/SettingsPath';
|
||||
import { TableCell } from '@/ui/layout/table/components/TableCell';
|
||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||
import { useRecoilValue } from 'recoil';
|
||||
import { Button, H2Title, IconPlus, Section } from 'twenty-ui';
|
||||
import { FeatureFlagKey } from '~/generated/graphql';
|
||||
import { useNavigateSettings } from '~/hooks/useNavigateSettings';
|
||||
|
||||
const StyledCreateRoleSection = styled(Section)`
|
||||
border-top: 1px solid ${({ theme }) => theme.border.color.light};
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledTableRows = styled.div`
|
||||
padding-bottom: ${({ theme }) => theme.spacing(2)};
|
||||
padding-top: ${({ theme }) => theme.spacing(2)};
|
||||
`;
|
||||
|
||||
const StyledNoRoles = styled(TableCell)`
|
||||
color: ${({ theme }) => theme.font.color.tertiary};
|
||||
`;
|
||||
|
||||
export const SettingsRolesList = () => {
|
||||
const navigateSettings = useNavigateSettings();
|
||||
const isPermissionsV2Enabled = useIsFeatureEnabled(
|
||||
FeatureFlagKey.IsPermissionsV2Enabled,
|
||||
);
|
||||
|
||||
const settingsAllRoles = useRecoilValue(settingsAllRolesSelector);
|
||||
|
||||
return (
|
||||
<Section>
|
||||
<H2Title
|
||||
title={t`All roles`}
|
||||
description={t`Assign roles to specify each member's access permissions`}
|
||||
/>
|
||||
<Table>
|
||||
<SettingsRolesTableHeader />
|
||||
<StyledTableRows>
|
||||
{settingsAllRoles.length === 0 ? (
|
||||
<StyledNoRoles>{t`No roles found`}</StyledNoRoles>
|
||||
) : (
|
||||
settingsAllRoles.map((role) => (
|
||||
<SettingsRolesTableRow key={role.id} role={role} />
|
||||
))
|
||||
)}
|
||||
</StyledTableRows>
|
||||
</Table>
|
||||
<StyledCreateRoleSection>
|
||||
<Button
|
||||
Icon={IconPlus}
|
||||
title={t`Create Role`}
|
||||
variant="secondary"
|
||||
size="small"
|
||||
soon={!isPermissionsV2Enabled}
|
||||
disabled={!isPermissionsV2Enabled}
|
||||
onClick={() => navigateSettings(SettingsPath.RoleCreate)}
|
||||
/>
|
||||
</StyledCreateRoleSection>
|
||||
</Section>
|
||||
);
|
||||
};
|
||||
+43
@@ -0,0 +1,43 @@
|
||||
import { settingsPersistedRoleFamilyState } from '@/settings/roles/states/settingsPersistedRoleFamilyState';
|
||||
import { settingsRoleIdsState } from '@/settings/roles/states/settingsRoleIdsState';
|
||||
import { settingsRolesIsLoadingState } from '@/settings/roles/states/settingsRolesIsLoadingState';
|
||||
import { useEffect } from 'react';
|
||||
import { useRecoilCallback, useSetRecoilState } from 'recoil';
|
||||
import { isDefined } from 'twenty-shared/utils';
|
||||
import { Role, useGetRolesQuery } from '~/generated/graphql';
|
||||
|
||||
export const SettingsRolesQueryEffect = () => {
|
||||
const { data, loading } = useGetRolesQuery({
|
||||
fetchPolicy: 'network-only',
|
||||
});
|
||||
|
||||
const setSettingsRolesIsLoading = useSetRecoilState(
|
||||
settingsRolesIsLoadingState,
|
||||
);
|
||||
|
||||
const populateRoles = useRecoilCallback(
|
||||
({ set }) =>
|
||||
(roles: Role[]) => {
|
||||
const roleIds = roles.map((role) => role.id);
|
||||
set(settingsRoleIdsState, roleIds);
|
||||
roles.forEach((role) => {
|
||||
set(settingsPersistedRoleFamilyState(role.id), role);
|
||||
});
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
setSettingsRolesIsLoading(loading);
|
||||
if (!loading) {
|
||||
const roles = data?.getRoles;
|
||||
if (!isDefined(roles)) {
|
||||
return;
|
||||
}
|
||||
|
||||
populateRoles(roles);
|
||||
}
|
||||
}, [data, loading, populateRoles, setSettingsRolesIsLoading]);
|
||||
|
||||
return null;
|
||||
};
|
||||
+1
-1
@@ -3,7 +3,7 @@ import { TableHeader } from '@/ui/layout/table/components/TableHeader';
|
||||
import { TableRow } from '@/ui/layout/table/components/TableRow';
|
||||
import { Trans } from '@lingui/react/macro';
|
||||
|
||||
export const RolesTableHeader = () => {
|
||||
export const SettingsRolesTableHeader = () => {
|
||||
return (
|
||||
<Table>
|
||||
<TableRow gridAutoColumns="332px 3fr 2fr 1fr">
|
||||
+1
-1
@@ -52,7 +52,7 @@ const StyledTableRow = styled(TableRow)`
|
||||
}
|
||||
`;
|
||||
|
||||
export const RolesTableRow = ({ role }: { role: Role }) => {
|
||||
export const SettingsRolesTableRow = ({ role }: { role: Role }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const navigateSettings = useNavigateSettings();
|
||||
-24
@@ -1,24 +0,0 @@
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { ComponentDecorator, RouterDecorator } from 'twenty-ui';
|
||||
|
||||
import { Roles } from '@/settings/roles/components/Roles';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { getRolesMock } from '~/testing/mock-data/roles';
|
||||
|
||||
const meta: Meta<typeof Roles> = {
|
||||
title: 'Modules/Settings/Roles/Roles',
|
||||
component: Roles,
|
||||
decorators: [ComponentDecorator, I18nFrontDecorator, RouterDecorator],
|
||||
parameters: {
|
||||
maxWidth: 800,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof Roles>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
roles: getRolesMock(),
|
||||
},
|
||||
};
|
||||
-43
@@ -1,43 +0,0 @@
|
||||
import { currentWorkspaceState } from '@/auth/states/currentWorkspaceState';
|
||||
import { RolesDefaultRole } from '@/settings/roles/components/RolesDefaultRole';
|
||||
import { Meta, StoryObj } from '@storybook/react';
|
||||
import { RecoilRoot } from 'recoil';
|
||||
import { ComponentDecorator } from 'twenty-ui';
|
||||
import { I18nFrontDecorator } from '~/testing/decorators/I18nFrontDecorator';
|
||||
import { getRolesMock } from '~/testing/mock-data/roles';
|
||||
import { mockCurrentWorkspace } from '~/testing/mock-data/users';
|
||||
|
||||
const rolesMock = getRolesMock();
|
||||
|
||||
const RolesDefaultRoleWrapper = () => {
|
||||
return (
|
||||
<RecoilRoot
|
||||
initializeState={(snapshot) => {
|
||||
snapshot.set(currentWorkspaceState, {
|
||||
...mockCurrentWorkspace,
|
||||
defaultRole: rolesMock[1],
|
||||
});
|
||||
}}
|
||||
>
|
||||
<RolesDefaultRole roles={rolesMock} />
|
||||
</RecoilRoot>
|
||||
);
|
||||
};
|
||||
|
||||
const meta: Meta<typeof RolesDefaultRoleWrapper> = {
|
||||
title: 'Modules/Settings/Roles/RolesDefaultRole',
|
||||
component: RolesDefaultRoleWrapper,
|
||||
decorators: [ComponentDecorator, I18nFrontDecorator],
|
||||
parameters: {
|
||||
maxWidth: 800,
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof RolesDefaultRoleWrapper>;
|
||||
|
||||
export const Default: Story = {
|
||||
args: {
|
||||
roles: rolesMock,
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user