feat: Enable users to transfer to other queues and users with the same WhatsApp connection.

pull/22/head
adriano 2024-01-29 08:48:20 -03:00
parent d12a47f062
commit bc134c827c
25 changed files with 922 additions and 280 deletions

View File

@ -289,7 +289,7 @@ app.post('/api/session', async function (req, res) {
) )
}) })
if (whatsapp[0]['name'].split('->').length > 0) { if (whatsapp[0]['name']?.split('->')?.length > 0) {
whatsName = `${whatsapp[0]['name'].split('->')[0]} -> S${numberSession}` whatsName = `${whatsapp[0]['name'].split('->')[0]} -> S${numberSession}`
} else { } else {
whatsName = `${whatsapp[0]['name']} -> S${numberSession}` whatsName = `${whatsapp[0]['name']} -> S${numberSession}`

View File

@ -64,7 +64,11 @@ export const reportUserService = async (req: Request, res: Response): Promise<Re
const { userId, startDate, endDate } = req.query as IndexQuery const { userId, startDate, endDate } = req.query as IndexQuery
let usersProfile = await ListUserParamiterService({ profile: 'user' }) // let usersProfile = await ListUserParamiterService({ profile: 'user' })
let usersProfile = await ListUserParamiterService({
profile: "user",
raw: true
});
const sumUserOlineTime = await ShowUserServiceReport({ startDate, endDate, userId }); const sumUserOlineTime = await ShowUserServiceReport({ startDate, endDate, userId });
const closedByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'closed', userId }); const closedByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'closed', userId });

View File

@ -5,6 +5,12 @@ import AuthUserService from "../services/UserServices/AuthUserService";
import { SendRefreshToken } from "../helpers/SendRefreshToken"; import { SendRefreshToken } from "../helpers/SendRefreshToken";
import { RefreshTokenService } from "../services/AuthServices/RefreshTokenService"; import { RefreshTokenService } from "../services/AuthServices/RefreshTokenService";
import createOrUpdateOnlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService";
import { removeUserFromOlineList } from "../helpers/removeUserFromOnlineList";
// const usersSocket = require("./../libs/socket");
const usersSocket = require("../libs/socket");
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
const { email, password } = req.body; const { email, password } = req.body;
@ -15,6 +21,11 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
SendRefreshToken(res, refreshToken); SendRefreshToken(res, refreshToken);
const userOnline = await createOrUpdateOnlineUserService({
userId: serializedUser.id,
status: "online"
});
return res.status(200).json({ return res.status(200).json({
token, token,
user: serializedUser user: serializedUser
@ -47,5 +58,14 @@ export const remove = async (
): Promise<Response> => { ): Promise<Response> => {
res.clearCookie("jrt"); res.clearCookie("jrt");
const { userId } = req.params;
removeUserFromOlineList(userId);
const userOnline = await createOrUpdateOnlineUserService({
userId,
status: "offline"
});
return res.send(); return res.send();
}; };

View File

@ -64,6 +64,10 @@ import Contact from "../models/Contact";
import BotIsOnQueue from "../helpers/BotIsOnQueue"; import BotIsOnQueue from "../helpers/BotIsOnQueue";
import { setMessageAsRead } from "../helpers/SetMessageAsRead"; import { setMessageAsRead } from "../helpers/SetMessageAsRead";
import { getSettingValue } from "../helpers/WhaticketSettings"; import { getSettingValue } from "../helpers/WhaticketSettings";
import ListWhatsAppsForQueueService from "../services/WhatsappService/ListWhatsAppsForQueueService";
import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber";
import Whatsapp from "../models/Whatsapp";
import AppError from "../errors/AppError";
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const { const {
@ -129,17 +133,17 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
ticketData: { status: "open", userId: userId, queueId }, ticketData: { status: "open", userId: userId, queueId },
ticketId: ticket.id ticketId: ticket.id
}); });
} }
} }
if(!ticket){ if (!ticket) {
ticket = await CreateTicketService({ ticket = await CreateTicketService({
contactId, contactId,
status, status,
userId, userId,
queueId, queueId,
whatsappId whatsappId
}); });
} }
const io = getIO(); const io = getIO();
@ -249,24 +253,88 @@ export const update = async (
req.body.userId = null; req.body.userId = null;
} }
console.log("REQ.BODY: ", JSON.stringify(req.body, null, 6));
let ticketData: TicketData = req.body; let ticketData: TicketData = req.body;
if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") { if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") {
if (ticketData.transfer) { if (ticketData.transfer) {
const defaultWhatsapp: any = await GetDefaultWhatsApp({ const whatsappsByqueue = await ListWhatsAppsForQueueService(
userId: ticketData.userId ticketData.queueId
}); );
const _ticket: any = await Ticket.findByPk(ticketId); if (userOldInfo) {
let listTicketOpenPending: any = [];
if (defaultWhatsapp && ticketData.status != "open") { for (const w of whatsappsByqueue) {
await CheckContactOpenTickets( let whats = await ListWhatsAppsNumber(w.id);
_ticket.dataValues.contactId,
defaultWhatsapp.dataValues.id console.log("-------> WHATS: ", JSON.stringify(whats, null, 6));
); const ticket = await Ticket.findOne({
where: {
[Op.and]: [
{ contactId: userOldInfo.contactId },
{
whatsappId: {
[Op.in]: whats.whatsapps.map((w: any) => w.id)
}
},
{ status: { [Op.or]: ["open", "pending"] } }
]
}
});
if (ticket) {
listTicketOpenPending.push({
ticketId: ticket.id,
status: ticket.status,
userId: ticket.userId,
contactId: ticket.contactId,
whatsappId: ticket.whatsappId,
queueId: ticket.queueId
});
}
}
// console.log("userOldInfo: ", JSON.stringify(userOldInfo, null, 6));
// console.log("##########")
// console.log(
// "listTicketOpenPending: ",
// JSON.stringify(listTicketOpenPending)
// );
if (
listTicketOpenPending.filter(
(ob: any) => userOldInfo.whatsappId != ob.whatsappId
)?.length > 0
) {
throw new AppError("ERR_OTHER_OPEN_TICKET");
}
} }
ticketData.whatsappId = defaultWhatsapp.dataValues.id; //////////////////////////////////////////////
// const defaultWhatsapp: any = await GetDefaultWhatsApp({
// userId: ticketData.userId
// });
// console.log(
// "ticketData.userId: ",
// ticketData.userId,
// " | defaultWhatsapp: ",
// JSON.stringify(defaultWhatsapp, null, 6)
// );
// const _ticket: any = await Ticket.findByPk(ticketId);
// if (defaultWhatsapp && ticketData.status != "open") {
// await CheckContactOpenTickets(
// _ticket.dataValues.contactId,
// defaultWhatsapp.dataValues.id
// );
// }
// ticketData.whatsappId = defaultWhatsapp.dataValues.id;
} }
} }

View File

