diff --git a/webapp/channels/src/components/admin_console/admin_settings/__snapshots__/admin_setting.test.tsx.snap b/webapp/channels/src/components/admin_console/admin_settings/__snapshots__/admin_setting.test.tsx.snap
new file mode 100644
index 00000000000..26013068c1d
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/__snapshots__/admin_setting.test.tsx.snap
@@ -0,0 +1,39 @@
+// Jest Snapshot v1, https://goo.gl/fbAQLP
+
+exports[`AdminSettings should match snapshot 1`] = `
+
+`;
diff --git a/webapp/channels/src/components/admin_console/admin_settings/admin_setting.test.tsx b/webapp/channels/src/components/admin_console/admin_settings/admin_setting.test.tsx
new file mode 100644
index 00000000000..9248402388f
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/admin_setting.test.tsx
@@ -0,0 +1,117 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import type {ComponentProps} from 'react';
+import React from 'react';
+
+import {renderWithContext, screen} from 'tests/react_testing_utils';
+
+import AdminSettings from './admin_settings';
+
+function getProps(): ComponentProps {
+ return {
+ doSubmit: jest.fn(),
+ renderSettings: () => <>{'Some settings'}>,
+ renderTitle: () => <>{'Some title'}>,
+ saveNeeded: false,
+ saving: false,
+ isDisabled: false,
+ serverError: undefined,
+ };
+}
+
+describe('AdminSettings', () => {
+ it('should match snapshot', () => {
+ const props = getProps();
+ const {container} = renderWithContext();
+ expect(container).toMatchSnapshot();
+ });
+
+ it('save button should be disabled if there is no save needed', () => {
+ const props = getProps();
+ props.saveNeeded = false;
+ const {rerender} = renderWithContext();
+
+ expect(screen.getByTestId('saveSetting')).toBeDisabled();
+
+ props.saveNeeded = true;
+
+ rerender();
+
+ expect(screen.getByTestId('saveSetting')).not.toBeDisabled();
+ });
+
+ it('save button should be disabled if the component is disabled', () => {
+ const props = getProps();
+ props.saveNeeded = true;
+ props.isDisabled = true;
+ const {rerender} = renderWithContext();
+
+ expect(screen.getByTestId('saveSetting')).toBeDisabled();
+
+ props.isDisabled = false;
+
+ rerender();
+
+ expect(screen.getByTestId('saveSetting')).not.toBeDisabled();
+ });
+
+ it('should call doSubmit when the save button is pressed', () => {
+ const props = getProps();
+ props.saveNeeded = true;
+
+ const {rerender} = renderWithContext();
+
+ expect(props.doSubmit).not.toHaveBeenCalled();
+ screen.getByTestId('saveSetting').click();
+ expect(props.doSubmit).toHaveBeenCalled();
+
+ props.doSubmit = jest.fn();
+ rerender();
+
+ expect(props.doSubmit).not.toHaveBeenCalled();
+ screen.getByTestId('saveSetting').click();
+ expect(props.doSubmit).toHaveBeenCalled();
+ });
+
+ it('show saving message while saving', () => {
+ const props = getProps();
+ props.saving = true;
+ const {rerender} = renderWithContext();
+
+ expect(screen.getByText('Saving Config...')).toBeInTheDocument();
+
+ props.saving = false;
+
+ rerender();
+
+ expect(screen.queryByText('Saving Config...')).not.toBeInTheDocument();
+ });
+
+ it('should render the specified title and settings', () => {
+ const titleTestId = 'some test id for the title';
+ const settingsTestId = 'some test id for the settings';
+ const props = getProps();
+ props.renderTitle = () => {'hello world'};
+ props.renderSettings = () => {'hello world'};
+
+ renderWithContext();
+
+ expect(screen.getByTestId(titleTestId)).toBeInTheDocument();
+ expect(screen.getByTestId(settingsTestId)).toBeInTheDocument();
+ });
+
+ it('should show the error message', () => {
+ const serverError = 'some error';
+ const props = getProps();
+ props.serverError = serverError;
+ const {rerender} = renderWithContext();
+
+ expect(screen.getByText(serverError)).toBeInTheDocument();
+
+ props.serverError = undefined;
+ rerender();
+
+ expect(screen.queryByText(serverError)).not.toBeInTheDocument();
+ });
+});
diff --git a/webapp/channels/src/components/admin_console/admin_settings/admin_settings.tsx b/webapp/channels/src/components/admin_console/admin_settings/admin_settings.tsx
new file mode 100644
index 00000000000..b362b103e66
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/admin_settings.tsx
@@ -0,0 +1,79 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+/* eslint-disable react/require-optimization */
+
+import React, {useCallback} from 'react';
+import {FormattedMessage} from 'react-intl';
+
+import FormError from 'components/form_error';
+import SaveButton from 'components/save_button';
+import AdminHeader from 'components/widgets/admin_console/admin_header';
+import WithTooltip from 'components/with_tooltip';
+
+type Props = {
+ isDisabled?: boolean;
+ renderTitle: () => JSX.Element;
+ renderSettings: () => React.ReactNode;
+ doSubmit: () => void;
+ saving: boolean;
+ saveNeeded: boolean;
+ serverError?: React.ReactNode;
+}
+
+const AdminSettings = ({
+ doSubmit,
+ renderSettings,
+ renderTitle,
+ isDisabled,
+ saving,
+ saveNeeded,
+ serverError,
+}: Props) => {
+ const handleSubmit = useCallback((e: React.FormEvent | React.MouseEvent) => {
+ e.preventDefault();
+
+ doSubmit();
+ }, [doSubmit]);
+
+ return (
+
+ );
+};
+
+export default AdminSettings;
diff --git a/webapp/channels/src/components/admin_console/admin_settings/hooks.tsx b/webapp/channels/src/components/admin_console/admin_settings/hooks.tsx
new file mode 100644
index 00000000000..e517a9a48c8
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/hooks.tsx
@@ -0,0 +1,82 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {useCallback, useState} from 'react';
+import {useDispatch, useSelector} from 'react-redux';
+
+import type {AdminConfig} from '@mattermost/types/config';
+
+import {patchConfig} from 'mattermost-redux/actions/admin';
+import {getConfig, getEnvironmentConfig} from 'mattermost-redux/selectors/entities/admin';
+import {getLicense} from 'mattermost-redux/selectors/entities/general';
+
+import {setNavigationBlocked} from 'actions/admin_actions';
+
+import type {GlobalState} from 'types/store';
+
+import type {GetConfigFromStateFunction, GetStateFromConfigFunction, HandleSaveFunction} from './types';
+import {isSetByEnv} from './utils';
+
+export function useIsSetByEnv(path: string) {
+ return useSelector((state: GlobalState) => isSetByEnv(getEnvironmentConfig(state), path));
+}
+
+export const useAdminSettingState = >(
+ getConfigFromState: GetConfigFromStateFunction,
+ getStateFromConfig: GetStateFromConfigFunction,
+ preSave?: (values: T) => Promise,
+ handleSaved?: HandleSaveFunction,
+) => {
+ const dispatch = useDispatch();
+
+ const license = useSelector(getLicense);
+ const config = useSelector(getConfig) as AdminConfig;
+
+ const [saveNeeded, setSaveNeeded] = useState(false);
+ const [saving, setSaving] = useState(false);
+ const [serverError, setServerError] = useState(undefined);
+ const [settingValues, setSettingValues] = useState(() => getStateFromConfig(config, license));
+
+ const handleChange = useCallback((id: string, value: unknown) => {
+ setSaveNeeded(true);
+ setSettingValues((prev) => ({
+ ...prev,
+ [id]: value,
+ }));
+ dispatch(setNavigationBlocked(true));
+ }, [dispatch]);
+
+ const doSubmit = useCallback(async () => {
+ setSaving(true);
+ setServerError(undefined);
+
+ const configToPatch = getConfigFromState(settingValues);
+
+ if (preSave) {
+ const preSaveError = await preSave(settingValues);
+ if (preSaveError) {
+ setSaving(false);
+ setServerError(preSaveError);
+ handleSaved?.(configToPatch, handleChange);
+ }
+ }
+
+ const {data, error} = await dispatch(patchConfig(configToPatch));
+
+ if (data) {
+ setSettingValues(getStateFromConfig(data, license));
+ setSaveNeeded(false);
+ setSaving(false);
+
+ dispatch(setNavigationBlocked(false));
+ handleSaved?.(configToPatch, handleChange);
+ } else if (error) {
+ setSaving(false);
+ setServerError(error.message);
+
+ handleSaved?.(configToPatch, handleChange);
+ }
+ }, [dispatch, getConfigFromState, getStateFromConfig, handleChange, handleSaved, license, preSave, settingValues]);
+
+ return {handleChange, doSubmit, saveNeeded, saving, serverError, settingValues};
+};
diff --git a/webapp/channels/src/components/admin_console/admin_settings/types.ts b/webapp/channels/src/components/admin_console/admin_settings/types.ts
new file mode 100644
index 00000000000..c0adb8ca4a7
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/types.ts
@@ -0,0 +1,9 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import type {AdminConfig, ClientLicense} from '@mattermost/types/config';
+import type {DeepPartial} from '@mattermost/types/utilities';
+
+export type HandleSaveFunction = (config: DeepPartial, updateSettings: (id: string, value: unknown) => void) => void;
+export type GetStateFromConfigFunction> = (config: AdminConfig, license: ClientLicense) => State;
+export type GetConfigFromStateFunction> = (state: State) => DeepPartial;
diff --git a/webapp/channels/src/components/admin_console/admin_settings/utils.test.ts b/webapp/channels/src/components/admin_console/admin_settings/utils.test.ts
new file mode 100644
index 00000000000..9e45bbb068c
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/utils.test.ts
@@ -0,0 +1,226 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import {isSetByEnv, parseIntNonNegative, parseIntNonZero, parseIntZeroOrMin} from './utils';
+
+describe('isSetByEnv', () => {
+ it('properly returns true when the environment set the path', () => {
+ const result = isSetByEnv({
+ AnalyticsSettings: {
+ MaxUsersForStatistics: true,
+ },
+ }, 'AnalyticsSettings.MaxUsersForStatistics');
+ expect(result).toBe(true);
+ });
+
+ it('properly returns false when the environment does not set the path', () => {
+ const result = isSetByEnv({
+ AnalyticsSettings: {
+ MaxUsersForStatistics: false,
+ },
+ }, 'AnalyticsSettings.MaxUsersForStatistics');
+ expect(result).toBe(false);
+ });
+
+ it('properly returns false when the path does not exist', () => {
+ let result = isSetByEnv({}, 'AnalyticsSettings.MaxUsersForStatistics');
+ expect(result).toBe(false);
+
+ result = isSetByEnv({
+ AnalyticsSettings: {
+ MaxUsersForStatistics: true,
+ },
+ }, 'not.available.path');
+ expect(result).toBe(false);
+ });
+});
+
+describe('parseIntNonNegative', () => {
+ it('properly parse a positive number', () => {
+ let result = parseIntNonNegative('123');
+ expect(result).toBe(123);
+
+ result = parseIntNonNegative(123);
+ expect(result).toBe(123);
+ });
+
+ it('properly defaults to default value if negative', () => {
+ let result = parseIntNonNegative('-1', 100);
+ expect(result).toBe(100);
+
+ result = parseIntNonNegative(-1, 100);
+ expect(result).toBe(100);
+ });
+
+ it('properly defaults to default value if not a number', () => {
+ const result = parseIntNonNegative('hello world', 100);
+ expect(result).toBe(100);
+ });
+
+ it('string float values are rounded down', () => {
+ let result = parseIntNonNegative('1.99999', 100);
+ expect(result).toBe(1);
+
+ result = parseIntNonNegative(199.99999, 100);
+ expect(result).toBe(199.99999);
+ });
+
+ it('properly defaults to 0 if no default is given', () => {
+ let result = parseIntNonNegative('-123');
+ expect(result).toBe(0);
+
+ result = parseIntNonNegative(-123);
+ expect(result).toBe(0);
+
+ result = parseIntNonNegative('hello world');
+ expect(result).toBe(0);
+
+ result = parseIntNonNegative('-1.9999999');
+ expect(result).toBe(0);
+ });
+});
+
+describe('parseIntZeroOrMin', () => {
+ it('parse a positive number', () => {
+ let result = parseIntZeroOrMin('123', 100);
+ expect(result).toBe(123);
+
+ result = parseIntZeroOrMin(123, 100);
+ expect(result).toBe(123);
+ });
+
+ it('defaults to 0 if the value is negative or 0', () => {
+ let result = parseIntZeroOrMin('-1', 100);
+ expect(result).toBe(0);
+
+ result = parseIntZeroOrMin(-1, 100);
+ expect(result).toBe(0);
+
+ result = parseIntZeroOrMin('0', 100);
+ expect(result).toBe(0);
+
+ result = parseIntZeroOrMin(0, 100);
+ expect(result).toBe(0);
+ });
+
+ it('defaults to minimum value if lower', () => {
+ let result = parseIntZeroOrMin('99', 100);
+ expect(result).toBe(100);
+
+ result = parseIntZeroOrMin(99, 100);
+ expect(result).toBe(100);
+ });
+
+ it('defaults to 0 value if not a number', () => {
+ const result = parseIntZeroOrMin('hello world', 100);
+ expect(result).toBe(0);
+ });
+
+ it('string float values are rounded down', () => {
+ let result = parseIntZeroOrMin('199.99999', 100);
+ expect(result).toBe(199);
+
+ result = parseIntZeroOrMin(199.99999, 100);
+ expect(result).toBe(199.99999);
+ });
+
+ it('defaults to 1 if no min is given', () => {
+ let result = parseIntZeroOrMin('2');
+ expect(result).toBe(2);
+
+ result = parseIntZeroOrMin(0.00000000001);
+ expect(result).toBe(1);
+
+ result = parseIntZeroOrMin(1.00000000001);
+ expect(result).toBe(1.00000000001);
+ });
+
+ it('minimum allow float numbers', () => {
+ let result = parseIntZeroOrMin('1', 1.9999);
+ expect(result).toBe(1.9999);
+
+ result = parseIntZeroOrMin(1.9998, 1.9999);
+ expect(result).toBe(1.9999);
+ });
+});
+
+describe('parseIntNonZero', () => {
+ it('parse a positive number', () => {
+ let result = parseIntNonZero('123', 100, 50);
+ expect(result).toBe(123);
+
+ result = parseIntNonZero(123, 100, 50);
+ expect(result).toBe(123);
+ });
+
+ it('defaults to default value if the value lower than mininum value', () => {
+ let result = parseIntNonZero('-1', 100, 0);
+ expect(result).toBe(100);
+
+ result = parseIntNonZero(-1, 100, 0);
+ expect(result).toBe(100);
+
+ result = parseIntNonZero('150', 100, 200);
+ expect(result).toBe(100);
+
+ result = parseIntNonZero(150, 100, 200);
+ expect(result).toBe(100);
+ });
+
+ it('minimum value defaults to 1', () => {
+ let result = parseIntNonZero('1', 100);
+ expect(result).toBe(1);
+
+ result = parseIntNonZero('0', 100);
+ expect(result).toBe(100);
+
+ result = parseIntNonZero(1, 100);
+ expect(result).toBe(1);
+
+ result = parseIntNonZero(0.999999999, 100);
+ expect(result).toBe(100);
+ });
+
+ it('default value defaults to 1', () => {
+ let result = parseIntNonZero('99', undefined, 100);
+ expect(result).toBe(1);
+
+ result = parseIntNonZero('hello world');
+ expect(result).toBe(1);
+
+ result = parseIntNonZero(99.9999, undefined, 100);
+ expect(result).toBe(1);
+ });
+
+ it('defaults to default value if not a number', () => {
+ const result = parseIntNonZero('hello world', 100);
+ expect(result).toBe(100);
+ });
+
+ it('string float values are rounded down', () => {
+ let result = parseIntNonZero('199.99999', 100);
+ expect(result).toBe(199);
+
+ result = parseIntNonZero(199.99999, 100);
+ expect(result).toBe(199.99999);
+ });
+
+ it('default and minimum allow float numbers', () => {
+ let result = parseIntNonZero('1', 1.9999, 2);
+ expect(result).toBe(1.9999);
+
+ result = parseIntNonZero(1.9998, 10, 1.9999);
+ expect(result).toBe(10);
+ });
+
+ it('minimum and default allow negative numbers', () => {
+ let result = parseIntNonZero('1', -10, 2);
+ expect(result).toBe(-10);
+
+ result = parseIntNonZero('-5', 10, -2);
+ expect(result).toBe(10);
+
+ result = parseIntNonZero('-5', 10, -6);
+ expect(result).toBe(-5);
+ });
+});
diff --git a/webapp/channels/src/components/admin_console/admin_settings/utils.ts b/webapp/channels/src/components/admin_console/admin_settings/utils.ts
new file mode 100644
index 00000000000..a08c36fa282
--- /dev/null
+++ b/webapp/channels/src/components/admin_console/admin_settings/utils.ts
@@ -0,0 +1,58 @@
+// Copyright (c) 2015-present Mattermost, Inc. All Rights Reserved.
+// See LICENSE.txt for license information.
+
+import type {AdminConfig, EnvironmentConfig} from '@mattermost/types/config';
+
+function getConfigValue(config: AdminConfig | Partial, path: string) {
+ const pathParts = path.split('.');
+
+ return pathParts.reduce((obj: object | null, pathPart) => {
+ if (!obj) {
+ return null;
+ }
+ return obj[(pathPart as keyof object)];
+ }, config);
+}
+
+export function isSetByEnv(environmentConfig: Partial, path: string) {
+ return Boolean(getConfigValue(environmentConfig, path));
+}
+
+export const parseIntNonNegative = (str: string | number, defaultValue?: number) => {
+ const n = typeof str === 'string' ? parseInt(str, 10) : str;
+
+ if (isNaN(n) || n < 0) {
+ if (defaultValue) {
+ return defaultValue;
+ }
+ return 0;
+ }
+
+ return n;
+};
+
+export const parseIntZeroOrMin = (str: string | number, minimumValue = 1) => {
+ const n = typeof str === 'string' ? parseInt(str, 10) : str;
+
+ if (isNaN(n) || n < 0) {
+ return 0;
+ }
+ if (n > 0 && n < minimumValue) {
+ return minimumValue;
+ }
+
+ return n;
+};
+
+export const parseIntNonZero = (str: string | number, defaultValue?: number, minimumValue = 1) => {
+ const n = typeof str === 'string' ? parseInt(str, 10) : str;
+
+ if (isNaN(n) || n < minimumValue) {
+ if (defaultValue) {
+ return defaultValue;
+ }
+ return 1;
+ }
+
+ return n;
+};
diff --git a/webapp/channels/src/components/admin_console/bleve_settings.tsx b/webapp/channels/src/components/admin_console/bleve_settings.tsx
index a3644779c3e..6464a7562a3 100644
--- a/webapp/channels/src/components/admin_console/bleve_settings.tsx
+++ b/webapp/channels/src/components/admin_console/bleve_settings.tsx
@@ -13,10 +13,10 @@ import ExternalLink from 'components/external_link';
import {JobStatuses, JobTypes} from 'utils/constants';
-import AdminSettings from './admin_settings';
-import type {BaseProps, BaseState} from './admin_settings';
import BooleanSetting from './boolean_setting';
import JobsTable from './jobs';
+import OLDAdminSettings from './old_admin_settings';
+import type {BaseProps, BaseState} from './old_admin_settings';
import RequestButton from './request_button/request_button';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -60,7 +60,7 @@ export const searchableStrings = [
messages.enableSearchingDescription,
];
-export default class BleveSettings extends AdminSettings {
+export default class BleveSettings extends OLDAdminSettings {
getConfigFromState = (config: Props['config']) => {
if (config && config.BleveSettings) {
config.BleveSettings.IndexDir = this.state.indexDir;
diff --git a/webapp/channels/src/components/admin_console/cluster_settings.tsx b/webapp/channels/src/components/admin_console/cluster_settings.tsx
index a0e49d2e15b..6c84ddac5d0 100644
--- a/webapp/channels/src/components/admin_console/cluster_settings.tsx
+++ b/webapp/channels/src/components/admin_console/cluster_settings.tsx
@@ -13,10 +13,10 @@ import WarningIcon from 'components/widgets/icons/fa_warning_icon';
import {DocLinks} from 'utils/constants';
-import type {BaseProps, BaseState} from './admin_settings';
-import AdminSettings from './admin_settings';
import BooleanSetting from './boolean_setting';
import ClusterTableContainer from './cluster_table_container';
+import type {BaseProps, BaseState} from './old_admin_settings';
+import OLDAdminSettings from './old_admin_settings';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -73,7 +73,7 @@ export const searchableStrings = [
messages.gossipPortDesc,
];
-export default class ClusterSettings extends AdminSettings {
+export default class ClusterSettings extends OLDAdminSettings {
getConfigFromState = (config: AdminConfig) => {
config.ClusterSettings.Enable = this.state.Enable;
config.ClusterSettings.ClusterName = this.state.ClusterName;
diff --git a/webapp/channels/src/components/admin_console/custom_terms_of_service_settings/custom_terms_of_service_settings.tsx b/webapp/channels/src/components/admin_console/custom_terms_of_service_settings/custom_terms_of_service_settings.tsx
index 74394dbba55..35646708512 100644
--- a/webapp/channels/src/components/admin_console/custom_terms_of_service_settings/custom_terms_of_service_settings.tsx
+++ b/webapp/channels/src/components/admin_console/custom_terms_of_service_settings/custom_terms_of_service_settings.tsx
@@ -10,9 +10,9 @@ import type {TermsOfService} from '@mattermost/types/terms_of_service';
import type {ActionResult} from 'mattermost-redux/types/actions';
-import AdminSettings from 'components/admin_console/admin_settings';
-import type {BaseProps, BaseState} from 'components/admin_console/admin_settings';
import BooleanSetting from 'components/admin_console/boolean_setting';
+import OLDAdminSettings from 'components/admin_console/old_admin_settings';
+import type {BaseProps, BaseState} from 'components/admin_console/old_admin_settings';
import SettingsGroup from 'components/admin_console/settings_group';
import TextSetting from 'components/admin_console/text_setting';
import LoadingScreen from 'components/loading_screen';
@@ -66,7 +66,7 @@ export const searchableStrings = [
messages.termsOfServiceReAcceptanceHelp,
];
-export default class CustomTermsOfServiceSettings extends AdminSettings {
+export default class CustomTermsOfServiceSettings extends OLDAdminSettings {
constructor(props: Props) {
super(props);
diff --git a/webapp/channels/src/components/admin_console/database_settings.tsx b/webapp/channels/src/components/admin_console/database_settings.tsx
index 2404becd5eb..4b6c4c5b84c 100644
--- a/webapp/channels/src/components/admin_console/database_settings.tsx
+++ b/webapp/channels/src/components/admin_console/database_settings.tsx
@@ -13,10 +13,10 @@ import ExternalLink from 'components/external_link';
import {DocLinks} from 'utils/constants';
-import type {BaseState} from './admin_settings';
-import AdminSettings from './admin_settings';
import BooleanSetting from './boolean_setting';
import MigrationsTable from './database';
+import type {BaseState} from './old_admin_settings';
+import OLDAdminSettings from './old_admin_settings';
import RequestButton from './request_button/request_button';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -98,7 +98,7 @@ export const searchableStrings: Array {
+export default class DatabaseSettings extends OLDAdminSettings {
constructor(props: Props) {
super(props);
diff --git a/webapp/channels/src/components/admin_console/elasticsearch_settings.tsx b/webapp/channels/src/components/admin_console/elasticsearch_settings.tsx
index 18a39956d91..7cb9d21bd6a 100644
--- a/webapp/channels/src/components/admin_console/elasticsearch_settings.tsx
+++ b/webapp/channels/src/components/admin_console/elasticsearch_settings.tsx
@@ -14,10 +14,10 @@ import ExternalLink from 'components/external_link';
import {DocLinks, JobStatuses, JobTypes} from 'utils/constants';
-import AdminSettings from './admin_settings';
-import type {BaseProps, BaseState} from './admin_settings';
import BooleanSetting from './boolean_setting';
import JobsTable from './jobs';
+import OLDAdminSettings from './old_admin_settings';
+import type {BaseProps, BaseState} from './old_admin_settings';
import RequestButton from './request_button/request_button';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -98,7 +98,7 @@ export const searchableStrings: Array {
+export default class ElasticsearchSettings extends OLDAdminSettings {
getConfigFromState = (config: AdminConfig) => {
config.ElasticsearchSettings.ConnectionURL = this.state.connectionUrl;
config.ElasticsearchSettings.Backend = this.state.backend;
diff --git a/webapp/channels/src/components/admin_console/message_export_settings.test.tsx b/webapp/channels/src/components/admin_console/message_export_settings.test.tsx
index c5529cb6937..58b4eb1a091 100644
--- a/webapp/channels/src/components/admin_console/message_export_settings.test.tsx
+++ b/webapp/channels/src/components/admin_console/message_export_settings.test.tsx
@@ -10,7 +10,7 @@ import type {MessageExportSettings as MessageExportSettingsClass} from 'componen
import {shallowWithIntl} from 'tests/helpers/intl-test-helper';
-import type {BaseProps} from './admin_settings';
+import type {BaseProps} from './old_admin_settings';
describe('components/MessageExportSettings', () => {
test('should match snapshot, disabled, actiance', () => {
diff --git a/webapp/channels/src/components/admin_console/message_export_settings.tsx b/webapp/channels/src/components/admin_console/message_export_settings.tsx
index c7c81aa6292..704f1dbf984 100644
--- a/webapp/channels/src/components/admin_console/message_export_settings.tsx
+++ b/webapp/channels/src/components/admin_console/message_export_settings.tsx
@@ -14,11 +14,11 @@ import ExternalLink from 'components/external_link';
import {DocLinks, JobTypes, exportFormats} from 'utils/constants';
-import type {BaseProps, BaseState} from './admin_settings';
-import AdminSettings from './admin_settings';
import BooleanSetting from './boolean_setting';
import DropdownSetting from './dropdown_setting';
import JobsTable from './jobs';
+import OLDAdminSettings from './old_admin_settings';
+import type {BaseProps, BaseState} from './old_admin_settings';
import RadioSetting from './radio_setting';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -82,7 +82,7 @@ string | MessageDescriptor | [MessageDescriptor, { [key: string]: any }]
messages.globalRelayEmailAddress_description,
];
-export class MessageExportSettings extends AdminSettings {
+export class MessageExportSettings extends OLDAdminSettings {
getConfigFromState = (config: AdminConfig) => {
config.MessageExportSettings.EnableExport = this.state.enableComplianceExport;
config.MessageExportSettings.ExportFormat = this.state.exportFormat;
diff --git a/webapp/channels/src/components/admin_console/admin_settings.tsx b/webapp/channels/src/components/admin_console/old_admin_settings.tsx
similarity index 98%
rename from webapp/channels/src/components/admin_console/admin_settings.tsx
rename to webapp/channels/src/components/admin_console/old_admin_settings.tsx
index 8fe14e7d0ee..df2278e24f3 100644
--- a/webapp/channels/src/components/admin_console/admin_settings.tsx
+++ b/webapp/channels/src/components/admin_console/old_admin_settings.tsx
@@ -34,7 +34,7 @@ type ClientErrorPlaceholder = {
server_error_id: string;
}
-export default abstract class AdminSettings extends React.Component {
+export default abstract class OLDAdminSettings extends React.Component {
public constructor(props: Props) {
super(props);
const stateInit = {
diff --git a/webapp/channels/src/components/admin_console/openid_convert/openid_convert.tsx b/webapp/channels/src/components/admin_console/openid_convert/openid_convert.tsx
index 7f461e85a52..ac7996f00ad 100644
--- a/webapp/channels/src/components/admin_console/openid_convert/openid_convert.tsx
+++ b/webapp/channels/src/components/admin_console/openid_convert/openid_convert.tsx
@@ -9,7 +9,7 @@ import type {DeepPartial} from '@mattermost/types/utilities';
import type {ActionResult} from 'mattermost-redux/types/actions';
-import type {BaseProps} from 'components/admin_console/admin_settings';
+import type {BaseProps} from 'components/admin_console/old_admin_settings';
import ExternalLink from 'components/external_link';
import FormError from 'components/form_error';
diff --git a/webapp/channels/src/components/admin_console/password_settings.tsx b/webapp/channels/src/components/admin_console/password_settings.tsx
index b2ceecd1fda..9be78a31cc3 100644
--- a/webapp/channels/src/components/admin_console/password_settings.tsx
+++ b/webapp/channels/src/components/admin_console/password_settings.tsx
@@ -11,11 +11,11 @@ import type {DeepPartial} from '@mattermost/types/utilities';
import Constants from 'utils/constants';
import {passwordErrors} from 'utils/password';
-import AdminSettings from './admin_settings';
-import type {BaseProps, BaseState} from './admin_settings';
import BlockableLink from './blockable_link';
import BooleanSetting from './boolean_setting';
import CheckboxSetting from './checkbox_setting';
+import type {BaseProps, BaseState} from './old_admin_settings';
+import OLDAdminSettings from './old_admin_settings';
import Setting from './setting';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -84,7 +84,7 @@ function getPasswordErrorsMessage(lowercase?: boolean, uppercase?: boolean, numb
return passwordErrors[key as KeyType];
}
-export default class PasswordSettings extends AdminSettings {
+export default class PasswordSettings extends OLDAdminSettings {
sampleErrorMsg: React.ReactNode;
constructor(props: Props) {
diff --git a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx
index fe9accd96b5..362ffa64aac 100644
--- a/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx
+++ b/webapp/channels/src/components/admin_console/plugin_management/plugin_management.tsx
@@ -21,9 +21,9 @@ import {appsPluginID} from 'utils/apps';
import {DeveloperLinks} from 'utils/constants';
import * as Utils from 'utils/utils';
-import AdminSettings from '../admin_settings';
-import type {BaseProps, BaseState} from '../admin_settings';
import BooleanSetting from '../boolean_setting';
+import OLDAdminSettings from '../old_admin_settings';
+import type {BaseProps, BaseState} from '../old_admin_settings';
import SettingsGroup from '../settings_group';
import TextSetting from '../text_setting';
@@ -495,7 +495,7 @@ type State = BaseState & {
requirePluginSignature: boolean;
removing: string | null;
}
-class PluginManagement extends AdminSettings {
+class PluginManagement extends OLDAdminSettings {
private fileInput: React.RefObject;
constructor(props: Props) {
super(props);
diff --git a/webapp/channels/src/components/admin_console/push_settings.tsx b/webapp/channels/src/components/admin_console/push_settings.tsx
index 71db7fe0c0f..12f6099afa4 100644
--- a/webapp/channels/src/components/admin_console/push_settings.tsx
+++ b/webapp/channels/src/components/admin_console/push_settings.tsx
@@ -11,9 +11,9 @@ import ExternalLink from 'components/external_link';
import {Constants, DocLinks} from 'utils/constants';
-import AdminSettings from './admin_settings';
-import type {BaseProps, BaseState} from './admin_settings';
import DropdownSetting from './dropdown_setting';
+import OLDAdminSettings from './old_admin_settings';
+import type {BaseProps, BaseState} from './old_admin_settings';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -57,7 +57,7 @@ export const searchableStrings = [
messages.pushServerTitle,
];
-class PushSettings extends AdminSettings {
+class PushSettings extends OLDAdminSettings {
canSave = () => {
return this.state.pushNotificationServerType !== PUSH_NOTIFICATIONS_MHPNS || this.state.agree;
};
diff --git a/webapp/channels/src/components/admin_console/session_length_settings.tsx b/webapp/channels/src/components/admin_console/session_length_settings.tsx
index ef104dee0f3..dd27c2c509b 100644
--- a/webapp/channels/src/components/admin_console/session_length_settings.tsx
+++ b/webapp/channels/src/components/admin_console/session_length_settings.tsx
@@ -6,9 +6,9 @@ import {FormattedMessage, defineMessage, defineMessages} from 'react-intl';
import type {AdminConfig, ClientLicense, ServiceSettings} from '@mattermost/types/config';
-import AdminSettings from './admin_settings';
-import type {BaseState, BaseProps} from './admin_settings';
import BooleanSetting from './boolean_setting';
+import OLDAdminSettings from './old_admin_settings';
+import type {BaseState, BaseProps} from './old_admin_settings';
import SettingsGroup from './settings_group';
import TextSetting from './text_setting';
@@ -69,7 +69,7 @@ export const searchableStrings = [
messages.sessionIdleTimeoutDesc,
];
-export default class SessionLengthSettings extends AdminSettings {
+export default class SessionLengthSettings extends OLDAdminSettings {
getConfigFromState = (config: AdminConfig) => {
const MINIMUM_IDLE_TIMEOUT = 5;