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;
console.log(
`startDate: ${startDate} | endDate: ${endDate} | queueId: ${queueId}`
);
const reportService = await ReportByNumberQueueService({
startDate,
endDate,

View File

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

View File

@ -79,41 +79,43 @@ const ReportByNumberQueueService = async ({
{ type: QueryTypes.SELECT }
);
// CHAT AVG WAINTING TIME
// CHAT 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
`
SELECT TIME_FORMAT(
SEC_TO_TIME(
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
)
)
), '%H:%i:%s') AS WAITING_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');`,
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')
ORDER BY
WAITING_TIME;`,
{ type: QueryTypes.SELECT }
);
@ -129,8 +131,8 @@ const ReportByNumberQueueService = async ({
AND w.number = ${number};`,
{ type: QueryTypes.SELECT }
);
);
reportServiceData.push({
id,
name,
@ -138,9 +140,7 @@ const ReportByNumberQueueService = async ({
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,
avgChatWaitingTime: avg(avgChatWaitingTime),
pendingChat: pendingChat[0]?.ticket_count
});
}
@ -205,31 +205,30 @@ const ReportByNumberQueueService = async ({
{ type: QueryTypes.SELECT }
);
// CHAT AVG WAINTING TIME
// CHAT 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
`SELECT TIME_FORMAT(
SEC_TO_TIME(
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
)
)
), '%H:%i:%s') AS WAITING_TIME
FROM Tickets t
JOIN Messages m ON t.id = m.ticketId
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.fromMe = 0
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 }
);
@ -254,7 +255,7 @@ const ReportByNumberQueueService = async ({
AND q.id = ${q.id};`,
{ type: QueryTypes.SELECT }
);
);
reportServiceData.push({
id,
@ -265,9 +266,7 @@ const ReportByNumberQueueService = async ({
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,
avgChatWaitingTime: avg(avgChatWaitingTime),
pendingChat: pendingChat[0]?.ticket_count
});
}
@ -278,3 +277,55 @@ const ReportByNumberQueueService = async ({
};
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') {
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)
console.log(' dataQuery?.data?.reportService: ', dataQuery?.data?.reportService)
dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery?.data?.reportService })
@ -958,48 +956,11 @@ const Report = () => {
<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 iniciadas', field: 'startedByAgent', },
{ title: 'Conversas recebidas', field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' },
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' },
@ -1009,29 +970,6 @@ const Report = () => {
}
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,
@ -1062,49 +1000,23 @@ const Report = () => {
<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: 'Fila', field: 'queueName',
cellStyle: (evt, rowData) => {
return {
whiteSpace: 'nowrap',
backgroundColor: rowData?.queueColor || 'inherit',
color: 'white'
}
}
},
{ title: 'Conversas iniciadas', field: 'startedByAgent', },
{ title: 'Conversas recebidas', field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' },
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' },
@ -1114,29 +1026,6 @@ const Report = () => {
}
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,
@ -1159,6 +1048,11 @@ const Report = () => {
fontSize: 14,
}
// rowStyle: rowData => ({
// backgroundColor: rowData.queueColor || 'inherit',
// fontSize: 14
// })
}}
/>