Merge branch 'el_lojas_melhorias' of https://github.com/AdrianoRobson/projeto-hit into el_lojas_melhorias

feat-scaling-ticket-remote-creation
willian-pessoa 2024-04-02 17:21:53 -03:00
commit 024c6920af
16 changed files with 471 additions and 188 deletions

View File

@ -183,11 +183,17 @@ socketIo.on('connect_error', async function (err) {
}) })
// //
const wwebVersion = '2.2402.5';
//NOVA OPÇÃO MD //NOVA OPÇÃO MD
client = new Client({ client = new Client({
authStrategy: new LocalAuth({ clientId: 'omnihit_sesssion' }), authStrategy: new LocalAuth({ clientId: 'omnihit_sesssion' }),
puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox'], executablePath: process.env.CHROME_BIN || '/usr/bin/google-chrome-stable' }, puppeteer: { args: ['--no-sandbox', '--disable-setuid-sandbox'], executablePath: process.env.CHROME_BIN || '/usr/bin/google-chrome-stable' },
webVersionCache: {
type: 'remote',
remotePath: `https://raw.githubusercontent.com/wppconnect-team/wa-version/main/html/${wwebVersion}.html`,
},
}) })
client.initialize() client.initialize()

View File

@ -8,6 +8,9 @@ import UpdateQueueService from "../services/QueueService/UpdateQueueService";
import Queue from "../models/Queue"; import Queue from "../models/Queue";
import AppError from "../errors/AppError"; import AppError from "../errors/AppError";
import { del, get, set } from "../helpers/RedisClient"; import { del, get, set } from "../helpers/RedisClient";
import { Op } from "sequelize";
import ListWhatsAppsService from "../services/WhatsappService/ListWhatsAppsService";
import Whatsapp from "../models/Whatsapp";
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const queues = await ListQueuesService(); const queues = await ListQueuesService();
@ -15,6 +18,58 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
return res.status(200).json(queues); return res.status(200).json(queues);
}; };
export const listQueues = async (
req: Request,
res: Response
): Promise<Response> => {
const whatsapps = await Whatsapp.findAll({
where: {
name: { [Op.ne]: "botqueue" },
number: { [Op.ne]: "" },
phoneNumberId: false
},
attributes: ["number"],
include: [
{
model: Queue,
as: "queues",
attributes: ["id", "name"]
}
]
});
const whats = whatsapps
?.filter((w: any) => w?.queues?.length > 0)
?.map((w: any) => {
const { number, queues } = w;
return {
number,
queues: queues?.map((q: any) => {
const { id, name } = q;
return { id, name };
})
};
});
let _queues: any = [];
for (const w of whats) {
const { queues } = w;
for (const q of queues) {
const { id: queueId, name } = q;
const auxQueue = _queues.findIndex((q: any) => q.queueId == queueId);
if (auxQueue == -1) {
_queues.push({ queueId, name });
}
}
}
return res.status(200).json(_queues);
};
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
const { name, color, greetingMessage } = req.body; const { name, color, greetingMessage } = req.body;

View File

@ -262,9 +262,7 @@ export const reportMessagesUserByDateStartDateEnd = async (
data_query_messages[i].fromMe = "Cliente"; data_query_messages[i].fromMe = "Cliente";
} }
data_query_messages[i].id = i + 1; data_query_messages[i].id = i + 1;
console.log("data_query_messages: ", data_query_messages[i]);
} }
return res.status(200).json(data_query_messages); return res.status(200).json(data_query_messages);

View File

