diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js
index 70ee4f738a..21ddb5c649 100644
--- a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js
+++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/index.js
@@ -4,7 +4,7 @@ import { withTranslation } from "react-i18next";
import Text from "@appserver/components/text";
import { setDocumentTitle } from "../../../../../../helpers/utils";
import { MainContainer } from "../StyledSecurity";
-import TfaSection from "../sub-components/tfa";
+import TfaSection from "./tfa";
import MobileView from "./mobileView";
import { isMobile } from "react-device-detect";
import CategoryWrapper from "../sub-components/category-wrapper";
diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/tfa.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/tfa.js
new file mode 100644
index 0000000000..44ed7e9266
--- /dev/null
+++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/access-portal/tfa.js
@@ -0,0 +1,176 @@
+import React, { useState, useEffect } from "react";
+import styled from "styled-components";
+import { withRouter } from "react-router";
+import { withTranslation } from "react-i18next";
+import { inject, observer } from "mobx-react";
+import RadioButtonGroup from "@appserver/components/radio-button-group";
+import Button from "@appserver/components/button";
+import Text from "@appserver/components/text";
+import toastr from "@appserver/components/toast/toastr";
+import SectionLoader from "../sub-components/section-loader";
+
+const MainContainer = styled.div`
+ width: 100%;
+
+ .box {
+ margin-bottom: 24px;
+ }
+`;
+
+const ButtonsWrapper = styled.div`
+ display: flex;
+ flex-direction: row;
+ gap: 8px;
+ align-items: center;
+
+ @media (max-width: 375px) {
+ position: absolute;
+ bottom: 16px;
+ width: calc(100vw - 32px);
+
+ .button {
+ height: 40px;
+ width: 100%;
+ }
+
+ .reminder {
+ position: absolute;
+ bottom: 48px;
+ }
+ }
+`;
+
+const TwoFactorAuth = (props) => {
+ const { t } = props;
+ const [type, setType] = useState("none");
+ const [currentState, setCurrentState] = useState("");
+ const [smsDisabled, setSmsDisabled] = useState(false);
+ const [appDisabled, setAppDisabled] = useState(false);
+ const [showReminder, setShowReminder] = useState(false);
+ const [isLoading, setIsLoading] = useState(false);
+
+ const getSettings = async () => {
+ const { getTfaType, getTfaSettings } = props;
+ const type = await getTfaType();
+ setType(type);
+
+ const settings = await getTfaSettings();
+ setSmsDisabled(settings[0].avaliable);
+ setAppDisabled(settings[1].avaliable);
+ };
+
+ useEffect(() => {
+ getSettings();
+ setCurrentState(type);
+ setIsLoading(true);
+ }, []);
+
+ const onSelectTfaType = (e) => {
+ if (type !== e.target.value) {
+ setType(e.target.value);
+ setShowReminder(true);
+ }
+ if (e.target.value === currentState) {
+ setShowReminder(false);
+ }
+ };
+
+ const onSaveClick = () => {
+ const { t, setTfaSettings, getTfaConfirmLink, history } = props;
+
+ setTfaSettings(type).then((res) => {
+ toastr.success(t("SuccessfullySaveSettingsMessage"));
+ if (type !== "none") {
+ getTfaConfirmLink(res).then((link) =>
+ history.push(link.replace(window.location.origin, ""))
+ );
+ }
+ setType(type);
+ setShowReminder(false);
+ });
+ };
+
+ const onCancelClick = () => {
+ setShowReminder(false);
+ setType(currentState);
+ };
+
+ if (!isLoading) return ;
+ return (
+
+
+
+
+
+
+ {showReminder && (
+
+ {t("YouHaveUnsavedChanges")}
+
+ )}
+
+
+ );
+};
+
+export default inject(({ auth }) => {
+ const { organizationName } = auth.settingsStore;
+ const {
+ getTfaType,
+ getTfaSettings,
+ setTfaSettings,
+ getTfaConfirmLink,
+ } = auth.tfaStore;
+
+ return {
+ organizationName,
+ getTfaType,
+ getTfaSettings,
+ setTfaSettings,
+ getTfaConfirmLink,
+ };
+})(
+ withTranslation(["Settings", "Common"])(withRouter(observer(TwoFactorAuth)))
+);
diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/section-loader.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/section-loader.js
new file mode 100644
index 0000000000..223763d718
--- /dev/null
+++ b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/section-loader.js
@@ -0,0 +1,26 @@
+import React from "react";
+import styled from "styled-components";
+import Loaders from "@appserver/common/components/Loaders";
+
+const StyledLoader = styled.div`
+ display: flex;
+ flex-direction: column;
+ gap: 8px;
+ width: 300px;
+
+ @media (max-width: 375px) {
+ width: 100%;
+ }
+`;
+
+const SectionLoader = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default SectionLoader;
diff --git a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/tfa.js b/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/tfa.js
deleted file mode 100644
index a3134be7aa..0000000000
--- a/web/ASC.Web.Client/src/components/pages/Settings/categories/security/sub-components/tfa.js
+++ /dev/null
@@ -1,138 +0,0 @@
-import React, { Component } from "react";
-import { withRouter } from "react-router";
-import { withTranslation } from "react-i18next";
-import styled from "styled-components";
-import Text from "@appserver/components/text";
-import RadioButtonGroup from "@appserver/components/radio-button-group";
-import Button from "@appserver/components/button";
-import toastr from "@appserver/components/toast/toastr";
-import Loader from "@appserver/components/loader";
-import { showLoader, hideLoader } from "@appserver/common/utils";
-
-import { setDocumentTitle } from "../../../../../../helpers/utils";
-import { inject } from "mobx-react";
-
-const MainContainer = styled.div`
- width: 100%;
-
- .save-button {
- margin-top: 32px;
- }
-
- .page_loader {
- position: fixed;
- left: 50%;
- }
-`;
-
-const HeaderContainer = styled.div`
- margin: 0 0 16px 0;
-`;
-
-class TfaPage extends Component {
- constructor(props) {
- super(props);
-
- const { t } = props;
- this.state = {
- isLoaded: false,
- type: "none",
- showButton: false,
- smsDisabled: false,
- appDisabled: false,
- };
- }
-
- async componentDidMount() {
- const { getTfaType, getTfaSettings } = this.props;
- showLoader();
-
- const type = await getTfaType();
- this.setState({ type: type, isLoaded: true });
-
- const r = await getTfaSettings();
- this.setState({ smsDisabled: r[0].avaliable, appDisabled: r[1].avaliable });
-
- hideLoader();
- }
-
- onSelectTfaType = async (e) => {
- const { getTfaType } = this.props;
-
- const type = await getTfaType();
-
- if (type !== e.target.value) {
- this.setState({ type: e.target.value, showButton: true });
- } else {
- this.setState({ type: e.target.value, showButton: false });
- }
- };
-
- saveSettings = () => {
- const { type } = this.state;
- const { t, setTfaSettings, getTfaConfirmLink, history } = this.props;
-
- setTfaSettings(type).then((res) => {
- toastr.success(t("SuccessfullySaveSettingsMessage"));
- if (type !== "none") {
- getTfaConfirmLink(res).then((link) =>
- history.push(link.replace(window.location.origin, ""))
- );
- }
- this.setState({ type: type, showButton: false });
- });
- };
-
- render() {
- const { isLoaded, type, showButton, smsDisabled, appDisabled } = this.state;
- const { t } = this.props;
-
- return !isLoaded ? (
-
- ) : (
-
-
- {showButton && (
-
- )}
-
- );
- }
-}
-
-export default inject(({ auth }) => ({
- organizationName: auth.settingsStore.organizationName,
- getTfaType: auth.tfaStore.getTfaType,
- getTfaSettings: auth.tfaStore.getTfaSettings,
- setTfaSettings: auth.tfaStore.setTfaSettings,
- getTfaConfirmLink: auth.tfaStore.getTfaConfirmLink,
-}))(withTranslation(["Settings", "Common"])(withRouter(TfaPage)));
diff --git a/web/ASC.Web.Client/src/components/pages/Settings/index.js b/web/ASC.Web.Client/src/components/pages/Settings/index.js
index 347ec75f80..6f9b43048b 100644
--- a/web/ASC.Web.Client/src/components/pages/Settings/index.js
+++ b/web/ASC.Web.Client/src/components/pages/Settings/index.js
@@ -4,12 +4,13 @@ import { withRouter } from "react-router";
import Layout from "./Layout";
import { combineUrl } from "@appserver/common/utils";
import AppServerConfig from "@appserver/common/constants/AppServerConfig";
+import { isMobile } from "react-device-detect";
const SecuritySettings = lazy(() => import("./categories/security/index.js"));
const Admins = lazy(() =>
import("./categories/security/sub-components/admins")
);
-const TfaPage = lazy(() => import("./categories/security/sub-components/tfa"));
+const TfaPage = lazy(() => import("./categories/security/access-portal/tfa"));
const CustomizationSettings = lazy(() =>
import("./categories/common/customization")
@@ -95,7 +96,11 @@ const Settings = () => {
/> */}
-
+