diff --git a/apps/meteor/client/components/GenericModal.tsx b/apps/meteor/client/components/GenericModal.tsx index b03f83f95a54..187d73444920 100644 --- a/apps/meteor/client/components/GenericModal.tsx +++ b/apps/meteor/client/components/GenericModal.tsx @@ -17,10 +17,11 @@ type GenericModalProps = RequiredModalProps & { title?: string | ReactElement; icon?: ComponentProps['name'] | ReactElement | null; confirmDisabled?: boolean; + tagline?: ReactNode; onCancel?: () => void; onClose?: () => void; onConfirm: () => void; -}; +} & Omit, 'title'>; const iconMap: Record['name']> = { danger: 'modal-warning', @@ -68,6 +69,7 @@ const GenericModal: FC = ({ onConfirm, dontAskAgain, confirmDisabled, + tagline, ...props }) => { const t = useTranslation(); @@ -77,6 +79,7 @@ const GenericModal: FC = ({ {renderIcon(icon, variant)} + {tagline && {tagline}} {title ?? t('Are_you_sure')} diff --git a/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx b/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx new file mode 100644 index 000000000000..c6b005c1293d --- /dev/null +++ b/apps/meteor/client/views/admin/permissions/CustomRoleUpsellModal.tsx @@ -0,0 +1,36 @@ +import { Modal, Box } from '@rocket.chat/fuselage'; +import { useTranslation } from '@rocket.chat/ui-contexts'; +import type { VFC } from 'react'; +import React from 'react'; + +import GenericModal from '../../../components/GenericModal'; + +type CustomRoleUpsellModalProps = { + onClose: () => void; +}; + +const CustomRoleUpsellModal: VFC = ({ onClose }) => { + const t = useTranslation(); + return ( + window.open('https://go.rocket.chat/i/ce-custom-roles')} + variant='warning' + icon={null} + > + + + {t('Custom_roles_upsell_add_custom_roles_workspace')} + +
+

{t('Custom_roles_upsell_add_custom_roles_workspace_description')}

+
+ ); +}; + +export default CustomRoleUpsellModal; diff --git a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx index 21bb594cc848..3dd9fc586328 100644 --- a/apps/meteor/client/views/admin/permissions/EditRolePage.tsx +++ b/apps/meteor/client/views/admin/permissions/EditRolePage.tsx @@ -95,14 +95,14 @@ const EditRolePage = ({ role, isEnterprise }: { role?: IRole; isEnterprise: bool - + - {!role?.protected && role?._id && ( diff --git a/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx b/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx index a0a79c237972..cbede6b0df61 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsContextBar.tsx @@ -1,9 +1,11 @@ import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useRouteParameter, useRoute, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRouteParameter, useRoute, useTranslation, useSetModal } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; -import React from 'react'; +import React, { useEffect } from 'react'; import VerticalBar from '../../../components/VerticalBar'; +import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; +import CustomRoleUpsellModal from './CustomRoleUpsellModal'; import EditRolePageWithData from './EditRolePageWithData'; const PermissionsContextBar = (): ReactElement | null => { @@ -11,11 +13,23 @@ const PermissionsContextBar = (): ReactElement | null => { const _id = useRouteParameter('_id'); const context = useRouteParameter('context'); const router = useRoute('admin-permissions'); + const setModal = useSetModal(); + const { data } = useIsEnterprise(); + const isEnterprise = !!data?.isEnterprise; const handleCloseVerticalBar = useMutableCallback(() => { router.push({}); }); + useEffect(() => { + if (context !== 'new' || isEnterprise) { + return; + } + + setModal( setModal()} />); + handleCloseVerticalBar(); + }, [context, isEnterprise, handleCloseVerticalBar, setModal]); + return ( (context && ( diff --git a/apps/meteor/client/views/admin/permissions/PermissionsRouter.tsx b/apps/meteor/client/views/admin/permissions/PermissionsRouter.tsx index a746a4bf3452..31ab597df913 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsRouter.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsRouter.tsx @@ -2,6 +2,8 @@ import { useRouteParameter, usePermission } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React from 'react'; +import PageSkeleton from '../../../components/PageSkeleton'; +import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; import NotAuthorizedPage from '../../notAuthorized/NotAuthorizedPage'; import PermissionsTable from './PermissionsTable'; import UsersInRole from './UsersInRole'; @@ -10,6 +12,11 @@ const PermissionsRouter = (): ReactElement => { const canViewPermission = usePermission('access-permissions'); const canViewSettingPermission = usePermission('access-setting-permissions'); const context = useRouteParameter('context'); + const { data, isLoading } = useIsEnterprise(); + + if (isLoading) { + ; + } if (!canViewPermission && !canViewSettingPermission) { return ; @@ -19,7 +26,7 @@ const PermissionsRouter = (): ReactElement => { return ; } - return ; + return ; }; export default PermissionsRouter; diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx index 63e04b8ace9d..d51c7f5cd2cf 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/PermissionsTable.tsx @@ -1,19 +1,20 @@ -import { Margins, Icon, Tabs, Button, Pagination, Tile } from '@rocket.chat/fuselage'; +import { Margins, Tabs, Button, Pagination, Tile } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; -import { useRoute, usePermission, useMethod, useTranslation } from '@rocket.chat/ui-contexts'; +import { useRoute, usePermission, useMethod, useTranslation, useSetModal } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; import React, { useState } from 'react'; import { GenericTable, GenericTableHeader, GenericTableHeaderCell, GenericTableBody } from '../../../../components/GenericTable'; import { usePagination } from '../../../../components/GenericTable/hooks/usePagination'; import Page from '../../../../components/Page'; +import CustomRoleUpsellModal from '../CustomRoleUpsellModal'; import PermissionsContextBar from '../PermissionsContextBar'; import { usePermissionsAndRoles } from '../hooks/usePermissionsAndRoles'; import PermissionRow from './PermissionRow'; import PermissionsTableFilter from './PermissionsTableFilter'; import RoleHeader from './RoleHeader'; -const PermissionsTable = (): ReactElement => { +const PermissionsTable = ({ isEnterprise }: { isEnterprise: boolean }): ReactElement => { const t = useTranslation(); const [filter, setFilter] = useState(''); const canViewPermission = usePermission('access-permissions'); @@ -21,6 +22,7 @@ const PermissionsTable = (): ReactElement => { const defaultType = canViewPermission ? 'permissions' : 'settings'; const [type, setType] = useState(defaultType); const router = useRoute('admin-permissions'); + const setModal = useSetModal(); const grantRole = useMethod('authorization:addPermissionToRole'); const removeRole = useMethod('authorization:removeRoleFromPermission'); @@ -43,6 +45,10 @@ const PermissionsTable = (): ReactElement => { }); const handleAdd = useMutableCallback(() => { + if (!isEnterprise) { + setModal( setModal()} />); + return; + } router.push({ context: 'new', }); @@ -52,8 +58,8 @@ const PermissionsTable = (): ReactElement => { - diff --git a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx index a0d488b11370..fc801bbd4c5d 100644 --- a/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx +++ b/apps/meteor/client/views/admin/permissions/PermissionsTable/RoleHeader.tsx @@ -1,6 +1,5 @@ import type { IRole } from '@rocket.chat/core-typings'; -import { css } from '@rocket.chat/css-in-js'; -import { Margins, Box, Icon } from '@rocket.chat/fuselage'; +import { Margins, Icon, Button } from '@rocket.chat/fuselage'; import { useMutableCallback } from '@rocket.chat/fuselage-hooks'; import { useRoute } from '@rocket.chat/ui-contexts'; import type { ReactElement } from 'react'; @@ -26,24 +25,12 @@ const RoleHeader = ({ _id, name, description }: RoleHeaderProps): ReactElement = return ( - + ); }; diff --git a/apps/meteor/client/views/admin/permissions/RoleForm.tsx b/apps/meteor/client/views/admin/permissions/RoleForm.tsx index 4563f4585694..6fcae6b620b2 100644 --- a/apps/meteor/client/views/admin/permissions/RoleForm.tsx +++ b/apps/meteor/client/views/admin/permissions/RoleForm.tsx @@ -9,9 +9,10 @@ type RoleFormProps = { className?: string; editing?: boolean; isProtected?: boolean; + isDisabled?: boolean; }; -const RoleForm = ({ className, editing = false, isProtected = false }: RoleFormProps): ReactElement => { +const RoleForm = ({ className, editing = false, isProtected = false, isDisabled = false }: RoleFormProps): ReactElement => { const t = useTranslation(); const { register, @@ -32,14 +33,14 @@ const RoleForm = ({ className, editing = false, isProtected = false }: RoleFormP {t('Role')} - + {errors?.name && {t('error-the-field-is-required', { field: t('Role') })}} {t('Description')} - + {'Leave the description field blank if you dont want to show the role'} @@ -49,7 +50,9 @@ const RoleForm = ({ className, editing = false, isProtected = false }: RoleFormP + )} /> @@ -60,7 +63,7 @@ const RoleForm = ({ className, editing = false, isProtected = false }: RoleFormP } + render={({ field }): ReactElement => } /> diff --git a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json index baf23096e17e..277f01212f17 100644 --- a/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json +++ b/apps/meteor/packages/rocketchat-i18n/i18n/en.i18n.json @@ -1435,6 +1435,9 @@ "Custom_OAuth_has_been_removed": "Custom OAuth has been removed", "Custom_oauth_helper": "When setting up your OAuth Provider, you'll have to inform a Callback URL. Use
%s
.", "Custom_oauth_unique_name": "Custom OAuth unique name", + "Custom_roles": "Custom roles", + "Custom_roles_upsell_add_custom_roles_workspace": "Add custom roles to suit your workspace", + "Custom_roles_upsell_add_custom_roles_workspace_description": "Custom roles allow you to set permissions for the people in your workspace. Set all the roles you need to make sure people have a safe environment to work on.", "Custom_Script_Logged_In": "Custom Script for Logged In Users", "Custom_Script_Logged_In_Description": "Custom Script that will run ALWAYS and to ANY user that is logged in. e.g. (whenever you enter the chat and you are logged in)", "Custom_Script_Logged_Out": "Custom Script for Logged Out Users", @@ -1836,6 +1839,7 @@ "Enterprise": "Enterprise", "Enterprise_capabilities": "Enterprise capabilities", "Enterprise_Description": "Manually update your Enterprise license.", + "Enterprise_feature": "Enterprise feature", "Enterprise_License": "Enterprise License", "Enterprise_License_Description": "If your workspace is registered and license is provided by Rocket.Chat Cloud you don't need to manually update the license here.", "Entertainment": "Entertainment", @@ -4587,6 +4591,7 @@ "Take_rocket_chat_with_you_with_mobile_applications": "Take Rocket.Chat with you with mobile applications.", "Taken_at": "Taken at", "Talk_Time": "Talk Time", + "Talk_to_sales": "Talk to sales", "Talk_to_your_workspace_administrator_about_enabling_video_conferencing": "Talk to your workspace administrator about enabling video conferencing", "Target user not allowed to receive messages": "Target user not allowed to receive messages", "TargetRoom": "Target Room", diff --git a/apps/meteor/public/images/custom-role-upsell-modal.png b/apps/meteor/public/images/custom-role-upsell-modal.png new file mode 100644 index 000000000000..fb97846acdc0 Binary files /dev/null and b/apps/meteor/public/images/custom-role-upsell-modal.png differ diff --git a/apps/meteor/tests/e2e/administration.spec.ts b/apps/meteor/tests/e2e/administration.spec.ts index c9f1ebd072a8..52a288969ef9 100644 --- a/apps/meteor/tests/e2e/administration.spec.ts +++ b/apps/meteor/tests/e2e/administration.spec.ts @@ -2,6 +2,7 @@ import { faker } from '@faker-js/faker'; import { test, expect } from './utils/test'; import { Admin } from './page-objects'; +import { IS_EE } from './config/constants'; test.use({ storageState: 'admin-session.json' }); @@ -58,6 +59,18 @@ test.describe.parallel('administration', () => { }); }); + test.describe('Permissions', () => { + test.beforeEach(async ({ page }) => { + await page.goto('/admin/permissions'); + }); + + test('expect open upsell modal if not enterprise', async ({ page }) => { + test.skip(IS_EE); + await poAdmin.btnCreateRole.click(); + await page.waitForSelector('dialog[id="custom-roles"]'); + }); + }); + test.describe('Settings', () => { test.describe('General', () => { test.beforeEach(async ({ page }) => { diff --git a/apps/meteor/tests/e2e/page-objects/admin.ts b/apps/meteor/tests/e2e/page-objects/admin.ts index 9f207d6b4aa4..4f37d7a604d3 100644 --- a/apps/meteor/tests/e2e/page-objects/admin.ts +++ b/apps/meteor/tests/e2e/page-objects/admin.ts @@ -129,6 +129,10 @@ export class Admin { return this.page.locator('//label[@title="Assets_logo"]/following-sibling::span >> role=button[name="Delete"]'); } + get btnCreateRole(): Locator { + return this.page.locator('button[name="New role"]'); + } + get inputAssetsLogo(): Locator { return this.page.locator('//label[@title="Assets_logo"]/following-sibling::span >> input[type="file"]'); }