Compare commits

...

7 Commits

Author SHA1 Message Date
adriano 8447628fbf feat: Create routes and controllers for generating reports by WhatsApp numbers and queues in the backend, and update frontend for report visualization 2024-03-21 18:33:00 -03:00
adriano d298f75efc Merge branch 'el_lojas_melhorias' of github.com:AdrianoRobson/projeto-hit into el_lojas_melhorias 2024-03-21 09:21:01 -03:00
adriano 84896ec09c git commit -m "fix: Correct marking of bot messages from Omnihit application to be ignored upon return. Updated the frontend to chaining the notify data " 2024-03-21 09:16:31 -03:00
gustavo-gsp 9b6e48f54e feat: add notification for tickets in waiting status
Details:
- Implemented notification functionality for tickets that are in waiting status.
2024-03-20 14:33:36 -03:00
gustavo-gsp 82863f9d0e Merge branch 'el_lojas_melhorias' of github.com:AdrianoRobson/projeto-hit into el_lojas_melhorias 2024-03-20 14:17:25 -03:00
gustavo-gsp 18d0420949 error correction in notifications for already opened tickets 2024-03-20 14:08:35 -03:00
adriano 1894fd35ab feat: modify reports page
Details:
- Added a new hidden column called "messages" to the tickets table.
- Implemented search functionality based on ticket messages.
2024-03-20 11:33:30 -03:00
9 changed files with 742 additions and 176 deletions

View File

@ -19,6 +19,7 @@ import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUser
import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
import { getIO } from "../libs/socket";
import { Json } from "sequelize/types/lib/utils";
import ReportByNumberQueueService from "../services/ReportServices/ReportByNumberQueueService";
type IndexQuery = {
userId: string;
@ -48,17 +49,6 @@ export const reportUserByDateStartDateEnd = async (
}
const {
userId,
startDate,
endDate,
pageNumber,
userQueues,
createdOrUpdated,
queueId
} = req.query as IndexQuery;
console.log(
"userId, startDate, endDate, pageNumber, userQueues, createdOrUpdated, queueId: ",
userId,
startDate,
endDate,
@ -66,9 +56,7 @@ export const reportUserByDateStartDateEnd = async (
userQueues,
createdOrUpdated,
queueId
);
// return res.status(200).json({ tickets:[], count:0, hasMore:false, queues:[] });
} = req.query as IndexQuery;
const { tickets, count, hasMore } = await ShowTicketReport({
userId,
@ -302,3 +290,56 @@ export const reportOnQueue = async (
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 });
};

View File

@ -13,6 +13,14 @@ reportRoutes.post("/reports/onqueue", ReportController.reportOnQueue);
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);
export default reportRoutes;

View File

@ -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;

View File

