From bc134c827cccb22ad5e3546549b0e26e42fcb992 Mon Sep 17 00:00:00 2001 From: adriano Date: Mon, 29 Jan 2024 08:48:20 -0300 Subject: [PATCH] feat: Enable users to transfer to other queues and users with the same WhatsApp connection. --- TEST_SERVER1/api/app.js | 2 +- backend/src/controllers/ReportController.ts | 6 +- backend/src/controllers/SessionController.ts | 20 ++ backend/src/controllers/TicketController.ts | 106 ++++++-- backend/src/controllers/UserController.ts | 183 ++++++++++--- ...ble-queue-transfer-by-whatsapp-settings.ts | 22 ++ .../src/helpers/CheckContactOpenTickets.ts | 16 +- backend/src/helpers/WhoIsOnlineMonitor.ts | 6 +- .../src/helpers/removeUserFromOnlineList.ts | 17 ++ backend/src/libs/socket.ts | 4 +- backend/src/routes/authRoutes.ts | 2 +- backend/src/routes/userRoutes.ts | 2 + .../TicketServices/CreateTicketService.ts | 30 +++ .../ListUserByWhatsappQueuesService.ts | 67 +++++ .../UserServices/ListUserParamiterService.ts | 66 ++--- .../ListWhatsAppsForQueueService.ts | 22 ++ .../src/components/NewTicketModal/index.js | 11 +- .../components/TransferTicketModal/index.js | 243 ++++++++++++++---- frontend/src/components/UserModal/index.js | 27 +- frontend/src/hooks/useAuth.js/index.js | 5 +- frontend/src/layout/MainListItems.js | 29 ++- frontend/src/pages/Contacts/index.js | 59 ++++- frontend/src/pages/Dashboard/index.js | 216 ++++++++-------- frontend/src/pages/Settings/index.js | 29 +++ frontend/src/rules.js | 12 +- 25 files changed, 922 insertions(+), 280 deletions(-) create mode 100644 backend/src/database/seeds/20240126192419-add-enable-queue-transfer-by-whatsapp-settings.ts create mode 100644 backend/src/helpers/removeUserFromOnlineList.ts create mode 100644 backend/src/services/UserServices/ListUserByWhatsappQueuesService.ts create mode 100644 backend/src/services/WhatsappService/ListWhatsAppsForQueueService.ts diff --git a/TEST_SERVER1/api/app.js b/TEST_SERVER1/api/app.js index dd4f9ef..f9a598c 100644 --- a/TEST_SERVER1/api/app.js +++ b/TEST_SERVER1/api/app.js @@ -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}` } else { whatsName = `${whatsapp[0]['name']} -> S${numberSession}` diff --git a/backend/src/controllers/ReportController.ts b/backend/src/controllers/ReportController.ts index a731ecf..10580be 100644 --- a/backend/src/controllers/ReportController.ts +++ b/backend/src/controllers/ReportController.ts @@ -64,7 +64,11 @@ export const reportUserService = async (req: Request, res: Response): Promise => { const { email, password } = req.body; @@ -15,6 +21,11 @@ export const store = async (req: Request, res: Response): Promise => { SendRefreshToken(res, refreshToken); + const userOnline = await createOrUpdateOnlineUserService({ + userId: serializedUser.id, + status: "online" + }); + return res.status(200).json({ token, user: serializedUser @@ -47,5 +58,14 @@ export const remove = async ( ): Promise => { res.clearCookie("jrt"); + const { userId } = req.params; + + removeUserFromOlineList(userId); + + const userOnline = await createOrUpdateOnlineUserService({ + userId, + status: "offline" + }); + return res.send(); }; diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 735dd69..e5c792b 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -64,6 +64,10 @@ import Contact from "../models/Contact"; import BotIsOnQueue from "../helpers/BotIsOnQueue"; import { setMessageAsRead } from "../helpers/SetMessageAsRead"; 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 => { const { @@ -129,17 +133,17 @@ export const store = async (req: Request, res: Response): Promise => { ticketData: { status: "open", userId: userId, queueId }, ticketId: ticket.id }); - } + } } - if(!ticket){ - ticket = await CreateTicketService({ - contactId, - status, - userId, - queueId, - whatsappId - }); + if (!ticket) { + ticket = await CreateTicketService({ + contactId, + status, + userId, + queueId, + whatsappId + }); } const io = getIO(); @@ -249,24 +253,88 @@ export const update = async ( req.body.userId = null; } + console.log("REQ.BODY: ", JSON.stringify(req.body, null, 6)); + let ticketData: TicketData = req.body; if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") { if (ticketData.transfer) { - const defaultWhatsapp: any = await GetDefaultWhatsApp({ - userId: ticketData.userId - }); + const whatsappsByqueue = await ListWhatsAppsForQueueService( + ticketData.queueId + ); - const _ticket: any = await Ticket.findByPk(ticketId); + if (userOldInfo) { + let listTicketOpenPending: any = []; - if (defaultWhatsapp && ticketData.status != "open") { - await CheckContactOpenTickets( - _ticket.dataValues.contactId, - defaultWhatsapp.dataValues.id - ); + for (const w of whatsappsByqueue) { + let whats = await ListWhatsAppsNumber(w.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; } } diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index c41b912..c1c558c 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -10,16 +10,28 @@ import UpdateUserService from "../services/UserServices/UpdateUserService"; import ShowUserService from "../services/UserServices/ShowUserService"; import DeleteUserService from "../services/UserServices/DeleteUserService"; -import ListUserParamiterService from "../services/UserServices/ListUserParamiterService" +import ListUser from "../services/UserServices/ListUserParamiterService"; import User from "../models/User"; -import { startWhoIsOnlineMonitor, stopWhoIsOnlineMonitor } from "../helpers/WhoIsOnlineMonitor" -import UserOnlineTIme from '../models/UserOnlineTime' +import { + 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 = { searchParam: string; pageNumber: string; profile?: string; + userId: string; }; export const index = async (req: Request, res: Response): Promise => { @@ -31,13 +43,21 @@ export const index = async (req: Request, res: Response): Promise => { profile }); - if (req.user.profile !== 'master') { - + if (req.user.profile !== "master") { let auxUsers: Array = []; + // for (var user of users) { + // if (user.profile !== 'master') { + // auxUsers.push(user) + // } + // } + for (var user of users) { - if (user.profile !== 'master') { - auxUsers.push(user) + if (user.profile !== "master") { + 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 => { return res.json({ users, count, hasMore }); - - // const { users, count, hasMore } = await ListUsersService({ // searchParam, // pageNumber // }); - // if(req.user.profile!=='master'){ + // if(req.user.profile!=='master'){ - // let auxUsers: Array = []; + // let auxUsers: Array = []; - // for (var user of users) { + // for (var user of users) { // if(user.profile!=='master'){ // auxUsers.push(user) - // } - // } + // } + // } - // return res.json({ users: auxUsers, count, hasMore }); - // } + // return res.json({ users: auxUsers, count, hasMore }); + // } // return res.json({ users, count, hasMore }); }; +// export const usersByWhatsappQueue = async (req: Request, res: Response): Promise => { +// const { profile } = req.query as IndexQuery; + +// const users = await ListUser({ +// profile +// }); + +// return res.json({ users }); +// }; + +export const all = async (req: Request, res: Response): Promise => { + 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 => { 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); } else if (req.url !== "/signup" && req.user.profile !== "master") { throw new AppError("ERR_NO_PERMISSION", 403); @@ -93,9 +159,8 @@ export const store = async (req: Request, res: Response): Promise => { user }); - // await stopWhoIsOnlineMonitor() - await startWhoIsOnlineMonitor() + await startWhoIsOnlineMonitor(); return res.status(200).json(user); }; @@ -108,17 +173,18 @@ export const show = async (req: Request, res: Response): Promise => { return res.status(200).json(user); }; - -export const logoutUser = async (req: Request, res: Response): Promise => { +export const logoutUser = async ( + req: Request, + res: Response +): Promise => { const { userId } = req.params; - - await stopWhoIsOnlineMonitor() + await stopWhoIsOnlineMonitor(); let onlineTime = { userId: userId, - status: 'logout...' - } + status: "logout..." + }; const io = getIO(); io.emit("onlineStatus", { @@ -126,25 +192,73 @@ export const logoutUser = async (req: Request, res: Response): Promise userOnlineTime: onlineTime }); - await startWhoIsOnlineMonitor() + await startWhoIsOnlineMonitor(); // return res.status(200).json({}); }; - export const update = async ( req: Request, res: Response ): Promise => { - 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); } const { userId } = req.params; 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(); io.emit("user", { @@ -152,6 +266,8 @@ export const update = async ( user }); + user.userQueuesAttendance = userQueuesAttendance; + return res.status(200).json(user); }; @@ -165,26 +281,23 @@ export const remove = async ( throw new AppError("ERR_NO_PERMISSION", 403); } - await DeleteUserService(userId); - const io = getIO(); io.emit("user", { action: "delete", userId }); - - //test del - await stopWhoIsOnlineMonitor() + //test del + await stopWhoIsOnlineMonitor(); io.emit("onlineStatus", { action: "delete", userOnlineTime: userId }); - await startWhoIsOnlineMonitor() + await startWhoIsOnlineMonitor(); // return res.status(200).json({ message: "User deleted" }); diff --git a/backend/src/database/seeds/20240126192419-add-enable-queue-transfer-by-whatsapp-settings.ts b/backend/src/database/seeds/20240126192419-add-enable-queue-transfer-by-whatsapp-settings.ts new file mode 100644 index 0000000..9ffa596 --- /dev/null +++ b/backend/src/database/seeds/20240126192419-add-enable-queue-transfer-by-whatsapp-settings.ts @@ -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", {}); + } +}; diff --git a/backend/src/helpers/CheckContactOpenTickets.ts b/backend/src/helpers/CheckContactOpenTickets.ts index 6c75ce4..6cfc24d 100644 --- a/backend/src/helpers/CheckContactOpenTickets.ts +++ b/backend/src/helpers/CheckContactOpenTickets.ts @@ -3,16 +3,22 @@ import AppError from "../errors/AppError"; import Ticket from "../models/Ticket"; import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber"; import { getSettingValue } from "./WhaticketSettings"; +import ListWhatsAppsForQueueService from "../services/WhatsappService/ListWhatsAppsForQueueService"; const CheckContactOpenTickets = async ( contactId: number, - whatsappId: number | string -): Promise => { + whatsappId: number | string, + handle?: boolean +): Promise => { let ticket; if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") { let whats = await ListWhatsAppsNumber(whatsappId); + console.log("contactId: ", contactId, " | whatsappId: ", whatsappId); + + console.log("WHATS: ", JSON.stringify(whats, null, 6)); + ticket = await Ticket.findOne({ where: { [Op.and]: [ @@ -22,6 +28,8 @@ const CheckContactOpenTickets = async ( ] } }); + + console.log("TICKET: ", JSON.stringify(ticket, null, 6)); } else { ticket = await Ticket.findOne({ where: { contactId, status: { [Op.or]: ["open", "pending"] } } @@ -29,8 +37,12 @@ const CheckContactOpenTickets = async ( } if (ticket) { + if (handle) return true; + throw new AppError("ERR_OTHER_OPEN_TICKET"); } + + }; export default CheckContactOpenTickets; diff --git a/backend/src/helpers/WhoIsOnlineMonitor.ts b/backend/src/helpers/WhoIsOnlineMonitor.ts index 3296df5..940bbb3 100644 --- a/backend/src/helpers/WhoIsOnlineMonitor.ts +++ b/backend/src/helpers/WhoIsOnlineMonitor.ts @@ -67,7 +67,7 @@ const monitor = async () => { const usersSocket = require('./../libs/socket'); - if (usersSocket.ob) { + if (usersSocket?.ob) { if (count > 1) { count = 0 @@ -81,7 +81,7 @@ const monitor = async () => { if (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.listOnlineAux = [] @@ -103,7 +103,7 @@ const monitor = async () => { const userOnline = await createOrUpdateOnlineUserService({ userId: el.id, status: 'online' }) - if (userOnline) { + if (userOnline) { el.onlineTime = userOnline.onlineTime el.updatedAt = userOnline.updatedAt, diff --git a/backend/src/helpers/removeUserFromOnlineList.ts b/backend/src/helpers/removeUserFromOnlineList.ts new file mode 100644 index 0000000..2096f82 --- /dev/null +++ b/backend/src/helpers/removeUserFromOnlineList.ts @@ -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); + } +}; diff --git a/backend/src/libs/socket.ts b/backend/src/libs/socket.ts index ccc554b..65382d6 100644 --- a/backend/src/libs/socket.ts +++ b/backend/src/libs/socket.ts @@ -139,7 +139,7 @@ export const initIO = (httpServer: Server): SocketIO => { lstOnline.push({ id: userId, status: "online", try: 0 }); 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; } else { @@ -154,7 +154,7 @@ export const initIO = (httpServer: Server): SocketIO => { if (index == -1) { 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; } else { diff --git a/backend/src/routes/authRoutes.ts b/backend/src/routes/authRoutes.ts index 8428fe9..fbde45a 100644 --- a/backend/src/routes/authRoutes.ts +++ b/backend/src/routes/authRoutes.ts @@ -11,6 +11,6 @@ authRoutes.post("/login", SessionController.store); authRoutes.post("/refresh_token", SessionController.update); -authRoutes.delete("/logout", isAuth, SessionController.remove); +authRoutes.delete("/logout/:userId", isAuth, SessionController.remove); export default authRoutes; diff --git a/backend/src/routes/userRoutes.ts b/backend/src/routes/userRoutes.ts index 8dede31..726690d 100644 --- a/backend/src/routes/userRoutes.ts +++ b/backend/src/routes/userRoutes.ts @@ -6,6 +6,8 @@ import * as UserController from "../controllers/UserController"; const userRoutes = Router(); +userRoutes.get("/users/all", isAuth, UserController.all); + userRoutes.get("/users", isAuth, UserController.index); userRoutes.post("/users", isAuth, UserController.store); diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts index 36dc292..1b2f072 100644 --- a/backend/src/services/TicketServices/CreateTicketService.ts +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -16,6 +16,8 @@ import { createOrUpdateTicketCache } from "../../helpers/TicketCache"; import User from "../../models/User"; import whatsappQueueMatchingUserQueue from "../../helpers/whatsappQueueMatchingUserQueue"; import Whatsapp from "../../models/Whatsapp"; +import ListWhatsAppsForQueueService from "../WhatsappService/ListWhatsAppsForQueueService"; +import { json } from "sequelize"; let flatten = require("flat"); interface Request { @@ -42,6 +44,27 @@ const CreateTicketService = async ({ 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)); // throw new AppError("test error"); @@ -59,6 +82,13 @@ const CreateTicketService = async ({ queueId = matchingQueue ? matchingQueue.queueId : undefined; } + console.log( + "xxxxxxxxxxxx contactId: ", + contactId, + " | defaultWhatsapp.id: ", + defaultWhatsapp.id + ); + await CheckContactOpenTickets(contactId, defaultWhatsapp.id); const { isGroup } = await ShowContactService(contactId); diff --git a/backend/src/services/UserServices/ListUserByWhatsappQueuesService.ts b/backend/src/services/UserServices/ListUserByWhatsappQueuesService.ts new file mode 100644 index 0000000..6da43ab --- /dev/null +++ b/backend/src/services/UserServices/ListUserByWhatsappQueuesService.ts @@ -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 => { + 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 => { + 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 => { + 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; diff --git a/backend/src/services/UserServices/ListUserParamiterService.ts b/backend/src/services/UserServices/ListUserParamiterService.ts index e8ff0bd..fc7bd32 100644 --- a/backend/src/services/UserServices/ListUserParamiterService.ts +++ b/backend/src/services/UserServices/ListUserParamiterService.ts @@ -1,4 +1,3 @@ - import { Op, Sequelize } from "sequelize"; import Queue from "../../models/Queue"; import User from "../../models/User"; @@ -7,65 +6,44 @@ import UserQueue from "../../models/UserQueue"; interface Request { userId?: string | number; profile?: string; + raw?: boolean; + userIds?: string | number } - -const ListUser = async ({ profile, userId }: Request): Promise => { - - let where_clause = {} +const ListUser = async ({ profile, userId, raw, userIds }: Request): Promise => { + let where_clause = {}; if (userId && profile) { where_clause = { - [Op.and]: [ - { userId: userId }, - { profile: profile } - ] - } - } - else if (userId) { + [Op.and]: [{ userId: userId }, { profile: profile }] + }; + } else if (userId) { where_clause = { - [Op.and]: [ - { userId: userId }, - ] - } - } - else if (profile) { + [Op.and]: [{ userId: userId }] + }; + } else if (profile) { where_clause = { profile: profile - } + }; + } else if (userIds) { + where_clause = { + id: { [Op.in]: userIds } + }; } - const users = await User.findAll({ where: where_clause, - raw: true, - attributes: ['id', 'name', 'email'], - + raw, + attributes: ["id", "name", "email"], - // include: [ - // { - // model: UserQueue, - // separate: true, - - // attributes: ['id',], - - // order: [ - // ['createdAt', 'ASC'] - // ] - // }, - // ], - - - - order: [["id", "ASC"]], - }) + include: [ + { model: Queue, as: "queues", attributes: ["id", "name", "color"] } + ], + order: [["id", "ASC"]] + }); return users; }; export default ListUser; - - - - diff --git a/backend/src/services/WhatsappService/ListWhatsAppsForQueueService.ts b/backend/src/services/WhatsappService/ListWhatsAppsForQueueService.ts new file mode 100644 index 0000000..1f16647 --- /dev/null +++ b/backend/src/services/WhatsappService/ListWhatsAppsForQueueService.ts @@ -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 => { + 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; + \ No newline at end of file diff --git a/frontend/src/components/NewTicketModal/index.js b/frontend/src/components/NewTicketModal/index.js index 1d0a16a..4ff67f7 100644 --- a/frontend/src/components/NewTicketModal/index.js +++ b/frontend/src/components/NewTicketModal/index.js @@ -87,8 +87,15 @@ const NewTicketModal = ({ modalOpen, onClose }) => { if (newValue?.number) { setSelectedContact(newValue); } 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) } }; diff --git a/frontend/src/components/TransferTicketModal/index.js b/frontend/src/components/TransferTicketModal/index.js index 8680578..2e89106 100644 --- a/frontend/src/components/TransferTicketModal/index.js +++ b/frontend/src/components/TransferTicketModal/index.js @@ -1,30 +1,34 @@ -import React, { useState, useContext, useMemo } from "react"; -import { useHistory } from "react-router-dom"; +import React, { useState, useContext, useMemo, useEffect } from "react" +import { useHistory } from "react-router-dom" +import openSocket from "socket.io-client" -import Button from "@material-ui/core/Button"; -import Dialog from "@material-ui/core/Dialog"; -import Select from "@material-ui/core/Select"; -import FormControl from "@material-ui/core/FormControl"; -import InputLabel from "@material-ui/core/InputLabel"; -import MenuItem from "@material-ui/core/MenuItem"; -import { makeStyles } from "@material-ui/core"; +import Button from "@material-ui/core/Button" +import Dialog from "@material-ui/core/Dialog" +import Select from "@material-ui/core/Select" +import FormControl from "@material-ui/core/FormControl" +import InputLabel from "@material-ui/core/InputLabel" +import MenuItem from "@material-ui/core/MenuItem" +import { makeStyles } from "@material-ui/core" -import DialogActions from "@material-ui/core/DialogActions"; -import DialogContent from "@material-ui/core/DialogContent"; -import DialogTitle from "@material-ui/core/DialogTitle"; +import DialogActions from "@material-ui/core/DialogActions" +import DialogContent from "@material-ui/core/DialogContent" +import DialogTitle from "@material-ui/core/DialogTitle" -import { i18n } from "../../translate/i18n"; -import api from "../../services/api"; -import ButtonWithSpinner from "../ButtonWithSpinner"; -import toastError from "../../errors/toastError"; +import { i18n } from "../../translate/i18n" +import api from "../../services/api" +import ButtonWithSpinner from "../ButtonWithSpinner" +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) => ({ maxWidth: { width: "100%", }, -})); +})) // Receive array of queues arrays // 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 history = useHistory(); - const { whatsApps } = useContext(WhatsAppsContext); - const [loading, setLoading] = useState(false); - const [selectedQueue, setSelectedQueue] = useState(''); - const classes = useStyles(); - const queues = useMemo(() => { - if (!whatsApps) return [] + const history = useHistory() + const { whatsApps } = useContext(WhatsAppsContext) + const { user } = useContext(AuthContext) + + const [loading, setLoading] = useState(false) + const [selectedQueue, setSelectedQueue] = useState('') + 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.filter(({ status }) => status === "CONNECTED" ).map(({ queues }) => queues) const uniqueQueuesAvailable = queueArraysToOneArray(whatsAppsQueues) return uniqueQueuesAvailable }, [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 = () => { - onClose(); - }; + onClose() + } const handleSaveTicket = async e => { - e.preventDefault(); - if (!ticketid) return; - if (!selectedQueue) return; - setLoading(true); + e.preventDefault() + if (!ticketid) return + if (!selectedQueue) return + setLoading(true) try { - let data = {}; + let data = {} if (selectedQueue && selectedQueue !== null) { 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); - history.push(`/tickets`); + await api.put(`/tickets/${ticketid}`, data) + + setLoading(false) + history.push(`/tickets`) } catch (err) { - setLoading(false); - toastError(err); + setLoading(false) + 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 ( @@ -98,6 +230,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => { {i18n.t("transferTicketModal.fieldQueueLabel")} + + + +
+ + + +
@@ -140,7 +293,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
- ); -}; + ) +} -export default TransferTicketModal; \ No newline at end of file +export default TransferTicketModal \ No newline at end of file diff --git a/frontend/src/components/UserModal/index.js b/frontend/src/components/UserModal/index.js index d827ef8..3296e48 100644 --- a/frontend/src/components/UserModal/index.js +++ b/frontend/src/components/UserModal/index.js @@ -121,7 +121,31 @@ const UserModal = ({ open, onClose, userId }) => { const userData = { ...values, queueIds: selectedQueueIds }; try { 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 { await api.post("/users", userData); } @@ -229,6 +253,7 @@ const UserModal = ({ open, onClose, userId }) => { id="profile-selection" required > + Supervisor Admin User diff --git a/frontend/src/hooks/useAuth.js/index.js b/frontend/src/hooks/useAuth.js/index.js index 6e26b05..80db7f4 100644 --- a/frontend/src/hooks/useAuth.js/index.js +++ b/frontend/src/hooks/useAuth.js/index.js @@ -130,8 +130,9 @@ const useAuth = () => { const handleLogout = async () => { setLoading(true) - try { - await api.delete('/auth/logout') + try { + await api.delete(`/auth/logout/${user.id}`); + setIsAuth(false) setUser({}) localStorage.removeItem('token') diff --git a/frontend/src/layout/MainListItems.js b/frontend/src/layout/MainListItems.js index e55c074..9451b18 100644 --- a/frontend/src/layout/MainListItems.js +++ b/frontend/src/layout/MainListItems.js @@ -103,20 +103,31 @@ const MainListItems = (props) => { primary={i18n.t('mainDrawer.listItems.quickAnswers')} icon={} /> + + + ( + <> + + {i18n.t("mainDrawer.listItems.administration")} + } + /> + + )} + /> + + ( <> - - - {i18n.t('mainDrawer.listItems.administration')} - - } - /> + { const [deletingContact, setDeletingContact] = useState(null) const [confirmOpen, setConfirmOpen] = useState(false) const [hasMore, setHasMore] = useState(false) + const [settings, setSettings] = useState([]) const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined) @@ -161,10 +162,25 @@ const Contacts = () => { } - 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(() => { @@ -301,9 +317,44 @@ const Contacts = () => { 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) - 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 = () => { diff --git a/frontend/src/pages/Dashboard/index.js b/frontend/src/pages/Dashboard/index.js index 88d6121..16c4f2b 100644 --- a/frontend/src/pages/Dashboard/index.js +++ b/frontend/src/pages/Dashboard/index.js @@ -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 Container from "@material-ui/core/Container"; -import Grid from "@material-ui/core/Grid"; -import { makeStyles } from "@material-ui/core/styles"; -import Typography from "@material-ui/core/Typography"; -import Tooltip from "@mui/material/Tooltip"; -import Zoom from "@mui/material/Zoom"; -import IconButton from "@mui/material/IconButton"; -import Info from "@material-ui/icons/Info"; +import Paper from "@material-ui/core/Paper" +import Container from "@material-ui/core/Container" +import Grid from "@material-ui/core/Grid" +import { makeStyles } from "@material-ui/core/styles" +import Typography from "@material-ui/core/Typography" +import Tooltip from "@mui/material/Tooltip" +import Zoom from "@mui/material/Zoom" +import IconButton from "@mui/material/IconButton" +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 Chart from "./Chart"; -import openSocket from "socket.io-client"; -import api from "../../services/api"; +import Chart from "./Chart" +import openSocket from "socket.io-client" +import api from "../../services/api" -import { Can } from "../../components/Can"; -import TableUser from "../../components/DashboardUser/TableUser"; +import { Can } from "../../components/Can" +import TableUser from "../../components/DashboardUser/TableUser" const useStyles = makeStyles((theme) => ({ container: { @@ -104,15 +104,15 @@ const useStyles = makeStyles((theme) => ({ textAlign: "center", borderRadius: "5px", }, -})); +})) var _fifo -const sumOnlineTimeNow = (oldOnlineTimeSum) => { +const sumOnlineTimeNow = (oldOnlineTimeSum) => { let onlineTime = new Date() - if (!oldOnlineTimeSum.onlineTime) { + if (!oldOnlineTimeSum.onlineTime) { oldOnlineTimeSum.onlineTime = `${oldOnlineTimeSum.updatedAt.split(' ')[0]} 00:00:00` @@ -136,70 +136,70 @@ const sumOnlineTimeNow = (oldOnlineTimeSum) => { 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 } const reducer = (state, action) => { 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) { - state.splice(userIndex, 1); + state.splice(userIndex, 1) } - return [...state]; + return [...state] } if (action.type === "LOAD_QUERY") { - const queries = action.payload; - const newQueries = []; + const queries = action.payload + const newQueries = [] queries.forEach((query) => { - const queryIndex = state.findIndex((q) => q.id === query.id); + const queryIndex = state.findIndex((q) => q.id === query.id) if (queryIndex !== -1) { - state[queryIndex] = query; + state[queryIndex] = query } else { - newQueries.push(query); + newQueries.push(query) } - }); + }) - return [...state, ...newQueries]; + return [...state, ...newQueries] } if (action.type === "UPDATE_STATUS_ONLINE") { - let onlineUser = action.payload; - let index = -1; + let onlineUser = action.payload + let index = -1 // console.log('UPDATE_STATUS_ONLINE: ', onlineUser) - let onlySumOpenClosed = false; + let onlySumOpenClosed = false if (onlineUser.sumOpen || onlineUser.sumClosed) { index = state.findIndex( (e) => (onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) || (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId) - ); + ) - onlySumOpenClosed = true; + onlySumOpenClosed = true } else { - index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`); + index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`) } if (index !== -1) { if (!onlySumOpenClosed) { if (!("statusOnline" in state[index])) { - state[index].statusOnline = onlineUser; + state[index].statusOnline = onlineUser } 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 ("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])) { state[index].sumOnlineTime = { userId: onlineUser.userId, sum: onlineUser.onlineTime.split(" ")[1], - }; + } } } if (onlineUser.sumOpen) { 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])) { - state[index].sumOpen = onlineUser.sumOpen; + state[index].sumOpen = onlineUser.sumOpen } } if (onlineUser.sumClosed) { 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])) { - state[index].sumClosed = onlineUser.sumClosed; + state[index].sumClosed = onlineUser.sumClosed } } if (onlineUser.openClosedInQueue) { - state[index].openClosedInQueue = onlineUser.openClosedInQueue; + state[index].openClosedInQueue = onlineUser.openClosedInQueue } if (onlineUser.openClosedOutQueue) { - state[index].openClosedOutQueue = onlineUser.openClosedOutQueue; + state[index].openClosedOutQueue = onlineUser.openClosedOutQueue } } - return [...state]; + return [...state] } if (action.type === "RESET") { - return []; + return [] } -}; +} const Dashboard = () => { - const classes = useStyles(); - const [usersOnlineInfo, dispatch] = useReducer(reducer, []); - const [ticketStatusChange, setStatus] = useState(); - const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 }); + const classes = useStyles() + const [usersOnlineInfo, dispatch] = useReducer(reducer, []) + const [ticketStatusChange, setStatus] = useState() + const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 }) - const { user } = useContext(AuthContext); + const { user } = useContext(AuthContext) useEffect(() => { - dispatch({ type: "RESET" }); - }, []); + dispatch({ type: "RESET" }) + }, []) const handleLogouOnlineUser = async (userId) => { try { - await api.get(`/users/logout/${userId}`); + await api.get(`/users/logout/${userId}`) //toast.success(("Desloged!")); //handleDeleteRows(scheduleId) } catch (err) { // toastError(err); } - }; + } useEffect(() => { //setLoading(true); @@ -282,26 +281,26 @@ const Dashboard = () => { // setLoading(true); const fetchQueries = async () => { try { - let date = new Date().toLocaleDateString("pt-BR").split("/"); - let dateToday = `${date[2]}-${date[1]}-${date[0]}`; + let date = new Date().toLocaleDateString("pt-BR").split("/") + let dateToday = `${date[2]}-${date[1]}-${date[0]}` const { data } = await api.get("/reports/user/services", { params: { userId: null, startDate: dateToday, endDate: dateToday }, - }); + }) // console.log('data.data: ', data.usersProfile) - dispatch({ type: "RESET" }); - dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }); + dispatch({ type: "RESET" }) + dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }) } catch (err) { } - }; + } - fetchQueries(); - }, 500); - return () => clearTimeout(delayDebounceFn); - }, []); + fetchQueries() + }, 500) + return () => clearTimeout(delayDebounceFn) + }, []) useEffect(() => { @@ -310,18 +309,18 @@ const Dashboard = () => { if (!usersOnlineInfo || usersOnlineInfo.length === 0) return if (_fifo) { - clearInterval(_fifo); + clearInterval(_fifo) } _fifo = setInterval(() => { for (let i = 0; i < usersOnlineInfo.length; i++) { - if (usersOnlineInfo[i].statusOnline && usersOnlineInfo[i].statusOnline.status === 'online') { - - let onlineTimeCurrent = sumOnlineTimeNow({ onlineTime: usersOnlineInfo[i].statusOnline.onlineTime, updatedAt: usersOnlineInfo[i].statusOnline.updatedAt }) + if (usersOnlineInfo[i].statusOnline && usersOnlineInfo[i].statusOnline.status === 'online') { - 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]) useEffect(() => { - const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + const socket = openSocket(process.env.REACT_APP_BACKEND_URL) socket.on("ticketStatus", (data) => { if (data.action === "update") { - setStatus(""); - setStatus(data.ticketStatus.status); + setStatus("") + setStatus(data.ticketStatus.status) } - }); + }) socket.on("onlineStatus", (data) => { - if (data.action === "logout" || data.action === "update") { - dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }); + if (data.action === "logout" || data.action === "update") { + + dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }) } 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) => { if (data.action === "delete") { - dispatch({ type: "DELETE_USER", payload: +data.userId }); + dispatch({ type: "DELETE_USER", payload: +data.userId }) } - }); + }) return () => { - socket.disconnect(); - }; - }, []); + socket.disconnect() + } + }, []) useEffect(() => { if (ticketStatusChange === "") return const delayDebounceFn = setTimeout(() => { const fetchQueries = async () => { try { - let date = new Date().toLocaleDateString("pt-BR").split("/"); - let dateToday = `${date[2]}-${date[1]}-${date[0]}`; + let date = new Date().toLocaleDateString("pt-BR").split("/") + let dateToday = `${date[2]}-${date[1]}-${date[0]}` const _open = await api.get("/tickets/count", { params: { status: "open", date: dateToday }, - }); + }) const _closed = await api.get("/tickets/count", { params: { status: "closed", date: dateToday }, - }); + }) const _pending = await api.get("/tickets/count", { params: { status: "pending" }, - }); + }) const _openAll = await api.get("/tickets/count", { params: { status: "open" }, - }); + }) setTicktsStatus({ open: _open.data.count, openAll: _openAll.data.count, closed: _closed.data.count, pending: _pending.data.count, - }); + }) // setOpen(_open.data.count); // setClosed(_closed.data.count); // setPending(_pending.data.count); } catch (err) { - console.log(err); + console.log(err) } - }; + } - fetchQueries(); - }, 500); - return () => clearTimeout(delayDebounceFn); - }, [ticketStatusChange]); + fetchQueries() + }, 500) + return () => clearTimeout(delayDebounceFn) + }, [ticketStatusChange]) return ( { )} /> - ); -}; + ) +} -export default Dashboard; +export default Dashboard diff --git a/frontend/src/pages/Settings/index.js b/frontend/src/pages/Settings/index.js index 71792a2..be352a3 100644 --- a/frontend/src/pages/Settings/index.js +++ b/frontend/src/pages/Settings/index.js @@ -257,6 +257,35 @@ const Settings = () => { +
+ + + + Escopo de transferência de usuario por filas atribuidas a conexão e usuarios atribuido a filas + + + + + +
+ + )} /> diff --git a/frontend/src/rules.js b/frontend/src/rules.js index 1b7c1ad..10b27a5 100644 --- a/frontend/src/rules.js +++ b/frontend/src/rules.js @@ -3,11 +3,20 @@ const rules = { static: [], }, + supervisor: { + static: [ + "menu-users:view", + "user-view:show", + "user-modal:editQueues" + ] + }, + + admin: { static: [ 'show-icon-edit-whatsapp', 'show-icon-edit-queue', - + 'menu-users:view', 'drawer-admin-items:view', 'tickets-manager:showall', 'user-modal:editProfile', @@ -25,6 +34,7 @@ const rules = { master: { static: [ + 'menu-users:view', 'url-remote-session:show', 'show-icon-edit-whatsapp', 'show-icon-add-queue',