Просмотр исходного кода

Merge pull request #2070 from serghey-rodin/release/react-integration

React integration ready for v1.0.0 release.
Serghey Rodin 4 лет назад
Родитель
Сommit
c520eb283a
100 измененных файлов с 1229 добавлено и 773 удалено
  1. 1 3
      web/js/react/public/index.html
  2. 16 13
      web/js/react/src/ControlPanelService/Backup.js
  3. 14 12
      web/js/react/src/ControlPanelService/Cron.js
  4. 15 11
      web/js/react/src/ControlPanelService/Db.js
  5. 16 11
      web/js/react/src/ControlPanelService/Dns.js
  6. 2 2
      web/js/react/src/ControlPanelService/Favorites.js
  7. 18 14
      web/js/react/src/ControlPanelService/Firewalls.js
  8. 139 145
      web/js/react/src/ControlPanelService/GeneratorOptions.js
  9. 15 12
      web/js/react/src/ControlPanelService/Ip.js
  10. 1 1
      web/js/react/src/ControlPanelService/Languages.js
  11. 1 1
      web/js/react/src/ControlPanelService/Logs.js
  12. 17 14
      web/js/react/src/ControlPanelService/Mail.js
  13. 10 4
      web/js/react/src/ControlPanelService/Notifications.js
  14. 14 11
      web/js/react/src/ControlPanelService/Package.js
  15. 3 3
      web/js/react/src/ControlPanelService/RRD.js
  16. 1 1
      web/js/react/src/ControlPanelService/ResetPassword.js
  17. 7 2
      web/js/react/src/ControlPanelService/Search.js
  18. 15 11
      web/js/react/src/ControlPanelService/Server.js
  19. 1 1
      web/js/react/src/ControlPanelService/Statistics.js
  20. 19 7
      web/js/react/src/ControlPanelService/Updates.js
  21. 1 1
      web/js/react/src/ControlPanelService/UserNS.js
  22. 21 13
      web/js/react/src/ControlPanelService/Users.js
  23. 15 11
      web/js/react/src/ControlPanelService/Web.js
  24. 1 1
      web/js/react/src/ControlPanelService/WebLogs.js
  25. 16 4
      web/js/react/src/FileManagerHelper.js
  26. 15 0
      web/js/react/src/actions/Notification/notificationActions.js
  27. 2 0
      web/js/react/src/actions/Notification/notificationTypes.js
  28. 35 31
      web/js/react/src/actions/Session/sessionActions.js
  29. 1 0
      web/js/react/src/actions/Session/sessionTypes.js
  30. 3 2
      web/js/react/src/components/Backup/Backup.jsx
  31. 2 2
      web/js/react/src/components/Backup/Exclusion/Edit/index.jsx
  32. 4 1
      web/js/react/src/components/Backup/Exclusion/index.jsx
  33. 2 1
      web/js/react/src/components/Backup/RestoreSetting/RestoreSetting.jsx
  34. 21 4
      web/js/react/src/components/Backup/RestoreSetting/RestoreSetting.scss
  35. 4 2
      web/js/react/src/components/Backup/RestoreSettings/BackupRestoreSettings.jsx
  36. 33 21
      web/js/react/src/components/ControlPanel/AddItemLayout/AddItemLayout.scss
  37. 3 9
      web/js/react/src/components/ControlPanel/AddItemLayout/Form/Checkbox/Checkbox.jsx
  38. 2 1
      web/js/react/src/components/ControlPanel/AddItemLayout/Form/NameServers/NameServers.jsx
  39. 3 2
      web/js/react/src/components/ControlPanel/AddItemLayout/Form/Password/Password.jsx
  40. 2 1
      web/js/react/src/components/ControlPanel/AddItemLayout/Form/SelectInput/SelectInput.jsx
  41. 2 1
      web/js/react/src/components/ControlPanel/Hotkeys/Hotkeys.jsx
  42. 10 11
      web/js/react/src/components/ControlPanel/Hotkeys/Hotkeys.scss
  43. 37 44
      web/js/react/src/components/ControlPanel/ListItem/ListItem.jsx
  44. 18 4
      web/js/react/src/components/ControlPanel/ListItem/ListItem.scss
  45. 3 2
      web/js/react/src/components/ControlPanel/Modal/Modal.jsx
  46. 4 2
      web/js/react/src/components/ControlPanel/Modal/Modal.scss
  47. 2 2
      web/js/react/src/components/CronJob/Add/AddCronJob.jsx
  48. 4 3
      web/js/react/src/components/CronJob/CronJob.jsx
  49. 2 2
      web/js/react/src/components/CronJob/Edit/EditCronJob.jsx
  50. 2 1
      web/js/react/src/components/CronJob/Generator/Generator.jsx
  51. 20 9
      web/js/react/src/components/CronJob/Generator/Generator.scss
  52. 4 2
      web/js/react/src/components/CronJob/Generator/OtherSelects/FifthTabSelects.jsx
  53. 4 2
      web/js/react/src/components/CronJob/Generator/OtherSelects/FourthTabSelects.jsx
  54. 4 2
      web/js/react/src/components/CronJob/Generator/OtherSelects/SecondTabSelects.jsx
  55. 4 2
      web/js/react/src/components/CronJob/Generator/OtherSelects/ThirdTabSelects.jsx
  56. 5 2
      web/js/react/src/components/CronJob/Generator/RunCommandSelect/RunCommandSelect.jsx
  57. 2 2
      web/js/react/src/components/DNSRecord/Add/AddDNSRecord.jsx
  58. 3 2
      web/js/react/src/components/DNSRecord/DNSRecord.jsx
  59. 5 5
      web/js/react/src/components/DNSRecord/Edit/EditDNSRecord.jsx
  60. 2 2
      web/js/react/src/components/Database/Add/AddDatabase.jsx
  61. 4 3
      web/js/react/src/components/Database/Database.jsx
  62. 2 2
      web/js/react/src/components/Database/Edit/EditDatabase.jsx
  63. 2 2
      web/js/react/src/components/DomainNameSystem/Add/AddDomainNameSystem.jsx
  64. 2 1
      web/js/react/src/components/DomainNameSystem/Add/AdvancedOptions/AdvancedOptions.jsx
  65. 4 3
      web/js/react/src/components/DomainNameSystem/DomainNameSystem.jsx
  66. 2 2
      web/js/react/src/components/DomainNameSystem/Edit/EditDomainNameSystem.jsx
  67. 2 2
      web/js/react/src/components/Firewall/Add/AddFirewall.jsx
  68. 2 2
      web/js/react/src/components/Firewall/Add/Banlist/index.jsx
  69. 3 2
      web/js/react/src/components/Firewall/Ban/index.jsx
  70. 2 2
      web/js/react/src/components/Firewall/Edit/EditFirewall.jsx
  71. 4 3
      web/js/react/src/components/Firewall/Firewall.jsx
  72. 3 3
      web/js/react/src/components/ForgotPassword/index.jsx
  73. 117 42
      web/js/react/src/components/Hotkeys/Hotkeys.jsx
  74. 54 48
      web/js/react/src/components/Hotkeys/Hotkeys.scss
  75. 1 1
      web/js/react/src/components/InternetProtocol/Add/AddInternetProtocol.jsx
  76. 2 2
      web/js/react/src/components/InternetProtocol/Edit/EditInternetProtocol.jsx
  77. 3 2
      web/js/react/src/components/InternetProtocol/InternetProtocol.jsx
  78. 1 1
      web/js/react/src/components/Lists/DirectoryList/DirectoryList.jsx
  79. 9 3
      web/js/react/src/components/Lists/Row/Row.jsx
  80. 13 2
      web/js/react/src/components/Lists/Row/Row.scss
  81. 28 7
      web/js/react/src/components/Login/Login.scss
  82. 8 8
      web/js/react/src/components/Login/LoginForm.jsx
  83. 2 2
      web/js/react/src/components/Mail/Add/AddMail.jsx
  84. 2 2
      web/js/react/src/components/Mail/Edit/EditMail.jsx
  85. 4 3
      web/js/react/src/components/Mail/Mail.jsx
  86. 2 2
      web/js/react/src/components/MailAccount/Add/AddMailAccount.jsx
  87. 3 3
      web/js/react/src/components/MailAccount/Edit/EditMailAccount.jsx
  88. 4 3
      web/js/react/src/components/MailAccount/MailAccount.jsx
  89. 3 2
      web/js/react/src/components/MailAccount/MailInfoBlock/MailInfoBlock.jsx
  90. 34 11
      web/js/react/src/components/MainNav/MainNav.jsx
  91. 3 1
      web/js/react/src/components/MainNav/Mobile/MobileTopNav.scss
  92. 9 0
      web/js/react/src/components/MainNav/Panel/Notifications/Bell.jsx
  93. 9 0
      web/js/react/src/components/MainNav/Panel/Notifications/BellUnread.jsx
  94. 28 17
      web/js/react/src/components/MainNav/Panel/Notifications/Notifications.jsx
  95. 24 7
      web/js/react/src/components/MainNav/Panel/Notifications/Notifications.scss
  96. 52 30
      web/js/react/src/components/MainNav/Panel/Panel.jsx
  97. 64 25
      web/js/react/src/components/MainNav/Panel/Panel.scss
  98. 46 24
      web/js/react/src/components/MainNav/Stat-menu/Menu.jsx
  99. 20 9
      web/js/react/src/components/MainNav/Stat-menu/Menu.scss
  100. 4 1
      web/js/react/src/components/MainNav/Toolbar/Checkbox/Checkbox.jsx

+ 1 - 3
web/js/react/public/index.html

@@ -3,12 +3,10 @@
 
 <head>
   <meta charset="utf-8" />
-  <link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
+  <link rel="icon" href="/images/favicon.ico">
   <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
   <meta name="theme-color" content="#000000" />
   <link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
-  <link rel="stylesheet" href="/node_modules/bootstrap/dist/css/bootstrap.css">
-  <link rel="stylesheet" href="/node_modules/bootstrap/dist/js/bootstrap.min.js">
   <title>Vesta</title>
 </head>
 

+ 16 - 13
web/js/react/src/ControlPanelService/Backup.js

@@ -1,14 +1,14 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const token = localStorage.getItem("token");
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/backup/backup.php';
+const webApiUri = '/api/v1/list/backup/index.php';
 const scheduleBackupUri = '/schedule/backup/';