@ -10,16 +10,28 @@ import UpdateUserService from "../services/UserServices/UpdateUserService";
import ShowUserService from "../services/UserServices/ShowUserService"; import ShowUserService from "../services/UserServices/ShowUserService";
import DeleteUserService from "../services/UserServices/DeleteUserService"; import DeleteUserService from "../services/UserServices/DeleteUserService";
import ListUserParamiterService from "../services/UserServices/ListUserParamiterService" import ListUser from "../services/UserServices/ListUserParamiterService";
import User from "../models/User"; import User from "../models/User";
import { startWhoIsOnlineMonitor, stopWhoIsOnlineMonitor } from "../helpers/WhoIsOnlineMonitor" import {
import UserOnlineTIme from '../models/UserOnlineTime' startWhoIsOnlineMonitor,
stopWhoIsOnlineMonitor
} from "../helpers/WhoIsOnlineMonitor";
import UserOnlineTIme from "../models/UserOnlineTime";
import { format, subMonths } from "date-fns";
import { ptBR } from "date-fns/locale";
import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue";
import { splitDateTime } from "../helpers/SplitDateTime";
import ListUserByWhatsappQueuesService from "../services/UserServices/ListUserByWhatsappQueuesService";
import { json } from "sequelize";
import { getSettingValue } from "../helpers/WhaticketSettings";
type IndexQuery = { type IndexQuery = {
searchParam: string; searchParam: string;
pageNumber: string; pageNumber: string;
profile?: string; profile?: string;
userId: string;
}; };
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
@ -31,13 +43,21 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
profile profile
}); });
if (req.user.profile !== 'master') { if (req.user.profile !== "master") {
let auxUsers: Array<object> = []; let auxUsers: Array<object> = [];
// for (var user of users) {
// if (user.profile !== 'master') {
// auxUsers.push(user)
// }
// }
for (var user of users) { for (var user of users) {
if (user.profile !== 'master') { if (user.profile !== "master") {
auxUsers.push(user) if (req.user.profile == "supervisor" && user.profile == "admin")
continue;
auxUsers.push(user);
} }
} }
@ -46,34 +66,80 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
return res.json({ users, count, hasMore }); return res.json({ users, count, hasMore });
// const { users, count, hasMore } = await ListUsersService({ // const { users, count, hasMore } = await ListUsersService({
// searchParam, // searchParam,
// pageNumber // pageNumber
// }); // });
// if(req.user.profile!=='master'){ // if(req.user.profile!=='master'){
// let auxUsers: Array<object> = []; // let auxUsers: Array<object> = [];
// for (var user of users) { // for (var user of users) {
// if(user.profile!=='master'){ // if(user.profile!=='master'){
// auxUsers.push(user) // auxUsers.push(user)
// } // }
// } // }
// return res.json({ users: auxUsers, count, hasMore }); // return res.json({ users: auxUsers, count, hasMore });
// } // }
// return res.json({ users, count, hasMore }); // return res.json({ users, count, hasMore });
}; };
// export const usersByWhatsappQueue = async (req: Request, res: Response): Promise<Response> => {
// const { profile } = req.query as IndexQuery;
// const users = await ListUser({
// profile
// });
// return res.json({ users });
// };
export const all = async (req: Request, res: Response): Promise<Response> => {
const { userId, profile } = req.query as IndexQuery;
console.log(
"userId: ",
userId,
" | profile: ",
profile,
' | getSettingValue("queueTransferByWhatsappScope")?.value: ',
getSettingValue("queueTransferByWhatsappScope")?.value
);
if (getSettingValue("queueTransferByWhatsappScope")?.value == "enabled") {
const obj = await ListUserByWhatsappQueuesService(
userId,
'"admin", "user", "supervisor"'
);
const usersByWhatsqueue = obj.users;
const queues = obj.queues;
let userIds = usersByWhatsqueue.map((w: any) => w.userId);
const users = await ListUser({
userIds
});
return res.json({ users, queues });
} else {
const users = await ListUser({
profile
});
return res.json({ users });
}
};
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
const { email, password, name, profile, queueIds } = req.body; const { email, password, name, profile, queueIds } = req.body;
if (req.url === "/signup" && (await CheckSettingsHelper("userCreation")) === "disabled") { if (
req.url === "/signup" &&
(await CheckSettingsHelper("userCreation")) === "disabled"
) {
throw new AppError("ERR_USER_CREATION_DISABLED", 403); throw new AppError("ERR_USER_CREATION_DISABLED", 403);
} else if (req.url !== "/signup" && req.user.profile !== "master") { } else if (req.url !== "/signup" && req.user.profile !== "master") {
throw new AppError("ERR_NO_PERMISSION", 403); throw new AppError("ERR_NO_PERMISSION", 403);
@ -93,9 +159,8 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
user user
}); });
// await stopWhoIsOnlineMonitor() // await stopWhoIsOnlineMonitor()
await startWhoIsOnlineMonitor() await startWhoIsOnlineMonitor();
return res.status(200).json(user); return res.status(200).json(user);
}; };
@ -108,17 +173,18 @@ export const show = async (req: Request, res: Response): Promise<Response> => {
return res.status(200).json(user); return res.status(200).json(user);
}; };
export const logoutUser = async (
export const logoutUser = async (req: Request, res: Response): Promise<Response> => { req: Request,
res: Response
): Promise<Response> => {
const { userId } = req.params; const { userId } = req.params;
await stopWhoIsOnlineMonitor();
await stopWhoIsOnlineMonitor()
let onlineTime = { let onlineTime = {
userId: userId, userId: userId,
status: 'logout...' status: "logout..."
} };
const io = getIO(); const io = getIO();
io.emit("onlineStatus", { io.emit("onlineStatus", {
@ -126,25 +192,73 @@ export const logoutUser = async (req: Request, res: Response): Promise<Response>
userOnlineTime: onlineTime userOnlineTime: onlineTime
}); });
await startWhoIsOnlineMonitor() await startWhoIsOnlineMonitor();
// //
return res.status(200).json({}); return res.status(200).json({});
}; };
export const update = async ( export const update = async (
req: Request, req: Request,
res: Response res: Response
): Promise<Response> => { ): Promise<Response> => {
if (req.user.profile !== "admin" && req.user.profile !== "master") { if (
req.user.profile !== "admin" &&
req.user.profile !== "master" &&
req.user.profile !== "supervisor"
) {
throw new AppError("ERR_NO_PERMISSION", 403); throw new AppError("ERR_NO_PERMISSION", 403);
} }
const { userId } = req.params; const { userId } = req.params;
const userData = req.body; const userData = req.body;
const user = await UpdateUserService({ userData, userId }); const dateToday = splitDateTime(
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
);
const currentDate = new Date();
const tenMonthsAgo = subMonths(currentDate, 10);
const formattedDate = format(tenMonthsAgo, "yyyy-MM-dd");
console.log("dateToday.fullDate: ", dateToday.fullDate);
console.log("formattedDate 10 months ago: ", formattedDate);
const openByUserOnQueue: any[] = await CountTicketsByUserQueue({
startDate: formattedDate,
endDate: dateToday.fullDate,
status: "open",
clientChatStart: true,
userId: userId
});
// console.log('------> openByUserOnQueue: ', openByUserOnQueue)
// console.log()
// console.log('------> 1 userData.queueIds: ', userData.queueIds)
let userQueuesAttendance = [];
if ((openByUserOnQueue && openByUserOnQueue.length) > 0) {
userQueuesAttendance = openByUserOnQueue.filter(
(e: any) => !userData.queueIds.includes(e.queueId)
);
if (userQueuesAttendance && userQueuesAttendance.length > 0) {
const queueInAttendance = userQueuesAttendance.map(e => e.queueId);
const mergedSet = new Set([...userData.queueIds, ...queueInAttendance]);
// Convert the Set back to an array
userData.queueIds = Array.from(mergedSet);
// console.log('------> 2 userData.queueIds: ', userData.queueIds)
}
}
// console.log('userQueuesAttendance: ', userQueuesAttendance)
// return res.status(200).json({});
let user: any = await UpdateUserService({ userData, userId });
const io = getIO(); const io = getIO();
io.emit("user", { io.emit("user", {
@ -152,6 +266,8 @@ export const update = async (
user user
}); });
user.userQueuesAttendance = userQueuesAttendance;
return res.status(200).json(user); return res.status(200).json(user);
}; };
@ -165,26 +281,23 @@ export const remove = async (
throw new AppError("ERR_NO_PERMISSION", 403); throw new AppError("ERR_NO_PERMISSION", 403);
} }
await DeleteUserService(userId); await DeleteUserService(userId);
const io = getIO(); const io = getIO();
io.emit("user", { io.emit("user", {
action: "delete", action: "delete",
userId userId
}); });
//test del
//test del await stopWhoIsOnlineMonitor();
await stopWhoIsOnlineMonitor()
io.emit("onlineStatus", { io.emit("onlineStatus", {
action: "delete", action: "delete",
userOnlineTime: userId userOnlineTime: userId
}); });
await startWhoIsOnlineMonitor() await startWhoIsOnlineMonitor();
// //
return res.status(200).json({ message: "User deleted" }); return res.status(200).json({ message: "User deleted" });

