hiesoftrd 1 rok temu
rodzic
commit
da5e327625

+ 238 - 0
frontend/src/hooks/useAuth.js/index.js

@@ -0,0 +1,238 @@
+import { useState, useEffect, useContext } from "react";
+import { useHistory } from "react-router-dom";
+import { has, isArray } from "lodash";
+
+import { toast } from "react-toastify";
+
+import { i18n } from "../../translate/i18n";
+import api from "../../services/api";
+import toastError from "../../errors/toastError";
+import { SocketContext } from "../../context/Socket/SocketContext";
+import moment from "moment";
+const useAuth = () => {
+  const history = useHistory();
+  const [isAuth, setIsAuth] = useState(false);
+  const [loading, setLoading] = useState(true);
+  const [user, setUser] = useState({});
+
+  api.interceptors.request.use(
+    (config) => {
+      const token = localStorage.getItem("token");
+      if (token) {
+        config.headers["Authorization"] = `Bearer ${JSON.parse(token)}`;
+        setIsAuth(true);
+      }
+      return config;
+    },
+    (error) => {
+      Promise.reject(error);
+    }
+  );
+
+  let isRefreshing = false;
+  let failedRequestsQueue = [];
+
+  api.interceptors.response.use(
+    (response) => {
+      return response;
+    },
+    async (error) => {
+      const originalRequest = error.config;
+
+      if (error?.response?.status === 403 && !originalRequest._retry) {
+        if (isRefreshing) {
+          return new Promise((resolve, reject) => {
+            failedRequestsQueue.push({ resolve, reject });
+          })
+            .then((token) => {
+              originalRequest.headers.Authorization = `Bearer ${token}`;
+              return api(originalRequest);
+            })
+            .catch((err) => {
+              return Promise.reject(err);
+            });
+        }
+
+        originalRequest._retry = true;
+        isRefreshing = true;
+
+        try {
+          const { data } = await api.post("/auth/refresh_token");
+
+          if (data) {
+            localStorage.setItem("token", JSON.stringify(data.token));
+            api.defaults.headers.Authorization = `Bearer ${data.token}`;
+
+            failedRequestsQueue.forEach((request) => {
+              request.resolve(data.token);
+            });
+            failedRequestsQueue = [];
+          }
+
+          return api(originalRequest);
+        } catch (refreshError) {
+          failedRequestsQueue.forEach((request) => {
+            request.reject(refreshError);
+          });
+          failedRequestsQueue = [];
+
+          localStorage.removeItem("token");
+          localStorage.removeItem("companyId");
+          api.defaults.headers.Authorization = undefined;
+          setIsAuth(false);
+
+          return Promise.reject(refreshError);
+        } finally {
+          isRefreshing = false;
+        }
+      }
+
+      if (
+        error?.response?.status === 401 ||
+        (error?.response?.status === 403 && originalRequest._retry)
+      ) {
+        localStorage.removeItem("token");
+        localStorage.removeItem("companyId");
+        api.defaults.headers.Authorization = undefined;
+        setIsAuth(false);
+      }
+
+      return Promise.reject(error);
+    }
+  );
+
+  const socketManager = useContext(SocketContext);
+
+  useEffect(() => {
+    const token = localStorage.getItem("token");
+    (async () => {
+      if (token) {
+        try {
+          const { data } = await api.post("/auth/refresh_token");
+          api.defaults.headers.Authorization = `Bearer ${data.token}`;
+          setIsAuth(true);
+          setUser(data.user);
+        } catch (err) {
+          toastError(err);
+        }
+      }
+      setLoading(false);
+    })();
+  }, []);
+
+  useEffect(() => {
+    const companyId = localStorage.getItem("companyId");
+    if (companyId) {
+      const socket = socketManager.getSocket(companyId);
+
+      socket.on(`company-${companyId}-user`, (data) => {
+        if (data.action === "update" && data.user.id === user.id) {
+          setUser(data.user);
+        }
+      });
+
+      return () => {
+        socket.disconnect();
+      };
+    }
+  }, [socketManager, user]);
+
+  const handleLogin = async (userData) => {
+    setLoading(true);
+
+    try {
+      const { data } = await api.post("/auth/login", userData);
+      const {
+        user: { companyId, id, company },
+      } = data;
+
+      if (has(company, "settings") && isArray(company.settings)) {
+        const setting = company.settings.find(
+          (s) => s.key === "campaignsEnabled"
+        );
+        if (setting && setting.value === "true") {
+          localStorage.setItem("cshow", null); //regra pra exibir campanhas
+        }
+      }
+
+      moment.locale("pt-br");
+      const dueDate = data.user.company.dueDate;
+      const hoje = moment(moment()).format("DD/MM/yyyy");
+      const vencimento = moment(dueDate).format("DD/MM/yyyy");
+
+      var diff = moment(dueDate).diff(moment(moment()).format());
+
+      var before = moment(moment().format()).isBefore(dueDate);
+      var dias = moment.duration(diff).asDays();
+
+      if (before === true) {
+        localStorage.setItem("token", JSON.stringify(data.token));
+        localStorage.setItem("companyId", companyId);
+        localStorage.setItem("userId", id);
+        localStorage.setItem("companyDueDate", vencimento);
+        api.defaults.headers.Authorization = `Bearer ${data.token}`;
+        setUser(data.user);
+        setIsAuth(true);
+        toast.success(i18n.t("auth.toasts.success"));
+        if (Math.round(dias) < 5) {
+          toast.warn(
+            `Sua assinatura vence em ${Math.round(dias)} ${
+              Math.round(dias) === 1 ? "dia" : "dias"
+            } `
+          );
+        }
+        history.push("/tickets");
+        setLoading(false);
+      } else {
+        toastError(`Opss! Sua assinatura venceu ${vencimento}.
+Entre em contato com o Suporte para mais informações! `);
+        setLoading(false);
+      }
+
+      //quebra linha
+    } catch (err) {
+      toastError(err);
+      setLoading(false);
+    }
+  };
+
+  const handleLogout = async () => {
+    setLoading(true);
+
+    try {
+      await api.delete("/auth/logout");
+      setIsAuth(false);
+      setUser({});
+      localStorage.removeItem("token");
+      localStorage.removeItem("companyId");
+      localStorage.removeItem("userId");
+      localStorage.removeItem("cshow");
+      api.defaults.headers.Authorization = undefined;
+      setLoading(false);
+      history.push("/login");
+    } catch (err) {
+      toastError(err);
+      setLoading(false);
+    }
+  };
+
+  const getCurrentUserInfo = async () => {
+    try {
+      const { data } = await api.get("/auth/me");
+      return data;
+    } catch (err) {
+      toastError(err);
+    }
+  };
+
+  return {
+    isAuth,
+    user,
+    loading,
+    handleLogin,
+    handleLogout,
+    getCurrentUserInfo,
+  };
+};
+
+export default useAuth;

+ 85 - 0
frontend/src/hooks/useCompanies/index.js

