Parcourir la source

UI 1.0.0-6 release.

Alexander il y a 4 ans
Parent
commit
0420ecc0e6
58 fichiers modifiés avec 374 ajouts et 151 suppressions
  1. 2 1
      src/react/jsconfig.json
  2. 9 0
      src/react/package-lock.json
  3. 1 0
      src/react/package.json
  4. 9 1
      src/react/src/actions/MenuCounters/menuCounterActions.js
  5. 1 0
      src/react/src/actions/Panel/panelTypes.js
  6. 37 6
      src/react/src/actions/Session/sessionActions.js
  7. 0 1
      src/react/src/components/Backup/RestoreSettings/BackupRestoreSettings.jsx
  8. 3 2
      src/react/src/components/ControlPanel/AddItemLayout/AddItemLayout.scss
  9. 1 0
      src/react/src/components/ControlPanel/AddItemLayout/Form/Password/Password.jsx
  10. 1 1
      src/react/src/components/Firewall/Add/AddFirewall.scss
  11. 1 1
      src/react/src/components/InternetProtocol/InternetProtocol.jsx
  12. 8 4
      src/react/src/components/MailAccount/Add/AddMailAccount.jsx
  13. 1 1
      src/react/src/components/MailAccount/Add/AddMailAccount.scss
  14. 3 1
      src/react/src/components/MailAccount/Edit/EditMailAccount.jsx
  15. 19 9
      src/react/src/components/MailAccount/MailInfoBlock/MailInfoBlock.jsx
  16. 1 1
      src/react/src/components/MainNav/Mobile/MobileTopNav.scss
  17. 5 4
      src/react/src/components/MainNav/Panel/Notifications/Notifications.jsx
  18. 38 23
      src/react/src/components/MainNav/Panel/Panel.jsx
  19. 9 15
      src/react/src/components/MainNav/Panel/Panel.scss
  20. 17 3
      src/react/src/components/MainNav/Stat-menu/Menu.jsx
  21. 0 4
      src/react/src/components/MainNav/Stat-menu/Menu.scss
  22. 25 2
      src/react/src/components/MainNav/Toolbar/Toolbar.scss
  23. 1 1
      src/react/src/components/Package/Add/AddPackage.scss
  24. 2 1
      src/react/src/components/Package/Package.jsx
  25. 1 1
      src/react/src/components/Server/Edit/Bind9/Bind9.scss
  26. 1 1
      src/react/src/components/Server/Edit/Dovecot/Dovecot.scss
  27. 3 2
      src/react/src/components/Server/Edit/EditDatabaseOption.jsx
  28. 2 1
      src/react/src/components/Server/Edit/EditMailOption.jsx
  29. 0 6
      src/react/src/components/Server/Edit/EditServer.jsx
  30. 1 1
      src/react/src/components/Server/Edit/EditServer.scss
  31. 2 2
      src/react/src/components/Server/Edit/Httpd/EditHttpd.scss
  32. 1 1
      src/react/src/components/Server/Edit/Mysql/Mysql.scss
  33. 2 2
      src/react/src/components/Server/Edit/Nginx/EditServerNginx.scss
  34. 2 2
      src/react/src/components/Server/Edit/PHP/EditPhp.scss
  35. 1 1
      src/react/src/components/Server/Edit/Postgresql/Postgresql.scss
  36. 1 1
      src/react/src/components/Server/Edit/Service/Service.scss
  37. 5 3
      src/react/src/components/TopPanel/TopPanel.jsx
  38. 18 0
      src/react/src/components/TopPanel/TopPanel.scss
  39. 2 2
      src/react/src/components/User/User.scss
  40. 2 1
      src/react/src/components/WebDomain/Add/AddWebDomain.jsx
  41. 0 2
      src/react/src/components/WebDomain/Add/SslSupport/SslSupport.jsx
  42. 1 1
      src/react/src/components/WebDomain/Edit/SslSupport/SslSupport.scss
  43. 1 1
      src/react/src/components/WebDomain/WebDomain.jsx
  44. 5 1
      src/react/src/components/WebDomain/WebDomain.scss
  45. 1 1
      src/react/src/containers/App/App.js
  46. 1 1
      src/react/src/containers/MailAccounts/MailAccounts.jsx
  47. 1 1
      src/react/src/containers/MailAccounts/MailAccounts.scss
  48. 1 1
      src/react/src/containers/Mails/Mails.jsx
  49. 1 1
      src/react/src/containers/Servers/Servers.jsx
  50. 38 17
      src/react/src/containers/ServiceInfo/index.jsx
  51. 30 1
      src/react/src/containers/ServiceInfo/styles.scss
  52. 8 0
      src/react/src/containers/Web/Web.jsx
  53. 1 1
      src/react/src/containers/WebLogs/WebLogs.jsx
  54. 24 6
      src/react/src/containers/WebLogs/WebLogs.scss
  55. 1 1
      src/react/src/reducers/Notification/notificationReducer.js
  56. 19 0
      src/react/src/reducers/Panel/panel.js
  57. 1 6
      src/react/src/reducers/Session/sessionReducer.js
  58. 2 0
      src/react/src/reducers/rootReducer.js

+ 2 - 1
src/react/jsconfig.json

@@ -2,5 +2,6 @@
   "compilerOptions": {
     "baseUrl": "."
   },
-  "include": ["src"]
+  "include": ["src"],
+  "exclude": ["node_modules", "build"]
 }

+ 9 - 0
src/react/package-lock.json

@@ -11526,6 +11526,15 @@
       "resolved": "https://registry.npmjs.org/react-lifecycles-compat/-/react-lifecycles-compat-3.0.4.tgz",
       "integrity": "sha512-fBASbA6LnOU9dOU2eW7aQ8xmYBSXUIWr+UmF9b1efZBazGNO+rcXT/icdKnYm2pTwcRylVUYwW7H1PHfLekVzA=="
     },
