Merge branch 'el_lojas_melhorias' of https://github.com/AdrianoRobson/projeto-hit into el_lojas_melhorias
commit
024c6920af
|
@ -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()
|
||||||
|
|
|
@ -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;
|
||||||
|
|
||||||
|
|
|
@ -263,8 +263,6 @@ export const reportMessagesUserByDateStartDateEnd = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
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);
|
||||||
|
|
|
@ -20,7 +20,7 @@ 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 (
|
||||||
|
@ -40,6 +40,7 @@ export const updateTicketSettings = async (
|
||||||
): Promise<Response> => {
|
): Promise<Response> => {
|
||||||
const {
|
const {
|
||||||
number,
|
number,
|
||||||
|
saturdayBusinessTime,
|
||||||
outBusinessHours,
|
outBusinessHours,
|
||||||
ticketExpiration,
|
ticketExpiration,
|
||||||
weekend,
|
weekend,
|
||||||
|
@ -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,
|
||||||
|
|
|
@ -75,8 +75,8 @@ 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 {
|
||||||
|
@ -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,19 +139,15 @@ export const remoteTicketCreation = async (
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
const whatsapp = await Whatsapp.findOne({
|
const whatsapps = await ListWhatsAppsForQueueService(queueId, "CONNECTED");
|
||||||
where: { number: contact_from, status: "CONNECTED" }
|
|
||||||
|
if (!whatsapps || whatsapps?.length == 0) {
|
||||||
|
return res.status(500).json({
|
||||||
|
msg: `queueId ${queueId} does not have a WhatsApp number associated with it or the number's session is disconnected.`
|
||||||
});
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (whatsapp) {
|
const { id: whatsappId } = whatsapps[0];
|
||||||
const { id: whatsappId, number, status } = whatsapp;
|
|
||||||
|
|
||||||
const queue: any = await WhatsappQueue.findOne({
|
|
||||||
where: { whatsappId },
|
|
||||||
attributes: ["queueId"]
|
|
||||||
});
|
|
||||||
|
|
||||||
const { queueId } = queue;
|
|
||||||
|
|
||||||
// const validNumber = await CheckIsValidContact(contact_to, true);
|
// const validNumber = await CheckIsValidContact(contact_to, true);
|
||||||
const validNumber = contact_to;
|
const validNumber = contact_to;
|
||||||
|
@ -213,7 +209,7 @@ export const remoteTicketCreation = async (
|
||||||
undefined,
|
undefined,
|
||||||
queueId
|
queueId
|
||||||
);
|
);
|
||||||
botSendMessage(ticket, msg);
|
botSendMessage(ticket, `${msg}`);
|
||||||
}
|
}
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
|
@ -234,14 +230,6 @@ export const remoteTicketCreation = async (
|
||||||
return res
|
return res
|
||||||
.status(500)
|
.status(500)
|
||||||
.json({ msg: `The number ${contact_to} does not exist on WhatsApp` });
|
.json({ msg: `The number ${contact_to} does not exist on WhatsApp` });
|
||||||
}
|
|
||||||
|
|
||||||
console.log(
|
|
||||||
`REMOTE TICKET CREATION FROM ENDPOINT | STATUS: 500 | MSG: Whatsapp number ${contact_from} disconnected or it doesn't exist in omnihit`
|
|
||||||
);
|
|
||||||
return res.status(500).json({
|
|
||||||
msg: `Whatsapp number ${contact_from} disconnected or it doesn't exist in omnihit`
|
|
||||||
});
|
|
||||||
};
|
};
|
||||||
|
|
||||||
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]: [
|
||||||
|
|
|
@ -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", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -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 };
|
||||||
|
|
|
@ -22,7 +22,8 @@ const isAuth = (req: Request, res: Response, next: NextFunction): void => {
|
||||||
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();
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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,9 +165,7 @@ 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(
|
||||||
|
@ -182,7 +194,7 @@ const ShowTicketReport = async ({
|
||||||
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')
|
||||||
|
|
|
@ -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);
|
||||||
|
|
||||||
|
|
|
@ -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> => {
|
||||||
|
let distinctWhatsapps: any;
|
||||||
|
|
||||||
|
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}
|
JOIN WhatsappQueues wq ON w.id = wq.whatsappId AND wq.queueId = ${queueId}
|
||||||
GROUP BY w.number;`,
|
GROUP BY w.number;`,
|
||||||
{ type: QueryTypes.SELECT }
|
{ type: QueryTypes.SELECT }
|
||||||
);
|
);
|
||||||
|
}
|
||||||
|
|
||||||
return distinctWhatsapps;
|
return distinctWhatsapps;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListWhatsAppsForQueueService;
|
export default ListWhatsAppsForQueueService;
|
||||||
|
|
|
@ -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,
|
||||||
|
@ -122,6 +130,9 @@ const ConfigModal = ({ open, onClose, change }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
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,
|
||||||
|
@ -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 />
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -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}>
|
||||||
|
|
|
@ -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(() => {
|
||||||
|
@ -289,10 +289,15 @@ const Dashboard = () => {
|
||||||
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>
|
||||||
|
|
Loading…
Reference in New Issue