diff --git a/products/ASC.People/Client/src/App.js b/products/ASC.People/Client/src/App.js index 2cb35cc64b..732534454f 100644 --- a/products/ASC.People/Client/src/App.js +++ b/products/ASC.People/Client/src/App.js @@ -2,14 +2,13 @@ import React, { Suspense } from "react"; import { connect } from "react-redux"; import { Router, Switch, Redirect } from "react-router-dom"; import { Loader } from "asc-web-components"; -import PeopleLayout from "./components/Layout/index"; import Home from "./components/pages/Home"; import Profile from './components/pages/Profile'; import ProfileAction from './components/pages/ProfileAction'; import GroupAction from './components/pages/GroupAction'; import Reassign from './components/pages/Reassign'; import Import from './components/pages/Import'; -import { history, PrivateRoute, PublicRoute, Login, Error404 } from "asc-web-common"; +import { history, PrivateRoute, PublicRoute, Login, Error404, StudioLayout } from "asc-web-common"; /*const Profile = lazy(() => import("./components/pages/Profile")); const ProfileAction = lazy(() => import("./components/pages/ProfileAction")); @@ -19,7 +18,7 @@ const App = ({ settings }) => { const { homepage } = settings; return ( - + } > @@ -65,7 +64,7 @@ const App = ({ settings }) => { - + ); }; diff --git a/products/ASC.People/Client/src/components/Layout/i18n.js b/products/ASC.People/Client/src/components/Layout/i18n.js deleted file mode 100644 index abb38c631d..0000000000 --- a/products/ASC.People/Client/src/components/Layout/i18n.js +++ /dev/null @@ -1,61 +0,0 @@ -import i18n from "i18next"; -import Backend from "i18next-xhr-backend"; -import config from "../../../package.json"; - -const newInstance = i18n.createInstance(); - -if (process.env.NODE_ENV === "production") { - newInstance - .use(Backend) - .init({ - lng: 'en', - fallbackLng: "en", - debug: true, - - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - format: function (value, format) { - if (format === 'lowercase') return value.toLowerCase(); - return value; - } - }, - - react: { - useSuspense: false - }, - backend: { - loadPath: `${config.homepage}/locales/Layout/{{lng}}/{{ns}}.json` - } - }); -} else if (process.env.NODE_ENV === "development") { - - const resources = { - en: { - translation: require("./locales/en/translation.json") - }, - ru: { - translation: require("./locales/ru/translation.json") - } - }; - - newInstance.init({ - resources: resources, - lng: 'en', - fallbackLng: "en", - debug: true, - - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - format: function (value, format) { - if (format === 'lowercase') return value.toLowerCase(); - return value; - } - }, - - react: { - useSuspense: false - } - }); -} - -export default newInstance; diff --git a/products/ASC.People/Client/src/components/Layout/index.js b/products/ASC.People/Client/src/components/Layout/index.js deleted file mode 100644 index 780c29b824..0000000000 --- a/products/ASC.People/Client/src/components/Layout/index.js +++ /dev/null @@ -1,126 +0,0 @@ -import React from 'react'; -import { connect } from 'react-redux'; -import PropTypes from 'prop-types'; -import { withRouter } from "react-router"; -import { Layout, Toast } from 'asc-web-components'; -import { withTranslation, I18nextProvider } from 'react-i18next'; -import i18n from "./i18n"; -import { store } from 'asc-web-common'; -const { logout } = store.auth.actions; - -class PurePeopleLayout extends React.Component { - shouldComponentUpdate(nextProps) { - if (this.props.hasChanges !== nextProps.hasChanges) { - return true; - } - - return false; - } - - onProfileClick = () => { - console.log('ProfileBtn'); - const { history, settings } = this.props; - history.push(`${settings.homepage}/view/@self`); - } - - onAboutClick = () => { - window.location.href = "/about"; - } - - onLogoutClick = () => { - this.props.logout(); - } - - onLogoClick = () => window.open("/", '_self'); - - render() { - const { hasChanges, children, t } = this.props; - - const currentUserActions = [ - { - key: 'ProfileBtn', label: t('Profile'), onClick: this.onProfileClick - }, - { - key: 'AboutBtn', label: t('AboutCompanyTitle'), onClick: this.onAboutClick - }, - { - key: 'LogoutBtn', label: t('LogoutButton'), onClick: this.onLogoutClick - }, - ]; - - const newProps = hasChanges - ? { - currentUserActions: currentUserActions, - onLogoClick: this.onLogoClick, - ...this.props - } - : {}; - - console.log("PeopleLayout render", newProps); - return ( - <> - - {children} - > - ); - } -}; - - -const getAvailableModules = (modules, currentUser) => { - const isUserAdmin = currentUser.isAdmin; - const customModules = isUserAdmin ? [ - { - separator: true, - id: "nav-separator-2" - }, - { - id: 'settings', - title: 'Settings', - iconName: "SettingsIcon", - notifications: 0, - url: '/settings', - onClick: () => window.open('/settings', "_self"), - onBadgeClick: e => console.log("SettingsIconBadge Clicked", e) - }] : []; - - const separator = { separator: true, id: 'nav-separator-1' }; - const products = modules.map(product => { - return { - id: product.id, - title: product.title, - iconName: 'PeopleIcon', - notifications: 0, - url: product.link, - onClick: () => window.open(product.link, '_self'), - onBadgeClick: e => console.log('PeopleIconBadge Clicked', e) - }; - }) || []; - - return products.length ? [separator, ...products, ...customModules] : products; -}; - -function mapStateToProps(state) { - return { - hasChanges: state.auth.isAuthenticated && state.auth.isLoaded, - availableModules: getAvailableModules(state.auth.modules, state.auth.user), - currentUser: state.auth.user, - currentModuleId: state.auth.settings.currentProductId, - settings: state.auth.settings, - language: state.auth.user.cultureName || state.auth.settings.culture, - }; -} - -const PeopleLayoutContainer = withTranslation()(PurePeopleLayout); - -const PeopleLayout = (props) => { - const { language } = props; - i18n.changeLanguage(language); - return (); -}; - -PeopleLayout.propTypes = { - logout: PropTypes.func.isRequired -}; - -export default connect(mapStateToProps, { logout })(withRouter((PeopleLayout))); diff --git a/web/ASC.Web.Client/src/App.js b/web/ASC.Web.Client/src/App.js index 288f7eb7dc..421837429c 100644 --- a/web/ASC.Web.Client/src/App.js +++ b/web/ASC.Web.Client/src/App.js @@ -1,8 +1,7 @@ import React, { Suspense, lazy } from "react"; import { Router, Route, Switch } from "react-router-dom"; import { Loader } from "asc-web-components"; -import StudioLayout from "./components/Layout/index"; -import { history, PrivateRoute, PublicRoute, Login, Error404 } from "asc-web-common"; +import { history, PrivateRoute, PublicRoute, Login, Error404, StudioLayout} from "asc-web-common"; const Home = lazy(() => import("./components/pages/Home")); const About = lazy(() => import("./components/pages/About")); diff --git a/web/ASC.Web.Client/src/components/Layout/i18n.js b/web/ASC.Web.Client/src/components/Layout/i18n.js deleted file mode 100644 index 7eeb017e67..0000000000 --- a/web/ASC.Web.Client/src/components/Layout/i18n.js +++ /dev/null @@ -1,60 +0,0 @@ -import i18n from "i18next"; -import Backend from "i18next-xhr-backend"; - -const newInstance = i18n.createInstance(); - -if (process.env.NODE_ENV === "production") { - newInstance - .use(Backend) - .init({ - lng: 'en', - fallbackLng: "en", - debug: false, - - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - format: function (value, format) { - if (format === 'lowercase') return value.toLowerCase(); - return value; - } - }, - - react: { - useSuspense: false - }, - backend: { - loadPath: `/locales/Layout/{{lng}}/{{ns}}.json` - } - }); -} else if (process.env.NODE_ENV === "development") { - - const resources = { - en: { - translation: require("./locales/en/translation.json") - }, - ru: { - translation: require("./locales/ru/translation.json") - } - }; - - newInstance.init({ - resources: resources, - lng: 'en', - fallbackLng: "en", - debug: true, - - interpolation: { - escapeValue: false, // not needed for react as it escapes by default - format: function (value, format) { - if (format === 'lowercase') return value.toLowerCase(); - return value; - } - }, - - react: { - useSuspense: false - } - }); -} - -export default newInstance; \ No newline at end of file diff --git a/web/ASC.Web.Client/src/components/Layout/index.js b/web/ASC.Web.Client/src/components/Layout/index.js deleted file mode 100644 index e7b38e23b7..0000000000 --- a/web/ASC.Web.Client/src/components/Layout/index.js +++ /dev/null @@ -1,124 +0,0 @@ -import React from "react"; -import { connect } from "react-redux"; -import PropTypes from "prop-types"; -import { withRouter } from "react-router"; -import { Layout, Toast } from "asc-web-components"; -import { withTranslation, I18nextProvider } from 'react-i18next'; -import i18n from "./i18n"; -import isEqual from "lodash/isEqual"; -import { store } from 'asc-web-common'; -const { logout } = store.auth.actions; - -class PureStudioLayout extends React.Component { - shouldComponentUpdate(nextProps, nextState) { - return !isEqual(this.props, nextProps) || !isEqual(this.state, nextState); - } - - onProfileClick = () => { - window.location.href = "/products/people/view/@self"; - } - - onAboutClick = () => { - this.props.history.push("/about"); - } - - onLogoutClick = () => { - this.props.logout(); - this.props.history.push("/"); - } - - onLogoClick = () => this.props.history.push("/"); - - render() { - const { hasChanges, children, t } = this.props; - - const currentUserActions = [ - { - key: 'ProfileBtn', label: t("Profile"), onClick: this.onProfileClick - }, - { - key: 'AboutBtn', label: t("AboutCompanyTitle"), onClick: this.onAboutClick - }, - { - key: 'LogoutBtn', label: t("LogoutButton"), onClick: this.onLogoutClick - }, - ]; - - const newProps = hasChanges - ? { - currentUserActions: currentUserActions, - onLogoClick: this.onLogoClick, - ...this.props - } - : {}; - - console.log("StudioLayout render", newProps); - return ( - <> - - {children} - > - ); - } -}; - - -const getAvailableModules = (modules, currentUser) => { - const isUserAdmin = currentUser.isAdmin; - const separator = { separator: true, id: "nav-separator-1" }; - const customModules = isUserAdmin ? [ - { - separator: true, - id: "nav-separator-2" - }, - { - id: 'settings', - title: 'Settings', - iconName: "SettingsIcon", - notifications: 0, - url: '/settings', - onClick: () => window.open('/settings', "_self"), - onBadgeClick: e => console.log("SettingsIconBadge Clicked", e) - }] : []; - const products = - modules.map(product => { - return { - id: product.id, - title: product.title, - iconName: "PeopleIcon", - notifications: 0, - url: product.link, - onClick: () => window.open(product.link, "_self"), - onBadgeClick: e => console.log("PeopleIconBadge Clicked", e) - }; - }) || []; - - return products.length ? [separator, ...products, ...customModules] : products; -}; - -function mapStateToProps(state) { - let availableModules = getAvailableModules(state.auth.modules, state.auth.user); - return { - hasChanges: state.auth.isAuthenticated && state.auth.isLoaded, - availableModules: availableModules, - currentUser: state.auth.user, - currentModuleId: state.auth.settings.currentProductId, - language: state.auth.user.cultureName || state.auth.settings.culture, - }; -}; -const StudioLayoutContainer = withTranslation()(PureStudioLayout); - -const StudioLayout = (props) => { - const { language } = props; - i18n.changeLanguage(language); - return (); -}; - -StudioLayout.propTypes = { - logout: PropTypes.func.isRequired -}; - -export default connect( - mapStateToProps, - { logout } -)(withRouter(StudioLayout)); diff --git a/web/ASC.Web.Client/src/components/Layout/locales/en/translation.json b/web/ASC.Web.Client/src/components/Layout/locales/en/translation.json deleted file mode 100644 index 63d5bb0dbe..0000000000 --- a/web/ASC.Web.Client/src/components/Layout/locales/en/translation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Profile": "Profile", - "AboutCompanyTitle": "About this program", - "LogoutButton": "Sign Out" -} \ No newline at end of file diff --git a/web/ASC.Web.Client/src/components/Layout/locales/ru/translation.json b/web/ASC.Web.Client/src/components/Layout/locales/ru/translation.json deleted file mode 100644 index 0883767d3c..0000000000 --- a/web/ASC.Web.Client/src/components/Layout/locales/ru/translation.json +++ /dev/null @@ -1,5 +0,0 @@ -{ - "Profile": "Профиль", - "AboutCompanyTitle": "О программе", - "LogoutButton": "Выйти" -} \ No newline at end of file diff --git a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/activateUser.js b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/activateUser.js index 4d9cf76e9b..7ecff10599 100644 --- a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/activateUser.js +++ b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/activateUser.js @@ -3,7 +3,6 @@ import { withRouter } from "react-router"; import { withTranslation } from 'react-i18next'; import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } from 'asc-web-components'; import styled from 'styled-components'; -import { Collapse } from 'reactstrap'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { store, constants } from 'asc-web-common'; @@ -298,10 +297,9 @@ class Confirm extends React.PureComponent { */} - - {this.state.errorText} - + + {this.state.errorText} + ) diff --git a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changeOwner.js b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changeOwner.js index 754cd68caa..a9c26f2db4 100644 --- a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changeOwner.js +++ b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changeOwner.js @@ -4,44 +4,44 @@ import { withTranslation } from "react-i18next"; import { connect } from "react-redux"; import PropTypes from "prop-types"; import styled from "styled-components"; -import { Container, Row, Col, Card, CardTitle, CardImg } from "reactstrap"; import { Button, PageLayout, Text, toastr } from "asc-web-components"; //import { } from "../../../../../src/store/auth/actions"; -const BodyStyle = styled(Container)` +const BodyStyle = styled.div` margin-top: 70px; - .buttons-style { - margin-top: 20px; - min-width: 110px; - } - .button-style { - margin-right: 8px; - } - .confirm_text { - margin: 12px 0; - } + .owner-container { + display: grid; - .password-card { - border: none; - .card-img { - max-width: 216px; - max-height: 35px; - } - .card-title { - word-wrap: break-word; - margin: 8px 0; - text-align: left; - font-size: 24px; - color: #116d9d; - } - } + .owner-wrapper { + align-self: center; + justify-self: center; + .owner-img { + max-width: 216px; + max-height: 35px; + } + .owner-title { + word-wrap: break-word; + margin: 8px 0; + text-align: left; + font-size: 24px; + color: #116d9d; + } + .owner-confirm_text { + margin: 20px 0 12px 0; + } + .owner-buttons { + margin-top: 20px; + min-width: 110px; + } + .owner-button { + margin-right: 8px; + } - .row_display { - display: flex; - } - .confirm-text { - margin-top: 32px; + .owner-confirm-message { + margin-top: 32px; + } + } } `; @@ -68,61 +68,47 @@ class Form extends React.PureComponent { render() { const { t, greetingTitle } = this.props; - const mdOptions = { size: 6, offset: 2 }; return ( - - - - - {greetingTitle} - - - - - - + + + + {greetingTitle} + {t("ConfirmOwnerPortalTitle", { newOwner: "NEW OWNER" })} - - - {this.state.showButtons ? ( - - - - - - - ) : ( - - - + {this.state.showButtons ? ( + <> + + + > + ) : ( + {t("ConfirmOwnerPortalSuccessMessage")} - - - )} + )} + + ); } diff --git a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changePassword.js b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changePassword.js index 325fdda331..730c2e6c99 100644 --- a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changePassword.js +++ b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/changePassword.js @@ -4,7 +4,6 @@ import { withTranslation } from "react-i18next"; import { connect } from "react-redux"; import PropTypes from "prop-types"; import styled from "styled-components"; -import { Container, Row, Col, Card, CardTitle, CardImg } from "reactstrap"; import { Button, PageLayout, @@ -13,34 +12,32 @@ import { Loader, toastr } from "asc-web-components"; -import { store } from 'asc-web-common'; -const { changePassword, getConfirmationInfo, logout } = store.auth.actions; +import { store } from "asc-web-common"; +const { changePassword, getConfirmationInfo, logout } = store.auth.actions; -const BodyStyle = styled(Container)` - margin-top: 70px; - p { +const BodyStyle = styled.form` + margin: 70px auto 0 auto; + max-width: 500px; + + .password-header { + margin-bottom: 24px; + + .password-logo { + max-width: 216px; + max-height: 35px; + } + + .password-title { + margin: 8px 0; + } + } + + .password-text { margin-bottom: 5px; } - .button-style { - margin-top: 20px; - } - .password-row { - margin: 23px 0 0; - .password-card { - border: none; - .card-img { - max-width: 216px; - max-height: 35px; - } - .card-title { - word-wrap: break-word; - margin: 8px 0; - text-align: left; - font-size: 24px; - color: #116d9d; - } - } + .password-button { + margin-top: 20px; } `; @@ -106,8 +103,7 @@ class Form extends React.PureComponent { componentDidMount() { const { getConfirmationInfo, history } = this.props; - getConfirmationInfo(this.state.key) - .catch(error => { + getConfirmationInfo(this.state.key).catch(error => { toastr.error(this.props.t(`${error}`)); history.push("/"); }); @@ -126,69 +122,65 @@ class Form extends React.PureComponent { render() { const { settings, isConfirmLoaded, t, greetingTitle } = this.props; const { isLoading, password, passwordEmpty } = this.state; - const mdOptions = { size: 6, offset: 3 }; return !isConfirmLoaded ? ( ) : ( - - - - - - {greetingTitle} - - - {t("PassworResetTitle")} - - - - - + + + + {greetingTitle} + + + + {t("PassworResetTitle")} + + + ); } @@ -215,11 +207,12 @@ function mapStateToProps(state) { isConfirmLoaded: state.auth.isConfirmLoaded, settings: state.auth.settings.passwordSettings, isAuthenticated: state.auth.isAuthenticated, - greetingTitle: state.auth.settings.greetingSettings, + greetingTitle: state.auth.settings.greetingSettings }; } -export default connect( - mapStateToProps, - { changePassword, getConfirmationInfo, logout } -)(withRouter(withTranslation()(ChangePasswordForm))); +export default connect(mapStateToProps, { + changePassword, + getConfirmationInfo, + logout +})(withRouter(withTranslation()(ChangePasswordForm))); diff --git a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js index 5ef6fa84bf..8691a5875a 100644 --- a/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js +++ b/web/ASC.Web.Client/src/components/pages/Confirm/sub-components/createUser.js @@ -3,7 +3,6 @@ import { withRouter } from "react-router"; import { withTranslation } from 'react-i18next'; import { Button, TextInput, PageLayout, Text, PasswordInput, toastr, Loader } from 'asc-web-components'; import styled from 'styled-components'; -import { Collapse } from 'reactstrap'; import { connect } from 'react-redux'; import PropTypes from 'prop-types'; import { store } from 'asc-web-common'; @@ -318,10 +317,9 @@ class Confirm extends React.PureComponent { */} - - {this.state.errorText} - + + {this.state.errorText} + ) diff --git a/web/ASC.Web.Client/src/components/pages/Settings/Layout/index.js b/web/ASC.Web.Client/src/components/pages/Settings/Layout/index.js index 562bb381e4..b38e6d6585 100644 --- a/web/ASC.Web.Client/src/components/pages/Settings/Layout/index.js +++ b/web/ASC.Web.Client/src/components/pages/Settings/Layout/index.js @@ -1,6 +1,6 @@ -import React, { Suspense, useEffect } from "react"; +import React, { useEffect } from "react"; import { connect } from 'react-redux'; -import { Loader, PageLayout } from "asc-web-components"; +import { PageLayout } from "asc-web-components"; import i18n from "../i18n"; import { I18nextProvider } from "react-i18next"; import { @@ -20,18 +20,13 @@ const Layout = ({ currentProductId, setCurrentProductId, language, children }) = return ( - } - > - } - articleBodyContent={} - sectionHeaderContent={} - sectionBodyContent={children} - /> - - + } + articleBodyContent={} + sectionHeaderContent={} + sectionBodyContent={children} + /> ); }; diff --git a/web/ASC.Web.Common/package.json b/web/ASC.Web.Common/package.json index 2eb4da7dcd..8dd2f023bb 100644 --- a/web/ASC.Web.Common/package.json +++ b/web/ASC.Web.Common/package.json @@ -1,6 +1,6 @@ { "name": "asc-web-common", - "version": "1.0.1", + "version": "1.0.4", "description": "Ascensio System SIA common components and solutions library", "license": "AGPL-3.0", "files": [ @@ -122,4 +122,4 @@ "node": ">=8", "npm": ">=5" } -} \ No newline at end of file +} diff --git a/web/ASC.Web.Common/src/components/index.js b/web/ASC.Web.Common/src/components/index.js index de5d30970f..388184ea3a 100644 --- a/web/ASC.Web.Common/src/components/index.js +++ b/web/ASC.Web.Common/src/components/index.js @@ -1,3 +1,4 @@ export { default as PrivateRoute } from "./routing/privateRoute"; export { default as PublicRoute } from "./routing/publicRoute"; -export { default as Login } from "./login"; \ No newline at end of file +export { default as Login } from "./login"; +export { default as StudioLayout } from "./layout"; \ No newline at end of file diff --git a/web/ASC.Web.Common/src/components/layout/i18n.js b/web/ASC.Web.Common/src/components/layout/i18n.js new file mode 100644 index 0000000000..301eab76bc --- /dev/null +++ b/web/ASC.Web.Common/src/components/layout/i18n.js @@ -0,0 +1,35 @@ +import i18n from "i18next"; +import en from "./locales/en/translation.json"; +import ru from "./locales/ru/translation.json"; + +const newInstance = i18n.createInstance(); + +const resources = { + en: { + translation: en//require("./locales/en/translation.json") + }, + ru: { + translation: ru//require("./locales/ru/translation.json") + } +}; + +newInstance.init({ + resources: resources, + lng: 'en', + fallbackLng: "en", + debug: true, + + interpolation: { + escapeValue: false, // not needed for react as it escapes by default + format: function (value, format) { + if (format === 'lowercase') return value.toLowerCase(); + return value; + } + }, + + react: { + useSuspense: false + } +}); + +export default newInstance; \ No newline at end of file diff --git a/web/ASC.Web.Common/src/components/layout/index.js b/web/ASC.Web.Common/src/components/layout/index.js new file mode 100644 index 0000000000..347d335dcd --- /dev/null +++ b/web/ASC.Web.Common/src/components/layout/index.js @@ -0,0 +1,89 @@ +import React from "react"; +import { connect } from "react-redux"; +import PropTypes from "prop-types"; +import { withRouter } from "react-router"; +import { withTranslation, I18nextProvider } from "react-i18next"; +import i18n from "./i18n"; +import { logout } from "../../store/auth/actions"; +import PureStudioLayout from "./pureStudioLayout"; + +const getSeparator = id => { + return { + separator: true, + id: id + }; +}; + +const toModuleWrapper = (item, iconName) => { + return { + id: item.id, + title: item.title, + iconName: iconName || "PeopleIcon", + notifications: 0, + url: item.link, + onClick: () => window.open(item.link, "_self"), + onBadgeClick: e => console.log(iconName + " Badge Clicked", e) + }; +}; + +const getCustomModules = isAdmin => { + if (!isAdmin) { + return []; + } + + const separator = getSeparator("nav-modules-separator"); + const settingsModuleWrapper = toModuleWrapper( + { + id: "settings", + title: "Settings", + link: "/settings" + }, + "SettingsIcon" + ); + + return [separator, settingsModuleWrapper]; +}; + +const getAvailableModules = (modules, currentUser) => { + if (!modules.length) { + return []; + } + + const isUserAdmin = currentUser.isAdmin; + const customModules = getCustomModules(isUserAdmin); + const separator = getSeparator("nav-products-separator"); + const products = modules.map(toModuleWrapper); + + return [separator, ...products, ...customModules]; +}; + +function mapStateToProps(state) { + return { + hasChanges: state.auth.isAuthenticated && state.auth.isLoaded, + availableModules: getAvailableModules(state.auth.modules, state.auth.user), + currentUser: state.auth.user, + currentModuleId: state.auth.settings.currentProductId, + settings: state.auth.settings, + language: state.auth.user.cultureName || state.auth.settings.culture + }; +} + +const StudioLayoutContainer = withTranslation()(PureStudioLayout); + +const StudioLayout = props => { + const { language } = props; + i18n.changeLanguage(language); + + return ( + + + + ); +}; + +StudioLayout.propTypes = { + logout: PropTypes.func.isRequired, + language: PropTypes.string +}; + +export default connect(mapStateToProps, { logout })(withRouter(StudioLayout)); diff --git a/products/ASC.People/Client/src/components/Layout/locales/en/translation.json b/web/ASC.Web.Common/src/components/layout/locales/en/translation.json similarity index 100% rename from products/ASC.People/Client/src/components/Layout/locales/en/translation.json rename to web/ASC.Web.Common/src/components/layout/locales/en/translation.json diff --git a/products/ASC.People/Client/src/components/Layout/locales/ru/translation.json b/web/ASC.Web.Common/src/components/layout/locales/ru/translation.json similarity index 100% rename from products/ASC.People/Client/src/components/Layout/locales/ru/translation.json rename to web/ASC.Web.Common/src/components/layout/locales/ru/translation.json diff --git a/web/ASC.Web.Common/src/components/layout/pureStudioLayout.js b/web/ASC.Web.Common/src/components/layout/pureStudioLayout.js new file mode 100644 index 0000000000..f9a45c185d --- /dev/null +++ b/web/ASC.Web.Common/src/components/layout/pureStudioLayout.js @@ -0,0 +1,85 @@ +import React from "react"; +import PropTypes from "prop-types"; +import { Layout, Toast } from "asc-web-components"; + +class PureStudioLayout extends React.Component { + shouldComponentUpdate(nextProps) { + return this.props.hasChanges !== nextProps.hasChanges || + this.props.currentModuleId !== nextProps.currentModuleId; + } + + onProfileClick = () => { + const { history, settings } = this.props; + if (settings.homepage == "/products/people") { + history.push("/products/people/view/@self"); + } else { + window.open("/products/people/view/@self", "_self") + } + }; + + onAboutClick = () => { + window.open("/about", "_self") + }; + + onLogoutClick = () => { + this.props.logout(); + }; + + onLogoClick = () => { + window.open("/", "_self") + }; + + render() { + const { hasChanges, children, t } = this.props; + + const currentUserActions = [ + { + key: "ProfileBtn", + label: t("Profile"), + onClick: this.onProfileClick + }, + { + key: "AboutBtn", + label: t("AboutCompanyTitle"), + onClick: this.onAboutClick + }, + { + key: "LogoutBtn", + label: t("LogoutButton"), + onClick: this.onLogoutClick + } + ]; + + const newProps = hasChanges + ? { + currentUserActions: currentUserActions, + onLogoClick: this.onLogoClick, + ...this.props + } + : {}; + + console.log("PureStudioLayout render", newProps); + + return ( + <> + + + {children} + + > + ); + } +} + +PureStudioLayout.propTypes = { + logout: PropTypes.func.isRequired, + language: PropTypes.string, + hasChanges: PropTypes.bool, + currentModuleId: PropTypes.string, + history: PropTypes.object, + settings: PropTypes.object, + children: PropTypes.any, + t: PropTypes.func +}; + +export default PureStudioLayout; diff --git a/web/ASC.Web.Common/src/components/login/index.js b/web/ASC.Web.Common/src/components/login/index.js index 4a05aa6b57..2924d572c5 100644 --- a/web/ASC.Web.Common/src/components/login/index.js +++ b/web/ASC.Web.Common/src/components/login/index.js @@ -1,15 +1,6 @@ -import React, { useState, useEffect, useCallback } from "react"; +import React, { Component } from "react"; import PropTypes from "prop-types"; import { withRouter } from "react-router"; -import { - Collapse, - Container, - Row, - Col, - Card, - CardTitle, - CardImg -} from "reactstrap"; import { Button, TextInput, @@ -22,329 +13,348 @@ import { } from "asc-web-components"; import { connect } from "react-redux"; import styled from "styled-components"; -import { useTranslation } from "react-i18next"; +import { withTranslation, I18nextProvider } from "react-i18next"; import i18n from "./i18n"; import SubModalDialog from "./sub-components/modal-dialog"; import { login, setIsLoaded } from "../../store/auth/actions"; import { sendInstructionsToChangePassword } from "../../api/people"; -const FormContainer = styled(Container)` - margin-top: 70px; +const FormContainer = styled.form` + margin: 50px auto 0 auto; + max-width: 432px; - .link-style { - float: right; - line-height: 16px; + .login-header { + margin-bottom: 24px; + + .login-logo { + max-width: 216px; + max-height: 35px; + } + + .login-title { + margin: 8px 0; + } } - .text-body { + .login-input { + margin-bottom: 24px; + } + + .login-forgot-wrapper { + height: 36px; + + .login-checkbox-wrapper { + position: absolute; + display: inline-flex; + + .login-checkbox { + float: left; + span { + font-size: 12px; + } + } + .login-tooltip { + margin-left: 3px; + display: inline-flex; + } + } + .login-link { + float: right; + line-height: 16px; + } + } + + .login-button { margin-bottom: 16px; } - .btn-style { + .login-button-dialog { margin-right: 8px; } - - .checkbox { - float: left; - span { - font-size: 12px; - } - } - - .question-icon { - float: left; - margin-left: 4px; - line-height: 16px; - } - - .login-row { - margin: 23px 0 0; - - .login-card { - border: none; - - .card-img { - max-width: 216px; - max-height: 35px; - } - .card-title { - word-wrap: break-word; - margin: 8px 0; - text-align: left; - font-size: 24px; - color: #116d9d; - } - } - } - - .button-row { - margin: 16px 0 0; - } `; -const TooltipStyle = styled.span` - margin-left: 3px; - position: absolute; - margin-top: 2px; -`; +class Form extends Component { + constructor(props) { + super(props); -const mdOptions = { size: 6, offset: 3 }; + this.state = { + identifierValid: true, + identifier: "", + isLoading: false, + isDisabled: false, + passwordValid: true, + password: "", + isChecked: false, + openDialog: false, + email: "", + errorText: "" + }; + } -const Form = props => { - const { t } = useTranslation("translation", { i18n }); - const { login, setIsLoaded, match, history, language, greetingTitle } = props; - const { params } = match; - const [identifier, setIdentifier] = useState(params.confirmedEmail || ""); - const [identifierValid, setIdentifierValid] = useState(true); - const [password, setPassword] = useState(""); - const [passwordValid, setPasswordValid] = useState(true); - const [errorText, setErrorText] = useState(""); - const [isLoading, setIsLoading] = useState(false); - - const [openDialog, setOpenDialog] = useState(false); - const [email, setEmail] = useState(""); - const [isDisabled, setIsDisabled] = useState(false); - const [isChecked, setIsisChecked] = useState(false); - - const onClick = () => { - setOpenDialog(true); - setIsDisabled(true); - setEmail(identifier); + onChangeLogin = event => { + this.setState({ identifier: event.target.value }); + !this.state.identifierValid && this.setState({ identifierValid: true }); + this.state.errorText && this.setState({ errorText: "" }); }; - const onDialogClose = () => { - setOpenDialog(false); - setIsDisabled(false); - setIsLoading(false); - setEmail(""); + onChangePassword = event => { + this.setState({ password: event.target.value }); + !this.state.passwordValid && this.setState({ passwordValid: true }); + this.state.errorText && this.setState({ errorText: "" }); }; - const onSendPasswordInstructions = useCallback(() => { - setIsLoading(true); - sendInstructionsToChangePassword(email) - .then(res => toastr.success(res), message => toastr.error(message)) - .finally(onDialogClose()); - }, [email]); + onChangeEmail = event => { + this.setState({ email: event.target.value }); + }; - const onSubmit = useCallback(() => { - errorText && setErrorText(""); + onChangeCheckbox = () => this.setState({ isChecked: !this.state.isChecked }); + + onClick = () => { + this.setState({ + openDialog: true, + isDisabled: true, + email: this.state.identifier + }); + }; + + onKeyPress = event => { + if (event.key === "Enter") { + !this.state.isDisabled + ? this.onSubmit() + : this.onSendPasswordInstructions(); + } + }; + + onSendPasswordInstructions = () => { + this.setState({ isLoading: true }); + sendInstructionsToChangePassword(this.state.email) + .then( + res => toastr.success(res), + message => toastr.error(message) + ) + .finally(this.onDialogClose()); + }; + + onDialogClose = () => { + this.setState({ + openDialog: false, + isDisabled: false, + isLoading: false, + email: "" + }); + }; + + onSubmit = () => { + const { errorText, identifier, password } = this.state; + const { login, setIsLoaded, history } = this.props; + + errorText && this.setState({ errorText: "" }); let hasError = false; const userName = identifier.trim(); if (!userName) { hasError = true; - setIdentifierValid(!hasError); + this.setState({ identifierValid: !hasError }); } const pass = password.trim(); if (!pass) { hasError = true; - setPasswordValid(!hasError); + this.setState({ passwordValid: !hasError }); } if (hasError) return false; - setIsLoading(true); + this.setState({ isLoading: true }); - login(userName, pass).then( - () => { - //console.log("auth success", match, location, history); - setIsLoading(false); + login(userName, pass) + .then(() => { setIsLoaded(true); history.push("/"); - }, - error => { - //console.error("auth error", error); - setErrorText(error); - setIsLoading(false); - } - ); - }, [errorText, history, identifier, login, setIsLoaded, password]); + }) + .catch(error => { + this.setState({ errorText: error, isLoading: false }); + }); + }; - const onKeyPress = useCallback( - event => { - if (event.key === "Enter") { - !isDisabled ? onSubmit() : onSendPasswordInstructions(); - } - }, - [onSendPasswordInstructions, onSubmit, isDisabled] - ); - - useEffect(() => { + componentDidMount() { + const { language, match } = this.props; + const { params } = match; i18n.changeLanguage(language); - params.error && setErrorText(params.error); - window.addEventListener("keyup", onKeyPress); - // Remove event listeners on cleanup - return () => { - window.removeEventListener("keyup", onKeyPress); - }; - }, [onKeyPress, params, language]); + params.error && this.setState({ errorText: params.error }); + window.addEventListener("keyup", this.onKeyPress); + } - const onChangePassword = event => { - setPassword(event.target.value); - !passwordValid && setPasswordValid(true); - errorText && setErrorText(""); - }; + componentWillUnmount() { + window.removeEventListener("keyup", this.onKeyPress); + } - const onChangeLogin = event => { - setIdentifier(event.target.value); - !identifierValid && setIdentifierValid(true); - errorText && setErrorText(""); - }; + render() { + const { greetingTitle, match, t } = this.props; - const onChangeEmail = event => { - setEmail(event.target.value); - }; + const { + identifierValid, + identifier, + isLoading, + passwordValid, + password, + isChecked, + openDialog, + email, + errorText + } = this.state; + const { params } = match; - // console.log('Login render'); + //console.log("Login render"); - return ( - - - - - + + + + {greetingTitle} + + + + + + + + + + - - {greetingTitle} - - - - - - - - - - - - - - - - - - {t("ForgotPassword")} - - setIsisChecked(!isChecked)} - label={t("Remember")} - /> - {t("RememberHelper")} } /> - - - + - {openDialog ? ( - - ) : null} + + {t("ForgotPassword")} + + - - - - - - {params.confirmedEmail && ( - - - - {t("MessageEmailConfirmed")} {t("MessageAuthorize")} - - - - )} - - - - {errorText} - - - - + ) : null} + + + + {params.confirmedEmail && ( + + {t("MessageEmailConfirmed")} {t("MessageAuthorize")} + + )} + + {errorText} + + + ); + } +} + +const FormWrapper = withTranslation()(Form); + +const LoginForm = props => { + const { language } = props; + i18n.changeLanguage(language); + + return ( + + } /> + ); }; -const LoginForm = props => ( - } /> -); - -LoginForm.propTypes = { +Form.propTypes = { login: PropTypes.func.isRequired, match: PropTypes.object.isRequired, - location: PropTypes.object.isRequired, - history: PropTypes.object.isRequired + history: PropTypes.object.isRequired, + setIsLoaded: PropTypes.func.isRequired, + greetingTitle: PropTypes.string.isRequired, + t: PropTypes.func.isRequired, + language: PropTypes.string.isRequired }; -LoginForm.defaultProps = { +Form.defaultProps = { identifier: "", password: "", email: "" }; +LoginForm.propTypes = { + language: PropTypes.string.isRequired +}; + function mapStateToProps(state) { return { language: state.auth.user.cultureName || state.auth.settings.culture, @@ -352,7 +362,6 @@ function mapStateToProps(state) { }; } -export default connect( - mapStateToProps, - { login, setIsLoaded } -)(withRouter(LoginForm)); +export default connect(mapStateToProps, { login, setIsLoaded })( + withRouter(LoginForm) +); diff --git a/web/ASC.Web.Common/src/components/login/sub-components/modal-dialog.js b/web/ASC.Web.Common/src/components/login/sub-components/modal-dialog.js index 80f360630c..c19decdd4f 100644 --- a/web/ASC.Web.Common/src/components/login/sub-components/modal-dialog.js +++ b/web/ASC.Web.Common/src/components/login/sub-components/modal-dialog.js @@ -45,7 +45,7 @@ class SubModalDialog extends React.Component { ]} footerContent={[ { this.setState({isOpen: !this.state.isOpen}); - } + }; render() { const { isOpen, displayType } = this.state; @@ -178,8 +178,7 @@ class HelpButton extends React.Component { @@ -203,7 +202,8 @@ HelpButton.propTypes = { PropTypes.arrayOf(PropTypes.node), PropTypes.node ]), - tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.object]).isRequired, + tooltipContent: PropTypes.oneOfType([PropTypes.string, PropTypes.object]) + .isRequired, offsetRight: PropTypes.number, tooltipMaxWidth: PropTypes.number, tooltipId: PropTypes.string, diff --git a/web/ASC.Web.Components/src/components/page-layout/sub-components/article.js b/web/ASC.Web.Components/src/components/page-layout/sub-components/article.js index 7427338417..cb7ff37631 100644 --- a/web/ASC.Web.Components/src/components/page-layout/sub-components/article.js +++ b/web/ASC.Web.Components/src/components/page-layout/sub-components/article.js @@ -31,8 +31,13 @@ const StyledArticle = styled.article` z-index: 400; ` : ` - display: none; - width: 0px; + width: 240px; + min-width: 240px; + position: fixed; + height: 100%; + top: 0; + left: -240px; + z-index: 400; `} } `; diff --git a/web/ASC.Web.Components/src/components/toggle-button/index.js b/web/ASC.Web.Components/src/components/toggle-button/index.js index cb7c06b479..7caa9fc90d 100644 --- a/web/ASC.Web.Components/src/components/toggle-button/index.js +++ b/web/ASC.Web.Components/src/components/toggle-button/index.js @@ -56,7 +56,7 @@ class ToggleButton extends Component { constructor(props) { super(props); this.state = { - checked: this.props.isChecked + checked: props.isChecked }; } @@ -104,4 +104,8 @@ ToggleButton.propTypes = { className: PropTypes.string }; +ToggleIcon.propTypes = { + isChecked: PropTypes.bool +} + export default ToggleButton;