diff --git a/backend/src/controllers/ReportController.ts b/backend/src/controllers/ReportController.ts index 588e494..7f63d19 100644 --- a/backend/src/controllers/ReportController.ts +++ b/backend/src/controllers/ReportController.ts @@ -1,132 +1,241 @@ //relatorio -import { Request, Response } from "express"; -import AppError from "../errors/AppError"; +import { Request, Response } from "express"; +import AppError from "../errors/AppError"; import ShowTicketReport from "../services/TicketServices/ShowTicketReport"; import ShowMessageReport from "../services/MessageServices/ShowMessageReport"; -import onlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService"; +import onlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService"; import User from "../models/User"; import Queue from "../models/Queue"; import UserOnlineTime from "../models/UserOnlineTime"; -import { Op, Sequelize,literal } from "sequelize"; +import { Op, Sequelize, literal } from "sequelize"; import format from 'date-fns/format'; -import ptBR from 'date-fns/locale/pt-BR'; +import ptBR from 'date-fns/locale/pt-BR'; import { splitDateTime } from "../helpers/SplitDateTime"; import ListUserOnlineOffline from "../services/UserServices/ListUsersOnlineOfflineService"; import ListUserParamiterService from "../services/UserServices/ListUserParamiterService"; import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport"; +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; + +import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser"; +import { filter } from "bluebird"; + + type IndexQuery = { - userId: string; - startDate: string; - endDate: string; - pageNumber: string; - }; - - -export const reportUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { - - if (req.user.profile !== "master" && req.user.profile !== "admin") { - throw new AppError("ERR_NO_PERMISSION", 403); - } - - const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery - - - console.log('PAGE NUMBER: ', pageNumber) - - - const { tickets, count, hasMore } = await ShowTicketReport({userId, startDate, endDate, pageNumber}); - - // return res.status(200).json(data_query); - - return res.status(200).json({ tickets, count, hasMore }); + userId: string; + startDate: string; + endDate: string; + pageNumber: string; }; -export const reportUserService= async (req: Request, res: Response): Promise => { +export const reportUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { - if (req.user.profile !== "master" && req.user.profile !== "admin") { - throw new AppError("ERR_NO_PERMISSION", 403); - } - const { userId, startDate, endDate } = req.query as IndexQuery - + if (req.user.profile !== "master" && req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } - let usersProfile = await ListUserParamiterService({profile: 'user'}) - - const sumUserOlineTime = await ShowUserServiceReport({startDate, endDate, userId}); - const closedByUser = await ShowUserServiceReport({startDate, endDate, ticketStatus: 'closed', userId}); - const openByUser = await ShowUserServiceReport({startDate, endDate, ticketStatus: 'open', userId}); + const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery - let dateTime = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) - const onlineUsers = await ListUserOnlineOffline({ date: dateTime.fullDate }) - - usersProfile.map((user:any) => { + console.log('PAGE NUMBER: ', pageNumber) - let index = sumUserOlineTime.findIndex((e:any) => e.userId == user.id) - if (index != -1) { - user.sumOnlineTime = sumUserOlineTime[index]; - } + const { tickets, count, hasMore } = await ShowTicketReport({ userId, startDate, endDate, pageNumber }); - index = closedByUser.findIndex((e:any) => e.userId == user.id) + // return res.status(200).json(data_query); - if (index != -1) { - user.sumClosed = closedByUser[index]; - } - - index = openByUser.findIndex((e:any) => e.userId == user.id) - - if (index != -1) { - user.sumOpen = openByUser[index] - } - - - index = onlineUsers.findIndex((e:any) => e.userId == user.id) - - if (index != -1) { - user.statusOnline = onlineUsers[index] - } - - if(startDate.length>0 && startDate.split('-').length == 3){ - let date = startDate.split('-') - user.startDate = `${date[2]}/${date[1]}/${date[0]}` - } - - if(endDate.length>0 && endDate.split('-').length == 3){ - let date = endDate.split('-') - user.endDate = `${date[2]}/${date[1]}/${date[0]}` - } - - }) - - return res.status(200).json(usersProfile); - + return res.status(200).json({ tickets, count, hasMore }); }; +export const reportUserService = async (req: Request, res: Response): Promise => { -export const reportMessagesUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { + if (req.user.profile !== "master" && req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + const { userId, startDate, endDate } = req.query as IndexQuery - if (req.user.profile !== "master" && req.user.profile !== "admin") { - throw new AppError("ERR_NO_PERMISSION", 403); + + let usersProfile = await ListUserParamiterService({ profile: 'user' }) + + const sumUserOlineTime = await ShowUserServiceReport({ startDate, endDate, userId }); + const closedByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'closed', userId }); + const openByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'open', userId }); + + let dateTime = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + const onlineUsers = await ListUserOnlineOffline({ date: dateTime.fullDate }) + + const openByUserOnQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: true }) + const openByUserOutQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: false }) + + const closedByUserOnQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: true }) + const closedUserOutQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: false }) + + // let openQueueInOut = openByUserOnQueue.concat(openByUserOutQueue) + // let closedQueueInOut = closedByUserOnQueue.concat(closedUserOutQueue) + + + const queuesByUser = await ShowQueuesByUser({ profile: 'user' }) + + let openCloseOnQueue = openByUserOnQueue.concat(closedByUserOnQueue) + let openCloseOutQueue = openByUserOutQueue.concat(closedUserOutQueue) + + for (let i = 0; i < queuesByUser.length; i++) { + + queuesByUser[i].countOpen = 0 + queuesByUser[i].countClosed = 0 + + for (let x = 0; x < openCloseOnQueue.length; x++) { + if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'open')) { + queuesByUser[i].countOpen = openCloseOnQueue[x].totAttendance + } + else if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'closed')) { + queuesByUser[i].countClosed = openCloseOnQueue[x].totAttendance + } + + } + } + + + // console.log('queuesByUser: ', queuesByUser) + + + + // console.log('queuesByUser: ',queuesByUser) + // console.log() + // console.log('CLIENT START TRUE openByUserOnQueue: ', openByUserOnQueue) + // console.log() + // console.log('CLIENT START FALSE openByUserOutQueue: ', openByUserOutQueue) + // console.log() + // console.log('CLIENT START TRUE closedByUserOnQueue: ', closedByUserOnQueue) + // console.log() + // console.log('CLIENT START FALSE closedUserOutQueue: ', closedUserOutQueue) + + + usersProfile.map((user: any) => { + + let index = sumUserOlineTime.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.sumOnlineTime = sumUserOlineTime[index]; } - const { userId, startDate, endDate } = req.query as IndexQuery + index = closedByUser.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.sumClosed = closedByUser[index]; + } + + index = openByUser.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.sumOpen = openByUser[index] + } + + + + + + // let openByUserOut = openQueueInOut.filter((e: any) => e.userId == user.id && !e.queueName) + // let openByUserIn = openQueueInOut.filter((e: any) => e.userId == user.id && e.queueName) + + // if (openByUserOut && openByUserOut.length > 0) { + // user.openTicketByUserOut = openByUserOut + // } + // if (openByUserIn && openByUserIn.length > 0) { + // user.openTicketByUserIn = openByUserIn + // } + + // let closedByUserOut = closedQueueInOut.filter((e: any) => e.userId == user.id && !e.queueName) + // let closedByUserIn = closedQueueInOut.filter((e: any) => e.userId == user.id && e.queueName) + + // if (closedByUserOut && closedByUserOut.length > 0) { + // user.closedTicketByUserOut = closedByUserOut + // } + // if (closedByUserIn && closedByUserIn.length > 0) { + // user.closedTicketByUserIn = closedByUserIn + // } + + + + + + // OPEN, CLOSED TICKETS STARTED BY USERS + let openClosedOutQueue = {} + let open = openCloseOutQueue.filter((e) => e.userId == user.id && e.status == 'open') + let closed = openCloseOutQueue.filter((e) => e.userId == user.id && e.status == 'closed') + + openClosedOutQueue = { + ...openClosedOutQueue, + userId: user.id, + countOpen: open && open.length > 0 ? open[0].totAttendance : 0, + countClosed: closed && closed.length > 0 ? closed[0].totAttendance : 0 + } + + user.openClosedOutQueue = openClosedOutQueue + + + // OPEN, CLOSED TICKETS STARTED BY CLIENTS + let openClosedInQueue = queuesByUser.filter((e) => e.userId == user.id) + + if (openClosedInQueue && openClosedInQueue.length > 0) { + user.openClosedInQueue = openClosedInQueue + } + + + + + + + + + index = onlineUsers.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.statusOnline = onlineUsers[index] + } + + if (startDate.length > 0 && startDate.split('-').length == 3) { + let date = startDate.split('-') + user.startDate = `${date[2]}/${date[1]}/${date[0]}` + } + + if (endDate.length > 0 && endDate.split('-').length == 3) { + let date = endDate.split('-') + user.endDate = `${date[2]}/${date[1]}/${date[0]}` + } + + }) + + return res.status(200).json(usersProfile); - const data_query_messages = await ShowMessageReport(userId, startDate, endDate); - - return res.status(200).json(data_query_messages); - }; - + +export const reportMessagesUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { + + if (req.user.profile !== "master" && req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + const { userId, startDate, endDate } = req.query as IndexQuery + + const data_query_messages = await ShowMessageReport(userId, startDate, endDate); + + return res.status(200).json(data_query_messages); + +}; + + + - \ No newline at end of file diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 128b10c..e68af61 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -21,6 +21,8 @@ import format from 'date-fns/format'; + + type IndexQuery = { searchParam: string; pageNumber: string; @@ -44,8 +46,12 @@ import ListStatusChatEndService from "../services/StatusChatEndService/ListStatu import Ticket from "../models/Ticket"; import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport"; import TicketEmiterSumOpenClosedByUser from "../helpers/OnlineReporEmiterInfoByUser"; +import CountTicketService from "../services/TicketServices/CountTicketService"; +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; +import ShowUserService from "../services/UserServices/ShowUserService"; + +export const index = async (req: Request, res: Response): Promise => { -export const index = async (req: Request, res: Response): Promise => { const { pageNumber, status, @@ -55,8 +61,7 @@ export const index = async (req: Request, res: Response): Promise => { queueIds: queueIdsStringified, withUnreadMessages, unlimited - } = req.query as IndexQuery; - + } = req.query as IndexQuery; const userId = req.user.id; @@ -99,7 +104,7 @@ export const store = async (req: Request, res: Response): Promise => { console.log('TICKET QUEUE CHOICE !!!!!!!') } else { - ticket = await CreateTicketService({ contactId, status, userId }); + ticket = await CreateTicketService({ contactId, status, userId }); } const io = getIO(); @@ -138,6 +143,17 @@ export const show = async (req: Request, res: Response): Promise => { }; +export const count = async (req: Request, res: Response): Promise => { + + type indexQ = { status: string; date?: string; }; + const {status, date} = req.query as IndexQuery + + const ticketCount = await CountTicketService(status, date); + + return res.status(200).json(ticketCount); +}; + + @@ -214,19 +230,22 @@ export const update = async (req: Request, res: Response): Promise => const ticketData: TicketData = req.body; - //ticketData: { status: 'open', userId: 4 } , ticketId + //ticketData: { status: 'open', userId: 4 } , ticketId const { ticket } = await UpdateTicketService({ ticketData, ticketId }); + const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + TicketEmiterSumOpenClosedByUser(ticketData.userId.toString(), dateToday.fullDate, dateToday.fullDate) + ticket2 = ticket } - // test del + if (userOldInfo) { @@ -234,12 +253,15 @@ export const update = async (req: Request, res: Response): Promise => if (userOldInfo.userId) { - TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate) + // console.log('FECHOU...') + + TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate) + } } - // + return res.status(200).json(ticket2); }; @@ -248,7 +270,6 @@ export const update = async (req: Request, res: Response): Promise => - // export const update = async ( // req: Request, // res: Response diff --git a/backend/src/helpers/OnlineReporEmiterInfoByUser.ts b/backend/src/helpers/OnlineReporEmiterInfoByUser.ts index 382d001..7b2abbd 100644 --- a/backend/src/helpers/OnlineReporEmiterInfoByUser.ts +++ b/backend/src/helpers/OnlineReporEmiterInfoByUser.ts @@ -3,23 +3,78 @@ import { splitDateTime } from "../helpers/SplitDateTime"; import format from 'date-fns/format'; import ShowUserServiceReport from '../services/UserServices/ShowUserServiceReport'; import { getIO } from "../libs/socket"; +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; +import ShowQueuesByUser from '../services/UserServices/ShowQueuesByUser'; + +// import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; -const TicketEmiterSumOpenClosedByUser = async (userId: string, startDate: string, endDate: string) => { +const TicketEmiterSumOpenClosedByUser = async (userId: string, startDate: string, endDate: string) => { - const openByUser: any[] = await ShowUserServiceReport({ startDate: startDate, endDate:endDate, ticketStatus: 'open', userId: userId.toString() }); - const closedByUser: any[] = await ShowUserServiceReport({ startDate: endDate, endDate:endDate, ticketStatus: 'closed', userId: userId.toString() }); + const openByUser: any[] = await ShowUserServiceReport({ startDate: startDate, endDate: endDate, ticketStatus: 'open', userId: userId.toString() }); + const closedByUser: any[] = await ShowUserServiceReport({ startDate: endDate, endDate: endDate, ticketStatus: 'closed', userId: userId.toString() }); + + + const openByUserOnQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: true, userId: userId }) + const openByUserOutQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: false, userId: userId }) + + const closedByUserOnQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: true, userId: userId }) + const closedUserOutQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: false, userId: userId }) + + + + const queuesByUser = await ShowQueuesByUser({ profile: 'user', userId: userId }) + + let openCloseOnQueue = openByUserOnQueue.concat(closedByUserOnQueue) + let openCloseOutQueue = openByUserOutQueue.concat(closedUserOutQueue) + + + // OPEN, CLOSED TICKETS STARTED BY CLIENTS + for (let i = 0; i < queuesByUser.length; i++) { + + queuesByUser[i].countOpen = 0 + queuesByUser[i].countClosed = 0 + + for (let x = 0; x < openCloseOnQueue.length; x++) { + if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'open')) { + queuesByUser[i].countOpen = openCloseOnQueue[x].totAttendance + } + else if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'closed')) { + queuesByUser[i].countClosed = openCloseOnQueue[x].totAttendance + } + + } + } + + + // OPEN, CLOSED TICKETS STARTED BY USERS + let openClosedOutQueue = {} + let open = openCloseOutQueue.filter((e) => e.status == 'open') + let closed = openCloseOutQueue.filter((e) => e.status == 'closed') + + openClosedOutQueue = { + ...openClosedOutQueue, + userId: userId, + countOpen: open && open.length > 0 ? open[0].totAttendance : 0, + countClosed: closed && closed.length > 0 ? closed[0].totAttendance : 0 + } - //openByUser : [ { id: 13, status: 'online' } ] const io = getIO(); io.emit("onlineStatus", { action: "update", - userOnlineTime: { - sumOpen: openByUser.length > 0 ? openByUser[0] : { userId: userId, count: '' }, - sumClosed: closedByUser.length > 0 ? closedByUser[0] : { userId: userId, count: '' }, + userOnlineTime: { + sumOpen: openByUser.length > 0 ? openByUser[0] : { userId: userId, count: '' }, + sumClosed: closedByUser.length > 0 ? closedByUser[0] : { userId: userId, count: '' }, + + openClosedOutQueue: openClosedOutQueue, + openClosedInQueue: queuesByUser + + } - }); + }); }; diff --git a/backend/src/routes/ticketRoutes.ts b/backend/src/routes/ticketRoutes.ts index 742d375..e2c0a76 100644 --- a/backend/src/routes/ticketRoutes.ts +++ b/backend/src/routes/ticketRoutes.ts @@ -4,9 +4,11 @@ import isAuth from "../middleware/isAuth"; import * as TicketController from "../controllers/TicketController"; const ticketRoutes = express.Router(); - -ticketRoutes.get("/tickets", isAuth, TicketController.index); + +ticketRoutes.get("/tickets/count", isAuth, TicketController.count); + +ticketRoutes.get("/tickets", isAuth, TicketController.index); ticketRoutes.get("/tickets/:ticketId", isAuth, TicketController.show); diff --git a/backend/src/server.ts b/backend/src/server.ts index 103fa46..ac9bccc 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -16,4 +16,4 @@ StartAllWhatsAppsSessions(); gracefulShutdown(server); startSchedulingMonitor(5000) -startWhoIsOnlineMonitor(5000) +startWhoIsOnlineMonitor(2000) diff --git a/backend/src/services/TicketServices/CountTicketService.ts b/backend/src/services/TicketServices/CountTicketService.ts new file mode 100644 index 0000000..2cdde93 --- /dev/null +++ b/backend/src/services/TicketServices/CountTicketService.ts @@ -0,0 +1,50 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; +import { Op, where } from "sequelize"; +import { Sequelize } from "sequelize"; + +import { format } from "date-fns"; +import ptBR from 'date-fns/locale/pt-BR'; + +import { splitDateTime } from "../../helpers/SplitDateTime"; +const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + +const CountTicketService = async (status: string, date?: string): Promise => { + + let where_clause = {} + + if (date) { + where_clause = { + createdAt: { + [Op.gte]: date + ' 00:00:00.000000', + [Op.lte]: date + ' 23:59:59.999999' + } + } + } + else { + // where_clause = { + // createdAt: { + // [Op.gte]: dateToday.fullDate + ' 00:00:00.000000', + // [Op.lte]: dateToday.fullDate + ' 23:59:59.999999' + // } + // } + } + + + where_clause = { ...where_clause, status: status } + + const ticket = await Ticket.findAll({ + where: where_clause, + raw: true, + attributes: [[Sequelize.fn("COUNT", Sequelize.col("Ticket.status")), "count"]], + }); + + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + return ticket[0]; +}; + +export default CountTicketService; \ No newline at end of file diff --git a/backend/src/services/UserServices/CountTicketsByUserQueue.ts b/backend/src/services/UserServices/CountTicketsByUserQueue.ts new file mode 100644 index 0000000..dc13108 --- /dev/null +++ b/backend/src/services/UserServices/CountTicketsByUserQueue.ts @@ -0,0 +1,73 @@ + +import { Sequelize, } from "sequelize"; + +const dbConfig = require("../../config/database"); +const sequelize = new Sequelize(dbConfig); + +const { QueryTypes } = require('sequelize'); + +interface Request { + startDate: string; + endDate: string; + status: string; + clientChatStart: boolean; + userId?: string | number; +} + +const CountTicketsByUserQueue = async ({ startDate, endDate, status, clientChatStart, userId }: Request): Promise => { + + let usersQueueInfo = [] + + if (clientChatStart) { + + if (userId) { + // CONSULTANDO POR CONVERSAS INICIADAS PELO CLIENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, Queues.id as queueId, Queues.name as queueName, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId = Queues.id \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users inner join Queues on Tickets.queueId = Queues.id and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') and Users.id = '${userId}' group by Users.email, Queues.name;`, { type: QueryTypes.SELECT }); + + } else { + + // CONSULTANDO POR CONVERSAS INICIADAS PELO CLIENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, Queues.id as queueId, Queues.name as queueName, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId = Queues.id \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users inner join Queues on Tickets.queueId = Queues.id and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') group by Users.email, Queues.name;`, { type: QueryTypes.SELECT }); + + } + } + else { + + if (userId) { + // CONSULTANDO POR CONVERSAS INICIADAS PELO ATENDENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId is null \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users on \ + Tickets.queueId is null and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') and Users.id = '${userId}' group by Users.email;`, { type: QueryTypes.SELECT }); + } + else { + // CONSULTANDO POR CONVERSAS INICIADAS PELO ATENDENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId is null \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users on \ + Tickets.queueId is null and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') group by Users.email;`, { type: QueryTypes.SELECT }); + } + + + } + + return usersQueueInfo; +}; + +export default CountTicketsByUserQueue; + + + + diff --git a/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts b/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts index b7fbe80..3075da3 100644 --- a/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts +++ b/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts @@ -116,7 +116,8 @@ const CreateOrUpdateUserOnlineTime = async ({ console.log('CreatedAt string: ', createdAtString) console.log('UpdatedAt string: ', updatedAtString) - // + // + io.emit("onlineStatus", { action: "update", @@ -132,7 +133,7 @@ const CreateOrUpdateUserOnlineTime = async ({ } else if (oldStatus == 'offline' && status === 'online') { await userOnlineTime.update({ status }) - + io.emit("onlineStatus", { action: "update", userOnlineTime: { diff --git a/backend/src/services/UserServices/ListUserParamiterService.ts b/backend/src/services/UserServices/ListUserParamiterService.ts index 70f985a..e8ff0bd 100644 --- a/backend/src/services/UserServices/ListUserParamiterService.ts +++ b/backend/src/services/UserServices/ListUserParamiterService.ts @@ -1,48 +1,66 @@ - -import { Op, Sequelize } from "sequelize"; + +import { Op, Sequelize } from "sequelize"; +import Queue from "../../models/Queue"; import User from "../../models/User"; +import UserQueue from "../../models/UserQueue"; interface Request { - userId?: string | number; - profile?: string; + userId?: string | number; + profile?: string; +} + + +const ListUser = async ({ profile, userId }: Request): Promise => { + + let where_clause = {} + + if (userId && profile) { + where_clause = { + [Op.and]: [ + { userId: userId }, + { profile: profile } + ] + } } - - -const ListUser = async ({profile, userId}: Request): Promise => { - - let where_clause = {} - - if(userId && profile){ - where_clause = { - [Op.and]: [ - {userId: userId}, - {profile: profile} - ] - } + else if (userId) { + where_clause = { + [Op.and]: [ + { userId: userId }, + ] } - else if(userId){ - where_clause = { - [Op.and]: [ - {userId: userId}, - ] - } - } - else if(profile){ - where_clause = { - profile: profile - } + } + else if (profile) { + where_clause = { + profile: profile } - + } - const users = await User.findAll({ + + const users = await User.findAll({ where: where_clause, - raw:true, + raw: true, attributes: ['id', 'name', 'email'], - - order: [["id", "ASC"]], - }) + // include: [ + // { + // model: UserQueue, + // separate: true, + + // attributes: ['id',], + + // order: [ + // ['createdAt', 'ASC'] + // ] + // }, + // ], + + + + order: [["id", "ASC"]], + }) + + return users; }; @@ -51,4 +69,3 @@ export default ListUser; - \ No newline at end of file diff --git a/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts b/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts index 1fe4205..04f3cb0 100644 --- a/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts +++ b/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts @@ -61,11 +61,11 @@ const ListUserOnlineOffline = async ({date, userId, status}: Request): Promise => { + + let queueByUsersInfo = [] + + if (userId) { + // CONSULTANDO FILAS PELO ID DO USUARIO + queueByUsersInfo = await sequelize.query(`select UserQueues.userId, UserQueues.queueId, Users.name, + Queues.name, Queues.color from UserQueues inner join Users inner join Queues on + UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.id = '${userId}' and Users.profile = '${profile}' order by userId, queueId;`, { type: QueryTypes.SELECT }); + + } else { + + // CONSULTANDO FILAS PELO USUARIO + queueByUsersInfo = await sequelize.query(`select UserQueues.userId, UserQueues.queueId, Users.name, + Queues.name, Queues.color from UserQueues inner join Users inner join Queues on + UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.profile = '${profile}' order by userId, queueId;`, { type: QueryTypes.SELECT }); + + } + + return queueByUsersInfo; +}; + +export default QueuesByUser; + + + + diff --git a/backend/src/services/UserServices/ShowUserServiceReport.ts b/backend/src/services/UserServices/ShowUserServiceReport.ts index c6129ab..f9de30d 100644 --- a/backend/src/services/UserServices/ShowUserServiceReport.ts +++ b/backend/src/services/UserServices/ShowUserServiceReport.ts @@ -106,6 +106,8 @@ const ShowUserServiceReport = async ({ } + // console.log('>>>>>>>>>>>>>> objQuery: ', objQuery) + if (!objQuery) { diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 3a1d336..2699cd5 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -10,7 +10,8 @@ import TicketsListSkeleton from "../TicketsListSkeleton"; import useTickets from "../../hooks/useTickets"; import { i18n } from "../../translate/i18n"; -import { AuthContext } from "../../context/Auth/AuthContext"; +import { AuthContext } from "../../context/Auth/AuthContext"; + const useStyles = makeStyles(theme => ({ ticketsListWrapper: { @@ -73,9 +74,9 @@ const useStyles = makeStyles(theme => ({ const reducer = (state, action) => { if (action.type === "LOAD_TICKETS") { - const newTickets = action.payload; - - + const newTickets = action.payload; + + newTickets.forEach(ticket => { // console.log('* ticket.unreadMessages: ',ticket.unreadMessages) @@ -108,7 +109,7 @@ const reducer = (state, action) => { if (action.type === "UPDATE_TICKET") { const ticket = action.payload; - // console.log('++++++++++++ UPDATE_TICKET: ',ticket) + // console.log('++++++++++++ UPDATE_TICKET: ',ticket) const ticketIndex = state.findIndex(t => t.id === ticket.id); if (ticketIndex !== -1) { @@ -124,23 +125,23 @@ const reducer = (state, action) => { const message = action.payload.message - const ticket = action.payload.ticket; + const ticket = action.payload.ticket; const ticketIndex = state.findIndex(t => t.id === ticket.id); if (ticketIndex !== -1) { - + // console.log('>>>>>> ticketIndex: ', ticketIndex) // console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message) - - if(!message.fromMe){ - ticket.unreadMessages +=1 + + if (!message.fromMe) { + ticket.unreadMessages += 1 } state[ticketIndex] = ticket; state.unshift(state.splice(ticketIndex, 1)[0]); - + } else { state.unshift(ticket); } @@ -223,7 +224,8 @@ const TicketsList = (props) => { }); - socket.on("ticket", data => { + socket.on("ticket", data => { + if (data.action === "updateUnread") { dispatch({ type: "RESET_UNREAD", @@ -238,13 +240,13 @@ const TicketsList = (props) => { }); } - if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { + if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); } if (data.action === "delete") { dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); - } + } }); socket.on("appMessage", data => { @@ -288,11 +290,11 @@ const TicketsList = (props) => { setPageNumber(prevState => prevState + 1); }; - const handleScroll = e => { + const handleScroll = e => { if (!hasMore || loading) return; - const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; if (scrollHeight - (scrollTop + 100) < clientHeight) { loadMore(); diff --git a/frontend/src/hooks/useTickets/index.js b/frontend/src/hooks/useTickets/index.js index c1250b3..e56e38f 100644 --- a/frontend/src/hooks/useTickets/index.js +++ b/frontend/src/hooks/useTickets/index.js @@ -15,7 +15,7 @@ const useTickets = ({ }) => { const [loading, setLoading] = useState(true); const [hasMore, setHasMore] = useState(false); - const [tickets, setTickets] = useState([]); + const [tickets, setTickets] = useState([]); useEffect(() => { setLoading(true); diff --git a/frontend/src/pages/Dashboard/Chart.js b/frontend/src/pages/Dashboard/Chart.js index d6207f5..77671b0 100644 --- a/frontend/src/pages/Dashboard/Chart.js +++ b/frontend/src/pages/Dashboard/Chart.js @@ -16,12 +16,19 @@ import { i18n } from "../../translate/i18n"; import Title from "./Title"; import useTickets from "../../hooks/useTickets"; -const Chart = () => { +const Chart = (props) => { const theme = useTheme(); const date = useRef(new Date().toISOString()); const { tickets } = useTickets({ date: date.current, unlimited: "true" }); + + + // useEffect(()=>{ + // console.log('kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk') + // },[props.change]) + + const [chartData, setChartData] = useState([ { time: "08:00", amount: 0 }, { time: "09:00", amount: 0 }, @@ -42,10 +49,7 @@ const Chart = () => { let aux = [...prevState]; aux.forEach(a => { - tickets.forEach(ticket => { - format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time && - a.amount++; - }); + tickets.forEach(ticket => { format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time && a.amount++; }); }); return aux; @@ -54,9 +58,8 @@ const Chart = () => { return ( - {`${i18n.t("dashboard.charts.perDay.title")}${ - tickets.length - }`} + {`${i18n.t("dashboard.charts.perDay.title")}${tickets.length + }`} ({ container: { paddingTop: theme.spacing(4), @@ -44,29 +50,314 @@ const useStyles = makeStyles(theme => ({ }, })) + + + +const reducer = (state, action) => { + + if (action.type === "DELETE_USER_STATUS") { + + const userId = action.payload; + + const userIndex = state.findIndex((u) => `${u.id}` === `${userId}`); + + if (userIndex !== -1) { + state.splice(userIndex, 1); + } + + return [...state]; + } + + + + if (action.type === 'LOAD_QUERY') { + + const queries = action.payload + const newQueries = [] + + queries.forEach((query) => { + + const queryIndex = state.findIndex((q) => q.id === query.id) + + if (queryIndex !== -1) { + state[queryIndex] = query + } + else { + newQueries.push(query) + } + + }) + + return [...state, ...newQueries] + } + + if (action.type === "UPDATE_STATUS_ONLINE") { + + let onlineUser = action.payload + let index = -1 + + 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 + } + else { + index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`) + } + + + if (index !== -1) { + + if (!onlySumOpenClosed) { + + if (!("statusOnline" in state[index])) { + + state[index].statusOnline = onlineUser + + } + else if ("statusOnline" in state[index]) { + + state[index].statusOnline['status'] = onlineUser.status + + } + + } + + + if ("onlineTime" in onlineUser) { + + if ("sumOnlineTime" in state[index]) { + 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]) { + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1 | state[index].sumOpen["count"]: ', state[index].sumOpen['count'], ' | onlineUser.sumOpen.count: ', onlineUser.sumOpen.count) + state[index].sumOpen['count'] = onlineUser.sumOpen.count + } else if (!("sumOpen" in state[index])) { + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + state[index].sumOpen = onlineUser.sumOpen + } + + } + + if (onlineUser.sumClosed) { + + if ("sumClosed" in state[index]) { + // console.log(' >>>>>>>>>>>>>>>>>> sumClosed 1 | state[index].sumClosed["count"]: ', state[index].sumClosed['count'], ' | onlineUser.sumClosed.count: ', onlineUser.sumClosed.count) + state[index].sumClosed['count'] = onlineUser.sumClosed.count + } else if (!("sumClosed" in state[index])) { + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + state[index].sumClosed = onlineUser.sumClosed + } + + } + + + + if(onlineUser.openClosedInQueue){ + state[index].openClosedInQueue = onlineUser.openClosedInQueue + } + if(onlineUser.openClosedOutQueue){ + state[index].openClosedOutQueue = onlineUser.openClosedOutQueue + } + + + + // if(onlineUser.closedTicketByUserIn){ + // state[index].closedTicketByUserIn = onlineUser.closedTicketByUserIn + // } + // if(onlineUser.closedTicketByUserOut){ + // state[index].closedTicketByUserOut = onlineUser.closedTicketByUserOut + // } + // if(onlineUser.openTicketByUserIn){ + // state[index].openTicketByUserIn = onlineUser.openTicketByUserIn + // } + // if(onlineUser.openTicketByUserOut){ + // state[index].openTicketByUserOut = onlineUser.openTicketByUserOut + // } + + } + return [...state] + + } + + if (action.type === "RESET") { + return []; + } + +} + + + const Dashboard = () => { const classes = useStyles() + const [usersOnlineInfo, dispatch] = useReducer(reducer, []) + + const [open, setOpen] = useState(0) + const [closed, setClosed] = useState(0) + const [pending, setPending] = useState(0) const { user } = useContext(AuthContext); - var userQueueIds = []; + // var userQueueIds = []; - if (user.queues && user.queues.length > 0) { - userQueueIds = user.queues.map(q => q.id); - } + // if (user.queues && user.queues.length > 0) { + // userQueueIds = user.queues.map(q => q.id); + // } - const GetTickets = (status, showAll, withUnreadMessages, unlimited) => { + // const GetTickets = (status, showAll, withUnreadMessages, unlimited) => { + + // const { tickets } = useTickets({ + // status: status, + // showAll: showAll, + // withUnreadMessages: withUnreadMessages, + // queueIds: JSON.stringify(userQueueIds), + // unlimited: unlimited + // }); + // return tickets.length; + // } + + useEffect(() => { + + dispatch({ type: "RESET" }); + + }, []); + + + + + const handleLogouOnlineUser = async (userId) => { + try { + await api.get(`/users/logout/${userId}`); + //toast.success(("Desloged!")); + //handleDeleteRows(scheduleId) + } catch (err) { + // toastError(err); + } + + + }; + + + useEffect(() => { + + //setLoading(true); + + const delayDebounceFn = setTimeout(() => { + + // setLoading(true); + const fetchQueries = async () => { + try { + + let date = new Date().toLocaleDateString('pt-BR').split('/') + let dateToday = `${date[2]}-${date[1]}-${date[0]}` + + const dataQuery = await api.get("/reports/user/services", { params: { userId: null, startDate: dateToday, endDate: dateToday }, }); + dispatch({ type: "RESET" }) + dispatch({ type: "LOAD_QUERY", payload: dataQuery.data }); + + console.log('xxx REPORT 2 dataQuery : ', dataQuery.data) + + //console.log() + + } catch (err) { + console.log(err); + } + }; + + fetchQueries(); + + }, 500); + return () => clearTimeout(delayDebounceFn); + + }, []); + + + + useEffect(() => { + + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + + // socket.on("ticket", (data) => { + // console.log('OK') + // }); + + socket.on("onlineStatus", (data) => { + + if (data.action === "logout" || (data.action === "update")) { + + // console.log('>>>>>>> data.userOnlineTime: ', data.userOnlineTime) + + + dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }); + + } + else if (data.action === "delete") { + dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); + } - const { tickets } = useTickets({ - status: status, - showAll: showAll, - withUnreadMessages: withUnreadMessages, - queueIds: JSON.stringify(userQueueIds), - unlimited: unlimited }); - return tickets.length; - } - return ( + socket.on("user", (data) => { + + if (data.action === "delete") { + // console.log(' entrou no delete user: ', data) + dispatch({ type: "DELETE_USER", payload: +data.userId }); + } + }); + + return () => { + socket.disconnect(); + }; + + + }, []); + + + useEffect(() => { + + const delayDebounceFn = setTimeout(() => { + + const fetchQueries = async () => { + + try { + + 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', date: dateToday } }); + + setOpen(_open.data.count) + setClosed(_closed.data.count) + setPending(_pending.data.count) + + + } catch (err) { + console.log(err); + } + }; + + fetchQueries(); + + }, 500); + return () => clearTimeout(delayDebounceFn); + + }, [usersOnlineInfo]); + + + + + return ( {
- + @@ -83,7 +374,8 @@ const Dashboard = () => { - {GetTickets("open", "true", "false", "true")} + {/* {GetTickets("open", "true", "false", "true")} */} + {open} @@ -95,7 +387,8 @@ const Dashboard = () => { - {GetTickets("pending", "true", "false", "true")} + {/* {GetTickets("pending", "true", "false", "true")} */} + {pending} @@ -107,25 +400,175 @@ const Dashboard = () => { - {GetTickets("closed", "true", "false", "true")} - - + {/* {GetTickets("closed", "true", "false", "true")} */} + {closed} + + - + + + + + + + + {'Total online'} + + + + {usersOnlineInfo && + usersOnlineInfo.filter((user) => user.statusOnline && user.statusOnline.status === 'online').length + } + + + + + + + + {'Total offline'} + + + + {usersOnlineInfo && + usersOnlineInfo.filter((user) => !user.statusOnline || user.statusOnline.status === 'offline').length + } + + + + + + + + + + + + + + + {usersOnlineInfo && + + usersOnlineInfo.map((userInfo, index) => ( + + // {option.label} + + + <> + {userInfo.statusOnline && + + + + + + + {userInfo.name} + + + + + {userInfo.statusOnline && + userInfo.statusOnline.status + } + + + + Em atendimento: {userInfo.sumOpen && userInfo.sumOpen.count} + + + + Finalizado: {userInfo.sumClosed && userInfo.sumClosed.count} + + + + Tempo online: {userInfo.sumOnlineTime && userInfo.sumOnlineTime.sum} + + + +
+ +
Em atendimento(open/closed) por fila, conversas iniciadas pelos clientes:
+ + + {userInfo.openClosedInQueue && + + userInfo.openClosedInQueue.map((info, index) => ( + <> + + {info.name}: OPEN {info.countOpen} | CLOSED {info.countClosed} + + + )) + + } + +
+ +
+ +
Em atendimento(open/closed) sem fila, conversas iniciadas por atendentes:
+ + {userInfo.openClosedOutQueue && + + + SEM FILA: OPEN {userInfo.openClosedOutQueue.countOpen} | CLOSED {userInfo.openClosedOutQueue.countClosed} + + + } + +
+ + + {userInfo.statusOnline && userInfo.statusOnline.status === "online" && + + userInfo.statusOnline && + + + + + + + + } + + + + +
+
+
+ } + + + + )) + } + + + -
-
+ + )} /> - - + + diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index c2467c6..8476a78 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -417,7 +417,7 @@ const Report = () => { //setLoading(false); - // console.log('REPORT 2 dataQuery : ', dataQuery.data) + console.log('REPORT 2 dataQuery : ', dataQuery.data) //console.log() @@ -536,17 +536,10 @@ const Report = () => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - socket.on("onlineStatus", (data) => { - - // setLoading(true); - + socket.on("onlineStatus", (data) => { let date = new Date().toLocaleDateString('pt-BR').split('/') - let dateToday = `${date[2]}-${date[1]}-${date[0]}` - - // console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR')) - // console.log('date2: ', startDate) - + let dateToday = `${date[2]}-${date[1]}-${date[0]}` if (data.action === "logout" || (data.action === "update" && ((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) { @@ -558,9 +551,7 @@ const Report = () => { } else if (data.action === "delete") { dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); - } - - // setLoading(false); + } }); @@ -608,7 +599,8 @@ const Report = () => { // ))) // } - setData(query.map((column) => { return { ...column } })) + setData(query.map((column) => { return { ...column } })) + }, [query])