Merge pull request #18 from RenatoDiGiacomo/dashboard/merge

Dashboard/merge
pull/20/head
Adriano Robson 2022-08-09 11:12:25 -03:00 committed by GitHub
commit d1ceb54590
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
37 changed files with 1672 additions and 770 deletions

View File

@ -10,7 +10,7 @@ import User from "../models/User";
import Queue from "../models/Queue"; import Queue from "../models/Queue";
import UserOnlineTime from "../models/UserOnlineTime"; import UserOnlineTime from "../models/UserOnlineTime";
import { Op, Sequelize,literal } from "sequelize"; import { Op, Sequelize, literal } from "sequelize";
import format from 'date-fns/format'; 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 { splitDateTime } from "../helpers/SplitDateTime";
@ -19,12 +19,19 @@ import ListUserParamiterService from "../services/UserServices/ListUserParamiter
import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport"; import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport";
import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue";
import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
import { filter } from "bluebird";
type IndexQuery = { type IndexQuery = {
userId: string; userId: string;
startDate: string; startDate: string;
endDate: string; endDate: string;
}; pageNumber: string;
};
export const reportUserByDateStartDateEnd = async (req: Request, res: Response): Promise<Response> => { export const reportUserByDateStartDateEnd = async (req: Request, res: Response): Promise<Response> => {
@ -33,17 +40,21 @@ export const reportUserByDateStartDateEnd = async (req: Request, res: Response):
throw new AppError("ERR_NO_PERMISSION", 403); throw new AppError("ERR_NO_PERMISSION", 403);
} }
const { userId, startDate, endDate } = req.query as IndexQuery const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery
const data_query = await ShowTicketReport(userId, startDate, endDate); console.log('PAGE NUMBER: ', pageNumber)
return res.status(200).json(data_query);
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 });
}; };
export const reportUserService= async (req: Request, res: Response): Promise<Response> => { export const reportUserService = async (req: Request, res: Response): Promise<Response> => {
if (req.user.profile !== "master" && req.user.profile !== "admin") { if (req.user.profile !== "master" && req.user.profile !== "admin") {
throw new AppError("ERR_NO_PERMISSION", 403); throw new AppError("ERR_NO_PERMISSION", 403);
@ -51,49 +62,153 @@ export const reportUserService= async (req: Request, res: Response): Promise<Res
const { userId, startDate, endDate } = req.query as IndexQuery const { userId, startDate, endDate } = req.query as IndexQuery
let usersProfile = await ListUserParamiterService({profile: 'user'}) let usersProfile = await ListUserParamiterService({ profile: 'user' })
const sumUserOlineTime = await ShowUserServiceReport({startDate, endDate, userId}); const sumUserOlineTime = await ShowUserServiceReport({ startDate, endDate, userId });
const closedByUser = await ShowUserServiceReport({startDate, endDate, ticketStatus: 'closed', userId}); const closedByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'closed', userId });
const openByUser = await ShowUserServiceReport({startDate, endDate, ticketStatus: 'open', 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 }))) let dateTime = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
const onlineUsers = await ListUserOnlineOffline({ date: dateTime.fullDate }) 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 })
usersProfile.map((user:any) => { 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 index = sumUserOlineTime.findIndex((e:any) => e.userId == user.id) // 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) { if (index != -1) {
user.sumOnlineTime = sumUserOlineTime[index]; user.sumOnlineTime = sumUserOlineTime[index];
} }
index = closedByUser.findIndex((e:any) => e.userId == user.id) index = closedByUser.findIndex((e: any) => e.userId == user.id)
if (index != -1) { if (index != -1) {
user.sumClosed = closedByUser[index]; user.sumClosed = closedByUser[index];
} }
index = openByUser.findIndex((e:any) => e.userId == user.id) index = openByUser.findIndex((e: any) => e.userId == user.id)
if (index != -1) { if (index != -1) {
user.sumOpen = openByUser[index] user.sumOpen = openByUser[index]
} }
index = onlineUsers.findIndex((e:any) => e.userId == user.id)
// 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) { if (index != -1) {
user.statusOnline = onlineUsers[index] user.statusOnline = onlineUsers[index]
} }
if(startDate.length>0 && startDate.split('-').length == 3){ if (startDate.length > 0 && startDate.split('-').length == 3) {
let date = startDate.split('-') let date = startDate.split('-')
user.startDate = `${date[2]}/${date[1]}/${date[0]}` user.startDate = `${date[2]}/${date[1]}/${date[0]}`
} }
if(endDate.length>0 && endDate.split('-').length == 3){ if (endDate.length > 0 && endDate.split('-').length == 3) {
let date = endDate.split('-') let date = endDate.split('-')
user.endDate = `${date[2]}/${date[1]}/${date[0]}` user.endDate = `${date[2]}/${date[1]}/${date[0]}`
} }
@ -124,4 +239,3 @@ export const reportMessagesUserByDateStartDateEnd = async (req: Request, res: Re

View File

@ -21,6 +21,8 @@ import format from 'date-fns/format';
type IndexQuery = { type IndexQuery = {
searchParam: string; searchParam: string;
pageNumber: string; pageNumber: string;
@ -44,8 +46,12 @@ import ListStatusChatEndService from "../services/StatusChatEndService/ListStatu
import Ticket from "../models/Ticket"; import Ticket from "../models/Ticket";
import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport"; import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport";
import TicketEmiterSumOpenClosedByUser from "../helpers/OnlineReporEmiterInfoByUser"; 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<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const { const {
pageNumber, pageNumber,
status, status,
@ -58,7 +64,6 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
} = req.query as IndexQuery; } = req.query as IndexQuery;
const userId = req.user.id; const userId = req.user.id;
let queueIds: number[] = []; let queueIds: number[] = [];
@ -138,6 +143,17 @@ export const show = async (req: Request, res: Response): Promise<Response> => {
}; };
export const count = async (req: Request, res: Response): Promise<Response> => {
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);
};
@ -221,12 +237,15 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
ticketId 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 ticket2 = ticket
} }
// test del
if (userOldInfo) { if (userOldInfo) {
@ -234,12 +253,15 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
if (userOldInfo.userId) { if (userOldInfo.userId) {
// console.log('FECHOU...')
TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate) TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate)
} }
} }
//
return res.status(200).json(ticket2); return res.status(200).json(ticket2);
}; };
@ -248,7 +270,6 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
// export const update = async ( // export const update = async (
// req: Request, // req: Request,
// res: Response // res: Response

View File

@ -6,7 +6,7 @@ module.exports = {
"StatusChatEnds", "StatusChatEnds",
[ [
{ {
name: "SEM RETORNO DO CLIENTE", name: "FINALIZADO",
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date() updatedAt: new Date()
}, },

View File

@ -3,14 +3,64 @@ import { splitDateTime } from "../helpers/SplitDateTime";
import format from 'date-fns/format'; import format from 'date-fns/format';
import ShowUserServiceReport from '../services/UserServices/ShowUserServiceReport'; import ShowUserServiceReport from '../services/UserServices/ShowUserServiceReport';
import { getIO } from "../libs/socket"; 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 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 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(); const io = getIO();
io.emit("onlineStatus", { io.emit("onlineStatus", {
@ -18,9 +68,15 @@ const TicketEmiterSumOpenClosedByUser = async (userId: string, startDate: string
userOnlineTime: { userOnlineTime: {
sumOpen: openByUser.length > 0 ? openByUser[0] : { userId: userId, count: '' }, sumOpen: openByUser.length > 0 ? openByUser[0] : { userId: userId, count: '' },
sumClosed: closedByUser.length > 0 ? closedByUser[0] : { userId: userId, count: '' }, sumClosed: closedByUser.length > 0 ? closedByUser[0] : { userId: userId, count: '' },
openClosedOutQueue: openClosedOutQueue,
openClosedInQueue: queuesByUser
} }
}); });
}; };
export default TicketEmiterSumOpenClosedByUser export default TicketEmiterSumOpenClosedByUser

View File

@ -13,6 +13,8 @@ const fastFolderSize = require('fast-folder-size')
const { promisify } = require('util') const { promisify } = require('util')
const fs = require('fs') const fs = require('fs')
const { exec } = require("child_process");
let scheduler_monitor: any; let scheduler_monitor: any;
let timeInterval = 5 let timeInterval = 5
@ -53,6 +55,35 @@ const monitor = async () => {
} }
exec("df -h /", (error: any, stdout: any, stderr: any) => {
if (error) {
console.log(`exec error: ${error.message}`);
return;
}
if (stderr) {
console.log(`exec stderr: ${stderr}`);
return;
}
stdout = stdout.split(/\r?\n/)
stdout = stdout[1].trim().split(/\s+/)
// DISK SPACE MONITORING
const io = getIO();
io.emit("diskSpaceMonit", {
action: "update",
diskSpace: {
size: stdout[1],
used: stdout[2],
available: stdout[3],
use: stdout[4]
}
});
});
// WHATS SESSION SIZE MONITORING // WHATS SESSION SIZE MONITORING
const whatsapps = await ListWhatsAppsService(); const whatsapps = await ListWhatsAppsService();

View File

@ -67,7 +67,7 @@ export const initWbot = async (whatsapp: Whatsapp, backupSessionRestore: boolean
// usando instancia do chrome // usando instancia do chrome
const wbot: Session = new Client({ const wbot: Session = new Client({
session: sessionCfg, authStrategy: new LocalAuth({ clientId: 'bd_' + whatsapp.id }), session: sessionCfg, authStrategy: new LocalAuth({ clientId: 'bd_' + whatsapp.id }),
puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox'], executablePath: process.env.CHROME_BIN || '/usr/bin/google-chrome-stable' }, puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox'], executablePath: process.env.CHROME_BIN || undefined },
}); });