-const backupDetailsUri = '/list/backup/backup.php';
-const backupExclusionsUri = '/api/list/backup/exclusions/index.php';
-const backupExclusionsInfoUri = '/api/edit/backup/exclusions/index.php';
-const backupRestoreSettingUri = '/api/schedule/restore/index.php';
-const bulkRestoreUri = '/api/bulk/restore/index.php';
+const backupDetailsUri = '/api/v1/list/backup/index.php';
+const backupExclusionsUri = '/api/v1/list/backup/exclusions/index.php';
+const backupExclusionsInfoUri = '/api/v1/edit/backup/exclusions/index.php';
+const backupRestoreSettingUri = '/api/v1/schedule/restore/index.php';
+const bulkRestoreUri = '/api/v1/bulk/restore/index.php';
 
 export const getBackupList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -17,18 +17,21 @@ export const getBackupList = () => {
 export const bulkAction = (action, backups) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   backups.forEach(backup => {
     formData.append("backup[]", backup);
-    formData.append("delete_url", `/delete/backup/?backup=${backup}&token=${token}`);
   });
 
-  return axios.post(BASE_URL + '/bulk/backup/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/backup/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const scheduleBackup = () => {
@@ -45,7 +48,7 @@ export const restoreBackupSetting = params => {
 
 export const bulkRestore = (action, selection, backup) => {
   const formData = new FormData();
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
   formData.append("action", action);
   formData.append("backup", backup);
 
@@ -73,7 +76,7 @@ export const updateBackupExclusions = data => {
 
   return axios.post(BASE_URL + backupExclusionsInfoUri, formDataObject, {
     params: {
-      token
+      token: getAuthToken()
     }
   });
 }

+ 14 - 12
web/js/react/src/ControlPanelService/Cron.js

@@ -1,11 +1,11 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const token = localStorage.getItem("token");
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/cron/cron.php';
-const cronAddApiUri = '/api/add/cron/index.php';
-const jobInfoUri = '/api/edit/cron/index.php';
-const updateCronJobUri = '/api/edit/cron/index.php';
+const webApiUri = '/api/v1/list/cron/index.php';
+const cronAddApiUri = '/api/v1/add/cron/index.php';
+const jobInfoUri = '/api/v1/edit/cron/index.php';
+const updateCronJobUri = '/api/v1/edit/cron/index.php';
 
 export const getCronList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -14,19 +14,21 @@ export const getCronList = () => {
 export const bulkAction = (action, domainNameSystems) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   domainNameSystems.forEach(domainNameSystem => {
     formData.append("job[]", domainNameSystem);
-    formData.append("suspend_url", `/suspend/cron/?job=${domainNameSystem}&token=${token}`);
-    formData.append("delete_url", `/delete/cron/?job=${domainNameSystem}&token=${token}`);
   });
 
-  return axios.post(BASE_URL + '/bulk/cron/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/cron/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const addCronJob = data => {
@@ -43,7 +45,7 @@ export const getCronJobInfo = job => {
   return axios.get(BASE_URL + jobInfoUri, {
     params: {
       job,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -58,7 +60,7 @@ export const updateCronJob = (data, job) => {
   return axios.post(BASE_URL + updateCronJobUri, formDataObject, {
     params: {
       job,
-      token
+      token: getAuthToken()
     }
   });
 }

+ 15 - 11
web/js/react/src/ControlPanelService/Db.js

@@ -1,12 +1,12 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const token = localStorage.getItem("token");
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/db/db.php';
-const addDbApiUri = '/api/add/db/index.php';
-const optionalDbInfoUri = '/api/add/db/index.php';
-const dbInfoUri = '/api/edit/db/index.php';
-const updateDatabaseUri = '/api/edit/db/index.php';
+const webApiUri = '/api/v1/list/db/index.php';
+const addDbApiUri = '/api/v1/add/db/index.php';
+const optionalDbInfoUri = '/api/v1/add/db/index.php';
+const dbInfoUri = '/api/v1/edit/db/index.php';
+const updateDatabaseUri = '/api/v1/edit/db/index.php';
 
 export const getDatabaseList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -15,17 +15,21 @@ export const getDatabaseList = () => {
 export const bulkAction = (action, domainNameSystems) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   domainNameSystems.forEach(domainNameSystem => {
     formData.append("database[]", domainNameSystem);
   });
 
-  return axios.post(BASE_URL + '/bulk/db/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/db/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const getDbOptionalInfo = () => {
@@ -85,7 +89,7 @@ export const getDatabaseInfo = database => {
   return axios.get(BASE_URL + dbInfoUri, {
     params: {
       database,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -100,7 +104,7 @@ export const updateDatabase = (data, database) => {
   return axios.post(BASE_URL + updateDatabaseUri, formDataObject, {
     params: {
       database,
-      token
+      token: getAuthToken()
     }
   });
 }

+ 16 - 11
web/js/react/src/ControlPanelService/Dns.js

@@ -1,11 +1,11 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const updateDNSUri = '/api/edit/dns/index.php';
-const addDnsApiUri = '/api/add/dns/index.php';
-const dNSInfoUri = '/api/edit/dns/index.php';
-const token = localStorage.getItem("token");
+const updateDNSUri = '/api/v1/edit/dns/index.php';
+const addDnsApiUri = '/api/v1/add/dns/index.php';
+const dNSInfoUri = '/api/v1/edit/dns/index.php';
 const BASE_URL = window.location.origin;
-const dnsApiUri = '/list/dns/dns.php';
+const dnsApiUri = '/api/v1/list/dns/index.php';
 
 export const getDnsList = () => {
   return axios.get(BASE_URL + dnsApiUri);
@@ -22,17 +22,21 @@ export const getDNSRecordInfo = (domain, recordId) => {
 export const bulkAction = (action, domainNameSystems) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   domainNameSystems.forEach(domainNameSystem => {
     formData.append("domain[]", domainNameSystem);
   });
 
-  return axios.post(BASE_URL + '/bulk/dns/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/dns/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const addDomainNameSystem = data => {
@@ -59,12 +63,12 @@ export const getDNSInfo = domain => {
   return axios.get(BASE_URL + dNSInfoUri, {
     params: {
       domain,
-      token
+      token: getAuthToken()
     }
   });
 }
 
-export const updateDNS = (data, domain) => {
+export const updateDNS = (data, domain, recordId) => {
   let formDataObject = new FormData();
 
   for (let key in data) {
@@ -74,7 +78,8 @@ export const updateDNS = (data, domain) => {
   return axios.post(BASE_URL + updateDNSUri, formDataObject, {
     params: {
       domain,
-      token
+      record_id: recordId,
+      token: getAuthToken()
     }
   });
 }

+ 2 - 2
web/js/react/src/ControlPanelService/Favorites.js

@@ -1,6 +1,6 @@
 import axios from "axios";
-let addFavoriteUri = '/add/favorite/index.php';
-let deleteFavoriteUri = '/delete/favorite/index.php';
+let addFavoriteUri = '/api/v1/add/favorite/index.php';
+let deleteFavoriteUri = '/api/v1/delete/favorite/index.php';
 let BASE_URL = window.location.origin;
 
 

+ 18 - 14
web/js/react/src/ControlPanelService/Firewalls.js

@@ -1,13 +1,13 @@
 import axios from 'axios';
+import { getAuthToken } from 'src/utils/token';
 
 const BASE_URL = window.location.origin;
-const token = localStorage.getItem("token");
-const usersUri = '/list/firewall/firewall.php';
-const addFirewallUri = '/api/add/firewall/index.php';
-const firewallInfoUri = '/api/edit/firewall/index.php';
-const updateFirewallUri = '/api/edit/firewall/index.php';
-const addBanIpsUri = '/api/add/firewall/banlist/index.php';
-const banListUri = '/list/firewall/banlist/banlist.php';
+const usersUri = '/api/v1/list/firewall/index.php';
+const addFirewallUri = '/api/v1/add/firewall/index.php';
+const firewallInfoUri = '/api/v1/edit/firewall/index.php';
+const updateFirewallUri = '/api/v1/edit/firewall/index.php';
+const addBanIpsUri = '/api/v1/add/firewall/banlist/index.php';
+const banListUri = '/api/v1/list/firewall/banlist/index.php';
 
 export const getFirewallList = () => {
   return axios.get(BASE_URL + usersUri);
@@ -20,17 +20,21 @@ export const getBanList = () => {
 export const bulkAction = (action, firewalls) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   firewalls.forEach(firewall => {
     formData.append("rule[]", firewall);
   });
 
-  return axios.post(BASE_URL + '/bulk/firewall/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/firewall/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const getBanIps = data => {
@@ -42,7 +46,7 @@ export const getBanIps = data => {
 
   return axios.get(BASE_URL + addBanIpsUri, {
     params: {
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -56,7 +60,7 @@ export const addBanIp = (data) => {
 
   return axios.get(BASE_URL + addBanIpsUri, {
     params: {
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -75,7 +79,7 @@ export const getFirewallInfo = rule => {
   return axios.get(BASE_URL + firewallInfoUri, {
     params: {
       rule,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -90,7 +94,7 @@ export const updateFirewall = (data, rule) => {
   return axios.post(BASE_URL + updateFirewallUri, formDataObject, {
     params: {
       rule,
-      token
+      token: getAuthToken()
     }
   });
 }

+ 139 - 145
web/js/react/src/ControlPanelService/GeneratorOptions.js

@@ -1,145 +1,139 @@
-const { i18n } = window.GLOBAL.App;
-
-export const minutesRunCommandsOptions = [
-  { name: i18n['every minute'] ?? 'every minute', value: '*' },
-  { name: i18n['every two minutes'] ?? 'every two minutes', value: '*/2' },
-  { name: `${i18n.every || 'every'} 5`, value: '*/5' },
-  { name: `${i18n.every || 'every'} 10`, value: '*/10' },
-  { name: `${i18n.every || 'every'} 15`, value: '*/15' },
-  { name: `${i18n.every || 'every'} 30`, value: '*/30' }
-];
-
-export const hoursRunCommandsOptions = [
-  { name: i18n['every hour'] ?? 'every hour', value: '*' },
-  { name: i18n['every two hours'] ?? 'every two hours', value: '*/2' },
-  { name: `${i18n.every || 'every'} 6`, value: '*/6' },
-  { name: `${i18n.every || 'every'} 12`, value: '*/12' }
-];
-
-export const daysRunCommandsOptions = [
-  { name: i18n['every day'] ?? 'every day', value: '*' },
-  { name: i18n['every odd day'] ?? 'every odd day', value: '1-31/2' },
-  { name: i18n['every even day'] ?? 'every even day', value: '*/2' },
-  { name: `${i18n.every || 'every'} 3`, value: '*/3' },
-  { name: `${i18n.every || 'every'} 5`, value: '*/5' },
-  { name: `${i18n.every || 'every'} 10`, value: '*/10' },
-  { name: `${i18n.every || 'every'} 15`, value: '*/15' }
-];
-
-export const hoursOptions = [
-  { name: '00', value: '0' },
-  { name: '01', value: '1' },
-  { name: '02', value: '2' },
-  { name: '03', value: '3' },
-  { name: '04', value: '4' },
-  { name: '05', value: '5' },
-  { name: '06', value: '6' },
-  { name: '07', value: '7' },
-  { name: '08', value: '8' },
-  { name: '09', value: '9' },
-  { name: '10', value: '10' },
-  { name: '11', value: '11' },
-  { name: '12', value: '12' },
-  { name: '13', value: '13' },
-  { name: '14', value: '14' },
-  { name: '15', value: '15' },
-  { name: '16', value: '16' },
-  { name: '17', value: '17' },
-  { name: '18', value: '18' },
-  { name: '19', value: '19' },
-  { name: '20', value: '20' },
-  { name: '21', value: '21' },
-  { name: '22', value: '22' },
-  { name: '23', value: '23' }
-];
-
-export const hourlyMinutesOptions = [
-  { name: '00', value: '0' },
-  { name: '15', value: '15' },
-  { name: '30', value: '30' },
-  { name: '45', value: '45' }
-];
-
-export const dailyMinutesOptions = [
-  { name: '00', value: '0' },
-  { name: '01', value: '1' },
-  { name: '02', value: '2' },
-  { name: '05', value: '5' },
-  { name: '10', value: '10' },
-  { name: '15', value: '15' },
-  { name: '20', value: '20' },
-  { name: '25', value: '25' },
-  { name: '30', value: '30' },
-  { name: '35', value: '35' },
-  { name: '40', value: '40' },
-  { name: '45', value: '45' },
-  { name: '50', value: '50' },
-  { name: '55', value: '55' }
-];
-
-export const weeklyRunCommandOptions = [
-  { name: i18n['every day'] ?? 'every day', value: '*' },
-  { name: i18n['weekdays (5 days)'] ?? 'weekdays (5 days)', value: '1,2,3,4,5' },
-  { name: i18n['weekend (2 days)'] ?? 'weekend (2 days)', value: '0,6' },
-  { name: i18n.Monday ?? 'Monday', value: '1' },
-  { name: i18n.Tuesday ?? 'Tuesday', value: '2' },
-  { name: i18n.Wednesday ?? 'Wednesday', value: '3' },
-  { name: i18n.Thursday ?? 'Thursday', value: '4' },
-  { name: i18n.Friday ?? 'Friday', value: '5' },
-  { name: i18n.Saturday ?? 'Saturday', value: '6' },
-  { name: i18n.Sunday ?? 'Sunday', value: '0' }
-];
-
-export const monthlyRunCommandOptions = [
-  { name: i18n['every month'] ?? 'every month', value: '*' },
-  { name: i18n['every odd month'] ?? 'every odd month', value: '1-11/2' },
-  { name: i18n['every even month'] ?? 'every even month', value: '*/2' },
-  { name: `${i18n.every || 'every'} 3`, value: '*/3' },
-  { name: `${i18n.every || 'every'} 6`, value: '*/6' },
-  { name: i18n.Jan ?? 'Jan', value: '1' },
-  { name: i18n.Feb ?? 'Feb', value: '2' },
-  { name: i18n.Mar ?? 'Mar', value: '3' },
-  { name: i18n.Apr ?? 'Apr', value: '4' },
-  { name: i18n.May ?? 'May', value: '5' },
-  { name: i18n.Jun ?? 'Jun', value: '6' },
-  { name: i18n.Jul ?? 'Jul', value: '7' },
-  { name: i18n.Aug ?? 'Aug', value: '8' },
-  { name: i18n.Sep ?? 'Sep', value: '9' },
-  { name: i18n.Oct ?? 'Oct', value: '10' },
-  { name: i18n.Nov ?? 'Nov', value: '11' },
-  { name: i18n.Dec ?? 'Dec', value: '12' }
-];
-
-export const dateOptions = [
-  { name: '1', value: '1' },
-  { name: '2', value: '2' },
-  { name: '3', value: '3' },
-  { name: '4', value: '4' },
-  { name: '5', value: '5' },
-  { name: '6', value: '6' },
-  { name: '7', value: '7' },
-  { name: '8', value: '8' },
-  { name: '9', value: '9' },
-  { name: '10', value: '10' },
-  { name: '11', value: '11' },
-  { name: '12', value: '12' },
-  { name: '13', value: '13' },
-  { name: '14', value: '14' },
-  { name: '15', value: '15' },
-  { name: '16', value: '16' },
-  { name: '17', value: '17' },
-  { name: '18', value: '18' },
-  { name: '19', value: '19' },
-  { name: '20', value: '20' },
-  { name: '21', value: '21' },
-  { name: '22', value: '22' },
-  { name: '23', value: '23' },
-  { name: '24', value: '24' },
-  { name: '25', value: '25' },
-  { name: '26', value: '26' },
-  { name: '27', value: '27' },
-  { name: '28', value: '28' },
-  { name: '29', value: '29' },
-  { name: '30', value: '30' },
-  { name: '31', value: '31' }
-];
+export const generatorOptions = i18n => {
+  return {
+    minutesRunCommandsOptions: [
+      { name: i18n['every minute'] ?? 'every minute', value: '*' },
+      { name: i18n['every two minutes'] ?? 'every two minutes', value: '*/2' },
+      { name: `${i18n.every || 'every'} 5`, value: '*/5' },
+      { name: `${i18n.every || 'every'} 10`, value: '*/10' },
+      { name: `${i18n.every || 'every'} 15`, value: '*/15' },
+      { name: `${i18n.every || 'every'} 30`, value: '*/30' }
+    ],
+    hoursRunCommandsOptions: [
+      { name: i18n['every hour'] ?? 'every hour', value: '*' },
+      { name: i18n['every two hours'] ?? 'every two hours', value: '*/2' },
+      { name: `${i18n.every || 'every'} 6`, value: '*/6' },
+      { name: `${i18n.every || 'every'} 12`, value: '*/12' }
+    ],
+    daysRunCommandsOptions: [
+      { name: i18n['every day'] ?? 'every day', value: '*' },
+      { name: i18n['every odd day'] ?? 'every odd day', value: '1-31/2' },
+      { name: i18n['every even day'] ?? 'every even day', value: '*/2' },
+      { name: `${i18n.every || 'every'} 3`, value: '*/3' },
+      { name: `${i18n.every || 'every'} 5`, value: '*/5' },
+      { name: `${i18n.every || 'every'} 10`, value: '*/10' },
+      { name: `${i18n.every || 'every'} 15`, value: '*/15' }
+    ],
+    hoursOptions: [
+      { name: '00', value: '0' },
+      { name: '01', value: '1' },
+      { name: '02', value: '2' },
+      { name: '03', value: '3' },
+      { name: '04', value: '4' },
+      { name: '05', value: '5' },
+      { name: '06', value: '6' },
+      { name: '07', value: '7' },
+      { name: '08', value: '8' },
+      { name: '09', value: '9' },
+      { name: '10', value: '10' },
+      { name: '11', value: '11' },
+      { name: '12', value: '12' },
+      { name: '13', value: '13' },
+      { name: '14', value: '14' },
+      { name: '15', value: '15' },
+      { name: '16', value: '16' },
+      { name: '17', value: '17' },
+      { name: '18', value: '18' },
+      { name: '19', value: '19' },
+      { name: '20', value: '20' },
+      { name: '21', value: '21' },
+      { name: '22', value: '22' },
+      { name: '23', value: '23' }
+    ],
+    hourlyMinutesOptions: [
+      { name: '00', value: '0' },
+      { name: '15', value: '15' },
+      { name: '30', value: '30' },
+      { name: '45', value: '45' }
+    ],
+    dailyMinutesOptions: [
+      { name: '00', value: '0' },
+      { name: '01', value: '1' },
+      { name: '02', value: '2' },
+      { name: '05', value: '5' },
+      { name: '10', value: '10' },
+      { name: '15', value: '15' },
+      { name: '20', value: '20' },
+      { name: '25', value: '25' },
+      { name: '30', value: '30' },
+      { name: '35', value: '35' },
+      { name: '40', value: '40' },
+      { name: '45', value: '45' },
+      { name: '50', value: '50' },
+      { name: '55', value: '55' }
+    ],
+    weeklyRunCommandOptions: [
+      { name: i18n['every day'] ?? 'every day', value: '*' },
+      { name: i18n['weekdays (5 days)'] ?? 'weekdays (5 days)', value: '1,2,3,4,5' },
+      { name: i18n['weekend (2 days)'] ?? 'weekend (2 days)', value: '0,6' },
+      { name: i18n.Monday ?? 'Monday', value: '1' },
+      { name: i18n.Tuesday ?? 'Tuesday', value: '2' },
+      { name: i18n.Wednesday ?? 'Wednesday', value: '3' },
+      { name: i18n.Thursday ?? 'Thursday', value: '4' },
+      { name: i18n.Friday ?? 'Friday', value: '5' },
+      { name: i18n.Saturday ?? 'Saturday', value: '6' },
+      { name: i18n.Sunday ?? 'Sunday', value: '0' }
+    ],
+    monthlyRunCommandOptions: [
+      { name: i18n['every month'] ?? 'every month', value: '*' },
+      { name: i18n['every odd month'] ?? 'every odd month', value: '1-11/2' },
+      { name: i18n['every even month'] ?? 'every even month', value: '*/2' },
+      { name: `${i18n.every || 'every'} 3`, value: '*/3' },
+      { name: `${i18n.every || 'every'} 6`, value: '*/6' },
+      { name: i18n.Jan ?? 'Jan', value: '1' },
+      { name: i18n.Feb ?? 'Feb', value: '2' },
+      { name: i18n.Mar ?? 'Mar', value: '3' },
+      { name: i18n.Apr ?? 'Apr', value: '4' },
+      { name: i18n.May ?? 'May', value: '5' },
+      { name: i18n.Jun ?? 'Jun', value: '6' },
+      { name: i18n.Jul ?? 'Jul', value: '7' },
+      { name: i18n.Aug ?? 'Aug', value: '8' },
+      { name: i18n.Sep ?? 'Sep', value: '9' },
+      { name: i18n.Oct ?? 'Oct', value: '10' },
+      { name: i18n.Nov ?? 'Nov', value: '11' },
+      { name: i18n.Dec ?? 'Dec', value: '12' }
+    ],
+    dateOptions: [
+      { name: '1', value: '1' },
+      { name: '2', value: '2' },
+      { name: '3', value: '3' },
+      { name: '4', value: '4' },
+      { name: '5', value: '5' },
+      { name: '6', value: '6' },
+      { name: '7', value: '7' },
+      { name: '8', value: '8' },
+      { name: '9', value: '9' },
+      { name: '10', value: '10' },
+      { name: '11', value: '11' },
+      { name: '12', value: '12' },
+      { name: '13', value: '13' },
+      { name: '14', value: '14' },
+      { name: '15', value: '15' },
+      { name: '16', value: '16' },
+      { name: '17', value: '17' },
+      { name: '18', value: '18' },
+      { name: '19', value: '19' },
+      { name: '20', value: '20' },
+      { name: '21', value: '21' },
+      { name: '22', value: '22' },
+      { name: '23', value: '23' },
+      { name: '24', value: '24' },
+      { name: '25', value: '25' },
+      { name: '26', value: '26' },
+      { name: '27', value: '27' },
+      { name: '28', value: '28' },
+      { name: '29', value: '29' },
+      { name: '30', value: '30' },
+      { name: '31', value: '31' }
+    ]
+  }
+}

+ 15 - 12
web/js/react/src/ControlPanelService/Ip.js

@@ -1,12 +1,12 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const token = localStorage.getItem("token");
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/ip/ip.php';
-const addIpApiUri = '/api/add/ip/index.php';
-const additionalInfoUri = '/api/add/ip/index.php';
-const ipInfoUri = '/api/edit/ip/index.php';
-const updateIpUri = '/api/edit/ip/index.php';
+const webApiUri = '/api/v1/list/ip/index.php';
+const addIpApiUri = '/api/v1/add/ip/index.php';
+const additionalInfoUri = '/api/v1/add/ip/index.php';
+const ipInfoUri = '/api/v1/edit/ip/index.php';
+const updateIpUri = '/api/v1/edit/ip/index.php';
 
 export const getIpList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -15,18 +15,21 @@ export const getIpList = () => {
 export const bulkAction = (action, internetProtocols) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   internetProtocols.forEach(internetProtocol => {
     formData.append("ip[]", internetProtocol);
-    formData.append("delete_url", `/delete/ip/?ip=${internetProtocol}&token=${token}`);
   });
 
-  return axios.post(BASE_URL + '/bulk/ip/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/ip/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const getAdditionalInfo = () => {
@@ -47,7 +50,7 @@ export const getInternetProtocolInfo = ip => {
   return axios.get(BASE_URL + ipInfoUri, {
     params: {
       ip,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -62,7 +65,7 @@ export const updateInternetProtocol = (data, ip) => {
   return axios.post(BASE_URL + updateIpUri, formDataObject, {
     params: {
       ip,
-      token
+      token: getAuthToken()
     }
   });
 }

+ 1 - 1
web/js/react/src/ControlPanelService/Languages.js

@@ -1,6 +1,6 @@
 import axios from "axios";
 
-const webApiUri = '/api/languages.php';
+const webApiUri = '/api/v1/languages.php';
 const BASE_URL = window.location.origin;
 
 export const getLanguages = () => {

+ 1 - 1
web/js/react/src/ControlPanelService/Logs.js

@@ -1,7 +1,7 @@
 import axios from "axios";
 
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/log/log.php';
+const webApiUri = '/api/v1/list/log/index.php';
 
 export const getLogsList = () => {
   return axios.get(BASE_URL + webApiUri);

+ 17 - 14
web/js/react/src/ControlPanelService/Mail.js

@@ -1,12 +1,11 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const token = localStorage.getItem("token");
-const { i18n } = window.GLOBAL.App;
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/mail/mail.php';
-const addMailApiUri = '/api/add/mail/index.php';
-const mailInfoUri = '/api/edit/mail/index.php';
-const updateMailUri = '/api/edit/mail/index.php';
+const webApiUri = '/api/v1/list/mail/index.php';
+const addMailApiUri = '/api/v1/add/mail/index.php';
+const mailInfoUri = '/api/v1/edit/mail/index.php';
+const updateMailUri = '/api/v1/edit/mail/index.php';
 
 export const getMailList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -23,30 +22,34 @@ export const getMailAccountInfo = (domain, account) => {
 export const bulkAction = (action, domainNameSystems) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   domainNameSystems.forEach(domainNameSystem => {
     formData.append("domain[]", domainNameSystem);
   });
 
-  return axios.post(BASE_URL + '/bulk/mail/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/mail/', formData);
 };
 
 export const bulkMailAccountAction = (action, domain, accounts = []) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
   formData.append("domain", domain);
 
   accounts.forEach(account => {
     formData.append("account[]", account);
   });
 
-  return axios.post(BASE_URL + '/bulk/mail/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/mail/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const addMail = data => {
@@ -83,7 +86,7 @@ export const getMailInfo = domain => {
   return axios.get(BASE_URL + mailInfoUri, {
     params: {
       domain,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -98,12 +101,12 @@ export const updateMail = (data, domain) => {
   return axios.post(BASE_URL + updateMailUri, formDataObject, {
     params: {
       domain,
-      token
+      token: getAuthToken()
     }
   });
 }
 
-export const mailInfoBlockSelectOptions = [
+export const mailInfoBlockSelectOptions = i18n => [
   {
     value: i18n['Use server hostname'],
     type: 'hostname',

+ 10 - 4
web/js/react/src/ControlPanelService/Notifications.js

@@ -1,10 +1,16 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 let BASE_URL = window.location.origin;
-let getNotificationsUri = '/list/notifications/?ajax=1';
-let deleteNotificationsUri = '/delete/notification';
+let getNotificationsUri = '/api/v1/list/notifications/index.php';
+let deleteNotificationsUri = '/api/v1/delete/notification/index.php';
 
 export const getAppNotifications = () => {
-  return axios.get(BASE_URL + getNotificationsUri);
+  return axios.get(BASE_URL + getNotificationsUri, {
+    params: {
+      ajax: 1,
+      token: getAuthToken()
+    }
+  });
 }
 
 export const deleteNotification = id => {
@@ -12,7 +18,7 @@ export const deleteNotification = id => {
     params: {
       'delete': 1,
       'notification_id': id,
-      'token': localStorage.getItem("token")
+      token: getAuthToken()
     }
   });
 }

+ 14 - 11
web/js/react/src/ControlPanelService/Package.js

@@ -1,12 +1,12 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
 const BASE_URL = window.location.origin;
-const token = localStorage.getItem("token");
-const webApiUri = '/list/package/package.php';
-const additionalPackageInfoUri = '/api/add/package/index.php';
-const addPackageUri = '/api/add/package/index.php';
-const packageInfoUri = '/api/edit/package/index.php';
-const updatePackageUri = '/api/edit/package/index.php';
+const webApiUri = '/api/v1/list/package/index.php';
+const additionalPackageInfoUri = '/api/v1/add/package/index.php';
+const addPackageUri = '/api/v1/add/package/index.php';
+const packageInfoUri = '/api/v1/edit/package/index.php';
+const updatePackageUri = '/api/v1/edit/package/index.php';
 
 export const getPackageList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -15,18 +15,21 @@ export const getPackageList = () => {
 export const bulkAction = (action, backups) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   backups.forEach(backup => {
     formData.append("package[]", backup);
-    formData.append("delete_url", `/delete/package/?package=${backup}&token=${token}`);
   });
 
-  return axios.post(BASE_URL + '/bulk/package/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/package/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const addPackage = data => {
@@ -47,7 +50,7 @@ export const getPackageInfo = item => {
   return axios.get(BASE_URL + packageInfoUri, {
     params: {
       package: item,
-      token
+      token: getAuthToken()
     }
   });
 }

+ 3 - 3
web/js/react/src/ControlPanelService/RRD.js

@@ -1,12 +1,12 @@
 import axios from "axios";
 
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/rrd/rrd.php';
+const webApiUri = '/api/v1/list/rrd/index.php';
 
 export const getRrdList = () => {
   return axios.get(BASE_URL + webApiUri);
 }
 
 export function generateImagePath(period, type, rrd) {
-  return `/list/rrd/image.php?/rrd/${type}/${period}-${rrd}.png`;
-}
+  return `/api/v1/list/rrd/image.php?/rrd/${type}/${period}-${rrd}.png`;
+}

+ 1 - 1
web/js/react/src/ControlPanelService/ResetPassword.js

@@ -1,7 +1,7 @@
 import axios from "axios";
 
 const BASE_URL = window.location.origin;
-const resetPasswordUri = '/api/reset/index.php';
+const resetPasswordUri = '/api/v1/reset/index.php';
 
 export const resetPassword = (user = '', code = '', password = '', confirmPassword = '') => {
   const formData = new FormData();

+ 7 - 2
web/js/react/src/ControlPanelService/Search.js

@@ -1,4 +1,5 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
 const BASE_URL = window.location.origin;
 const webApiUri = '/search/search.php';
@@ -8,5 +9,9 @@ export const getSearchResultsList = term => {
 }
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
-}
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
+}

+ 15 - 11
web/js/react/src/ControlPanelService/Server.js

@@ -1,9 +1,9 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
 const BASE_URL = window.location.origin;
-const token = localStorage.getItem("token");
-const webApiUri = '/list/server/server.php';
-const serverAdditionalInfoUri = '/api/edit/server/index.php';
+const webApiUri = '/api/v1/list/server/index.php';
+const serverAdditionalInfoUri = '/api/v1/edit/server/index.php';
 
 export const getServersList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -12,23 +12,27 @@ export const getServersList = () => {
 export const bulkAction = (action, services) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   services.forEach(service => {
     formData.append("service[]", service);
   });
 
-  return axios.post(BASE_URL + '/api/bulk/service/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/service/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const getServerAdditionalInfo = () => {
   return axios.get(BASE_URL + serverAdditionalInfoUri, {
     params: {
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -40,19 +44,19 @@ export const updateService = (data, uri = '') => {
     formDataObject.append(key, data[key]);
   }
 
-  return axios.post(BASE_URL + `/api/edit/server/${uri}/index.php`, formDataObject, {
+  return axios.post(BASE_URL + `/api/v1/edit/server/${uri}/index.php`, formDataObject, {
     params: {
-      token
+      token: getAuthToken()
     }
   });
 }
 
 export const getServiceInfo = service => {
-  return axios.get(`${BASE_URL}/api/edit/server/${service}/index.php`);
+  return axios.get(`${BASE_URL}/api/v1/edit/server/${service}/index.php`);
 }
 
 export const getServiceLogs = service => {
-  return axios.get(`${BASE_URL}/list/server/server.php?${service}`);
+  return axios.get(`${BASE_URL}${webApiUri}?${service}`);
 }
 
 export const services = [

+ 1 - 1
web/js/react/src/ControlPanelService/Statistics.js

@@ -1,7 +1,7 @@
 import axios from "axios";
 
 const BASE_URL = window.location.origin;
-const webApiUri = '/list/stats/stats.php';
+const webApiUri = '/api/v1/list/stats/index.php';
 
 export const getStatisticsList = user => {
   return axios.get(BASE_URL + webApiUri + '?user=' + user);

+ 19 - 7
web/js/react/src/ControlPanelService/Updates.js

@@ -1,9 +1,9 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
 const deleteAutoUpdateUri = '/delete/cron/autoupdate/';
 const addAutoUpdateUri = '/add/cron/autoupdate/';
-const webApiUri = '/list/updates/updates.php';
-const token = localStorage.getItem("token");
+const webApiUri = '/api/v1/list/updates/index.php';
 const BASE_URL = window.location.origin;
 
 export const getUpdatesList = () => {
@@ -13,23 +13,35 @@ export const getUpdatesList = () => {
 export const bulkAction = (action, updates) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   updates.forEach(update => {
     formData.append("pkg[]", update);
   });
 
-  return axios.post(BASE_URL + '/bulk/vesta/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/vesta/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(`${BASE_URL}${uri}?token=${token}`);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const enableAutoUpdate = () => {
-  return axios.get(`${BASE_URL}${addAutoUpdateUri}?token=${token}`);
+  return axios.get(`${BASE_URL}${addAutoUpdateUri}`, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 };
 
 export const disableAutoUpdate = () => {
-  return axios.get(`${BASE_URL}${deleteAutoUpdateUri}?token=${token}`);
+  return axios.get(`${BASE_URL}${deleteAutoUpdateUri}`, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 };

+ 1 - 1
web/js/react/src/ControlPanelService/UserNS.js

@@ -1,7 +1,7 @@
 import axios from "axios";
 
 const BASE_URL = window.location.origin;
-const userNSApiUri = '/api/list-user-ns.php';
+const userNSApiUri = '/api/v1/list-user-ns.php';
 
 export const getUserNS = () => {
   return axios.get(BASE_URL + userNSApiUri);

+ 21 - 13
web/js/react/src/ControlPanelService/Users.js

@@ -1,30 +1,38 @@
 import axios from 'axios';
+import { getAuthToken } from 'src/utils/token';
 
-let token = localStorage.getItem('token');
 const BASE_URL = window.location.origin;
-const usersUri = '/list/user/user.php';
-const addUsersUri = '/api/add/user/index.php';
-const userInfoUri = '/api/edit/user/index.php';
-const updateUserUri = '/api/edit/user/index.php';
+const usersUri = '/api/v1/list/user/index.php';
+const addUsersUri = '/api/v1/add/user/index.php';
+const userInfoUri = '/api/v1/edit/user/index.php';
+const updateUserUri = '/api/v1/edit/user/index.php';
 
 export const getUsersList = () => {
-  return axios.get(BASE_URL + usersUri);
+  return axios.get(BASE_URL + usersUri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const bulkAction = (action, selectedUsers) => {
   const formData = new FormData();
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
   formData.append("action", action);
 
   selectedUsers.forEach(user => {
     formData.append("user[]", user);
   });
 
-  return axios.post(BASE_URL + '/bulk/user/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/user/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const addUser = data => {
@@ -34,7 +42,7 @@ export const addUser = data => {
     formDataObject.append(key, data[key]);
   }
 
-  formDataObject.append("token", token);
+  formDataObject.append("token", getAuthToken());
   formDataObject.append("ok", "Add");
 
   return axios.post(BASE_URL + addUsersUri, formDataObject);
@@ -44,7 +52,7 @@ export const getUserInfo = username => {
   return axios.get(BASE_URL + userInfoUri, {
     params: {
       user: username,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -59,7 +67,7 @@ export const updateUser = (data, user) => {
   return axios.post(BASE_URL + updateUserUri, formDataObject, {
     params: {
       user,
-      token
+      token: getAuthToken()
     }
   });
-}
+}

+ 15 - 11
web/js/react/src/ControlPanelService/Web.js

@@ -1,12 +1,12 @@
 import axios from "axios";
+import { getAuthToken } from "src/utils/token";
 
-const token = localStorage.getItem("token");
 const BASE_URL = window.location.origin;
-const addWebUri = '/api/add/web/index.php';
-const webApiUri = '/list/web/web.php';
-const webStatsUri = '/api/web-stats.php';
-const domainInfoUri = '/api/edit/web/index.php';
-const updateDomainUri = '/api/edit/web/index.php';
+const addWebUri = '/api/v1/add/web/index.php';
+const webApiUri = '/api/v1/list/web/index.php';
+const webStatsUri = '/api/v1/web-stats.php';
+const domainInfoUri = '/api/v1/edit/web/index.php';
+const updateDomainUri = '/api/v1/edit/web/index.php';
 
 export const getWebList = () => {
   return axios.get(BASE_URL + webApiUri);
@@ -15,17 +15,21 @@ export const getWebList = () => {
 export const bulkAction = (action, webDomains) => {
   const formData = new FormData();
   formData.append("action", action);
-  formData.append("token", token);
+  formData.append("token", getAuthToken());
 
   webDomains.forEach(webDomain => {
     formData.append("domain[]", webDomain);
   });
 
-  return axios.post(BASE_URL + '/bulk/web/', formData);
+  return axios.post(BASE_URL + '/api/v1/bulk/web/', formData);
 };
 
 export const handleAction = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + uri, {
+    params: {
+      token: getAuthToken()
+    }
+  });
 }
 
 export const addWeb = data => {
@@ -46,7 +50,7 @@ export const getDomainInfo = domain => {
   return axios.get(BASE_URL + domainInfoUri, {
     params: {
       domain,
-      token
+      token: getAuthToken()
     }
   });
 }
@@ -61,7 +65,7 @@ export const updateWebDomain = (data, domain) => {
   return axios.post(BASE_URL + updateDomainUri, formDataObject, {
     params: {
       domain,
-      token
+      token: getAuthToken()
     }
   });
 }

+ 1 - 1
web/js/react/src/ControlPanelService/WebLogs.js

@@ -3,5 +3,5 @@ import axios from "axios";
 const BASE_URL = window.location.origin;
 
 export const getWebLogs = uri => {
-  return axios.get(BASE_URL + uri);
+  return axios.get(BASE_URL + '/api/v1' +uri);
 }

+ 16 - 4
web/js/react/src/FileManagerHelper.js

@@ -1,23 +1,35 @@
 import axios from "axios";
+import QueryString from "qs";
 const server = window.location.origin + "/file_manager/fm_api.php?";
 
 export function validateAction(url) {
   return axios.get(url);
 }
 
-export function cacheData(currentUser, history) {
+export function cacheData(currentUser, history, rootDir) {
+  const parsedQueryString = QueryString.parse(history.location.search, { ignoreQueryPrefix: true });
+
+  if (parsedQueryString.path) {
+    localStorage.setItem("activeWindow", "left");
+    localStorage.setItem("leftListPath", parsedQueryString.path);
+    localStorage.setItem("rightListPath", parsedQueryString.path);
+    return;
+  }
+
   if (localStorage.getItem("lastUser") === null || currentUser !== localStorage.getItem("lastUser")) {
     localStorage.setItem("lastUser", currentUser);
     localStorage.setItem("activeWindow", "left");
-    localStorage.setItem("leftListPath", window.GLOBAL.ROOT_DIR);
-    localStorage.setItem("rightListPath", window.GLOBAL.ROOT_DIR);
+    localStorage.setItem("leftListPath", rootDir);
+    localStorage.setItem("rightListPath", rootDir);
+    return;
   }
 
   if (localStorage.getItem("activeWindow") === null || localStorage.getItem("leftListPath") === null || localStorage.getItem("rightListPath") === null) {
     let path = history.location.search.substring(6).split('/');
     localStorage.setItem("activeWindow", "left");
     localStorage.setItem("leftListPath", path);
-    localStorage.setItem("rightListPath", window.GLOBAL.ROOT_DIR);
+    localStorage.setItem("rightListPath", rootDir);
+    return;
   }
 }
 

+ 15 - 0
web/js/react/src/actions/Notification/notificationActions.js

@@ -0,0 +1,15 @@
+import { ADD_NOTIFICATIONS, REMOVE_NOTIFICATIONS } from './notificationTypes';
+
+export const addNotifications = value => {
+  return {
+    type: ADD_NOTIFICATIONS,
+    value
+  };
+};
+
+export const removeNotifications = () => {
+  return {
+    type: REMOVE_NOTIFICATIONS,
+    value: []
+  };
+};

+ 2 - 0
web/js/react/src/actions/Notification/notificationTypes.js

@@ -0,0 +1,2 @@
+export const ADD_NOTIFICATIONS = 'ADD_NOTIFICATIONS';
+export const REMOVE_NOTIFICATIONS = 'REMOVE_NOTIFICATIONS';

+ 35 - 31
web/js/react/src/actions/Session/sessionActions.js

@@ -1,29 +1,31 @@
-import { LOGIN, LOGOUT, LOGGED_OUT_AS, RESET_PASSWORD } from './sessionTypes';
+import { LOGIN, LOGOUT, LOGGED_OUT_AS, CHECK_AUTH, RESET_PASSWORD } from './sessionTypes';
 import { checkAuth, signIn, signInAs, signOut } from 'src/services/session';
-import { resetAuthToken, setAuthToken } from 'src/utils/token';
 import { resetPassword } from 'src/ControlPanelService/ResetPassword';
+import { resetAuthToken, setAuthToken } from 'src/utils/token';
 
 const LOGOUT_RESPONSE = 'logged_out';
 const LOGOUT_AS_RESPONSE = 'logged_out_as';
 
 export const login = (user, password) => dispatch => {
   return new Promise((resolve, reject) => {
-    signIn(user, password).then((response) => {
-      const { error, session, token, panel, data, user } = response.data;
+    signIn({ user, password }).then((response) => {
+      const { error, session, token, panel, data, user, i18n } = response.data;
+
       if (token) setAuthToken(token);
 
       dispatch({
         type: LOGIN,
         value: {
-          token: data ? token : '',
+          token: token || '',
           panel,
           session,
+          i18n: i18n || {},
           userName: user,
           user: data,
           error
         },
       });
-      resolve(response.data);
+      resolve(token);
     }, (error) => {
       reject(error);
     });
@@ -34,7 +36,6 @@ export const reset = ({ user = '', code = '', password = '', password_confirm =
   return new Promise((resolve, reject) => {
     resetPassword(user, code, password, password_confirm).then((response) => {
       const { error, session, token, panel, user } = response.data;
-      if (token) setAuthToken(token);
 
       dispatch({
         type: RESET_PASSWORD,
@@ -47,7 +48,7 @@ export const reset = ({ user = '', code = '', password = '', password_confirm =
           error
         },
       });
-      resolve(response.data);
+      resolve(token);
     }, (error) => {
       reject(error);
     });
@@ -57,22 +58,23 @@ export const reset = ({ user = '', code = '', password = '', password_confirm =
 export const loginAs = username => dispatch => {
   return new Promise((resolve, reject) => {
     signInAs(username).then((response) => {
-      const { error, token, session, panel, data, user } = response.data;
+      const { error, token, session, panel, data, user, i18n } = response.data;
       if (token) setAuthToken(token);
 
       dispatch({
         type: LOGIN,
         value: {
-          token,
-          panel,
-          session,
           userName: user,
           user: data,
-          error,
-        },
+          i18n,
+          session,
+          panel,
+          token,
+          error
+        }
       });
 
-      resolve(response.data);
+      resolve(token);
     }, (error) => {
       console.error(error);
       reject();
@@ -83,7 +85,7 @@ export const loginAs = username => dispatch => {
 export const logout = () => (dispatch, getState) => {
   return new Promise((resolve, reject) => {
     signOut().then((response) => {
-      const { logout_response, panel, session, user, data, token } = response.data;
+      const { logout_response, error, userName, user, i18n, session, panel } = response.data;
 
       if (logout_response === LOGOUT_RESPONSE) {
         resetAuthToken();
@@ -91,27 +93,28 @@ export const logout = () => (dispatch, getState) => {
         dispatch({
           type: LOGOUT,
           value: {
-            userName: user,
+            userName: '',
             user: {},
-            token,
-            panel,
-            session,
-            error: ''
+            token: '',
+            panel: {},
+            session: {},
+            i18n: [],
+            error,
           },
         });
 
         resolve();
       } else if (logout_response === LOGOUT_AS_RESPONSE) {
-        const { token } = getState().session;
         dispatch({
           type: LOGGED_OUT_AS,
           value: {
-            userName: user,
-            user: data,
+            userName,
+            user,
             session,
             panel,
-            token,
-            error: ''
+            token: '',
+            i18n,
+            error,
           },
         });
 
@@ -126,17 +129,18 @@ export const logout = () => (dispatch, getState) => {
   });
 }
 
-export const setToken = (token) => (dispatch, getState) => {
+export const checkAuthHandler = () => (dispatch, getState) => {
   return new Promise((resolve, reject) => {
-    checkAuth(token)
+    checkAuth()
       .then(res => {
-        const { user, data, session, panel, error } = res.data;
+        const { user, data, session, panel, error, i18n, token } = res.data;
 
         dispatch({
-          type: LOGIN,
+          type: CHECK_AUTH,
           value: {
             userName: user,
             user: data,
+            i18n,
             session,
             panel,
             token,
@@ -144,7 +148,7 @@ export const setToken = (token) => (dispatch, getState) => {
           }
         });
 
-        resolve();
+        resolve(token);
       })
       .catch(err => {
         reject();

+ 1 - 0
web/js/react/src/actions/Session/sessionTypes.js

@@ -1,5 +1,6 @@
 export const LOGIN = 'LOGIN';
 export const LOGOUT = 'LOGOUT';
 export const LOGIN_AS = 'LOGIN_AS';
+export const CHECK_AUTH = 'CHECK_AUTH';
 export const LOGGED_OUT_AS = 'LOGGED_OUT_AS';
 export const RESET_PASSWORD = 'RESET_PASSWORD';

+ 3 - 2
web/js/react/src/components/Backup/Backup.jsx

@@ -5,10 +5,11 @@ import Container from '../ControlPanel/Container/Container';
 import { faFileDownload } from '@fortawesome/free-solid-svg-icons'
 import './Backup.scss';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 const Backup = props => {
   const { data } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const toggleFav = (starred) => {
@@ -24,7 +25,7 @@ const Backup = props => {
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/backup/?backup=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/backup/?backup=${data.NAME}`);
   }
 
   return (

+ 2 - 2
web/js/react/src/components/Backup/Exclusion/Edit/index.jsx

@@ -7,14 +7,14 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Toolbar from 'src/components/MainNav/Toolbar/Toolbar';
 import Spinner from 'src/components/Spinner/Spinner';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import { Helmet } from 'react-helmet';
 
 import './style.scss';
 
 const EditBackupExclusions = () => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 4 - 1
web/js/react/src/components/Backup/Exclusion/index.jsx

@@ -1,16 +1,19 @@
 import React from 'react';
+import { useSelector } from 'react-redux';
 import Container from '../../ControlPanel/Container/Container';
 
 import './style.scss';
 
 const Exclusion = ({ data, focused }) => {
+  const { i18n } = useSelector(state => state.session);
+
   const renderExclusionItems = () => {
     if (!Array.isArray(data.ITEMS)) {
       for (let item in data.ITEMS) {
         return <><b>{item}</b> &nbsp; {data.ITEMS[item]}<br /></>;
       }
     } else {
-      return window.GLOBAL.App.i18n['no exclusions'];
+      return i18n['no exclusions'];
     }
   }
 

+ 2 - 1
web/js/react/src/components/Backup/RestoreSetting/RestoreSetting.jsx

@@ -5,9 +5,10 @@ import ListItem from '../../ControlPanel/ListItem/ListItem';
 import { Link } from 'react-router-dom';
 
 import './RestoreSetting.scss';
+import { useSelector } from 'react-redux';
 
 export default function RestoreSetting({ data, checkItemFunc = () => { }, restoreSetting = () => { } }) {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
 
   const displayBackupDetailName = type => {
     switch (type) {

+ 21 - 4
web/js/react/src/components/Backup/RestoreSetting/RestoreSetting.scss

@@ -1,3 +1,14 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$textColor: #555;
+
 .backups-restore-settings {
   .list-item {
     .r-col {
@@ -18,12 +29,12 @@
           display: flex;
           justify-content: center;
           align-items: center;
-          color: #777;
+          color: $textColor;
           padding: 10px 15px;
           text-decoration: none;
     
           &:hover {
-            background: rgb(145, 145, 145);
+            background: $whiteBackground;
             color: white;
     
             svg {
@@ -33,7 +44,7 @@
         }
     
         svg {
-          color: #777;
+          color: $textColor;
         }
       }
 
@@ -43,7 +54,13 @@
         }
 
         &:hover {
-          background-color: #9fbf0c;
+          color: $hoverButtonText;
+          background-color: $primaryLight;
+        }
+        
+        &:hover {
+          color: $activeButtonText;
+          background-color: $primaryActive;
         }
       }
     }

+ 4 - 2
web/js/react/src/components/Backup/RestoreSettings/BackupRestoreSettings.jsx

@@ -17,7 +17,7 @@ import { Helmet } from 'react-helmet';
 import './BackupRestoreSettings.scss';
 
 export default function BackupRestoreSettings(props) {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
   const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
   const { focusedElement } = useSelector(state => state.mainNavigation);
@@ -153,6 +153,8 @@ export default function BackupRestoreSettings(props) {
         setState({
           ...state,
           totalAmount: result.data.totalAmount,
+          selection: [],
+          toggledAll: false,
           loading: false
         });
       })
@@ -165,7 +167,7 @@ export default function BackupRestoreSettings(props) {
         acc.push({
           type: cat,
           name: item,
-          restoreLinkParams: `?backup=${props.backup}&type=${cat.toLowerCase()}&object=${item}&token=${token}`
+          restoreLinkParams: `?backup=${props.backup}&type=${cat.toLowerCase()}&object=${item}`
         });
       });
 

+ 33 - 21
web/js/react/src/components/ControlPanel/AddItemLayout/AddItemLayout.scss

@@ -1,15 +1,22 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
 $textColor: #555;
-$addButtonBackground: #9FBF0C;
-$addButtonBackgroundHover: #C0E60F;
-$optionalButtonHover: #9FBF0C;
-$optionalButtonActive: #c0e60f;
-$deleteButtonColorHover: #FF3438;
-$deleteButtonColorActive: #FF5F5F;
+
+$optionalButtonHover: $primary;
+$optionalButtonActive: $primaryLight;
+$deleteButtonColorHover: #b00e5b;
+$deleteButtonColorActive: #b11661;
 $backButtonBackground: #DFDEDD;
 $backButtonBackgroundHover: #999;
 $transition: all 200ms cubic-bezier(0.4, 0.1, 0.5, 0.85);
 $errorColor: #BE5ABF;
-$successColor: $addButtonBackground;
 
 .content .edit-template {
   padding-bottom: 2rem;
@@ -25,7 +32,7 @@ $successColor: $addButtonBackground;
   
     div.error,
     div.success {
-      width: fit-content;
+      width: fit-content !important;
   
       span {
         font-weight: bold;
@@ -47,10 +54,10 @@ $successColor: $addButtonBackground;
       }
   
       span.ok-message {
-        color: $successColor;
+        color: $primary;
   
         svg {
-          color: $successColor;
+          color: $primary;
         }
       }
     }
@@ -84,15 +91,15 @@ $successColor: $addButtonBackground;
   form {
     button,
     a {
-      color:#2C9491;
+      color:$primary;
       font-weight: bold;
     
       &:hover {
-        color: #ff6701;
+        color: $secondaryLight;
       }
     
       &:active {
-        color: #F7D616;
+        color: $secondaryActive;
       }
     }
 
@@ -139,14 +146,14 @@ $successColor: $addButtonBackground;
       &:focus,
       &:active {
         outline: none;
-        border-color: #55C9C0;
+        border-color: $primaryActive;
         box-shadow: unset;
       }
     }
 
     textarea {
       &:focus {
-        background: #D7F9FF;;
+        background: #D7F9FF;
       }
     }
 
@@ -172,7 +179,7 @@ $successColor: $addButtonBackground;
       &:focus,
       &:active {
         box-shadow: unset;
-        border-color: #55C9C0;
+        border-color: $primaryActive;
         background: #D7F9FF;
       }
     }
@@ -221,12 +228,17 @@ $successColor: $addButtonBackground;
       }
 
       .add {
-        color: white;
-        background: $addButtonBackground;
+        color: $activeButtonText;
+        background: $primary;
 
         &:hover {
-          color: #555;
-          background: $addButtonBackgroundHover;
+          color: $hoverButtonText;
+          background: $primaryLight;
+        }
+        
+        &:active {
+          color: $activeButtonText;
+          background: $primaryActive;
         }
       }
 
@@ -258,7 +270,7 @@ $successColor: $addButtonBackground;
     }
 
     .status {
-      color: #9FBF0C;
+      color: $primary;
       font-size: 11px;
       letter-spacing: 1px;
       text-transform: uppercase;

+ 3 - 9
web/js/react/src/components/ControlPanel/AddItemLayout/Form/Checkbox/Checkbox.jsx

@@ -1,17 +1,11 @@
 import React, { useEffect, useState } from 'react';
 
 const Checkbox = ({ name, id, title, defaultChecked = false, onChange = () => { }, checked }) => {
-  const [checkedState, setCheckedState] = useState(false);
+  const [checkedState, setCheckedState] = useState(defaultChecked);
 
   useEffect(() => {
-    if (!!checked) {
-      setCheckedState(checked);
-    }
-
-    if (!!defaultChecked) {
-      setCheckedState(defaultChecked);
-    }
-  }, [checked, defaultChecked]);
+    setCheckedState(checked);
+  }, [checked]);
 
   const changeCheckbox = event => {
     setCheckedState(event.target.checked);

+ 2 - 1
web/js/react/src/components/ControlPanel/AddItemLayout/Form/NameServers/NameServers.jsx

@@ -1,7 +1,8 @@
 import React, { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
 
 const NameServers = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const [state, setState] = useState({
     nameServersAmount: [],
     usersNS: []

+ 3 - 2
web/js/react/src/components/ControlPanel/AddItemLayout/Form/Password/Password.jsx

@@ -1,8 +1,9 @@
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import React, { useEffect, useState } from 'react';
+import { useSelector } from 'react-redux';
 
 const Password = ({ defaultValue, onChange = () => { }, id, name, title, showGenerationButton = true, ...props }) => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const [state, setState] = useState({
     hidePassword: false,
     generatedPassword: ''
@@ -42,7 +43,7 @@ const Password = ({ defaultValue, onChange = () => { }, id, name, title, showGen
         {title ? title : i18n.Password}
         {
           showGenerationButton && (
-            <>/ <button type="button" className="generate-password" onClick={() => generatePassword()}>
+            <> / <button type="button" className="generate-password" onClick={() => generatePassword()}>
               {i18n.Generate}
             </button></>
           )

+ 2 - 1
web/js/react/src/components/ControlPanel/AddItemLayout/Form/SelectInput/SelectInput.jsx

@@ -1,7 +1,8 @@
 import React from 'react';
+import { useSelector } from 'react-redux';
 
 const SelectInput = ({ options = [], id, name, title, optionalTitle = '', selected = '', onChange = () => { }, disabled = false }) => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
 
   const renderOptions = () => {
     return options.map((option, index) =>

+ 2 - 1
web/js/react/src/components/ControlPanel/Hotkeys/Hotkeys.jsx

@@ -1,9 +1,10 @@
 import React, { useEffect } from 'react';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import './Hotkeys.scss';
+import { useSelector } from 'react-redux';
 
 const Hotkeys = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
 
   useEffect(() => {
     window.addEventListener("keyup", toggleShortcutsLit);

+ 10 - 11
web/js/react/src/components/ControlPanel/Hotkeys/Hotkeys.scss

@@ -1,3 +1,5 @@
+$secondary: #fcac04;
+
 .hotkeys-list {
   position: fixed;
   bottom: 0;
@@ -6,39 +8,36 @@
   flex-direction: column;
   transform: translateX(-45%);
   width: 53%;
-  background: rgba(50, 50, 50, 0.9);
+  background: #222e44de;
   font-size: 13px;
 
   .head {
     display: flex;
     justify-content: space-between;
     align-items: center;
-    border-bottom: 1px solid #333;
+    border-bottom: 1px solid $secondary;
 
     .name {
       text-transform: uppercase;
       padding: 5px 0 5px 10px;
       font-size: 12px;
       font-weight: bold;
-      color: #ffcc00;
+      color: $secondary;
       letter-spacing: 2px;
       padding: 15px;
     }
 
     .close {
       padding: 12px;
+      opacity: 1 !important;
 
       svg {
-        color: #a1a1a1;
+        color: $secondary;
       }
 
       &:hover {
         cursor: pointer;
-        background: black;
-      }
-
-      &:active {
-        background: #55c9c0;
+        background: #222e44;
       }
     }
   }
@@ -58,12 +57,12 @@
 
         span.name {
           margin-right: 15px;
-          color: #48F4EF;
+          color: $secondary;
           font-weight: bold;
         }
     
         span.description {
-          color: #929292;
+          color: white;
         }
       }
 

+ 37 - 44
web/js/react/src/components/ControlPanel/ListItem/ListItem.jsx

@@ -1,24 +1,20 @@
-import React, { Component } from 'react';
+import React, { Component, useEffect, useState } from 'react';
 import Container from '../Container/Container';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { useSelector } from 'react-redux';
 import './ListItem.scss';
 
-class ListItem extends Component {
-  state = {
-    starred: false
-  }
-
-  UNSAFE_componentWillMount() {
-    this.setState({ starred: this.props.starred === 1 });
-  }
+const ListItem = (props) => {
+  const { i18n } = useSelector(state => state.session);
+  const [state, setState] = useState({ starred: false });
 
-  UNSAFE_componentWillReceiveProps(nextProps) {
-    this.setState({
-      starred: nextProps.starred === 1
-    });
-  }
+  useEffect(() => {
+    if (props.hasOwnProperty('starred')) {
+      setState({ ...state, starred: Boolean(props.starred) });
+    }
+  }, [props.starred]);
 
-  printDate = date => {
+  const printDate = date => {
     if (date) {
       let newDate = new Date(date);
       let day = newDate.getDate();
@@ -30,19 +26,18 @@ class ListItem extends Component {
     }
   }
 
-  toggleItem = () => {
-    this.props.checkItem();
+  const toggleItem = () => {
+    props.checkItem();
   }
 
-  starItem = () => {
-    this.setState({ starred: !this.state.starred }, () => {
-      this.props.toggleFav(this.state.starred);
-    });
+  const starItem = () => {
+    setState({ ...state, starred: !state.starred });
+    props.toggleFav(!state.starred);
   }
 
-  className = () => {
-    const { starred } = this.state;
-    const { checked, outdated, suspended, stopped, focused, sysInfo } = this.props;
+  const className = () => {
+    const { starred } = state;
+    const { checked, outdated, suspended, stopped, focused, sysInfo } = props;
     let className = 'list-item';
 
     if (checked) {
@@ -76,25 +71,23 @@ class ListItem extends Component {
     return className;
   }
 
-  render() {
-    return (
-      <div className={this.className()} id={this.props.id}>
-        <Container className="l-col w-14">
-          {this.printDate(this.props.date)}
-          <div className="text-status">
-            <div className="checkbox"><input type="checkbox" onChange={(e) => this.toggleItem(e)} checked={this.props.checked} /></div>
-            {this.props.leftNameText}
-          </div>
-          <div className="star">
-            <div className="checkbox"><input type="checkbox" onChange={(e) => this.toggleItem(e)} checked={this.props.checked} /></div>
-            <div onClick={this.starItem}><FontAwesomeIcon icon="star" /></div>
-          </div>
-          {this.props.suspended && <div className='suspended'>{window.GLOBAL.App.i18n.suspended}</div>}
-        </Container>
-        {this.props.children}
-      </div>
-    );
-  }
+  return (
+    <div className={className()} id={props.id}>
+      <Container className="l-col w-14">
+        {printDate(props.date)}
+        <div className="text-status">
+          <div className="checkbox"><input type="checkbox" onChange={toggleItem} checked={props.checked} /></div>
+          {props.leftNameText}
+        </div>
+        <div className="star">
+          <div className="checkbox"><input type="checkbox" onChange={toggleItem} checked={props.checked} /></div>
+          <div onClick={starItem}><FontAwesomeIcon icon="star" /></div>
+        </div>
+        {props.suspended && <div className='suspended'>{i18n.suspended}</div>}
+      </Container>
+      {props.children}
+    </div>
+  );
 }
 
-export default ListItem;
+export default ListItem;

+ 18 - 4
web/js/react/src/components/ControlPanel/ListItem/ListItem.scss

@@ -1,3 +1,11 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #2e5bb1;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$textColor: #555;
+
 .list-item {
   display: flex;
   justify-content: flex-start;
@@ -63,12 +71,12 @@
 }
 
 .list-item.starred {
-  border-left: 2px solid #ff6701;
+  border-left: 2px solid $primary;
 
   .l-col div.star {
     div > svg {
       opacity: 1;
-      color: #ff6701;
+      color: $primary;
     }
   }
 }
@@ -112,7 +120,13 @@
 }
 
 .list-item.focused {
-  border-left: 2px solid #5edad0;
+  border-left: 2px solid $secondaryLight;
+
+  .l-col div.star {
+    div > svg {
+      color: $secondaryLight;
+    }
+  }
 
   .actions {
     opacity: 1;
@@ -127,7 +141,7 @@
         width: 25px;
         height: 25px;
         margin-left: 15px;
-        background: #69a298;
+        background: $secondaryLight;
         color: white;
         display: flex;
         justify-content: center;

+ 3 - 2
web/js/react/src/components/ControlPanel/Modal/Modal.jsx

@@ -1,9 +1,10 @@
 import React from 'react';
+import { useSelector } from 'react-redux';
 import './Modal.scss';
 
-const { i18n } = window.GLOBAL.App;
-
 const Modal = ({ show, text, onSave, onCancel, showSaveButton = true, showCancelButton = true }) => {
+  const { i18n } = useSelector(state => state.session);
+
   return (
     <div>
       <div className={`modal fade ${show ? 'show' : ''}`} id="c-panel-modal" tabindex="-1" role="dialog" aria-labelledby="exampleModalLabel" aria-hidden="true" style={{ display: show ? 'block' : 'none' }}>

+ 4 - 2
web/js/react/src/components/ControlPanel/Modal/Modal.scss

@@ -1,3 +1,5 @@
+$primary: #2c54ac;
+
 div.modal {
   z-index: 2;
 }
@@ -11,8 +13,8 @@ div.content .modal .modal-content {
 
   .modal-footer {
     .btn-primary {
-      background: #9FBF0C;
-      border: 1px solid #9FBF0C;
+      background: $primary;
+      border: 1px solid $primary;
     }
   }
 }

+ 2 - 2
web/js/react/src/components/CronJob/Add/AddCronJob.jsx

@@ -8,13 +8,13 @@ import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import Generator from '../Generator/Generator';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddCronJob.scss';
 import { Helmet } from 'react-helmet';
 
 const AddCronJob = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
   const history = useHistory();
   const dispatch = useDispatch();

+ 4 - 3
web/js/react/src/components/CronJob/CronJob.jsx

@@ -4,10 +4,11 @@ import ListItem from '../ControlPanel/ListItem/ListItem';
 import Container from '../ControlPanel/Container/Container';
 import './CronJob.scss';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 const CronJob = props => {
   const { data } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const toggleFav = (starred) => {
@@ -24,11 +25,11 @@ const CronJob = props => {
 
   const handleSuspend = () => {
     let suspendedStatus = data.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend' === 'yes' ? 'unsuspend' : 'suspend';
-    props.handleModal(data.suspend_conf, `/${suspendedStatus}/cron/?job=${data.NAME}&token=${token}`);
+    props.handleModal(data.suspend_conf, `/${suspendedStatus}/cron/index.php?job=${data.NAME}`);
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/cron/?job=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/delete/cron/index.php?job=${data.NAME}`);
   }
 
   return (

+ 2 - 2
web/js/react/src/components/CronJob/Edit/EditCronJob.jsx

@@ -8,7 +8,7 @@ import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import Generator from '../Generator/Generator';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 
 import './EditCronJob.scss';
@@ -16,7 +16,7 @@ import { Helmet } from 'react-helmet';
 
 const EditMail = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 2 - 1
web/js/react/src/components/CronJob/Generator/Generator.jsx

@@ -6,10 +6,11 @@ import { Link, useHistory } from 'react-router-dom';
 import QS from 'qs';
 
 import './Generator.scss';
+import { useSelector } from 'react-redux';
 
 const Generator = props => {
   const formElement = useRef(null);
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const [state, setState] = useState({
     activeTab: '1'

+ 20 - 9
web/js/react/src/components/CronJob/Generator/Generator.scss

@@ -1,3 +1,14 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$textColor: #555;
+
 .cron-job-generator {
   border: 1px solid #d9d9d9;
   padding: 1rem 1.5rem;
@@ -17,16 +28,16 @@
       text-decoration: none;
 
       &:hover {
-        color: #ff6701;
+        color: $secondaryLight;
       }
 
       &:active {
-        color: #55C9C0;
+        color: $primaryActive;
       }
     }
 
     a.active {
-      color: #ff6701;
+      color: $secondaryActive;
     }
   }
 
@@ -65,7 +76,7 @@
         &:focus,
         &:active {
           outline: none;
-          border-color: #55C9C0;
+          border-color: $primaryActive;
           box-shadow: unset;
         }
       }
@@ -78,20 +89,20 @@
 
       .form-actions {    
         button {
-          background: #55C9C0;
+          background: $primary;
           color: white;
           border-radius: 3px;
           padding: .35rem 1.1rem;
           font-size: 14px;
     
           &:hover {
-            color: #555;
-            background: #5BD8CF;
+            color: $hoverButtonText;
+            background: $primaryLight;
           }
     
           &:active {
-            color: white;
-            background: #D1D70D;
+            color: $activeButtonText;
+            background: $primaryActive;
           }
         }
       }

+ 4 - 2
web/js/react/src/components/CronJob/Generator/OtherSelects/FifthTabSelects.jsx

@@ -1,8 +1,10 @@
 import React from 'react';
-import { dailyMinutesOptions, hoursOptions, dateOptions } from '../../../../ControlPanelService/GeneratorOptions';
+import { useSelector } from 'react-redux';
+import { generatorOptions } from '../../../../ControlPanelService/GeneratorOptions';
 
 export default function FifthTabSelects() {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
+  const { dailyMinutesOptions, hoursOptions, dateOptions } = generatorOptions(i18n);
 
   const renderDate = () => {
     return dateOptions.map((option, index) => <option key={index} value={option.value}>{option.name}</option>);

+ 4 - 2
web/js/react/src/components/CronJob/Generator/OtherSelects/FourthTabSelects.jsx

@@ -1,8 +1,10 @@
 import React from 'react';
-import { dailyMinutesOptions, hoursOptions } from '../../../../ControlPanelService/GeneratorOptions';
+import { useSelector } from 'react-redux';
+import { generatorOptions } from '../../../../ControlPanelService/GeneratorOptions';
 
 export default function FourthTabSelects() {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
+  const { dailyMinutesOptions, hoursOptions } = generatorOptions(i18n);
 
   const renderHours = () => {
     return hoursOptions.map((option, index) => <option key={index} value={option.value}>{option.name}</option>);

+ 4 - 2
web/js/react/src/components/CronJob/Generator/OtherSelects/SecondTabSelects.jsx

@@ -1,8 +1,10 @@
 import React from 'react';
-import { hourlyMinutesOptions } from '../../../../ControlPanelService/GeneratorOptions';
+import { useSelector } from 'react-redux';
+import { generatorOptions } from '../../../../ControlPanelService/GeneratorOptions';
 
 export default function SecondTabSelects() {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
+  const { hourlyMinutesOptions } = generatorOptions(i18n);
 
   const renderOptions = () => {
     return hourlyMinutesOptions.map((option, index) => <option key={index} value={option.value}>{option.name}</option>);

+ 4 - 2
web/js/react/src/components/CronJob/Generator/OtherSelects/ThirdTabSelects.jsx

@@ -1,8 +1,10 @@
 import React from 'react';
-import { dailyMinutesOptions, hoursOptions } from '../../../../ControlPanelService/GeneratorOptions';
+import { useSelector } from 'react-redux';
+import { generatorOptions } from '../../../../ControlPanelService/GeneratorOptions';
 
 export default function ThirdTabSelects() {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
+  const { dailyMinutesOptions, hoursOptions } = generatorOptions(i18n);
 
   const renderHours = () => {
     return hoursOptions.map((option, index) => <option key={index} value={option.value}>{option.name}</option>);

+ 5 - 2
web/js/react/src/components/CronJob/Generator/RunCommandSelect/RunCommandSelect.jsx

@@ -1,8 +1,9 @@
 import React, { useEffect, useState } from 'react';
-import { daysRunCommandsOptions, hoursRunCommandsOptions, minutesRunCommandsOptions, monthlyRunCommandOptions, weeklyRunCommandOptions } from '../../../../ControlPanelService/GeneratorOptions';
+import { useSelector } from 'react-redux';
+import { generatorOptions } from '../../../../ControlPanelService/GeneratorOptions';
 
 const RunCommandSelect = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
 
   const [state, setState] = useState({
     activeTab: ''
@@ -13,6 +14,8 @@ const RunCommandSelect = props => {
   }, [props]);
 
   const renderOptions = () => {
+    const { daysRunCommandsOptions, hoursRunCommandsOptions, minutesRunCommandsOptions, monthlyRunCommandOptions, weeklyRunCommandOptions } = generatorOptions(i18n);
+
     switch (state.activeTab) {
       case '1': return minutesRunCommandsOptions.map(option => <option value={option.value}>{option.name}</option>);
       case '2': return hoursRunCommandsOptions.map(option => <option value={option.value}>{option.name}</option>);

+ 2 - 2
web/js/react/src/components/DNSRecord/Add/AddDNSRecord.jsx

@@ -10,13 +10,13 @@ import { addDomainNameSystemRecord } from '../../../ControlPanelService/Dns';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddDNSRecord.scss'
 import { Helmet } from 'react-helmet';
 
 export default function AddDNSRecord(props) {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const token = localStorage.getItem("token");
   const history = useHistory();

+ 3 - 2
web/js/react/src/components/DNSRecord/DNSRecord.jsx

@@ -3,9 +3,10 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Container from '../ControlPanel/Container/Container';
 import ListItem from '../ControlPanel/ListItem/ListItem';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 export default function DnsRecord({ data, domain, handleModal, ...props }) {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const toggleFav = (starred) => {
@@ -21,7 +22,7 @@ export default function DnsRecord({ data, domain, handleModal, ...props }) {
   }
 
   const handleDelete = () => {
-    handleModal(data.delete_conf, `/delete/dns/?domain=${domain}&record_id=${data.ID}&token=${token}`);
+    handleModal(data.delete_conf, `/api/v1/delete/dns/?domain=${domain}&record_id=${data.ID}`);
   }
 
   return (

+ 5 - 5
web/js/react/src/components/DNSRecord/Edit/EditDNSRecord.jsx

@@ -8,13 +8,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 import { Helmet } from 'react-helmet';
 
 export default function EditDNSRecord(props) {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const history = useHistory();
   const [state, setState] = useState({
@@ -72,13 +72,13 @@ export default function EditDNSRecord(props) {
     }
 
     updatedRecord['v_domain'] = state.data.domain;
-    updatedRecord['v_rec'] = state.data.record;
+    updatedRecord['v_record_id'] = props.record_id;
     updatedRecord['v_type'] = state.data.type;
 
     if (Object.keys(updatedRecord).length !== 0 && updatedRecord.constructor === Object) {
       setState({ ...state, loading: true });
 
-      updateDNS(updatedRecord, state.data.domain)
+      updateDNS(updatedRecord, props.domain, props.record_id)
         .then(result => {
           if (result.status === 200) {
             const { error_msg, ok_msg } = result.data;
@@ -131,7 +131,7 @@ export default function EditDNSRecord(props) {
             <TextInput
               value={state.data.rec}
               title={i18n['Record']}
-              name="v_rec"
+              name="v_record_id"
               id="domain"
               disabled />
 

+ 2 - 2
web/js/react/src/components/Database/Add/AddDatabase.jsx

@@ -8,13 +8,13 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddDatabase.scss'
 import { Helmet } from 'react-helmet';
 
 const AddDatabase = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
   const dispatch = useDispatch();
   const history = useHistory();

+ 4 - 3
web/js/react/src/components/Database/Database.jsx

@@ -3,10 +3,11 @@ import ListItem from '../ControlPanel/ListItem/ListItem';
 import Container from '../ControlPanel/Container/Container';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 const Database = props => {
   const { data } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const toggleFav = (starred) => {
@@ -23,11 +24,11 @@ const Database = props => {
 
   const handleSuspend = () => {
     let suspendedStatus = data.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend' === 'yes' ? 'unsuspend' : 'suspend';
-    props.handleModal(data.suspend_conf, `/${suspendedStatus}/db/?database=${data.NAME}&token=${token}`);
+    props.handleModal(data.suspend_conf, `/api/v1/${suspendedStatus}/db/index.php?database=${data.NAME}`);
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/db/?database=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/db/index.php?database=${data.NAME}`);
   }
 
   return (

+ 2 - 2
web/js/react/src/components/Database/Edit/EditDatabase.jsx

@@ -9,7 +9,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 
 import './EditDatabase.scss';
@@ -17,7 +17,7 @@ import { Helmet } from 'react-helmet';
 
 const EditDatabase = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 2 - 2
web/js/react/src/components/DomainNameSystem/Add/AddDomainNameSystem.jsx

@@ -7,7 +7,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddDomainNameSystem.scss';
 import AdvancedOptions from './AdvancedOptions/AdvancedOptions';
@@ -15,7 +15,7 @@ import { addDomainNameSystem } from '../../../ControlPanelService/Dns';
 import { Helmet } from 'react-helmet';
 
 const AddDomainNameSystem = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const token = localStorage.getItem("token");
   const history = useHistory();

+ 2 - 1
web/js/react/src/components/DomainNameSystem/Add/AdvancedOptions/AdvancedOptions.jsx

@@ -2,9 +2,10 @@ import React, { useEffect, useState } from 'react';
 import dayjs from 'dayjs';
 
 import './AdvancedOptions.scss';
+import { useSelector } from 'react-redux';
 
 const AdvancedOptions = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const [state, setState] = useState({
     nameServersAmount: [],
     userNS: props.userNS

+ 4 - 3
web/js/react/src/components/DomainNameSystem/DomainNameSystem.jsx

@@ -4,10 +4,11 @@ import Container from '../ControlPanel/Container/Container';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import './DomainNameSystem.scss';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 const DomainNameSystem = props => {
   const { data } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const toggleFav = (starred) => {
@@ -24,11 +25,11 @@ const DomainNameSystem = props => {
 
   const handleSuspend = () => {
     let suspendedStatus = data.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend' === 'yes' ? 'unsuspend' : 'suspend';
-    props.handleModal(data.suspend_conf, `/${suspendedStatus}/dns?domain=${data.NAME}&token=${token}`);
+    props.handleModal(data.suspend_conf, `/api/v1/${suspendedStatus}/dns/index.php?domain=${data.NAME}`);
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/dns?domain=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/dns/index.php?domain=${data.NAME}`);
   }
 
   return (

+ 2 - 2
web/js/react/src/components/DomainNameSystem/Edit/EditDomainNameSystem.jsx

@@ -8,7 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 
 import './EditDomainNameSystem.scss';
@@ -16,7 +16,7 @@ import { Helmet } from 'react-helmet';
 
 const EditDomainNameSystem = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 2 - 2
web/js/react/src/components/Firewall/Add/AddFirewall.jsx

@@ -8,14 +8,14 @@ import { addFirewall } from '../../../ControlPanelService/Firewalls';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddFirewall.scss';
 import { Helmet } from 'react-helmet';
 
 const AddFirewall = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const history = useHistory();
   const [state, setState] = useState({

+ 2 - 2
web/js/react/src/components/Firewall/Add/Banlist/index.jsx

@@ -6,11 +6,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from 'src/components/Spinner/Spinner';
 import Toolbar from '../../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import { Helmet } from 'react-helmet';
 
 const AddBanIP = () => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const userLanguage = localStorage.getItem("language");
   const history = useHistory();
   const dispatch = useDispatch();

+ 3 - 2
web/js/react/src/components/Firewall/Ban/index.jsx

@@ -2,9 +2,10 @@ import React from 'react';
 import ListItem from 'src/components/ControlPanel/ListItem/ListItem';
 import Container from 'src/components/ControlPanel/Container/Container';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { useSelector } from 'react-redux';
 
 const Ban = ({ data, ...props }) => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const checkItem = () => {
@@ -12,7 +13,7 @@ const Ban = ({ data, ...props }) => {
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/api/delete/firewall/banlist/?ip=${data.NAME}&chain=${data.CHAIN}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/firewall/banlist/?ip=${data.NAME}&chain=${data.CHAIN}`);
   }
 
   return (

+ 2 - 2
web/js/react/src/components/Firewall/Edit/EditFirewall.jsx

@@ -8,7 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 
 import './EditFirewall.scss';
@@ -16,7 +16,7 @@ import { Helmet } from 'react-helmet';
 
 const EditFirewall = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 4 - 3
web/js/react/src/components/Firewall/Firewall.jsx

@@ -4,10 +4,11 @@ import Container from '../ControlPanel/Container/Container';
 import ListItem from '../ControlPanel/ListItem/ListItem';
 import { Link } from 'react-router-dom';
 import './Firewall.scss';
+import { useSelector } from 'react-redux';
 
 const Firewall = ({ data, ...props }) => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
 
   const toggleFav = (starred) => {
     if (starred) {
@@ -23,11 +24,11 @@ const Firewall = ({ data, ...props }) => {
 
   const handleSuspend = () => {
     let suspendedStatus = data.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend';
-    props.handleModal(data.suspend_conf, `/${suspendedStatus}/firewall/?rule=${data.NAME}&token=${token}`);
+    props.handleModal(data.suspend_conf, `/api/v1/${suspendedStatus}/firewall/index.php?rule=${data.NAME}`);
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/firewall/?rule=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/firewall/index.php?rule=${data.NAME}`);
   }
 
   return (

+ 3 - 3
web/js/react/src/components/ForgotPassword/index.jsx

@@ -13,7 +13,7 @@ import { Helmet } from 'react-helmet';
 import QueryString from 'qs';
 
 export default function ForgotPassword() {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const history = useHistory();
   const [loading, setLoading] = useState(false);
@@ -43,7 +43,7 @@ export default function ForgotPassword() {
       return;
     }
 
-    if (session.token) {
+    if (session.token && session.userName) {
       history.push('/list/user/');
     }
   }, [session]);
@@ -120,7 +120,7 @@ export default function ForgotPassword() {
           <form onSubmit={submitHandler}>
             <div className="c1">
               <Link to="/">
-                <img src="https://r5.vestacp.com:8083/images/vesta_logo.png" alt="Logo" />
+                <img src="/images/logo.png" alt="Logo" />
               </Link>
             </div>
             <div className="c2">

+ 117 - 42
web/js/react/src/components/Hotkeys/Hotkeys.jsx

@@ -1,56 +1,131 @@
-import React from 'react';
-import './Hotkeys.scss'
+import React, { useEffect } from 'react';
+import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { useSelector } from 'react-redux';
+import './Hotkeys.scss';
 
-function style(style) {
-  if (style === "inactive") {
-    return "none";
-  } else {
-    return "block";
-  }
-}
+const Hotkeys = props => {
+  const { i18n } = useSelector(state => state.session);
+
+  useEffect(() => {
+    window.addEventListener("keyup", toggleShortcutsLit);
 
-const Hotkeys = (props) => {
-  const { i18n } = window.GLOBAL.App;
+    return () => window.removeEventListener("keyup", toggleShortcutsLit);
+  }, [props.reference]);
+
+  const toggleShortcutsLit = event => {
+    let isSearchInputFocused = document.querySelector('input:focus') || document.querySelector('textarea:focus');
+
+    if (event.keyCode === 72 && !isSearchInputFocused) {
+      props.toggleHotkeys();
+    }
+  }
 
   return (
-    <div className="panel panel-default" style={{ display: style(props.style) }}>
-      <div className="panel-heading">
-        <h2>Shortcuts</h2>
-        <button type="button" className="close" onClick={props.close} >
-          <span aria-hidden="true">&times;</span>
-        </button>
+    <div className="hotkeys-list hide" ref={props.reference}>
+      <div className="head">
+        <div className="name">{i18n.Shortcuts}</div>
+        <div className="close" onClick={() => props.toggleHotkeys()}><FontAwesomeIcon icon="times" /></div>
       </div>
-      <div className="panel-body">
+      <div className="body">
         <ul>
-          <li><span className="shortcut">u</span> {i18n.Upload}</li>
-          <li><span className="shortcut">n</span> {i18n['New File']}</li>
-          <li><span className="shortcut">F7</span> {i18n['New Folder']}</li>
-          <li><span className="shortcut">d</span> {i18n.Download}</li>
-          <li><span className="shortcut">F2 / Shift + F6</span> {i18n.Rename}</li>
-          <li><span className="shortcut">m</span> {i18n.Move}</li>
-          <li><span className="shortcut">F5</span> {i18n.Copy}</li>
-          <li><span className="shortcut">F8 / Del</span> {i18n.Delete}</li>
-          <li><span className="shortcut">F2</span> {i18n['Save File (in text editor)']}</li>
-          <li><span className="shortcut">h</span> {i18n[['Display/Close shortcuts']]}</li>
-          <li><span className="shortcut">Esc</span> {i18n['Close Popup / Cancel']}</li>
-          <li><span className="shortcut">F10</span> Close Preview / Editor</li>
+          <li>
+            <span className="name">u</span>
+            <span className="description">{i18n['Upload']}</span>
+          </li>
+          <li>
+            <span className="name">n</span>
+            <span className="description">{i18n['New Fille']}</span>
+          </li>
+          <li>
+            <span className="name">F7</span>
+            <span className="description">{i18n['New Folder']}</span>
+          </li>
+          <li>
+            <span className="name">d</span>
+            <span className="description">{i18n['Download']}</span>
+          </li>
+          <li className="space-top">
+            <span className="name">F2 / Shift + F6</span>
+            <span className="description">{i18n['Rename']}</span>
+          </li>
+          <li>
+            <span className="name">m</span>
+            <span className="description">{i18n['Move']}</span>
+          </li>
+          <li>
+            <span className="name">F5</span>
+            <span className="description">{i18n['Copy']}</span>
+          </li>
+          <li>
+            <span className="name">F5</span>
+            <span className="description">{i18n['Copy']}</span>
+          </li>
+          <li>
+            <span className="name">F8 / Del</span>
+            <span className="description">{i18n['Delete']}</span>
+          </li>
+          <li>
+            <span className="name">F2</span>
+            <span className="description">{i18n['Save File (in text editor)']}</span>
+          </li>
+          <li>
+            <span className="name">h</span>
+            <span className="description">{i18n['Display/Close shortcuts']}</span>
+          </li>
+          <li>
+            <span className="name">Esc</span>
+            <span className="description">{i18n['Close Popup / Cancel']}</span>
+          </li>
+          <li>
+            <span className="name">F10</span>
+            <span className="description">{i18n['Close Preview / Editor']}</span>
+          </li>
         </ul>
         <ul>
-          <li><span className="shortcut">&#8593;</span> {i18n['Move Cursor Up']}</li>
-          <li><span className="shortcut">&#8595;</span> {i18n['Move Cursor Down']}</li>
-          <li><span className="shortcut">&#8592;</span> {i18n['Switch to Left Tab']}</li>
-          <li><span className="shortcut">&#8594;</span> {i18n['Switch to Right Tab']}</li>
-          <li><span className="shortcut">a</span> {i18n.Archive}</li>
-          <li><span className="shortcut">Tab</span> {i18n['Switch Tab']}</li>
-          <li><span className="shortcut">Enter</span> {i18n['Open File / Enter Directory']}</li>
-          <li><span className="shortcut">F4</span>{i18n['Edit File']}</li>
-          <li><span className="shortcut">Backspace</span> {i18n['Go to Parent Directory']}</li>
-          <li><span className="shortcut">Ctr + Click</span> {i18n['Add File to the Current Selection']}</li>
-          <li><span className="shortcut">Shift + Cursor up/down</span> {i18n['Select Bunch of Files']}</li>
+          <li>
+            <span className="name">&#8593;</span>
+            <span className="description">{i18n['Move Cursor Up']}</span>
+          </li>
+          <li>
+            <span className="name">&#8595;</span>
+            <span className="description">{i18n['Move Cursor Down']}</span>
+          </li>
+          <li>
+            <span className="name">&#8592;</span>
+            <span className="description">{i18n['Switch to Left Tab']}</span>
+          </li>
+          <li>
+            <span className="name">&#8594;</span>
+            <span className="description">{i18n['Switch to Right Tab']}</span>
+          </li>
+          <li>
+            <span className="name">a</span>
+            <span className="description">{i18n['Archive']}</span>
+          </li>
+          <li>
+            <span className="name">Enter</span>
+            <span className="description">{i18n['Open File / Enter Directory']}</span>
+          </li>
+          <li>
+            <span className="name">F4</span>
+            <span className="description">{i18n['Edit File']}</span>
+          </li>
+          <li>
+            <span className="name">Backspace</span>
+            <span className="description">{i18n['Go to Parent Directory']}</span>
+          </li>
+          <li>
+            <span className="name">Ctr + Click</span>
+            <span className="description">{i18n['Add File to the Current Selection']}</span>
+          </li>
+          <li>
+            <span className="name">Shift + Cursor up/down</span>
+            <span className="description">{i18n['Select Bunch of Files']}</span>
+          </li>
         </ul>
       </div>
     </div>
   );
 }
 
-export default Hotkeys;
+export default Hotkeys;

+ 54 - 48
web/js/react/src/components/Hotkeys/Hotkeys.scss

@@ -1,72 +1,78 @@
-.panel {
-  margin: 0;
-  position: absolute;
-  right: 30%;
+$secondary: #fcac04;
+
+.hotkeys-list {
+  position: fixed;
   bottom: 0;
-  width: 40%;
-  font-size: 15px;
-  background: #38383891;
+  right: 0;
+  display: flex;
+  flex-direction: column;
+  transform: translateX(-45%);
+  width: 53%;
+  background: #222e44de;
+  font-size: 13px;
+
+  .head {
+    display: flex;
+    justify-content: space-between;
+    align-items: center;
+    border-bottom: 1px solid $secondary;
 
-  .panel-heading {
-    background: #474646b0;
-    text-align: center;
-    color: #ffcc00;
-    padding: 10px;
+    .name {
+      text-transform: uppercase;
+      padding: 5px 0 5px 10px;
+      font-size: 12px;
+      font-weight: bold;
+      color: $secondary;
+      letter-spacing: 2px;
+      padding: 15px;
+    }
 
     .close {
-      position: absolute;
-      color: white;
-      right: 10px;
-      top: 10px;
-      opacity: 1;
+      padding: 12px;
+      opacity: 1 !important;
+
+      svg {
+        color: $secondary;
+      }
 
-      &:hover,&:focus{
-        color: #333;
-        outline: none;
-        text-decoration: none;
+      &:hover {
         cursor: pointer;
+        background: #222e44;
       }
     }
   }
 
-  .panel-body {
+  .body {
     display: flex;
-    justify-content: space-between;
-    background: rgba(56,56,56,0.4);
 
     ul {
+      padding: 25px 10px;
+      margin: 0;
       width: 50%;
-      color: white;
+      list-style: none;
+      margin-left: 3rem;
 
       li {
-        list-style-type: none;
-        margin-bottom: 10px;
+        padding: 5px;
 
-        .shortcut {
-          color: #48F4EF;
+        span.name {
+          margin-right: 15px;
+          color: $secondary;
+          font-weight: bold;
         }
+    
+        span.description {
+          color: white;
+        }
+      }
+
+      li.space-top {
+        padding-top: 30px;
       }
     }
   }
 }
 
-.hotkeys-button {
-  color: white;
-  background: rgb(170, 166, 166);
-  position: absolute;
-  right: 5px;
-  bottom: 5px;
-  width: 40px;
-  height: 38px;
-  text-align: center;
-  border-radius: 30px;
-  cursor: pointer;
-
-  svg {
-    margin-top: 7px;
-  }
-
-  &:hover {
-    background: #aacc0d;
-  }
+.hide {
+  display: none;
 }

+ 1 - 1
web/js/react/src/components/InternetProtocol/Add/AddInternetProtocol.jsx

@@ -17,7 +17,7 @@ import { Helmet } from 'react-helmet';
 
 const AddInternetProtocol = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const session = useSelector(state => state.session);
   const dispatch = useDispatch();
   const history = useHistory();

+ 2 - 2
web/js/react/src/components/InternetProtocol/Edit/EditInternetProtocol.jsx

@@ -10,7 +10,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 
 import './EditInternetProtocol.scss';
@@ -18,7 +18,7 @@ import { Helmet } from 'react-helmet';
 
 const EditInternetProtocol = () => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 3 - 2
web/js/react/src/components/InternetProtocol/InternetProtocol.jsx

@@ -4,10 +4,11 @@ import Container from '../ControlPanel/Container/Container';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import './InternetProtocol.scss';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 const InternetProtocol = props => {
   const { data } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
 
   const toggleFav = (starred) => {
@@ -23,7 +24,7 @@ const InternetProtocol = props => {
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/ip/?ip=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/ip/?ip=${data.NAME}`);
   }
 
   return (

+ 1 - 1
web/js/react/src/components/Lists/DirectoryList/DirectoryList.jsx

@@ -70,7 +70,7 @@ class DirectoryList extends Component {
   }
 
   isHomeDirectory = () => {
-    return this.props.path === window.GLOBAL.ROOT_DIR;
+    return this.props.path === this.props.rootDir;
   }
 
   toggleActiveList = () => {

+ 9 - 3
web/js/react/src/components/Lists/Row/Row.jsx

@@ -4,6 +4,7 @@ import PropTypes from 'prop-types';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import { faJs, faCss3, faPhp, faHtml5, faSass } from '@fortawesome/free-brands-svg-icons';
 import './Row.scss';
+import { connect } from 'react-redux';
 
 class Row extends Component {
   static propTypes = {
@@ -121,9 +122,8 @@ class Row extends Component {
 
     let date = new Date(fDate),
       months = ['Jan', 'Feb', 'Mar', 'Apr', 'May', 'Jun', 'Jul', 'Aug', 'Sep', 'Oct', 'Nov', 'Dec'],
-      appMonths = window.GLOBAL.App.i18n,
       getDay = date.getDate(),
-      getMonth = appMonths[months[date.getMonth()]];
+      getMonth = this.props.session.i18n[months[date.getMonth()]];
     return (<span className="date">{getMonth} {getDay}</span>);
   }
 
@@ -188,4 +188,10 @@ class Row extends Component {
   }
 }
 
-export default withRouter(Row);
+function mapStateToProps(state) {
+  return {
+    session: state.session
+  }
+}
+
+export default connect(mapStateToProps)(withRouter(Row));

+ 13 - 2
web/js/react/src/components/Lists/Row/Row.scss

@@ -1,3 +1,14 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$textColor: #555;
+
 .list .list-container ul li svg {
   width: 25px;
   vertical-align: top;
@@ -126,7 +137,7 @@
 }
 
 .list .list-container ul li .fOwner {
-  color: #81A64F;
+  color: #896417;
   font-style: italic;
   width: 50px;
   font-size: 12px;
@@ -134,7 +145,7 @@
 }
 
 .value {
-  color: #44a8b3;
+  color: $primary;
 }
 
 .unit {

+ 28 - 7
web/js/react/src/components/Login/Login.scss

@@ -1,3 +1,14 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$textColor: #555;
+
 .login-page {
   display: flex;
   justify-content: center;
@@ -45,11 +56,15 @@
         display: flex;
         justify-content: center;
         align-items: center;
-        width: 30%;
+        width: 35%;
+
+        img {
+          width: 110%;
+        }
       }
 
       .c2 {
-        width: 70%;
+        width: 65%;
         margin-left: 3.5rem;
 
         .forgot-password {
@@ -60,8 +75,8 @@
         }
 
         button[type="submit"] {
-          background-color: #9FBF0C;
-          border: 1px solid #9FBF0C;
+          background-color: $primary;
+          border: 1px solid $primary;
           padding: 1px 16px 3px;
           font-size: 13px;;
           width: 100px;
@@ -70,9 +85,15 @@
           border-radius: 3px;
 
           &:hover {
-            border: 1px solid #C0E60F;
-            background-color: #C0E60F;
-            color: #555;
+            color: $primary;
+            border: 1px solid $primaryLight;
+            background-color: $primaryLight;
+          }
+
+          &:active {
+            color: #fafafa;
+            border: 1px solid $primary;
+            background-color: $primary;
           }
 
           .disabled {

+ 8 - 8
web/js/react/src/components/Login/LoginForm.jsx

@@ -11,7 +11,7 @@ import './Login.scss';
 import { Helmet } from 'react-helmet';
 
 export default function LoginForm() {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const history = useHistory();
   const [loading, setLoading] = useState(false);
@@ -28,7 +28,7 @@ export default function LoginForm() {
       return;
     }
 
-    if (session.token) {
+    if (session.token && session.userName) {
       history.push('/list/user/');
     }
   }, [session]);
@@ -57,7 +57,7 @@ export default function LoginForm() {
   return (
     <div className="login-page">
       <Helmet>
-        <title>{`Vesta - ${i18n.LOGIN}`}</title>
+        <title>{`Vesta - ${i18n.LOGIN ?? 'LOGIN'}`}</title>
       </Helmet>
       {loading && <Spinner />}
       <div className="login-form-wrapper">
@@ -65,24 +65,24 @@ export default function LoginForm() {
           <form onSubmit={submitHandler}>
             <div className="c1">
               <Link to="/">
-                <img src="https://r5.vestacp.com:8083/images/vesta_logo.png" alt="Logo" />
+                <img src="/images/logo.png" alt="Logo" />
               </Link>
             </div>
             <div className="c2">
               <TextInput
                 onChange={changeInputHandler}
-                title={i18n['Username']}
+                title={i18n['Username'] ?? 'Username'}
                 value={formValues.user}
                 name="user"
                 id="user" />
 
               <TextInput
                 onChange={changeInputHandler}
-                title={i18n['Password']}
+                title={i18n['Password'] ?? 'Password'}
                 value={formValues.password}
                 optionalTitle={(
                   <Link className="forgot-password" tabIndex="-1" to="/reset">
-                    {i18n['forgot password']}
+                    {i18n['forgot password'] ?? 'forgot password'}
                   </Link>
                 )}
                 name="password"
@@ -90,7 +90,7 @@ export default function LoginForm() {
                 id="password" />
 
               <button type="submit" disabled={loading} className={loading ? 'disabled' : ''}>
-                {i18n['Log in']}
+                {i18n['Log in'] ?? 'Log in'}
               </button>
 
               <div className="error-message">{errorMessage}</div>

+ 2 - 2
web/js/react/src/components/Mail/Add/AddMail.jsx

@@ -7,13 +7,13 @@ import { addMail } from '../../../ControlPanelService/Mail';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddMail.scss'
 import { Helmet } from 'react-helmet';
 
 const AddMail = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const token = localStorage.getItem("token");
   const history = useHistory();

+ 2 - 2
web/js/react/src/components/Mail/Edit/EditMail.jsx

@@ -8,7 +8,7 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Spinner from '../../../components/Spinner/Spinner';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import QS from 'qs';
 
 import './EditMail.scss';
@@ -16,7 +16,7 @@ import { Helmet } from 'react-helmet';
 
 const EditMail = props => {
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const history = useHistory();
   const dispatch = useDispatch();
   const [state, setState] = useState({

+ 4 - 3
web/js/react/src/components/Mail/Mail.jsx

@@ -4,10 +4,11 @@ import Container from '../ControlPanel/Container/Container';
 import ListItem from '../ControlPanel/ListItem/ListItem';
 import { Link } from 'react-router-dom';
 import './Mail.scss';
+import { useSelector } from 'react-redux';
 
 const Mail = props => {
   const { data } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
   const printStat = (stat, text) => {
     if (text === 'no') {
@@ -31,11 +32,11 @@ const Mail = props => {
 
   const handleSuspend = () => {
     let suspendedStatus = data.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend' === 'yes' ? 'unsuspend' : 'suspend';
-    props.handleModal(data.suspend_conf, `/${suspendedStatus}/mail?domain=${data.NAME}&token=${token}`);
+    props.handleModal(data.suspend_conf, `/api/v1/${suspendedStatus}/mail/index.php?domain=${data.NAME}`);
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/mail?domain=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/mail/index.php?domain=${data.NAME}`);
   }
 
   return (

+ 2 - 2
web/js/react/src/components/MailAccount/Add/AddMailAccount.jsx

@@ -13,13 +13,13 @@ import MailInfoBlock from '../MailInfoBlock/MailInfoBlock';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 
 import './AddMailAccount.scss';
 import { Helmet } from 'react-helmet';
 
 export default function AddMailAccount(props) {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const token = localStorage.getItem("token");
   const history = useHistory();

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

@@ -6,20 +6,20 @@ import TextInput from 'src/components/ControlPanel/AddItemLayout/Form/TextInput/
 import Password from 'src/components/ControlPanel/AddItemLayout/Form/Password/Password';
 import TextArea from 'src/components/ControlPanel/AddItemLayout/Form/TextArea/TextArea';
 import Checkbox from 'src/components/ControlPanel/AddItemLayout/Form/Checkbox/Checkbox';
-import { addMailAccount, editMailAccount, getMailAccountInfo } from '../../../ControlPanelService/Mail';
+import { editMailAccount, getMailAccountInfo } from '../../../ControlPanelService/Mail';
 import AddItemLayout from '../../ControlPanel/AddItemLayout/AddItemLayout';
 import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import MailInfoBlock from '../MailInfoBlock/MailInfoBlock';
 import Toolbar from '../../MainNav/Toolbar/Toolbar';
 import { useHistory } from 'react-router-dom';
 import Spinner from '../../Spinner/Spinner';
-import { useDispatch } from 'react-redux';
+import { useDispatch, useSelector } from 'react-redux';
 import { Helmet } from 'react-helmet';
 
 export default function EditMailAccount(props) {
   const [autoreplyChecked, setAutoreplyChecked] = useState(false);
   const token = localStorage.getItem("token");
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const dispatch = useDispatch();
   const history = useHistory();
   const [state, setState] = useState({

+ 4 - 3
web/js/react/src/components/MailAccount/MailAccount.jsx

@@ -3,10 +3,11 @@ import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
 import Container from '../ControlPanel/Container/Container';
 import ListItem from '../ControlPanel/ListItem/ListItem';
 import { Link } from 'react-router-dom';
+import { useSelector } from 'react-redux';
 
 export default function MailAccount(props) {
   const { data, domain } = props;
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const token = localStorage.getItem("token");
   const printStat = (stat, text) => {
     if (text === 'no') {
@@ -30,11 +31,11 @@ export default function MailAccount(props) {
 
   const handleSuspend = () => {
     let suspendedStatus = data.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend' === 'yes' ? 'unsuspend' : 'suspend';
-    props.handleModal(data.suspend_conf, `/${suspendedStatus}/mail?domain=${domain}&account=${data.NAME}&token=${token}`);
+    props.handleModal(data.suspend_conf, `/api/v1/${suspendedStatus}/mail/index.php?domain=${domain}&account=${data.NAME}`);
   }
 
   const handleDelete = () => {
-    props.handleModal(data.delete_conf, `/delete/mail?domain=${domain}&account=${data.NAME}&token=${token}`);
+    props.handleModal(data.delete_conf, `/api/v1/delete/mail/index.php?domain=${domain}&account=${data.NAME}`);
   }
 
   return (

+ 3 - 2
web/js/react/src/components/MailAccount/MailInfoBlock/MailInfoBlock.jsx

@@ -5,7 +5,7 @@ import { mailInfoBlockSelectOptions } from 'src/ControlPanelService/Mail';
 import './MailInfoBlock.scss';
 
 export default function MailInfoBlock({ webMail, hostName, domain, password }) {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n } = useSelector(state => state.session);
   const [selectedOption, setSelectedOption] = useState('');
   const { userName } = useSelector(state => state.session);
   const [state, setState] = useState({
@@ -40,7 +40,8 @@ export default function MailInfoBlock({ webMail, hostName, domain, password }) {
   }, [selectedOption]);
 
   const renderSelectOptions = () => {
-    return mailInfoBlockSelectOptions.map(option =>
+    const options = mailInfoBlockSelectOptions(i18n);
+    return options.map(option =>
       <option key={option.type} value={option.type}>{option.value}</option>
     );
   }

+ 34 - 11
web/js/react/src/components/MainNav/MainNav.jsx

@@ -6,17 +6,34 @@ import MobileTopNav from '../MainNav/Mobile/MobileTopNav';
 import Menu from '../MainNav/Stat-menu/Menu';
 import Panel from '../MainNav/Panel/Panel';
 import './MainNav.scss';
+import { useHistory } from 'react-router';
+import Spinner from '../Spinner/Spinner';
 
-const MainNav = props => {
+const MainNav = () => {
+  const history = useHistory();
+  const [loading, setLoading] = useState(true);
   const [state, setState] = useState({
     menuHeight: 135,
+    tabs: [],
     showTopNav: false
   });
 
-  const { activeElement, focusedElement, menuTabs } = useSelector(state => state.mainNavigation);
+  const { userName, user, session: { look } } = useSelector(state => state.session);
+  const { activeElement, focusedElement, adminMenuTabs, userMenuTabs } = useSelector(state => state.mainNavigation);
   const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
   const dispatch = useDispatch();
 
+  useEffect(() => {
+    if (!userName || !Object.entries(user).length) {
+      return history.push('/login');
+    }
+
+    const tabs = look ? userMenuTabs : adminMenuTabs;
+    setState({ ...state, tabs });
+
+    setLoading(false);
+  }, [userName, user, history]);
+
   const controlFocusedTabWithCallback = useCallback(event => {
     let isSearchInputFocused = document.querySelector('input:focus') || document.querySelector('textarea:focus') || document.querySelector('textarea:focus');
     let currentActiveTabPositionInArray;
@@ -32,9 +49,9 @@ const MainNav = props => {
 
       if (!focusedElement) {
         dispatch(addFocusedElement(activeElement));
-        currentActiveTabPositionInArray = menuTabs.indexOf(activeElement);
+        currentActiveTabPositionInArray = state.tabs.indexOf(activeElement);
       } else {
-        currentActiveTabPositionInArray = menuTabs.indexOf(focusedElement);
+        currentActiveTabPositionInArray = state.tabs.indexOf(focusedElement);
       }
     }
 
@@ -43,14 +60,14 @@ const MainNav = props => {
     }
 
     if (event.keyCode === 37) {
-      let newFocusedMenuTab = handleLeftArrowKey(menuTabs, currentActiveTabPositionInArray);
+      let newFocusedMenuTab = handleLeftArrowKey(state.tabs, currentActiveTabPositionInArray);
       dispatch(addFocusedElement(newFocusedMenuTab));
     } else if (event.keyCode === 39) {
-      let newFocusedMenuTab = handleRightArrowKey(menuTabs, currentActiveTabPositionInArray);
+      let newFocusedMenuTab = handleRightArrowKey(state.tabs, currentActiveTabPositionInArray);
       dispatch(addFocusedElement(newFocusedMenuTab));
     } else if (event.keyCode === 13) {
-      if (!controlPanelFocusedElement && focusedElement) {
-        props.history.push({ pathname: focusedElement });
+      if (!controlPanelFocusedElement && focusedElement && (focusedElement !== activeElement)) {
+        history.push({ pathname: focusedElement });
         dispatch(addActiveElement(focusedElement));
         dispatch(removeFocusedElement());
       }
@@ -74,7 +91,7 @@ const MainNav = props => {
   }, [activeElement]);
 
   useEffect(() => {
-    dispatch(addActiveElement(props.history.location.pathname));
+    dispatch(addActiveElement(history.location.pathname));
   }, []);
 
   const handleLeftArrowKey = (array, indexInArray) => {
@@ -149,8 +166,14 @@ const MainNav = props => {
 
   return (
     <div className="main-nav">
-      <Panel showTopNav={showTopNav} visibleNav={state.showTopNav} />
-      {topNavigation()}
+      {
+        loading
+          ? <Spinner />
+          : (<>
+            <Panel showTopNav={showTopNav} visibleNav={state.showTopNav} />
+            {topNavigation()}
+          </>)
+      }
     </div>
   );
 }

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

@@ -1,3 +1,5 @@
+$secondaryLight: #f8b014;
+
 .mobile-top-nav-wrapper.hide {
   opacity: 0;
 }
@@ -58,7 +60,7 @@
         padding: 2px;
 
         &:hover {
-          color: #ff6701;
+          color: $secondaryLight;
         }
       }
     }

+ 9 - 0
web/js/react/src/components/MainNav/Panel/Notifications/Bell.jsx

@@ -0,0 +1,9 @@
+import React from 'react';
+
+export default function Bell(props) {
+  return (
+    <svg width="24px" height="24px" viewBox="0 0 24 24" id="_24x24_On_Light_Notification" data-name="24x24/On Light/Notification" xmlns="http://www.w3.org/2000/svg">
+      <rect id="view-box" width="24" height="24" fill="none" />
+      <path id="Shape" d="M6,17v-.5H2.25A2.253,2.253,0,0,1,0,14.25v-.382a2.542,2.542,0,0,1,1.415-2.289A1.248,1.248,0,0,0,2.1,10.572l.446-4.91a6.225,6.225,0,0,1,12.4,0l.446,4.91a1.26,1.26,0,0,0,.686,1.005,2.547,2.547,0,0,1,1.418,2.29v.382a2.252,2.252,0,0,1-2.25,2.25H11.5V17A2.75,2.75,0,0,1,6,17Zm1.5,0A1.25,1.25,0,0,0,10,17v-.5H7.5ZM4.045,5.8,3.6,10.708A2.738,2.738,0,0,1,2.089,12.92a1.055,1.055,0,0,0-.589.949v.382A.751.751,0,0,0,2.25,15h13A.751.751,0,0,0,16,14.25v-.382a1.054,1.054,0,0,0-.586-.948A2.739,2.739,0,0,1,13.9,10.708L13.456,5.8a4.725,4.725,0,0,0-9.411,0Z" transform="translate(3.25 2.25)" fill="#f8b014" />
+    </svg>);
+}

+ 9 - 0
web/js/react/src/components/MainNav/Panel/Notifications/BellUnread.jsx

@@ -0,0 +1,9 @@
+import React from 'react';
+
+export default function BellUnread(props) {
+  return (<svg width="24px" height="24px" viewBox="0 0 24 24" id="_24x24_On_Light_Notification-Alert" data-name="24x24/On Light/Notification-Alert" xmlns="http://www.w3.org/2000/svg">
+    <rect id="view-box" width="24" height="24" fill="none" />
+    <path id="Shape" d="M6,17v-.5H2.25A2.253,2.253,0,0,1,0,14.25v-.382a2.542,2.542,0,0,1,1.415-2.289A1.247,1.247,0,0,0,2.1,10.572l.446-4.91A6.227,6.227,0,0,1,10.618.286a5.477,5.477,0,0,0-.635,1.374A4.794,4.794,0,0,0,8.75,1.5,4.7,4.7,0,0,0,4.045,5.8L3.6,10.708A2.739,2.739,0,0,1,2.089,12.92a1.055,1.055,0,0,0-.589.949v.382A.751.751,0,0,0,2.25,15h13A.751.751,0,0,0,16,14.25v-.382a1.053,1.053,0,0,0-.586-.948A2.739,2.739,0,0,1,13.9,10.708l-.2-2.18a5.473,5.473,0,0,0,1.526.221l.166,1.822a1.26,1.26,0,0,0,.686,1.005,2.547,2.547,0,0,1,1.418,2.29v.382a2.252,2.252,0,0,1-2.25,2.25H11.5V17A2.75,2.75,0,0,1,6,17Zm1.5,0A1.25,1.25,0,0,0,10,17v-.5H7.5ZM15.047,6.744A3.486,3.486,0,0,1,13.5,6.28L13.456,5.8a4.7,4.7,0,0,0-1.648-3.185,3.5,3.5,0,0,1,.61-1.417A6.221,6.221,0,0,1,14.95,5.662l.1,1.081v0Z" transform="translate(3.25 2.25)" fill="#f8b014" />
+    <path id="Shape-2" data-name="Shape" d="M3.5,7A3.5,3.5,0,1,1,7,3.5,3.5,3.5,0,0,1,3.5,7Z" transform="translate(15 2)" fill="#f8b014" />
+  </svg>);
+}

+ 28 - 17
web/js/react/src/components/MainNav/Panel/Notifications/Notifications.jsx

@@ -1,31 +1,38 @@
 import React, { useState, useEffect } from 'react';
-import { getAppNotifications, deleteNotification } from '../../../../ControlPanelService/Notifications';
-import { FontAwesomeIcon } from '@fortawesome/react-fontawesome';
+import { getAppNotifications, deleteNotification } from 'src/ControlPanelService/Notifications';
+import { addNotifications } from 'src/actions/Notification/notificationActions';
+import Bell from './Bell';
+import BellUnread from './BellUnread';
+import { useDispatch, useSelector } from 'react-redux';
 import './Notifications.scss';
 
 const Notifications = () => {
-  const [notifications, setNotifications] = useState([]);
-  const [loading, setLoading] = useState(true);
+  const { i18n } = useSelector(state => state.session);
+  const { notifications } = useSelector(state => state.notifications);
+  const dispatch = useDispatch();
+  const [loading, setLoading] = useState(false);
 
   useEffect(() => {
     fetchData();
   }, []);
 
   const fetchData = () => {
+    setLoading(true);
     getAppNotifications()
       .then(res => {
-        if (res.data) {
-          const result = [];
+        const result = [];
 
-          for (let notification in res.data) {
-            result.push(res.data[notification]);
-          }
-
-          setNotifications(result);
-          setLoading(false);
+        for (let notification in res.data.result) {
+          result.push(res.data.result[notification]);
         }
+
+        dispatch(addNotifications(result));
+        setLoading(false);
+      })
+      .catch(err => {
+        console.error(err);
+        setLoading(false);
       })
-      .catch(err => console.error(err))
   }
 
   const removeNotification = id => {
@@ -40,20 +47,20 @@ const Notifications = () => {
     if (notifications.length) {
       return notifications.map(item => {
         return (
-          <React.Fragment>
+          <>
             <div className="dropdown-item">
               <span className="title"><b>{item.TOPIC}</b></span>
               <span className="delete-notification" onClick={() => removeNotification(item.ID)}></span>
             </div>
             <div dangerouslySetInnerHTML={{ __html: item.NOTICE }}></div>
             <div className="dropdown-divider"></div>
-          </React.Fragment>
+          </>
         );
       });
     } else {
       return (
         <div className="dropdown-item" style={{ cursor: 'default', marginBottom: '10' }}>
-          <span className="title">{window.GLOBAL.App.Constants.NOTIFICATIONS_EMPTY}</span>
+          <span className="title">{i18n['no notifications']}</span>
         </div>
       );
     }
@@ -63,7 +70,11 @@ const Notifications = () => {
     <div className="btn-group">
       <button type="button" className="btn btn-danger dropdown-toggle" data-toggle="dropdown" aria-haspopup="true" aria-expanded="false">
         <div className="bell">
-          <FontAwesomeIcon icon="bell" />
+          {
+            notifications.length
+              ? <BellUnread />
+              : <Bell />
+          }
         </div>
       </button>
       <div className="dropdown-menu">

+ 24 - 7
web/js/react/src/components/MainNav/Panel/Notifications/Notifications.scss

@@ -1,3 +1,17 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$notifications: #e49d45;
+$notificationsLight: #de9234;
+$notificationsActive: #6f4e2b;
+$textColor: #555;
+
 .top-panel .profile-menu {
   div {
     width: auto;
@@ -31,14 +45,17 @@
     border-color: unset;
     box-shadow: unset;
   }
-  
+
   .dropdown-menu {
     display: none;
     height: auto;
     max-height: 75vh;
     overflow: auto;
     cursor: default;
-    background: #454545;
+    background: #222e44;
+    border: 1px solid #fcac04;
+    border-width: 1px 0 0 0;
+    box-shadow: rgb(34, 46, 68) 0px 0px 10px -1px;
 
     > div {
       cursor: default;
@@ -65,18 +82,18 @@
         width: 100%;
         padding: 10px 0 0 10px;
         text-align: left;
-        color: #C4DA5E;
+        color: $secondary;
       }
 
       span.delete-notification {
         width: 10px;
         height: 10px;
-        background: #C4DA5E;
+        background: $secondary;
         border-radius: 50%;
         cursor: pointer;
 
         &:hover {
-          border: 2px solid #C4DA5E;
+          border: 2px solid $secondaryLight;
           background: transparent;
         }
       }
@@ -94,10 +111,10 @@
 
       a {
         display: contents;
-        color: #5ABDB5;
+        color: $notifications;
 
         &:hover {
-          color: #2CA99B;
+          color: $notificationsLight;
         }
       }
     }

+ 52 - 30
web/js/react/src/components/MainNav/Panel/Panel.jsx

@@ -9,7 +9,7 @@ import { Link, useHistory } from "react-router-dom";
 import './Panel.scss';
 
 const Panel = props => {
-  const { i18n } = window.GLOBAL.App;
+  const { i18n, userName, session: { look, user } } = useSelector(state => state.session);
   const session = useSelector(state => state.session);
   const { activeElement, focusedElement } = useSelector(state => state.mainNavigation);
   const dispatch = useDispatch();
@@ -29,7 +29,7 @@ const Panel = props => {
     }
   }
 
-  const className = activeName => {
+  const className = (activeName, extraClass = '') => {
     let className = 'top-link';
 
     if (activeName === activeElement) {
@@ -40,12 +40,14 @@ const Panel = props => {
       className += ' focus';
     }
 
-    return className;
+    return className + ` ${extraClass}`;
   }
 
   const handleState = (tab, event) => {
-    event.preventDefault();
-    history.push(tab);
+    if (`${window.location.pathname}${window.location.search}` === tab) {
+      return event.preventDefault();
+    }
+
     dispatch(addActiveElement(tab));
   }
 
@@ -67,45 +69,65 @@ const Panel = props => {
     <div className="panel-wrapper">
       {loading && <Spinner />}
 
-      <div className="top-panel">
+      <div className={`top-panel ${user ? 'long-profile' : ''}`}>
         <div className="container left-menu">
           <div className="logo">
             <Link to="/list/user/" onClick={() => dispatch(addActiveElement('/list/user/'))}>
-              <div className="logo-img"></div>
+              <div>
+                <img src="/images/white_logo.png" alt="Logo" />
+              </div>
             </Link>
           </div>
-          <div className={className("/list/package/")}>
-            <button onClick={event => handleState("/list/package/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Packages}</button>
-          </div>
-          <div className={className("/list/ip/")}>
-            <button onClick={event => handleState("/list/ip/", event)} onKeyPress={event => event.preventDefault()}>{i18n.IP}</button>
-          </div>
-          <div className={className("/list/rrd/")}>
-            <button onClick={event => handleState("/list/rrd/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Graphs}</button>
-          </div>
+          {userName === 'admin' && (<>
+            <div className={className("/list/package/")}>
+              <Link to="/list/package/" onClick={event => handleState("/list/package/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Packages}</Link>
+            </div>
+            <div className={className("/list/ip/")}>
+              <Link to="/list/ip/" onClick={event => handleState("/list/ip/", event)} onKeyPress={event => event.preventDefault()}>{i18n.IP}</Link>
+            </div>
+            <div className={className("/list/rrd/")}>
+              <Link to="/list/rrd/" onClick={event => handleState("/list/rrd/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Graphs}</Link>
+            </div>
+          </>)}
           <div className={className("/list/stats/")}>
-            <button onClick={event => handleState("/list/stats/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Statistics}</button>
+            <Link to="/list/stats/" onClick={event => handleState("/list/stats/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Statistics}</Link>
           </div>
           <div className={className("/list/log/")}>
-            <button onClick={event => handleState("/list/log/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Log}</button>
+            <Link to="/list/log/" onClick={event => handleState("/list/log/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Log}</Link>
           </div>
-          <div className={className("/list/updates/")}>
-            <button onClick={event => handleState("/list/updates/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Updates}</button>
-          </div>
-          {session.session.FIREWALL_SYSTEM && <div className={className("/list/firewall/")}>
-            <button onClick={event => handleState("/list/firewall/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Firewall}</button>
-          </div>}
-          {session.session.FILEMANAGER_KEY && <div className="fm">
-            <a href="/list/directory/">{i18n['File Manager']}</a>
+          {userName === 'admin' && (<>
+            <div className={className("/list/updates/")}>
+              <Link to="/list/updates/" onClick={event => handleState("/list/updates/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Updates}</Link>
+            </div>
+            {session.session.FIREWALL_SYSTEM && <div className={className("/list/firewall/")}>
+              <Link to="/list/firewall/" onClick={event => handleState("/list/firewall/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Firewall}</Link>
+            </div>}
+          </>)}
+          {session.session.FILEMANAGER_KEY && <div className={className("/list/directory/", "fm")}>
+            <Link to="/list/directory/">{i18n['File Manager']}</Link>
           </div>}
-          {session.session.SOFTACULOUS === "yes" && <div><a href="/list/softaculous/">{i18n.Apps}</a>
+          {session.session.SOFTACULOUS === "yes" && <div className={className("/softaculous/")}><a href="/softaculous/">{i18n.Apps ?? 'Apps'}</a>
           </div>}
-          <div className={className("/list/server/")}>
-            <button onClick={event => handleState("/list/server/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Server}</button></div>
+          {userName === 'admin' && (
+            <div className={className("/list/server/")}>
+              <Link to="/list/server/" onClick={event => handleState("/list/server/", event)} onKeyPress={event => event.preventDefault()}>{i18n.Server}</Link>
+            </div>
+          )}
         </div>
         <div className="container profile-menu">
           <Notifications />
-          <div><Link to={`/edit/user?user=${session.userName}`}>{session.userName}</Link></div>
+          <div>
+            <Link to={`/edit/user?user=${session.userName}`}>
+              {look
+                ? <div className="long-username">
+                  <span>{user}</span>
+                  <FontAwesomeIcon icon="long-arrow-alt-right" />
+                  <span>{look}</span>
+                </div>
+                : session.userName
+              }
+            </Link>
+          </div>
           <div><button onClick={signOut}>{i18n['Log out']}</button></div>
         </div>
       </div>

+ 64 - 25
web/js/react/src/components/MainNav/Panel/Panel.scss

@@ -1,3 +1,14 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$textColor: #555;
+
 .top-panel.small-device {
   display: none;
 }
@@ -10,7 +21,7 @@
   width: 100%;
   text-align: center;
   color: white;
-  background: #111;
+  background: #222e44;
   height: 34px;
   align-items: center;
   padding: 0 13%;
@@ -22,13 +33,10 @@
     width: 80%;
     height: 100%;
 
-    .logo .logo-img {
-      background-image: url("/images/sprite.png?1446554103");
-      background-position: -117px -57px;
-      background-repeat: no-repeat;
-      height: 22px;
-      width: 70px;
-      margin-left: -2px;
+    .logo div {
+      img {
+        width: 82%;
+      }
 
       &:hover {
         background-color: transparent;
@@ -65,7 +73,7 @@
       background: white;
       
       a, button {
-        color: #f79b44;
+        color: $secondary;
         font-weight: bold;
 
         &:hover {
@@ -73,13 +81,13 @@
         }
 
         &:active {
-          background: #ff6701;
+          background: $secondaryActive;
           color: white;
         }
       }
 
       &:hover {
-        background: #f79b44;
+        background: $secondaryLight;
       }
     }
   }
@@ -91,9 +99,11 @@
     div {
       width: 7rem;
       height: 100%;
+      transform: translateX(-5px);
+      padding-right: 5px;
 
       &:hover {
-        background: #f79b44;
+        background: $secondaryLight;
       }
     }
 
@@ -113,8 +123,7 @@
 
     .top-link.focus {
       a, button {
-        color: #5edad0;
-        text-decoration: underline;
+        color: $secondaryActive;
       }
     }
   }
@@ -129,20 +138,21 @@
 
     div.bell {
       width: auto;
-      color: #C0E60E;
+      color: $secondary;
+      padding: 3px 0;
 
       svg {
         border-radius: 30px;
-        width: 25px;
-        height: 25px;
-        padding: 5px;
+        width: 100%;
+        height: 100%;
+        padding: 3px;
 
         &:hover {
-          background: rgba(255, 255, 255, 0.4);
+          background: #79522294;
         }
 
-        &:hover {
-          background: #c0e6198a;
+        &:active {
+          background: #866032;
         }
       }
     }
@@ -152,11 +162,11 @@
       font-weight: 700;
     
       &:hover {
-        color: #ffd62e;
+        color: $secondaryLight;
       }
 
       &:active {
-        color: #f79b44;
+        color: $secondaryActive;
       }
     }
 
@@ -167,11 +177,40 @@
       font-weight: 100;
 
       &:hover {
-        color: #C0E60E;
+        color: $secondaryLight;
       }
 
       &:active {
-        color: #ffd62e;
+        color: $secondaryActive;
+      }
+    }
+  }
+}
+
+.top-panel.long-profile {
+  .left-menu {
+    width: 75%;
+  }
+
+  .profile-menu {
+    width: 25%;
+
+    > div + div {
+      width: max-content;
+    }
+
+    .long-username {
+      display: flex;
+      justify-content: center;
+      align-items: center;
+      width: auto;
+
+      > span:nth-child(1) {
+        margin-right: 5px;
+      }
+
+      > span:nth-child(3) {
+        margin-left: 5px;
       }
     }
   }

+ 46 - 24
web/js/react/src/components/MainNav/Stat-menu/Menu.jsx

@@ -1,7 +1,7 @@
 import React, { useEffect } from 'react';
 import { addActiveElement } from '../../../actions/MainNavigation/mainNavigationActions';
 import { useSelector, useDispatch } from "react-redux";
-import { useHistory } from "react-router-dom";
+import { Link } from "react-router-dom";
 
 import './Menu.scss';
 
@@ -26,12 +26,9 @@ const style = ({ menuHeight, mobile }) => {
 }
 
 const Menu = props => {
-  const session = useSelector(state => state.session);
   const { activeElement, focusedElement } = useSelector(state => state.mainNavigation);
-  const { user } = useSelector(state => state.session);
-  const { i18n } = window.GLOBAL.App;
+  const { user, i18n, session: { look } } = useSelector(state => state.session);
   const dispatch = useDispatch();
-  const history = useHistory();
 
   useEffect(() => {
     if (user.LANGUAGE) {
@@ -40,8 +37,10 @@ const Menu = props => {
   }, [user]);
 
   const handleState = (tab, event) => {
-    event.preventDefault();
-    history.push(tab);
+    if (`${window.location.pathname}${window.location.search}` === tab) {
+      return event.preventDefault();
+    }
+
     dispatch(addActiveElement(tab));
   }
 
@@ -49,77 +48,100 @@ const Menu = props => {
     return `stat ${activeName === activeElement && 'l-active'} ${activeName === focusedElement && 'focus'}`;
   }
 
+  const sizeFormatter = (bytes, decimals) => {
+    if (!bytes) return null;
+
+    if (bytes === "0") {
+      return <span className="value">0 <span className="unit">b</span></span>;
+    }
+
+    let k = 1024,
+      dm = decimals <= 0 ? 0 : decimals || 2,
+      sizes = ['b', 'kb', 'Mb', 'GB'],
+      i = Math.floor(Math.log(bytes) / Math.log(k));
+    return (<span className="value">{parseFloat((bytes / Math.pow(k, i)).toFixed(dm))} <span className="unit">{sizes[i]}</span></span>);
+  }
+
   return (
     <div className="menu-wrapper">
       <div className={className(props.menuHeight)} style={{ height: style(props) }}>
         <div className={statClassName("/list/user/")}>
-          <button onClick={event => handleState("/list/user/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/user/" onClick={event => handleState("/list/user/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.USER}</h3>
             <div className="stats">
-              <div><span>{i18n.users}:</span> <span>{user.U_USERS}</span></div>
-              <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_USERS}</span></div>
+              {
+                look
+                  ? (<>
+                    <div><span>{i18n.Disk}:</span> <span>{sizeFormatter(user.U_DISK)}</span></div>
+                    <div><span>{i18n.Bandwidth}:</span> <span>{sizeFormatter(user.U_BANDWIDTH)}</span></div>
+                  </>)
+                  : (<>
+                    <div><span>{i18n.users}:</span> <span>{user.U_USERS}</span></div>
+                    <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_USERS}</span></div>
+                  </>)
+              }
             </div>
-          </button>
+          </Link>
         </div>
         <div className={statClassName("/list/web/")}>
-          <button onClick={event => handleState("/list/web/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/web/" onClick={event => handleState("/list/web/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.WEB}</h3>
             <div className="stats">
               <div><span>{i18n.domains}:</span> <span>{user.U_WEB_DOMAINS}</span></div>
               <div><span>{i18n.aliases}:</span> <span>{user.U_WEB_ALIASES}</span></div>
               <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_WEB}</span></div>
             </div>
-          </button>
+          </Link>
         </div>
         <div className={statClassName("/list/dns/")}>
-          <button onClick={event => handleState("/list/dns/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/dns/" onClick={event => handleState("/list/dns/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.DNS}</h3>
             <div className="stats">
               <div><span>{i18n.domains}:</span> <span>{user.U_DNS_DOMAINS}</span></div>
               <div><span>{i18n.records}:</span> <span>{user.U_DNS_RECORDS}</span></div>
               <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_DNS}</span></div>
             </div>
-          </button>
+          </Link>
         </div>
         <div className={statClassName("/list/mail/")}>
-          <button onClick={event => handleState("/list/mail/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/mail/" onClick={event => handleState("/list/mail/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.MAIL}</h3>
             <div className="stats">
               <div><span>{i18n.domains}:</span> <span>{user.U_MAIL_DOMAINS}</span></div>
               <div><span>{i18n.accounts}:</span> <span>{user.U_MAIL_ACCOUNTS}</span></div>
               <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_MAIL}</span></div>
             </div>
-          </button>
+          </Link>
         </div>
         <div className={statClassName("/list/db/")}>
-          <button onClick={event => handleState("/list/db/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/db/" onClick={event => handleState("/list/db/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.DB}</h3>
             <div className="stats">
               <div><span>{i18n.databases}:</span> <span>{user.U_DATABASES}</span></div>
               <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_DB}</span></div>
             </div>
-          </button>
+          </Link>
         </div>
         <div className={statClassName("/list/cron/")}>
-          <button onClick={event => handleState("/list/cron/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/cron/" onClick={event => handleState("/list/cron/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.CRON}</h3>
             <div className="stats">
               <div><span>{i18n.jobs}:</span> <span>{user.U_CRON_JOBS}</span></div>
               <div><span>{i18n.spnd}:</span> <span>{user.SUSPENDED_CRON}</span></div>
             </div>
-          </button>
+          </Link>
         </div>
         <div className={statClassName("/list/backup/") + ' last'}>
-          <button onClick={event => handleState("/list/backup/", event)} onKeyPress={event => event.preventDefault()}>
+          <Link to="/list/backup/" onClick={event => handleState("/list/backup/", event)} onKeyPress={event => event.preventDefault()}>
             <h3>{i18n.BACKUP}</h3>
             <div className="stats">
               <div><span>{i18n.backups}:</span> <span>{user.U_BACKUPS}</span></div>
             </div>
-          </button>
+          </Link>
         </div>
       </div>
     </div>
   );
 }
 
-export default Menu;
+export default Menu;

+ 20 - 9
web/js/react/src/components/MainNav/Stat-menu/Menu.scss

@@ -1,3 +1,14 @@
+$whiteBackground: #ececec;
+$primary: #2c54ac;
+$primaryLight: #d7dcef;
+$primaryActive: #1e5cb2;
+$secondary: #fcac04;
+$secondaryLight: #f8b014;
+$secondaryActive: #fdb51c;
+$hoverButtonText: #2c54ac;
+$activeButtonText: #fff;
+$textColor: #555;
+
 .menu-wrapper {
   position: fixed;
   width: 100%;
@@ -71,10 +82,10 @@
 
       &:hover {
         cursor: pointer;
-        border-bottom: 3px solid #ff6701;
+        border-bottom: 3px solid $secondaryLight;
       
         h3 {
-          color: #ff6701;
+          color: $secondary;
         }
 
         .stats {
@@ -83,10 +94,10 @@
       }
 
       &:active {
-        border-color: #f72b44;
+        border-color: $secondaryActive;
 
         h3 {
-          color: #f72b44;
+          color: $secondary;
         }
       }
 
@@ -102,20 +113,20 @@
     }
 
     .l-active {
-      border-bottom: 3px solid #ff6701;
+      border-bottom: 3px solid $primary;
 
       h3 {
-        color: #ff6701;
+        color: $primary;
         font-size: 18px;
         margin-bottom: 25px;
       }
     }
 
     .stat.focus {
-      border-bottom: 3px solid #5edad0 !important;
+      border-bottom: 3px solid $secondaryLight !important;
       
       a, h3 {
-        color: #36B3A9 !important;
+        color: $secondaryLight !important;
       }
     }
   }
@@ -203,7 +214,7 @@
         }
 
         .stat.l-active {
-          background: #ff67010d;
+          background: #fdac020d;
         }
       }
     }

+ 4 - 1
web/js/react/src/components/MainNav/Toolbar/Checkbox/Checkbox.jsx

@@ -1,4 +1,5 @@
 import React from 'react';
+import { useSelector } from 'react-redux';
 import './Checkbox.scss';
 
 function toggleAll(props, e) {
@@ -6,13 +7,15 @@ function toggleAll(props, e) {
 }
 
 const Checkbox = (props) => {
+  const { i18n } = useSelector(state => state.session);
+
   return (
     <div className="input-group-prepend">
       <div className="input-group-text">
         <input type="checkbox" onChange={(e) => toggleAll(props, e)} aria-label="Checkbox for following text input" id="checkbox" checked={props.toggled} />
       </div>
       <span className="input-group-text">
-        <label htmlFor="checkbox">{window.GLOBAL.App.i18n['toggle all']}</label>
+        <label htmlFor="checkbox">{i18n['toggle all']}</label>
       </span>
     </div>
   );

Некоторые файлы не были показаны из-за большого количества измененных файлов