diff --git a/apps/meteor/client/views/admin/EditableSettingsContext.ts b/apps/meteor/client/views/admin/EditableSettingsContext.ts index 24c09cde2f3b4..f54f08b7c2289 100644 --- a/apps/meteor/client/views/admin/EditableSettingsContext.ts +++ b/apps/meteor/client/views/admin/EditableSettingsContext.ts @@ -1,4 +1,4 @@ -import type { ISettingBase, ISettingColor, ISetting } from '@rocket.chat/core-typings'; +import type { ISettingBase, ISettingColor, ISetting, LicenseModule } from '@rocket.chat/core-typings'; import type { SettingsContextQuery } from '@rocket.chat/ui-contexts'; import { createContext, useContext, useMemo } from 'react'; import { useSyncExternalStore } from 'use-sync-external-store/shim'; @@ -29,6 +29,7 @@ export type EditableSettingsContextValue = { ) => [subscribe: (onStoreChange: () => void) => () => void, getSnapshot: () => ISetting['_id'][]]; readonly dispatch: (changes: Partial[]) => void; readonly isEnterprise: boolean; + readonly activeModules: LicenseModule[]; }; export const EditableSettingsContext = createContext({ @@ -38,10 +39,13 @@ export const EditableSettingsContext = createContext [(): (() => void) => (): void => undefined, (): ISetting['_id'][] => []], dispatch: () => undefined, isEnterprise: false, + activeModules: [], }); export const useIsEnterprise = (): boolean => useContext(EditableSettingsContext).isEnterprise; +export const useLicenseActiveModules = () => useContext(EditableSettingsContext).activeModules; + export const useEditableSetting = (_id: ISetting['_id']): EditableSetting | undefined => { const { queryEditableSetting } = useContext(EditableSettingsContext); diff --git a/apps/meteor/client/views/admin/settings/EditableSettingsProvider.tsx b/apps/meteor/client/views/admin/settings/EditableSettingsProvider.tsx index be33c4617bf33..68e859aa93792 100644 --- a/apps/meteor/client/views/admin/settings/EditableSettingsProvider.tsx +++ b/apps/meteor/client/views/admin/settings/EditableSettingsProvider.tsx @@ -8,7 +8,7 @@ import type { FilterOperators } from 'mongodb'; import type { MutableRefObject, ReactNode } from 'react'; import React, { useEffect, useMemo, useRef } from 'react'; -import { useIsEnterprise } from '../../../hooks/useIsEnterprise'; +import { useLicenseBase } from '../../../hooks/useLicense'; import { createReactiveSubscriptionFactory } from '../../../lib/createReactiveSubscriptionFactory'; import type { EditableSetting, EditableSettingsContextValue } from '../EditableSettingsContext'; import { EditableSettingsContext } from '../EditableSettingsContext'; @@ -193,9 +193,12 @@ const EditableSettingsProvider = ({ children, query = defaultQuery, omit = defau Tracker.flush(); }); - const { data } = useIsEnterprise(); + const { data } = useLicenseBase({ + select: (data) => ({ isEnterprise: Boolean(data?.license.license), activeModules: data?.license.activeModules }), + }); const isEnterprise = data?.isEnterprise ?? false; + const activeModules = useMemo(() => data?.activeModules ?? [], [data]); const contextValue = useMemo( () => ({ @@ -205,8 +208,9 @@ const EditableSettingsProvider = ({ children, query = defaultQuery, omit = defau queryGroupTabs, dispatch, isEnterprise, + activeModules, }), - [queryEditableSetting, queryEditableSettings, queryGroupSections, queryGroupTabs, dispatch, isEnterprise], + [queryEditableSetting, queryEditableSettings, queryGroupSections, queryGroupTabs, dispatch, isEnterprise, activeModules], ); return ; diff --git a/apps/meteor/client/views/admin/settings/Setting/Setting.tsx b/apps/meteor/client/views/admin/settings/Setting/Setting.tsx index 9e74f2e36b751..7511c7c56bd31 100644 --- a/apps/meteor/client/views/admin/settings/Setting/Setting.tsx +++ b/apps/meteor/client/views/admin/settings/Setting/Setting.tsx @@ -1,4 +1,4 @@ -import type { ISettingColor, SettingEditor, SettingValue } from '@rocket.chat/core-typings'; +import type { ISettingColor, LicenseModule, SettingEditor, SettingValue } from '@rocket.chat/core-typings'; import { isSettingColor, isSetting } from '@rocket.chat/core-typings'; import { Box, Button, Tag } from '@rocket.chat/fuselage'; import { useDebouncedCallback } from '@rocket.chat/fuselage-hooks'; @@ -9,7 +9,7 @@ import { useTranslation } from 'react-i18next'; import MemoizedSetting from './MemoizedSetting'; import MarkdownText from '../../../../components/MarkdownText'; -import { useEditableSetting, useEditableSettingsDispatch, useIsEnterprise } from '../../EditableSettingsContext'; +import { useEditableSetting, useEditableSettingsDispatch, useIsEnterprise, useLicenseActiveModules } from '../../EditableSettingsContext'; type SettingProps = { className?: string; @@ -21,6 +21,7 @@ function Setting({ className = undefined, settingId, sectionChanged }: SettingPr const setting = useEditableSetting(settingId); const persistedSetting = useSettingStructure(settingId); const isEnterprise = useIsEnterprise(); + const activeModules = useLicenseActiveModules(); if (!setting || !persistedSetting) { throw new Error(`Setting ${settingId} not found`); @@ -105,12 +106,21 @@ function Setting({ className = undefined, settingId, sectionChanged }: SettingPr ) : undefined, [i18n, i18nDescription, t], ); + const callout = useMemo( () => alert && , [alert, i18n, t], ); - const shouldDisableEnterprise = setting.enterprise && !isEnterprise; + const hasSettingModule = useMemo(() => { + if (!setting?.modules) { + return false; + } + + return setting.modules.every((module) => activeModules.includes(module as LicenseModule)); + }, [activeModules, setting?.modules]); + + const shouldDisableEnterprise = setting.enterprise && !isEnterprise && !hasSettingModule; const PRICING_URL = 'https://go.rocket.chat/i/see-paid-plan-customize-homepage'; diff --git a/apps/meteor/client/views/omnichannel/contactInfo/ContactInfo/ContactInfo.tsx b/apps/meteor/client/views/omnichannel/contactInfo/ContactInfo/ContactInfo.tsx index de955b08b72b7..a54867cdff74b 100644 --- a/apps/meteor/client/views/omnichannel/contactInfo/ContactInfo/ContactInfo.tsx +++ b/apps/meteor/client/views/omnichannel/contactInfo/ContactInfo/ContactInfo.tsx @@ -1,7 +1,7 @@ import type { ILivechatContact, Serialized } from '@rocket.chat/core-typings'; import { Box, Button, ButtonGroup, Callout, IconButton, Tabs, TabsItem } from '@rocket.chat/fuselage'; import { UserAvatar } from '@rocket.chat/ui-avatar'; -import { usePermission, useRouter, useRouteParameter, useSetModal } from '@rocket.chat/ui-contexts'; +import { usePermission, useRouteParameter, useSetModal } from '@rocket.chat/ui-contexts'; import React from 'react'; import { useTranslation } from 'react-i18next'; @@ -22,21 +22,16 @@ type ContactInfoProps = { const ContactInfo = ({ contact, onClose }: ContactInfoProps) => { const { t } = useTranslation(); - const { getRouteName } = useRouter(); const setModal = useSetModal(); - const currentRouteName = getRouteName(); const handleNavigate = useContactRoute(); const context = useRouteParameter('context'); const formatDate = useFormatDate(); - const canEditContact = usePermission('edit-omnichannel-contact'); const { name, emails, phones, conflictingFields, createdAt, lastChat, contactManager, customFields: userCustomFields } = contact; const hasConflicts = conflictingFields && conflictingFields?.length > 0; - const showContactHistory = (currentRouteName === 'live' || currentRouteName === 'omnichannel-directory') && lastChat; - const customFieldEntries = useValidCustomFields(userCustomFields); return ( @@ -90,11 +85,9 @@ const ContactInfo = ({ contact, onClose }: ContactInfoProps) => { handleNavigate({ context: 'channels' })} selected={context === 'channels'}> {t('Channels')} - {showContactHistory && ( - handleNavigate({ context: 'history' })} selected={context === 'history'}> - {t('History')} - - )} + handleNavigate({ context: 'history' })} selected={context === 'history'}> + {t('History')} + {context === 'details' && ( { /> )} {context === 'channels' && } - {context === 'history' && showContactHistory && } + {context === 'history' && } ); };