mirror of
https://github.com/cheveguerra/whaticket-community.git
synced 2026-04-19 20:29:17 +00:00
feat: show an alert when connection with whatsapp brokes
This commit is contained in:
@@ -2,6 +2,7 @@ import { Request, Response } from "express";
|
|||||||
import { getWbot } from "../libs/wbot";
|
import { getWbot } from "../libs/wbot";
|
||||||
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
|
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
|
||||||
import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession";
|
import { StartWhatsAppSession } from "../services/WbotServices/StartWhatsAppSession";
|
||||||
|
import UpdateWhatsAppService from "../services/WhatsappService/UpdateWhatsAppService";
|
||||||
|
|
||||||
const store = async (req: Request, res: Response): Promise<Response> => {
|
const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const { whatsappId } = req.params;
|
const { whatsappId } = req.params;
|
||||||
@@ -14,9 +15,11 @@ const store = async (req: Request, res: Response): Promise<Response> => {
|
|||||||
|
|
||||||
const update = async (req: Request, res: Response): Promise<Response> => {
|
const update = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const { whatsappId } = req.params;
|
const { whatsappId } = req.params;
|
||||||
const whatsapp = await ShowWhatsAppService(whatsappId);
|
|
||||||
|
|
||||||
await whatsapp.update({ session: "" });
|
const { whatsapp } = await UpdateWhatsAppService({
|
||||||
|
whatsappId,
|
||||||
|
whatsappData: { session: "" }
|
||||||
|
});
|
||||||
|
|
||||||
StartWhatsAppSession(whatsapp);
|
StartWhatsAppSession(whatsapp);
|
||||||
|
|
||||||
|
|||||||
@@ -21,12 +21,6 @@ export const initWbot = async (whatsapp: Whatsapp): Promise<Session> => {
|
|||||||
sessionCfg = JSON.parse(whatsapp.session);
|
sessionCfg = JSON.parse(whatsapp.session);
|
||||||
}
|
}
|
||||||
|
|
||||||
const currentSessionIndex = sessions.findIndex(s => s.id === whatsapp.id);
|
|
||||||
if (currentSessionIndex !== -1) {
|
|
||||||
sessions[currentSessionIndex].destroy();
|
|
||||||
sessions.splice(currentSessionIndex, 1);
|
|
||||||
}
|
|
||||||
|
|
||||||
const wbot: Session = new Client({
|
const wbot: Session = new Client({
|
||||||
session: sessionCfg
|
session: sessionCfg
|
||||||
});
|
});
|
||||||
|
|||||||
@@ -7,6 +7,7 @@ import Whatsapp from "../../models/Whatsapp";
|
|||||||
interface WhatsappData {
|
interface WhatsappData {
|
||||||
name?: string;
|
name?: string;
|
||||||
status?: string;
|
status?: string;
|
||||||
|
session?: string;
|
||||||
isDefault?: boolean;
|
isDefault?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -29,7 +30,7 @@ const UpdateWhatsAppService = async ({
|
|||||||
isDefault: Yup.boolean()
|
isDefault: Yup.boolean()
|
||||||
});
|
});
|
||||||
|
|
||||||
const { name, status, isDefault } = whatsappData;
|
const { name, status, isDefault, session } = whatsappData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await schema.validate({ name, status, isDefault });
|
await schema.validate({ name, status, isDefault });
|
||||||
@@ -58,6 +59,7 @@ const UpdateWhatsAppService = async ({
|
|||||||
await whatsapp.update({
|
await whatsapp.update({
|
||||||
name,
|
name,
|
||||||
status,
|
status,
|
||||||
|
session,
|
||||||
isDefault
|
isDefault
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
import React from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
@@ -15,6 +15,9 @@ import GroupIcon from "@material-ui/icons/Group";
|
|||||||
import ContactPhoneIcon from "@material-ui/icons/ContactPhone";
|
import ContactPhoneIcon from "@material-ui/icons/ContactPhone";
|
||||||
|
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n";
|
||||||
|
import { Badge } from "@material-ui/core";
|
||||||
|
|
||||||
|
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext";
|
||||||
|
|
||||||
function ListItemLink(props) {
|
function ListItemLink(props) {
|
||||||
const { icon, primary, to, className } = props;
|
const { icon, primary, to, className } = props;
|
||||||
@@ -39,13 +42,44 @@ function ListItemLink(props) {
|
|||||||
|
|
||||||
const MainListItems = () => {
|
const MainListItems = () => {
|
||||||
const userProfile = localStorage.getItem("profile");
|
const userProfile = localStorage.getItem("profile");
|
||||||
|
const { whatsApps } = useContext(WhatsAppsContext);
|
||||||
|
const [connectionWarning, setConnectionWarning] = useState(false);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const delayDebounceFn = setTimeout(() => {
|
||||||
|
if (whatsApps.length > 0) {
|
||||||
|
const offlineWhats = whatsApps.filter(whats => {
|
||||||
|
if (
|
||||||
|
whats.status === "qrcode" ||
|
||||||
|
whats.status === "PAIRING" ||
|
||||||
|
whats.status === "DISCONNECTED" ||
|
||||||
|
whats.status === "TIMEOUT" ||
|
||||||
|
whats.status === "OPENING"
|
||||||
|
)
|
||||||
|
return true;
|
||||||
|
else return false;
|
||||||
|
});
|
||||||
|
if (offlineWhats.length > 0) {
|
||||||
|
setConnectionWarning(true);
|
||||||
|
} else {
|
||||||
|
setConnectionWarning(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, 2000);
|
||||||
|
return () => clearTimeout(delayDebounceFn);
|
||||||
|
}, [whatsApps]);
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
<ListItemLink to="/" primary="Dashboard" icon={<DashboardIcon />} />
|
<ListItemLink to="/" primary="Dashboard" icon={<DashboardIcon />} />
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/connections"
|
to="/connections"
|
||||||
primary={i18n.t("mainDrawer.listItems.connections")}
|
primary={i18n.t("mainDrawer.listItems.connections")}
|
||||||
icon={<SyncAltIcon />}
|
icon={
|
||||||
|
<Badge badgeContent={connectionWarning ? "!" : 0} color="error">
|
||||||
|
<SyncAltIcon />
|
||||||
|
</Badge>
|
||||||
|
}
|
||||||
/>
|
/>
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/tickets"
|
to="/tickets"
|
||||||
|
|||||||
@@ -106,7 +106,7 @@ const useStyles = makeStyles(theme => ({
|
|||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const LoggedInLayout = ({ appTitle, children }) => {
|
const LoggedInLayout = ({ children }) => {
|
||||||
const drawerState = localStorage.getItem("drawerOpen");
|
const drawerState = localStorage.getItem("drawerOpen");
|
||||||
const userId = +localStorage.getItem("userId");
|
const userId = +localStorage.getItem("userId");
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|||||||
@@ -11,6 +11,48 @@ const useAuth = () => {
|
|||||||
const [isAuth, setIsAuth] = useState(false);
|
const [isAuth, setIsAuth] = useState(false);
|
||||||
const [loading, setLoading] = useState(true);
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
api.interceptors.response.use(
|
||||||
|
response => {
|
||||||
|
return response;
|
||||||
|
},
|
||||||
|
async error => {
|
||||||
|
const originalRequest = error.config;
|
||||||
|
if (error?.response?.status === 403 && !originalRequest._retry) {
|
||||||
|
originalRequest._retry = true;
|
||||||
|
|
||||||
|
const { data } = await api.post("/auth/refresh_token");
|
||||||
|
if (data) {
|
||||||
|
localStorage.setItem("token", JSON.stringify(data.token));
|
||||||
|
api.defaults.headers.Authorization = `Bearer ${data.token}`;
|
||||||
|
}
|
||||||
|
return api(originalRequest);
|
||||||
|
}
|
||||||
|
if (error?.response?.status === 401) {
|
||||||
|
localStorage.removeItem("token");
|
||||||
|
localStorage.removeItem("username");
|
||||||
|
localStorage.removeItem("profile");
|
||||||
|
localStorage.removeItem("userId");
|
||||||
|
api.defaults.headers.Authorization = undefined;
|
||||||
|
setIsAuth(false);
|
||||||
|
}
|
||||||
|
return Promise.reject(error);
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const token = localStorage.getItem("token");
|
const token = localStorage.getItem("token");
|
||||||
if (token) {
|
if (token) {
|
||||||
@@ -18,48 +60,6 @@ const useAuth = () => {
|
|||||||
setIsAuth(true);
|
setIsAuth(true);
|
||||||
}
|
}
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
|
|
||||||
api.interceptors.response.use(
|
|
||||||
response => {
|
|
||||||
return response;
|
|
||||||
},
|
|
||||||
async error => {
|
|
||||||
const originalRequest = error.config;
|
|
||||||
if (error?.response?.status === 403 && !originalRequest._retry) {
|
|
||||||
originalRequest._retry = true;
|
|
||||||
|
|
||||||
const { data } = await api.post("/auth/refresh_token");
|
|
||||||
if (data) {
|
|
||||||
localStorage.setItem("token", JSON.stringify(data.token));
|
|
||||||
api.defaults.headers.Authorization = `Bearer ${data.token}`;
|
|
||||||
}
|
|
||||||
return api(originalRequest);
|
|
||||||
}
|
|
||||||
if (error?.response?.status === 401) {
|
|
||||||
localStorage.removeItem("token");
|
|
||||||
localStorage.removeItem("username");
|
|
||||||
localStorage.removeItem("profile");
|
|
||||||
localStorage.removeItem("userId");
|
|
||||||
api.defaults.headers.Authorization = undefined;
|
|
||||||
setIsAuth(false);
|
|
||||||
}
|
|
||||||
return Promise.reject(error);
|
|
||||||
}
|
|
||||||
);
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleLogin = async (e, user) => {
|
const handleLogin = async (e, user) => {
|
||||||
@@ -106,7 +106,7 @@ const useAuth = () => {
|
|||||||
history.push("/login");
|
history.push("/login");
|
||||||
};
|
};
|
||||||
|
|
||||||
return { isAuth, loading, handleLogin, handleLogout };
|
return { isAuth, setIsAuth, loading, handleLogin, handleLogout };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default useAuth;
|
export default useAuth;
|
||||||
|
|||||||
17
frontend/src/context/WhatsApp/WhatsAppsContext.js
Normal file
17
frontend/src/context/WhatsApp/WhatsAppsContext.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import React, { createContext } from "react";
|
||||||
|
|
||||||
|
import useWhatsApps from "./useWhatsApps";
|
||||||
|
|
||||||
|
const WhatsAppsContext = createContext();
|
||||||
|
|
||||||
|
const WhatsAppsProvider = ({ children }) => {
|
||||||
|
const { loading, whatsApps } = useWhatsApps();
|
||||||
|
|
||||||
|
return (
|
||||||
|
<WhatsAppsContext.Provider value={{ whatsApps, loading }}>
|
||||||
|
{children}
|
||||||
|
</WhatsAppsContext.Provider>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export { WhatsAppsContext, WhatsAppsProvider };
|
||||||
114
frontend/src/context/WhatsApp/useWhatsApps.js
Normal file
114
frontend/src/context/WhatsApp/useWhatsApps.js
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { useState, useEffect, useReducer } from "react";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import openSocket from "socket.io-client";
|
||||||
|
|
||||||
|
import api from "../../services/api";
|
||||||
|
import { i18n } from "../../translate/i18n";
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true);
|
||||||
|
const fetchSession = async () => {
|
||||||
|
try {
|
||||||
|
const { data } = await api.get("/whatsapp/");
|
||||||
|
dispatch({ type: "LOAD_WHATSAPPS", payload: data });
|
||||||
|
setLoading(false);
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false);
|
||||||
|
const errorMsg = err.response?.data?.error;
|
||||||
|
if (errorMsg) {
|
||||||
|
if (i18n.exists(`backendErrors.${errorMsg}`)) {
|
||||||
|
toast.error(i18n.t(`backendErrors.${errorMsg}`));
|
||||||
|
} else {
|
||||||
|
toast.error(err.response.data.error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error("Unknown error");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
fetchSession();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||||
|
|
||||||
|
socket.on("whatsapp", data => {
|
||||||
|
if (data.action === "update") {
|
||||||
|
dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("whatsapp", data => {
|
||||||
|
if (data.action === "delete") {
|
||||||
|
dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
socket.on("whatsappSession", data => {
|
||||||
|
if (data.action === "update") {
|
||||||
|
dispatch({ type: "UPDATE_SESSION", payload: data.session });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
return { whatsApps, loading };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useWhatsApps;
|
||||||
@@ -1,5 +1,4 @@
|
|||||||
import React, { useState, useEffect, useReducer, useCallback } from "react";
|
import React, { useState, useCallback, useContext } from "react";
|
||||||
import openSocket from "socket.io-client";
|
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { format, parseISO } from "date-fns";
|
import { format, parseISO } from "date-fns";
|
||||||
|
|
||||||
@@ -16,13 +15,13 @@ import {
|
|||||||
Paper,
|
Paper,
|
||||||
Tooltip,
|
Tooltip,
|
||||||
Typography,
|
Typography,
|
||||||
|
CircularProgress,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core";
|
||||||
import {
|
import {
|
||||||
Edit,
|
Edit,
|
||||||
CheckCircle,
|
CheckCircle,
|
||||||
SignalCellularConnectedNoInternet2Bar,
|
SignalCellularConnectedNoInternet2Bar,
|
||||||
SignalCellularConnectedNoInternet0Bar,
|
SignalCellularConnectedNoInternet0Bar,
|
||||||
Schedule,
|
|
||||||
SignalCellular4Bar,
|
SignalCellular4Bar,
|
||||||
CropFree,
|
CropFree,
|
||||||
DeleteOutline,
|
DeleteOutline,
|
||||||
@@ -37,58 +36,9 @@ import TableRowSkeleton from "../../components/TableRowSkeleton";
|
|||||||
import api from "../../services/api";
|
import api from "../../services/api";
|
||||||
import WhatsAppModal from "../../components/WhatsAppModal";
|
import WhatsAppModal from "../../components/WhatsAppModal";
|
||||||
import ConfirmationModal from "../../components/ConfirmationModal";
|
import ConfirmationModal from "../../components/ConfirmationModal";
|
||||||
import ButtonWithSpinner from "../../components/ButtonWithSpinner";
|
|
||||||
import QrcodeModal from "../../components/QrcodeModal";
|
import QrcodeModal from "../../components/QrcodeModal";
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n";
|
||||||
|
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext";
|
||||||
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 useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
mainPaper: {
|
mainPaper: {
|
||||||
@@ -112,6 +62,9 @@ const useStyles = makeStyles(theme => ({
|
|||||||
tooltipPopper: {
|
tooltipPopper: {
|
||||||
textAlign: "center",
|
textAlign: "center",
|
||||||
},
|
},
|
||||||
|
buttonProgress: {
|
||||||
|
color: green[500],
|
||||||
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
const CustomToolTip = ({ title, content, children }) => {
|
const CustomToolTip = ({ title, content, children }) => {
|
||||||
@@ -141,12 +94,10 @@ const CustomToolTip = ({ title, content, children }) => {
|
|||||||
const Connections = () => {
|
const Connections = () => {
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
const [whatsApps, dispatch] = useReducer(reducer, []);
|
const { whatsApps, loading } = useContext(WhatsAppsContext);
|
||||||
|
|
||||||
const [whatsAppModalOpen, setWhatsAppModalOpen] = useState(false);
|
const [whatsAppModalOpen, setWhatsAppModalOpen] = useState(false);
|
||||||
const [qrModalOpen, setQrModalOpen] = useState(false);
|
const [qrModalOpen, setQrModalOpen] = useState(false);
|
||||||
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null);
|
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null);
|
||||||
const [loading, setLoading] = useState(true);
|
|
||||||
const [confirmModalOpen, setConfirmModalOpen] = useState(false);
|
const [confirmModalOpen, setConfirmModalOpen] = useState(false);
|
||||||
const confirmationModalInitialState = {
|
const confirmationModalInitialState = {
|
||||||
action: "",
|
action: "",
|
||||||
@@ -159,63 +110,19 @@ const Connections = () => {
|
|||||||
confirmationModalInitialState
|
confirmationModalInitialState
|
||||||
);
|
);
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setLoading(true);
|
|
||||||
const fetchSession = async () => {
|
|
||||||
try {
|
|
||||||
const { data } = await api.get("/whatsapp/");
|
|
||||||
dispatch({ type: "LOAD_WHATSAPPS", payload: data });
|
|
||||||
setLoading(false);
|
|
||||||
} catch (err) {
|
|
||||||
setLoading(false);
|
|
||||||
const errorMsg = err.response?.data?.error;
|
|
||||||
if (errorMsg) {
|
|
||||||
if (i18n.exists(`backendErrors.${errorMsg}`)) {
|
|
||||||
toast.error(i18n.t(`backendErrors.${errorMsg}`));
|
|
||||||
} else {
|
|
||||||
toast.error(err.response.data.error);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
toast.error("Unknown error");
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
fetchSession();
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
|
||||||
|
|
||||||
socket.on("whatsapp", data => {
|
|
||||||
if (data.action === "update") {
|
|
||||||
dispatch({ type: "UPDATE_WHATSAPPS", payload: data.whatsapp });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("whatsapp", data => {
|
|
||||||
if (data.action === "delete") {
|
|
||||||
dispatch({ type: "DELETE_WHATSAPPS", payload: data.whatsappId });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
socket.on("whatsappSession", data => {
|
|
||||||
if (data.action === "update") {
|
|
||||||
dispatch({ type: "UPDATE_SESSION", payload: data.session });
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
|
||||||
socket.disconnect();
|
|
||||||
};
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleStartWhatsAppSession = async whatsAppId => {
|
const handleStartWhatsAppSession = async whatsAppId => {
|
||||||
try {
|
try {
|
||||||
await api.post(`/whatsappsession/${whatsAppId}`);
|
await api.post(`/whatsappsession/${whatsAppId}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
const errorMsg = err.response?.data?.error;
|
||||||
if (err.response && err.response.data && err.response.data.error) {
|
if (errorMsg) {
|
||||||
toast.error(err.response.data.error);
|
if (i18n.exists(`backendErrors.${errorMsg}`)) {
|
||||||
|
toast.error(i18n.t(`backendErrors.${errorMsg}`));
|
||||||
|
} else {
|
||||||
|
toast.error(err.response.data.error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -224,9 +131,15 @@ const Connections = () => {
|
|||||||
try {
|
try {
|
||||||
await api.put(`/whatsappsession/${whatsAppId}`);
|
await api.put(`/whatsappsession/${whatsAppId}`);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
const errorMsg = err.response?.data?.error;
|
||||||
if (err.response && err.response.data && err.response.data.error) {
|
if (errorMsg) {
|
||||||
toast.error(err.response.data.error);
|
if (i18n.exists(`backendErrors.${errorMsg}`)) {
|
||||||
|
toast.error(i18n.t(`backendErrors.${errorMsg}`));
|
||||||
|
} else {
|
||||||
|
toast.error(err.response.data.error);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
toast.error("Unknown error");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
@@ -256,108 +169,6 @@ const Connections = () => {
|
|||||||
setWhatsAppModalOpen(true);
|
setWhatsAppModalOpen(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
const renderActionButtons = whatsApp => {
|
|
||||||
return (
|
|
||||||
<>
|
|
||||||
{whatsApp.status === "qrcode" && (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="contained"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => handleOpenQrModal(whatsApp)}
|
|
||||||
>
|
|
||||||
{i18n.t("connections.buttons.qrcode")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{whatsApp.status === "DISCONNECTED" && (
|
|
||||||
<>
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
color="primary"
|
|
||||||
onClick={() => handleStartWhatsAppSession(whatsApp.id)}
|
|
||||||
>
|
|
||||||
{i18n.t("connections.buttons.tryAgain")}
|
|
||||||
</Button>{" "}
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
color="secondary"
|
|
||||||
onClick={() => handleRequestNewQrCode(whatsApp.id)}
|
|
||||||
>
|
|
||||||
{i18n.t("connections.buttons.newQr")}
|
|
||||||
</Button>
|
|
||||||
</>
|
|
||||||
)}
|
|
||||||
{(whatsApp.status === "CONNECTED" ||
|
|
||||||
whatsApp.status === "PAIRING" ||
|
|
||||||
whatsApp.status === "TIMEOUT") && (
|
|
||||||
<Button
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
color="secondary"
|
|
||||||
onClick={() => {
|
|
||||||
handleOpenConfirmationModal("disconnect", whatsApp.id);
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{i18n.t("connections.buttons.disconnect")}
|
|
||||||
</Button>
|
|
||||||
)}
|
|
||||||
{whatsApp.status === "OPENING" && (
|
|
||||||
<ButtonWithSpinner
|
|
||||||
size="small"
|
|
||||||
variant="outlined"
|
|
||||||
loading={true}
|
|
||||||
color="default"
|
|
||||||
>
|
|
||||||
{i18n.t("connections.buttons.connecting")}
|
|
||||||
</ButtonWithSpinner>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const renderStatusToolTips = whatsApp => {
|
|
||||||
return (
|
|
||||||
<div className={classes.customTableCell}>
|
|
||||||
{whatsApp.status === "DISCONNECTED" && (
|
|
||||||
<CustomToolTip
|
|
||||||
title={i18n.t("connections.toolTips.disconnected.title")}
|
|
||||||
content={i18n.t("connections.toolTips.disconnected.content")}
|
|
||||||
>
|
|
||||||
<SignalCellularConnectedNoInternet0Bar color="secondary" />
|
|
||||||
</CustomToolTip>
|
|
||||||
)}
|
|
||||||
{whatsApp.status === "OPENING" && (
|
|
||||||
<CustomToolTip title={i18n.t("connections.toolTips.opening.title")}>
|
|
||||||
<Schedule color="disabled" />
|
|
||||||
</CustomToolTip>
|
|
||||||
)}
|
|
||||||
{whatsApp.status === "qrcode" && (
|
|
||||||
<CustomToolTip
|
|
||||||
title={i18n.t("connections.toolTips.qrcode.title")}
|
|
||||||
content={i18n.t("connections.toolTips.qrcode.content")}
|
|
||||||
>
|
|
||||||
<CropFree />
|
|
||||||
</CustomToolTip>
|
|
||||||
)}
|
|
||||||
{whatsApp.status === "CONNECTED" && (
|
|
||||||
<CustomToolTip title={i18n.t("connections.toolTips.connected.title")}>
|
|
||||||
<SignalCellular4Bar style={{ color: green[500] }} />
|
|
||||||
</CustomToolTip>
|
|
||||||
)}
|
|
||||||
{(whatsApp.status === "TIMEOUT" || whatsApp.status === "PAIRING") && (
|
|
||||||
<CustomToolTip
|
|
||||||
title={i18n.t("connections.toolTips.timeout.title")}
|
|
||||||
content={i18n.t("connections.toolTips.timeout.content")}
|
|
||||||
>
|
|
||||||
<SignalCellularConnectedNoInternet2Bar color="secondary" />
|
|
||||||
</CustomToolTip>
|
|
||||||
)}
|
|
||||||
</div>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const handleOpenConfirmationModal = (action, whatsAppId) => {
|
const handleOpenConfirmationModal = (action, whatsAppId) => {
|
||||||
if (action === "disconnect") {
|
if (action === "disconnect") {
|
||||||
setConfirmModalInfo({
|
setConfirmModalInfo({
|
||||||
@@ -418,6 +229,101 @@ const Connections = () => {
|
|||||||
setConfirmModalInfo(confirmationModalInitialState);
|
setConfirmModalInfo(confirmationModalInitialState);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const renderActionButtons = whatsApp => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{whatsApp.status === "qrcode" && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => handleOpenQrModal(whatsApp)}
|
||||||
|
>
|
||||||
|
{i18n.t("connections.buttons.qrcode")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{whatsApp.status === "DISCONNECTED" && (
|
||||||
|
<>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => handleStartWhatsAppSession(whatsApp.id)}
|
||||||
|
>
|
||||||
|
{i18n.t("connections.buttons.tryAgain")}
|
||||||
|
</Button>{" "}
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => handleRequestNewQrCode(whatsApp.id)}
|
||||||
|
>
|
||||||
|
{i18n.t("connections.buttons.newQr")}
|
||||||
|
</Button>
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
{(whatsApp.status === "CONNECTED" ||
|
||||||
|
whatsApp.status === "PAIRING" ||
|
||||||
|
whatsApp.status === "TIMEOUT") && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="outlined"
|
||||||
|
color="secondary"
|
||||||
|
onClick={() => {
|
||||||
|
handleOpenConfirmationModal("disconnect", whatsApp.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{i18n.t("connections.buttons.disconnect")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{whatsApp.status === "OPENING" && (
|
||||||
|
<Button size="small" variant="outlined" disabled color="default">
|
||||||
|
{i18n.t("connections.buttons.connecting")}
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
const renderStatusToolTips = whatsApp => {
|
||||||
|
return (
|
||||||
|
<div className={classes.customTableCell}>
|
||||||
|
{whatsApp.status === "DISCONNECTED" && (
|
||||||
|
<CustomToolTip
|
||||||
|
title={i18n.t("connections.toolTips.disconnected.title")}
|
||||||
|
content={i18n.t("connections.toolTips.disconnected.content")}
|
||||||
|
>
|
||||||
|
<SignalCellularConnectedNoInternet0Bar color="secondary" />
|
||||||
|
</CustomToolTip>
|
||||||
|
)}
|
||||||
|
{whatsApp.status === "OPENING" && (
|
||||||
|
<CircularProgress size={24} className={classes.buttonProgress} />
|
||||||
|
)}
|
||||||
|
{whatsApp.status === "qrcode" && (
|
||||||
|
<CustomToolTip
|
||||||
|
title={i18n.t("connections.toolTips.qrcode.title")}
|
||||||
|
content={i18n.t("connections.toolTips.qrcode.content")}
|
||||||
|
>
|
||||||
|
<CropFree />
|
||||||
|
</CustomToolTip>
|
||||||
|
)}
|
||||||
|
{whatsApp.status === "CONNECTED" && (
|
||||||
|
<CustomToolTip title={i18n.t("connections.toolTips.connected.title")}>
|
||||||
|
<SignalCellular4Bar style={{ color: green[500] }} />
|
||||||
|
</CustomToolTip>
|
||||||
|
)}
|
||||||
|
{(whatsApp.status === "TIMEOUT" || whatsApp.status === "PAIRING") && (
|
||||||
|
<CustomToolTip
|
||||||
|
title={i18n.t("connections.toolTips.timeout.title")}
|
||||||
|
content={i18n.t("connections.toolTips.timeout.content")}
|
||||||
|
>
|
||||||
|
<SignalCellularConnectedNoInternet2Bar color="secondary" />
|
||||||
|
</CustomToolTip>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<MainContainer>
|
<MainContainer>
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import Settings from "../pages/Settings/";
|
|||||||
import Users from "../pages/Users";
|
import Users from "../pages/Users";
|
||||||
import Contacts from "../pages/Contacts/";
|
import Contacts from "../pages/Contacts/";
|
||||||
import { AuthProvider } from "../context/Auth/AuthContext";
|
import { AuthProvider } from "../context/Auth/AuthContext";
|
||||||
|
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
|
||||||
import Route from "./Route";
|
import Route from "./Route";
|
||||||
|
|
||||||
const Routes = () => {
|
const Routes = () => {
|
||||||
@@ -21,24 +22,26 @@ const Routes = () => {
|
|||||||
<Switch>
|
<Switch>
|
||||||
<Route exact path="/login" component={Login} />
|
<Route exact path="/login" component={Login} />
|
||||||
<Route exact path="/signup" component={Signup} />
|
<Route exact path="/signup" component={Signup} />
|
||||||
<LoggedInLayout>
|
<WhatsAppsProvider>
|
||||||
<Route exact path="/" component={Dashboard} isPrivate />
|
<LoggedInLayout>
|
||||||
<Route
|
<Route exact path="/" component={Dashboard} isPrivate />
|
||||||
exact
|
<Route
|
||||||
path="/tickets/:ticketId?"
|
exact
|
||||||
component={Tickets}
|
path="/tickets/:ticketId?"
|
||||||
isPrivate
|
component={Tickets}
|
||||||
/>
|
isPrivate
|
||||||
<Route
|
/>
|
||||||
exact
|
<Route
|
||||||
path="/connections"
|
exact
|
||||||
component={Connections}
|
path="/connections"
|
||||||
isPrivate
|
component={Connections}
|
||||||
/>
|
isPrivate
|
||||||
<Route exact path="/contacts" component={Contacts} isPrivate />
|
/>
|
||||||
<Route exact path="/users" component={Users} isPrivate />
|
<Route exact path="/contacts" component={Contacts} isPrivate />
|
||||||
<Route exact path="/Settings" component={Settings} isPrivate />
|
<Route exact path="/users" component={Users} isPrivate />
|
||||||
</LoggedInLayout>
|
<Route exact path="/Settings" component={Settings} isPrivate />
|
||||||
|
</LoggedInLayout>
|
||||||
|
</WhatsAppsProvider>
|
||||||
</Switch>
|
</Switch>
|
||||||
<ToastContainer autoClose={3000} />
|
<ToastContainer autoClose={3000} />
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
|
|||||||
@@ -65,9 +65,6 @@ const messages = {
|
|||||||
content:
|
content:
|
||||||
"Make sure your cell phone is connected to the internet and try again, or request a new QR Code",
|
"Make sure your cell phone is connected to the internet and try again, or request a new QR Code",
|
||||||
},
|
},
|
||||||
opening: {
|
|
||||||
title: "Starting session...",
|
|
||||||
},
|
|
||||||
qrcode: {
|
qrcode: {
|
||||||
title: "Waiting for QR Code read",
|
title: "Waiting for QR Code read",
|
||||||
content:
|
content:
|
||||||
|
|||||||
@@ -67,9 +67,6 @@ const messages = {
|
|||||||
content:
|
content:
|
||||||
"Asegúrese de que su teléfono celular esté conectado a Internet y vuelva a intentarlo o solicite un nuevo código QR",
|
"Asegúrese de que su teléfono celular esté conectado a Internet y vuelva a intentarlo o solicite un nuevo código QR",
|
||||||
},
|
},
|
||||||
opening: {
|
|
||||||
title: "Iniciando sesión ...",
|
|
||||||
},
|
|
||||||
qrcode: {
|
qrcode: {
|
||||||
title: "Esperando la lectura del código QR",
|
title: "Esperando la lectura del código QR",
|
||||||
content:
|
content:
|
||||||
|
|||||||
@@ -66,9 +66,6 @@ const messages = {
|
|||||||
content:
|
content:
|
||||||
"Certifique-se de que seu celular esteja conectado à internet e tente novamente, ou solicite um novo QR Code",
|
"Certifique-se de que seu celular esteja conectado à internet e tente novamente, ou solicite um novo QR Code",
|
||||||
},
|
},
|
||||||
opening: {
|
|
||||||
title: "Iniciando a sessão...",
|
|
||||||
},
|
|
||||||
qrcode: {
|
qrcode: {
|
||||||
title: "Esperando leitura do QR Code",
|
title: "Esperando leitura do QR Code",
|
||||||
content:
|
content:
|
||||||
|
|||||||
Reference in New Issue
Block a user