View File

@ -6,6 +6,8 @@ import * as TicketController from "../controllers/TicketController";
const ticketRoutes = express.Router(); const ticketRoutes = express.Router();
ticketRoutes.get("/tickets/count", isAuth, TicketController.count);
ticketRoutes.get("/tickets", isAuth, TicketController.index); ticketRoutes.get("/tickets", isAuth, TicketController.index);
ticketRoutes.get("/tickets/:ticketId", isAuth, TicketController.show); ticketRoutes.get("/tickets/:ticketId", isAuth, TicketController.show);

View File

@ -16,4 +16,4 @@ StartAllWhatsAppsSessions();
gracefulShutdown(server); gracefulShutdown(server);
startSchedulingMonitor(5000) startSchedulingMonitor(5000)
startWhoIsOnlineMonitor(5000) startWhoIsOnlineMonitor(2000)

View File

@ -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<any> => {
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;

View File

@ -54,6 +54,12 @@ const CreateTicketService = async ({
TicketEmiterSumOpenClosedByUser(userId.toString(), dateToday.fullDate, dateToday.fullDate) TicketEmiterSumOpenClosedByUser(userId.toString(), dateToday.fullDate, dateToday.fullDate)
const io = getIO();
io.emit("ticketStatus", {
action: "update",
ticketStatus: {ticketId: ticket.id, status: ticket.status}
});
// //

View File

@ -39,7 +39,7 @@ const ListTicketsService = async ({
showAll, showAll,
userId, userId,
withUnreadMessages, withUnreadMessages,
unlimited='false' unlimited = 'false'
}: Request): Promise<Response> => { }: Request): Promise<Response> => {
let whereCondition: Filterable["where"] = { let whereCondition: Filterable["where"] = {
[Op.or]: [{ userId }, { status: "pending" }], [Op.or]: [{ userId }, { status: "pending" }],
@ -47,6 +47,8 @@ const ListTicketsService = async ({
}; };
let includeCondition: Includeable[]; let includeCondition: Includeable[];
includeCondition = [ includeCondition = [
{ {
model: Contact, model: Contact,
@ -65,20 +67,21 @@ const ListTicketsService = async ({
} }
if (status) { if (status) {
whereCondition = { ...whereCondition, status };
// console.log('TEST unlimited: ', unlimited)
if (unlimited === 'true' && status !== 'pending') {
whereCondition = { whereCondition = {
...whereCondition, ...whereCondition,
status createdAt: {
}; [Op.gte]: dateToday.fullDate + ' 00:00:00.000000',
[Op.lte]: dateToday.fullDate + ' 23:59:59.999999'
// if (unlimited) { }
// whereCondition = { }
// ...whereCondition, }
// createdAt: {
// [Op.gte]: dateToday.fullDate + ' 00:00:00.000000',
// [Op.lte]: dateToday.fullDate + ' 23:59:59.999999'
// }
// }
// }
} }
@ -92,11 +95,7 @@ const ListTicketsService = async ({
as: "messages", as: "messages",
attributes: ["id", "body"], attributes: ["id", "body"],
where: { where: {
body: where( body: where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`)
fn("LOWER", col("body")),
"LIKE",
`%${sanitizedSearchParam}%`
)
}, },
required: false, required: false,
duplicating: false duplicating: false
@ -107,19 +106,13 @@ const ListTicketsService = async ({
...whereCondition, ...whereCondition,
[Op.or]: [ [Op.or]: [
{ {
"$contact.name$": where( "$contact.name$": where(fn("LOWER", col("contact.name")), "LIKE", `%${sanitizedSearchParam}%`)
fn("LOWER", col("contact.name")),
"LIKE",
`%${sanitizedSearchParam}%`
)
}, },
{ "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } }, { "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } },
{ {
"$message.body$": where( "$message.body$": where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`)
fn("LOWER", col("body")),
"LIKE",
`%${sanitizedSearchParam}%`
)
} }
] ]
}; };

View File

@ -16,12 +16,31 @@ import { startOfDay, endOfDay, parseISO, getDate} from "date-fns";
import { string } from "yup/lib/locale"; import { string } from "yup/lib/locale";
import Whatsapp from "../../models/Whatsapp"; import Whatsapp from "../../models/Whatsapp";
interface Request {
userId: string | number;
startDate: string;
endDate: string;
pageNumber?: string;
}
interface Response {
tickets: Ticket[];
count: number;
hasMore: boolean;
}
//Report by user, startDate, endDate //Report by user, startDate, endDate
const ShowTicketReport = async (id: string | number, startDate: string, endDate: string): Promise<Ticket[]> => { const ShowTicketReport = async ({
userId,
startDate,
endDate,
pageNumber = "1"
}: Request): Promise<Response> => {
let where_clause = {} let where_clause = {}
if(id=='0'){ if(userId=='0'){
where_clause = { where_clause = {
createdAt: { createdAt: {
[Op.gte]: startDate+' 00:00:00.000000', [Op.gte]: startDate+' 00:00:00.000000',
@ -31,7 +50,7 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate:
} }
else{ else{
where_clause = { where_clause = {
userid: id, userid: userId,
createdAt: { createdAt: {
[Op.gte]: startDate+' 00:00:00.000000', [Op.gte]: startDate+' 00:00:00.000000',
[Op.lte]: endDate +' 23:59:59.999999' [Op.lte]: endDate +' 23:59:59.999999'
@ -40,11 +59,14 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate:
} }
const limit = 40;
const offset = limit * (+pageNumber - 1);
const {count, rows: tickets} = await Ticket.findAndCountAll({
const ticket = await Ticket.findAll({
where: where_clause , where: where_clause ,
limit,
offset,
//attributes: ['id', 'status', 'createdAt', 'updatedAt'], //attributes: ['id', 'status', 'createdAt', 'updatedAt'],
attributes: ['id', 'status', 'statusChatEnd', [Sequelize.fn("DATE_FORMAT",Sequelize.col("Ticket.createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"], attributes: ['id', 'status', 'statusChatEnd', [Sequelize.fn("DATE_FORMAT",Sequelize.col("Ticket.createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"],
@ -82,14 +104,20 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate:
}, },
], ],
order: [
['id', 'ASC']
]
}); });
const hasMore = count > offset + tickets.length;
if (!ticket) {
if (!tickets) {
throw new AppError("ERR_NO_TICKET_FOUND", 404); throw new AppError("ERR_NO_TICKET_FOUND", 404);
} }
return ticket; return {tickets, count, hasMore};
}; };
export default ShowTicketReport; export default ShowTicketReport;

View File

@ -61,7 +61,6 @@ const UpdateTicketService = async ({
} }
io.to(ticket.status) io.to(ticket.status)
.to("notification") .to("notification")
.to(ticketId.toString()) .to(ticketId.toString())
@ -70,6 +69,13 @@ const UpdateTicketService = async ({
ticket ticket
}); });
io.emit("ticketStatus", {
action: "update",
ticketStatus: {ticketId: ticket.id, status: ticket.status}
});
return { ticket, oldStatus, oldUserId }; return { ticket, oldStatus, oldUserId };
}; };

View File

@ -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<any[]> => {
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;

View File

@ -118,6 +118,7 @@ const CreateOrUpdateUserOnlineTime = async ({
console.log('UpdatedAt string: ', updatedAtString) console.log('UpdatedAt string: ', updatedAtString)
// //
io.emit("onlineStatus", { io.emit("onlineStatus", {
action: "update", action: "update",
userOnlineTime: { userOnlineTime: {

View File

@ -1,33 +1,35 @@
import { Op, Sequelize } from "sequelize"; import { Op, Sequelize } from "sequelize";
import Queue from "../../models/Queue";
import User from "../../models/User"; import User from "../../models/User";
import UserQueue from "../../models/UserQueue";
interface Request { interface Request {
userId?: string | number; userId?: string | number;
profile?: string; profile?: string;
} }
const ListUser = async ({profile, userId}: Request): Promise<User[]> => { const ListUser = async ({ profile, userId }: Request): Promise<User[]> => {
let where_clause = {} let where_clause = {}
if(userId && profile){ if (userId && profile) {
where_clause = { where_clause = {
[Op.and]: [ [Op.and]: [
{userId: userId}, { userId: userId },
{profile: profile} { profile: profile }
] ]
} }
} }
else if(userId){ else if (userId) {
where_clause = { where_clause = {
[Op.and]: [ [Op.and]: [
{userId: userId}, { userId: userId },
] ]
} }
} }
else if(profile){ else if (profile) {
where_clause = { where_clause = {
profile: profile profile: profile
} }
@ -36,9 +38,25 @@ const ListUser = async ({profile, userId}: Request): Promise<User[]> => {
const users = await User.findAll({ const users = await User.findAll({
where: where_clause, where: where_clause,
raw:true, raw: true,
attributes: ['id', 'name', 'email'], attributes: ['id', 'name', 'email'],
// include: [
// {
// model: UserQueue,
// separate: true,
// attributes: ['id',],
// order: [
// ['createdAt', 'ASC']
// ]
// },
// ],
order: [["id", "ASC"]], order: [["id", "ASC"]],
}) })
@ -51,4 +69,3 @@ export default ListUser;

View File

@ -0,0 +1,40 @@
import { Sequelize, } from "sequelize";
const dbConfig = require("../../config/database");
const sequelize = new Sequelize(dbConfig);
const { QueryTypes } = require('sequelize');
interface Request {
profile: string;
userId?: string | number;
}
const QueuesByUser = async ({ profile, userId }: Request): Promise<any[]> => {
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;

View File

@ -106,6 +106,8 @@ const ShowUserServiceReport = async ({
} }
// console.log('>>>>>>>>>>>>>> objQuery: ', objQuery)
if (!objQuery) { if (!objQuery) {

View File

@ -825,6 +825,7 @@ const handleMsgAck = async (msg: WbotMessage, ack: MessageAck) => {
action: "update", action: "update",
message: messageToUpdate message: messageToUpdate
}); });
} catch (err) { } catch (err) {
Sentry.captureException(err); Sentry.captureException(err);
logger.error(`Error handling message ack. Err: ${err}`); logger.error(`Error handling message ack. Err: ${err}`);

View File

@ -0,0 +1,193 @@
import React from "react";
import Paper from "@material-ui/core/Paper";
import Grid from "@material-ui/core/Grid";
import Typography from "@material-ui/core/Typography";
import Avatar from "@mui/material/Avatar";
import Card from "@mui/material/Card";
import CardHeader from "@mui/material/CardHeader";
import CardContent from "@mui/material/CardContent";
import CardActions from "@mui/material/CardActions";
import { Button } from "@material-ui/core";
import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import CancelIcon from "@material-ui/icons/Cancel";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import ErrorIcon from "@material-ui/icons/Error";
import RemoveCircleIcon from "@material-ui/icons/RemoveCircle";
const CardUser = ({ classes, usersOnlineInfo, logout }) => {
const [search, setSearch] = React.useState("");
const [filterStatus, setFilterStatus] = React.useState(null);
const handleFilterChange = (event) => {
setFilterStatus(event.target.value);
};
const handlesearch = (event) => {
setSearch(event.target.value.toLowerCase());
};
return (
<Grid item xs={12}>
<Paper className={classes.cardPaperFix} sx={12} variant="outlined">
<Grid container sx={12} justifyContent="space-between" alignItems="baseline">
<Grid item sx={4}>
<Typography
component="h4"
variant="h6"
color="primary"
style={{ marginBottom: "16px" }}
>
Lista de Usuários
</Typography>
</Grid>
<Grid item sx={8} width="100%">
<Box sx={{ marginBottom: 2, display: "flex", gap: "12px" }}>
<TextField
id="outlined-basic"
label="Usuário"
variant="standard"
value={search}
onChange={handlesearch}
/>
<FormControl fullWidth variant="standard">
<InputLabel id="status">Status</InputLabel>
<Select
labelId="status"
id="status"
value={filterStatus}
label="Status"
onChange={handleFilterChange}
>
<MenuItem value={null}>Todos</MenuItem>
<MenuItem value={"online"}>Online</MenuItem>
<MenuItem value={"offline"}>Offline</MenuItem>
<MenuItem value={"not"}>Não entrou</MenuItem>
</Select>
</FormControl>
</Box>
</Grid>
</Grid>
<Grid container spacing={3}>
{usersOnlineInfo &&
usersOnlineInfo
.filter((e) => {
if (filterStatus === null) return e;
if (filterStatus === "not") return !e.statusOnline;
return e.statusOnline && e.statusOnline.status === filterStatus;
})
.filter((e) => {
return e.name.toLowerCase().includes(search);
})
.sort((a) => {
if (a.statusOnline) {
if (a.statusOnline.status === "online") {
return -1;
}
return 0;
}
return 0;
})
.map((user, index) => (
<Grid
item
xs={12}
sm={6}
md={6}
lg={3}
key={index}
style={{ position: "relative" }}
>
<Card variant="outlined">
<CardHeader
avatar={
<Avatar
style={{
backgroundColor: user.statusOnline
? user.statusOnline.status === "online"
? "green"
: user.statusOnline.status === "offline"
? "red"
: "black"
: "grey",
}}
>
{user.statusOnline ? (
user.statusOnline.status === "online" ? (
<CheckCircleIcon style={{ color: "white" }} />
) : user.statusOnline.status === "offline" ? (
<CancelIcon style={{ color: "white" }} />
) : (
<ErrorIcon style={{ color: "yellow" }} />
)
) : (
<RemoveCircleIcon style={{ color: "black" }} />
)}
</Avatar>
}
title={
<Typography variant="h5" component="div">
{user.name}
</Typography>
}
/>
<CardContent>
<Typography variant="h6" component="h1" color="textPrimary">
Em atendimento:
<Typography component="p" color="textPrimary" paragraph>
{user.sumOpen && user.sumOpen.count ? user.sumOpen.count : 0}
</Typography>
</Typography>
<Typography variant="h6" component="h1" color="textPrimary">
Finalizado:
<Typography component="p" color="textPrimary" paragraph>
{user.sumClosed && user.sumClosed.count ? user.sumClosed.count : 0}
</Typography>
</Typography>
<Typography variant="h6" component="h1" color="textPrimary">
Tempo online:
<Typography component="p" color="textPrimary" paragraph>
{user.sumOnlineTime && user.sumOnlineTime.sum
? user.sumOnlineTime.sum
: "Não entrou Hoje"}
</Typography>
</Typography>
</CardContent>
<CardActions>
{user.statusOnline &&
user.statusOnline.status === "online" &&
user.statusOnline && (
<Button
className={classes.logginBtn}
variant="contained"
color="primary"
onClick={(e) => {
logout(user.id);
}}
>
{"Deslogar"}
</Button>
)}
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Paper>
</Grid>
);
};
export default CardUser;

View File

@ -0,0 +1,208 @@
import React from "react";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import Typography from "@material-ui/core/Typography";
import Grid from "@material-ui/core/Grid";
import Table from "@material-ui/core/Table";
import TableBody from "@material-ui/core/TableBody";
import TableCell from "@material-ui/core/TableCell";
import TableContainer from "@material-ui/core/TableContainer";
import TableHead from "@material-ui/core/TableHead";
import TableRow from "@material-ui/core/TableRow";
import Paper from "@material-ui/core/Paper";
import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import CancelIcon from "@material-ui/icons/Cancel";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import ErrorIcon from "@material-ui/icons/Error";
import RemoveCircleIcon from "@material-ui/icons/RemoveCircle";
import PowerSettingsNewIcon from "@material-ui/icons/PowerSettingsNew";
const TableUser = ({ classes, usersOnlineInfo, logout }) => {
const [search, setSearch] = React.useState("");
const [filterStatus, setFilterStatus] = React.useState(null);
const handleFilterChange = (event) => {
setFilterStatus(event.target.value);
};
const handlesearch = (event) => {
setSearch(event.target.value.toLowerCase());
};
console.log(usersOnlineInfo);
return (
<Grid item xs={12}>
<Paper className={classes.cardPaperFix} sx={12} variant="outlined">
<Grid container sx={12} justifyContent="space-between" alignItems="baseline">
<Grid item sx={4}>
<Typography
component="h4"
variant="h6"
color="primary"
style={{ marginBottom: "16px" }}
>
Lista de Usuários
</Typography>
</Grid>
<Grid item sx={8} width="100%">
<Box sx={{ marginBottom: 2, display: "flex", gap: "12px" }}>
<TextField
id="outlined-basic"
label="Usuário"
variant="standard"
value={search}
onChange={handlesearch}
/>
<FormControl fullWidth variant="standard">
<InputLabel id="status">Status</InputLabel>
<Select
labelId="status"
id="status"
value={filterStatus}
label="Status"
onChange={handleFilterChange}
>
<MenuItem value={null}>Todos</MenuItem>
<MenuItem value={"online"}>Online</MenuItem>
<MenuItem value={"offline"}>Offline</MenuItem>
<MenuItem value={"not"}>Não entrou</MenuItem>
</Select>
</FormControl>
</Box>
</Grid>
</Grid>
<Grid container spacing={3} style={{ marginTop: "16px", marginBottom: "16px" }}>
<TableContainer component={Paper}>
<Table>
<TableHead>
<TableRow className={classes.tableRowHead}>
<TableCell>Nome</TableCell>
<TableCell>Em Atendimento/Finalizado(s)</TableCell>
<TableCell>Por Fila abertos</TableCell>
<TableCell>Por Fila Fechados</TableCell>
<TableCell>Tempo Online</TableCell>
<TableCell>Ações</TableCell>
</TableRow>
</TableHead>
<TableBody>
{usersOnlineInfo &&
usersOnlineInfo
.filter((e) => {
if (filterStatus === null) return e;
if (filterStatus === "not") return !e.statusOnline;
return e.statusOnline && e.statusOnline.status === filterStatus;
})
.filter((e) => {
return e.name.toLowerCase().includes(search);
})
.sort((a) => {
if (a.statusOnline) {
if (a.statusOnline.status === "online") {
return -1;
}
return 0;
}
return 0;
})
.map((user, index) => (
<TableRow key={index} className={classes.tableRowBody}>
<TableCell>
<Typography
style={{ display: "flex", verticalAlign: "center", gap: "6px" }}
>
{user.statusOnline ? (
user.statusOnline.status === "online" ? (
<CheckCircleIcon style={{ color: "green" }} />
) : user.statusOnline.status === "offline" ? (
<CancelIcon style={{ color: "red" }} />
) : (
<ErrorIcon style={{ color: "gold" }} />
)
) : (
<RemoveCircleIcon style={{ color: "black" }} />
)}
{user.name}
</Typography>
</TableCell>
<TableCell>
<div style={{ display: "flex", alignItems: "center", gap: "12px" }}>
<Typography className={classes.tableCounterOpen}>
{user.sumOpen ? user.sumOpen.count : "0"}
</Typography>
<Typography className={classes.tableCounterClosed}>
{user.sumClosed ? user.sumClosed.count : "0"}
</Typography>
</div>
</TableCell>
<TableCell>
<div
style={{
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: "12px",
}}
>
<Typography title="Cobrança">0</Typography>
<Typography>0</Typography>
<Typography>0</Typography>
<Typography>0</Typography>
<Typography>0</Typography>
</div>
</TableCell>
<TableCell>
<div
style={{
display: "flex",
flexWrap: "wrap",
alignItems: "center",
gap: "12px",
}}
>
<Typography title="Cobrança">0</Typography>
<Typography>0</Typography>
<Typography>0</Typography>
<Typography>0</Typography>
<Typography>0</Typography>
</div>
</TableCell>
<TableCell>
{user.sumOnlineTime ? user.sumOnlineTime.sum : "Não entrou"}
</TableCell>
<TableCell>
{user.statusOnline && user.statusOnline.status === "online" ? (
<PowerSettingsNewIcon
style={{ color: "red", cursor: "pointer" }}
onClick={(e) => {
logout(user.id);
}}
/>
) : (
<PowerSettingsNewIcon
style={{ color: "grey", cursor: "not-allowed" }}
/>
)}
</TableCell>
</TableRow>
))}
</TableBody>
</Table>
</TableContainer>
</Grid>
</Paper>
</Grid>
);
};
export default TableUser;

View File

@ -362,15 +362,17 @@ const MessagesList = ({ ticketId, isGroup }) => {
socket.on("connect", () => socket.emit("joinChatBox", ticketId)); socket.on("connect", () => socket.emit("joinChatBox", ticketId));
socket.on("appMessage", (data) => { socket.on("appMessage", (data) => {
if (data.action === "create") { if (data.action === "create") {
dispatch({ type: "ADD_MESSAGE", payload: data.message }); dispatch({ type: "ADD_MESSAGE", payload: data.message });
// console.log('* NOVA MENSAGEM CAP: ', data.message)
scrollToBottom(); scrollToBottom();
} }
if (data.action === "update") { if (data.action === "update") {
console.log('2 THIS IS THE DATA: ', data)
dispatch({ type: "UPDATE_MESSAGE", payload: data.message }); dispatch({ type: "UPDATE_MESSAGE", payload: data.message });
} }
}); });

View File

@ -4,36 +4,76 @@ import MaterialTable from 'material-table';
import Modal from '../Modal' import Modal from '../Modal'
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import chat from '@material-ui/icons/Chat';
// import Button from "@material-ui/core/Button";
import React from 'react'; import React from 'react';
const MTable = (props) => { const MTable = (props) => {
const [selectedRow, setSelectedRow] = useState(null); const tableRef = React.useRef();
const openInNewTab = url => {
window.open(url, '_blank', 'noopener,noreferrer');
};
const [selectedRow, setSelectedRow] = useState(null);
//const dataLoad = props.data.map((dt) => { return { ...dt }}); //const dataLoad = props.data.map((dt) => { return { ...dt }});
const dataLoad = props.data.map(({ user, ...others }) => ({ ...others, 'user': user ? user : { name: 'Aguardando atendente', email: '' } })); const dataLoad = props.data.map(({ user, ...others }) => ({ ...others, 'user': user ? user : { name: 'Aguardando atendente', email: '' } }));
const columnsLoad = props.columns.map((column) => { return { ...column } }); const columnsLoad = props.columns.map((column) => { return { ...column } });
useEffect(() => { useEffect(() => {
console.log(`You have clicked the button ${selectedRow} times`) console.log(`You have clicked the button ${selectedRow} times`)
// console.log('TABLE REF: ', tableRef)
}, [selectedRow]); }, [selectedRow]);
useEffect(() => {
if (!tableRef.current) return
const element = tableRef.current.tableContainerDiv.current;
element.addEventListener('scroll', props.handleScroll);
return () => {
element.removeEventListener('scroll', props.handleScroll);
};
}, [props]);
return ( return (
<>
{/* <Button onClick={handleTest}>Toggle</Button> */}
{/* <CircularProgress /> */}
<MaterialTable <MaterialTable
title={props.table_title} title={props.table_title}
columns={columnsLoad} columns={columnsLoad}
data={dataLoad} data={dataLoad}
maxWidth={true} maxWidth={true}
tableRef={tableRef}
localization={{ header: { actions: props.hasChild ? 'Ticket' : null },}}
onRowClick={(evt, selectedRow) => { onRowClick={(evt, selectedRow) => {
if(props.removeClickRow){ if (props.removeClickRow) {
return return
} }
@ -56,23 +96,52 @@ const MTable = (props) => {
} }
} }
actions={[
(rowData) => {
if(props.hasChild){
return {
icon: chat,
tooltip: `Ticket id ${rowData.id}`,
disable: false,
onClick: (event, rowData) => {
openInNewTab(`/tickets/${rowData.id}`)
}
}
}
}
]}
options={{ options={{
search: true, search: true,
selection: false, selection: false,
paging: false, paging: false,
padding: 'dense', padding: 'dense',
exportButton: props.hasChild ? true : null,
exportAllData: true,
sorting: true ? props.hasChild : false, sorting: true ? props.hasChild : false,
//loadingType: 'linear', // loadingType: 'circular',
searchFieldStyle: { searchFieldStyle: {
width: 300, width: 300,
}, },
pageSize: 20, pageSize: 20,
headerStyle: { headerStyle: {
position: "sticky", position: "sticky",
top: "0" top: "0"
}, },
maxBodyHeight: "400px", maxBodyHeight: "400px",
// minBodyHeight: "85vh", //FIXME to calculate dynamic height, needed for correct scroll position identification
// maxBodyHeight: "85vh",
rowStyle: rowData => ({ rowStyle: rowData => ({
fontSize: 12, fontSize: 12,
@ -80,6 +149,9 @@ const MTable = (props) => {
}) })
}} }}
/> />
</>
); );
}; };

View File

@ -12,6 +12,7 @@ import useTickets from "../../hooks/useTickets";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
ticketsListWrapper: { ticketsListWrapper: {
position: "relative", position: "relative",
@ -134,8 +135,8 @@ const reducer = (state, action) => {
// console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message) // console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message)
if(!message.fromMe){ if (!message.fromMe) {
ticket.unreadMessages +=1 ticket.unreadMessages += 1
} }
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
@ -195,6 +196,9 @@ const TicketsList = (props) => {
useEffect(() => { useEffect(() => {
if (!status && !searchParam) return; if (!status && !searchParam) return;
// console.log('lllllllllllllllllllllllllllllllll')
dispatch({ dispatch({
type: "LOAD_TICKETS", type: "LOAD_TICKETS",
payload: tickets, payload: tickets,
@ -221,6 +225,7 @@ const TicketsList = (props) => {
}); });
socket.on("ticket", data => { socket.on("ticket", data => {
if (data.action === "updateUnread") { if (data.action === "updateUnread") {
dispatch({ dispatch({
type: "RESET_UNREAD", type: "RESET_UNREAD",
@ -286,6 +291,7 @@ const TicketsList = (props) => {
}; };
const handleScroll = e => { const handleScroll = e => {
if (!hasMore || loading) return; if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;

View File

@ -125,7 +125,6 @@ const TicketsManager = () => {
const handleSearch = (e) => { const handleSearch = (e) => {
const searchedTerm = e.target.value.toLowerCase().trim(); const searchedTerm = e.target.value.toLowerCase().trim();
console.log(searchedTerm)
clearTimeout(searchTimeout); clearTimeout(searchTimeout);

View File

@ -39,6 +39,19 @@ const reducer = (state, action) => {
} }
// if (action.type === "UPDATE_DISK_SPACE_MONIT") {
// const whatsApp = action.payload;
// const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id);
// if (whatsAppIndex !== -1) {
// state[whatsAppIndex].sessionSize = whatsApp.sessionSize;
// return [...state];
// } else {
// return [whatsApp, ...state];
// }
// }
if (action.type === "UPDATE_WHATSAPPS_SESSION_MONIT") { if (action.type === "UPDATE_WHATSAPPS_SESSION_MONIT") {
const whatsApp = action.payload; const whatsApp = action.payload;
const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id);
@ -108,10 +121,6 @@ const useWhatsApps = () => {
} }
}); });
//test del
socket.on("whatsappSessionMonit", data => { socket.on("whatsappSessionMonit", data => {
if (data.action === "update") { if (data.action === "update") {
@ -121,7 +130,7 @@ const useWhatsApps = () => {
} }
}); });
//
return () => { return () => {
socket.disconnect(); socket.disconnect();

View File

@ -1,7 +1,9 @@
import React, { useState, useCallback, useContext } from "react"; import React, { useState, useCallback, useEffect, useContext } from "react";
import { toast } from "react-toastify"; import { toast } from "react-toastify";
import { format, parseISO } from "date-fns"; import { format, parseISO } from "date-fns";
import openSocket from "socket.io-client";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors";
import { import {
@ -111,6 +113,8 @@ const Connections = () => {
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null); const [selectedWhatsApp, setSelectedWhatsApp] = useState(null);
const [confirmModalOpen, setConfirmModalOpen] = useState(false); const [confirmModalOpen, setConfirmModalOpen] = useState(false);
const [diskSpaceInfo, setDiskSpaceInfo] = useState({});
@ -324,6 +328,23 @@ const Connections = () => {
); );
}; };
useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("diskSpaceMonit", data => {
if (data.action === "update") {
setDiskSpaceInfo(data.diskSpace)
}
});
return () => {
socket.disconnect();
};
}, []);
return ( return (
<Can <Can
@ -355,7 +376,10 @@ const Connections = () => {
/> />
<MainHeader> <MainHeader>
<Title>{i18n.t("connections.title")}</Title> <Title>{i18n.t("connections.title")}</Title>
<MainHeaderButtonsWrapper> <MainHeaderButtonsWrapper>
<Can <Can
role={user.profile} role={user.profile}
@ -370,10 +394,51 @@ const Connections = () => {
)} )}
/> />
</MainHeaderButtonsWrapper> </MainHeaderButtonsWrapper>
</MainHeader> </MainHeader>
<Paper className={classes.mainPaper} variant="outlined"> <Paper className={classes.mainPaper} variant="outlined">
<>
<Can
role={user.profile}
perform="space-disk-info:show"
yes={() => (
<>
<Table size="small">
<TableHead>
<TableRow>
<TableCell align="center">
Size
</TableCell>
<TableCell align="center">
Used
</TableCell>
<TableCell align="center">
Available
</TableCell>
<TableCell align="center">
Use%
</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell align="center">{diskSpaceInfo.size}</TableCell>
<TableCell align="center">{diskSpaceInfo.used}</TableCell>
<TableCell align="center">{diskSpaceInfo.available}</TableCell>
<TableCell align="center">{diskSpaceInfo.use}</TableCell>
</TableRow>
</TableBody>
</Table>
<br />
</>
)}
/>
<Table size="small"> <Table size="small">
<TableHead> <TableHead>
<TableRow> <TableRow>
@ -557,6 +622,8 @@ const Connections = () => {
)} )}
</TableBody> </TableBody>
</Table> </Table>
</>
</Paper> </Paper>
</MainContainer> </MainContainer>
)} )}

View File

@ -36,17 +36,23 @@ import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../../components/Can"; import { Can } from "../../components/Can";
const reducer = (state, action) => { const reducer = (state, action) => {
if (action.type === "LOAD_CONTACTS") { if (action.type === "LOAD_CONTACTS") {
const contacts = action.payload; const contacts = action.payload;
const newContacts = []; const newContacts = [];
contacts.forEach((contact) => { contacts.forEach((contact) => {
const contactIndex = state.findIndex((c) => c.id === contact.id); const contactIndex = state.findIndex((c) => c.id === contact.id);
if (contactIndex !== -1) { if (contactIndex !== -1) {
state[contactIndex] = contact; state[contactIndex] = contact;
} else { } else {
newContacts.push(contact); newContacts.push(contact);
} }
}); });
return [...state, ...newContacts]; return [...state, ...newContacts];
@ -210,6 +216,9 @@ const Contacts = () => {
const handleScroll = (e) => { const handleScroll = (e) => {
if (!hasMore || loading) return; if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
// console.log('scrollTop: ', scrollTop, ' | scrollHeight: ', scrollHeight, ' | clientHeight: ', clientHeight)
if (scrollHeight - (scrollTop + 100) < clientHeight) { if (scrollHeight - (scrollTop + 100) < clientHeight) {
loadMore(); loadMore();
} }

View File

@ -16,11 +16,11 @@ import { i18n } from "../../translate/i18n";
import Title from "./Title"; import Title from "./Title";
import useTickets from "../../hooks/useTickets"; import useTickets from "../../hooks/useTickets";
const Chart = () => { const Chart = (props) => {
const theme = useTheme(); const theme = useTheme();
const date = useRef(new Date().toISOString()); const date = useRef(new Date().toISOString());
const { tickets } = useTickets({ date: date.current }); let { tickets } = useTickets({ date: date.current, unlimited: "true" });
const [chartData, setChartData] = useState([ const [chartData, setChartData] = useState([
{ time: "08:00", amount: 0 }, { time: "08:00", amount: 0 },
@ -42,10 +42,7 @@ const Chart = () => {
let aux = [...prevState]; let aux = [...prevState];
aux.forEach(a => { aux.forEach(a => {
tickets.forEach(ticket => { tickets.forEach(ticket => { format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time && a.amount++; });
format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time &&
a.amount++;
});
}); });
return aux; return aux;
@ -54,8 +51,7 @@ const Chart = () => {
return ( return (
<React.Fragment> <React.Fragment>
<Title>{`${i18n.t("dashboard.charts.perDay.title")}${ <Title>{`${i18n.t("dashboard.charts.perDay.title")}${tickets.length
tickets.length
}`}</Title> }`}</Title>
<ResponsiveContainer> <ResponsiveContainer>
<BarChart <BarChart

View File

@ -5,25 +5,10 @@ import Container from "@material-ui/core/Container";
import Grid from "@material-ui/core/Grid"; import Grid from "@material-ui/core/Grid";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import Typography from "@material-ui/core/Typography"; import Typography from "@material-ui/core/Typography";
import Card from "@mui/material/Card";
import CardHeader from "@mui/material/CardHeader";
import CardContent from "@mui/material/CardContent";
import CardActions from "@mui/material/CardActions";
import Avatar from "@mui/material/Avatar";
import Tooltip from "@mui/material/Tooltip"; import Tooltip from "@mui/material/Tooltip";
import Zoom from "@mui/material/Zoom"; import Zoom from "@mui/material/Zoom";
import IconButton from "@mui/material/IconButton"; import IconButton from "@mui/material/IconButton";
import Box from "@mui/material/Box";
import InputLabel from "@mui/material/InputLabel";
import MenuItem from "@mui/material/MenuItem";
import FormControl from "@mui/material/FormControl";
import Select from "@mui/material/Select";
import TextField from "@mui/material/TextField";
import Info from "@material-ui/icons/Info"; import Info from "@material-ui/icons/Info";
import CancelIcon from "@material-ui/icons/Cancel";
import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import ErrorIcon from "@material-ui/icons/Error";
import RemoveCircleIcon from "@material-ui/icons/RemoveCircle";
import useTickets from "../../hooks/useTickets"; import useTickets from "../../hooks/useTickets";
@ -38,9 +23,8 @@ import openSocket from "socket.io-client";
import api from "../../services/api"; import api from "../../services/api";
import { Can } from "../../components/Can"; import { Can } from "../../components/Can";
import CardUser from "../../components/DashboardUser/CardUser";
import { Button } from "@material-ui/core"; import TableUser from "../../components/DashboardUser/TableUser";
import { id } from "date-fns/locale";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
container: { container: {
@ -78,10 +62,10 @@ const useStyles = makeStyles((theme) => ({
}, },
cardPaperFix: { cardPaperFix: {
textTransform: "capitalize", textTransform: "capitalize",
padding: theme.spacing(2), paddingLeft: theme.spacing(4),
paddingBottom: theme.spacing(4), paddingRight: theme.spacing(4),
height: "500px", height: window.innerWidth <= 992 ? "500px" : "auto",
overflowY: "scroll", overflowY: "hidden",
}, },
cardStyleFix: { cardStyleFix: {
display: "flex", display: "flex",
@ -96,6 +80,29 @@ const useStyles = makeStyles((theme) => ({
right: "21px", right: "21px",
fontSize: "12px", fontSize: "12px",
}, },
tableRowHead: {
backgroundColor: "lightgrey",
},
tableRowBody: {
textAlign: "center",
" &:nth-child(even)": {
backgroundColor: "#f7f7f7",
},
},
tableCounterOpen: {
color: "white",
backgroundColor: "green",
width: "25px",
textAlign: "center",
borderRadius: "5px",
},
tableCounterClosed: {
color: "white",
backgroundColor: "red",
width: "25px",
textAlign: "center",
borderRadius: "5px",
},
})); }));
const reducer = (state, action) => { const reducer = (state, action) => {
@ -168,19 +175,30 @@ const reducer = (state, action) => {
if (onlineUser.sumOpen) { if (onlineUser.sumOpen) {
if ("sumOpen" in state[index]) { 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; state[index].sumOpen["count"] = onlineUser.sumOpen.count;
} else if (!("sumOpen" in state[index])) { } else if (!("sumOpen" in state[index])) {
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1')
state[index].sumOpen = onlineUser.sumOpen; state[index].sumOpen = onlineUser.sumOpen;
} }
} }
if (onlineUser.sumClosed) { if (onlineUser.sumClosed) {
if ("sumClosed" in state[index]) { if ("sumClosed" in state[index]) {
// 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; state[index].sumClosed["count"] = onlineUser.sumClosed.count;
} else if (!("sumClosed" in state[index])) { } else if (!("sumClosed" in state[index])) {
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1')
state[index].sumClosed = onlineUser.sumClosed; state[index].sumClosed = onlineUser.sumClosed;
} }
} }
if (onlineUser.openClosedInQueue) {
state[index].openClosedInQueue = onlineUser.openClosedInQueue;
}
if (onlineUser.openClosedOutQueue) {
state[index].openClosedOutQueue = onlineUser.openClosedOutQueue;
}
} }
return [...state]; return [...state];
} }
@ -193,25 +211,12 @@ const reducer = (state, action) => {
const Dashboard = () => { const Dashboard = () => {
const classes = useStyles(); const classes = useStyles();
const [usersOnlineInfo, dispatch] = useReducer(reducer, []); const [usersOnlineInfo, dispatch] = useReducer(reducer, []);
const [search, setSearch] = useState(""); const [ticketStatusChange, setStatus] = useState();
const [filterStatus, setFilterStatus] = useState(null); const [open, setOpen] = useState(0);
const [closed, setClosed] = useState(0);
const [pending, setPending] = useState(0);
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext);
var userQueueIds = [];
if (user.queues && user.queues.length > 0) {
userQueueIds = user.queues.map((q) => q.id);
}
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(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
@ -243,6 +248,8 @@ const Dashboard = () => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
dispatch({ type: "LOAD_QUERY", payload: dataQuery.data }); dispatch({ type: "LOAD_QUERY", payload: dataQuery.data });
console.log("xxx REPORT 2 dataQuery : ", dataQuery.data);
//console.log() //console.log()
} catch (err) { } catch (err) {
console.log(err); console.log(err);
@ -257,8 +264,18 @@ const Dashboard = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("ticketStatus", (data) => {
// console.log('data: ',data)
if (data.action === "update") {
setStatus(data.ticketStatus.status);
}
});
socket.on("onlineStatus", (data) => { socket.on("onlineStatus", (data) => {
if (data.action === "logout" || data.action === "update") { if (data.action === "logout" || data.action === "update") {
// console.log('>>>>>>> data.userOnlineTime: ', data.userOnlineTime)
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }); dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime });
} else if (data.action === "delete") { } else if (data.action === "delete") {
dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime });
@ -277,13 +294,36 @@ const Dashboard = () => {
}; };
}, []); }, []);
const handleFilterChange = (event) => { useEffect(() => {
setFilterStatus(event.target.value); const delayDebounceFn = setTimeout(() => {
}; const fetchQueries = async () => {
const handlesearch = (event) => { try {
setSearch(event.target.value.toLowerCase()); 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);
}, [ticketStatusChange]);
return ( return (
<Can <Can
role={user.profile} role={user.profile}
@ -323,7 +363,7 @@ const Dashboard = () => {
</Typography> </Typography>
<Grid item> <Grid item>
<Typography component="h1" variant="h4"> <Typography component="h1" variant="h4">
{GetTickets("open", "true", "false", "true")} {open}
</Typography> </Typography>
</Grid> </Grid>
</Paper> </Paper>
@ -339,7 +379,7 @@ const Dashboard = () => {
</Typography> </Typography>
<Grid item> <Grid item>
<Typography component="h1" variant="h4"> <Typography component="h1" variant="h4">
{GetTickets("pending", "true", "false", "true")} {pending}
</Typography> </Typography>
</Grid> </Grid>
</Paper> </Paper>
@ -355,7 +395,7 @@ const Dashboard = () => {
</Typography> </Typography>
<Grid item> <Grid item>
<Typography component="h1" variant="h4"> <Typography component="h1" variant="h4">
{GetTickets("closed", "true", "false", "true")} {closed}
</Typography> </Typography>
</Grid> </Grid>
</Paper> </Paper>
@ -446,158 +486,19 @@ const Dashboard = () => {
</Grid> </Grid>
</Paper> </Paper>
</Grid> </Grid>
<Grid item xs={12}> {window.innerWidth <= 992 ? (
<Paper className={classes.cardPaperFix} sx={12} variant="outlined"> <CardUser
<Grid container sx={12} justifyContent="space-between"> classes={classes}
<Grid item sx={4}> usersOnlineInfo={usersOnlineInfo}
<Typography logout={handleLogouOnlineUser}
component="h4"
variant="h6"
color="primary"
style={{ marginBottom: "16px" }}
>
Lista de Usuários
</Typography>
</Grid>
<Grid item sx={8} width="100%">
<Box sx={{ marginBottom: 2, display: "flex", gap: "12px" }}>
<TextField
id="outlined-basic"
label="Usuário"
variant="standard"
value={search}
onChange={handlesearch}
/> />
<FormControl fullWidth variant="standard">
<InputLabel id="status">Status</InputLabel>
<Select
labelId="status"
id="status"
value={filterStatus}
label="Status"
onChange={handleFilterChange}
>
<MenuItem value={null}>Todos</MenuItem>
<MenuItem value={"online"}>Online</MenuItem>
<MenuItem value={"offline"}>Offline</MenuItem>
<MenuItem value={"not"}>Não entrou</MenuItem>
</Select>
</FormControl>
</Box>
</Grid>
</Grid>
<Grid container spacing={3}>
{usersOnlineInfo &&
usersOnlineInfo
.filter((e) => {
if (filterStatus === null) return e;
if (filterStatus === "not") return !e.statusOnline;
return e.statusOnline && e.statusOnline.status === filterStatus;
})
.filter((e) => {
return e.name.toLowerCase().includes(search);
}).sort((a) => {
if (a.statusOnline) {
if (a.statusOnline.status === "online") {
return -1;
}
return 0;
}
return 0;
})
.map((user, index) => (
<Grid
item
xs={12}
sm={6}
md={6}
lg={3}
key={index}
style={{ position: "relative" }}
>
<Card variant="outlined">
<CardHeader
avatar={
<Avatar
style={{
backgroundColor: user.statusOnline
? user.statusOnline.status === "online"
? "green"
: user.statusOnline.status === "offline"
? "red"
: "black"
: "grey",
}}
>
{user.statusOnline ? (
user.statusOnline.status === "online" ? (
<CheckCircleIcon style={{ color: "white" }} />
) : user.statusOnline.status === "offline" ? (
<CancelIcon style={{ color: "white" }} />
) : ( ) : (
<ErrorIcon style={{ color: "yellow" }} /> <TableUser
) classes={classes}
) : ( usersOnlineInfo={usersOnlineInfo}
<RemoveCircleIcon style={{ color: "black" }} /> logout={handleLogouOnlineUser}
)}
</Avatar>
}
title={
<Typography variant="h5" component="div">
{user.name}
</Typography>
}
/> />
<CardContent>
<Typography variant="h6" component="h1" color="textPrimary">
Em atendimento:
<Typography component="p" color="textPrimary" paragraph>
{user.sumOpen && user.sumOpen.count ? user.sumOpen.count : 0}
</Typography>
</Typography>
<Typography variant="h6" component="h1" color="textPrimary">
Finalizado:
<Typography component="p" color="textPrimary" paragraph>
{user.sumClosed && user.sumClosed.count
? user.sumClosed.count
: 0}
</Typography>
</Typography>
<Typography variant="h6" component="h1" color="textPrimary">
Tempo online:
<Typography component="p" color="textPrimary" paragraph>
{user.sumOnlineTime && user.sumOnlineTime.sum
? user.sumOnlineTime.sum
: "Não entrou Hoje"}
</Typography>
</Typography>
</CardContent>
<CardActions>
{user.statusOnline &&
user.statusOnline.status === "online" &&
user.statusOnline && (
<Button
className={classes.logginBtn}
variant="contained"
color="primary"
onClick={(e) => {
handleLogouOnlineUser(user.id);
}}
>
{"Deslogar"}
</Button>
)} )}
</CardActions>
</Card>
</Grid>
))}
</Grid>
</Paper>
</Grid>
</Grid> </Grid>
</Paper> </Paper>
</Grid> </Grid>

View File

@ -18,8 +18,11 @@ import MaterialTable from 'material-table';
import LogoutIcon from '@material-ui/icons/CancelOutlined'; import LogoutIcon from '@material-ui/icons/CancelOutlined';
import { CSVLink } from "react-csv"; import { CSVLink } from "react-csv";
// import CircularProgress from '@mui/material/CircularProgress';
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
@ -117,7 +120,6 @@ const reducerQ = (state, action) => {
if (action.type === 'LOAD_QUERY') { if (action.type === 'LOAD_QUERY') {
const queries = action.payload const queries = action.payload
const newQueries = [] const newQueries = []
@ -301,19 +303,26 @@ let columnsData = [
{ title: 'Criado', field: 'createdAt' }, { title: 'Criado', field: 'createdAt' },
//{title: 'Atualizado', field: 'updatedAt'}, //{title: 'Atualizado', field: 'updatedAt'},
{title: 'Status de encerramento', field: 'statusChatEnd'}]; { title: 'Status de encerramento', field: 'statusChatEnd' }];
const Report = () => { const Report = () => {
const csvLink = useRef() const csvLink = useRef()
const { user: userA } = useContext(AuthContext); const { user: userA } = useContext(AuthContext);
//-------- //--------
const [searchParam] = useState(""); const [searchParam] = useState("");
//const [loading, setLoading] = useState(false);
//const [hasMore, setHasMore] = useState(false); const [loading, setLoading] = useState(false);
const [hasMore, setHasMore] = useState(false);
const [pageNumberTickets, setTicketsPageNumber] = useState(1);
const [totalCountTickets, setTotalCountTickets] = useState(0);
const [pageNumber, setPageNumber] = useState(1); const [pageNumber, setPageNumber] = useState(1);
const [users, dispatch] = useReducer(reducer, []); const [users, dispatch] = useReducer(reducer, []);
//const [columns, setColums] = useState([]) //const [columns, setColums] = useState([])
@ -336,7 +345,7 @@ const Report = () => {
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
dispatchQ({ type: "RESET" }) dispatchQ({ type: "RESET" })
setTicketsPageNumber(1)
setPageNumber(1); setPageNumber(1);
}, [searchParam, profile]); }, [searchParam, profile]);
@ -380,20 +389,24 @@ const Report = () => {
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
setLoading(true);
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
if (reportOption === '1') { if (reportOption === '1') {
const dataQuery = await api.get("/reports/", { params: { userId, startDate, endDate }, }); const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets }, });
dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data });
//setLoading(false); // console.log('dataQuery: ', data)
// console.log('pageNumberTickets: ', pageNumberTickets)
// console.log('dataQuery: ', dataQuery.data) // dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: data.tickets });
setHasMore(data.hasMore);
setTotalCountTickets(data.count)
setLoading(false);
// console.log()
} }
else if (reportOption === '2') { else if (reportOption === '2') {
@ -404,7 +417,7 @@ const Report = () => {
//setLoading(false); //setLoading(false);
// console.log('REPORT 2 dataQuery : ', dataQuery.data) console.log('REPORT 2 dataQuery : ', dataQuery.data)
//console.log() //console.log()
@ -420,7 +433,7 @@ const Report = () => {
}, 500); }, 500);
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn);
}, [userId, startDate, endDate, reportOption]); }, [userId, startDate, endDate, reportOption, pageNumberTickets, totalCountTickets]);
// Get from child 1 // Get from child 1
@ -464,27 +477,15 @@ const Report = () => {
}, [reportOption]) }, [reportOption])
// useEffect(() => {
// console.log('>>>>>>>>>>>>>>>>>> New query: ', query)
// }, [query])
// test del
const handleCSVMessages = () => { const handleCSVMessages = () => {
// setLoading(true);
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
const dataQuery = await api.get("/reports/messages", { params: { userId, startDate, endDate }, }); const dataQuery = await api.get("/reports/messages", { params: { userId, startDate, endDate }, });
// console.log('dataQuery messages: ', dataQuery.data) console.log('dataQuery messages: ', dataQuery.data)
if (dataQuery.data.length > 0) { if (dataQuery.data.length > 0) {
@ -499,15 +500,10 @@ const Report = () => {
} }
} }
// console.log('dataCSVFormat: ', dataCSVFormat)
setDataCSV(dataCSVFormat) setDataCSV(dataCSVFormat)
setIsMount(false); setIsMount(false);
// setDataCSV(dataQuery.data)
} }
// setLoading(false);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -542,16 +538,9 @@ const Report = () => {
socket.on("onlineStatus", (data) => { socket.on("onlineStatus", (data) => {
// setLoading(true);
let date = new Date().toLocaleDateString('pt-BR').split('/') let date = new Date().toLocaleDateString('pt-BR').split('/')
let dateToday = `${date[2]}-${date[1]}-${date[0]}` let dateToday = `${date[2]}-${date[1]}-${date[0]}`
// console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR'))
// console.log('date2: ', startDate)
if (data.action === "logout" || (data.action === "update" && if (data.action === "logout" || (data.action === "update" &&
((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) { ((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) {
@ -564,8 +553,6 @@ const Report = () => {
dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime });
} }
// setLoading(false);
}); });
socket.on("user", (data) => { socket.on("user", (data) => {
@ -581,9 +568,13 @@ const Report = () => {
}; };
} }
else if (reportOption === "1") {
dispatchQ({ type: "RESET" })
setTicketsPageNumber(1)
}
}, [reportOption, startDate, endDate]); }, [reportOption, startDate, endDate, userId]);
// const handleDeleteRows = (id) => { // const handleDeleteRows = (id) => {
@ -610,6 +601,7 @@ const Report = () => {
setData(query.map((column) => { return { ...column } })) setData(query.map((column) => { return { ...column } }))
}, [query]) }, [query])
const handleLogouOnlineUser = async (userId) => { const handleLogouOnlineUser = async (userId) => {
@ -625,6 +617,30 @@ const Report = () => {
}; };
const loadMore = () => {
setTicketsPageNumber((prevState) => prevState + 1);
};
const handleScroll = (e) => {
if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
// console.log('scrollTop: ', scrollTop,
// ' | scrollHeight: ', scrollHeight,
// ' | clientHeight: ', clientHeight,
// ' | scrollHeight - (scrollTop + 1): ', scrollHeight - (scrollTop + 1))
if (scrollHeight - (scrollTop + 1) < clientHeight) {
loadMore();
// e.currentTarget.scrollTo(0, 200);
}
};
return ( return (
<Can <Can
@ -639,7 +655,7 @@ const Report = () => {
return { 'value': obj.id, 'label': obj.name } return { 'value': obj.id, 'label': obj.name }
})} /></Item> })} /></Item>
<Item><DatePicker1 func={datePicker1Value} minDate={true} startEmpty={false} title={'Data inicio'} /></Item> <Item><DatePicker1 func={datePicker1Value} minDate={false} startEmpty={false} title={'Data inicio'} /></Item>
<Item><DatePicker2 func={datePicker2Value} minDate={false} startEmpty={false} title={'Data fim'} /></Item> <Item><DatePicker2 func={datePicker2Value} minDate={false} startEmpty={false} title={'Data fim'} /></Item>
<Item sx={{ display: 'grid', gridColumn: '4 / 5', }}> <Item sx={{ display: 'grid', gridColumn: '4 / 5', }}>
@ -651,13 +667,15 @@ const Report = () => {
{reportOption === '1' && {reportOption === '1' &&
<div> <div>
{/* <Button <Button style={{display: "none"}}
variant="contained" variant="contained"
color="primary" color="primary"
onClick={(e) => handleCSVMessages()} onClick={(e) => {
handleCSVMessages()
}}
> >
{"CSV ALL"} {"CSV ALL"}
</Button> */} </Button>
<div> <div>
<CSVLink <CSVLink
@ -674,10 +692,6 @@ const Report = () => {
</Item> </Item>
</Box> </Box>
<Box sx={{ <Box sx={{
@ -688,11 +702,22 @@ const Report = () => {
{reportOption === '1' && {reportOption === '1' &&
// <div onScroll={handleScroll} style={{ height: 400, overflowY: "scroll" }}>
// </div>
<>
<MTable data={query} <MTable data={query}
columns={columnsData} columns={columnsData}
hasChild={true} hasChild={true}
removeClickRow={false} removeClickRow={false}
handleScroll={handleScroll}
table_title={'Atendimento por atendentes'} /> table_title={'Atendimento por atendentes'} />
</>
} }
{reportOption === '2' && {reportOption === '2' &&
@ -711,7 +736,7 @@ const Report = () => {
[ [
// { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => <img src={rowData['ticket.contact.profilePicUrl']} alt="imagem de perfil do whatsapp" style={{ width: 40, borderRadius: '50%' }} /> }, // { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => <img src={rowData['ticket.contact.profilePicUrl']} alt="imagem de perfil do whatsapp" style={{ width: 40, borderRadius: '50%' }} /> },
{ title: 'Nome', field: 'name', cellStyle: {whiteSpace: 'nowrap'}, }, { title: 'Nome', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ {
title: 'Status', field: 'statusOnline.status', title: 'Status', field: 'statusOnline.status',

View File

@ -21,12 +21,8 @@ import { AuthProvider } from "../context/Auth/AuthContext";
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext"; import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
import Route from "./Route"; import Route from "./Route";
//console.log('---AuthProvider: ',AuthProvider) //console.log('---AuthProvider: ',AuthProvider)
const Routes = () => { const Routes = () => {
return ( return (
<BrowserRouter> <BrowserRouter>
@ -37,41 +33,18 @@ const Routes = () => {
<WhatsAppsProvider> <WhatsAppsProvider>
<LoggedInLayout> <LoggedInLayout>
<Route exact path="/" component={Dashboard} isPrivate /> <Route exact path="/" component={Dashboard} isPrivate />
<Route <Route exact path="/tickets/:ticketId?" component={Tickets} isPrivate />
exact
path="/tickets/:ticketId?"
component={Tickets}
isPrivate
/>
<Route <Route exact path="/connections" component={Connections} isPrivate />
exact
path="/connections"
component={Connections}
isPrivate
/>
<Route
exact
path="/report"
component={Report}
isPrivate
/>
<Route exact path="/report" component={Report} isPrivate />
<Route exact path="/contacts" component={Contacts} isPrivate /> <Route exact path="/contacts" component={Contacts} isPrivate />
<Route exact path="/schedulesReminder" component={SchedulesReminder} isPrivate /> <Route exact path="/schedulesReminder" component={SchedulesReminder} isPrivate />
<Route exact path="/users" component={Users} isPrivate /> <Route exact path="/users" component={Users} isPrivate />
<Route <Route exact path="/quickAnswers" component={QuickAnswers} isPrivate />
exact
path="/quickAnswers"
component={QuickAnswers}
isPrivate
/>
<Route exact path="/Settings" component={Settings} isPrivate /> <Route exact path="/Settings" component={Settings} isPrivate />
<Route exact path="/Queues" component={Queues} isPrivate /> <Route exact path="/Queues" component={Queues} isPrivate />
</LoggedInLayout> </LoggedInLayout>

View File

@ -29,6 +29,7 @@ const rules = {
"show-icon-add-queue", "show-icon-add-queue",
"show-icon-edit-queue", "show-icon-edit-queue",
"show-icon-delete-queue", "show-icon-delete-queue",
"space-disk-info:show",
"drawer-admin-items:view", "drawer-admin-items:view",