View File

@ -0,0 +1,22 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"Settings",
[
{
key: "queueTransferByWhatsappScope",
value: "disabled",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("Settings", {});
}
};

View File

@ -3,16 +3,22 @@ import AppError from "../errors/AppError";
import Ticket from "../models/Ticket"; import Ticket from "../models/Ticket";
import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber"; import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber";
import { getSettingValue } from "./WhaticketSettings"; import { getSettingValue } from "./WhaticketSettings";
import ListWhatsAppsForQueueService from "../services/WhatsappService/ListWhatsAppsForQueueService";
const CheckContactOpenTickets = async ( const CheckContactOpenTickets = async (
contactId: number, contactId: number,
whatsappId: number | string whatsappId: number | string,
): Promise<void> => { handle?: boolean
): Promise<void | any> => {
let ticket; let ticket;
if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") { if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") {
let whats = await ListWhatsAppsNumber(whatsappId); let whats = await ListWhatsAppsNumber(whatsappId);
console.log("contactId: ", contactId, " | whatsappId: ", whatsappId);
console.log("WHATS: ", JSON.stringify(whats, null, 6));
ticket = await Ticket.findOne({ ticket = await Ticket.findOne({
where: { where: {
[Op.and]: [ [Op.and]: [
@ -22,6 +28,8 @@ const CheckContactOpenTickets = async (
] ]
} }
}); });
console.log("TICKET: ", JSON.stringify(ticket, null, 6));
} else { } else {
ticket = await Ticket.findOne({ ticket = await Ticket.findOne({
where: { contactId, status: { [Op.or]: ["open", "pending"] } } where: { contactId, status: { [Op.or]: ["open", "pending"] } }
@ -29,8 +37,12 @@ const CheckContactOpenTickets = async (
} }
if (ticket) { if (ticket) {
if (handle) return true;
throw new AppError("ERR_OTHER_OPEN_TICKET"); throw new AppError("ERR_OTHER_OPEN_TICKET");
} }
}; };
export default CheckContactOpenTickets; export default CheckContactOpenTickets;

View File

@ -67,7 +67,7 @@ const monitor = async () => {
const usersSocket = require('./../libs/socket'); const usersSocket = require('./../libs/socket');
if (usersSocket.ob) { if (usersSocket?.ob) {
if (count > 1) { if (count > 1) {
count = 0 count = 0
@ -81,7 +81,7 @@ const monitor = async () => {
if (uuid) { if (uuid) {
if (uuid == usersSocket.ob.uuid) { if (uuid == usersSocket.ob.uuid) {
console.log('ALL USERS CLIENTS OFFLINE 1...........') console.log('ALL USERS CLIENTS OFFLINE 1...........')
usersSocket.ob.listOnline = [] usersSocket.ob.listOnline = []
usersSocket.ob.listOnlineAux = [] usersSocket.ob.listOnlineAux = []
@ -103,7 +103,7 @@ const monitor = async () => {
const userOnline = await createOrUpdateOnlineUserService({ userId: el.id, status: 'online' }) const userOnline = await createOrUpdateOnlineUserService({ userId: el.id, status: 'online' })
if (userOnline) { if (userOnline) {
el.onlineTime = userOnline.onlineTime el.onlineTime = userOnline.onlineTime
el.updatedAt = userOnline.updatedAt, el.updatedAt = userOnline.updatedAt,

View File

@ -0,0 +1,17 @@
const usersSocket = require("../libs/socket");
export const removeUserFromOlineList = (userId: any) => {
let index = usersSocket.ob.listOnline.findIndex((o: any) => o.id == userId);
if (index != -1) {
usersSocket.ob.listOnline.splice(index, 1);
}
index = -1;
index = usersSocket.ob.listOnlineAux.findIndex((o: any) => o.id == userId);
if (index != -1) {
usersSocket.ob.listOnlineAux.splice(index, 1);
}
};

View File

@ -139,7 +139,7 @@ export const initIO = (httpServer: Server): SocketIO => {
lstOnline.push({ id: userId, status: "online", try: 0 }); lstOnline.push({ id: userId, status: "online", try: 0 });
lstOnlineAux.push({ id: userId }); lstOnlineAux.push({ id: userId });
console.log(" 1 PUSHED NEW USER ID 1: ", userId); console.log(" 1 - PUSHED NEW USER ID 1: ", userId);
obj.listOnline = lstOnline; obj.listOnline = lstOnline;
} else { } else {
@ -154,7 +154,7 @@ export const initIO = (httpServer: Server): SocketIO => {
if (index == -1) { if (index == -1) {
lstOnline.push({ id: userId, status: "online", try: 0 }); lstOnline.push({ id: userId, status: "online", try: 0 });
console.log(" 2 PUSHED NEW USER ID: ", userId); console.log(" 2 - PUSHED NEW USER ID: ", userId);
obj.listOnline = lstOnline; obj.listOnline = lstOnline;
} else { } else {

View File

@ -11,6 +11,6 @@ authRoutes.post("/login", SessionController.store);
authRoutes.post("/refresh_token", SessionController.update); authRoutes.post("/refresh_token", SessionController.update);
authRoutes.delete("/logout", isAuth, SessionController.remove); authRoutes.delete("/logout/:userId", isAuth, SessionController.remove);
export default authRoutes; export default authRoutes;

View File

@ -6,6 +6,8 @@ import * as UserController from "../controllers/UserController";
const userRoutes = Router(); const userRoutes = Router();
userRoutes.get("/users/all", isAuth, UserController.all);
userRoutes.get("/users", isAuth, UserController.index); userRoutes.get("/users", isAuth, UserController.index);
userRoutes.post("/users", isAuth, UserController.store); userRoutes.post("/users", isAuth, UserController.store);

View File

@ -16,6 +16,8 @@ import { createOrUpdateTicketCache } from "../../helpers/TicketCache";
import User from "../../models/User"; import User from "../../models/User";
import whatsappQueueMatchingUserQueue from "../../helpers/whatsappQueueMatchingUserQueue"; import whatsappQueueMatchingUserQueue from "../../helpers/whatsappQueueMatchingUserQueue";
import Whatsapp from "../../models/Whatsapp"; import Whatsapp from "../../models/Whatsapp";
import ListWhatsAppsForQueueService from "../WhatsappService/ListWhatsAppsForQueueService";
import { json } from "sequelize";
let flatten = require("flat"); let flatten = require("flat");
interface Request { interface Request {
@ -42,6 +44,27 @@ const CreateTicketService = async ({
defaultWhatsapp = await GetDefaultWhatsApp({ userId, queueId }); defaultWhatsapp = await GetDefaultWhatsApp({ userId, queueId });
} }
// if (queueId) {
// const whatsappsByQueue = await ListWhatsAppsForQueueService(queueId);
// const exist = await CheckContactOpenTickets(
// contactId,
// defaultWhatsapp.id,
// true
// );
// console.log(
// "whatsappsByQueue: ",
// JSON.stringify(whatsappsByQueue, null, 6)
// );
// if (exist) {
// console.log("kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk");
// }
// return new Ticket();
// }
// console.log(JSON.stringify(defaultWhatsapp, null, 2)); // console.log(JSON.stringify(defaultWhatsapp, null, 2));
// throw new AppError("test error"); // throw new AppError("test error");
@ -59,6 +82,13 @@ const CreateTicketService = async ({
queueId = matchingQueue ? matchingQueue.queueId : undefined; queueId = matchingQueue ? matchingQueue.queueId : undefined;
} }
console.log(
"xxxxxxxxxxxx contactId: ",
contactId,
" | defaultWhatsapp.id: ",
defaultWhatsapp.id
);
await CheckContactOpenTickets(contactId, defaultWhatsapp.id); await CheckContactOpenTickets(contactId, defaultWhatsapp.id);
const { isGroup } = await ShowContactService(contactId); const { isGroup } = await ShowContactService(contactId);

View File

@ -0,0 +1,67 @@
import { Sequelize } from "sequelize";
import Whatsapp from "../../models/Whatsapp";
import WhatsappQueue from "../../models/WhatsappQueue";
import { List } from "whatsapp-web.js";
const dbConfig = require("../../config/database");
const { QueryTypes } = require("sequelize");
const sequelize = new Sequelize(dbConfig);
const ListWhatsappQueueByUserQueue = async (
userId: number | string,
profiles: string
): Promise<any> => {
const users = await sequelize.query(
`select wq.whatsappId, wq.queueId, uq.userId, uq.queueId, u.id, u.name, u.profile, w.number
from WhatsappQueues wq join UserQueues uq join Users u join Whatsapps w on
wq.queueId = uq.queueId where u.id = uq.userId and u.profile in (${profiles}) and u.id = ${userId}
and w.id = wq.whatsappId group by w.number ;`,
{ type: QueryTypes.SELECT }
);
return users;
};
const ListWhatsappQueesByUser = async (whatsappIds: string): Promise<any> => {
const users = await sequelize.query(
`SELECT
wq.whatsappId, wq.queueId, q.name, q.id, q.name, q.color
FROM
WhatsappQueues wq
JOIN
Queues q ON wq.whatsappId IN (${whatsappIds}) AND q.id = wq.queueId group by wq.queueId;
`,
{ type: QueryTypes.SELECT }
);
return users;
};
const ListUserByWhatsappQueuesService = async (
userId: number | string,
profiles: string
): Promise<any> => {
const whatsapps: any = await ListWhatsappQueueByUserQueue(userId, profiles);
let whatsappIds = whatsapps.map((w: any) => w.whatsappId);
if (whatsappIds.length == 0) return { users: [], queues: [] };
console.log("----------> whatsappIds: ", whatsappIds, " | userId: ", userId);
whatsappIds = whatsappIds.join(", ");
const users: any = await sequelize.query(
`select wq.whatsappId, wq.queueId, uq.userId, uq.queueId, u.id, u.name, u.profile
from WhatsappQueues wq join UserQueues uq join Users u on
wq.queueId = uq.queueId where u.id = uq.userId and u.profile in (${profiles}) and wq.whatsappId in (${whatsappIds}) group by u.id;`,
{ type: QueryTypes.SELECT }
);
const queues = await ListWhatsappQueesByUser(whatsappIds);
return { users, queues };
};
export default ListUserByWhatsappQueuesService;

View File

@ -1,4 +1,3 @@
import { Op, Sequelize } from "sequelize"; import { Op, Sequelize } from "sequelize";
import Queue from "../../models/Queue"; import Queue from "../../models/Queue";
import User from "../../models/User"; import User from "../../models/User";
@ -7,65 +6,44 @@ import UserQueue from "../../models/UserQueue";
interface Request { interface Request {
userId?: string | number; userId?: string | number;
profile?: string; profile?: string;
raw?: boolean;
userIds?: string | number
} }
const ListUser = async ({ profile, userId, raw, userIds }: Request): Promise<User[]> => {
const ListUser = async ({ profile, userId }: Request): Promise<User[]> => { let where_clause = {};
let where_clause = {}
if (userId && profile) { if (userId && profile) {
where_clause = { where_clause = {
[Op.and]: [ [Op.and]: [{ userId: userId }, { profile: profile }]
{ userId: userId }, };
{ profile: profile } } else if (userId) {
]
}
}
else if (userId) {
where_clause = { where_clause = {
[Op.and]: [ [Op.and]: [{ userId: userId }]
{ userId: userId }, };
] } else if (profile) {
}
}
else if (profile) {
where_clause = { where_clause = {
profile: profile profile: profile
} };
} else if (userIds) {
where_clause = {
id: { [Op.in]: userIds }
};
} }
const users = await User.findAll({ const users = await User.findAll({
where: where_clause, where: where_clause,
raw: true, raw,
attributes: ['id', 'name', 'email'], attributes: ["id", "name", "email"],
// include: [ include: [
// { { model: Queue, as: "queues", attributes: ["id", "name", "color"] }
// model: UserQueue, ],
// separate: true,
// attributes: ['id',],
// order: [
// ['createdAt', 'ASC']
// ]
// },
// ],
order: [["id", "ASC"]],
})
order: [["id", "ASC"]]
});
return users; return users;
}; };
export default ListUser; export default ListUser;

View File

@ -0,0 +1,22 @@
import { Sequelize } from "sequelize";
import Whatsapp from "../../models/Whatsapp";
import WhatsappQueue from "../../models/WhatsappQueue";
const dbConfig = require("../../config/database");
const { QueryTypes } = require("sequelize");
const sequelize = new Sequelize(dbConfig);
const ListWhatsAppsForQueueService = async (queueId: number | string): Promise<any> => {
const distinctWhatsapps = await sequelize.query(
`SELECT w.id, w.number, w.status, wq.whatsappId, wq.queueId
FROM Whatsapps w
JOIN WhatsappQueues wq ON w.id = wq.whatsappId AND wq.queueId = ${queueId}
GROUP BY w.number;`,
{ type: QueryTypes.SELECT }
);
return distinctWhatsapps;
};
export default ListWhatsAppsForQueueService;

View File

@ -87,8 +87,15 @@ const NewTicketModal = ({ modalOpen, onClose }) => {
if (newValue?.number) { if (newValue?.number) {
setSelectedContact(newValue); setSelectedContact(newValue);
} else if (newValue?.name) { } else if (newValue?.name) {
setNewContact({ name: newValue.name });
setContactModalOpen(true); if (/^-?\d+(\.\d+)?$/.test(newValue?.name)) {
setNewContact({ number: newValue.name })
}
else {
setNewContact({ name: newValue.name })
}
setContactModalOpen(true)
} }
}; };

View File

@ -1,30 +1,34 @@
import React, { useState, useContext, useMemo } from "react"; import React, { useState, useContext, useMemo, useEffect } from "react"
import { useHistory } from "react-router-dom"; import { useHistory } from "react-router-dom"
import openSocket from "socket.io-client"
import Button from "@material-ui/core/Button"; import Button from "@material-ui/core/Button"
import Dialog from "@material-ui/core/Dialog"; import Dialog from "@material-ui/core/Dialog"
import Select from "@material-ui/core/Select"; import Select from "@material-ui/core/Select"
import FormControl from "@material-ui/core/FormControl"; import FormControl from "@material-ui/core/FormControl"
import InputLabel from "@material-ui/core/InputLabel"; import InputLabel from "@material-ui/core/InputLabel"
import MenuItem from "@material-ui/core/MenuItem"; import MenuItem from "@material-ui/core/MenuItem"
import { makeStyles } from "@material-ui/core"; import { makeStyles } from "@material-ui/core"
import DialogActions from "@material-ui/core/DialogActions"; import DialogActions from "@material-ui/core/DialogActions"
import DialogContent from "@material-ui/core/DialogContent"; import DialogContent from "@material-ui/core/DialogContent"
import DialogTitle from "@material-ui/core/DialogTitle"; import DialogTitle from "@material-ui/core/DialogTitle"
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n"
import api from "../../services/api"; import api from "../../services/api"
import ButtonWithSpinner from "../ButtonWithSpinner"; import ButtonWithSpinner from "../ButtonWithSpinner"
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError"
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext"; import { AuthContext } from "../../context/Auth/AuthContext"
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext"
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
maxWidth: { maxWidth: {
width: "100%", width: "100%",
}, },
})); }))
// Receive array of queues arrays // Receive array of queues arrays
// Return a new array with unique queues from all arrays has passed by the parameter // Return a new array with unique queues from all arrays has passed by the parameter
@ -45,49 +49,177 @@ const queueArraysToOneArray = (array) => {
const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => { const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
const history = useHistory(); const history = useHistory()
const { whatsApps } = useContext(WhatsAppsContext); const { whatsApps } = useContext(WhatsAppsContext)
const [loading, setLoading] = useState(false); const { user } = useContext(AuthContext)
const [selectedQueue, setSelectedQueue] = useState('');
const classes = useStyles(); const [loading, setLoading] = useState(false)
const queues = useMemo(() => { const [selectedQueue, setSelectedQueue] = useState('')
if (!whatsApps) return [] const [selectedUser, setSelectedUser] = useState('')
const classes = useStyles()
const [users, setUsers] = useState([])
const [queues, setQueues] = useState([])
const [_queues, setQueuesByWhats] = useState([])
// const isRun = useRef(false)
const [itemHover, setItemHover] = useState(-1)
const [settings, setSettings] = useState([])
const _queues2 = useMemo(() => {
if (!whatsApps) return []
const whatsAppsQueues = whatsApps.map(({ queues }) => queues) const whatsAppsQueues = whatsApps.map(({ queues }) => queues)
//const whatsAppsQueues = whatsApps.filter(({ status }) => status === "CONNECTED" ).map(({ queues }) => queues) //const whatsAppsQueues = whatsApps.filter(({ status }) => status === "CONNECTED" ).map(({ queues }) => queues)
const uniqueQueuesAvailable = queueArraysToOneArray(whatsAppsQueues) const uniqueQueuesAvailable = queueArraysToOneArray(whatsAppsQueues)
return uniqueQueuesAvailable return uniqueQueuesAvailable
}, [whatsApps]) }, [whatsApps])
const [itemHover, setItemHover] = useState(-1)
// useEffect(() => {
// if (isRun.current) return
// // setQueues(_queues2)
// isRun.current = true
// }, [whatsApps])
useEffect(() => {
if (selectedUser) {
let { queues } = users.find(u => +u.id === +selectedUser)
const userQueues = queues.map((q) => {
const { id, color, name } = q
return { id, color, name }
})
setQueues(userQueues)
setSelectedQueue('')
}
else {
if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled') {
setQueues(_queues)
}
else {
setQueues(_queues2)
}
setSelectedUser('')
}
}, [selectedUser, settings, _queues2, _queues, users])
useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => {
console.log('settings updated ----------------------------xxxxxxxxxxxx')
if (data.action === 'update') {
setSettings((prevState) => {
const aux = [...prevState]
const settingIndex = aux.findIndex((s) => s.key === data.setting.key)
aux[settingIndex].value = data.setting.value
return aux
})
}
})
return () => {
socket.disconnect()
}
}, [])
useEffect(() => {
const fetchSession = async () => {
try {
const { data } = await api.get('/settings')
setSettings(data.settings)
} catch (err) {
toastError(err)
}
}
fetchSession()
}, [])
const handleClose = () => { const handleClose = () => {
onClose(); onClose()
}; }
const handleSaveTicket = async e => { const handleSaveTicket = async e => {
e.preventDefault(); e.preventDefault()
if (!ticketid) return; if (!ticketid) return
if (!selectedQueue) return; if (!selectedQueue) return
setLoading(true); setLoading(true)
try { try {
let data = {}; let data = {}
if (selectedQueue && selectedQueue !== null) { if (selectedQueue && selectedQueue !== null) {
data.queueId = selectedQueue data.queueId = selectedQueue
} }
// test del PARA APARECER NA FILA DE OUTRO ATENDENTE E O MESMO CLICAR EM ACEITAR AO INVES DE ENVIAR PARA ATENDENDO
data.status = 'pending'
data.transfer = true
await api.put(`/tickets/${ticketid}`, data); if (selectedUser) {
data.userId = selectedUser
}
else {
// test del PARA APARECER NA FILA DE OUTRO ATENDENTE E O MESMO CLICAR EM ACEITAR AO INVES DE ENVIAR PARA ATENDENDO
data.status = 'pending'
data.transfer = true
}
setLoading(false); await api.put(`/tickets/${ticketid}`, data)
history.push(`/tickets`);
setLoading(false)
history.push(`/tickets`)
} catch (err) { } catch (err) {
setLoading(false); setLoading(false)
toastError(err); toastError(err)
} }
}; }
useEffect(() => {
const delayDebounceFn = setTimeout(() => {
const fetchUsers = async () => {
try {
if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled') {
const { data } = await api.get(`/users/all`, {
params: { userId: user.id },
})
setUsers(data.users)
setQueuesByWhats(data.queues)
setQueues(data.queues)
}
else {
const { data } = await api.get(`/users/all`, {
params: { profile: 'user' },
})
setUsers(data.users)
setQueues(_queues2)
}
} catch (err) {
console.log(err)
}
}
fetchUsers()
}, 500)
return () => clearTimeout(delayDebounceFn)
}, [settings, _queues2, user.id])
return ( return (
<Dialog open={modalOpen} onClose={handleClose} maxWidth="lg" scroll="paper"> <Dialog open={modalOpen} onClose={handleClose} maxWidth="lg" scroll="paper">
@ -98,6 +230,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
<DialogContent dividers> <DialogContent dividers>
<FormControl variant="outlined" className={classes.maxWidth}> <FormControl variant="outlined" className={classes.maxWidth}>
<InputLabel>{i18n.t("transferTicketModal.fieldQueueLabel")}</InputLabel> <InputLabel>{i18n.t("transferTicketModal.fieldQueueLabel")}</InputLabel>
<Select <Select
value={selectedQueue} value={selectedQueue}
onChange={(e) => setSelectedQueue(e.target.value)} onChange={(e) => setSelectedQueue(e.target.value)}
@ -118,6 +251,26 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
<br />
<Select
value={selectedUser}
onChange={e => setSelectedUser(e.target.value)}
label={'Transfeir para usuario'}
>
<MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem>
{users.map((user) => (
<MenuItem
key={user.id}
value={user.id}
>{user.name}
</MenuItem>
))}
</Select>
</FormControl> </FormControl>
</DialogContent> </DialogContent>
<DialogActions> <DialogActions>
@ -140,7 +293,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
</DialogActions> </DialogActions>
</form> </form>
</Dialog > </Dialog >
); )
}; }
export default TransferTicketModal; export default TransferTicketModal

View File

@ -121,7 +121,31 @@ const UserModal = ({ open, onClose, userId }) => {
const userData = { ...values, queueIds: selectedQueueIds }; const userData = { ...values, queueIds: selectedQueueIds };
try { try {
if (userId) { if (userId) {
await api.put(`/users/${userId}`, userData);
const user = await api.put(`/users/${userId}`, userData);
console.log('USER: ', user.data)
if (user && user.data.userQueuesAttendance.length > 0) {
const userQueueInAttendance = user.data.userQueuesAttendance.map((e) => ({ "queue": e.queueName, "open": e.totAttendance }))
console.log('userQueueInAttendance: ', userQueueInAttendance)
let msg = '\nAVISO \n\nO atendente possui atendimento(s) em aberto na(s) fila(s) abaixo: \n\n'
userQueueInAttendance.forEach((e) => {
msg += `Fila: ${e.queue}\nAberto: ${e.open}\n\n`
})
msg += 'Para remover o atendente da(s) fila(s) acima é necessário que o mesmo encerre os atendimentos aberto(s) nessas filas.\n'
alert(msg)
}
} else { } else {
await api.post("/users", userData); await api.post("/users", userData);
} }
@ -229,6 +253,7 @@ const UserModal = ({ open, onClose, userId }) => {
id="profile-selection" id="profile-selection"
required required
> >
<MenuItem value="supervisor">Supervisor</MenuItem>
<MenuItem value="admin">Admin</MenuItem> <MenuItem value="admin">Admin</MenuItem>
<MenuItem value="user">User</MenuItem> <MenuItem value="user">User</MenuItem>
</Field> </Field>

View File

@ -130,8 +130,9 @@ const useAuth = () => {
const handleLogout = async () => { const handleLogout = async () => {
setLoading(true) setLoading(true)
try { try {
await api.delete('/auth/logout') await api.delete(`/auth/logout/${user.id}`);
setIsAuth(false) setIsAuth(false)
setUser({}) setUser({})
localStorage.removeItem('token') localStorage.removeItem('token')

View File

@ -103,20 +103,31 @@ const MainListItems = (props) => {
primary={i18n.t('mainDrawer.listItems.quickAnswers')} primary={i18n.t('mainDrawer.listItems.quickAnswers')}
icon={<QuestionAnswerOutlinedIcon />} icon={<QuestionAnswerOutlinedIcon />}
/> />
<Can
role={user.profile}
perform="menu-users:view"
yes={() => (
<>
<Divider />
<ListSubheader inset>{i18n.t("mainDrawer.listItems.administration")}</ListSubheader>
<ListItemLink
to="/users"
primary={i18n.t("mainDrawer.listItems.users")}
icon={<PeopleAltOutlinedIcon />}
/>
</>
)}
/>
<Can <Can
role={user.profile} role={user.profile}
perform="drawer-admin-items:view" perform="drawer-admin-items:view"
yes={() => ( yes={() => (
<> <>
<Divider />
<ListSubheader inset>
{i18n.t('mainDrawer.listItems.administration')}
</ListSubheader>
<ListItemLink
to="/users"
primary={i18n.t('mainDrawer.listItems.users')}
icon={<PeopleAltOutlinedIcon />}
/>
<ListItemLink <ListItemLink
to="/queues" to="/queues"
primary={i18n.t('mainDrawer.listItems.queues')} primary={i18n.t('mainDrawer.listItems.queues')}

View File

@ -1,4 +1,4 @@
import React, { useState, useEffect, useReducer, useContext } from "react" import React, { useState, useEffect, useReducer, useCallback, useContext } from "react"
import openSocket from "socket.io-client" import openSocket from "socket.io-client"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import { useHistory } from "react-router-dom" import { useHistory } from "react-router-dom"
@ -116,6 +116,7 @@ const Contacts = () => {
const [deletingContact, setDeletingContact] = useState(null) const [deletingContact, setDeletingContact] = useState(null)
const [confirmOpen, setConfirmOpen] = useState(false) const [confirmOpen, setConfirmOpen] = useState(false)
const [hasMore, setHasMore] = useState(false) const [hasMore, setHasMore] = useState(false)
const [settings, setSettings] = useState([])
const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined) const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined)
@ -161,10 +162,25 @@ const Contacts = () => {
} }
useEffect(() => { useEffect(() => {
const fetchSession = async () => {
try {
const { data } = await api.get('/settings')
setSettings(data.settings)
} catch (err) {
toastError(err)
}
}
fetchSession()
}, [])
const getSettingValue = (key) => {
const { value } = settings.find((s) => s?.key === key)
return value
}
useEffect(() => {
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
@ -301,9 +317,44 @@ const Contacts = () => {
setContactModalOpen(false) setContactModalOpen(false)
} }
const handleOpenCreateTicketModal = (contactId) => {
const handleSaveTicketOneQueue = useCallback(async (contactId, userId, queueId) => {
if (!contactId || !userId) {
console.log("Missing contactId or userId")
return
};
if (!queueId) {
toast.warning("Nenhuma Fila Selecionada")
return
}
// if (isMounted.current) setLoading(true)
try {
const { data: ticket } = await api.post("/tickets", {
contactId: contactId,
userId: userId,
queueId: queueId,
status: "open",
})
history.push(`/tickets/${ticket.id}`)
} catch (err) {
toastError(err)
}
// if (isMounted.current) setLoading(false)
}, [history])
const handleOpenCreateTicketModal = (contactId) => {
setSelectedContactId(contactId) setSelectedContactId(contactId)
setIsCreateTicketModalOpen(true)
if (getSettingValue('whatsaAppCloudApi') === 'disabled' && user?.queues?.length === 1){
handleSaveTicketOneQueue(contactId, user.id, user.queues[0].id)
}
else{
setIsCreateTicketModalOpen(true)
}
// setSelectedContactId(contactId)
// setIsCreateTicketModalOpen(true)
} }
const handleCloseCreateTicketModal = () => { const handleCloseCreateTicketModal = () => {

View File

@ -1,25 +1,25 @@
import React, { useContext, useReducer, useEffect, useState } from "react"; import React, { useContext, useReducer, useEffect, useState } from "react"
import { addHours, addMinutes, addSeconds, intervalToDuration } from "date-fns"; import { addHours, addMinutes, addSeconds, intervalToDuration } from "date-fns"
import Paper from "@material-ui/core/Paper"; import Paper from "@material-ui/core/Paper"
import Container from "@material-ui/core/Container"; import Container from "@material-ui/core/Container"
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid"
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles"
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography"
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip"
import Zoom from "@mui/material/Zoom"; import Zoom from "@mui/material/Zoom"
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton"
import Info from "@material-ui/icons/Info"; import Info from "@material-ui/icons/Info"
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext"
// import { i18n } from "../../translate/i18n"; // import { i18n } from "../../translate/i18n";
import Chart from "./Chart"; import Chart from "./Chart"
import openSocket from "socket.io-client"; import openSocket from "socket.io-client"
import api from "../../services/api"; import api from "../../services/api"
import { Can } from "../../components/Can"; import { Can } from "../../components/Can"
import TableUser from "../../components/DashboardUser/TableUser"; import TableUser from "../../components/DashboardUser/TableUser"
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
container: { container: {
@ -104,15 +104,15 @@ const useStyles = makeStyles((theme) => ({
textAlign: "center", textAlign: "center",
borderRadius: "5px", borderRadius: "5px",
}, },
})); }))
var _fifo var _fifo
const sumOnlineTimeNow = (oldOnlineTimeSum) => { const sumOnlineTimeNow = (oldOnlineTimeSum) => {
let onlineTime = new Date() let onlineTime = new Date()
if (!oldOnlineTimeSum.onlineTime) { if (!oldOnlineTimeSum.onlineTime) {
oldOnlineTimeSum.onlineTime = `${oldOnlineTimeSum.updatedAt.split(' ')[0]} 00:00:00` oldOnlineTimeSum.onlineTime = `${oldOnlineTimeSum.updatedAt.split(' ')[0]} 00:00:00`
@ -136,70 +136,70 @@ const sumOnlineTimeNow = (oldOnlineTimeSum) => {
onlineTime = addSeconds(onlineTime, newtTime.seconds) onlineTime = addSeconds(onlineTime, newtTime.seconds)
} }
const isoDate = new Date(onlineTime); const isoDate = new Date(onlineTime)
const newOnlinetime = isoDate.toJSON().slice(0, 19).replace('T', ' '); const newOnlinetime = isoDate.toJSON().slice(0, 19).replace('T', ' ')
return newOnlinetime return newOnlinetime
} }
const reducer = (state, action) => { const reducer = (state, action) => {
if (action.type === "DELETE_USER_STATUS") { if (action.type === "DELETE_USER_STATUS") {
const userId = action.payload; const userId = action.payload
const userIndex = state.findIndex((u) => `${u.id}` === `${userId}`); const userIndex = state.findIndex((u) => `${u.id}` === `${userId}`)
if (userIndex !== -1) { if (userIndex !== -1) {
state.splice(userIndex, 1); state.splice(userIndex, 1)
} }
return [...state]; return [...state]
} }
if (action.type === "LOAD_QUERY") { if (action.type === "LOAD_QUERY") {
const queries = action.payload; const queries = action.payload
const newQueries = []; const newQueries = []
queries.forEach((query) => { queries.forEach((query) => {
const queryIndex = state.findIndex((q) => q.id === query.id); const queryIndex = state.findIndex((q) => q.id === query.id)
if (queryIndex !== -1) { if (queryIndex !== -1) {
state[queryIndex] = query; state[queryIndex] = query
} else { } else {
newQueries.push(query); newQueries.push(query)
} }
}); })
return [...state, ...newQueries]; return [...state, ...newQueries]
} }
if (action.type === "UPDATE_STATUS_ONLINE") { if (action.type === "UPDATE_STATUS_ONLINE") {
let onlineUser = action.payload; let onlineUser = action.payload
let index = -1; let index = -1
// console.log('UPDATE_STATUS_ONLINE: ', onlineUser) // console.log('UPDATE_STATUS_ONLINE: ', onlineUser)
let onlySumOpenClosed = false; let onlySumOpenClosed = false
if (onlineUser.sumOpen || onlineUser.sumClosed) { if (onlineUser.sumOpen || onlineUser.sumClosed) {
index = state.findIndex( index = state.findIndex(
(e) => (e) =>
(onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) || (onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) ||
(onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId) (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId)
); )
onlySumOpenClosed = true; onlySumOpenClosed = true
} else { } else {
index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`); index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`)
} }
if (index !== -1) { if (index !== -1) {
if (!onlySumOpenClosed) { if (!onlySumOpenClosed) {
if (!("statusOnline" in state[index])) { if (!("statusOnline" in state[index])) {
state[index].statusOnline = onlineUser; state[index].statusOnline = onlineUser
} else if ("statusOnline" in state[index]) { } else if ("statusOnline" in state[index]) {
state[index].statusOnline["status"] = onlineUser.status; state[index].statusOnline["status"] = onlineUser.status
} }
} }
@ -207,73 +207,72 @@ const reducer = (state, action) => {
if ("onlineTime" in onlineUser) { if ("onlineTime" in onlineUser) {
if ("sumOnlineTime" in state[index]) { if ("sumOnlineTime" in state[index]) {
// console.log(' ffffffffffffffffffffffffffff ') state[index].sumOnlineTime.sum = onlineUser.onlineTime.split(" ")[1]
state[index].sumOnlineTime.sum = onlineUser.onlineTime.split(" ")[1]
} else if (!("sumOnlineTime" in state[index])) { } else if (!("sumOnlineTime" in state[index])) {
state[index].sumOnlineTime = { state[index].sumOnlineTime = {
userId: onlineUser.userId, userId: onlineUser.userId,
sum: onlineUser.onlineTime.split(" ")[1], sum: onlineUser.onlineTime.split(" ")[1],
}; }
} }
} }
if (onlineUser.sumOpen) { if (onlineUser.sumOpen) {
if ("sumOpen" in state[index]) { if ("sumOpen" in state[index]) {
state[index].sumOpen["count"] = onlineUser.sumOpen.count; state[index].sumOpen["count"] = onlineUser.sumOpen.count
} else if (!("sumOpen" in state[index])) { } else if (!("sumOpen" in state[index])) {
state[index].sumOpen = onlineUser.sumOpen; state[index].sumOpen = onlineUser.sumOpen
} }
} }
if (onlineUser.sumClosed) { if (onlineUser.sumClosed) {
if ("sumClosed" in state[index]) { if ("sumClosed" in state[index]) {
state[index].sumClosed["count"] = onlineUser.sumClosed.count; state[index].sumClosed["count"] = onlineUser.sumClosed.count
} else if (!("sumClosed" in state[index])) { } else if (!("sumClosed" in state[index])) {
state[index].sumClosed = onlineUser.sumClosed; state[index].sumClosed = onlineUser.sumClosed
} }
} }
if (onlineUser.openClosedInQueue) { if (onlineUser.openClosedInQueue) {
state[index].openClosedInQueue = onlineUser.openClosedInQueue; state[index].openClosedInQueue = onlineUser.openClosedInQueue
} }
if (onlineUser.openClosedOutQueue) { if (onlineUser.openClosedOutQueue) {
state[index].openClosedOutQueue = onlineUser.openClosedOutQueue; state[index].openClosedOutQueue = onlineUser.openClosedOutQueue
} }
} }
return [...state]; return [...state]
} }
if (action.type === "RESET") { if (action.type === "RESET") {
return []; return []
} }
}; }
const Dashboard = () => { const Dashboard = () => {
const classes = useStyles(); const classes = useStyles()
const [usersOnlineInfo, dispatch] = useReducer(reducer, []); const [usersOnlineInfo, dispatch] = useReducer(reducer, [])
const [ticketStatusChange, setStatus] = useState(); const [ticketStatusChange, setStatus] = useState()
const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 }); const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 })
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext)
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" })
}, []); }, [])
const handleLogouOnlineUser = async (userId) => { const handleLogouOnlineUser = async (userId) => {
try { try {
await api.get(`/users/logout/${userId}`); await api.get(`/users/logout/${userId}`)
//toast.success(("Desloged!")); //toast.success(("Desloged!"));
//handleDeleteRows(scheduleId) //handleDeleteRows(scheduleId)
} catch (err) { } catch (err) {
// toastError(err); // toastError(err);
} }
}; }
useEffect(() => { useEffect(() => {
//setLoading(true); //setLoading(true);
@ -282,26 +281,26 @@ const Dashboard = () => {
// setLoading(true); // setLoading(true);
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
let date = new Date().toLocaleDateString("pt-BR").split("/"); let date = new Date().toLocaleDateString("pt-BR").split("/")
let dateToday = `${date[2]}-${date[1]}-${date[0]}`; let dateToday = `${date[2]}-${date[1]}-${date[0]}`
const { data } = await api.get("/reports/user/services", { const { data } = await api.get("/reports/user/services", {
params: { userId: null, startDate: dateToday, endDate: dateToday }, params: { userId: null, startDate: dateToday, endDate: dateToday },
}); })
// console.log('data.data: ', data.usersProfile) // console.log('data.data: ', data.usersProfile)
dispatch({ type: "RESET" }); dispatch({ type: "RESET" })
dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }); dispatch({ type: "LOAD_QUERY", payload: data.usersProfile })
} catch (err) { } catch (err) {
} }
}; }
fetchQueries(); fetchQueries()
}, 500); }, 500)
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn)
}, []); }, [])
useEffect(() => { useEffect(() => {
@ -310,18 +309,18 @@ const Dashboard = () => {
if (!usersOnlineInfo || usersOnlineInfo.length === 0) return if (!usersOnlineInfo || usersOnlineInfo.length === 0) return
if (_fifo) { if (_fifo) {
clearInterval(_fifo); clearInterval(_fifo)
} }
_fifo = setInterval(() => { _fifo = setInterval(() => {
for (let i = 0; i < usersOnlineInfo.length; i++) { for (let i = 0; i < usersOnlineInfo.length; i++) {
if (usersOnlineInfo[i].statusOnline && usersOnlineInfo[i].statusOnline.status === 'online') { if (usersOnlineInfo[i].statusOnline && usersOnlineInfo[i].statusOnline.status === 'online') {
let onlineTimeCurrent = sumOnlineTimeNow({ onlineTime: usersOnlineInfo[i].statusOnline.onlineTime, updatedAt: usersOnlineInfo[i].statusOnline.updatedAt })
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: { userId: usersOnlineInfo[i].id, status: usersOnlineInfo[i].statusOnline.status, onlineTime: onlineTimeCurrent } }); let onlineTimeCurrent = sumOnlineTimeNow({ onlineTime: usersOnlineInfo[i].statusOnline.onlineTime, updatedAt: usersOnlineInfo[i].statusOnline.updatedAt })
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: { userId: usersOnlineInfo[i].id, status: usersOnlineInfo[i].statusOnline.status, onlineTime: onlineTimeCurrent } })
} }
} }
@ -340,80 +339,81 @@ const Dashboard = () => {
// }) // })
}, 3000); }, 3000)
}, [usersOnlineInfo]) }, [usersOnlineInfo])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("ticketStatus", (data) => { socket.on("ticketStatus", (data) => {
if (data.action === "update") { if (data.action === "update") {
setStatus(""); setStatus("")
setStatus(data.ticketStatus.status); setStatus(data.ticketStatus.status)
} }
}); })
socket.on("onlineStatus", (data) => { socket.on("onlineStatus", (data) => {
if (data.action === "logout" || data.action === "update") { if (data.action === "logout" || data.action === "update") {
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime });
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime })
} else if (data.action === "delete") { } else if (data.action === "delete") {
dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime })
} }
}); })
socket.on("user", (data) => { socket.on("user", (data) => {
if (data.action === "delete") { if (data.action === "delete") {
dispatch({ type: "DELETE_USER", payload: +data.userId }); dispatch({ type: "DELETE_USER", payload: +data.userId })
} }
}); })
return () => { return () => {
socket.disconnect(); socket.disconnect()
}; }
}, []); }, [])
useEffect(() => { useEffect(() => {
if (ticketStatusChange === "") return if (ticketStatusChange === "") return
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
let date = new Date().toLocaleDateString("pt-BR").split("/"); let date = new Date().toLocaleDateString("pt-BR").split("/")
let dateToday = `${date[2]}-${date[1]}-${date[0]}`; let dateToday = `${date[2]}-${date[1]}-${date[0]}`
const _open = await api.get("/tickets/count", { const _open = await api.get("/tickets/count", {
params: { status: "open", date: dateToday }, params: { status: "open", date: dateToday },
}); })
const _closed = await api.get("/tickets/count", { const _closed = await api.get("/tickets/count", {
params: { status: "closed", date: dateToday }, params: { status: "closed", date: dateToday },
}); })
const _pending = await api.get("/tickets/count", { const _pending = await api.get("/tickets/count", {
params: { status: "pending" }, params: { status: "pending" },
}); })
const _openAll = await api.get("/tickets/count", { const _openAll = await api.get("/tickets/count", {
params: { status: "open" }, params: { status: "open" },
}); })
setTicktsStatus({ setTicktsStatus({
open: _open.data.count, open: _open.data.count,
openAll: _openAll.data.count, openAll: _openAll.data.count,
closed: _closed.data.count, closed: _closed.data.count,
pending: _pending.data.count, pending: _pending.data.count,
}); })
// setOpen(_open.data.count); // setOpen(_open.data.count);
// setClosed(_closed.data.count); // setClosed(_closed.data.count);
// setPending(_pending.data.count); // setPending(_pending.data.count);
} catch (err) { } catch (err) {
console.log(err); console.log(err)
} }
}; }
fetchQueries(); fetchQueries()
}, 500); }, 500)
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn)
}, [ticketStatusChange]); }, [ticketStatusChange])
return ( return (
<Can <Can
@ -602,7 +602,7 @@ const Dashboard = () => {
</Container> </Container>
)} )}
/> />
); )
}; }
export default Dashboard; export default Dashboard