@@ -0,0 +1,85 @@
+import api from "../../services/api";
+
+const useCompanies = () => {
+
+    const save = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/companies',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const findAll = async (id) => {
+        const { data } = await api.request({
+            url: `/companies`,
+            method: 'GET'
+        });
+        return data;
+    }
+
+    const list = async (id) => {
+        const { data } = await api.request({
+            url: `/companies/list`,
+            method: 'GET'
+        });
+        return data;
+    }
+
+    const find = async (id) => {
+        const { data } = await api.request({
+            url: `/companies/${id}`,
+            method: 'GET'
+        });
+        return data;
+    }
+
+    const finding = async (id) => {
+        const { data } = await api.request({
+            url: `/companies/${id}`,
+            method: 'GET'
+        });
+        return data;
+    }
+
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/companies/${data.id}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    const remove = async (id) => {
+        const { data } = await api.request({
+            url: `/companies/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    const updateSchedules = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/companies/${data.id}/schedules`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    return {
+        save,
+        update,
+        remove,
+        list,
+        find,
+        finding,
+        findAll,
+        updateSchedules
+    }
+}
+
+export default useCompanies;

+ 57 - 0
frontend/src/hooks/useContactListItems/index.js

@@ -0,0 +1,57 @@
+import api from "../../services/api";
+
+const useContactListItems = () => {
+  const findAll = async (params) => {
+    const { data } = await api.request({
+      url: "/contact-list-items",
+      method: "GET",
+      params,
+    });
+    return data;
+  };
+
+  const save = async (data) => {
+    const { data: responseData } = await api.request({
+      url: "/contact-list-items",
+      method: "POST",
+      data,
+    });
+    return responseData;
+  };
+
+  const update = async (data) => {
+    const { data: responseData } = await api.request({
+      url: `/contact-list-items/${data.id}`,
+      method: "PUT",
+      data,
+    });
+    return responseData;
+  };
+
+  const deleteRecord = async (id) => {
+    const { data } = await api.request({
+      url: `/contact-list-items/${id}`,
+      method: "DELETE",
+    });
+    return data;
+  };
+
+  const list = async (params) => {
+    const { data } = await api.request({
+      url: `/contact-list-items/list`,
+      method: "GET",
+      params,
+    });
+    return data;
+  };
+
+  return {
+    save,
+    update,
+    deleteRecord,
+    list,
+    findAll,
+  };
+};
+
+export default useContactListItems;

+ 56 - 0
frontend/src/hooks/useContactLists/index.js

@@ -0,0 +1,56 @@
+import api from "../../services/api";
+
+const useContactLists = () => {
+  const save = async (data) => {
+    const { data: responseData } = await api.request({
+      url: "/contact-lists",
+      method: "POST",
+      data,
+    });
+    return responseData;
+  };
+
+  const update = async (data) => {
+    const { data: responseData } = await api.request({
+      url: `/contact-lists/${data.id}`,
+      method: "PUT",
+      data,
+    });
+    return responseData;
+  };
+
+  const deleteRecord = async (id) => {
+    const { data } = await api.request({
+      url: `/contact-lists/${id}`,
+      method: "DELETE",
+    });
+    return data;
+  };
+
+  const findById = async (id) => {
+    const { data } = await api.request({
+      url: `/contact-lists/${id}`,
+      method: "GET",
+    });
+    return data;
+  };
+
+  const list = async (params) => {
+    const { data } = await api.request({
+      url: "/contact-lists/list",
+      method: "GET",
+      params,
+    });
+    return data;
+  };
+
+  return {
+    findById,
+    save,
+    update,
+    deleteRecord,
+    list,
+  };
+};
+
+export default useContactLists;

+ 45 - 0
frontend/src/hooks/useContacts/index.js

@@ -0,0 +1,45 @@
+import { useState, useEffect } from "react";
+import toastError from "../../errors/toastError";
+
+import api from "../../services/api";
+
+const useContacts = ({ searchParam, pageNumber, date, dateStart, dateEnd }) => {
+    const [loading, setLoading] = useState(true);
+    const [hasMore, setHasMore] = useState(false);
+    const [contacts, setContacts] = useState([]);
+    const [count, setCount] = useState(0);
+
+    useEffect(() => {
+        setLoading(true);
+        const delayDebounceFn = setTimeout(() => {
+            const fetchContacts = async () => {
+                try {
+                    const { data } = await api.get("/contacts", {
+                        params: {
+                            searchParam,
+                            pageNumber,
+                            date,
+                            dateStart,
+                            dateEnd,
+                        },
+                    });
+                    setContacts(data.contacts);
+
+                    setHasMore(data.hasMore);
+                    setCount(data.count);
+                    setLoading(false);
+                } catch (err) {
+                    setLoading(false);
+                    toastError(err);
+                }
+            };
+
+            fetchContacts();
+        }, 500);
+        return () => clearTimeout(delayDebounceFn);
+    }, [searchParam, pageNumber, date, dateStart, dateEnd]);
+
+    return { contacts, loading, hasMore, count };
+};
+
+export default useContacts;

+ 19 - 0
frontend/src/hooks/useDashboard/index.js

@@ -0,0 +1,19 @@
+import api from "../../services/api";
+
+const useDashboard = () => {
+
+    const find = async (params) => {
+        const { data } = await api.request({
+            url: `/dashboard`,
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    return {
+        find
+    }
+}
+
+export default useDashboard;

+ 30 - 0
frontend/src/hooks/useDate/index.js

@@ -0,0 +1,30 @@
+import moment from "moment";
+
+export function useDate() {
+  function dateToClient(strDate) {
+    if (moment(strDate).isValid()) {
+      return moment(strDate).format("DD/MM/YYYY");
+    }
+    return strDate;
+  }
+
+  function datetimeToClient(strDate) {
+    if (moment(strDate).isValid()) {
+      return moment(strDate).format("DD/MM/YYYY HH:mm");
+    }
+    return strDate;
+  }
+
+  function dateToDatabase(strDate) {
+    if (moment(strDate, "DD/MM/YYYY").isValid()) {
+      return moment(strDate).format("YYYY-MM-DD HH:mm:ss");
+    }
+    return strDate;
+  }
+
+  return {
+    dateToClient,
+    datetimeToClient,
+    dateToDatabase,
+  };
+}

+ 58 - 0
frontend/src/hooks/useHelps/index.js

@@ -0,0 +1,58 @@
+import api from "../../services/api";
+
+const usePlans = () => {
+
+    const findAll = async (params) => {
+        const { data } = await api.request({
+            url: `/helps`,
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const list = async (params) => {
+        const { data } = await api.request({
+            url: '/helps/list',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const save = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/helps',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/helps/${data.id}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    const remove = async (id) => {
+        const { data } = await api.request({
+            url: `/helps/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    return {
+        findAll,
+        list,
+        save,
+        update,
+        remove
+    }
+}
+
+export default usePlans;

+ 67 - 0
frontend/src/hooks/useInvoices/index.js

@@ -0,0 +1,67 @@
+import api, { openApi } from "../../services/api";
+
+const useInvoices = () => {
+
+    const getInvoicesList = async (params) => {
+        const { data } = await openApi.request({
+            url: '/invoices/list',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const list = async (params) => {
+        const { data } = await api.request({
+            url: '/invoices/all',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const save = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/invoices',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const show = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/invoices/${id}',
+            method: 'GET',
+            data
+        });
+        return responseData;
+    }
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/invoices/${data.id}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    const remove = async (id) => {
+        const { data } = await api.request({
+            url: `/invoices/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    return {
+        getInvoicesList,
+        list,
+        save,
+        update,
+        remove
+    }
+}
+
+export default useInvoices;

+ 29 - 0
frontend/src/hooks/useLocalStorage/index.js

@@ -0,0 +1,29 @@
+import { useState } from "react";
+import toastError from "../../errors/toastError";
+
+export function useLocalStorage(key, initialValue) {
+	const [storedValue, setStoredValue] = useState(() => {
+		try {
+			const item = localStorage.getItem(key);
+			return item ? JSON.parse(item) : initialValue;
+		} catch (error) {
+			toastError(error);
+			return initialValue;
+		}
+	});
+
+	const setValue = value => {
+		try {
+			const valueToStore =
+				value instanceof Function ? value(storedValue) : value;
+
+			setStoredValue(valueToStore);
+
+			localStorage.setItem(key, JSON.stringify(valueToStore));
+		} catch (error) {
+			toastError(error);
+		}
+	};
+
+	return [storedValue, setValue];
+}

+ 38 - 0
frontend/src/hooks/useMessages/index.js

@@ -0,0 +1,38 @@
+import { useState, useEffect } from "react";
+import toastError from "../../errors/toastError";
+
+import api from "../../services/api";
+
+const useMessages = ({ fromMe, dateStart, dateEnd }) => {
+    const [loading, setLoading] = useState(true);
+    const [count, setCount] = useState(0);
+
+    useEffect(() => {
+        setLoading(true);
+        const delayDebounceFn = setTimeout(() => {
+            const fetchMessages = async () => {
+                try {
+                    const { data } = await api.get("/messages-allMe", {
+                        params: {
+                            fromMe,
+                            dateStart,
+                            dateEnd,
+                        },
+                    });
+                    setCount(data.count[0].count);
+                    setLoading(false);
+                } catch (err) {
+                    setLoading(false);
+                    toastError(err);
+                }
+            };
+
+            fetchMessages();
+        }, 500);
+        return () => clearTimeout(delayDebounceFn);
+    }, [dateStart, dateEnd]);
+
+    return { count };
+};
+
+export default useMessages;

+ 77 - 0
frontend/src/hooks/usePlans/index.js

@@ -0,0 +1,77 @@
+import api, { openApi } from "../../services/api";
+
+const usePlans = () => {
+
+    const getPlanList = async (params) => {
+        const { data } = await openApi.request({
+            url: '/plans/list',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const list = async (params) => {
+        const { data } = await api.request({
+            url: '/plans/all',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const finder = async (id) => {
+        const { data } = await api.request({
+            url: `/plans/${id}`,
+            method: 'GET'
+        });
+        return data;
+    }
+
+    const save = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/plans',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/plans/${data.id}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    const remove = async (id) => {
+        const { data } = await api.request({
+            url: `/plans/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    const getPlanCompany = async (params, id) => {
+        const { data } = await api.request({
+            url: `/companies/listPlan/${id}`,
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    return {
+        getPlanList,
+        list,
+        save,
+        update,
+        finder,
+        remove,
+        getPlanCompany
+    }
+}
+
+export default usePlans;

+ 12 - 0
frontend/src/hooks/useQueues/index.js

@@ -0,0 +1,12 @@
+import api from "../../services/api";
+
+const useQueues = () => {
+	const findAll = async () => {
+        const { data } = await api.get("/queue");
+        return data;
+    }
+
+	return { findAll };
+};
+
+export default useQueues;

+ 48 - 0
frontend/src/hooks/useQuickMessages/index.js

@@ -0,0 +1,48 @@
+import api from "../../services/api";
+
+const useQuickMessages = () => {
+
+    const save = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/quick-messages',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/quick-messages/${data.id}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    const deleteRecord = async (id) => {
+        const { data } = await api.request({
+            url: `/quick-messages/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    const list = async (params) => {
+        const { data } = await api.request({
+            url: '/quick-messages/list',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    return {
+        save,
+        update,
+        deleteRecord,
+        list
+    }
+}
+
+export default useQuickMessages;

+ 48 - 0
frontend/src/hooks/useQuickMessages_OLD/index.js

@@ -0,0 +1,48 @@
+import api from "../../services/api";
+
+const useQuickMessages = () => {
+
+    const save = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/quick-messages',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/quick-messages/${data.id}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    const deleteRecord = async (id) => {
+        const { data } = await api.request({
+            url: `/quick-messages/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    const list = async (params) => {
+        const { data } = await api.request({
+            url: '/quick-messages/list',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    return {
+        save,
+        update,
+        deleteRecord,
+        list
+    }
+}
+
+export default useQuickMessages;

+ 29 - 0
frontend/src/hooks/useSettings/index.js

@@ -0,0 +1,29 @@
+import api from "../../services/api";
+
+const useSettings = () => {
+
+    const getAll = async (params) => {
+        const { data } = await api.request({
+            url: '/settings',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    const update = async (data) => {
+        const { data: responseData } = await api.request({
+            url: `/settings/${data.key}`,
+            method: 'PUT',
+            data
+        });
+        return responseData;
+    }
+
+    return {
+		getAll,
+        update
+    }
+}
+
+export default useSettings;

+ 38 - 0
frontend/src/hooks/useTicketNotes/index.js

@@ -0,0 +1,38 @@
+import api from "../../services/api";
+
+const useTicketNotes = () => {
+
+    const saveNote = async (data) => {
+        const { data: responseData } = await api.request({
+            url: '/ticket-notes',
+            method: 'POST',
+            data
+        });
+        return responseData;
+    }
+
+    const deleteNote = async (id) => {
+        const { data } = await api.request({
+            url: `/ticket-notes/${id}`,
+            method: 'DELETE'
+        });
+        return data;
+    }
+
+    const listNotes = async (params) => {
+        const { data } = await api.request({
+            url: '/ticket-notes/list',
+            method: 'GET',
+            params
+        });
+        return data;
+    }
+
+    return {
+        saveNote,
+        deleteNote,
+        listNotes
+    }
+}
+
+export default useTicketNotes;

+ 68 - 0
frontend/src/hooks/useTickets/index.js

@@ -0,0 +1,68 @@
+import { useState, useEffect } from "react";
+import toastError from "../../errors/toastError";
+
+import api from "../../services/api";
+
+const useTickets = ({
+  searchParam,
+  tags,
+  users,
+  pageNumber,
+  status,
+  date,
+  updatedAt,
+  showAll,
+  queueIds,
+  withUnreadMessages,
+}) => {
+  const [loading, setLoading] = useState(true);
+  const [hasMore, setHasMore] = useState(false);
+  const [tickets, setTickets] = useState([]);
+
+  useEffect(() => {
+    setLoading(true);
+    const delayDebounceFn = setTimeout(() => {
+      const fetchTickets = async () => {
+        try {
+          const { data } = await api.get("/tickets", {
+            params: {
+              searchParam,
+              pageNumber,
+              tags,
+              users,
+              status,
+              date,
+              updatedAt,
+              showAll,
+              queueIds,
+              withUnreadMessages,
+            },
+          });
+          setTickets(data.tickets);
+          setHasMore(data.hasMore);
+          setLoading(false);
+        } catch (err) {
+          setLoading(false);
+          toastError(err);
+        }
+      };
+      fetchTickets();
+    }, 500);
+    return () => clearTimeout(delayDebounceFn);
+  }, [
+    searchParam,
+    tags,
+    users,
+    pageNumber,
+    status,
+    date,
+    updatedAt,
+    showAll,
+    queueIds,
+    withUnreadMessages,
+  ]);
+
+  return { tickets, loading, hasMore };
+};
+
+export default useTickets;

+ 38 - 0
frontend/src/hooks/useUsers/index.js

@@ -0,0 +1,38 @@
+import { useState, useEffect } from "react";
+import toastError from "../../errors/toastError";
+
+import api from "../../services/api";
+
+const useUsers = () => {
+    const [loading, setLoading] = useState(true);
+    const [hasMore, setHasMore] = useState(false);
+    const [users, setUsers] = useState([]);
+    const [count, setCount] = useState(0);
+
+    useEffect(() => {
+        setLoading(true);
+        const delayDebounceFn = setTimeout(() => {
+            const fetchUsers = async () => {
+                try {
+                    const { data } = await api.get("/users", {
+                        params: {},
+                    });
+                    setUsers(data.users);
+                    setHasMore(data.hasMore);
+                    setCount(data.count);
+                    setLoading(false);
+                } catch (err) {
+                    setLoading(false);
+                    toastError(err);
+                }
+            };
+
+            fetchUsers();
+        }, 500);
+        return () => clearTimeout(delayDebounceFn);
+    }, []);
+
+    return { users, loading, hasMore, count };
+};
+
+export default useUsers;

+ 21 - 0
frontend/src/hooks/useVersion/index.js

@@ -0,0 +1,21 @@
+import api from "../../services/api";
+
+const useVersion = () => {
+
+    const getVersion = async () => {
+        const { data } = await api.request({
+            url: '/version',
+            method: 'GET',
+        });
+        return data;
+    }
+
+    return {
+        getVersion
+    }
+}
+
+export default useVersion;
+
+
+

+ 107 - 0
frontend/src/hooks/useWhatsApps/index.js

@@ -0,0 +1,107 @@
+import { useState, useEffect, useReducer, useContext } from "react";
+import toastError from "../../errors/toastError";
+
+import api from "../../services/api";
+import { SocketContext } from "../../context/Socket/SocketContext";
+
+const reducer = (state, action) => {
+  if (action.type === "LOAD_WHATSAPPS") {
+    const whatsApps = action.payload;
+
+    return [...whatsApps];
+  }
+
+  if (action.type === "UPDATE_WHATSAPPS") {
+    const whatsApp = action.payload;
+    const whatsAppIndex = state.findIndex((s) => s.id === whatsApp.id);
+
+    if (whatsAppIndex !== -1) {
+      state[whatsAppIndex] = whatsApp;
+      return [...state];
+    } else {
+      return [whatsApp, ...state];
+    }
+  }
+
+  if (action.type === "UPDATE_SESSION") {
+    const whatsApp = action.payload;
+    const whatsAppIndex = state.findIndex((s) => s.id === whatsApp.id);
+
+    if (whatsAppIndex !== -1) {
+      state[whatsAppIndex].status = whatsApp.status;
+      state[whatsAppIndex].updatedAt = whatsApp.updatedAt;
+      state[whatsAppIndex].qrcode = whatsApp.qrcode;
+      state[whatsAppIndex].retries = whatsApp.retries;
+      return [...state];
+    } else {
+      return [...state];
+    }
+  }
+
+  if (action.type === "DELETE_WHATSAPPS") {
+    const whatsAppId = action.payload;
+
+    const whatsAppIndex = state.findIndex((s) => s.id === whatsAppId);
+    if (whatsAppIndex !== -1) {
+      state.splice(whatsAppIndex, 1);
+    }
+    return [...state];
+  }
+
+  if (action.type === "RESET") {
+    return [];
+  }
+};
+
+const useWhatsApps = () => {
+  const [whatsApps, dispatch] = useReducer(reducer, []);
+  const [loading, setLoading] = useState(true);
+
+  const socketManager = useContext(SocketContext);
+
+  useEffect(() => {
+    setLoading(true);
+    const fetchSession = async () => {
+      try {
+        const { data } = await api.get("/whatsapp/?session=0");
+        dispatch({ type: "LOAD_WHATSAPPS", payload: data });
+        setLoading(false);
+      } catch (err) {
+        setLoading(false);
+        toastError(err);
+      }
+    };
+    fetchSession();
+  }, []);
+
+  useEffect(() => {
+    const companyId = localStorage.getItem("companyId");
+    const socket = socketManager.getSocket(companyId);
+
+    socket.on(`company-${companyId}-whatsapp`, (data) => {
+      if (data.action === "update") {
+        dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp });
+      }
+    });
+
+    socket.on(`company-${companyId}-whatsapp`, (data) => {
+      if (data.action === "delete") {
+        dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId });
+      }
+    });
+
+    socket.on(`company-${companyId}-whatsappSession`, (data) => {
+      if (data.action === "update") {
+        dispatch({ type: "UPDATE_SESSION", payload: data.session });
+      }
+    });
+
+    return () => {
+      socket.disconnect();
+    };
+  }, [socketManager]);
+
+  return { whatsApps, loading };
+};
+
+export default useWhatsApps;

+ 521 - 0
frontend/src/layout/MainListItems.js

@@ -0,0 +1,521 @@
+import React, { useContext, useEffect, useReducer, useState } from "react";
+import { Link as RouterLink, useHistory } from "react-router-dom";
+
+import ListItem from "@material-ui/core/ListItem";
+import ListItemIcon from "@material-ui/core/ListItemIcon";
+import ListItemText from "@material-ui/core/ListItemText";
+import ListSubheader from "@material-ui/core/ListSubheader";
+import Divider from "@material-ui/core/Divider";
+import { Badge, Collapse, List } from "@material-ui/core";
+import DashboardOutlinedIcon from "@material-ui/icons/DashboardOutlined";
+import WhatsAppIcon from "@material-ui/icons/WhatsApp";
+import SyncAltIcon from "@material-ui/icons/SyncAlt";
+import SettingsOutlinedIcon from "@material-ui/icons/SettingsOutlined";
+import PeopleAltOutlinedIcon from "@material-ui/icons/PeopleAltOutlined";
+import ContactPhoneOutlinedIcon from "@material-ui/icons/ContactPhoneOutlined";
+import AccountTreeOutlinedIcon from "@material-ui/icons/AccountTreeOutlined";
+import FlashOnIcon from "@material-ui/icons/FlashOn";
+import HelpOutlineIcon from "@material-ui/icons/HelpOutline";
+import CodeRoundedIcon from "@material-ui/icons/CodeRounded";
+import EventIcon from "@material-ui/icons/Event";
+import LocalOfferIcon from "@material-ui/icons/LocalOffer";
+import EventAvailableIcon from "@material-ui/icons/EventAvailable";
+import ExpandLessIcon from "@material-ui/icons/ExpandLess";
+import ExpandMoreIcon from "@material-ui/icons/ExpandMore";
+import PeopleIcon from "@material-ui/icons/People";
+import ListIcon from "@material-ui/icons/ListAlt";
+import AnnouncementIcon from "@material-ui/icons/Announcement";
+import ForumIcon from "@material-ui/icons/Forum";
+import LocalAtmIcon from '@material-ui/icons/LocalAtm';
+import RotateRight from "@material-ui/icons/RotateRight";
+import { i18n } from "../translate/i18n";
+import { WhatsAppsContext } from "../context/WhatsApp/WhatsAppsContext";
+import { AuthContext } from "../context/Auth/AuthContext";
+import LoyaltyRoundedIcon from '@material-ui/icons/LoyaltyRounded';
+import { Can } from "../components/Can";
+import { SocketContext } from "../context/Socket/SocketContext";
+import { isArray } from "lodash";
+import TableChartIcon from '@material-ui/icons/TableChart';
+import api from "../services/api";
+import BorderColorIcon from '@material-ui/icons/BorderColor';
+import ToDoList from "../pages/ToDoList/";
+import toastError from "../errors/toastError";
+import { makeStyles } from "@material-ui/core/styles";
+import { AllInclusive, AttachFile, BlurCircular, DeviceHubOutlined, Schedule } from '@material-ui/icons';
+import usePlans from "../hooks/usePlans";
+import Typography from "@material-ui/core/Typography";
+import useVersion from "../hooks/useVersion";
+
+const useStyles = makeStyles((theme) => ({
+  ListSubheader: {
+    height: 26,
+    marginTop: "-15px",
+    marginBottom: "-10px",
+  },
+}));
+
+
+function ListItemLink(props) {
+  const { icon, primary, to, className } = props;
+
+  const renderLink = React.useMemo(
+    () =>
+      React.forwardRef((itemProps, ref) => (
+        <RouterLink to={to} ref={ref} {...itemProps} />
+      )),
+    [to]
+  );
+
+  return (
+    <li>
+      <ListItem button dense component={renderLink} className={className}>
+        {icon ? <ListItemIcon>{icon}</ListItemIcon> : null}
+        <ListItemText primary={primary} />
+      </ListItem>
+    </li>
+  );
+}
+
+const reducer = (state, action) => {
+  if (action.type === "LOAD_CHATS") {
+    const chats = action.payload;
+    const newChats = [];
+
+    if (isArray(chats)) {
+      chats.forEach((chat) => {
+        const chatIndex = state.findIndex((u) => u.id === chat.id);
+        if (chatIndex !== -1) {
+          state[chatIndex] = chat;
+        } else {
+          newChats.push(chat);
+        }
+      });
+    }
+
+    return [...state, ...newChats];
+  }
+
+  if (action.type === "UPDATE_CHATS") {
+    const chat = action.payload;
+    const chatIndex = state.findIndex((u) => u.id === chat.id);
+
+    if (chatIndex !== -1) {
+      state[chatIndex] = chat;
+      return [...state];
+    } else {
+      return [chat, ...state];
+    }
+  }
+
+  if (action.type === "DELETE_CHAT") {
+    const chatId = action.payload;
+
+    const chatIndex = state.findIndex((u) => u.id === chatId);
+    if (chatIndex !== -1) {
+      state.splice(chatIndex, 1);
+    }
+    return [...state];
+  }
+
+  if (action.type === "RESET") {
+    return [];
+  }
+
+  if (action.type === "CHANGE_CHAT") {
+    const changedChats = state.map((chat) => {
+      if (chat.id === action.payload.chat.id) {
+        return action.payload.chat;
+      }
+      return chat;
+    });
+    return changedChats;
+  }
+};
+
+const MainListItems = (props) => {
+  const classes = useStyles();
+  const { drawerClose, collapsed } = props;
+  const { whatsApps } = useContext(WhatsAppsContext);
+  const { user, handleLogout } = useContext(AuthContext);
+  const [connectionWarning, setConnectionWarning] = useState(false);
+  const [openCampaignSubmenu, setOpenCampaignSubmenu] = useState(false);
+  const [showCampaigns, setShowCampaigns] = useState(false);
+  const [showKanban, setShowKanban] = useState(false);
+  const [showOpenAi, setShowOpenAi] = useState(false);
+  const [showIntegrations, setShowIntegrations] = useState(false); const history = useHistory();
+  const [showSchedules, setShowSchedules] = useState(false);
+  const [showInternalChat, setShowInternalChat] = useState(false);
+  const [showExternalApi, setShowExternalApi] = useState(false);
+
+
+  const [invisible, setInvisible] = useState(true);
+  const [pageNumber, setPageNumber] = useState(1);
+  const [searchParam] = useState("");
+  const [chats, dispatch] = useReducer(reducer, []);
+  const { getPlanCompany } = usePlans();
+  
+  const [version, setVersion] = useState(false);
+  
+  
+  const { getVersion } = useVersion();
+
+  const socketManager = useContext(SocketContext);
+
+  useEffect(() => {
+    async function fetchVersion() {
+      const _version = await getVersion();
+      setVersion(_version.version);
+    }
+    fetchVersion();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+ 
+
+  useEffect(() => {
+    dispatch({ type: "RESET" });
+    setPageNumber(1);
+  }, [searchParam]);
+
+  useEffect(() => {
+    async function fetchData() {
+      const companyId = user.companyId;
+      const planConfigs = await getPlanCompany(undefined, companyId);
+
+      setShowCampaigns(planConfigs.plan.useCampaigns);
+      setShowKanban(planConfigs.plan.useKanban);
+      setShowOpenAi(planConfigs.plan.useOpenAi);
+      setShowIntegrations(planConfigs.plan.useIntegrations);
+      setShowSchedules(planConfigs.plan.useSchedules);
+      setShowInternalChat(planConfigs.plan.useInternalChat);
+      setShowExternalApi(planConfigs.plan.useExternalApi);
+    }
+    fetchData();
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, []);
+
+
+
+  useEffect(() => {
+    const delayDebounceFn = setTimeout(() => {
+      fetchChats();
+    }, 500);
+    return () => clearTimeout(delayDebounceFn);
+    // eslint-disable-next-line react-hooks/exhaustive-deps
+  }, [searchParam, pageNumber]);
+
+  useEffect(() => {
+    const companyId = localStorage.getItem("companyId");
+    const socket = socketManager.getSocket(companyId);
+
+    socket.on(`company-${companyId}-chat`, (data) => {
+      if (data.action === "new-message") {
+        dispatch({ type: "CHANGE_CHAT", payload: data });
+      }
+      if (data.action === "update") {
+        dispatch({ type: "CHANGE_CHAT", payload: data });
+      }
+    });
+    return () => {
+      socket.disconnect();
+    };
+  }, [socketManager]);
+
+  useEffect(() => {
+    let unreadsCount = 0;
+    if (chats.length > 0) {
+      for (let chat of chats) {
+        for (let chatUser of chat.users) {
+          if (chatUser.userId === user.id) {
+            unreadsCount += chatUser.unreads;
+          }
+        }
+      }
+    }
+    if (unreadsCount > 0) {
+      setInvisible(false);
+    } else {
+      setInvisible(true);
+    }
+  }, [chats, user.id]);
+
+  useEffect(() => {
+    if (localStorage.getItem("cshow")) {
+      setShowCampaigns(true);
+    }
+  }, []);
+
+  useEffect(() => {
+    const delayDebounceFn = setTimeout(() => {
+      if (whatsApps.length > 0) {
+        const offlineWhats = whatsApps.filter((whats) => {
+          return (
+            whats.status === "qrcode" ||
+            whats.status === "PAIRING" ||
+            whats.status === "DISCONNECTED" ||
+            whats.status === "TIMEOUT" ||
+            whats.status === "OPENING"
+          );
+        });
+        if (offlineWhats.length > 0) {
+          setConnectionWarning(true);
+        } else {
+          setConnectionWarning(false);
+        }
+      }
+    }, 2000);
+    return () => clearTimeout(delayDebounceFn);
+  }, [whatsApps]);
+
+  const fetchChats = async () => {
+    try {
+      const { data } = await api.get("/chats/", {
+        params: { searchParam, pageNumber },
+      });
+      dispatch({ type: "LOAD_CHATS", payload: data.records });
+    } catch (err) {
+      toastError(err);
+    }
+  };
+
+  const handleClickLogout = () => {
+    //handleCloseMenu();
+    handleLogout();
+  };
+
+  return (
+    <div onClick={drawerClose}>
+      <Can
+        role={user.profile}
+        perform="dashboard:view"
+        yes={() => (
+          <ListItemLink
+            to="/"
+            primary="Dashboard"
+            icon={<DashboardOutlinedIcon />}
+          />
+        )}
+      />
+
+      <ListItemLink
+        to="/tickets"
+        primary={i18n.t("mainDrawer.listItems.tickets")}
+        icon={<WhatsAppIcon />}
+      />
+	  
+	{showKanban && (  
+	  <ListItemLink
+        to="/kanban"
+        primary={`Kanban`}
+        icon={<TableChartIcon />}
+      />
+	  )}
+
+
+      <ListItemLink
+        to="/quick-messages"
+        primary={i18n.t("mainDrawer.listItems.quickMessages")}
+        icon={<FlashOnIcon />}
+      />
+	  
+	  <ListItemLink
+        to="/todolist"
+        primary={i18n.t("mainDrawer.listItems.tasks")}
+        icon={<BorderColorIcon />}
+      />
+
+      <ListItemLink
+        to="/contacts"
+        primary={i18n.t("mainDrawer.listItems.contacts")}
+        icon={<ContactPhoneOutlinedIcon />}
+      />
+
+      <ListItemLink
+        to="/schedules"
+        primary={i18n.t("mainDrawer.listItems.schedules")}
+        icon={<EventIcon />}
+      />
+
+      <ListItemLink
+        to="/tags"
+        primary={i18n.t("mainDrawer.listItems.tags")}
+        icon={<LocalOfferIcon />}
+      />
+
+      <ListItemLink
+        to="/chats"
+        primary={i18n.t("mainDrawer.listItems.chats")}
+        icon={
+          <Badge color="secondary" variant="dot" invisible={invisible}>
+            <ForumIcon />
+          </Badge>
+        }
+      />
+
+      <ListItemLink
+        to="/helps"
+        primary={i18n.t("mainDrawer.listItems.helps")}
+        icon={<HelpOutlineIcon />}
+      />
+
+      <Can
+        role={user.profile}
+        perform="drawer-admin-items:view"
+        yes={() => (
+          <>
+            <Divider />
+            <ListSubheader
+              hidden={collapsed}
+              style={{
+                position: "relative",
+                fontSize: "17px",
+                textAlign: "left",
+                paddingLeft: 20
+              }}
+              inset
+              color="inherit">
+              {i18n.t("mainDrawer.listItems.administration")}
+            </ListSubheader>
+			
+            {showCampaigns && (
+              <>
+                <ListItem
+                  button
+                  onClick={() => setOpenCampaignSubmenu((prev) => !prev)}
+                >
+                  <ListItemIcon>
+                    <EventAvailableIcon />
+                  </ListItemIcon>
+                  <ListItemText
+                    primary={i18n.t("mainDrawer.listItems.campaigns")}
+                  />
+                  {openCampaignSubmenu ? (
+                    <ExpandLessIcon />
+                  ) : (
+                    <ExpandMoreIcon />
+                  )}
+                </ListItem>
+                <Collapse
+                  style={{ paddingLeft: 15 }}
+                  in={openCampaignSubmenu}
+                  timeout="auto"
+                  unmountOnExit
+                >
+                  <List component="div" disablePadding>
+                    <ListItem onClick={() => history.push("/campaigns")} button>
+                      <ListItemIcon>
+                        <ListIcon />
+                      </ListItemIcon>
+                      <ListItemText primary="Listagem" />
+                    </ListItem>
+                    <ListItem
+                      onClick={() => history.push("/contact-lists")}
+                      button
+                    >
+                      <ListItemIcon>
+                        <PeopleIcon />
+                      </ListItemIcon>
+                      <ListItemText primary="Listas de Contatos" />
+                    </ListItem>
+                    <ListItem
+                      onClick={() => history.push("/campaigns-config")}
+                      button
+                    >
+                      <ListItemIcon>
+                        <SettingsOutlinedIcon />
+                      </ListItemIcon>
+                      <ListItemText primary="Configurações" />
+                    </ListItem>
+                  </List>
+                </Collapse>
+              </>
+            )}
+            {user.super && (
+              <ListItemLink
+                to="/announcements"
+                primary={i18n.t("mainDrawer.listItems.annoucements")}
+                icon={<AnnouncementIcon />}
+              />
+            )}
+            {showOpenAi && (
+              <ListItemLink
+                to="/prompts"
+                primary={i18n.t("mainDrawer.listItems.prompts")}
+                icon={<AllInclusive />}
+              />
+            )}
+
+            {showIntegrations && (
+              <ListItemLink
+                to="/queue-integration"
+                primary={i18n.t("mainDrawer.listItems.queueIntegration")}
+                icon={<DeviceHubOutlined />}
+              />
+            )}
+            <ListItemLink
+              to="/connections"
+              primary={i18n.t("mainDrawer.listItems.connections")}
+              icon={
+                <Badge badgeContent={connectionWarning ? "!" : 0} color="error">
+                  <SyncAltIcon />
+                </Badge>
+              }
+            />
+            <ListItemLink
+              to="/files"
+              primary={i18n.t("mainDrawer.listItems.files")}
+              icon={<AttachFile />}
+            />
+            <ListItemLink
+              to="/queues"
+              primary={i18n.t("mainDrawer.listItems.queues")}
+              icon={<AccountTreeOutlinedIcon />}
+            />
+            <ListItemLink
+              to="/users"
+              primary={i18n.t("mainDrawer.listItems.users")}
+              icon={<PeopleAltOutlinedIcon />}
+            />
+            {showExternalApi && (
+              <>
+                <ListItemLink
+                  to="/messages-api"
+                  primary={i18n.t("mainDrawer.listItems.messagesAPI")}
+                  icon={<CodeRoundedIcon />}
+                />
+              </>
+            )}
+            <ListItemLink
+              to="/financeiro"
+              primary={i18n.t("mainDrawer.listItems.financeiro")}
+              icon={<LocalAtmIcon />}
+            />
+
+            <ListItemLink
+              to="/settings"
+              primary={i18n.t("mainDrawer.listItems.settings")}
+              icon={<SettingsOutlinedIcon />}
+            />
+			
+			
+            {!collapsed && <React.Fragment>
+              <Divider />
+              {/* 
+              // IMAGEM NO MENU
+              <Hidden only={['sm', 'xs']}>
+                <img style={{ width: "100%", padding: "10px" }} src={logo} alt="image" />            
+              </Hidden> 
+              */}
+              <Typography style={{ fontSize: "12px", padding: "10px", textAlign: "right", fontWeight: "bold" }}>
+                {version}
+              </Typography>
+            </React.Fragment>
+            }
+			
+          </>
+        )}
+      />
+    </div>
+  );
+};
+
+export default MainListItems;

+ 518 - 0
frontend/src/layout/index.js

@@ -0,0 +1,518 @@
+import React, { useState, useContext, useEffect } from "react";
+import clsx from "clsx";
+import moment from "moment";
+import {
+  makeStyles,
+  Drawer,
+  AppBar,
+  Toolbar,
+  List,
+  Typography,
+  Divider,
+  MenuItem,
+  IconButton,
+  Menu,
+  useTheme,
+  useMediaQuery,
+} from "@material-ui/core";
+
+import MenuIcon from "@material-ui/icons/Menu";
+import ChevronLeftIcon from "@material-ui/icons/ChevronLeft";
+import AccountCircle from "@material-ui/icons/AccountCircle";
+import CachedIcon from "@material-ui/icons/Cached";
+
+import MainListItems from "./MainListItems";
+import NotificationsPopOver from "../components/NotificationsPopOver";
+import NotificationsVolume from "../components/NotificationsVolume";
+import UserModal from "../components/UserModal";
+import { AuthContext } from "../context/Auth/AuthContext";
+import BackdropLoading from "../components/BackdropLoading";
+import DarkMode from "../components/DarkMode";
+import { i18n } from "../translate/i18n";
+import toastError from "../errors/toastError";
+import AnnouncementsPopover from "../components/AnnouncementsPopover";
+
+import logo from "../assets/logo.png";
+import { SocketContext } from "../context/Socket/SocketContext";
+import ChatPopover from "../pages/Chat/ChatPopover";
+
+import { useDate } from "../hooks/useDate";
+
+import ColorModeContext from "../layout/themeContext";
+import Brightness4Icon from '@material-ui/icons/Brightness4';
+import Brightness7Icon from '@material-ui/icons/Brightness7';
+import LanguageControl from "../components/LanguageControl";
+import { LanguageOutlined } from "@material-ui/icons";
+
+const drawerWidth = 240;
+
+const useStyles = makeStyles((theme) => ({
+  root: {
+    display: "flex",
+    height: "100vh",
+    [theme.breakpoints.down("sm")]: {
+      height: "calc(100vh - 56px)",
+    },
+    backgroundColor: theme.palette.fancyBackground,
+    '& .MuiButton-outlinedPrimary': {
+      color: theme.mode === 'light' ? '#FFF' : '#FFF',
+	  //backgroundColor: theme.mode === 'light' ? '#682ee2' : '#682ee2',
+	backgroundColor: theme.mode === 'light' ? theme.palette.primary.main : '#1c1c1c',
+      //border: theme.mode === 'light' ? '1px solid rgba(0 124 102)' : '1px solid rgba(255, 255, 255, 0.5)',
+    },
+    '& .MuiTab-textColorPrimary.Mui-selected': {
+      color: theme.mode === 'light' ? 'Primary' : '#FFF',
+    }
+  },
+  avatar: {
+    width: "100%",
+  },
+  toolbar: {
+    paddingRight: 24, // keep right padding when drawer closed
+    color: theme.palette.dark.main,
+    background: theme.palette.barraSuperior,
+  },
+  toolbarIcon: {
+    display: "flex",
+    alignItems: "center",
+    justifyContent: "space-between",
+    padding: "0 8px",
+    minHeight: "48px",
+    [theme.breakpoints.down("sm")]: {
+      height: "48px"
+    }
+  },
+  appBar: {
+    zIndex: theme.zIndex.drawer + 1,
+    transition: theme.transitions.create(["width", "margin"], {
+      easing: theme.transitions.easing.sharp,
+      duration: theme.transitions.duration.leavingScreen,
+    }),
+  },
+  appBarShift: {
+    marginLeft: drawerWidth,
+    width: `calc(100% - ${drawerWidth}px)`,
+    transition: theme.transitions.create(["width", "margin"], {
+      easing: theme.transitions.easing.sharp,
+      duration: theme.transitions.duration.enteringScreen,
+    }),
+    [theme.breakpoints.down("sm")]: {
+      display: "none"
+    }
+  },
+  menuButton: {
+    marginRight: 36,
+  },
+  menuButtonHidden: {
+    display: "none",
+  },
+  title: {
+    flexGrow: 1,
+    fontSize: 14,
+    color: "white",
+  },
+  drawerPaper: {
+    position: "relative",
+    whiteSpace: "nowrap",
+    width: drawerWidth,
+    transition: theme.transitions.create("width", {
+      easing: theme.transitions.easing.sharp,
+      duration: theme.transitions.duration.enteringScreen,
+    }),
+    [theme.breakpoints.down("sm")]: {
+      width: "100%"
+    },
+    ...theme.scrollbarStylesSoft
+  },
+  drawerPaperClose: {
+    overflowX: "hidden",
+    transition: theme.transitions.create("width", {
+      easing: theme.transitions.easing.sharp,
+      duration: theme.transitions.duration.leavingScreen,
+    }),
+    width: theme.spacing(7),
+    [theme.breakpoints.up("sm")]: {
+      width: theme.spacing(9),
+    },
+    [theme.breakpoints.down("sm")]: {
+      width: "100%"
+    }
+  },
+  appBarSpacer: {
+    minHeight: "48px",
+  },
+  content: {
+    flex: 1,
+    overflow: "auto",
+
+  },
+  container: {
+    paddingTop: theme.spacing(4),
+    paddingBottom: theme.spacing(4),
+  },
+  paper: {
+    padding: theme.spacing(2),
+    display: "flex",
+    overflow: "auto",
+    flexDirection: "column"
+  },
+  containerWithScroll: {
+    flex: 1,
+    padding: theme.spacing(1),
+    overflowY: "scroll",
+    ...theme.scrollbarStyles,
+  },
+  NotificationsPopOver: {
+    // color: theme.barraSuperior.secondary.main,
+  },
+  logo: {
+    width: "80%",
+    height: "auto",
+    maxWidth: 180,
+    [theme.breakpoints.down("sm")]: {
+      width: "auto",
+      height: "80%",
+      maxWidth: 180,
+    },
+    logo: theme.logo
+  },
+}));
+
+const LoggedInLayout = ({ children, themeToggle }) => {
+  const classes = useStyles();
+  const [userModalOpen, setUserModalOpen] = useState(false);
+  const [anchorEl, setAnchorEl] = useState(null);
+  const [menuOpen, setMenuOpen] = useState(false);
+  const { handleLogout, loading } = useContext(AuthContext);
+  const [drawerOpen, setDrawerOpen] = useState(false);
+  const [drawerVariant, setDrawerVariant] = useState("permanent");
+  // const [dueDate, setDueDate] = useState("");
+  const { user } = useContext(AuthContext);
+
+  const theme = useTheme();
+  const { colorMode } = useContext(ColorModeContext);
+  const greaterThenSm = useMediaQuery(theme.breakpoints.up("sm"));
+
+  const [volume, setVolume] = useState(localStorage.getItem("volume") || 1);
+
+  const { dateToClient } = useDate();
+
+  // Languages
+  const [anchorElLanguage, setAnchorElLanguage] = useState(null);
+  const [menuLanguageOpen, setMenuLanguageOpen] = useState(false);
+
+
+  //################### CODIGOS DE TESTE #########################################
+  // useEffect(() => {
+  //   navigator.getBattery().then((battery) => {
+  //     console.log(`Battery Charging: ${battery.charging}`);
+  //     console.log(`Battery Level: ${battery.level * 100}%`);
+  //     console.log(`Charging Time: ${battery.chargingTime}`);
+  //     console.log(`Discharging Time: ${battery.dischargingTime}`);
+  //   })
+  // }, []);
+
+  // useEffect(() => {
+  //   const geoLocation = navigator.geolocation
+
+  //   geoLocation.getCurrentPosition((position) => {
+  //     let lat = position.coords.latitude;
+  //     let long = position.coords.longitude;
+
+  //     console.log('latitude: ', lat)
+  //     console.log('longitude: ', long)
+  //   })
+  // }, []);
+
+  // useEffect(() => {
+  //   const nucleos = window.navigator.hardwareConcurrency;
+
+  //   console.log('Nucleos: ', nucleos)
+  // }, []);
+
+  // useEffect(() => {
+  //   console.log('userAgent', navigator.userAgent)
+  //   if (
+  //     navigator.userAgent.match(/Android/i)
+  //     || navigator.userAgent.match(/webOS/i)
+  //     || navigator.userAgent.match(/iPhone/i)
+  //     || navigator.userAgent.match(/iPad/i)
+  //     || navigator.userAgent.match(/iPod/i)
+  //     || navigator.userAgent.match(/BlackBerry/i)
+  //     || navigator.userAgent.match(/Windows Phone/i)
+  //   ) {
+  //     console.log('é mobile ', true) //celular
+  //   }
+  //   else {
+  //     console.log('não é mobile: ', false) //nao é celular
+  //   }
+  // }, []);
+  //##############################################################################
+
+  const socketManager = useContext(SocketContext);
+
+  useEffect(() => {
+    if (document.body.offsetWidth > 1200) {
+      setDrawerOpen(true);
+    }
+  }, []);
+
+  useEffect(() => {
+    if (document.body.offsetWidth < 600) {
+      setDrawerVariant("temporary");
+    } else {
+      setDrawerVariant("permanent");
+    }
+  }, [drawerOpen]);
+
+  useEffect(() => {
+    const companyId = localStorage.getItem("companyId");
+    const userId = localStorage.getItem("userId");
+
+    const socket = socketManager.getSocket(companyId);
+
+    socket.on(`company-${companyId}-auth`, (data) => {
+      if (data.user.id === +userId) {
+        toastError("Sua conta foi acessada em outro computador.");
+        setTimeout(() => {
+          localStorage.clear();
+          window.location.reload();
+        }, 1000);
+      }
+    });
+
+    socket.emit("userStatus");
+    const interval = setInterval(() => {
+      socket.emit("userStatus");
+    }, 1000 * 60 * 5);
+
+    return () => {
+      socket.disconnect();
+      clearInterval(interval);
+    };
+  }, [socketManager]);
+
+  const handleMenu = (event) => {
+    setAnchorEl(event.currentTarget);
+    setMenuOpen(true);
+  };
+
+  const handlemenuLanguage = ( event ) => {
+    setAnchorElLanguage(event.currentTarget);
+    setMenuLanguageOpen( true );
+  }
+
+  const handleCloseMenu = () => {
+    setAnchorEl(null);
+    setMenuOpen(false);
+  };
+
+  const handleCloseMenuLanguage = (  ) => {
+    setAnchorElLanguage(null);
+    setMenuLanguageOpen(false);
+  }
+
+  const handleOpenUserModal = () => {
+    setUserModalOpen(true);
+    handleCloseMenu();
+  };
+
+  const handleClickLogout = () => {
+    handleCloseMenu();
+    handleLogout();
+  };
+
+  const drawerClose = () => {
+    if (document.body.offsetWidth < 600) {
+      setDrawerOpen(false);
+    }
+  };
+
+  const handleRefreshPage = () => {
+    window.location.reload(false);
+  }
+
+  const handleMenuItemClick = () => {
+    const { innerWidth: width } = window;
+    if (width <= 600) {
+      setDrawerOpen(false);
+    }
+  };
+
+  const toggleColorMode = () => {
+    colorMode.toggleColorMode();
+  }
+
+  if (loading) {
+    return <BackdropLoading />;
+  }
+
+  return (
+    <div className={classes.root}>
+      <Drawer
+        variant={drawerVariant}
+        className={drawerOpen ? classes.drawerPaper : classes.drawerPaperClose}
+        classes={{
+          paper: clsx(
+            classes.drawerPaper,
+            !drawerOpen && classes.drawerPaperClose
+          ),
+        }}
+        open={drawerOpen}
+      >
+        <div className={classes.toolbarIcon}>
+          <img src={logo} className={classes.logo} alt="logo" />
+          <IconButton onClick={() => setDrawerOpen(!drawerOpen)}>
+            <ChevronLeftIcon />
+          </IconButton>
+        </div>
+        <Divider />
+        <List className={classes.containerWithScroll}>
+          <MainListItems drawerClose={drawerClose} collapsed={!drawerOpen} />
+        </List>
+        <Divider />
+      </Drawer>
+      <UserModal
+        open={userModalOpen}
+        onClose={() => setUserModalOpen(false)}
+        userId={user?.id}
+      />
+      <AppBar
+        position="absolute"
+        className={clsx(classes.appBar, drawerOpen && classes.appBarShift)}
+        color="primary"
+      >
+        <Toolbar variant="dense" className={classes.toolbar}>
+          <IconButton
+            edge="start"
+            variant="contained"
+            aria-label="open drawer"
+            onClick={() => setDrawerOpen(!drawerOpen)}
+            className={clsx(
+              classes.menuButton,
+              drawerOpen && classes.menuButtonHidden
+            )}
+          >
+            <MenuIcon />
+          </IconButton>
+
+          <Typography
+            component="h2"
+            variant="h6"
+            color="inherit"
+            noWrap
+            className={classes.title}
+          >
+            {/* {greaterThenSm && user?.profile === "admin" && getDateAndDifDays(user?.company?.dueDate).difData < 7 ? ( */}
+            {greaterThenSm && user?.profile === "admin" && user?.company?.dueDate ? (
+              <>
+                {i18n.t("mainDrawer.appBar.greeting.hello")} <b>{user.name}</b>, {i18n.t("mainDrawer.appBar.greeting.welcome")} <b>{user?.company?.name}</b>! ({i18n.t("mainDrawer.appBar.greeting.active")} {dateToClient(user?.company?.dueDate)})
+              </>
+            ) : (
+              <>
+                {i18n.t("mainDrawer.appBar.greeting.hello")} <b>{user.name}</b>, {i18n.t("mainDrawer.appBar.greeting.welcome")} <b>{user?.company?.name}</b>!
+              </>
+            )}
+          </Typography>
+          
+          <div>
+            <IconButton edge="start">
+              <LanguageOutlined
+                aria-label="account of current user"
+                aria-controls="menu-appbar"
+                aria-haspopup="true"
+                onClick={handlemenuLanguage}
+                variant="contained"
+                style={{ color: "white",marginRight:10 }}
+              />
+            </IconButton>
+            <Menu
+              id="menu-appbar-language"
+              anchorEl={anchorElLanguage}
+              getContentAnchorEl={null}
+              anchorOrigin={{
+                vertical: "bottom",
+                horizontal: "right",
+              }}
+              transformOrigin={{
+                vertical: "top",
+                horizontal: "right",
+              }}
+              open={menuLanguageOpen}
+              onClose={handleCloseMenuLanguage}
+            >
+              <MenuItem>
+                <LanguageControl />
+              </MenuItem>
+            </Menu>
+          </div>          
+
+          <IconButton edge="start" onClick={toggleColorMode}>
+            {theme.mode === 'dark' ? <Brightness7Icon style={{ color: "white" }} /> : <Brightness4Icon style={{ color: "white" }} />}
+          </IconButton>
+
+          <NotificationsVolume
+            setVolume={setVolume}
+            volume={volume}
+          />
+
+          <IconButton
+            onClick={handleRefreshPage}
+            aria-label={i18n.t("mainDrawer.appBar.refresh")}
+            color="inherit"
+          >
+            <CachedIcon style={{ color: "white" }} />
+          </IconButton>
+
+          {user.id && <NotificationsPopOver volume={volume} />}
+
+          <AnnouncementsPopover />
+
+          <ChatPopover />
+
+          <div>
+            <IconButton
+              aria-label="account of current user"
+              aria-controls="menu-appbar"
+              aria-haspopup="true"
+              onClick={handleMenu}
+              variant="contained"
+              style={{ color: "white" }}
+            >
+              <AccountCircle />
+            </IconButton>
+            <Menu
+              id="menu-appbar"
+              anchorEl={anchorEl}
+              getContentAnchorEl={null}
+              anchorOrigin={{
+                vertical: "bottom",
+                horizontal: "right",
+              }}
+              transformOrigin={{
+                vertical: "top",
+                horizontal: "right",
+              }}
+              open={menuOpen}
+              onClose={handleCloseMenu}
+            >
+              <MenuItem onClick={handleOpenUserModal}>
+                {i18n.t("mainDrawer.appBar.user.profile")}
+              </MenuItem>
+              <MenuItem onClick={handleClickLogout}>
+                {i18n.t("mainDrawer.appBar.user.logout")}
+              </MenuItem>
+            </Menu>
+          </div>
+        </Toolbar>
+      </AppBar>
+      <main className={classes.content}>
+        <div className={classes.appBarSpacer} />
+
+        {children ? children : null}
+      </main>
+    </div>
+  );
+};
+
+export default LoggedInLayout;

+ 5 - 0
frontend/src/layout/themeContext.js

@@ -0,0 +1,5 @@
+import React from "react";
+
+const ColorModeContext = React.createContext({ toggleColorMode: () => {} });
+
+export default ColorModeContext;