feat: Improve display of queue and WhatsApp number-based interaction report

feat-scaling-ticket-remote-creation
adriano 2024-03-25 16:00:24 -03:00
parent 8447628fbf
commit 9f040ce953
4 changed files with 132 additions and 190 deletions

View File

@ -331,10 +331,7 @@ export const reportServiceByQueue = async (
const { startDate, endDate, queueId } = req.query as IndexQuery; const { startDate, endDate, queueId } = req.query as IndexQuery;
console.log(
`startDate: ${startDate} | endDate: ${endDate} | queueId: ${queueId}`
);
const reportService = await ReportByNumberQueueService({ const reportService = await ReportByNumberQueueService({
startDate, startDate,
endDate, endDate,

View File

@ -30,7 +30,7 @@ class User extends Model<User> {
name: string; name: string;
@Column @Column
email: string; email: string;
@Column(DataType.VIRTUAL) @Column(DataType.VIRTUAL)
password: string; password: string;

View File

@ -79,41 +79,43 @@ const ReportByNumberQueueService = async ({
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
// CHAT AVG WAINTING TIME // CHAT WAINTING TIME
const avgChatWaitingTime: any = await sequelize.query( const avgChatWaitingTime: any = await sequelize.query(
`SELECT SEC_TO_TIME( `
AVG( SELECT TIME_FORMAT(
TIMESTAMPDIFF( SEC_TO_TIME(
SECOND, TIMESTAMPDIFF(
( SECOND,
SELECT createdAt (
FROM Messages SELECT createdAt
WHERE ticketId = m.ticketId FROM Messages
AND fromMe = 0 WHERE ticketId = m.ticketId
ORDER BY createdAt ASC AND fromMe = 0
LIMIT 1 ORDER BY createdAt ASC
), LIMIT 1
( ),
SELECT createdAt (
FROM Messages SELECT createdAt
WHERE ticketId = m.ticketId FROM Messages
AND fromAgent = 1 WHERE ticketId = m.ticketId
ORDER BY createdAt ASC AND fromAgent = 1
LIMIT 1 ORDER BY createdAt ASC
) LIMIT 1
) )
) )
) AS AVG_AWAITING_TIME ), '%H:%i:%s') AS WAITING_TIME
FROM Tickets t FROM Tickets t
JOIN Messages m ON t.id = m.ticketId JOIN Messages m ON t.id = m.ticketId
JOIN Whatsapps w ON t.whatsappId = w.id JOIN Whatsapps w ON t.whatsappId = w.id
JOIN Queues q ON q.id = t.queueId JOIN Queues q ON q.id = t.queueId
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999' 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.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
AND m.fromMe = 0 AND m.fromMe = 0
-- AND q.id = 2 -- AND q.id = 2
AND w.number = ${number} AND w.number = ${number}
AND (t.status = 'open' OR t.status = 'closed');`, AND (t.status = 'open' OR t.status = 'closed')
ORDER BY
WAITING_TIME;`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
@ -129,8 +131,8 @@ const ReportByNumberQueueService = async ({
AND w.number = ${number};`, AND w.number = ${number};`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
reportServiceData.push({ reportServiceData.push({
id, id,
name, name,
@ -138,9 +140,7 @@ const ReportByNumberQueueService = async ({
startedByAgent: startedByAgent[0]?.ticket_count, startedByAgent: startedByAgent[0]?.ticket_count,
startedByClient: startedByClient[0]?.ticket_count, startedByClient: startedByClient[0]?.ticket_count,
closedChat: closedChat[0]?.ticket_count, closedChat: closedChat[0]?.ticket_count,
avgChatWaitingTime: avgChatWaitingTime[0]?.AVG_AWAITING_TIME avgChatWaitingTime: avg(avgChatWaitingTime),
? avgChatWaitingTime[0]?.AVG_AWAITING_TIME
: 0,
pendingChat: pendingChat[0]?.ticket_count pendingChat: pendingChat[0]?.ticket_count
}); });
} }
@ -205,31 +205,30 @@ const ReportByNumberQueueService = async ({
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
// CHAT AVG WAINTING TIME // CHAT WAINTING TIME
const avgChatWaitingTime: any = await sequelize.query( const avgChatWaitingTime: any = await sequelize.query(
`SELECT SEC_TO_TIME( `SELECT TIME_FORMAT(
AVG( SEC_TO_TIME(
TIMESTAMPDIFF( TIMESTAMPDIFF(
SECOND, SECOND,
( (
SELECT createdAt SELECT createdAt
FROM Messages FROM Messages
WHERE ticketId = m.ticketId WHERE ticketId = m.ticketId
AND fromMe = 0 AND fromMe = 0
ORDER BY createdAt ASC ORDER BY createdAt ASC
LIMIT 1 LIMIT 1
), ),
( (
SELECT createdAt SELECT createdAt
FROM Messages FROM Messages
WHERE ticketId = m.ticketId WHERE ticketId = m.ticketId
AND fromAgent = 1 AND fromAgent = 1
ORDER BY createdAt ASC ORDER BY createdAt ASC
LIMIT 1 LIMIT 1
) )
) )
) ), '%H:%i:%s') AS WAITING_TIME
) AS AVG_AWAITING_TIME
FROM Tickets t FROM Tickets t
JOIN Messages m ON t.id = m.ticketId JOIN Messages m ON t.id = m.ticketId
JOIN Whatsapps w ON t.whatsappId = w.id JOIN Whatsapps w ON t.whatsappId = w.id
@ -238,7 +237,9 @@ const ReportByNumberQueueService = async ({
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id) AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
AND m.fromMe = 0 AND m.fromMe = 0
AND q.id = ${q.id} AND q.id = ${q.id}
AND (t.status = 'open' OR t.status = 'closed');`, AND (t.status = 'open' OR t.status = 'closed')
ORDER BY
WAITING_TIME;`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
@ -254,7 +255,7 @@ const ReportByNumberQueueService = async ({
AND q.id = ${q.id};`, AND q.id = ${q.id};`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
reportServiceData.push({ reportServiceData.push({
id, id,
@ -265,9 +266,7 @@ const ReportByNumberQueueService = async ({
startedByAgent: startedByAgent[0]?.ticket_count, startedByAgent: startedByAgent[0]?.ticket_count,
startedByClient: startedByClient[0]?.ticket_count, startedByClient: startedByClient[0]?.ticket_count,
closedChat: closedChat[0]?.ticket_count, closedChat: closedChat[0]?.ticket_count,
avgChatWaitingTime: avgChatWaitingTime[0]?.AVG_AWAITING_TIME avgChatWaitingTime: avg(avgChatWaitingTime),
? avgChatWaitingTime[0]?.AVG_AWAITING_TIME
: 0,
pendingChat: pendingChat[0]?.ticket_count pendingChat: pendingChat[0]?.ticket_count
}); });
} }
@ -278,3 +277,55 @@ const ReportByNumberQueueService = async ({
}; };
export default ReportByNumberQueueService; export default ReportByNumberQueueService;
function avg(avgChatWaitingTime: any) {
let waitingAVG: any = avgChatWaitingTime
.filter((t: any) => t?.WAITING_TIME)
.map((t: any) => t.WAITING_TIME)
if (waitingAVG.length > 0) {
let midIndex = Math.floor((0 + waitingAVG.length) / 2)
if (waitingAVG.length % 2 == 1) {
waitingAVG = waitingAVG[midIndex]
} else {
waitingAVG = calculateAverageTime(
waitingAVG[midIndex - 1],
waitingAVG[midIndex]
)
}
} else {
waitingAVG = 0
}
return waitingAVG
}
function calculateAverageTime(time1: string, time2: string) {
// Function to parse time string to seconds
function timeStringToSeconds(timeString: string) {
const [hours, minutes, seconds] = timeString.split(":").map(Number);
return hours * 3600 + minutes * 60 + seconds;
}
// Function to convert seconds to time string
function secondsToTimeString(seconds: number) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const remainingSeconds = seconds % 60;
return `${hours.toString().padStart(2, "0")}:${minutes
.toString()
.padStart(2, "0")}:${remainingSeconds.toString().padStart(2, "0")}`;
}
// Convert time strings to seconds
const time1Seconds = timeStringToSeconds(time1);
const time2Seconds = timeStringToSeconds(time2);
// Calculate average seconds
const averageSeconds = Math.round((time1Seconds + time2Seconds) / 2);
// Convert average seconds back to time string
const averageTime = secondsToTimeString(averageSeconds);
return averageTime;
}

View File

@ -395,15 +395,13 @@ const Report = () => {
else if (reportOption === '3') { else if (reportOption === '3') {
const dataQuery = await api.get("/reports/services/numbers", { params: { startDate, endDate }, }) const dataQuery = await api.get("/reports/services/numbers", { params: { startDate, endDate }, })
console.log('DATA QUERY.data numbers: ', dataQuery.data)
dispatchQ({ type: "RESET" }) dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService }) dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService })
} }
else if (reportOption === '4') { else if (reportOption === '4') {
const dataQuery = await api.get("/reports/services/queues", { params: { startDate, endDate }, }) const dataQuery = await api.get("/reports/services/queues", { params: { startDate, endDate }, })
console.log('DATA QUERY.data queues: ', dataQuery.data) console.log(' dataQuery?.data?.reportService: ', dataQuery?.data?.reportService)
dispatchQ({ type: "RESET" }) dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService }) dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService })
@ -958,48 +956,11 @@ const Report = () => {
<MaterialTable <MaterialTable
localization={{
// header: {
// actions: 'Deslogar'
// },
}}
title={i18n.t("reports.listTitles.title4_1")} title={i18n.t("reports.listTitles.title4_1")}
columns={ 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: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ title: 'Conversas iniciadas', field: 'startedByAgent', },
{
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 recebidas', field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' }, { title: `Conversas finalizadas`, field: 'closedChat' },
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' }, { title: `Tempo médio de espera`, field: 'avgChatWaitingTime' },
@ -1009,29 +970,6 @@ const Report = () => {
} }
data={dataRows} 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,
@ -1062,49 +1000,23 @@ const Report = () => {
<MaterialTable <MaterialTable
localization={{
// header: {
// actions: 'Deslogar'
// },
}}
title={i18n.t("reports.listTitles.title5_1")} title={i18n.t("reports.listTitles.title5_1")}
columns={ 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: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ title: 'Fila', field: 'queueName', cellStyle: { whiteSpace: 'nowrap' }, },
{ {
title: 'Conversas iniciadas', field: 'startedByAgent', title: 'Fila', field: 'queueName',
cellStyle: (evt, rowData) => {
// cellStyle: (e, rowData) => { return {
whiteSpace: 'nowrap',
// if (rowData['statusOnline'] && rowData['statusOnline'].status) { backgroundColor: rowData?.queueColor || 'inherit',
color: 'white'
// 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 iniciadas', field: 'startedByAgent', },
{ title: 'Conversas recebidas', field: 'startedByClient' }, { title: 'Conversas recebidas', field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' }, { title: `Conversas finalizadas`, field: 'closedChat' },
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' }, { title: `Tempo médio de espera`, field: 'avgChatWaitingTime' },
@ -1114,29 +1026,6 @@ const Report = () => {
} }
data={dataRows} 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,
@ -1159,6 +1048,11 @@ const Report = () => {
fontSize: 14, fontSize: 14,
} }
// rowStyle: rowData => ({
// backgroundColor: rowData.queueColor || 'inherit',
// fontSize: 14
// })
}} }}
/> />