Mails.jsx 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422
  1. import React, { useState, useEffect } from 'react';
  2. import { addControlPanelContentFocusedElement, removeControlPanelContentFocusedElement } from '../../actions/ControlPanelContent/controlPanelContentActions';
  3. import { addActiveElement, removeFocusedElement } from '../../actions/MainNavigation/mainNavigationActions';
  4. import DropdownFilter from '../../components/MainNav/Toolbar/DropdownFilter/DropdownFilter';
  5. import { bulkAction, getMailList, handleAction } from '../../ControlPanelService/Mail';
  6. import * as MainNavigation from '../../actions/MainNavigation/mainNavigationActions';
  7. import SearchInput from '../../components/MainNav/Toolbar/SearchInput/SearchInput';
  8. import { addFavorite, deleteFavorite } from '../../ControlPanelService/Favorites';
  9. import LeftButton from '../../components/MainNav/Toolbar/LeftButton/LeftButton';
  10. import Checkbox from '../../components/MainNav/Toolbar/Checkbox/Checkbox';
  11. import Select from '../../components/MainNav/Toolbar/Select/Select';
  12. import Toolbar from '../../components/MainNav/Toolbar/Toolbar';
  13. import Modal from '../../components/ControlPanel/Modal/Modal';
  14. import Spinner from '../../components/Spinner/Spinner';
  15. import Mail from '../../components/Mail/Mail';
  16. import './Mails.scss';
  17. import { useSelector, useDispatch } from 'react-redux';
  18. import { Helmet } from 'react-helmet';
  19. import { refreshCounters } from 'src/actions/MenuCounters/menuCounterActions';
  20. const Mails = props => {
  21. const { i18n } = useSelector(state => state.session);
  22. const { controlPanelFocusedElement } = useSelector(state => state.controlPanelContent);
  23. const { focusedElement } = useSelector(state => state.mainNavigation);
  24. const dispatch = useDispatch();
  25. const [loading, setLoading] = useState(false);
  26. const [modal, setModal] = useState({
  27. text: '',
  28. visible: false,
  29. actionUrl: '',
  30. });
  31. const [state, setState] = useState({
  32. mails: [],
  33. mailFav: [],
  34. toggledAll: false,
  35. webMail: '',
  36. sorting: i18n.Date,
  37. order: "descending",
  38. selection: [],
  39. totalAmount: ''
  40. });
  41. useEffect(() => {
  42. dispatch(addActiveElement('/list/mail/'));
  43. dispatch(removeFocusedElement());
  44. dispatch(removeControlPanelContentFocusedElement());
  45. fetchData().then(() => setLoading(false));
  46. return () => {
  47. dispatch(removeControlPanelContentFocusedElement());
  48. }
  49. }, []);
  50. useEffect(() => {
  51. window.addEventListener("keydown", handleContentSelection);
  52. window.addEventListener("keydown", handleFocusedElementShortcuts);
  53. return () => {
  54. window.removeEventListener("keydown", handleContentSelection);
  55. window.removeEventListener("keydown", handleFocusedElementShortcuts);
  56. };
  57. }, [controlPanelFocusedElement, focusedElement, state.mails]);
  58. const handleContentSelection = event => {
  59. if (event.keyCode === 38 || event.keyCode === 40) {
  60. if (focusedElement) {
  61. dispatch(MainNavigation.removeFocusedElement());
  62. }
  63. }
  64. if (event.keyCode === 38) {
  65. event.preventDefault();
  66. handleArrowUp();
  67. } else if (event.keyCode === 40) {
  68. event.preventDefault();
  69. handleArrowDown();
  70. }
  71. }
  72. const initFocusedElement = mails => {
  73. mails[0]['FOCUSED'] = mails[0]['NAME'];
  74. setState({ ...state, mails });
  75. dispatch(addControlPanelContentFocusedElement(mails[0]['NAME']));
  76. }
  77. const handleArrowDown = () => {
  78. let mails = [...state.mails];
  79. if (focusedElement) {
  80. MainNavigation.removeFocusedElement();
  81. }
  82. if (controlPanelFocusedElement === '') {
  83. initFocusedElement(mails);
  84. return;
  85. }
  86. let focusedElementPosition = mails.findIndex(mail => mail.NAME === controlPanelFocusedElement);
  87. if (focusedElementPosition !== mails.length - 1) {
  88. let nextFocusedElement = mails[focusedElementPosition + 1];
  89. mails[focusedElementPosition]['FOCUSED'] = '';
  90. nextFocusedElement['FOCUSED'] = nextFocusedElement['NAME'];
  91. document.getElementById(nextFocusedElement['NAME']).scrollIntoView({ behavior: "smooth", block: "center" });
  92. setState({ ...state, mails });
  93. dispatch(addControlPanelContentFocusedElement(nextFocusedElement['NAME']));
  94. }
  95. }
  96. const handleArrowUp = () => {
  97. let mails = [...state.mails];
  98. if (focusedElement) {
  99. MainNavigation.removeFocusedElement();
  100. }
  101. if (controlPanelFocusedElement === '') {
  102. initFocusedElement(mails);
  103. return;
  104. }
  105. let focusedElementPosition = mails.findIndex(mail => mail.NAME === controlPanelFocusedElement);
  106. if (focusedElementPosition !== 0) {
  107. let nextFocusedElement = mails[focusedElementPosition - 1];
  108. mails[focusedElementPosition]['FOCUSED'] = '';
  109. nextFocusedElement['FOCUSED'] = nextFocusedElement['NAME'];
  110. document.getElementById(nextFocusedElement['NAME']).scrollIntoView({ behavior: "smooth", block: "center" });
  111. setState({ ...state, mails });
  112. dispatch(addControlPanelContentFocusedElement(nextFocusedElement['NAME']));
  113. }
  114. }
  115. const handleFocusedElementShortcuts = event => {
  116. let isSearchInputFocused = document.querySelector('input:focus') || document.querySelector('textarea:focus');
  117. if (controlPanelFocusedElement && !isSearchInputFocused) {
  118. switch (event.keyCode) {
  119. case 8: return handleDelete();
  120. case 13: return handleEdit();
  121. case 76: return handleLogs();
  122. case 78: return handleAddRecord();
  123. case 83: return handleSuspend();
  124. default: break;
  125. }
  126. }
  127. }
  128. const handleAddRecord = () => {
  129. props.history.push(`/add/mail/?domain=${controlPanelFocusedElement}`);
  130. }
  131. const handleLogs = () => {
  132. props.history.push(`/list/mail?domain=${controlPanelFocusedElement}&type=access`);
  133. }
  134. const handleEdit = () => {
  135. props.history.push(`/edit/mail?domain=${controlPanelFocusedElement}`);
  136. }
  137. const handleSuspend = () => {
  138. const { mails } = state;
  139. let currentMailData = mails.filter(mail => mail.NAME === controlPanelFocusedElement)[0];
  140. let suspendedStatus = currentMailData.SUSPENDED === 'yes' ? 'unsuspend' : 'suspend';
  141. displayModal(currentMailData.suspend_conf, `/api/v1/${suspendedStatus}/mail/index.php?domain=${controlPanelFocusedElement}`);
  142. }
  143. const handleDelete = () => {
  144. const { mails } = state;
  145. let currentMailData = mails.filter(mail => mail.NAME === controlPanelFocusedElement)[0];
  146. displayModal(currentMailData.delete_conf, `/api/v1/delete/mail/index.php?domain=${controlPanelFocusedElement}`);
  147. }
  148. const fetchData = () => {
  149. setLoading(true);
  150. return new Promise((resolve, reject) => {
  151. getMailList()
  152. .then(result => {
  153. setState({
  154. ...state,
  155. mails: reformatData(result.data.data),
  156. webMail: result.data.webMail,
  157. mailFav: result.data.mailFav,
  158. selection: [],
  159. toggledAll: false,
  160. totalAmount: result.data.totalAmount
  161. });
  162. resolve();
  163. })
  164. .catch(err => console.error(err));
  165. });
  166. }
  167. const reformatData = data => {
  168. let mails = [];
  169. for (let i in data) {
  170. data[i]['NAME'] = i;
  171. data[i]['FOCUSED'] = controlPanelFocusedElement === i;
  172. mails.push(data[i]);
  173. }
  174. return mails;
  175. }
  176. const changeSorting = (sorting, order) => {
  177. setState({
  178. ...state,
  179. sorting,
  180. order
  181. });
  182. }
  183. const mails = () => {
  184. const { mails } = state;
  185. const mailFav = { ...state.mailFav };
  186. const result = [];
  187. mails.forEach(mail => {
  188. mail.FOCUSED = controlPanelFocusedElement === mail.NAME;
  189. if (mailFav[mail.NAME]) {
  190. mail.STARRED = mailFav[mail.NAME];
  191. } else {
  192. mail.STARRED = 0;
  193. }
  194. result.push(mail);
  195. });
  196. let sortedResult = sortArray(result);
  197. return sortedResult.map((item, index) => {
  198. return <Mail data={item} key={index} toggleFav={toggleFav} checkItem={checkItem} handleModal={displayModal} />;
  199. });
  200. }
  201. const checkItem = name => {
  202. const { selection, mails } = state;
  203. let duplicate = [...selection];
  204. let mailsDuplicate = mails;
  205. let checkedItem = duplicate.indexOf(name);
  206. let incomingItem = mailsDuplicate.findIndex(mail => mail.NAME === name);
  207. mailsDuplicate[incomingItem].isChecked = !mailsDuplicate[incomingItem].isChecked;
  208. if (checkedItem !== -1) {
  209. duplicate.splice(checkedItem, 1);
  210. } else {
  211. duplicate.push(name);
  212. }
  213. setState({ ...state, mails: mailsDuplicate, selection: duplicate });
  214. }
  215. const sortArray = array => {
  216. const { order, sorting } = state;
  217. let sortingColumn = sortBy(sorting);
  218. if (order === "descending") {
  219. return array.sort((a, b) => (a[sortingColumn] < b[sortingColumn]) ? 1 : ((b[sortingColumn] < a[sortingColumn]) ? -1 : 0));
  220. } else {
  221. return array.sort((a, b) => (a[sortingColumn] > b[sortingColumn]) ? 1 : ((b[sortingColumn] > a[sortingColumn]) ? -1 : 0));
  222. }
  223. }
  224. const sortBy = sorting => {
  225. const { Date, Domains, Accounts, Disk, Starred } = i18n;
  226. switch (sorting) {
  227. case Date: return 'DATE';
  228. case Domains: return 'domain_account';
  229. case Accounts: return 'ACCOUNTS';
  230. case Disk: return 'U_DISK';
  231. case Starred: return 'STARRED';
  232. default: break;
  233. }
  234. }
  235. const toggleFav = (value, type) => {
  236. const { mailFav } = state;
  237. let mailFavDuplicate = mailFav;
  238. if (type === 'add') {
  239. mailFavDuplicate[value] = 1;
  240. addFavorite(value, 'mail')
  241. .then(() => {
  242. setState({ ...state, mailFav: mailFavDuplicate });
  243. })
  244. .catch(err => {
  245. console.error(err);
  246. });
  247. } else {
  248. mailFavDuplicate[value] = undefined;
  249. deleteFavorite(value, 'mail')
  250. .then(() => {
  251. setState({ ...state, mailFav: mailFavDuplicate });
  252. })
  253. .catch(err => {
  254. console.error(err);
  255. });
  256. }
  257. }
  258. const toggleAll = toggled => {
  259. const mailsDuplicate = [...state.mails];
  260. if (toggled) {
  261. let mailNames = [];
  262. let mails = mailsDuplicate.map(mail => {
  263. mailNames.push(mail.NAME);
  264. mail.isChecked = true;
  265. return mail;
  266. });
  267. setState({ ...state, mails, selection: mailNames, toggledAll: toggled });
  268. } else {
  269. let mails = mailsDuplicate.map(mail => {
  270. mail.isChecked = false;
  271. return mail;
  272. });
  273. setState({ ...state, mails, selection: [], toggledAll: toggled });
  274. }
  275. }
  276. const bulk = action => {
  277. const { selection } = state;
  278. if (selection.length && action) {
  279. setLoading(true);
  280. bulkAction(action, selection)
  281. .then(result => {
  282. if (result.status === 200) {
  283. toggleAll(false);
  284. fetchData().then(() => refreshMenuCounters());
  285. }
  286. })
  287. .catch(err => console.error(err));
  288. }
  289. }
  290. const displayModal = (text, url) => {
  291. setModal({
  292. ...modal,
  293. visible: true,
  294. text: text,
  295. actionUrl: url
  296. });
  297. }
  298. const modalConfirmHandler = () => {
  299. if (!modal.actionUrl) {
  300. return modalCancelHandler();
  301. }
  302. modalCancelHandler();
  303. setLoading(true);
  304. handleAction(modal.actionUrl)
  305. .then(res => {
  306. if (res.data.error) {
  307. setLoading(false);
  308. return displayModal(res.data.error, '');
  309. }
  310. fetchData().then(() => refreshMenuCounters())
  311. })
  312. .catch(err => { setLoading(false); console.error(err); });
  313. }
  314. const refreshMenuCounters = () => {
  315. dispatch(refreshCounters()).then(() => setLoading(false));
  316. }
  317. const modalCancelHandler = () => {
  318. setModal({
  319. ...modal,
  320. visible: false,
  321. text: '',
  322. actionUrl: ''
  323. });
  324. }
  325. return (
  326. <div className="mails">
  327. <Helmet>
  328. <title>{`Vesta - ${i18n.MAIL}`}</title>
  329. </Helmet>
  330. <Toolbar mobile={false} >
  331. <LeftButton name="Add Mail Domain" href="/add/mail" showLeftMenu={true} />
  332. <div className="r-menu">
  333. <div className="input-group input-group-sm">
  334. <a href={state.webMail} className="button-extra" type="submit">{i18n['open webMail']}</a>
  335. <Checkbox toggleAll={toggleAll} toggled={state.toggledAll} />
  336. <Select list='mailList' bulkAction={bulk} />
  337. <DropdownFilter changeSorting={changeSorting} sorting={state.sorting} order={state.order} list="mailList" />
  338. <SearchInput handleSearchTerm={term => props.changeSearchTerm(term)} />
  339. </div>
  340. </div>
  341. </Toolbar>
  342. <div className="mails-wrapper">
  343. {loading
  344. ? <Spinner />
  345. : (<>
  346. {mails()}
  347. <div className="total">{state.totalAmount}</div>
  348. </>)}
  349. </div>
  350. <Modal
  351. onSave={modalConfirmHandler}
  352. onCancel={modalCancelHandler}
  353. show={modal.visible}
  354. text={modal.text} />
  355. </div>
  356. );
  357. }
  358. export default Mails;