feat: Create routes and controllers for generating reports by WhatsApp numbers and queues in the backend, and update frontend for report visualization
parent
d298f75efc
commit
8447628fbf
|
@ -19,6 +19,7 @@ import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUser
|
||||||
import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
|
import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
|
||||||
import { getIO } from "../libs/socket";
|
import { getIO } from "../libs/socket";
|
||||||
import { Json } from "sequelize/types/lib/utils";
|
import { Json } from "sequelize/types/lib/utils";
|
||||||
|
import ReportByNumberQueueService from "../services/ReportServices/ReportByNumberQueueService";
|
||||||
|
|
||||||
type IndexQuery = {
|
type IndexQuery = {
|
||||||
userId: string;
|
userId: string;
|
||||||
|
@ -57,19 +58,6 @@ export const reportUserByDateStartDateEnd = async (
|
||||||
queueId
|
queueId
|
||||||
} = req.query as IndexQuery;
|
} = req.query as IndexQuery;
|
||||||
|
|
||||||
console.log(
|
|
||||||
"userId, startDate, endDate, pageNumber, userQueues, createdOrUpdated, queueId: ",
|
|
||||||
userId,
|
|
||||||
startDate,
|
|
||||||
endDate,
|
|
||||||
pageNumber,
|
|
||||||
userQueues,
|
|
||||||
createdOrUpdated,
|
|
||||||
queueId
|
|
||||||
);
|
|
||||||
|
|
||||||
// return res.status(200).json({ tickets:[], count:0, hasMore:false, queues:[] });
|
|
||||||
|
|
||||||
const { tickets, count, hasMore } = await ShowTicketReport({
|
const { tickets, count, hasMore } = await ShowTicketReport({
|
||||||
userId,
|
userId,
|
||||||
startDate,
|
startDate,
|
||||||
|
@ -302,3 +290,56 @@ export const reportOnQueue = async (
|
||||||
|
|
||||||
return res.status(200).json({ message: "ok" });
|
return res.status(200).json({ message: "ok" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const reportService = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
if (
|
||||||
|
req.user.profile !== "master" &&
|
||||||
|
req.user.profile !== "admin" &&
|
||||||
|
req.user.profile !== "supervisor"
|
||||||
|
) {
|
||||||
|
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { startDate, endDate, queueId } = req.query as IndexQuery;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`startDate: ${startDate} | endDate: ${endDate} | queueId: ${queueId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const reportService = await ReportByNumberQueueService({
|
||||||
|
startDate,
|
||||||
|
endDate
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({ reportService });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const reportServiceByQueue = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
if (
|
||||||
|
req.user.profile !== "master" &&
|
||||||
|
req.user.profile !== "admin" &&
|
||||||
|
req.user.profile !== "supervisor"
|
||||||
|
) {
|
||||||
|
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||||
|
}
|
||||||
|
|
||||||
|
const { startDate, endDate, queueId } = req.query as IndexQuery;
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`startDate: ${startDate} | endDate: ${endDate} | queueId: ${queueId}`
|
||||||
|
);
|
||||||
|
|
||||||
|
const reportService = await ReportByNumberQueueService({
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
queue: true
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json({ reportService });
|
||||||
|
};
|
||||||
|
|
|
@ -13,6 +13,14 @@ reportRoutes.post("/reports/onqueue", ReportController.reportOnQueue);
|
||||||
|
|
||||||
reportRoutes.get("/reports/user/services", isAuth, ReportController.reportUserService);
|
reportRoutes.get("/reports/user/services", isAuth, ReportController.reportUserService);
|
||||||
|
|
||||||
|
reportRoutes.get(
|
||||||
|
"/reports/services/numbers",
|
||||||
|
isAuth,
|
||||||
|
ReportController.reportService
|
||||||
|
);
|
||||||
|
|
||||||
|
reportRoutes.get("/reports/services/queues", isAuth, ReportController.reportServiceByQueue);
|
||||||
|
|
||||||
reportRoutes.get("/reports/messages", isAuth, ReportController.reportMessagesUserByDateStartDateEnd);
|
reportRoutes.get("/reports/messages", isAuth, ReportController.reportMessagesUserByDateStartDateEnd);
|
||||||
|
|
||||||
export default reportRoutes;
|
export default reportRoutes;
|
||||||
|
|
|
@ -0,0 +1,280 @@
|
||||||
|
import { Sequelize } from "sequelize";
|
||||||
|
|
||||||
|
const dbConfig = require("../../config/database");
|
||||||
|
const sequelize = new Sequelize(dbConfig);
|
||||||
|
const { QueryTypes } = require("sequelize");
|
||||||
|
|
||||||
|
import { splitDateTime } from "../../helpers/SplitDateTime";
|
||||||
|
import format from "date-fns/format";
|
||||||
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
import Whatsapp from "../../models/Whatsapp";
|
||||||
|
import { number } from "yup";
|
||||||
|
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
startDate: string | number;
|
||||||
|
endDate: string;
|
||||||
|
queue?: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ReportByNumberQueueService = async ({
|
||||||
|
startDate,
|
||||||
|
endDate,
|
||||||
|
queue = false
|
||||||
|
}: Request): Promise<any[]> => {
|
||||||
|
let reportServiceData: any[] = [];
|
||||||
|
|
||||||
|
const whatsapps = await Whatsapp.findAll();
|
||||||
|
|
||||||
|
if (!queue) {
|
||||||
|
for (const whatsapp of whatsapps) {
|
||||||
|
const { id, name, number } = whatsapp;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!number ||
|
||||||
|
reportServiceData.findIndex((w: any) => w?.number == number) != -1
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
console.log("NUMBER: ", number);
|
||||||
|
|
||||||
|
// CHAT STARTED BY AGENT
|
||||||
|
const startedByAgent: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
|
AND m.fromAgent = 1
|
||||||
|
AND w.number = ${number};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT STARTED BY CLIENT
|
||||||
|
const startedByClient: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
|
AND m.fromMe = 0
|
||||||
|
AND w.number = ${number};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT CLOSED
|
||||||
|
const closedChat: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND t.status = 'closed'
|
||||||
|
AND w.number = ${number};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT AVG WAINTING TIME
|
||||||
|
const avgChatWaitingTime: any = await sequelize.query(
|
||||||
|
`SELECT SEC_TO_TIME(
|
||||||
|
AVG(
|
||||||
|
TIMESTAMPDIFF(
|
||||||
|
SECOND,
|
||||||
|
(
|
||||||
|
SELECT createdAt
|
||||||
|
FROM Messages
|
||||||
|
WHERE ticketId = m.ticketId
|
||||||
|
AND fromMe = 0
|
||||||
|
ORDER BY createdAt ASC
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SELECT createdAt
|
||||||
|
FROM Messages
|
||||||
|
WHERE ticketId = m.ticketId
|
||||||
|
AND fromAgent = 1
|
||||||
|
ORDER BY createdAt ASC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) AS AVG_AWAITING_TIME
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
|
AND m.fromMe = 0
|
||||||
|
-- AND q.id = 2
|
||||||
|
AND w.number = ${number}
|
||||||
|
AND (t.status = 'open' OR t.status = 'closed');`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT PENDING
|
||||||
|
const pendingChat: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND t.status = 'pending'
|
||||||
|
AND w.number = ${number};`,
|
||||||
|
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
reportServiceData.push({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
number,
|
||||||
|
startedByAgent: startedByAgent[0]?.ticket_count,
|
||||||
|
startedByClient: startedByClient[0]?.ticket_count,
|
||||||
|
closedChat: closedChat[0]?.ticket_count,
|
||||||
|
avgChatWaitingTime: avgChatWaitingTime[0]?.AVG_AWAITING_TIME
|
||||||
|
? avgChatWaitingTime[0]?.AVG_AWAITING_TIME
|
||||||
|
: 0,
|
||||||
|
pendingChat: pendingChat[0]?.ticket_count
|
||||||
|
});
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
for (const whatsapp of whatsapps) {
|
||||||
|
const { id, name, number } = whatsapp;
|
||||||
|
|
||||||
|
if (
|
||||||
|
!number ||
|
||||||
|
reportServiceData.findIndex((w: any) => w?.number == number) != -1
|
||||||
|
)
|
||||||
|
continue;
|
||||||
|
|
||||||
|
const data = await ShowWhatsAppService(id);
|
||||||
|
|
||||||
|
const queues: any = data.queues.map((q: any) => {
|
||||||
|
const { id, name, color } = q;
|
||||||
|
return { id, name, color };
|
||||||
|
});
|
||||||
|
|
||||||
|
console.log("NUMBER 2: ", number);
|
||||||
|
|
||||||
|
for (const q of queues) {
|
||||||
|
// CHAT STARTED BY AGENT
|
||||||
|
const startedByAgent: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
|
AND m.fromAgent = 1
|
||||||
|
AND q.id = ${q.id};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT STARTED BY CLIENT
|
||||||
|
const startedByClient: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
|
AND m.fromMe = 0
|
||||||
|
AND q.id = ${q.id};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT CLOSED
|
||||||
|
const closedChat: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND t.status = 'closed'
|
||||||
|
AND q.id = ${q.id};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT AVG WAINTING TIME
|
||||||
|
const avgChatWaitingTime: any = await sequelize.query(
|
||||||
|
`SELECT SEC_TO_TIME(
|
||||||
|
AVG(
|
||||||
|
TIMESTAMPDIFF(
|
||||||
|
SECOND,
|
||||||
|
(
|
||||||
|
SELECT createdAt
|
||||||
|
FROM Messages
|
||||||
|
WHERE ticketId = m.ticketId
|
||||||
|
AND fromMe = 0
|
||||||
|
ORDER BY createdAt ASC
|
||||||
|
LIMIT 1
|
||||||
|
),
|
||||||
|
(
|
||||||
|
SELECT createdAt
|
||||||
|
FROM Messages
|
||||||
|
WHERE ticketId = m.ticketId
|
||||||
|
AND fromAgent = 1
|
||||||
|
ORDER BY createdAt ASC
|
||||||
|
LIMIT 1
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
) AS AVG_AWAITING_TIME
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
|
AND m.fromMe = 0
|
||||||
|
AND q.id = ${q.id}
|
||||||
|
AND (t.status = 'open' OR t.status = 'closed');`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
// CHAT PENDING
|
||||||
|
const pendingChat: any = await sequelize.query(
|
||||||
|
`SELECT COUNT(DISTINCT t.id) AS ticket_count
|
||||||
|
FROM Tickets t
|
||||||
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
|
JOIN Queues q ON q.id = t.queueId
|
||||||
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
|
AND t.status = 'pending'
|
||||||
|
AND q.id = ${q.id};`,
|
||||||
|
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
|
reportServiceData.push({
|
||||||
|
id,
|
||||||
|
name,
|
||||||
|
number,
|
||||||
|
queueName: q.name,
|
||||||
|
queueColor: q.color,
|
||||||
|
startedByAgent: startedByAgent[0]?.ticket_count,
|
||||||
|
startedByClient: startedByClient[0]?.ticket_count,
|
||||||
|
closedChat: closedChat[0]?.ticket_count,
|
||||||
|
avgChatWaitingTime: avgChatWaitingTime[0]?.AVG_AWAITING_TIME
|
||||||
|
? avgChatWaitingTime[0]?.AVG_AWAITING_TIME
|
||||||
|
: 0,
|
||||||
|
pendingChat: pendingChat[0]?.ticket_count
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return reportServiceData;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ReportByNumberQueueService;
|
|
@ -41,6 +41,28 @@ const useStyles = makeStyles(theme => ({
|
||||||
}))
|
}))
|
||||||
|
|
||||||
|
|
||||||
|
let _fifo
|
||||||
|
|
||||||
|
// const onlineEmitter = async (socket, user) => {
|
||||||
|
|
||||||
|
// try {
|
||||||
|
// clearInterval(_fifo);
|
||||||
|
|
||||||
|
// socket.emit("online", user.id)
|
||||||
|
|
||||||
|
// } catch (error) {
|
||||||
|
// console.log('error on onlineEmitter: ', error)
|
||||||
|
// }
|
||||||
|
// finally {
|
||||||
|
// _fifo = setInterval(onlineEmitter, 3000);
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
// _fifo = setInterval(onlineEmitter, 3000);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const NotificationsPopOver = () => {
|
const NotificationsPopOver = () => {
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
|
|
||||||
|
@ -65,6 +87,8 @@ const NotificationsPopOver = () => {
|
||||||
// const [lastRef] = useState(+history.location.pathname.split("/")[2])
|
// const [lastRef] = useState(+history.location.pathname.split("/")[2])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
soundAlertRef.current = play
|
soundAlertRef.current = play
|
||||||
|
|
||||||
|
@ -80,6 +104,9 @@ const NotificationsPopOver = () => {
|
||||||
}, [tickets])
|
}, [tickets])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
ticketIdRef.current = ticketIdUrl
|
ticketIdRef.current = ticketIdUrl
|
||||||
}, [ticketIdUrl])
|
}, [ticketIdUrl])
|
||||||
|
|
||||||
|
@ -103,6 +130,8 @@ const NotificationsPopOver = () => {
|
||||||
|
|
||||||
if (data.action === "logout") {
|
if (data.action === "logout") {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (`${user.id}` === data.userOnlineTime['userId']) {
|
if (`${user.id}` === data.userOnlineTime['userId']) {
|
||||||
|
|
||||||
socket.emit("online", { logoutUserId: user.id })
|
socket.emit("online", { logoutUserId: user.id })
|
||||||
|
@ -111,6 +140,7 @@ const NotificationsPopOver = () => {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
// socket.on("isOnline", (data) => {
|
// socket.on("isOnline", (data) => {
|
||||||
|
@ -129,24 +159,21 @@ const NotificationsPopOver = () => {
|
||||||
|
|
||||||
if (user.profile === 'user') {
|
if (user.profile === 'user') {
|
||||||
|
|
||||||
// if (_fifo) {
|
if (_fifo) {
|
||||||
// clearInterval(_fifo);
|
clearInterval(_fifo)
|
||||||
// }
|
}
|
||||||
|
|
||||||
// _fifo = setInterval(() => {
|
_fifo = setInterval(() => {
|
||||||
// socket.emit("online", user.id)
|
|
||||||
// }, 3000);
|
|
||||||
|
|
||||||
const intID = setInterval(() => {
|
|
||||||
console.log('emitting the online')
|
|
||||||
socket.emit("online", user.id)
|
socket.emit("online", user.id)
|
||||||
}, 3000)
|
}, 3000)
|
||||||
|
|
||||||
return () => clearInterval(intID)
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.disconnect()
|
socket.disconnect()
|
||||||
}
|
}
|
||||||
|
@ -164,6 +191,8 @@ const NotificationsPopOver = () => {
|
||||||
socket.on("ticket", data => {
|
socket.on("ticket", data => {
|
||||||
if (data.action === "updateUnread" || data.action === "delete") {
|
if (data.action === "updateUnread" || data.action === "delete") {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setNotifications(prevState => {
|
setNotifications(prevState => {
|
||||||
const ticketIndex = prevState.findIndex(t => t.id === data.ticketId)
|
const ticketIndex = prevState.findIndex(t => t.id === data.ticketId)
|
||||||
if (ticketIndex !== -1) {
|
if (ticketIndex !== -1) {
|
||||||
|
@ -189,14 +218,25 @@ const NotificationsPopOver = () => {
|
||||||
|
|
||||||
socket.on("appMessage", data => {
|
socket.on("appMessage", data => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (
|
if (
|
||||||
data.action === "create" &&
|
data.action === "create" &&
|
||||||
!data.message.read &&
|
!data.message.read &&
|
||||||
(data.ticket.userId === user?.id || !data.ticket.userId)
|
(data.ticket.userId === user?.id || !data.ticket.userId)
|
||||||
) {
|
) {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setNotifications(prevState => {
|
setNotifications(prevState => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// prevState.forEach((e)=>{
|
||||||
|
//
|
||||||
|
// })
|
||||||
|
|
||||||
const ticketIndex = prevState.findIndex(t => t.id === data.ticket.id)
|
const ticketIndex = prevState.findIndex(t => t.id === data.ticket.id)
|
||||||
if (ticketIndex !== -1) {
|
if (ticketIndex !== -1) {
|
||||||
|
|
||||||
|
@ -207,28 +247,26 @@ const NotificationsPopOver = () => {
|
||||||
return [data.ticket, ...prevState]
|
return [data.ticket, ...prevState]
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") ||
|
const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") ||
|
||||||
(data.ticket.userId && data.ticket.userId !== user?.id) ||
|
(data.ticket.userId && data.ticket.userId !== user?.id) ||
|
||||||
data.ticket.isGroup || !data.ticket.userId
|
data.ticket.isGroup || !data.ticket.userId
|
||||||
|
|
||||||
if (shouldNotNotificate) return
|
if (shouldNotNotificate) return
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleNotifications(data)
|
handleNotifications(data)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on('notifyPeding', data =>{
|
|
||||||
handleNotifications("", data);
|
|
||||||
});
|
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.disconnect()
|
socket.disconnect()
|
||||||
}
|
}
|
||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
const handleNotifications = (data, notify) => {
|
const handleNotifications = data => {
|
||||||
let isQueue = false;
|
|
||||||
if(!notify){
|
|
||||||
const { message, contact, ticket } = data
|
const { message, contact, ticket } = data
|
||||||
|
|
||||||
const options = {
|
const options = {
|
||||||
|
@ -259,34 +297,7 @@ const NotificationsPopOver = () => {
|
||||||
}
|
}
|
||||||
return [notification, ...prevState]
|
return [notification, ...prevState]
|
||||||
})
|
})
|
||||||
}else{
|
|
||||||
user.queues.forEach(queue =>{
|
|
||||||
if(queue.id === notify.data?.queue?.id){
|
|
||||||
isQueue = true;
|
|
||||||
}
|
|
||||||
})
|
|
||||||
if(!isQueue){
|
|
||||||
return;
|
|
||||||
}else {
|
|
||||||
const notification = new Notification(`${i18n.t("tickets.notification.messagePeding")} ${notify.data?.queue?.name}`);
|
|
||||||
notification.onclick = e => {
|
|
||||||
e.preventDefault()
|
|
||||||
window.focus()
|
|
||||||
historyRef.current.push(`/tickets`)
|
|
||||||
}
|
|
||||||
|
|
||||||
setDesktopNotifications(prevState => {
|
|
||||||
const notfiticationIndex = prevState.findIndex(
|
|
||||||
n => n.tag === notification.tag
|
|
||||||
)
|
|
||||||
if (notfiticationIndex !== -1) {
|
|
||||||
prevState[notfiticationIndex] = notification
|
|
||||||
return [...prevState]
|
|
||||||
}
|
|
||||||
return [notification, ...prevState]
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
soundAlertRef.current()
|
soundAlertRef.current()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -11,32 +11,31 @@ import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
import { Can } from "../../components/Can"
|
import { Can } from "../../components/Can"
|
||||||
import FormControlLabel from "@mui/material/FormControlLabel"
|
import FormControlLabel from "@mui/material/FormControlLabel"
|
||||||
import Checkbox from '@mui/material/Checkbox'
|
import Checkbox from '@mui/material/Checkbox'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
import { Button } from "@material-ui/core"
|
import { Button } from "@material-ui/core"
|
||||||
|
|
||||||
import ReportModal from "../../components/ReportModal"
|
import ReportModal from "../../components/ReportModal"
|
||||||
import ReportModalType from "../../components/ReportModalType"
|
import ReportModalType from "../../components/ReportModalType"
|
||||||
|
|
||||||
import MaterialTable from 'material-table'
|
import MaterialTable from 'material-table'
|
||||||
|
|
||||||
import LogoutIcon from '@material-ui/icons/CancelOutlined'
|
import LogoutIcon from '@material-ui/icons/CancelOutlined'
|
||||||
|
|
||||||
import apiBroker from "../../services/apiBroker"
|
import apiBroker from "../../services/apiBroker"
|
||||||
import fileDownload from 'js-file-download'
|
import fileDownload from 'js-file-download'
|
||||||
|
|
||||||
|
|
||||||
import openSocket from "socket.io-client"
|
import openSocket from "socket.io-client"
|
||||||
|
|
||||||
import { i18n } from "../../translate/i18n"
|
import { i18n } from "../../translate/i18n"
|
||||||
|
|
||||||
import Switch from '@mui/material/Switch'
|
import Switch from '@mui/material/Switch'
|
||||||
|
|
||||||
const label = { inputProps: { 'aria-label': 'Size switch demo' } }
|
const label = { inputProps: { 'aria-label': 'Size switch demo' } }
|
||||||
|
|
||||||
const report = [{ 'value': '1', 'label': 'Atendimento por atendentes' }, { 'value': '2', 'label': 'Usuários online/offline' }]
|
const report = [
|
||||||
const reportOptType = [{ 'value': '1', 'label': 'Padrão' }, { 'value': '2', 'label': 'Sintético' }, { 'value': '3', 'label': 'Analítico' }]
|
{ 'value': '1', 'label': 'Atendimento por atendentes' },
|
||||||
|
{ 'value': '2', 'label': 'Usuários online/offline' },
|
||||||
|
{ 'value': '3', 'label': 'Relatorio de atendimento por numeros' },
|
||||||
|
{ 'value': '4', 'label': 'Relatorio de atendimento por filas' },
|
||||||
|
]
|
||||||
|
|
||||||
|
const reportOptType = [
|
||||||
|
{ 'value': '1', 'label': 'Padrão' },
|
||||||
|
{ 'value': '2', 'label': 'Sintético' },
|
||||||
|
{ 'value': '3', 'label': 'Analítico' }
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -303,7 +302,7 @@ const Report = () => {
|
||||||
|
|
||||||
const [reportTypeList,] = useState(reportOptType)
|
const [reportTypeList,] = useState(reportOptType)
|
||||||
const [reportType, setReportType] = useState('1')
|
const [reportType, setReportType] = useState('1')
|
||||||
const [firstLoad, setFirstLoad] = useState(true);
|
const [firstLoad, setFirstLoad] = useState(true)
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -320,7 +319,7 @@ const Report = () => {
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
}
|
}
|
||||||
}, [firstLoad]);
|
}, [firstLoad])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
//setLoading(true);
|
//setLoading(true);
|
||||||
|
@ -375,9 +374,9 @@ const Report = () => {
|
||||||
const tickets = data.tickets.map(ticket => ({
|
const tickets = data.tickets.map(ticket => ({
|
||||||
...ticket,
|
...ticket,
|
||||||
messagesToFilter: ticket.messages.map(message => message.body).join(' '),
|
messagesToFilter: ticket.messages.map(message => message.body).join(' '),
|
||||||
}));
|
}))
|
||||||
dispatchQ({ type: "LOAD_QUERY", payload: tickets })
|
dispatchQ({ type: "LOAD_QUERY", payload: tickets })
|
||||||
console.log(tickets);
|
console.log(tickets)
|
||||||
setHasMore(data.hasMore)
|
setHasMore(data.hasMore)
|
||||||
setTotalCountTickets(data.count)
|
setTotalCountTickets(data.count)
|
||||||
setLoading(false)
|
setLoading(false)
|
||||||
|
@ -393,6 +392,22 @@ const Report = () => {
|
||||||
//setLoading(false);
|
//setLoading(false);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else if (reportOption === '3') {
|
||||||
|
const dataQuery = await api.get("/reports/services/numbers", { params: { startDate, endDate }, })
|
||||||
|
|
||||||
|
console.log('DATA QUERY.data numbers: ', dataQuery.data)
|
||||||
|
|
||||||
|
dispatchQ({ type: "RESET" })
|
||||||
|
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService })
|
||||||
|
}
|
||||||
|
else if (reportOption === '4') {
|
||||||
|
const dataQuery = await api.get("/reports/services/queues", { params: { startDate, endDate }, })
|
||||||
|
|
||||||
|
console.log('DATA QUERY.data queues: ', dataQuery.data)
|
||||||
|
|
||||||
|
dispatchQ({ type: "RESET" })
|
||||||
|
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService })
|
||||||
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err)
|
console.log(err)
|
||||||
|
@ -735,6 +750,8 @@ const Report = () => {
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', flexDirection: 'column', padding: '10px 0', alignItems: 'center', }}>
|
<Box sx={{ display: 'flex', flexDirection: 'column', padding: '10px 0', alignItems: 'center', }}>
|
||||||
|
|
||||||
|
{(reportOption === '1' || reportOption === '2') &&
|
||||||
|
<>
|
||||||
{checked ?
|
{checked ?
|
||||||
<SelectField
|
<SelectField
|
||||||
func={textFieldSelectUser}
|
func={textFieldSelectUser}
|
||||||
|
@ -751,7 +768,8 @@ const Report = () => {
|
||||||
return { 'value': obj.id, 'label': obj.name }
|
return { 'value': obj.id, 'label': obj.name }
|
||||||
})} />
|
})} />
|
||||||
}
|
}
|
||||||
|
</>
|
||||||
|
}
|
||||||
{reportOption === '1' &&
|
{reportOption === '1' &&
|
||||||
<div>
|
<div>
|
||||||
<label>
|
<label>
|
||||||
|
@ -767,6 +785,8 @@ const Report = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</Box>
|
</Box>
|
||||||
|
|
||||||
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
|
||||||
|
@ -908,6 +928,215 @@ const Report = () => {
|
||||||
]}
|
]}
|
||||||
|
|
||||||
|
|
||||||
|
options={
|
||||||
|
{
|
||||||
|
search: true,
|
||||||
|
selection: false,
|
||||||
|
paging: false,
|
||||||
|
padding: 'dense',
|
||||||
|
sorting: true,
|
||||||
|
searchFieldStyle: {
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
|
||||||
|
pageSize: 20,
|
||||||
|
headerStyle: {
|
||||||
|
position: "sticky",
|
||||||
|
top: "0"
|
||||||
|
},
|
||||||
|
maxBodyHeight: "400px",
|
||||||
|
|
||||||
|
rowStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
}
|
||||||
|
{reportOption === '3' &&
|
||||||
|
|
||||||
|
<MaterialTable
|
||||||
|
|
||||||
|
localization={{
|
||||||
|
|
||||||
|
// header: {
|
||||||
|
// actions: 'Deslogar'
|
||||||
|
// },
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
title={i18n.t("reports.listTitles.title4_1")}
|
||||||
|
columns={
|
||||||
|
[
|
||||||
|
|
||||||
|
// { 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: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
|
||||||
|
|
||||||
|
{
|
||||||
|
title: 'Conversas iniciadas', field: 'startedByAgent',
|
||||||
|
|
||||||
|
// cellStyle: (e, rowData) => {
|
||||||
|
|
||||||
|
// if (rowData['statusOnline'] && rowData['statusOnline'].status) {
|
||||||
|
|
||||||
|
// if (rowData['statusOnline'].status === 'offline') {
|
||||||
|
|
||||||
|
// return { color: "red" }
|
||||||
|
// }
|
||||||
|
// else if (rowData['statusOnline'].status === 'online') {
|
||||||
|
// return { color: "green" }
|
||||||
|
// }
|
||||||
|
// else if (rowData['statusOnline'].status === 'logout...') {
|
||||||
|
// return { color: "orange" }
|
||||||
|
// }
|
||||||
|
// else if (rowData['statusOnline'].status === 'waiting...') {
|
||||||
|
// return { color: "orange" }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// },
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{ title: 'Conversas recebidas', field: 'startedByClient' },
|
||||||
|
{ title: `Conversas finalizadas`, field: 'closedChat' },
|
||||||
|
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' },
|
||||||
|
{ title: 'Aguardando', field: 'pendingChat' }
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
data={dataRows}
|
||||||
|
|
||||||
|
// actions={[
|
||||||
|
// (rowData) => {
|
||||||
|
|
||||||
|
// if (rowData.statusOnline &&
|
||||||
|
// rowData.statusOnline['status'] &&
|
||||||
|
// rowData.statusOnline['status'] === 'online') {
|
||||||
|
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// icon: LogoutIcon,
|
||||||
|
// tooltip: 'deslogar',
|
||||||
|
// disable: false,
|
||||||
|
// onClick: (event, rowData) => {
|
||||||
|
// handleLogouOnlineUser(rowData.id)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]}
|
||||||
|
|
||||||
|
|
||||||
|
options={
|
||||||
|
{
|
||||||
|
search: true,
|
||||||
|
selection: false,
|
||||||
|
paging: false,
|
||||||
|
padding: 'dense',
|
||||||
|
sorting: true,
|
||||||
|
searchFieldStyle: {
|
||||||
|
width: 300,
|
||||||
|
},
|
||||||
|
|
||||||
|
pageSize: 20,
|
||||||
|
headerStyle: {
|
||||||
|
position: "sticky",
|
||||||
|
top: "0"
|
||||||
|
},
|
||||||
|
maxBodyHeight: "400px",
|
||||||
|
|
||||||
|
rowStyle: {
|
||||||
|
fontSize: 14,
|
||||||
|
}
|
||||||
|
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
|
||||||
|
}
|
||||||
|
{reportOption === '4' &&
|
||||||
|
|
||||||
|
<MaterialTable
|
||||||
|
|
||||||
|
localization={{
|
||||||
|
|
||||||
|
// header: {
|
||||||
|
// actions: 'Deslogar'
|
||||||
|
// },
|
||||||
|
|
||||||
|
}}
|
||||||
|
|
||||||
|
title={i18n.t("reports.listTitles.title5_1")}
|
||||||
|
columns={
|
||||||
|
[
|
||||||
|
|
||||||
|
// { 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: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
|
||||||
|
{ title: 'Fila', field: 'queueName', cellStyle: { whiteSpace: 'nowrap' }, },
|
||||||
|
{
|
||||||
|
title: 'Conversas iniciadas', field: 'startedByAgent',
|
||||||
|
|
||||||
|
// cellStyle: (e, rowData) => {
|
||||||
|
|
||||||
|
// if (rowData['statusOnline'] && rowData['statusOnline'].status) {
|
||||||
|
|
||||||
|
// if (rowData['statusOnline'].status === 'offline') {
|
||||||
|
|
||||||
|
// return { color: "red" }
|
||||||
|
// }
|
||||||
|
// else if (rowData['statusOnline'].status === 'online') {
|
||||||
|
// return { color: "green" }
|
||||||
|
// }
|
||||||
|
// else if (rowData['statusOnline'].status === 'logout...') {
|
||||||
|
// return { color: "orange" }
|
||||||
|
// }
|
||||||
|
// else if (rowData['statusOnline'].status === 'waiting...') {
|
||||||
|
// return { color: "orange" }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// },
|
||||||
|
|
||||||
|
},
|
||||||
|
|
||||||
|
{ title: 'Conversas recebidas', field: 'startedByClient' },
|
||||||
|
{ title: `Conversas finalizadas`, field: 'closedChat' },
|
||||||
|
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' },
|
||||||
|
{ title: 'Aguardando', field: 'pendingChat' }
|
||||||
|
|
||||||
|
]
|
||||||
|
}
|
||||||
|
data={dataRows}
|
||||||
|
|
||||||
|
// actions={[
|
||||||
|
// (rowData) => {
|
||||||
|
|
||||||
|
// if (rowData.statusOnline &&
|
||||||
|
// rowData.statusOnline['status'] &&
|
||||||
|
// rowData.statusOnline['status'] === 'online') {
|
||||||
|
|
||||||
|
|
||||||
|
// return {
|
||||||
|
// icon: LogoutIcon,
|
||||||
|
// tooltip: 'deslogar',
|
||||||
|
// disable: false,
|
||||||
|
// onClick: (event, rowData) => {
|
||||||
|
// handleLogouOnlineUser(rowData.id)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
|
||||||
|
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// ]}
|
||||||
|
|
||||||
|
|
||||||
options={
|
options={
|
||||||
{
|
{
|
||||||
search: true,
|
search: true,
|
||||||
|
|
|
@ -313,7 +313,9 @@ const messages = {
|
||||||
title0_1: "Lembretes/Agendamentos",
|
title0_1: "Lembretes/Agendamentos",
|
||||||
title1_1: "Atendimento por atendentes",
|
title1_1: "Atendimento por atendentes",
|
||||||
title2_1: "Chat do atendimento pelo Whatsapp",
|
title2_1: "Chat do atendimento pelo Whatsapp",
|
||||||
title3_1: "Usuários online/offline"
|
title3_1: "Usuários online/offline",
|
||||||
|
title4_1: "Relatório de atendimento por números",
|
||||||
|
title5_1: "Relatório de atendimento por filas"
|
||||||
},
|
},
|
||||||
listColumns:{
|
listColumns:{
|
||||||
column0_1: 'Ações',
|
column0_1: 'Ações',
|
||||||
|
|
Loading…
Reference in New Issue