@ -20,14 +20,14 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
// const config = await SettingTicket.findAll(); // const config = await SettingTicket.findAll();
return res.status(200).json({ settings, }); return res.status(200).json({ settings });
}; };
export const ticketSettings = async ( export const ticketSettings = async (
req: Request, req: Request,
res: Response res: Response
): Promise<Response> => { ): Promise<Response> => {
const { number } = req.params; const { number } = req.params;
const config = await SettingTicket.findAll({ where: { number } }); const config = await SettingTicket.findAll({ where: { number } });
@ -40,6 +40,7 @@ export const updateTicketSettings = async (
): Promise<Response> => { ): Promise<Response> => {
const { const {
number, number,
saturdayBusinessTime,
outBusinessHours, outBusinessHours,
ticketExpiration, ticketExpiration,
weekend, weekend,
@ -47,7 +48,7 @@ export const updateTicketSettings = async (
sunday, sunday,
holiday holiday
} = req.body; } = req.body;
if (!number) throw new AppError("No number selected", 400); if (!number) throw new AppError("No number selected", 400);
if (outBusinessHours && Object.keys(outBusinessHours).length > 0) { if (outBusinessHours && Object.keys(outBusinessHours).length > 0) {
@ -58,6 +59,14 @@ export const updateTicketSettings = async (
}); });
} }
if (saturdayBusinessTime && Object.keys(saturdayBusinessTime).length > 0) {
await updateSettingTicket({
...saturdayBusinessTime,
key: "saturdayBusinessTime",
number
});
}
if (ticketExpiration && Object.keys(ticketExpiration).length > 0) { if (ticketExpiration && Object.keys(ticketExpiration).length > 0) {
await updateSettingTicket({ await updateSettingTicket({
...ticketExpiration, ...ticketExpiration,

View File

@ -75,10 +75,10 @@ import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
import CreateContactService from "../services/ContactServices/CreateContactService"; import CreateContactService from "../services/ContactServices/CreateContactService";
import { botSendMessage } from "../services/WbotServices/wbotMessageListener"; import { botSendMessage } from "../services/WbotServices/wbotMessageListener";
import WhatsappQueue from "../models/WhatsappQueue"; import WhatsappQueue from "../models/WhatsappQueue";
import { get } from "../helpers/RedisClient" import { get } from "../helpers/RedisClient";
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService" import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService";
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const { const {
pageNumber, pageNumber,
status, status,
@ -110,8 +110,8 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
withUnreadMessages, withUnreadMessages,
unlimited, unlimited,
searchParamContent searchParamContent
}); });
return res.status(200).json({ tickets, count, hasMore }); return res.status(200).json({ tickets, count, hasMore });
}; };
@ -119,10 +119,10 @@ export const remoteTicketCreation = async (
req: Request, req: Request,
res: Response res: Response
): Promise<Response> => { ): Promise<Response> => {
const { contact_from, contact_to, msg, contact_name }: any = req.body; const { queueId, contact_to, msg, contact_name }: any = req.body;
const validate = ["contact_from", "contact_to", "msg"]; const validate = ["queueId", "contact_to", "msg"];
const validateOnlyNumber = ["contact_from", "contact_to"]; const validateOnlyNumber = ["queueId", "contact_to"];
for (let prop of validate) { for (let prop of validate) {
if (!req.body[prop]) if (!req.body[prop])
@ -139,109 +139,97 @@ export const remoteTicketCreation = async (
} }
} }
const whatsapp = await Whatsapp.findOne({ const whatsapps = await ListWhatsAppsForQueueService(queueId, "CONNECTED");
where: { number: contact_from, status: "CONNECTED" }
});
if (whatsapp) { if (!whatsapps || whatsapps?.length == 0) {
const { id: whatsappId, number, status } = whatsapp; return res.status(500).json({
msg: `queueId ${queueId} does not have a WhatsApp number associated with it or the number's session is disconnected.`
const queue: any = await WhatsappQueue.findOne({
where: { whatsappId },
attributes: ["queueId"]
}); });
}
const { queueId } = queue; const { id: whatsappId } = whatsapps[0];
// const validNumber = await CheckIsValidContact(contact_to, true); // const validNumber = await CheckIsValidContact(contact_to, true);
const validNumber = contact_to; const validNumber = contact_to;
if (validNumber) { if (validNumber) {
let contact = await Contact.findOne({ where: { number: validNumber } }); let contact = await Contact.findOne({ where: { number: validNumber } });
if (!contact) { if (!contact) {
// const profilePicUrl = await GetProfilePicUrl(validNumber); // const profilePicUrl = await GetProfilePicUrl(validNumber);
contact = await CreateContactService({ contact = await CreateContactService({
name: contact_name ? contact_name : contact_to, name: contact_name ? contact_name : contact_to,
number: validNumber number: validNumber
// profilePicUrl // profilePicUrl
});
const io = getIO();
io.emit("contact", {
action: "create",
contact
});
}
const { id: contactId } = contact;
const botInfo = await BotIsOnQueue("botqueue");
let ticket = await Ticket.findOne({
where: {
[Op.or]: [
{ contactId, status: "queueChoice" },
{ contactId, status: "open", userId: botInfo.userIdBot }
]
}
}); });
if (getSettingValue("whatsaAppCloudApi")?.value == "enabled") {
if (ticket) {
await UpdateTicketService({
ticketData: { status: "closed" },
ticketId: ticket.id
});
ticket = null;
}
} else {
if (ticket) {
await UpdateTicketService({
ticketData: { status: "closed" },
ticketId: ticket.id
});
}
}
if (!ticket) {
ticket = await FindOrCreateTicketService(
contact,
whatsappId,
0,
undefined,
queueId
);
botSendMessage(ticket, msg);
}
const io = getIO(); const io = getIO();
io.to(ticket.status).emit("ticket", { io.emit("contact", {
action: "update", action: "create",
ticket contact
}); });
console.log(
`REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 200 | MSG: success`
);
return res.status(200).json({ msg: "success" });
} }
const { id: contactId } = contact;
const botInfo = await BotIsOnQueue("botqueue");
let ticket = await Ticket.findOne({
where: {
[Op.or]: [
{ contactId, status: "queueChoice" },
{ contactId, status: "open", userId: botInfo.userIdBot }
]
}
});
if (getSettingValue("whatsaAppCloudApi")?.value == "enabled") {
if (ticket) {
await UpdateTicketService({
ticketData: { status: "closed" },
ticketId: ticket.id
});
ticket = null;
}
} else {
if (ticket) {
await UpdateTicketService({
ticketData: { status: "closed" },
ticketId: ticket.id
});
}
}
if (!ticket) {
ticket = await FindOrCreateTicketService(
contact,
whatsappId,
0,
undefined,
queueId
);
botSendMessage(ticket, `${msg}`);
}
const io = getIO();
io.to(ticket.status).emit("ticket", {
action: "update",
ticket
});
console.log( console.log(
`REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 500 | MSG: The number ${contact_to} does not exist on WhatsApp` `REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 200 | MSG: success`
); );
return res return res.status(200).json({ msg: "success" });
.status(500)
.json({ msg: `The number ${contact_to} does not exist on WhatsApp` });
} }
console.log( console.log(
`REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 500 | MSG: Whatsapp number ${contact_from} disconnected or it doesn't exist in omnihit` `REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 500 | MSG: The number ${contact_to} does not exist on WhatsApp`
); );
return res.status(500).json({ return res
msg: `Whatsapp number ${contact_from} disconnected or it doesn't exist in omnihit` .status(500)
}); .json({ msg: `The number ${contact_to} does not exist on WhatsApp` });
}; };
export const store = async (req: Request, res: Response): Promise<Response> => { export const store = async (req: Request, res: Response): Promise<Response> => {
@ -408,7 +396,6 @@ export const update = async (
for (const w of whatsappsByqueue) { for (const w of whatsappsByqueue) {
let whats = await ListWhatsAppsNumber(w.id); let whats = await ListWhatsAppsNumber(w.id);
console.log("-------> WHATS: ", JSON.stringify(whats, null, 6));
const ticket = await Ticket.findOne({ const ticket = await Ticket.findOne({
where: { where: {
[Op.and]: [ [Op.and]: [

View File

@ -0,0 +1,25 @@
import { QueryInterface } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.bulkInsert(
"SettingTickets",
[
{
message: "",
startTime: new Date(),
endTime: new Date(),
value: "disabled",
key: "saturdayBusinessTime",
createdAt: new Date(),
updatedAt: new Date()
}
],
{}
);
},
down: (queryInterface: QueryInterface) => {
return queryInterface.bulkDelete("SettingTickets", {});
}
};

View File

@ -39,7 +39,7 @@ const isHoliday = async (number: string | number) => {
locale: ptBR locale: ptBR
}) })
) )
); );
if (currentDate.fullDate == startTime.fullDate) { if (currentDate.fullDate == startTime.fullDate) {
obj.set = true; obj.set = true;
@ -62,21 +62,8 @@ const isWeekend = async (number: string | number) => {
weekend.value == "enabled" && weekend.value == "enabled" &&
weekend.message?.trim()?.length > 0 weekend.message?.trim()?.length > 0
) { ) {
// Specify your desired timezone
const brazilTimeZone = "America/Sao_Paulo";
const currentDateUtc = new Date();
// Convert UTC date to Brazil time zone
const currentDate = utcToZonedTime(currentDateUtc, brazilTimeZone);
// Format the date using the desired format
const formattedDate = _format(currentDate, "yyyy-MM-dd HH:mm:ssXXX");
const parsedDate = parseISO(formattedDate);
// Convert parsed date to Brazil time zone // Convert parsed date to Brazil time zone
const localDate = utcToZonedTime(parsedDate, brazilTimeZone); const localDate = localDateConvert();
// Check if it's Saturday or Sunday // Check if it's Saturday or Sunday
if (isSaturday(localDate)) { if (isSaturday(localDate)) {
@ -173,8 +160,104 @@ async function isOutBusinessTime(number: string | number) {
return obj; return obj;
} }
export { async function isOutBusinessTimeSaturday(number: string | number) {
isWeekend, let obj = { set: false, msg: "" };
isHoliday,
isOutBusinessTime // Convert parsed date to Brazil time zone
}; const localDate = localDateConvert();
// Check if it's Saturday or Sunday
if (!isSaturday(localDate)) {
return obj;
}
const outBusinessHoursSaturday = await SettingTicket.findOne({
where: { key: "saturdayBusinessTime", number }
});
let isWithinRange = false;
if (
outBusinessHoursSaturday &&
outBusinessHoursSaturday.value == "enabled" &&
outBusinessHoursSaturday?.message?.trim()?.length > 0
) {
const ticketDateTimeUpdate = splitDateTime(
new Date(
_format(new Date(), "yyyy-MM-dd HH:mm:ss", {
locale: ptBR
})
)
);
const startTime = splitDateTime(
new Date(
_format(
new Date(outBusinessHoursSaturday.startTime),
"yyyy-MM-dd HH:mm:ss",
{
locale: ptBR
}
)
)
);
const endTime = splitDateTime(
new Date(
_format(
new Date(outBusinessHoursSaturday.endTime),
"yyyy-MM-dd HH:mm:ss",
{
locale: ptBR
}
)
)
);
const format = "HH:mm:ss";
const parsedStartTime = parse(
ticketDateTimeUpdate.fullTime,
format,
new Date()
);
const parsedEndTime = parse(startTime.fullTime, format, new Date());
const parsedTimeToCheck = parse(endTime.fullTime, format, new Date());
const timeInterval = { start: parsedStartTime, end: parsedEndTime };
// If the time range spans across different days, handle the date part
if (parsedEndTime < parsedStartTime) {
const nextDay = new Date(parsedStartTime);
nextDay.setDate(nextDay.getDate() + 1);
timeInterval.end = nextDay;
}
isWithinRange = isWithinInterval(parsedTimeToCheck, timeInterval);
if (!isWithinRange) {
obj.set = true;
obj.msg = outBusinessHoursSaturday.message;
}
}
return obj;
}
function localDateConvert() {
const brazilTimeZone = "America/Sao_Paulo";
const currentDateUtc = new Date();
// Convert UTC date to Brazil time zone
const currentDate = utcToZonedTime(currentDateUtc, brazilTimeZone);
// Format the date using the desired format
const formattedDate = _format(currentDate, "yyyy-MM-dd HH:mm:ssXXX");
const parsedDate = parseISO(formattedDate);
// Convert parsed date to Brazil time zone
const localDate = utcToZonedTime(parsedDate, brazilTimeZone);
return localDate;
}
export { isWeekend, isHoliday, isOutBusinessTime, isOutBusinessTimeSaturday };

View File

@ -19,10 +19,11 @@ const isAuth = (req: Request, res: Response, next: NextFunction): void => {
throw new AppError("ERR_SESSION_EXPIRED", 401); throw new AppError("ERR_SESSION_EXPIRED", 401);
} }
const [, token] = authHeader.split(" "); const [, token] = authHeader.split(" ");
if ( if (
req.originalUrl == "/tickets/remote/create" && (req.originalUrl == "/queue/remote/list" ||
req.originalUrl == "/tickets/remote/create") &&
token === process.env.TOKEN_REMOTE_TICKET_CREATION token === process.env.TOKEN_REMOTE_TICKET_CREATION
) { ) {
return next(); return next();

View File

@ -11,6 +11,8 @@ queueRoutes.post("/queue", isAuth, QueueController.store);
queueRoutes.post("/queue/customization", QueueController.customization); queueRoutes.post("/queue/customization", QueueController.customization);
queueRoutes.get("/queue/remote/list", isAuth, QueueController.listQueues);
queueRoutes.get("/queue/:queueId", isAuth, QueueController.show); queueRoutes.get("/queue/:queueId", isAuth, QueueController.show);
queueRoutes.put("/queue/:queueId", isAuth, QueueController.update); queueRoutes.put("/queue/:queueId", isAuth, QueueController.update);

View File

@ -18,6 +18,7 @@ import { startOfDay, endOfDay, parseISO, getDate } from "date-fns";
import { string } from "yup/lib/locale"; import { string } from "yup/lib/locale";
import Whatsapp from "../../models/Whatsapp"; 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";
import { te } from "date-fns/locale";
interface Request { interface Request {
userId: string | number; userId: string | number;
@ -43,43 +44,56 @@ const ShowTicketReport = async ({
createdOrUpdated = "created", createdOrUpdated = "created",
queueId queueId
}: Request): Promise<Response> => { }: Request): Promise<Response> => {
let where_clause: any = {}; // let where_clause: any = {};
let query = ""; // let query = "";
if (userId !== "0") { // if (userId !== "0") {
where_clause.userid = userId; // where_clause.userid = userId;
query = `AND t.userId = ${userId}`; // query = `AND t.userId = ${userId}`;
} // }
// if (queueId) {
// where_clause.queueId = queueId;
// query = `AND t.queueId = ${queueId}`;
// }
const createdAtOrUpdatedAt =
createdOrUpdated == "created" ? "createdAt" : "updatedAt";
let where_clause = {};
if (queueId) { if (queueId) {
where_clause.queueId = queueId; where_clause = {
query = `AND t.queueId = ${queueId}`; queueId: queueId,
[createdAtOrUpdatedAt]: {
[Op.gte]: startDate + " 00:00:00.000000",
[Op.lte]: endDate + " 23:59:59.999999"
}
};
} else if (userId == "0") {
where_clause = {
[createdAtOrUpdatedAt]: {
[Op.gte]: startDate + " 00:00:00.000000",
[Op.lte]: endDate + " 23:59:59.999999"
}
};
} else if (userId != "0") {
where_clause = {
userid: userId,
[createdAtOrUpdatedAt]: {
[Op.gte]: startDate + " 00:00:00.000000",
[Op.lte]: endDate + " 23:59:59.999999"
}
};
} }
const limit = 40; const limit = 40;
const offset = limit * (+pageNumber - 1); const offset = limit * (+pageNumber - 1);
const createdAtOrUpdatedAt =
createdOrUpdated == "created" ? "createdAt" : "updatedAt";
const _ticketsId = await sequelize.query(
`SELECT t.id
FROM Tickets t
INNER JOIN (
SELECT DISTINCT ticketId
FROM Messages
WHERE ${createdAtOrUpdatedAt} >= '${startDate} 00:00:00' AND ${createdAtOrUpdatedAt} <= '${endDate} 23:59:59'
) AS m ON m.ticketId = t.id ${query};`,
{ type: QueryTypes.SELECT }
);
let { count, rows: tickets }: any = await Ticket.findAndCountAll({ let { count, rows: tickets }: any = await Ticket.findAndCountAll({
where: { where: where_clause,
id: { [Op.in]: _ticketsId.map((t: any) => t.id) }
},
limit, limit,
offset, offset,
attributes: [ attributes: [
"id", "id",
"status", "status",
@ -151,43 +165,41 @@ const ShowTicketReport = async ({
throw new AppError("ERR_NO_TICKET_FOUND", 404); throw new AppError("ERR_NO_TICKET_FOUND", 404);
} }
const ticketIds = tickets.map((t: any) => t.id); if (tickets.length > 0) {
if (ticketIds.length > 0) {
const waiting_time: any = await sequelize.query( const waiting_time: any = await sequelize.query(
`SELECT t.id as ticketId, t.status, TIME_FORMAT( `SELECT t.id as ticketId, t.status, TIME_FORMAT(
SEC_TO_TIME( 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 ), '%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 t.id IN (${ticketIds.join()}) AND t.id IN (${tickets.map((t: any) => t.id).join()})
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 t.status IN ('open', 'closed') AND t.status IN ('open', 'closed')
HAVING WAITING_TIME IS NOT NULL HAVING WAITING_TIME IS NOT NULL
ORDER BY ORDER BY
WAITING_TIME;`, WAITING_TIME;`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );

View File

@ -10,6 +10,7 @@ import path from "path";
import { import {
isHoliday, isHoliday,
isOutBusinessTime, isOutBusinessTime,
isOutBusinessTimeSaturday,
isWeekend isWeekend
} from "../../helpers/TicketConfig"; } from "../../helpers/TicketConfig";
@ -1221,6 +1222,13 @@ const outOfService = async (number: string) => {
objs.push({ type: "holiday", msg: holiday.msg }); objs.push({ type: "holiday", msg: holiday.msg });
} }
// MESSAGE TO SATURDAY BUSINESS TIME
const businessTimeSaturday = await isOutBusinessTimeSaturday(number);
if (businessTimeSaturday && businessTimeSaturday.set) {
objs.push({ type: "saturdayBusinessTime", msg: businessTimeSaturday.msg });
}
// MESSAGES TO SATURDAY OR SUNDAY // MESSAGES TO SATURDAY OR SUNDAY
const weekend: any = await isWeekend(number); const weekend: any = await isWeekend(number);

View File

@ -6,17 +6,30 @@ const { QueryTypes } = require("sequelize");
const sequelize = new Sequelize(dbConfig); const sequelize = new Sequelize(dbConfig);
const ListWhatsAppsForQueueService = async (queueId: number | string): Promise<any> => { const ListWhatsAppsForQueueService = async (
const distinctWhatsapps = await sequelize.query( queueId: number | string,
`SELECT w.id, w.number, w.status, wq.whatsappId, wq.queueId status?: string
FROM Whatsapps w ): Promise<any> => {
JOIN WhatsappQueues wq ON w.id = wq.whatsappId AND wq.queueId = ${queueId} let distinctWhatsapps: any;
GROUP BY w.number;`,
{ type: QueryTypes.SELECT } if (status) {
); distinctWhatsapps = await sequelize.query(
`SELECT w.id, w.number, w.status, wq.whatsappId, wq.queueId FROM Whatsapps w
JOIN WhatsappQueues wq ON w.id = wq.whatsappId AND wq.queueId = ${queueId} AND w.status = '${status}'
AND phoneNumberId = false
GROUP BY w.number;`,
{ type: QueryTypes.SELECT }
);
} else {
distinctWhatsapps = await sequelize.query(
`SELECT w.id, w.number, w.status, wq.whatsappId, wq.queueId FROM Whatsapps w
JOIN WhatsappQueues wq ON w.id = wq.whatsappId AND wq.queueId = ${queueId}
GROUP BY w.number;`,
{ type: QueryTypes.SELECT }
);
}
return distinctWhatsapps; return distinctWhatsapps;
}; };
export default ListWhatsAppsForQueueService; export default ListWhatsAppsForQueueService;

View File

@ -77,8 +77,16 @@ const ConfigModal = ({ open, onClose, change }) => {
const initialState = { const initialState = {
startTimeBus: new Date(), startTimeBus: new Date(),
endTimeBus: new Date(), endTimeBus: new Date(),
startTimeBusSaturday: new Date(),
endTimeBusSaturday: new Date(),
messageBus: '', messageBus: '',
messageBusSaturday: '',
businessTimeEnable: false, businessTimeEnable: false,
businessTimeEnableSaturday: false,
ticketTimeExpiration: new Date(), ticketTimeExpiration: new Date(),
ticketExpirationMsg: '', ticketExpirationMsg: '',
ticketExpirationEnable: false, ticketExpirationEnable: false,
@ -115,13 +123,16 @@ const ConfigModal = ({ open, onClose, change }) => {
if (!selectedNumber) return if (!selectedNumber) return
const { data } = await api.get(`/settings/ticket/${selectedNumber}`) const { data } = await api.get(`/settings/ticket/${selectedNumber}`)
if (data?.config && data.config.length === 0) { if (data?.config && data.config.length === 0) {
setConfig(initialState) setConfig(initialState)
return return
} }
const outBusinessHours = data.config.find((c) => c.key === "outBusinessHours") const outBusinessHours = data.config.find((c) => c.key === "outBusinessHours")
const saturdayBusinessTime = data.config.find((c) => c.key === "saturdayBusinessTime")
const ticketExpiration = data.config.find((c) => c.key === "ticketExpiration") const ticketExpiration = data.config.find((c) => c.key === "ticketExpiration")
const saturday = data.config.find((c) => c.key === "saturday") const saturday = data.config.find((c) => c.key === "saturday")
const sunday = data.config.find((c) => c.key === "sunday") const sunday = data.config.find((c) => c.key === "sunday")
@ -134,6 +145,11 @@ const ConfigModal = ({ open, onClose, change }) => {
messageBus: outBusinessHours.message, messageBus: outBusinessHours.message,
businessTimeEnable: outBusinessHours.value === 'enabled' ? true : false, businessTimeEnable: outBusinessHours.value === 'enabled' ? true : false,
startTimeBusSaturday: saturdayBusinessTime.startTime,
endTimeBusSaturday: saturdayBusinessTime.endTime,
messageBusSaturday: saturdayBusinessTime.message,
businessTimeEnableSaturday: saturdayBusinessTime.value === 'enabled' ? true : false,
ticketTimeExpiration: ticketExpiration.startTime, ticketTimeExpiration: ticketExpiration.startTime,
ticketExpirationMsg: ticketExpiration.message, ticketExpirationMsg: ticketExpiration.message,
ticketExpirationEnable: ticketExpiration.value === 'enabled' ? true : false, ticketExpirationEnable: ticketExpiration.value === 'enabled' ? true : false,
@ -165,6 +181,14 @@ const ConfigModal = ({ open, onClose, change }) => {
message: values.messageBus, message: values.messageBus,
value: values.businessTimeEnable ? 'enabled' : 'disabled' value: values.businessTimeEnable ? 'enabled' : 'disabled'
}, },
saturdayBusinessTime: {
startTime: values.startTimeBusSaturday,
endTime: values.endTimeBusSaturday,
message: values.messageBusSaturday,
value: values.businessTimeEnableSaturday ? 'enabled' : 'disabled'
},
ticketExpiration: { ticketExpiration: {
startTime: values.ticketTimeExpiration, startTime: values.ticketTimeExpiration,
message: values.ticketExpirationMsg, message: values.ticketExpirationMsg,
@ -205,7 +229,7 @@ const ConfigModal = ({ open, onClose, change }) => {
onClose() onClose()
// setConfig(initialState) // setConfig(initialState)
} }
return ( return (
<div className={classes.root}> <div className={classes.root}>
<Dialog <Dialog
@ -325,6 +349,61 @@ const ConfigModal = ({ open, onClose, change }) => {
</div> </div>
<br />
{/* SABADO INICIO */}
<div className={classes.multFieldLine}>
<Field
component={TimePicker}
name="startTimeBusSaturday"
label="Inicio atendimentos"
ampm={false}
openTo="hours"
views={['hours', 'minutes',]}
format="HH:mm"
/>
{' '}
<Field
component={TimePicker}
name="endTimeBusSaturday"
label="Fim atendimento"
ampm={false}
openTo="hours"
views={['hours', 'minutes',]}
format="HH:mm"
/>
<FormControlLabel
control={
<Field
as={Switch}
color="primary"
name="businessTimeEnableSaturday"
checked={values.businessTimeEnableSaturday}
/>
}
label={'Ativar/Desativar'} />
</div>
<div>
<Field
as={TextField}
label={'Mensagem fora do horário de atendimento sábado'}
type="messageBusSaturday"
multiline
rows={5}
fullWidth
name="messageBusSaturday"
error={
touched.messageBusSaturday && Boolean(errors.messageBusSaturday)
}
helperText={
touched.messageBusSaturday && errors.messageBusSaturday
}
variant="outlined"
margin="dense"
/>
</div>
{/* SABADO FIM */}
<br /> <br />

View File

@ -76,7 +76,7 @@ const PieChart = ({ data = dataExample }) => {
> >
<Box width="100%" height="100%" position="sticky" top="0" zIndex={1000}> <Box width="100%" height="100%" position="sticky" top="0" zIndex={1000}>
<Box sx={{ position: "absolute" }}> <Box sx={{ position: "absolute" }}>
<Title>Tickets Status</Title> <Title>Tickets encerramento</Title>
</Box> </Box>
<ResponsiveContainer width="100%" height="100%"> <ResponsiveContainer width="100%" height="100%">
<RechartsPieChart width={400} height={400}> <RechartsPieChart width={400} height={400}>

View File

@ -258,7 +258,7 @@ const Dashboard = () => {
const [usersOnlineInfo, dispatch] = useReducer(reducer, []) const [usersOnlineInfo, dispatch] = useReducer(reducer, [])
const [ticketStatusChange, setStatus] = useState() const [ticketStatusChange, setStatus] = useState()
const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 }) const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 })
const [ticketStatusChatEnd, setTicketStatusChatEnd] = useState([])
const { user } = useContext(AuthContext) const { user } = useContext(AuthContext)
useEffect(() => { useEffect(() => {
@ -287,12 +287,17 @@ const Dashboard = () => {
const { data } = await api.get("/reports/user/services", { const { data } = await api.get("/reports/user/services", {
params: { userId: null, startDate: dateToday, endDate: dateToday }, params: { userId: null, startDate: dateToday, endDate: dateToday },
}) })
// console.log('data.data: ', data.usersProfile)
dispatch({ type: "RESET" }) dispatch({ type: "RESET" })
dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }) dispatch({ type: "LOAD_QUERY", payload: data.usersProfile })
const { data: ticketStatusChatEndData } = await api.get("/reports/count/statusChatEnd", {
params: { startDate: dateToday, endDate: dateToday },
})
setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd)
} catch (err) { } catch (err) {
} }
@ -506,7 +511,7 @@ const Dashboard = () => {
</Grid> </Grid>
<Grid item xs={12} sm={12} md={6} lg={6}> <Grid item xs={12} sm={12} md={6} lg={6}>
<Paper className={classes.fixedHeightPaper} variant="outlined"> <Paper className={classes.fixedHeightPaper} variant="outlined">
<PieChart data={[]} /> <PieChart data={ticketStatusChatEnd} />
</Paper> </Paper>
</Grid> </Grid>
</Grid> </Grid>

View File

@ -363,7 +363,7 @@ const Report = () => {
if (reportOption === '1') { if (reportOption === '1') {
const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets, createdOrUpdated: selectedValue, queueId }, userQueues: userA.queues }) const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets, createdOrUpdated: selectedValue, queueId }, userQueues: userA.queues })
let ticketsQueue = data.tickets let ticketsQueue = data.tickets
let userQueues = userA.queues let userQueues = userA.queues
let filterQueuesTickets = [] let filterQueuesTickets = []