feat: Modify backend and frontend to display waiting time for customer service in report and update report controller queries accordingly
parent
9f040ce953
commit
dc5a6945d2
|
@ -20,6 +20,7 @@ import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
|
|||
import { getIO } from "../libs/socket";
|
||||
import { Json } from "sequelize/types/lib/utils";
|
||||
import ReportByNumberQueueService from "../services/ReportServices/ReportByNumberQueueService";
|
||||
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService";
|
||||
|
||||
type IndexQuery = {
|
||||
userId: string;
|
||||
|
@ -312,7 +313,7 @@ export const reportService = async (
|
|||
const reportService = await ReportByNumberQueueService({
|
||||
startDate,
|
||||
endDate
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json({ reportService });
|
||||
};
|
||||
|
@ -331,12 +332,37 @@ export const reportServiceByQueue = async (
|
|||
|
||||
const { startDate, endDate, queueId } = req.query as IndexQuery;
|
||||
|
||||
|
||||
const reportService = await ReportByNumberQueueService({
|
||||
startDate,
|
||||
endDate,
|
||||
queue: true
|
||||
});
|
||||
});
|
||||
|
||||
return res.status(200).json({ reportService });
|
||||
};
|
||||
|
||||
export const reportTicksCountByStatusChatEnds = 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 } = req.query as IndexQuery;
|
||||
|
||||
const dateToday = splitDateTime(
|
||||
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
|
||||
);
|
||||
|
||||
const reportStatusChatEnd = await CountStatusChatEndService(
|
||||
startDate || dateToday.fullDate,
|
||||
endDate || dateToday.fullDate
|
||||
);
|
||||
|
||||
return res.status(200).json({ reportStatusChatEnd });
|
||||
};
|
||||
|
|
|
@ -76,6 +76,7 @@ import CreateContactService from "../services/ContactServices/CreateContactServi
|
|||
import { botSendMessage } from "../services/WbotServices/wbotMessageListener";
|
||||
import WhatsappQueue from "../models/WhatsappQueue";
|
||||
import { get } from "../helpers/RedisClient"
|
||||
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService"
|
||||
|
||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||
const {
|
||||
|
@ -109,8 +110,8 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
|||
withUnreadMessages,
|
||||
unlimited,
|
||||
searchParamContent
|
||||
});
|
||||
|
||||
});
|
||||
|
||||
return res.status(200).json({ tickets, count, hasMore });
|
||||
};
|
||||
|
||||
|
@ -348,7 +349,8 @@ export const update = async (
|
|||
ticketData: {
|
||||
status: status,
|
||||
userId: userId,
|
||||
statusChatEnd: statusChatEndName.name
|
||||
statusChatEnd: statusChatEndName.name,
|
||||
statusChatEndId: statusChatEndName.id
|
||||
},
|
||||
ticketId
|
||||
});
|
||||
|
|
|
@ -0,0 +1,17 @@
|
|||
import { QueryInterface, DataTypes } from "sequelize";
|
||||
|
||||
module.exports = {
|
||||
up: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.addColumn("Tickets", "statusChatEndId", {
|
||||
type: DataTypes.INTEGER,
|
||||
references: { model: "StatusChatEnds", key: "id" },
|
||||
onUpdate: "CASCADE",
|
||||
onDelete: "SET NULL",
|
||||
allowNull: true
|
||||
});
|
||||
},
|
||||
|
||||
down: (queryInterface: QueryInterface) => {
|
||||
return queryInterface.removeColumn("Tickets", "statusChatEndId");
|
||||
}
|
||||
};
|
|
@ -10,25 +10,29 @@ import {
|
|||
} from "sequelize-typescript";
|
||||
|
||||
import SchedulingNotify from "./SchedulingNotify";
|
||||
import Ticket from "./Ticket"
|
||||
|
||||
@Table
|
||||
class StatusChatEnd extends Model<StatusChatEnd> {
|
||||
@PrimaryKey
|
||||
@AutoIncrement
|
||||
@Column
|
||||
id: number;
|
||||
|
||||
id: number;
|
||||
|
||||
@Column
|
||||
name: string;
|
||||
|
||||
|
||||
@CreatedAt
|
||||
createdAt: Date;
|
||||
|
||||
|
||||
@UpdatedAt
|
||||
updatedAt: Date;
|
||||
|
||||
@HasMany(() => SchedulingNotify)
|
||||
SchedulingNotifies: SchedulingNotify[];
|
||||
|
||||
@HasMany(() => Ticket)
|
||||
tickets: Ticket[];
|
||||
}
|
||||
|
||||
export default StatusChatEnd;
|
||||
|
|
|
@ -21,6 +21,7 @@ import User from "./User";
|
|||
import Whatsapp from "./Whatsapp";
|
||||
|
||||
import SchedulingNotify from "./SchedulingNotify";
|
||||
import StatusChatEnd from "./StatusChatEnd"
|
||||
|
||||
@Table
|
||||
class Ticket extends Model<Ticket> {
|
||||
|
@ -42,6 +43,10 @@ class Ticket extends Model<Ticket> {
|
|||
@Column
|
||||
isGroup: boolean;
|
||||
|
||||
@ForeignKey(() => StatusChatEnd)
|
||||
@Column
|
||||
statusChatEndId: number;
|
||||
|
||||
@Column
|
||||
statusChatEnd: string;
|
||||
|
||||
|
|
|
@ -1,26 +1,48 @@
|
|||
//relatorio
|
||||
import express from "express";
|
||||
import express from "express";
|
||||
|
||||
import isAuth from "../middleware/isAuth";
|
||||
|
||||
import * as ReportController from "../controllers/ReportController";
|
||||
import isAuth from "../middleware/isAuth";
|
||||
|
||||
const reportRoutes = express.Router();
|
||||
import * as ReportController from "../controllers/ReportController";
|
||||
|
||||
reportRoutes.get("/reports", isAuth, ReportController.reportUserByDateStartDateEnd);
|
||||
const reportRoutes = express.Router();
|
||||
|
||||
reportRoutes.post("/reports/onqueue", ReportController.reportOnQueue);
|
||||
reportRoutes.get(
|
||||
"/reports",
|
||||
isAuth,
|
||||
ReportController.reportUserByDateStartDateEnd
|
||||
);
|
||||
|
||||
reportRoutes.get("/reports/user/services", isAuth, ReportController.reportUserService);
|
||||
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/services/queues",
|
||||
isAuth,
|
||||
ReportController.reportServiceByQueue
|
||||
);
|
||||
|
||||
reportRoutes.get("/reports/messages", isAuth, ReportController.reportMessagesUserByDateStartDateEnd);
|
||||
reportRoutes.get(
|
||||
"/reports/messages",
|
||||
isAuth,
|
||||
ReportController.reportMessagesUserByDateStartDateEnd
|
||||
);
|
||||
|
||||
reportRoutes.get(
|
||||
"/reports/count/statusChatEnd",
|
||||
isAuth,
|
||||
ReportController.reportTicksCountByStatusChatEnds
|
||||
);
|
||||
|
||||
export default reportRoutes;
|
||||
|
|
|
@ -113,7 +113,8 @@ const ReportByNumberQueueService = async ({
|
|||
AND m.fromMe = 0
|
||||
-- AND q.id = 2
|
||||
AND w.number = ${number}
|
||||
AND (t.status = 'open' OR t.status = 'closed')
|
||||
AND t.status IN ('open', 'closed')
|
||||
HAVING WAITING_TIME IS NOT NULL
|
||||
ORDER BY
|
||||
WAITING_TIME;`,
|
||||
{ type: QueryTypes.SELECT }
|
||||
|
@ -237,7 +238,8 @@ 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 IN ('open', 'closed')
|
||||
HAVING WAITING_TIME IS NOT NULL
|
||||
ORDER BY
|
||||
WAITING_TIME;`,
|
||||
{ type: QueryTypes.SELECT }
|
||||
|
|
|
@ -0,0 +1,27 @@
|
|||
import StatusChatEnd from "../../models/StatusChatEnd";
|
||||
import AppError from "../../errors/AppError";
|
||||
|
||||
import { Sequelize } from "sequelize";
|
||||
import { splitDateTime } from "../../helpers/SplitDateTime"
|
||||
import ptBR from "date-fns/locale/pt-BR";
|
||||
import { format } from "date-fns"
|
||||
const dbConfig = require("../../config/database");
|
||||
const sequelize = new Sequelize(dbConfig);
|
||||
const { QueryTypes } = require("sequelize");
|
||||
|
||||
const CountStatusChatEndService = async (
|
||||
startDate: string,
|
||||
endDate: string
|
||||
) => {
|
||||
|
||||
const countStatusChatEnd: any = await sequelize.query(
|
||||
`select t.id, s.name, count(t.id) as count from Tickets t join StatusChatEnds s on
|
||||
t.statusChatEndId = s.id and DATE(t.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||
group by s.id;`,
|
||||
{ type: QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
return countStatusChatEnd;
|
||||
};
|
||||
|
||||
export default CountStatusChatEndService;
|
|
@ -7,7 +7,7 @@ import Queue from "../../models/Queue";
|
|||
import Message from "../../models/Message";
|
||||
import { userInfo } from "os";
|
||||
|
||||
import { Op, QueryTypes, where } from "sequelize";
|
||||
import { Op, QueryTypes, json, where } from "sequelize";
|
||||
|
||||
import { Sequelize } from "sequelize";
|
||||
import moment from "moment";
|
||||
|
@ -17,7 +17,7 @@ const sequelize = new Sequelize(dbConfig);
|
|||
import { startOfDay, endOfDay, parseISO, getDate } from "date-fns";
|
||||
import { string } from "yup/lib/locale";
|
||||
import Whatsapp from "../../models/Whatsapp";
|
||||
import Query from "mysql2/typings/mysql/lib/protocol/sequences/Query"
|
||||
import Query from "mysql2/typings/mysql/lib/protocol/sequences/Query";
|
||||
|
||||
interface Request {
|
||||
userId: string | number;
|
||||
|
@ -73,9 +73,7 @@ const ShowTicketReport = async ({
|
|||
{ type: QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
console.log('QUERY: ', query)
|
||||
|
||||
const { count, rows: tickets } = await Ticket.findAndCountAll({
|
||||
let { count, rows: tickets }: any = await Ticket.findAndCountAll({
|
||||
where: {
|
||||
id: { [Op.in]: _ticketsId.map((t: any) => t.id) }
|
||||
},
|
||||
|
@ -153,6 +151,58 @@ const ShowTicketReport = async ({
|
|||
throw new AppError("ERR_NO_TICKET_FOUND", 404);
|
||||
}
|
||||
|
||||
const ticketIds = tickets.map((t: any) => t.id);
|
||||
|
||||
if (ticketIds.length > 0) {
|
||||
const waiting_time: any = await sequelize.query(
|
||||
`SELECT t.id as ticketId, t.status, 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 t.id IN (${ticketIds.join()})
|
||||
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||
AND m.fromMe = 0
|
||||
AND t.status IN ('open', 'closed')
|
||||
HAVING WAITING_TIME IS NOT NULL
|
||||
ORDER BY
|
||||
WAITING_TIME;`,
|
||||
{ type: QueryTypes.SELECT }
|
||||
);
|
||||
|
||||
for (let w of waiting_time) {
|
||||
const { ticketId, status, WAITING_TIME } = w;
|
||||
|
||||
const index = tickets.findIndex((t: any) => +t?.id == +ticketId);
|
||||
|
||||
if (index != -1) {
|
||||
tickets[index].dataValues.waiting_time = WAITING_TIME;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return { tickets, count, hasMore };
|
||||
};
|
||||
|
||||
|
|
|
@ -10,7 +10,7 @@ import { createOrUpdateTicketCache } from "../../helpers/TicketCache";
|
|||
import AppError from "../../errors/AppError";
|
||||
import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
|
||||
import BotIsOnQueue from "../../helpers/BotIsOnQueue";
|
||||
import { deleteObject } from "../../helpers/RedisClient"
|
||||
import { deleteObject } from "../../helpers/RedisClient";
|
||||
var flatten = require("flat");
|
||||
|
||||
interface TicketData {
|
||||
|
@ -18,6 +18,7 @@ interface TicketData {
|
|||
userId?: number;
|
||||
queueId?: number;
|
||||
statusChatEnd?: string;
|
||||
statusChatEndId?: number;
|
||||
unreadMessages?: number;
|
||||
whatsappId?: string | number;
|
||||
}
|
||||
|
@ -46,6 +47,7 @@ const UpdateTicketService = async ({
|
|||
queueId,
|
||||
statusChatEnd,
|
||||
unreadMessages,
|
||||
statusChatEndId,
|
||||
whatsappId
|
||||
} = ticketData;
|
||||
|
||||
|
@ -66,13 +68,14 @@ const UpdateTicketService = async ({
|
|||
if (oldStatus === "closed") {
|
||||
await CheckContactOpenTickets(ticket.contact.id, ticket.whatsappId);
|
||||
}
|
||||
|
||||
|
||||
await ticket.update({
|
||||
status,
|
||||
queueId,
|
||||
userId,
|
||||
unreadMessages,
|
||||
statusChatEnd,
|
||||
statusChatEndId,
|
||||
whatsappId
|
||||
});
|
||||
|
||||
|
|
|
@ -235,6 +235,7 @@ 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: `Espera`, field: 'waiting_time' },
|
||||
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
|
||||
]
|
||||
|
||||
|
@ -249,6 +250,7 @@ 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: `Espera`, field: 'waiting_time' },
|
||||
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
|
||||
]
|
||||
|
||||
|
|
Loading…
Reference in New Issue