@ -80,7 +80,7 @@ const UpdateTicketService = async ({
if (msg?.trim().length > 0) {
setTimeout(async () => {
sendWhatsAppMessageSocket(ticket, msg);
sendWhatsAppMessageSocket(ticket, `\u200e${msg}`);
}, 2000);
}

View File

@ -392,7 +392,7 @@ const verifyQueue = async (
if (outService.length > 0) {
const { type, msg: msgOutService } = outService[0];
console.log(`${type} message ignored on queue`);
botSendMessage(ticket, msgOutService);
botSendMessage(ticket, `\u200e${msgOutService}`);
return;
}
@ -529,6 +529,7 @@ const transferTicket = async (
sendGreetingMessage?: boolean
) => {
const botInfo = await BotIsOnQueue("botqueue");
const io = getIO();
const queuesWhatsGreetingMessage = await queuesOutBot(
wbot,
@ -548,6 +549,7 @@ const transferTicket = async (
}
if (queue) await botTransferTicket(queue, ticket, sendGreetingMessage);
io.emit('notifyPeding', {data: {ticket, queue}});
};
const botTransferTicket = async (
@ -583,7 +585,7 @@ const botTransferTicketToUser = async (
};
const botSendMessage = (ticket: Ticket, msg: string) => {
const { phoneNumberId } = ticket;
const { phoneNumberId } = ticket;
const debouncedSentMessage = debounce(
async () => {
@ -641,27 +643,10 @@ const handleMessage = async (
let msgContact: any = wbot.msgContact;
// let groupContact: Contact | undefined;
if (msg.fromMe) {
const whatsapp = await whatsappInfo(wbot.id);
if (whatsapp?.number) {
const ticketExpiration = await SettingTicket.findOne({
where: { key: "ticketExpiration", number: whatsapp.number }
});
if (
ticketExpiration &&
ticketExpiration.value == "enabled" &&
ticketExpiration?.message.trim() == msg.body.trim()
) {
console.log("*********** TICKET EXPIRATION");
return;
}
}
if (msg.fromMe) {
// messages sent automatically by wbot have a special character in front of it
// if so, this message was already been stored in database;
// if (/\u200e/.test(msg.body[0])) return;
// if so, this message was already been stored in database;
if (/\u200e/.test(msg.body[0])) return;
// media messages sent from me from cell phone, first comes with "hasMedia = false" and type = "image/ptt/etc"
// in this case, return and let this message be handled by "media_uploaded" event, when it will have "hasMedia = true"
@ -933,25 +918,28 @@ const handleMessage = async (
ticket.status == "pending" &&
ticket.queueId
) {
let choosenQueue = await ShowQueueService(botInfo.botQueueId);
if (botInfo.isOnQueue) {
let choosenQueue = await ShowQueueService(botInfo.botQueueId);
await UpdateTicketService({
ticketData: {
status: "open",
userId: botInfo.userIdBot,
queueId: choosenQueue.id
},
ticketId: ticket.id
});
const menuMsg: any = await menu(msg.body, wbot.id, contact.id);
await botSendMessage(ticket, menuMsg.value);
await UpdateTicketService({
ticketData: {
status: "open",
userId: botInfo.userIdBot,
queueId: choosenQueue.id
},
ticketId: ticket.id
});
const menuMsg: any = await menu(msg.body, wbot.id, contact.id);
await botSendMessage(ticket, menuMsg.value);
}
return;
} else if (
!msg.fromMe &&
msg.body == "#" &&
ticket.status == "pending" &&
ticket.queueId
ticket.queueId &&
botInfo.isOnQueue
) {
let choosenQueue = await ShowQueueService(botInfo.botQueueId);
@ -988,7 +976,7 @@ const handleMessage = async (
if (ticket?.queueId) {
ticketHasQueue = true;
}
if (ticketHasQueue && ticket.status != "open") {
let whatsapp: any = await whatsappInfo(ticket?.whatsappId);
@ -1002,7 +990,7 @@ const handleMessage = async (
return;
}
botSendMessage(ticket, msgOutService);
botSendMessage(ticket, `\u200e${msgOutService}`);
return;
}
}

View File

@ -9,12 +9,12 @@ const ListWhatsAppsNumber = async (
let whatsapps: any = [];
if (whatsapp) {
if (status) {
if (status) {
whatsapps = await Whatsapp.findAll({
raw: true,
where: { number: whatsapp.number, status: status, },
attributes: ["id", "number", "status", "isDefault", "url", 'isOfficial']
});
});
} else {
whatsapps = await Whatsapp.findAll({
raw: true,

View File

@ -38,7 +38,29 @@ const useStyles = makeStyles(theme => ({
noShadow: {
boxShadow: "none !important",
},
}))
}))
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 = () => {
@ -63,7 +85,9 @@ const NotificationsPopOver = () => {
const { handleLogout } = useContext(AuthContext)
// const [lastRef] = useState(+history.location.pathname.split("/")[2])
useEffect(() => {
soundAlertRef.current = play
@ -80,6 +104,9 @@ const NotificationsPopOver = () => {
}, [tickets])
useEffect(() => {
ticketIdRef.current = ticketIdUrl
}, [ticketIdUrl])
@ -103,6 +130,8 @@ const NotificationsPopOver = () => {
if (data.action === "logout") {
if (`${user.id}` === data.userOnlineTime['userId']) {
socket.emit("online", { logoutUserId: user.id })
@ -111,6 +140,7 @@ const NotificationsPopOver = () => {
}
})
// socket.on("isOnline", (data) => {
@ -129,24 +159,21 @@ const NotificationsPopOver = () => {
if (user.profile === 'user') {
// if (_fifo) {
// clearInterval(_fifo);
// }
if (_fifo) {
clearInterval(_fifo)
}
// _fifo = setInterval(() => {
// socket.emit("online", user.id)
// }, 3000);
const intID = setInterval(() => {
console.log('emitting the online')
_fifo = setInterval(() => {
socket.emit("online", user.id)
}, 3000)
return () => clearInterval(intID)
}
return () => {
socket.disconnect()
}
@ -162,7 +189,9 @@ const NotificationsPopOver = () => {
socket.on("ticket", data => {
if (data.action === "updateUnread" || data.action === "delete") {
if (data.action === "updateUnread" || data.action === "delete") {
setNotifications(prevState => {
const ticketIndex = prevState.findIndex(t => t.id === data.ticketId)
@ -187,15 +216,26 @@ const NotificationsPopOver = () => {
}
})
socket.on("appMessage", data => {
socket.on("appMessage", data => {
if (
data.action === "create" &&
!data.message.read &&
(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)
if (ticketIndex !== -1) {
@ -205,88 +245,59 @@ const NotificationsPopOver = () => {
}
return [data.ticket, ...prevState]
})
})
const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") ||
(data.ticket.userId && data.ticket.userId !== user?.id) ||
data.ticket.isGroup || !data.ticket.userId
if (shouldNotNotificate) return
if (shouldNotNotificate) return
handleNotifications(data)
}
})
socket.on('notifyPeding', data =>{
handleNotifications("", data);
});
return () => {
socket.disconnect()
}
}, [user])
const handleNotifications = (data, notify) => {
let isQueue = false;
if(!notify){
const { message, contact, ticket } = data
const handleNotifications = data => {
const { message, contact, ticket } = data
const options = {
body: `${message.body} - ${format(new Date(), "HH:mm")}`,
icon: contact.profilePicUrl,
tag: ticket.id,
renotify: true,
}
const options = {
body: `${message.body} - ${format(new Date(), "HH:mm")}`,
icon: contact.profilePicUrl,
tag: ticket.id,
renotify: true,
}
const notification = new Notification(
`${i18n.t("tickets.notification.message")} ${contact.name}`,
options
const notification = new Notification(
`${i18n.t("tickets.notification.message")} ${contact.name}`,
options
)
notification.onclick = e => {
e.preventDefault()
window.focus()
historyRef.current.push(`/tickets/${ticket.id}`)
}
setDesktopNotifications(prevState => {
const notfiticationIndex = prevState.findIndex(
n => n.tag === notification.tag
)
notification.onclick = e => {
e.preventDefault()
window.focus()
historyRef.current.push(`/tickets/${ticket.id}`)
if (notfiticationIndex !== -1) {
prevState[notfiticationIndex] = notification
return [...prevState]
}
return [notification, ...prevState]
})
setDesktopNotifications(prevState => {
const notfiticationIndex = prevState.findIndex(
n => n.tag === notification.tag
)
if (notfiticationIndex !== -1) {
prevState[notfiticationIndex] = notification
return [...prevState]
}
return [notification, ...prevState]
})
}else{
user.queues.forEach(queue =>{
if(queue.id === notify.data.queue?.id){
isQueue = true;
}
})
}
if(!isQueue && notify){
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()
}

View File

@ -11,32 +11,31 @@ import { AuthContext } from "../../context/Auth/AuthContext"
import { Can } from "../../components/Can"
import FormControlLabel from "@mui/material/FormControlLabel"
import Checkbox from '@mui/material/Checkbox'
import { Button } from "@material-ui/core"
import ReportModal from "../../components/ReportModal"
import ReportModalType from "../../components/ReportModalType"
import MaterialTable from 'material-table'
import LogoutIcon from '@material-ui/icons/CancelOutlined'
import apiBroker from "../../services/apiBroker"
import fileDownload from 'js-file-download'
import openSocket from "socket.io-client"
import { i18n } from "../../translate/i18n"
import Switch from '@mui/material/Switch'
const label = { inputProps: { 'aria-label': 'Size switch demo' } }
const report = [{ 'value': '1', 'label': 'Atendimento por atendentes' }, { 'value': '2', 'label': 'Usuários online/offline' }]
const reportOptType = [{ 'value': '1', 'label': 'Padrão' }, { 'value': '2', 'label': 'Sintético' }, { 'value': '3', 'label': 'Analítico' }]
const report = [
{ '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' }
]
@ -235,7 +234,9 @@ let columnsData = [
{ title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' },
{ title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' },
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }]
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' },
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
]
let columnsDataSuper = [
{ title: `${i18n.t("reports.listColumns.column1_1")}`, field: 'whatsapp.name' },
@ -247,7 +248,8 @@ let columnsDataSuper = [
{ title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' },
{ title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' },
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' },
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
]
@ -300,7 +302,7 @@ const Report = () => {
const [reportTypeList,] = useState(reportOptType)
const [reportType, setReportType] = useState('1')
const [firstLoad, setFirstLoad] = useState(true);
const [firstLoad, setFirstLoad] = useState(true)
useEffect(() => {
@ -312,12 +314,12 @@ const Report = () => {
useEffect(() => {
if (firstLoad) {
if (firstLoad) {
setFirstLoad(false)
} else {
}
}, [firstLoad]);
}, [firstLoad])
useEffect(() => {
//setLoading(true);
@ -352,12 +354,12 @@ const Report = () => {
//setLoading(true);
if (firstLoad) return
const delayDebounceFn = setTimeout(() => {
const delayDebounceFn = setTimeout(() => {
setLoading(true)
const fetchQueries = async () => {
try {
if (reportOption === '1') {
if (reportOption === '1') {
const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets, createdOrUpdated: selectedValue, queueId }, userQueues: userA.queues })
let ticketsQueue = data.tickets
@ -369,8 +371,12 @@ const Report = () => {
filterQueuesTickets = ticketsQueue.filter(ticket => ticket?.queue?.name === userQueues[0]?.name)
}
data.tickets = filterQueuesTickets
dispatchQ({ type: "LOAD_QUERY", payload: data.tickets })
const tickets = data.tickets.map(ticket => ({
...ticket,
messagesToFilter: ticket.messages.map(message => message.body).join(' '),
}))
dispatchQ({ type: "LOAD_QUERY", payload: tickets })
console.log(tickets)
setHasMore(data.hasMore)
setTotalCountTickets(data.count)
setLoading(false)
@ -386,6 +392,22 @@ const Report = () => {
//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) {
console.log(err)
@ -435,7 +457,7 @@ const Report = () => {
setChecked(true)
}
setReport(data)
}
}
// Get from report type option
const reportTypeValue = (data) => {
@ -449,7 +471,7 @@ const Report = () => {
setReportType(data)
}
useEffect(() => {
if (reportOption === '1') {
@ -728,23 +750,26 @@ const Report = () => {
<Box sx={{ display: 'flex', flexDirection: 'column', padding: '10px 0', alignItems: 'center', }}>
{checked ?
<SelectField
func={textFieldSelectUser}
emptyField={true}
header={i18n.t("reports.user")}
currencies={users.map((obj) => {
return { 'value': obj.id, 'label': obj.name }
})} /> :
<SelectField
func={textFieldSelectQueue}
emptyField={true}
header={'Filas'}
currencies={queues.map((obj) => {
return { 'value': obj.id, 'label': obj.name }
})} />
{(reportOption === '1' || reportOption === '2') &&
<>
{checked ?
<SelectField
func={textFieldSelectUser}
emptyField={true}
header={i18n.t("reports.user")}
currencies={users.map((obj) => {
return { 'value': obj.id, 'label': obj.name }
})} /> :
<SelectField
func={textFieldSelectQueue}
emptyField={true}
header={'Filas'}
currencies={queues.map((obj) => {
return { 'value': obj.id, 'label': obj.name }
})} />
}
</>
}
{reportOption === '1' &&
<div>
<label>
@ -760,6 +785,8 @@ const Report = () => {
}
</Box>
<Box sx={{ display: 'flex', justifyContent: 'center', alignItems: 'center' }}>
@ -901,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={
{
search: true,

View File

@ -313,7 +313,9 @@ const messages = {
title0_1: "Lembretes/Agendamentos",
title1_1: "Atendimento por atendentes",
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:{
column0_1: 'Ações',