MainListItems.js 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521
  1. import React, { useContext, useEffect, useReducer, useState } from "react";
  2. import { Link as RouterLink, useHistory } from "react-router-dom";
  3. import ListItem from "@material-ui/core/ListItem";
  4. import ListItemIcon from "@material-ui/core/ListItemIcon";
  5. import ListItemText from "@material-ui/core/ListItemText";
  6. import ListSubheader from "@material-ui/core/ListSubheader";
  7. import Divider from "@material-ui/core/Divider";
  8. import { Badge, Collapse, List } from "@material-ui/core";
  9. import DashboardOutlinedIcon from "@material-ui/icons/DashboardOutlined";
  10. import WhatsAppIcon from "@material-ui/icons/WhatsApp";
  11. import SyncAltIcon from "@material-ui/icons/SyncAlt";
  12. import SettingsOutlinedIcon from "@material-ui/icons/SettingsOutlined";
  13. import PeopleAltOutlinedIcon from "@material-ui/icons/PeopleAltOutlined";
  14. import ContactPhoneOutlinedIcon from "@material-ui/icons/ContactPhoneOutlined";
  15. import AccountTreeOutlinedIcon from "@material-ui/icons/AccountTreeOutlined";
  16. import FlashOnIcon from "@material-ui/icons/FlashOn";
  17. import HelpOutlineIcon from "@material-ui/icons/HelpOutline";
  18. import CodeRoundedIcon from "@material-ui/icons/CodeRounded";
  19. import EventIcon from "@material-ui/icons/Event";
  20. import LocalOfferIcon from "@material-ui/icons/LocalOffer";
  21. import EventAvailableIcon from "@material-ui/icons/EventAvailable";
  22. import ExpandLessIcon from "@material-ui/icons/ExpandLess";
  23. import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
  24. import PeopleIcon from "@material-ui/icons/People";
  25. import ListIcon from "@material-ui/icons/ListAlt";
  26. import AnnouncementIcon from "@material-ui/icons/Announcement";
  27. import ForumIcon from "@material-ui/icons/Forum";
  28. import LocalAtmIcon from '@material-ui/icons/LocalAtm';
  29. import RotateRight from "@material-ui/icons/RotateRight";
  30. import { i18n } from "../translate/i18n";
  31. import { WhatsAppsContext } from "../context/WhatsApp/WhatsAppsContext";
  32. import { AuthContext } from "../context/Auth/AuthContext";
  33. import LoyaltyRoundedIcon from '@material-ui/icons/LoyaltyRounded';
  34. import { Can } from "../components/Can";
  35. import { SocketContext } from "../context/Socket/SocketContext";
  36. import { isArray } from "lodash";
  37. import TableChartIcon from '@material-ui/icons/TableChart';
  38. import api from "../services/api";
  39. import BorderColorIcon from '@material-ui/icons/BorderColor';
  40. import ToDoList from "../pages/ToDoList/";
  41. import toastError from "../errors/toastError";
  42. import { makeStyles } from "@material-ui/core/styles";
  43. import { AllInclusive, AttachFile, BlurCircular, DeviceHubOutlined, Schedule } from '@material-ui/icons';
  44. import usePlans from "../hooks/usePlans";
  45. import Typography from "@material-ui/core/Typography";
  46. import useVersion from "../hooks/useVersion";
  47. const useStyles = makeStyles((theme) => ({
  48. ListSubheader: {
  49. height: 26,
  50. marginTop: "-15px",
  51. marginBottom: "-10px",
  52. },
  53. }));
  54. function ListItemLink(props) {
  55. const { icon, primary, to, className } = props;
  56. const renderLink = React.useMemo(
  57. () =>
  58. React.forwardRef((itemProps, ref) => (
  59. <RouterLink to={to} ref={ref} {...itemProps} />
  60. )),
  61. [to]
  62. );
  63. return (
  64. <li>
  65. <ListItem button dense component={renderLink} className={className}>
  66. {icon ? <ListItemIcon>{icon}</ListItemIcon> : null}
  67. <ListItemText primary={primary} />
  68. </ListItem>
  69. </li>
  70. );
  71. }
  72. const reducer = (state, action) => {
  73. if (action.type === "LOAD_CHATS") {
  74. const chats = action.payload;
  75. const newChats = [];
  76. if (isArray(chats)) {
  77. chats.forEach((chat) => {
  78. const chatIndex = state.findIndex((u) => u.id === chat.id);
  79. if (chatIndex !== -1) {
  80. state[chatIndex] = chat;
  81. } else {
  82. newChats.push(chat);
  83. }
  84. });
  85. }
  86. return [...state, ...newChats];
  87. }
  88. if (action.type === "UPDATE_CHATS") {
  89. const chat = action.payload;
  90. const chatIndex = state.findIndex((u) => u.id === chat.id);
  91. if (chatIndex !== -1) {
  92. state[chatIndex] = chat;
  93. return [...state];
  94. } else {
  95. return [chat, ...state];
  96. }
  97. }
  98. if (action.type === "DELETE_CHAT") {
  99. const chatId = action.payload;
  100. const chatIndex = state.findIndex((u) => u.id === chatId);
  101. if (chatIndex !== -1) {
  102. state.splice(chatIndex, 1);
  103. }
  104. return [...state];
  105. }
  106. if (action.type === "RESET") {
  107. return [];
  108. }
  109. if (action.type === "CHANGE_CHAT") {
  110. const changedChats = state.map((chat) => {
  111. if (chat.id === action.payload.chat.id) {
  112. return action.payload.chat;
  113. }
  114. return chat;
  115. });
  116. return changedChats;
  117. }
  118. };
  119. const MainListItems = (props) => {
  120. const classes = useStyles();
  121. const { drawerClose, collapsed } = props;
  122. const { whatsApps } = useContext(WhatsAppsContext);
  123. const { user, handleLogout } = useContext(AuthContext);
  124. const [connectionWarning, setConnectionWarning] = useState(false);
  125. const [openCampaignSubmenu, setOpenCampaignSubmenu] = useState(false);
  126. const [showCampaigns, setShowCampaigns] = useState(false);
  127. const [showKanban, setShowKanban] = useState(false);
  128. const [showOpenAi, setShowOpenAi] = useState(false);
  129. const [showIntegrations, setShowIntegrations] = useState(false); const history = useHistory();
  130. const [showSchedules, setShowSchedules] = useState(false);
  131. const [showInternalChat, setShowInternalChat] = useState(false);
  132. const [showExternalApi, setShowExternalApi] = useState(false);
  133. const [invisible, setInvisible] = useState(true);
  134. const [pageNumber, setPageNumber] = useState(1);
  135. const [searchParam] = useState("");
  136. const [chats, dispatch] = useReducer(reducer, []);
  137. const { getPlanCompany } = usePlans();
  138. const [version, setVersion] = useState(false);
  139. const { getVersion } = useVersion();
  140. const socketManager = useContext(SocketContext);
  141. useEffect(() => {
  142. async function fetchVersion() {
  143. const _version = await getVersion();
  144. setVersion(_version.version);
  145. }
  146. fetchVersion();
  147. // eslint-disable-next-line react-hooks/exhaustive-deps
  148. }, []);
  149. useEffect(() => {
  150. dispatch({ type: "RESET" });
  151. setPageNumber(1);
  152. }, [searchParam]);
  153. useEffect(() => {
  154. async function fetchData() {
  155. const companyId = user.companyId;
  156. const planConfigs = await getPlanCompany(undefined, companyId);
  157. setShowCampaigns(planConfigs.plan.useCampaigns);
  158. setShowKanban(planConfigs.plan.useKanban);
  159. setShowOpenAi(planConfigs.plan.useOpenAi);
  160. setShowIntegrations(planConfigs.plan.useIntegrations);
  161. setShowSchedules(planConfigs.plan.useSchedules);
  162. setShowInternalChat(planConfigs.plan.useInternalChat);
  163. setShowExternalApi(planConfigs.plan.useExternalApi);
  164. }
  165. fetchData();
  166. // eslint-disable-next-line react-hooks/exhaustive-deps
  167. }, []);
  168. useEffect(() => {
  169. const delayDebounceFn = setTimeout(() => {
  170. fetchChats();
  171. }, 500);
  172. return () => clearTimeout(delayDebounceFn);
  173. // eslint-disable-next-line react-hooks/exhaustive-deps
  174. }, [searchParam, pageNumber]);
  175. useEffect(() => {
  176. const companyId = localStorage.getItem("companyId");
  177. const socket = socketManager.getSocket(companyId);
  178. socket.on(`company-${companyId}-chat`, (data) => {
  179. if (data.action === "new-message") {
  180. dispatch({ type: "CHANGE_CHAT", payload: data });
  181. }
  182. if (data.action === "update") {
  183. dispatch({ type: "CHANGE_CHAT", payload: data });
  184. }
  185. });
  186. return () => {
  187. socket.disconnect();
  188. };
  189. }, [socketManager]);
  190. useEffect(() => {
  191. let unreadsCount = 0;
  192. if (chats.length > 0) {
  193. for (let chat of chats) {
  194. for (let chatUser of chat.users) {
  195. if (chatUser.userId === user.id) {
  196. unreadsCount += chatUser.unreads;
  197. }
  198. }
  199. }
  200. }
  201. if (unreadsCount > 0) {
  202. setInvisible(false);
  203. } else {
  204. setInvisible(true);
  205. }
  206. }, [chats, user.id]);
  207. useEffect(() => {
  208. if (localStorage.getItem("cshow")) {
  209. setShowCampaigns(true);
  210. }
  211. }, []);
  212. useEffect(() => {
  213. const delayDebounceFn = setTimeout(() => {
  214. if (whatsApps.length > 0) {
  215. const offlineWhats = whatsApps.filter((whats) => {
  216. return (
  217. whats.status === "qrcode" ||
  218. whats.status === "PAIRING" ||
  219. whats.status === "DISCONNECTED" ||
  220. whats.status === "TIMEOUT" ||
  221. whats.status === "OPENING"
  222. );
  223. });
  224. if (offlineWhats.length > 0) {
  225. setConnectionWarning(true);
  226. } else {
  227. setConnectionWarning(false);
  228. }
  229. }
  230. }, 2000);
  231. return () => clearTimeout(delayDebounceFn);
  232. }, [whatsApps]);
  233. const fetchChats = async () => {
  234. try {
  235. const { data } = await api.get("/chats/", {
  236. params: { searchParam, pageNumber },
  237. });
  238. dispatch({ type: "LOAD_CHATS", payload: data.records });
  239. } catch (err) {
  240. toastError(err);
  241. }
  242. };
  243. const handleClickLogout = () => {
  244. //handleCloseMenu();
  245. handleLogout();
  246. };
  247. return (
  248. <div onClick={drawerClose}>
  249. <Can
  250. role={user.profile}
  251. perform="dashboard:view"
  252. yes={() => (
  253. <ListItemLink
  254. to="/"
  255. primary="Dashboard"
  256. icon={<DashboardOutlinedIcon />}
  257. />
  258. )}
  259. />
  260. <ListItemLink
  261. to="/tickets"
  262. primary={i18n.t("mainDrawer.listItems.tickets")}
  263. icon={<WhatsAppIcon />}
  264. />
  265. {showKanban && (
  266. <ListItemLink
  267. to="/kanban"
  268. primary={`Kanban`}
  269. icon={<TableChartIcon />}
  270. />
  271. )}
  272. <ListItemLink
  273. to="/quick-messages"
  274. primary={i18n.t("mainDrawer.listItems.quickMessages")}
  275. icon={<FlashOnIcon />}
  276. />
  277. <ListItemLink
  278. to="/todolist"
  279. primary={i18n.t("mainDrawer.listItems.tasks")}
  280. icon={<BorderColorIcon />}
  281. />
  282. <ListItemLink
  283. to="/contacts"
  284. primary={i18n.t("mainDrawer.listItems.contacts")}
  285. icon={<ContactPhoneOutlinedIcon />}
  286. />
  287. <ListItemLink
  288. to="/schedules"
  289. primary={i18n.t("mainDrawer.listItems.schedules")}
  290. icon={<EventIcon />}
  291. />
  292. <ListItemLink
  293. to="/tags"
  294. primary={i18n.t("mainDrawer.listItems.tags")}
  295. icon={<LocalOfferIcon />}
  296. />
  297. <ListItemLink
  298. to="/chats"
  299. primary={i18n.t("mainDrawer.listItems.chats")}
  300. icon={
  301. <Badge color="secondary" variant="dot" invisible={invisible}>
  302. <ForumIcon />
  303. </Badge>
  304. }
  305. />
  306. <ListItemLink
  307. to="/helps"
  308. primary={i18n.t("mainDrawer.listItems.helps")}
  309. icon={<HelpOutlineIcon />}
  310. />
  311. <Can
  312. role={user.profile}
  313. perform="drawer-admin-items:view"
  314. yes={() => (
  315. <>
  316. <Divider />
  317. <ListSubheader
  318. hidden={collapsed}
  319. style={{
  320. position: "relative",
  321. fontSize: "17px",
  322. textAlign: "left",
  323. paddingLeft: 20
  324. }}
  325. inset
  326. color="inherit">
  327. {i18n.t("mainDrawer.listItems.administration")}
  328. </ListSubheader>
  329. {showCampaigns && (
  330. <>
  331. <ListItem
  332. button
  333. onClick={() => setOpenCampaignSubmenu((prev) => !prev)}
  334. >
  335. <ListItemIcon>
  336. <EventAvailableIcon />
  337. </ListItemIcon>
  338. <ListItemText
  339. primary={i18n.t("mainDrawer.listItems.campaigns")}
  340. />
  341. {openCampaignSubmenu ? (
  342. <ExpandLessIcon />
  343. ) : (
  344. <ExpandMoreIcon />
  345. )}
  346. </ListItem>
  347. <Collapse
  348. style={{ paddingLeft: 15 }}
  349. in={openCampaignSubmenu}
  350. timeout="auto"
  351. unmountOnExit
  352. >
  353. <List component="div" disablePadding>
  354. <ListItem onClick={() => history.push("/campaigns")} button>
  355. <ListItemIcon>
  356. <ListIcon />
  357. </ListItemIcon>
  358. <ListItemText primary="Listagem" />
  359. </ListItem>
  360. <ListItem
  361. onClick={() => history.push("/contact-lists")}
  362. button
  363. >
  364. <ListItemIcon>
  365. <PeopleIcon />
  366. </ListItemIcon>
  367. <ListItemText primary="Listas de Contatos" />
  368. </ListItem>
  369. <ListItem
  370. onClick={() => history.push("/campaigns-config")}
  371. button
  372. >
  373. <ListItemIcon>
  374. <SettingsOutlinedIcon />
  375. </ListItemIcon>
  376. <ListItemText primary="Configurações" />
  377. </ListItem>
  378. </List>
  379. </Collapse>
  380. </>
  381. )}
  382. {user.super && (
  383. <ListItemLink
  384. to="/announcements"
  385. primary={i18n.t("mainDrawer.listItems.annoucements")}
  386. icon={<AnnouncementIcon />}
  387. />
  388. )}
  389. {showOpenAi && (
  390. <ListItemLink
  391. to="/prompts"
  392. primary={i18n.t("mainDrawer.listItems.prompts")}
  393. icon={<AllInclusive />}
  394. />
  395. )}
  396. {showIntegrations && (
  397. <ListItemLink
  398. to="/queue-integration"
  399. primary={i18n.t("mainDrawer.listItems.queueIntegration")}
  400. icon={<DeviceHubOutlined />}
  401. />
  402. )}
  403. <ListItemLink
  404. to="/connections"
  405. primary={i18n.t("mainDrawer.listItems.connections")}
  406. icon={
  407. <Badge badgeContent={connectionWarning ? "!" : 0} color="error">
  408. <SyncAltIcon />
  409. </Badge>
  410. }
  411. />
  412. <ListItemLink
  413. to="/files"
  414. primary={i18n.t("mainDrawer.listItems.files")}
  415. icon={<AttachFile />}
  416. />
  417. <ListItemLink
  418. to="/queues"
  419. primary={i18n.t("mainDrawer.listItems.queues")}
  420. icon={<AccountTreeOutlinedIcon />}
  421. />
  422. <ListItemLink
  423. to="/users"
  424. primary={i18n.t("mainDrawer.listItems.users")}
  425. icon={<PeopleAltOutlinedIcon />}
  426. />
  427. {showExternalApi && (
  428. <>
  429. <ListItemLink
  430. to="/messages-api"
  431. primary={i18n.t("mainDrawer.listItems.messagesAPI")}
  432. icon={<CodeRoundedIcon />}
  433. />
  434. </>
  435. )}
  436. <ListItemLink
  437. to="/financeiro"
  438. primary={i18n.t("mainDrawer.listItems.financeiro")}
  439. icon={<LocalAtmIcon />}
  440. />
  441. <ListItemLink
  442. to="/settings"
  443. primary={i18n.t("mainDrawer.listItems.settings")}
  444. icon={<SettingsOutlinedIcon />}
  445. />
  446. {!collapsed && <React.Fragment>
  447. <Divider />
  448. {/*
  449. // IMAGEM NO MENU
  450. <Hidden only={['sm', 'xs']}>
  451. <img style={{ width: "100%", padding: "10px" }} src={logo} alt="image" />
  452. </Hidden>
  453. */}
  454. <Typography style={{ fontSize: "12px", padding: "10px", textAlign: "right", fontWeight: "bold" }}>
  455. {version}
  456. </Typography>
  457. </React.Fragment>
  458. }
  459. </>
  460. )}
  461. />
  462. </div>
  463. );
  464. };
  465. export default MainListItems;