+    "react-perfect-scrollbar": {
+      "version": "1.5.8",
+      "resolved": "https://registry.npmjs.org/react-perfect-scrollbar/-/react-perfect-scrollbar-1.5.8.tgz",
+      "integrity": "sha512-bQ46m70gp/HJtiBOF3gRzBISSZn8FFGNxznTdmTG8AAwpxG1bJCyn7shrgjEvGSQ5FJEafVEiosY+ccER11OSA==",
+      "requires": {
+        "perfect-scrollbar": "^1.5.0",
+        "prop-types": "^15.6.1"
+      }
+    },
     "react-redux": {
       "version": "7.2.1",
       "resolved": "https://registry.npmjs.org/react-redux/-/react-redux-7.2.1.tgz",

+ 1 - 0
src/react/package.json

@@ -25,6 +25,7 @@
     "react-dom": "^16.10.2",
     "react-helmet": "^6.1.0",
     "react-html-parser": "^2.0.2",
+    "react-perfect-scrollbar": "^1.5.8",
     "react-redux": "^7.2.1",
     "react-router-dom": "^5.1.2",
     "react-scripts": "^3.4.1",

+ 9 - 1
src/react/src/actions/MenuCounters/menuCounterActions.js

@@ -1,12 +1,13 @@
 import { REFRESH_COUNTERS } from './menuCounterTypes';
 import { checkAuth } from 'src/services/session';
 import { setAuthToken } from 'src/utils/token';
+import { REFRESH_PANEL } from '../Panel/panelTypes';
 
 export const refreshCounters = () => (dispatch, getState) => {
   return new Promise((resolve, reject) => {
     checkAuth()
       .then(res => {
-        const { data, token } = res.data;
+        const { data, token, panel } = res.data;
 
         if (token) setAuthToken(token);
 
@@ -17,6 +18,13 @@ export const refreshCounters = () => (dispatch, getState) => {
           }
         });
 
+        dispatch({
+          type: REFRESH_PANEL,
+          value: {
+            panel
+          }
+        });
+
         resolve(token);
       })
       .catch(err => {

+ 1 - 0
src/react/src/actions/Panel/panelTypes.js

@@ -0,0 +1 @@
+export const REFRESH_PANEL = 'REFRESH_PANEL';

+ 37 - 6
src/react/src/actions/Session/sessionActions.js

@@ -4,6 +4,7 @@ import { resetPassword } from 'src/ControlPanelService/ResetPassword';
 import { resetAuthToken, setAuthToken } from 'src/utils/token';
 import { REFRESH_COUNTERS } from '../MenuCounters/menuCounterTypes';
 import { SET_USER_SESSION } from '../UserSession/userSessionTypes';
+import { REFRESH_PANEL } from '../Panel/panelTypes';
 
 const LOGOUT_RESPONSE = 'logged_out';
 const LOGOUT_AS_RESPONSE = 'logged_out_as';
@@ -19,12 +20,17 @@ export const login = (user, password) => dispatch => {
         type: LOGIN,
         value: {
           token: token || '',
-          panel,
           i18n: i18n || {},
           userName: user,
           error
         },
       });
+      dispatch({
+        type: REFRESH_PANEL,
+        value: {
+          panel
+        }
+      });
       dispatch({
         type: REFRESH_COUNTERS,
         value: {
@@ -51,11 +57,16 @@ export const reset = ({ user = '', code = '', password = '', password_confirm =
         type: RESET_PASSWORD,
         value: {
           token,
-          panel,
           userName: user,
           error
         },
       });
+      dispatch({
+        type: REFRESH_PANEL,
+        value: {
+          panel
+        }
+      });
       dispatch({
         type: REFRESH_COUNTERS,
         value: {
@@ -84,11 +95,16 @@ export const loginAs = username => dispatch => {
         value: {
           userName: user,
           i18n,
-          panel,
           token,
           error
         }
       });
+      dispatch({
+        type: REFRESH_PANEL,
+        value: {
+          panel
+        }
+      });
       dispatch({
         type: REFRESH_COUNTERS,
         value: {
@@ -121,12 +137,17 @@ export const logout = () => (dispatch, getState) => {
           value: {
             userName: '',
             token: '',
-            panel: {},
             session: {},
             i18n: [],
             error,
           },
         });
+        dispatch({
+          type: REFRESH_PANEL,
+          value: {
+            panel: {}
+          }
+        });
         dispatch({
           type: REFRESH_COUNTERS,
           value: {
@@ -144,12 +165,17 @@ export const logout = () => (dispatch, getState) => {
           type: LOGGED_OUT_AS,
           value: {
             userName,
-            panel,
             token: '',
             i18n,
             error,
           },
         });
+        dispatch({
+          type: REFRESH_PANEL,
+          value: {
+            panel
+          }
+        });
         dispatch({
           type: REFRESH_COUNTERS,
           value: {
@@ -185,11 +211,16 @@ export const checkAuthHandler = () => (dispatch, getState) => {
           value: {
             userName: user,
             i18n,
-            panel,
             token,
             error
           }
         });
+        dispatch({
+          type: REFRESH_PANEL,
+          value: {
+            panel
+          }
+        });
         dispatch({
           type: REFRESH_COUNTERS,
           value: {

+ 0 - 1
src/react/src/components/Backup/RestoreSettings/BackupRestoreSettings.jsx

@@ -18,7 +18,6 @@ import './BackupRestoreSettings.scss';
 
 export default function BackupRestoreSettings(props) {
   const { i18n } = useSelector(state => state.session);
-  const token = localStorage.getItem("token");
   const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
   const { focusedElement } = useSelector(state => state.mainNavigation);
   const dispatch = useDispatch();

+ 3 - 2
src/react/src/components/ControlPanel/AddItemLayout/AddItemLayout.scss

@@ -23,7 +23,7 @@ $errorColor: #BE5ABF;
   
     div.error,
     div.success {
-      width: max-content !important;
+      width: fit-content !important;
   
       span {
         font-weight: bold;
@@ -58,6 +58,7 @@ $errorColor: #BE5ABF;
       width: auto !important;
       padding: 10px 0;
       margin-left: 0;
+      margin-right: 10px;
     }
   }
 }
@@ -211,7 +212,7 @@ $errorColor: #BE5ABF;
     label.label-wrapper {
       display: flex;
       align-items: flex-end;
-      width: max-content;
+      width: fit-content;
 
       span {
         font-weight: normal;

+ 1 - 0
src/react/src/components/ControlPanel/AddItemLayout/Form/Password/Password.jsx

@@ -30,6 +30,7 @@ const Password = ({ defaultValue, onChange = () => { }, id, name, title, showGen
     }
 
     setState({ ...state, generatedPassword: result });
+    onChange(result);
   }
 
   const passwordInputHandler = value => {

+ 1 - 1
src/react/src/components/Firewall/Add/AddFirewall.scss

@@ -1,6 +1,6 @@
 .content .edit-template.add-firewall {
   .toolbar .search-toolbar-name {
-    width: max-content;
+    width: fit-content;
   }
 
   label.label-wrapper[for=ip] span {

+ 1 - 1
src/react/src/components/InternetProtocol/InternetProtocol.jsx

@@ -50,7 +50,7 @@ const InternetProtocol = props => {
           </Container>
           <Container className="c-3 w-35">
             <div>{i18n.Owner}: <span className="stat">{data.OWNER}</span></div>
-            <div>{i18n.Users}: <span className="stat">{data.U_SYS_USERS.replaceAll(',', ', ')}</span></div>
+            <div>{i18n.Users}: <span className="stat">{data.U_SYS_USERS.replace(/,/g, ', ')}</span></div>
           </Container>
         </div>
       </Container>

+ 8 - 4
src/react/src/components/MailAccount/Add/AddMailAccount.jsx

@@ -48,14 +48,18 @@ export default function AddMailAccount(props) {
     event.preventDefault();
     let newMailDomain = {};
 
-    for (var [name, value] of (new FormData(event.target)).entries()) {
+    for (var [name, value] of (new FormData(event.target)).entries()) {      
       newMailDomain[name] = value;
     }
 
     newMailDomain['ok_acc'] = 'add';
     newMailDomain['token'] = token;
     newMailDomain['v_domain'] = props.domain;
-    newMailDomain['Password'] = newMailDomain['v_password'];
+    newMailDomain['v_password'] = state.password;
+
+    if (!newMailDomain['v_quota']) newMailDomain['v_quota'] = '';
+    if (!newMailDomain['v_aliases']) newMailDomain['v_aliases'] = '';
+    if (!newMailDomain['v_fwd']) newMailDomain['v_fwd'] = '';
 
     if (Object.keys(newMailDomain).length !== 0 && newMailDomain.constructor === Object) {
       setState({ ...state, loading: true });
@@ -195,8 +199,8 @@ export default function AddMailAccount(props) {
 
               <TextInput
                 title={i18n['Send login credentials to email address']}
-                name="v_credentials"
-                id="credentials" />
+                name="v_send_email"
+                id="send_email" />
             </div>
 
             <div className="buttons-wrapper">

+ 1 - 1
src/react/src/components/MailAccount/Add/AddMailAccount.scss

@@ -1,6 +1,6 @@
 .content .edit-template.add-mail-account {
   .search-toolbar-name {
-    width: max-content;
+    width: fit-content;
   }
 
   form {

+ 3 - 1
src/react/src/components/MailAccount/Edit/EditMailAccount.jsx

@@ -63,10 +63,12 @@ export default function EditMailAccount(props) {
             if (error_msg) {
               setErrorMessage(error_msg);
               setOkMessage('');
+              setState({ ...state, loading: false });
             } else {
               dispatch(refreshCounters()).then(() => {
                 setErrorMessage('');
                 setOkMessage(ok_msg);
+                setState({ ...state, loading: false });
               });
             }
           }
@@ -200,7 +202,7 @@ export default function EditMailAccount(props) {
             </div>
 
             <div className="buttons-wrapper">
-              <button type="submit" className="add">{i18n.Add}</button>
+              <button type="submit" className="add">{i18n.Save}</button>
               <button type="button" className="back" onClick={goBack}>{i18n.Back}</button>
             </div>
           </form>

+ 19 - 9
src/react/src/components/MailAccount/MailInfoBlock/MailInfoBlock.jsx

@@ -46,6 +46,24 @@ export default function MailInfoBlock({ webMail, hostName, domain, userName = ''
     );
   }
 
+  const getCredentials = () => {
+    let result = '';
+
+    result += `${i18n['Username']}:${userName}@${domain}\n`;
+    result += `${i18n['Password']}:${password}\n`;
+    result += `${i18n['IMAP hostname']}:${state.imapHostName}\n`;
+    result += `${i18n['IMAP port']}:${state.imapPort}\n`;
+    result += `${i18n['IMAP security']}:${state.imapEncryption}\n`;
+    result += `${i18n['IMAP auth method']}:${i18n['Normal password']}\n`;
+    result += `${i18n['SMTP hostname']}:${state.smtpHostName}\n`;
+    result += `${i18n['SMTP port']}:${state.smtpPort}\n`;
+    result += `${i18n['SMTP security']}:${state.smtpEncryption}\n`;
+    result += `${i18n['SMTP auth method']}:${i18n['Normal password']}\n`;
+    result += `${i18n['Webmail URL']}:${`http://${window.location.hostname}${webMail}`}\n`;
+
+    return result;
+  }
+
   return (
     <div className="mail-info-block">
       <div class="form-group select-group">
@@ -109,15 +127,7 @@ export default function MailInfoBlock({ webMail, hostName, domain, userName = ''
             <span><Link to={{ pathname: `http://${window.location.hostname}${webMail}` }} target="_blank">{webMail}</Link></span>
           </div>
 
-          <input type="hidden" name={i18n['Username']} value={`@${domain}`} />
-          <input type="hidden" name={i18n['IMAP hostname']} value={state.imapHostName} />
-          <input type="hidden" name={i18n['SMTP hostname']} value={state.smtpHostName} />
-          <input type="hidden" name={i18n['IMAP port']} value={state.imapPort} />
-          <input type="hidden" name={i18n['SMTP port']} value={state.smtpPort} />
-          <input type="hidden" name={i18n['IMAP security']} value={state.imapEncryption} />
-          <input type="hidden" name={i18n['SMTP security']} value={state.smtpEncryption} />
-          <input type="hidden" name={i18n['IMAP auth method']} value={i18n['Normal password']} />
-          <input type="hidden" name={i18n['SMTP auth method']} value={i18n['Normal password']} />
+          <input type="hidden" name="v_credentials" value={getCredentials()} />
         </div>
       </div>
     </div>

+ 1 - 1
src/react/src/components/MainNav/Mobile/MobileTopNav.scss

@@ -45,7 +45,7 @@
       justify-content: center;
       align-items: center;
       padding: 0 10px !important;
-      width: max-content;
+      width: fit-content;
       height: 100%;
       text-decoration: none;
       color: $black;

+ 5 - 4
src/react/src/components/MainNav/Panel/Notifications/Notifications.jsx

@@ -14,7 +14,8 @@ const Notifications = () => {
   const [loading, setLoading] = useState(false);
 
   useEffect(() => {
-    if (!notifications) {
+    if (!notifications.length) {
+      console.log(notifications);
       fetchData();
     }
   }, [notifications]);
@@ -47,7 +48,7 @@ const Notifications = () => {
   }
 
   const renderOptions = () => {
-    if (notifications && notifications.length) {
+    if (notifications.length) {
       return notifications.map(item => {
         return (
           <>
@@ -74,7 +75,7 @@ const Notifications = () => {
       <button type="button" className="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
         <div className="bell">
           {
-            notifications && notifications.length
+            notifications.length
               ? <BellUnread />
               : <Bell />
           }
@@ -87,4 +88,4 @@ const Notifications = () => {
   );
 };
 
-export default Notifications;
+export default Notifications;

+ 38 - 23
src/react/src/components/MainNav/Panel/Panel.jsx

@@ -9,7 +9,8 @@ import { Link } from "react-router-dom";
 import './Panel.scss';
 
 const Panel = props => {
-  const { i18n, userName, panel } = useSelector(state => state.session);
+  const { i18n, userName } = useSelector(state => state.session);
+  const { panel } = useSelector(state => state.panel);
   const { session } = useSelector(state => state.userSession);
   const { activeElement, focusedElement } = useSelector(state => state.mainNavigation);
   const dispatch = useDispatch();
@@ -63,6 +64,40 @@ const Panel = props => {
         });
   }
 
+  const renderNotifications = () => {
+    if (panel[userName]) {
+      if (panel[userName]['NOTIFICATIONS'] === 'yes') {
+        return <Notifications />;
+      }
+    }
+  }
+
+  const renderSmallNavigation = () => {
+    if (document.documentElement.clientWidth < 900) {
+      return (<div className="top-panel small-device">
+        <div className="container left-menu">
+          <div className="logo">
+            <Link to="/list/user/" onClick={() => dispatch(addActiveElement('/list/user/'))}>
+              <div>
+                <img src="/images/white_logo.png" alt="Logo" />
+              </div>
+            </Link>
+          </div>
+        </div>
+        <div className="container hamburger" onClick={toggleNavigation}>
+          <span className="bar"></span>
+          <span className="bar"></span>
+          <span className="bar"></span>
+        </div>
+        <div className="container profile-menu">
+          {renderNotifications()}
+          <div><Link to={`/edit/user?user=${userName}`}>{userName}</Link></div>
+          <div><button onClick={signOut}>{i18n['Log out']}</button></div>
+        </div>
+      </div>);
+    }
+  }
+
   return (
     <div className="panel-wrapper">
       {loading && <Spinner />}
@@ -113,7 +148,7 @@ const Panel = props => {
           )}
         </div>
         <div className="container profile-menu">
-          {panel[userName]['NOTIFICATIONS'] === 'yes' && <Notifications />}
+          {renderNotifications()}
           <div className="edit-user">
             <Link to={`/edit/user?user=${userName}`}>
               {session.look
@@ -130,27 +165,7 @@ const Panel = props => {
         </div>
       </div>
 
-      <div className="top-panel small-device">
-        <div className="container left-menu">
-          <div className="logo">
-            <Link to="/list/user/" onClick={() => dispatch(addActiveElement('/list/user/'))}>
-              <div>
-                <img src="/images/white_logo.png" alt="Logo" />
-              </div>
-            </Link>
-          </div>
-        </div>
-        <div className="container hamburger" onClick={toggleNavigation}>
-          <span className="bar"></span>
-          <span className="bar"></span>
-          <span className="bar"></span>
-        </div>
-        <div className="container profile-menu">
-          {panel[userName]['NOTIFICATIONS'] === 'yes' && <Notifications />}
-          <div><Link to={`/edit/user?user=${userName}`}>{userName}</Link></div>
-          <div><button onClick={signOut}>{i18n['Log out']}</button></div>
-        </div>
-      </div>
+      {renderSmallNavigation()}
     </div>
   );
 }

+ 9 - 15
src/react/src/components/MainNav/Panel/Panel.scss

@@ -46,7 +46,7 @@
         justify-content: center;
         align-items: center;
         padding: 0 10px !important;
-        width: max-content;
+        width: fit-content;
         height: 100%;
         text-decoration: none;
         color: white;
@@ -91,7 +91,8 @@
     padding: 0;
     justify-content: space-between;
 
-    div {
+    div.top-link,
+    div.nav-link {
       flex: 1 1 auto;
       height: 100%;
       transform: translateX(-5px);
@@ -150,8 +151,8 @@
 
       svg {
         border-radius: 30px;
-        width: 100%;
-        height: 100%;
+        width: 40px;
+        height: 30px;
         padding: 3px;
 
         &:hover {
@@ -199,7 +200,7 @@
     justify-content: start;
 
     > div {
-      width: max-content;
+      width: fit-content;
       flex: unset;
       padding: 0 1rem;
     }
@@ -212,11 +213,10 @@
   }
 
   .profile-menu {
-    justify-content: space-between;
     align-items: center;
 
     > .edit-user {
-      width: max-content;
+      width: fit-content;
     }
 
     .long-username {
@@ -236,7 +236,7 @@
   }
 }
 
-@media screen and (min-width: $desktopMax) {
+@media screen and (max-width: 1350px) {
   .top-panel {
     padding: 0 10%;
   }
@@ -248,13 +248,7 @@
   }
 }
 
-@media screen and (max-width: 1200px) {
-  .top-panel {
-    padding: 0 10%;
-  }
-}
-
-@media (max-width: 1065px) {
+@media (max-width: 900px) {
   .top-panel {
     display: none;
   }

+ 17 - 3
src/react/src/components/MainNav/Stat-menu/Menu.jsx

@@ -4,6 +4,7 @@ import { useSelector, useDispatch } from "react-redux";
 import { Link } from "react-router-dom";
 
 import './Menu.scss';
+import Spinner from 'src/components/Spinner/Spinner';
 
 const className = height => {
   if (height === 35) {
@@ -27,7 +28,8 @@ const style = ({ menuHeight, mobile }) => {
 
 const Menu = props => {
   const { activeElement, focusedElement } = useSelector(state => state.mainNavigation);
-  const { i18n, panel, userName } = useSelector(state => state.session);
+  const { i18n, userName } = useSelector(state => state.session);
+  const { panel } = useSelector(state => state.panel);
   const { session } = useSelector(state => state.userSession);
   const { user } = useSelector(state => state.menuCounters);
   const dispatch = useDispatch();
@@ -50,6 +52,8 @@ const Menu = props => {
     return `stat ${activeName === activeElement && 'l-active'} ${activeName === focusedElement && 'focus'}`;
   }
 
+  if (!panel[userName]) return <Spinner />;
+
   return (
     <div className="menu-wrapper">
       <div className={className(props.menuHeight)} style={{ height: style(props) }}>
@@ -62,11 +66,21 @@ const Menu = props => {
                   ? (<>
                     <div>
                       <span>{i18n.Disk}:</span>
-                      <span><span className="value">{user.U_DISK} <span className="unit">{panel[session.look]['U_DISK_MEASURE']}</span></span></span>
+                      <span>
+                        <span className="value">
+                          {panel[session.look]['U_DISK']}
+                          <span className="unit">{panel[session.look]['U_DISK_MEASURE']}</span>
+                        </span>
+                      </span>
                     </div>
                     <div>
                       <span>{i18n.Bandwidth}:</span>
-                      <span><span className="value">{user.U_BANDWIDTH} <span className="unit">{panel[session.look]['U_BANDWIDTH_MEASURE']}</span></span></span>
+                      <span>
+                        <span className="value">
+                          {panel[session.look]['U_BANDWIDTH']}
+                          <span className="unit">{panel[session.look]['U_BANDWIDTH_MEASURE']}</span>
+                        </span>
+                      </span>
                     </div>
                   </>)
                   : (<>

+ 0 - 4
src/react/src/components/MainNav/Stat-menu/Menu.scss

@@ -99,10 +99,6 @@
       }
     }
 
-    .stat.last {
-      flex: 1;
-    }
-
     .l-active {
       border-bottom: 3px solid $primary;
 

+ 25 - 2
src/react/src/components/MainNav/Toolbar/Toolbar.scss

@@ -23,18 +23,23 @@
       flex-wrap: unset;
       width: 100%;
       align-items: center;
+      justify-content: flex-end;
     }
 
     a.button-extra,
     button.button-extra {
+      white-space: nowrap;
+      overflow: hidden;
+      text-overflow: ellipsis;
+      max-width: 100%;
+      justify-content: flex-start;
+      padding: 5px 10px;
       background: none;
       border: none;
-      padding: 0 0.75rem;
       text-decoration: none;
       color: rgb(129, 125, 125);
       font-size: 14px;
       display: flex;
-      justify-content: center;
       align-items: center;
       margin-right: 10px;
       border-radius: 3px;
@@ -152,11 +157,28 @@
   }
 }
 
+@media (max-width: 1250px) {
+  .toolbar {
+    .r-menu {
+      a.button-extra,
+      button.button-extra {
+        max-width: 30%;
+      }
+    }
+  }
+}
+
 @media (max-width: 900px) {
   .toolbar {
     .r-menu {
       overflow: scroll;
 
+      a.button-extra,
+      button.button-extra {
+        overflow: unset;
+        max-width: 100%;
+      }
+
       > div {
         margin-left: 3rem;
         position: relative;
@@ -164,6 +186,7 @@
         flex-wrap: unset;
         width: 100%;
         align-items: center;
+        justify-content: flex-start;
       }
 
       .select-wrapper + div {

+ 1 - 1
src/react/src/components/Package/Add/AddPackage.scss

@@ -3,7 +3,7 @@
     label.label-wrapper {
       display: flex;
       align-items: flex-end;
-      width: max-content;
+      width: fit-content;
 
       span {
         font-weight: normal;

+ 2 - 1
src/react/src/components/Package/Package.jsx

@@ -9,6 +9,7 @@ import { useSelector } from 'react-redux';
 const Package = props => {
   const { data } = props;
   const { i18n } = useSelector(state => state.session);
+  const { session } = useSelector(state => state.userSession);
 
   const printNameServers = servers => {
     let serversArray = servers.split(',');
@@ -50,7 +51,7 @@ const Package = props => {
         <div className="stats">
           <Container className="c-1 w-30">
             <div>{i18n['Web Template']}: <span><span className="stat">{data.WEB_TEMPLATE}</span></span></div>
-            <div>{i18n['Proxy Template']}: <span><span className="stat">{data.PROXY_TEMPLATE}</span></span></div>
+            {session.PROXY_SYSTEM && <div>{i18n['Proxy Template']}: <span><span className="stat">{data.PROXY_TEMPLATE}</span></span></div>}
             <div>{i18n['DNS Template']}: <span><span className="stat">{data.DNS_TEMPLATE}</span></span></div>
             <div>{i18n['SSH Access']}: <span><span className="stat">{data.SHELL}</span></span></div>
             <div>{i18n['Web Domains']}: <span><span className="stat">{data.WEB_DOMAINS}</span></span></div>

+ 1 - 1
src/react/src/components/Server/Edit/Bind9/Bind9.scss

@@ -1,7 +1,7 @@
 .content .edit-template.edit-bind9 {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
     }
   }
 

+ 1 - 1
src/react/src/components/Server/Edit/Dovecot/Dovecot.scss

@@ -1,7 +1,7 @@
 .content .edit-template.edit-bind9 {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
     }
   }
 

+ 3 - 2
src/react/src/components/Server/Edit/EditDatabaseOption.jsx

@@ -6,6 +6,7 @@ import { Link } from 'react-router-dom';
 import { useSelector } from 'react-redux';
 
 const EditDatabaseOption = ({ data, visible }) => {
+  const { DB_PMA_URL, DB_PGA_URL } = useSelector(state => state.userSession.session);
   const { i18n } = useSelector(state => state.session);
 
   const printPhpMyAdminHosts = () => {
@@ -85,7 +86,7 @@ const EditDatabaseOption = ({ data, visible }) => {
           <>
             <TextInput
               title={i18n['phpMyAdmin URL']}
-              value={data.mail_url}
+              value={DB_PMA_URL}
               name="v_mysql_url"
               id="mysql_url" />
 
@@ -108,7 +109,7 @@ const EditDatabaseOption = ({ data, visible }) => {
           <>
             <TextInput
               title={i18n['phpPgAdmin URL']}
-              value={data.pgsql_url}
+              value={DB_PGA_URL}
               name="v_pgsql_url"
               id="pgsql_url" />
 

+ 2 - 1
src/react/src/components/Server/Edit/EditMailOption.jsx

@@ -7,6 +7,7 @@ import { Link } from 'react-router-dom';
 import { useSelector } from 'react-redux';
 
 const EditMailOption = ({ data, visible }) => {
+  const { MAIL_URL } = useSelector(state => state.userSession.session);
   const { i18n } = useSelector(state => state.session);
   const [mailCertificateSystem, setMailCertificateSystem] = useState(false);
 
@@ -58,7 +59,7 @@ const EditMailOption = ({ data, visible }) => {
       <TextInput
         title={i18n['Webmail URL']}
         name="v_mail_url"
-        value={data.mail_url}
+        value={MAIL_URL}
         id="mail-url" />
 
       <br /><br />

+ 0 - 6
src/react/src/components/Server/Edit/EditServer.jsx

@@ -167,12 +167,6 @@ const EditServer = props => {
               name="v_language"
               id="language" />
 
-            {/* <TextInput
-              value={state.data.port}
-              title={i18n['Default Port'] ?? 'Default Port'}
-              name="port"
-              id="port" /> */}
-
             <div className="modules">
               <button type="button" onClick={() => toggleOption('webOption')}>
                 {i18n['WEB']}

+ 1 - 1
src/react/src/components/Server/Edit/EditServer.scss

@@ -111,7 +111,7 @@
           padding: 7px 15px;
           text-transform: capitalize;
           text-decoration: none;
-          width: max-content;
+          width: fit-content;
           margin-right: 10px;
 
           &:hover {

+ 2 - 2
src/react/src/components/Server/Edit/Httpd/EditHttpd.scss

@@ -4,7 +4,7 @@ $secondaryLight: #f8b014;
 .content .edit-template.edit-httpd {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
 
       a {
         color: $secondary;
@@ -17,7 +17,7 @@ $secondaryLight: #f8b014;
     }
 
     .link {
-      width: max-content;
+      width: fit-content;
       margin-left: 15px;
 
       a {

+ 1 - 1
src/react/src/components/Server/Edit/Mysql/Mysql.scss

@@ -1,7 +1,7 @@
 .content .edit-template.edit-mysql {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
     }
   }
 

+ 2 - 2
src/react/src/components/Server/Edit/Nginx/EditServerNginx.scss

@@ -3,11 +3,11 @@ $secondaryLight: #f8b014;
 .content .edit-template.edit-nginx {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
     }
 
     .link {
-      width: max-content;
+      width: fit-content;
       margin-left: 15px;
 
       a {

+ 2 - 2
src/react/src/components/Server/Edit/PHP/EditPhp.scss

@@ -5,13 +5,13 @@ $textColor: #555;
 .content .edit-template.edit-php {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
       color: $textColor;
     }
     
     .search-toolbar-name,
     .link {
-      width: max-content;
+      width: fit-content;
 
       a {
         text-decoration: none;

+ 1 - 1
src/react/src/components/Server/Edit/Postgresql/Postgresql.scss

@@ -1,7 +1,7 @@
 .content .edit-template.edit-pgsql {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
     }
   }
 

+ 1 - 1
src/react/src/components/Server/Edit/Service/Service.scss

@@ -1,7 +1,7 @@
 .content .edit-template.edit-service {
   .toolbar {
     .search-toolbar-name {
-      width: max-content;
+      width: fit-content;
     }
   }
 

+ 5 - 3
src/react/src/components/TopPanel/TopPanel.jsx

@@ -4,6 +4,7 @@ import { logout } from 'src/actions/Session/sessionActions';
 import { useDispatch, useSelector } from 'react-redux';
 import { Link, useHistory } from 'react-router-dom';
 import Spinner from '../Spinner/Spinner';
+import PerfectScrollbar from 'react-perfect-scrollbar';
 
 import './TopPanel.scss';
 
@@ -82,9 +83,10 @@ const TopPanel = ({ menuItems = [], extraMenuItems = [] }) => {
             </Link>
           </div>
 
-          {renderMenuItems()}
-
-          {renderExtraMenuItems()}
+          <PerfectScrollbar>
+            {renderMenuItems()}
+            {renderExtraMenuItems()}
+          </PerfectScrollbar>
         </div>
 
         <div className="container profile-menu">

+ 18 - 0
src/react/src/components/TopPanel/TopPanel.scss

@@ -16,6 +16,24 @@
   }
 
   .left-menu {
+    justify-content: unset;
+
+    .logo {
+      width: fit-content;
+      flex: unset;
+    }
+
+    .scrollbar-container {
+      &:hover {
+        background: none;
+      }
+
+      .nav-link {
+        flex: unset;
+        padding: 0 5px;
+      }
+    }
+
     a, button {
       padding: 0 !important;
       outline: none;

+ 2 - 2
src/react/src/components/User/User.scss

@@ -29,7 +29,7 @@
 
 span.stat.email{
   display: block;
-  width: max-content;
+  width: fit-content;
   text-overflow: ellipsis;
   white-space: nowrap;
   overflow: hidden;
@@ -208,7 +208,7 @@ span.stat.email{
 
   @media (max-width: 850px) {
     > div {
-      width: max-content;
+      width: fit-content;
 
       a, button {
         width: 100%;

+ 2 - 1
src/react/src/components/WebDomain/Add/AddWebDomain.jsx

@@ -20,7 +20,8 @@ import { refreshCounters } from 'src/actions/MenuCounters/menuCounterActions';
 import HtmlParser from 'react-html-parser';
 
 const AddWebDomain = props => {
-  const { i18n, panel, userName } = useSelector(state => state.session);
+  const { i18n, userName } = useSelector(state => state.session);
+  const { panel } = useSelector(state => state.panel);
   const { session } = useSelector(state => state.userSession);
   const dispatch = useDispatch();
   const token = localStorage.getItem("token");

+ 0 - 2
src/react/src/components/WebDomain/Add/SslSupport/SslSupport.jsx

@@ -1,6 +1,5 @@
 import React, { useState } from 'react';
 import { useSelector } from 'react-redux';
-import { Link } from 'react-router-dom';
 import TextArea from 'src/components/ControlPanel/AddItemLayout/Form/TextArea/TextArea';
 
 import './SslSupport.scss';
@@ -30,7 +29,6 @@ const SslSupport = props => {
         id="ssl-certificate"
         name="v_ssl_crt"
         title={i18n['SSL Certificate']}
-        value={props.sslCertificate}
         disabled={letsEncrypt}
         optionalTitle={<>/ <button type="button" onClick={() => props.setModalVisible(true)} className="generate-csr">{i18n['Generate CSR']}</button></>} />
 

+ 1 - 1
src/react/src/components/WebDomain/Edit/SslSupport/SslSupport.scss

@@ -13,7 +13,7 @@
         }
 
         &:nth-child(2) {
-          width: max-content;
+          width: fit-content;
         }
       }
     }

+ 1 - 1
src/react/src/components/WebDomain/WebDomain.jsx

@@ -72,7 +72,7 @@ export default function WebDomain(props) {
       <Container className="r-col w-85">
         <div className="name">
           <div>{data.NAME}</div>
-          <div><span className="dns-name-span">{data.ALIAS.replaceAll(',', ', ')}</span></div>
+          <div><span className="dns-name-span">{data.ALIAS.replace(/,/g, ', ')}</span></div>
         </div>
         <div>{data.IP}</div>
         <div className="stats">

+ 5 - 1
src/react/src/components/WebDomain/WebDomain.scss

@@ -19,12 +19,16 @@
 
   .name {
     display: flex;
-    align-items: center;
 
     > div:nth-child(1) {
       margin-right: 2rem;
     }
 
+    > div + div {
+      line-height: 10px;
+      margin-top: .75rem;
+    }
+
     .dns-name-span {
       font-style: italic;
       color: #858585;

+ 1 - 1
src/react/src/containers/App/App.js

@@ -106,7 +106,7 @@ const App = () => {
                 exact
                 component={Preview} />
               <AuthenticatedRoute
-                path="/list/server/:service"
+                path="/list/server/service/"
                 authenticated={session.userName}
                 component={ServiceInfo} />
               <AuthenticatedRoute

+ 1 - 1
src/react/src/containers/MailAccounts/MailAccounts.jsx

@@ -380,7 +380,7 @@ export default function MailAccounts(props) {
         <LeftButton name={i18n['Add Mail Account']} href={`/add/mail/?domain=${props.domain}`} showLeftMenu={true} />
         <div className="r-menu">
           <div className="input-group input-group-sm">
-            <a href={state.webMail} className="button-extra" type="submit">{i18n['open webmail']}</a>
+            {state.webMail && <a href={state.webMail} className="button-extra" type="submit">{i18n['open webmail']}</a>}
             <Checkbox toggleAll={toggleAll} toggled={state.toggledAll} />
             <Select list='mailList' bulkAction={bulk} />
             <DropdownFilter changeSorting={changeSorting} sorting={state.sorting} order={state.order} list="mailAccountList" />

+ 1 - 1
src/react/src/containers/MailAccounts/MailAccounts.scss

@@ -17,7 +17,7 @@
     div.list-item {
       .r-col {
         .stat.email {
-            width: max-content;
+            width: fit-content;
             text-transform: none;
         }
       }

+ 1 - 1
src/react/src/containers/Mails/Mails.jsx

@@ -426,4 +426,4 @@ const Mails = props => {
   );
 }
 
-export default Mails;
+export default Mails;

+ 1 - 1
src/react/src/containers/Servers/Servers.jsx

@@ -319,7 +319,7 @@ const Servers = props => {
         <LeftButton href="/edit/server/" list="server" name={i18n.configure} showLeftMenu={true} />
         <div className="r-menu">
           <div className="input-group input-group-sm">
-            <Link to="/list/server/cpu" className="button-extra">{i18n['show: CPU / MEM / NET / DISK']}</Link>
+            <Link to="/list/server/service/?srv=cpu" className="button-extra">{i18n['show: CPU / MEM / NET / DISK']}</Link>
             <Checkbox toggleAll={toggleAll} toggled={state.toggledAll} />
             <Select list='serverList' bulkAction={bulk} />
             <SearchInput handleSearchTerm={term => props.changeSearchTerm(term)} />

+ 38 - 17
src/react/src/containers/ServiceInfo/index.jsx

@@ -2,7 +2,7 @@ import React, { useEffect, useState } from 'react';
 
 import { addActiveElement } from 'src/actions/MainNavigation/mainNavigationActions';
 import TopPanel from 'src/components/TopPanel/TopPanel';
-import { useParams, useHistory } from 'react-router-dom';
+import { useHistory } from 'react-router-dom';
 import { useDispatch, useSelector } from 'react-redux';
 
 import { getServiceLogs } from 'src/ControlPanelService/Server';
@@ -11,13 +11,12 @@ import { Helmet } from 'react-helmet';
 import ReactHtmlParser from 'react-html-parser';
 
 import './styles.scss';
+import QueryString from 'qs';
 
 const ServiceInfo = () => {
   const { i18n, userName } = useSelector(state => state.session);
   const dispatch = useDispatch();
-  const { activeElement } = useSelector(state => state.mainNavigation);
   const history = useHistory();
-  const { service } = useParams();
   const [state, setState] = useState({
     data: "",
     loading: false
@@ -30,14 +29,28 @@ const ServiceInfo = () => {
   }, [userName]);
 
   useEffect(() => {
-    fetchData();
-    dispatch(addActiveElement(`/list/server/${service}`));
-  }, [activeElement]);
+    let queryParams = QueryString.parse(history.location.search, { ignoreQueryPrefix: true });
 
-  const fetchData = () => {
+    if (!queryParams.srv) {
+      fetchData('cpu');
+      dispatch(addActiveElement('/list/server/service/?srv=cpu'));
+      return;
+    }
+
+    if (!menuItems.find(item => item.service === queryParams.srv)) {
+      dispatch(addActiveElement('/list/server/service/?srv=cpu'));
+      history.push('/list/server/service/?srv=cpu');
+      return;
+    }
+
+    fetchData(queryParams.srv);
+    dispatch(addActiveElement(`/list/server/service/?srv=${queryParams.srv}`));
+  }, [history.location.search]);
+
+  const fetchData = serviceName => {
     setState({ ...state, loading: true });
 
-    getServiceLogs(service)
+    getServiceLogs(serviceName)
       .then(result => {
         setState({ ...state, data: result.data.service_log, loading: false });
       })
@@ -49,35 +62,43 @@ const ServiceInfo = () => {
 
   const menuItems = [
     {
-      route: '/list/server/cpu',
+      route: '/list/server/service/?srv=cpu',
+      service: 'cpu',
       name: i18n['CPU']
     },
     {
-      route: '/list/server/mem',
+      route: '/list/server/service/?srv=mem',
+      service: 'mem',
       name: i18n['MEMORY']
     },
     {
-      route: '/list/server/disk',
+      route: '/list/server/service/?srv=disk',
+      service: 'disk',
       name: i18n['DISK']
     },
     {
-      route: '/list/server/net',
+      route: '/list/server/service/?srv=net',
+      service: 'net',
       name: i18n['NETWORK']
     },
     {
-      route: '/list/server/web',
+      route: '/list/server/service/?srv=web',
+      service: 'web',
       name: i18n['WEB']
     },
     {
-      route: '/list/server/dns',
+      route: '/list/server/service/?srv=dns',
+      service: 'dns',
       name: i18n['DNS']
     },
     {
-      route: '/list/server/mail',
+      route: '/list/server/service/?srv=mail',
+      service: 'mail',
       name: i18n['MAIL']
     },
     {
-      route: '/list/server/db',
+      route: '/list/server/service/?srv=db',
+      service: 'db',
       name: i18n['DB']
     }
   ];
@@ -93,7 +114,7 @@ const ServiceInfo = () => {
           state.loading
             ? <Spinner />
             : (<pre>
-              {state.data.length && state.data.map(line => (<>{ReactHtmlParser(line)}<br /></>))}
+              {state.data && ReactHtmlParser(state.data)}
             </pre>)
         }
       </div>

+ 30 - 1
src/react/src/containers/ServiceInfo/styles.scss

@@ -1,7 +1,36 @@
-.service-info {
+.App .service-info {
+  @media screen and (max-width: 1066px) {
+    .top-panel {
+      display: flex;
+    }
+  }
+
+  @media screen and (min-width: 1200px) {
+    .top-panel {
+      padding: 0 13%;
+    }
+  }
+
   .content {
     font-size: 14px;
     color: #555;
     padding-top: 4rem;
+
+    @media screen and (min-width: 1067px) {
+      padding-top: 5rem !important;
+      margin-top: 0px !important;
+    }
+
+    @media screen and (max-width: 1066px) {
+      padding-top: 5rem !important;
+      margin-top: 0px !important;
+    }
+
+    table {
+      td,th {
+        padding: 5px 10px;
+        border: 1px solid black;
+      }
+    }
   }
 }

+ 8 - 0
src/react/src/containers/Web/Web.jsx

@@ -17,12 +17,16 @@ import { useDispatch, useSelector } from 'react-redux';
 import { Helmet } from 'react-helmet';
 import './Web.scss';
 import { refreshCounters } from 'src/actions/MenuCounters/menuCounterActions';
+import { useHistory } from 'react-router-dom';
 
 const Web = props => {
   const { i18n } = useSelector(state => state.session);
   const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
   const { focusedElement } = useSelector(state => state.mainNavigation);
+  const { panel } = useSelector(state => state.panel);
+  const { userName } = useSelector(state => state.session);
   const dispatch = useDispatch();
+  const history = useHistory();
   const [loading, setLoading] = useState(false);
   const [modal, setModal] = useState({
     text: '',
@@ -40,6 +44,10 @@ const Web = props => {
   });
 
   useEffect(() => {
+    if (panel[userName]['WEB_DOMAINS'] === '0') {
+      return history.push('/');
+    }
+
     dispatch(addActiveElement('/list/web/'));
     dispatch(removeFocusedElement());
     dispatch(removeControlPanelContentFocusedElement());

+ 1 - 1
src/react/src/containers/WebLogs/WebLogs.jsx

@@ -94,7 +94,7 @@ export default function WebLogs() {
       </Helmet>
       <TopPanel menuItems={menuItems} extraMenuItems={extraMenuItems} />
       <div className="content">
-        <h6>{state.prefix}</h6>
+        <h6><b>{state.prefix}</b></h6>
         <br />
         {
           state.loading

+ 24 - 6
src/react/src/containers/WebLogs/WebLogs.scss

@@ -1,12 +1,30 @@
-.web-logs {
+.App .web-logs {
+  .top-panel {
+    .left-menu {
+      .logo {
+        justify-content: start;
+        margin: 0;
+        
+        a {
+          padding: 0 !important;
+        }
+      }
+    }
+  }
+
   .content {
     font-size: 14px;
     color: #555;
     padding-top: 4rem;
-  }
 
-  .nav-link:nth-child(3),
-  .nav-link:nth-child(4) {
-    width: 9rem;
+    @media screen and (min-width: 1067px) {
+      padding-top: 5rem !important;
+      margin-top: 0px !important;
+    }
+
+    @media screen and (max-width: 1066px) {
+      padding-top: 5rem !important;
+      margin-top: 0px !important;
+    }
   }
-}
+}

+ 1 - 1
src/react/src/reducers/Notification/notificationReducer.js

@@ -1,7 +1,7 @@
 import { ADD_NOTIFICATIONS, REMOVE_NOTIFICATIONS } from 'src/actions/Notification/notificationTypes';
 
 const INITIAL_STATE = {
-  notifications: null
+  notifications: []
 };
 
 const notificationReducer = (state = INITIAL_STATE, action) => {

+ 19 - 0
src/react/src/reducers/Panel/panel.js

@@ -0,0 +1,19 @@
+import { REFRESH_PANEL } from '../../actions/Panel/panelTypes';
+
+const INITIAL_STATE = {
+  panel: {}
+};
+
+const panelReducer = (state = INITIAL_STATE, action) => {
+  switch (action.type) {
+    case REFRESH_PANEL:
+      return {
+        ...state,
+        panel: action.value.panel
+      };
+
+    default: return state;
+  }
+};
+
+export default panelReducer;

+ 1 - 6
src/react/src/reducers/Session/sessionReducer.js

@@ -4,8 +4,7 @@ const INITIAL_STATE = {
   token: '',
   error: '',
   i18n: {},
-  userName: '',
-  panel: {}
+  userName: ''
 };
 
 const sessionReducer = (state = INITIAL_STATE, action) => {
@@ -16,7 +15,6 @@ const sessionReducer = (state = INITIAL_STATE, action) => {
         token: action.value.token,
         userName: action.value.userName,
         i18n: action.value.i18n || {},
-        panel: action.value.panel,
         error: action.value.error
       };
 
@@ -26,7 +24,6 @@ const sessionReducer = (state = INITIAL_STATE, action) => {
         token: action.value.token,
         userName: action.value.userName,
         i18n: action.value.i18n || {},
-        panel: action.value.panel,
         error: action.value.error
       };
 
@@ -36,7 +33,6 @@ const sessionReducer = (state = INITIAL_STATE, action) => {
         token: action.value.token,
         userName: action.value.userName,
         i18n: action.value.i18n || {},
-        panel: action.value.panel,
         error: action.value.error
       };
 
@@ -45,7 +41,6 @@ const sessionReducer = (state = INITIAL_STATE, action) => {
       token: action.value.token,
       userName: action.value.userName,
       i18n: action.value.i18n || {},
-      panel: action.value.panel,
       error: action.value.error
     };
 

+ 2 - 0
src/react/src/reducers/rootReducer.js

@@ -5,6 +5,7 @@ import notificationReducer from './Notification/notificationReducer';
 import menuCounterReducer from './MenuCounters/menuCounterReducer';
 import userSessionReducer from './UserSession/userSessionReducer';
 import sessionReducer from './Session/sessionReducer';
+import panelReducer from './Panel/panel';
 
 export default combineReducers({
   mainNavigation: mainNavigationReducer,
@@ -13,4 +14,5 @@ export default combineReducers({
   menuCounters: menuCounterReducer,
   userSession: userSessionReducer,
   session: sessionReducer,
+  panel: panelReducer,
 });