View File

@ -257,6 +257,35 @@ const Settings = () => {
</div> </div>
<div className={classes.root}>
<Container className={classes.container} maxWidth="sm">
<Paper className={classes.paper}>
<Typography variant="body1">
Escopo de transferência de usuario por filas atribuidas a conexão e usuarios atribuido a filas
</Typography>
<Select
margin="dense"
variant="outlined"
native
id="queueTransferByWhatsappScope-setting"
name="queueTransferByWhatsappScope"
value={
settings &&
settings.length > 0 &&
getSettingValue('queueTransferByWhatsappScope')
}
className={classes.settingOption}
onChange={handleChangeSetting}
>
<option value="enabled">Ativado</option>
<option value="disabled">Desativado</option>
</Select>
</Paper>
</Container>
</div>
</div> </div>
)} )}
/> />

View File

@ -3,11 +3,20 @@ const rules = {
static: [], static: [],
}, },
supervisor: {
static: [
"menu-users:view",
"user-view:show",
"user-modal:editQueues"
]
},
admin: { admin: {
static: [ static: [
'show-icon-edit-whatsapp', 'show-icon-edit-whatsapp',
'show-icon-edit-queue', 'show-icon-edit-queue',
'menu-users:view',
'drawer-admin-items:view', 'drawer-admin-items:view',
'tickets-manager:showall', 'tickets-manager:showall',
'user-modal:editProfile', 'user-modal:editProfile',
@ -25,6 +34,7 @@ const rules = {
master: { master: {
static: [ static: [
'menu-users:view',
'url-remote-session:show', 'url-remote-session:show',
'show-icon-edit-whatsapp', 'show-icon-edit-whatsapp',
'show-icon-add-queue', 'show-icon-add-queue',