Merge branch 'update_newtemper' into modulo_campanha_update_newtemper
commit
2138d70fa9
|
@ -21,8 +21,8 @@
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cookie-parser": "^1.4.5",
|
"cookie-parser": "^1.4.5",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.30.0",
|
||||||
"date-fns-tz": "^1.3.4",
|
"date-fns-tz": "^1.3.8",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-async-errors": "^3.1.1",
|
"express-async-errors": "^3.1.1",
|
||||||
|
|
|
@ -6,6 +6,8 @@ import AppError from "../errors/AppError";
|
||||||
import UpdateSettingService from "../services/SettingServices/UpdateSettingService";
|
import UpdateSettingService from "../services/SettingServices/UpdateSettingService";
|
||||||
import ListSettingsService from "../services/SettingServices/ListSettingsService";
|
import ListSettingsService from "../services/SettingServices/ListSettingsService";
|
||||||
import loadSettings from "../helpers/LoadSettings";
|
import loadSettings from "../helpers/LoadSettings";
|
||||||
|
import updateSettingTicket from "../services/SettingServices/UpdateSettingTicket";
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
|
|
||||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
// if (req.user.profile !== "master") {
|
// if (req.user.profile !== "master") {
|
||||||
|
@ -14,7 +16,76 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
const settings = await ListSettingsService();
|
const settings = await ListSettingsService();
|
||||||
|
|
||||||
return res.status(200).json(settings);
|
const config = await SettingTicket.findAll();
|
||||||
|
|
||||||
|
return res.status(200).json({ settings, config });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTicketSettings = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
const {
|
||||||
|
outBusinessHours,
|
||||||
|
ticketExpiration,
|
||||||
|
weekend,
|
||||||
|
saturday,
|
||||||
|
sunday,
|
||||||
|
holiday
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
if (outBusinessHours && Object.keys(outBusinessHours).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...outBusinessHours,
|
||||||
|
key: "outBusinessHours"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketExpiration && Object.keys(ticketExpiration).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...ticketExpiration,
|
||||||
|
key: "ticketExpiration"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weekend && Object.keys(weekend).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...weekend,
|
||||||
|
key: "weekend"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saturday && Object.keys(saturday).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...saturday,
|
||||||
|
key: "saturday"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sunday && Object.keys(sunday).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...sunday,
|
||||||
|
key: "sunday"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holiday && Object.keys(holiday).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...holiday,
|
||||||
|
key: "holiday"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
outBusinessHours,
|
||||||
|
ticketExpiration,
|
||||||
|
weekend,
|
||||||
|
saturday,
|
||||||
|
sunday,
|
||||||
|
holiday
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const update = async (
|
export const update = async (
|
||||||
|
|
|
@ -14,6 +14,7 @@ import QuickAnswer from "../models/QuickAnswer";
|
||||||
import SchedulingNotify from "../models/SchedulingNotify";
|
import SchedulingNotify from "../models/SchedulingNotify";
|
||||||
import StatusChatEnd from "../models/StatusChatEnd";
|
import StatusChatEnd from "../models/StatusChatEnd";
|
||||||
import UserOnlineTime from "../models/UserOnlineTime";
|
import UserOnlineTime from "../models/UserOnlineTime";
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const dbConfig = require("../config/database");
|
const dbConfig = require("../config/database");
|
||||||
// import dbConfig from "../config/database";
|
// import dbConfig from "../config/database";
|
||||||
|
@ -36,6 +37,7 @@ const models = [
|
||||||
SchedulingNotify,
|
SchedulingNotify,
|
||||||
StatusChatEnd,
|
StatusChatEnd,
|
||||||
UserOnlineTime,
|
UserOnlineTime,
|
||||||
|
SettingTicket
|
||||||
];
|
];
|
||||||
|
|
||||||
sequelize.addModels(models);
|
sequelize.addModels(models);
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.createTable("SettingTickets", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
startTime: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
endTime: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.dropTable("SettingTickets");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"SettingTickets",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "outBusinessHours",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "ticketExpiration",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { QueryInterface } from "sequelize"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"SettingTickets",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "saturday",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "sunday",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {})
|
||||||
|
}
|
||||||
|
}
|
|
@ -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: "holiday",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -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: "weekend",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,84 @@
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
|
import ListTicketTimeLife from "../services/TicketServices/ListTicketTimeLife";
|
||||||
|
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
||||||
|
import BotIsOnQueue from "./BotIsOnQueue";
|
||||||
|
|
||||||
|
import {
|
||||||
|
format as _format,
|
||||||
|
isWithinInterval,
|
||||||
|
parse,
|
||||||
|
subMinutes
|
||||||
|
} from "date-fns";
|
||||||
|
|
||||||
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
import { splitDateTime } from "./SplitDateTime";
|
||||||
|
|
||||||
|
const fsPromises = require("fs/promises");
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
let timer: any;
|
||||||
|
|
||||||
|
const AutoCloseTickets = async () => {
|
||||||
|
try {
|
||||||
|
// const botInfo = await BotIsOnQueue('botqueue')
|
||||||
|
|
||||||
|
// if (!botInfo.userIdBot) return
|
||||||
|
|
||||||
|
const ticketExpiration = await SettingTicket.findOne({
|
||||||
|
where: { key: "ticketExpiration" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ticketExpiration && ticketExpiration.value == "enabled") {
|
||||||
|
const startTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(ticketExpiration.startTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const seconds = timeStringToSeconds(startTime.fullTime);
|
||||||
|
|
||||||
|
// console.log("Ticket seconds: ", seconds);
|
||||||
|
|
||||||
|
let tickets: any = await ListTicketTimeLife({
|
||||||
|
timeseconds: seconds,
|
||||||
|
status: "open"
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log("tickets: ", tickets);
|
||||||
|
|
||||||
|
for (let i = 0; i < tickets.length; i++) {
|
||||||
|
|
||||||
|
await UpdateTicketService({
|
||||||
|
ticketData: { status: "closed", statusChatEnd: "FINALIZADO" },
|
||||||
|
ticketId: tickets[i].ticket_id,
|
||||||
|
msg: ticketExpiration.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("There was an error on try close the bot tickets: ", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function timeStringToSeconds(timeString: any) {
|
||||||
|
const [hours, minutes, seconds] = timeString.split(":").map(Number);
|
||||||
|
return hours * 3600 + minutes * 60 + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedule = async () => {
|
||||||
|
try {
|
||||||
|
clearInterval(timer);
|
||||||
|
|
||||||
|
await AutoCloseTickets();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error on schedule: ", error);
|
||||||
|
} finally {
|
||||||
|
timer = setInterval(schedule, 60000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
timer = setInterval(schedule, 60000);
|
||||||
|
|
||||||
|
export default schedule;
|
|
@ -0,0 +1,56 @@
|
||||||
|
import { subSeconds } from "date-fns";
|
||||||
|
import Message from "../models/Message";
|
||||||
|
|
||||||
|
import { Op, Sequelize } from "sequelize";
|
||||||
|
|
||||||
|
const mostRepeatedPhrase = async (
|
||||||
|
ticketId: number | string,
|
||||||
|
fromMe: boolean = false
|
||||||
|
) => {
|
||||||
|
let res: any = { body: "", occurrences: 0 };
|
||||||
|
|
||||||
|
try {
|
||||||
|
const mostRepeatedPhrase: any = await Message.findOne({
|
||||||
|
where: {
|
||||||
|
ticketId: ticketId,
|
||||||
|
fromMe: fromMe ? fromMe : 0,
|
||||||
|
body: {
|
||||||
|
[Op.notRegexp]: "^[0-9]+$"
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
[Op.between]: [+subSeconds(new Date(), 150), +new Date()]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
attributes: [
|
||||||
|
"body",
|
||||||
|
[Sequelize.fn("COUNT", Sequelize.col("body")), "occurrences"]
|
||||||
|
],
|
||||||
|
|
||||||
|
group: ["body"],
|
||||||
|
order: [[Sequelize.literal("occurrences"), "DESC"]],
|
||||||
|
limit: 1
|
||||||
|
});
|
||||||
|
|
||||||
|
if (mostRepeatedPhrase) {
|
||||||
|
const { body, occurrences } = mostRepeatedPhrase.get();
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`The most repeated phrase is "${body}" with ${occurrences} occurrences.`
|
||||||
|
);
|
||||||
|
|
||||||
|
const isNumber = /^\d+$/.test(body.trim());
|
||||||
|
|
||||||
|
if (!isNumber) {
|
||||||
|
return { body, occurrences };
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
console.log("No phrases found.");
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error on MostRepeatedPhrase: ", error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { body: "", occurrences: 0 };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default mostRepeatedPhrase;
|
|
@ -0,0 +1,183 @@
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
|
import { splitDateTime } from "./SplitDateTime";
|
||||||
|
import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
|
||||||
|
|
||||||
|
import {
|
||||||
|
format as _format,
|
||||||
|
isWithinInterval,
|
||||||
|
parse,
|
||||||
|
subMinutes,
|
||||||
|
isSaturday,
|
||||||
|
isSunday,
|
||||||
|
parseISO
|
||||||
|
} from "date-fns";
|
||||||
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
|
||||||
|
const isHoliday = async () => {
|
||||||
|
let obj = { set: false, msg: "" };
|
||||||
|
|
||||||
|
const holiday = await SettingTicket.findOne({
|
||||||
|
where: { key: "holiday" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
holiday &&
|
||||||
|
holiday.value == "enabled" &&
|
||||||
|
holiday.message?.trim()?.length > 0
|
||||||
|
) {
|
||||||
|
const startTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(holiday.startTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentDate = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentDate.fullDate == startTime.fullDate) {
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = holiday.message.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWeekend = async () => {
|
||||||
|
let obj = { set: false, msg: "" };
|
||||||
|
|
||||||
|
const weekend = await SettingTicket.findOne({
|
||||||
|
where: { key: "weekend" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
weekend &&
|
||||||
|
weekend.value == "enabled" &&
|
||||||
|
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
|
||||||
|
const localDate = utcToZonedTime(parsedDate, brazilTimeZone);
|
||||||
|
|
||||||
|
// Check if it's Saturday or Sunday
|
||||||
|
if (isSaturday(localDate)) {
|
||||||
|
const saturday = await SettingTicket.findOne({
|
||||||
|
where: { key: "saturday" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (saturday && saturday.value == "enabled") {
|
||||||
|
// botSendMessage(ticket, weekend.message);
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = weekend.message;
|
||||||
|
}
|
||||||
|
} else if (isSunday(localDate)) {
|
||||||
|
const sunday = await SettingTicket.findOne({
|
||||||
|
where: { key: "sunday" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sunday && sunday.value == "enabled") {
|
||||||
|
// botSendMessage(ticket, weekend.message);
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = weekend.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// obj.set = true;
|
||||||
|
// obj.msg = weekend.message;
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function isOutBusinessTime() {
|
||||||
|
let obj = { set: false, msg: "" };
|
||||||
|
|
||||||
|
const outBusinessHours = await SettingTicket.findOne({
|
||||||
|
where: { key: "outBusinessHours" }
|
||||||
|
});
|
||||||
|
|
||||||
|
let isWithinRange = false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
outBusinessHours &&
|
||||||
|
outBusinessHours.value == "enabled" &&
|
||||||
|
outBusinessHours?.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(outBusinessHours.startTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const endTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(outBusinessHours.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 = outBusinessHours.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
isWeekend,
|
||||||
|
isHoliday,
|
||||||
|
isOutBusinessTime
|
||||||
|
};
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
Column,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
Model,
|
||||||
|
PrimaryKey,
|
||||||
|
AutoIncrement
|
||||||
|
} from "sequelize-typescript";
|
||||||
|
|
||||||
|
@Table
|
||||||
|
class SettingTicket extends Model<SettingTicket> {
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
startTime: Date;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
endTime: Date;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdatedAt
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingTicket;
|
|
@ -9,7 +9,15 @@ settingRoutes.get("/settings", SettingController.index);
|
||||||
|
|
||||||
// routes.get("/settings/:settingKey", isAuth, SettingsController.show);
|
// routes.get("/settings/:settingKey", isAuth, SettingsController.show);
|
||||||
|
|
||||||
|
settingRoutes.put(
|
||||||
|
"/settings/ticket",
|
||||||
|
isAuth,
|
||||||
|
SettingController.updateTicketSettings
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// change setting key to key in future
|
// change setting key to key in future
|
||||||
settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update);
|
settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update);
|
||||||
|
|
||||||
|
|
||||||
export default settingRoutes;
|
export default settingRoutes;
|
||||||
|
|
|
@ -10,6 +10,7 @@ import { cacheSize, flushCache, loadTicketsCache } from "./helpers/TicketCache";
|
||||||
import { loadContactsCache } from "./helpers/ContactsCache";
|
import { loadContactsCache } from "./helpers/ContactsCache";
|
||||||
import { loadSchedulesCache } from "./helpers/SchedulingNotifyCache";
|
import { loadSchedulesCache } from "./helpers/SchedulingNotifyCache";
|
||||||
import { delRestoreControllFile } from "./helpers/RestoreControll";
|
import { delRestoreControllFile } from "./helpers/RestoreControll";
|
||||||
|
import "./helpers/AutoCloseTickets";
|
||||||
|
|
||||||
import "./helpers/SchedulingNotifySendMessage";
|
import "./helpers/SchedulingNotifySendMessage";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import SettingTicket from "../../models/SettingTicket";
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
key: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
value: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSettingTicket = async ({
|
||||||
|
key,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
value,
|
||||||
|
message
|
||||||
|
}: Request): Promise<SettingTicket | undefined> => {
|
||||||
|
try {
|
||||||
|
const businessHours = await SettingTicket.findOne({ where: { key } });
|
||||||
|
|
||||||
|
if (!businessHours) {
|
||||||
|
throw new AppError("ERR_NO_SETTING_FOUND", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await businessHours.update({ startTime, endTime, message, value });
|
||||||
|
|
||||||
|
return businessHours;
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("===> Error on UpdateSettingService.ts file: \n", error);
|
||||||
|
throw new AppError(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateSettingTicket;
|
|
@ -0,0 +1,47 @@
|
||||||
|
|
||||||
|
import { Sequelize, } from "sequelize";
|
||||||
|
|
||||||
|
const dbConfig = require("../../config/database");
|
||||||
|
const sequelize = new Sequelize(dbConfig);
|
||||||
|
const { QueryTypes } = require('sequelize');
|
||||||
|
|
||||||
|
import { splitDateTime } from "../../helpers/SplitDateTime";
|
||||||
|
import format from 'date-fns/format';
|
||||||
|
import ptBR from 'date-fns/locale/pt-BR';
|
||||||
|
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
timeseconds: string | number;
|
||||||
|
status: string;
|
||||||
|
userId?: string | number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ListTicketTimeLife = async ({timeseconds, status, userId }: Request): Promise<any[]> => {
|
||||||
|
|
||||||
|
let tickets = []
|
||||||
|
|
||||||
|
let currentDate = format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })
|
||||||
|
|
||||||
|
// console.log('------------------> currentDate: ', currentDate)
|
||||||
|
|
||||||
|
if (userId) {
|
||||||
|
// CONSULTANDO FILAS PELO ID DO USUARIO
|
||||||
|
tickets = await sequelize.query(`select user.id as user_id, user.name as user_name, t.id as ticket_id from Tickets as t inner join Users as user on
|
||||||
|
t.userId = user.id and user.name = 'botqueue' and t.status='${status}' and (TIMESTAMPDIFF(SECOND, t.updatedAt, '${currentDate}')) >= ${timeseconds};`, { type: QueryTypes.SELECT });
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
// CONSULTANDO FILAS PELO USUARIO
|
||||||
|
tickets = await sequelize.query(`select id as ticket_id from Tickets where status='${status}' and
|
||||||
|
(TIMESTAMPDIFF(SECOND, updatedAt, '${currentDate}')) >= ${timeseconds};`, { type: QueryTypes.SELECT });
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
return tickets;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListTicketTimeLife;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -68,7 +68,8 @@ const UpdateTicketService = async ({
|
||||||
|
|
||||||
await ticket.reload();
|
await ticket.reload();
|
||||||
|
|
||||||
if (msg.length > 0) {
|
if (msg?.trim().length > 0) {
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
sendWhatsAppMessageSocket(ticket, msg);
|
sendWhatsAppMessageSocket(ticket, msg);
|
||||||
}, 2000);
|
}, 2000);
|
||||||
|
|
|
@ -7,9 +7,25 @@ import { copyFolder } from "../../helpers/CopyFolder";
|
||||||
import { removeDir } from "../../helpers/DeleteDirectory";
|
import { removeDir } from "../../helpers/DeleteDirectory";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { format } from "date-fns";
|
import {
|
||||||
|
isHoliday,
|
||||||
|
isOutBusinessTime,
|
||||||
|
isWeekend
|
||||||
|
} from "../../helpers/TicketConfig";
|
||||||
|
|
||||||
|
import {
|
||||||
|
format as _format,
|
||||||
|
isWithinInterval,
|
||||||
|
parse,
|
||||||
|
subMinutes,
|
||||||
|
isSaturday,
|
||||||
|
isSunday,
|
||||||
|
parseISO
|
||||||
|
} from "date-fns";
|
||||||
import ptBR from "date-fns/locale/pt-BR";
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
|
||||||
|
import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Contact as WbotContact,
|
Contact as WbotContact,
|
||||||
Message as WbotMessage,
|
Message as WbotMessage,
|
||||||
|
@ -69,6 +85,9 @@ import { getSettingValue } from "../../helpers/WhaticketSettings";
|
||||||
|
|
||||||
import { Op } from "sequelize";
|
import { Op } from "sequelize";
|
||||||
|
|
||||||
|
import SettingTicket from "../../models/SettingTicket";
|
||||||
|
import mostRepeatedPhrase from "../../helpers/MostRepeatedPhrase";
|
||||||
|
|
||||||
var lst: any[] = getWhatsappIds();
|
var lst: any[] = getWhatsappIds();
|
||||||
|
|
||||||
interface Session extends Client {
|
interface Session extends Client {
|
||||||
|
@ -245,27 +264,6 @@ const verifyQueue = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (choosenQueue) {
|
if (choosenQueue) {
|
||||||
// TEST DEL
|
|
||||||
// let _ticket = await Ticket.findOne({
|
|
||||||
// where: {
|
|
||||||
// status: {
|
|
||||||
// [Op.or]: ["open", "pending", "queueChoice"]
|
|
||||||
// },
|
|
||||||
// contactId: contact.id,
|
|
||||||
// queueId: choosenQueue.id
|
|
||||||
// }
|
|
||||||
// });
|
|
||||||
|
|
||||||
// if (_ticket) {
|
|
||||||
// await UpdateTicketService({
|
|
||||||
// ticketData: { queueId: choosenQueue.id },
|
|
||||||
// ticketId: ticket.id
|
|
||||||
// });
|
|
||||||
|
|
||||||
// return;
|
|
||||||
// }
|
|
||||||
//
|
|
||||||
|
|
||||||
// Atualizando o status do ticket para mostrar notificação para o atendente da fila escolhida pelo usuário. De queueChoice para pending
|
// Atualizando o status do ticket para mostrar notificação para o atendente da fila escolhida pelo usuário. De queueChoice para pending
|
||||||
if (queues.length > 1 && !botInfo.isOnQueue) {
|
if (queues.length > 1 && !botInfo.isOnQueue) {
|
||||||
await ticket.update({ status: "pending" });
|
await ticket.update({ status: "pending" });
|
||||||
|
@ -306,12 +304,20 @@ const verifyQueue = async (
|
||||||
sendWhatsAppMessageSocket(ticket, body);
|
sendWhatsAppMessageSocket(ticket, body);
|
||||||
} else {
|
} else {
|
||||||
//test del transfere o atendimento se entrar na ura infinita
|
//test del transfere o atendimento se entrar na ura infinita
|
||||||
let ticket_message = await ShowTicketMessage(ticket.id, false);
|
const repet: any = await mostRepeatedPhrase(ticket.id);
|
||||||
if (ticket_message.length > 10) {
|
|
||||||
|
if (repet.occurrences > 4) {
|
||||||
await UpdateTicketService({
|
await UpdateTicketService({
|
||||||
ticketData: { status: "pending", queueId: queues[0].id },
|
ticketData: { status: "pending", queueId: queues[0].id },
|
||||||
ticketId: ticket.id
|
ticketId: ticket.id
|
||||||
});
|
});
|
||||||
|
|
||||||
|
await SendWhatsAppMessage({
|
||||||
|
body: `Seu atendimento foi transferido para um agente!
|
||||||
|
`,
|
||||||
|
ticket,
|
||||||
|
number: `${contact.number}@c.us`
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
let options = "";
|
let options = "";
|
||||||
|
|
||||||
|
@ -379,19 +385,15 @@ const botTransferTicket = async (
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
const botSendMessage = (
|
const botSendMessage = (ticket: Ticket, msg: string) => {
|
||||||
ticket: Ticket,
|
|
||||||
contact: Contact,
|
|
||||||
wbot: Session,
|
|
||||||
msg: string
|
|
||||||
) => {
|
|
||||||
const debouncedSentMessage = debounce(
|
const debouncedSentMessage = debounce(
|
||||||
async () => {
|
async () => {
|
||||||
const sentMessage = await wbot.sendMessage(
|
//OLD
|
||||||
`${contact.number}@c.us`,
|
// const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, `${msg}`);
|
||||||
`${msg}`
|
// verifyMessage(sentMessage, ticket, contact);
|
||||||
);
|
|
||||||
verifyMessage(sentMessage, ticket, contact);
|
//NEW
|
||||||
|
await SendWhatsAppMessage({ body: msg, ticket });
|
||||||
},
|
},
|
||||||
3000,
|
3000,
|
||||||
ticket.id
|
ticket.id
|
||||||
|
@ -400,6 +402,27 @@ const botSendMessage = (
|
||||||
debouncedSentMessage();
|
debouncedSentMessage();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// const botSendMessage = (
|
||||||
|
// ticket: Ticket,
|
||||||
|
// contact: Contact,
|
||||||
|
// wbot: Session,
|
||||||
|
// msg: string
|
||||||
|
// ) => {
|
||||||
|
// const debouncedSentMessage = debounce(
|
||||||
|
// async () => {
|
||||||
|
// const sentMessage = await wbot.sendMessage(
|
||||||
|
// `${contact.number}@c.us`,
|
||||||
|
// `${msg}`
|
||||||
|
// );
|
||||||
|
// verifyMessage(sentMessage, ticket, contact);
|
||||||
|
// },
|
||||||
|
// 3000,
|
||||||
|
// ticket.id
|
||||||
|
// );
|
||||||
|
|
||||||
|
// debouncedSentMessage();
|
||||||
|
// };
|
||||||
|
|
||||||
const _clear_lst = () => {
|
const _clear_lst = () => {
|
||||||
console.log("THE lst.length: ", lst.length);
|
console.log("THE lst.length: ", lst.length);
|
||||||
|
|
||||||
|
@ -436,12 +459,6 @@ const handleMessage = async (msg: any, wbot: any): Promise<void> => {
|
||||||
|
|
||||||
// console.log('LIST OF ID MESSAGE lst: ', lst)
|
// console.log('LIST OF ID MESSAGE lst: ', lst)
|
||||||
|
|
||||||
console.log(
|
|
||||||
"PASSOU.................................FROM: ",
|
|
||||||
msg.from.split("@")[0],
|
|
||||||
" | ID: ",
|
|
||||||
msg.id.id
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!isValidMsg(msg)) {
|
if (!isValidMsg(msg)) {
|
||||||
|
@ -453,6 +470,19 @@ const handleMessage = async (msg: any, wbot: any): Promise<void> => {
|
||||||
// let groupContact: Contact | undefined;
|
// let groupContact: Contact | undefined;
|
||||||
|
|
||||||
if (msg.fromMe) {
|
if (msg.fromMe) {
|
||||||
|
const ticketExpiration = await SettingTicket.findOne({
|
||||||
|
where: { key: "ticketExpiration" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
ticketExpiration &&
|
||||||
|
ticketExpiration.value == "enabled" &&
|
||||||
|
ticketExpiration?.message.trim() == msg.body.trim()
|
||||||
|
) {
|
||||||
|
console.log("*********** TICKET EXPIRATION");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// console.log('FROM ME: ', msg.fromMe, ' | /\u200e/.test(msg.body[0]: ', (/\u200e/.test(msg.body[0])))
|
// console.log('FROM ME: ', msg.fromMe, ' | /\u200e/.test(msg.body[0]: ', (/\u200e/.test(msg.body[0])))
|
||||||
|
|
||||||
// messages sent automatically by wbot have a special character in front of it
|
// messages sent automatically by wbot have a special character in front of it
|
||||||
|
@ -490,7 +520,6 @@ const handleMessage = async (msg: any, wbot: any): Promise<void> => {
|
||||||
msg.from: ${msg.from}
|
msg.from: ${msg.from}
|
||||||
msg.to: ${msg.to}\n`);
|
msg.to: ${msg.to}\n`);
|
||||||
}
|
}
|
||||||
|
|
||||||
// const chat = await msg.getChat();
|
// const chat = await msg.getChat();
|
||||||
const chat = wbot.chat;
|
const chat = wbot.chat;
|
||||||
|
|
||||||
|
@ -587,343 +616,61 @@ const handleMessage = async (msg: any, wbot: any): Promise<void> => {
|
||||||
|
|
||||||
//Habilitar esse caso queira usar o bot
|
//Habilitar esse caso queira usar o bot
|
||||||
// const botInfo = await BotIsOnQueue('botqueue')
|
// const botInfo = await BotIsOnQueue('botqueue')
|
||||||
const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 };
|
// const botInfo = { isOnQueue: false, botQueueId: 0, userIdBot: 0 };
|
||||||
|
|
||||||
if (
|
|
||||||
botInfo.isOnQueue &&
|
|
||||||
!msg.fromMe &&
|
|
||||||
ticket.userId == botInfo.userIdBot
|
|
||||||
) {
|
|
||||||
if (msg.body === "0") {
|
|
||||||
const queue = await ShowQueueService(ticket.queue.id);
|
|
||||||
|
|
||||||
const greetingMessage = `\u200e${queue.greetingMessage}`;
|
|
||||||
|
|
||||||
let options = "";
|
|
||||||
|
|
||||||
data_ura.forEach((s, index) => {
|
|
||||||
options += `*${index + 1}* - ${s.option}\n`;
|
|
||||||
});
|
|
||||||
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`${greetingMessage}\n\n${options}\n${final_message.msg}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
// Pega as ultimas 9 opções numericas digitadas pelo cliente em orde DESC
|
|
||||||
// Consulta apenas mensagens do usuári
|
|
||||||
|
|
||||||
let lastOption = "";
|
|
||||||
|
|
||||||
let ura_length = data_ura.length;
|
|
||||||
|
|
||||||
let indexAttendant = data_ura.findIndex(u => u.atendente);
|
|
||||||
|
|
||||||
let opt_user_attendant = "-1";
|
|
||||||
|
|
||||||
if (indexAttendant != -1) {
|
|
||||||
opt_user_attendant = data_ura[indexAttendant].id;
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
let ticket_message = await ShowTicketMessage(
|
|
||||||
ticket.id,
|
|
||||||
true,
|
|
||||||
ura_length,
|
|
||||||
`^[0-${ura_length}}]$`
|
|
||||||
);
|
|
||||||
|
|
||||||
if (ticket_message.length > 1) {
|
|
||||||
lastOption = ticket_message[1].body;
|
|
||||||
|
|
||||||
const queuesWhatsGreetingMessage = await queuesOutBot(
|
|
||||||
wbot,
|
|
||||||
botInfo.botQueueId
|
|
||||||
);
|
|
||||||
|
|
||||||
const queues = queuesWhatsGreetingMessage.queues;
|
|
||||||
|
|
||||||
if (queues.length > 1) {
|
|
||||||
const index_opt_user_attendant = ticket_message.findIndex(
|
|
||||||
q => q.body == opt_user_attendant
|
|
||||||
);
|
|
||||||
const index0 = ticket_message.findIndex(q => q.body == "0");
|
|
||||||
|
|
||||||
if (index_opt_user_attendant != -1) {
|
|
||||||
if (index0 > -1 && index0 < index_opt_user_attendant) {
|
|
||||||
lastOption = "";
|
|
||||||
} else {
|
|
||||||
lastOption = opt_user_attendant;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
//
|
|
||||||
|
|
||||||
// È numero
|
|
||||||
if (
|
|
||||||
!Number.isNaN(Number(msg.body.trim())) &&
|
|
||||||
+msg.body >= 0 &&
|
|
||||||
+msg.body <= data_ura.length
|
|
||||||
) {
|
|
||||||
const indexUra = data_ura.findIndex(ura => ura.id == msg.body.trim());
|
|
||||||
|
|
||||||
if (indexUra != -1) {
|
|
||||||
if (
|
|
||||||
data_ura[indexUra].id != opt_user_attendant &&
|
|
||||||
lastOption != opt_user_attendant
|
|
||||||
) {
|
|
||||||
// test del
|
|
||||||
let next = true;
|
|
||||||
|
|
||||||
let indexAux = ticket_message.findIndex(e => e.body == "0");
|
|
||||||
|
|
||||||
let listMessage = null;
|
|
||||||
|
|
||||||
if (indexAux != -1) {
|
|
||||||
listMessage = ticket_message.slice(0, indexAux);
|
|
||||||
} else {
|
|
||||||
listMessage = ticket_message;
|
|
||||||
}
|
|
||||||
|
|
||||||
let id = "";
|
|
||||||
let subUra = null;
|
|
||||||
|
|
||||||
if (listMessage.length > 1) {
|
|
||||||
id = listMessage[listMessage.length - 1].body;
|
|
||||||
subUra = data_ura.filter(e => e.id == id)[0];
|
|
||||||
|
|
||||||
if (
|
|
||||||
subUra &&
|
|
||||||
(!subUra.subOptions || subUra.subOptions.length == 0)
|
|
||||||
) {
|
|
||||||
listMessage.pop();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (listMessage.length > 1) {
|
|
||||||
id = listMessage[listMessage.length - 1].body;
|
|
||||||
subUra = data_ura.filter(e => e.id == id)[0];
|
|
||||||
|
|
||||||
if (subUra.subOptions && subUra.subOptions.length > 0) {
|
|
||||||
if (
|
|
||||||
!Number.isNaN(Number(msg.body.trim())) &&
|
|
||||||
+msg.body >= 0 &&
|
|
||||||
+msg.body <= subUra.subOptions?.length &&
|
|
||||||
subUra.subOptions
|
|
||||||
) {
|
|
||||||
if (subUra.subOptions[+msg.body - 1].responseToClient) {
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`*${subUra.option}*\n\n${
|
|
||||||
subUra.subOptions[+msg.body - 1].responseToClient
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`*${subUra.option}*\n\n${
|
|
||||||
subUra.subOptions[+msg.body - 1].subOpt
|
|
||||||
}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
const queuesWhatsGreetingMessage = await queuesOutBot(
|
|
||||||
wbot,
|
|
||||||
botInfo.botQueueId
|
|
||||||
);
|
|
||||||
|
|
||||||
const queues = queuesWhatsGreetingMessage.queues;
|
|
||||||
|
|
||||||
if (queues.length > 0) {
|
|
||||||
await botTransferTicket(queues[0], ticket, contact, wbot);
|
|
||||||
} else {
|
|
||||||
console.log("NO QUEUE!");
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
let options = "";
|
|
||||||
let subOptions: any[] = subUra.subOptions;
|
|
||||||
|
|
||||||
subOptions?.forEach((s, index) => {
|
|
||||||
options += `*${index + 1}* - ${s.subOpt}\n`;
|
|
||||||
});
|
|
||||||
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`*${subUra.option}*\n\nDigite um número válido disponível no menu de opções de atendimento abaixo: \n${options}\n\n*0* - Voltar ao menu principal`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
next = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
//
|
|
||||||
if (next) {
|
|
||||||
if (
|
|
||||||
data_ura[indexUra].subOptions &&
|
|
||||||
data_ura[indexUra].subOptions.length > 0
|
|
||||||
) {
|
|
||||||
let options = "";
|
|
||||||
let option = data_ura[indexUra].option;
|
|
||||||
let subOptions: any[] = data_ura[indexUra].subOptions;
|
|
||||||
let description = data_ura[indexUra].description;
|
|
||||||
|
|
||||||
subOptions?.forEach((s, index) => {
|
|
||||||
options += `*${index + 1}* - ${s.subOpt}\n`;
|
|
||||||
});
|
|
||||||
|
|
||||||
const body = `\u200e${description}:\n${options}`;
|
|
||||||
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`*${option}*\n\n${body}\n\n *0* - Voltar ao menu principal`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
//test del deletar isso (Usar somente na hit)
|
|
||||||
if (data_ura[indexUra].closeChat) {
|
|
||||||
const { ticket: res } = await UpdateTicketService({
|
|
||||||
ticketData: {
|
|
||||||
status: "closed",
|
|
||||||
userId: botInfo.userIdBot
|
|
||||||
},
|
|
||||||
ticketId: ticket.id
|
|
||||||
});
|
|
||||||
|
|
||||||
///////////////////////////////
|
|
||||||
const whatsapp = await ShowWhatsAppService(
|
|
||||||
ticket.whatsappId
|
|
||||||
);
|
|
||||||
|
|
||||||
const { farewellMessage } = whatsapp;
|
|
||||||
|
|
||||||
if (farewellMessage) {
|
|
||||||
await SendWhatsAppMessage({
|
|
||||||
body: farewellMessage,
|
|
||||||
ticket: res
|
|
||||||
});
|
|
||||||
}
|
|
||||||
///////////////////////////////
|
|
||||||
} else {
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`${data_ura[indexUra].description}\n\n *0* - Voltar ao menu principal`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
//
|
|
||||||
|
|
||||||
// botSendMessage(ticket, contact, wbot, `${data_ura[indexUra].description}\n\n *0* - Voltar ao menu principal`)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (data_ura[indexUra].id == opt_user_attendant) {
|
|
||||||
const queuesWhatsGreetingMessage = await queuesOutBot(
|
|
||||||
wbot,
|
|
||||||
botInfo.botQueueId
|
|
||||||
);
|
|
||||||
|
|
||||||
const queues = queuesWhatsGreetingMessage.queues;
|
|
||||||
|
|
||||||
// Se fila for maior que 1 exibi as opções fila para atendimento humano
|
|
||||||
if (queues.length > 1) {
|
|
||||||
let options = "";
|
|
||||||
|
|
||||||
queues.forEach((queue, index) => {
|
|
||||||
options += `*${index + 1}* - ${queue.name}\n`;
|
|
||||||
});
|
|
||||||
|
|
||||||
const body = `\u200eSelecione uma das opções de atendimento abaixo:\n${options}`;
|
|
||||||
|
|
||||||
botSendMessage(ticket, contact, wbot, body);
|
|
||||||
} // Para situações onde há apenas uma fila com exclusão da fila do bot, já direciona o cliente para essa fila de atendimento humano
|
|
||||||
else if (queues.length == 1) {
|
|
||||||
await botTransferTicket(queues[0], ticket, contact, wbot);
|
|
||||||
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`${msg_client_transfer.msg}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else if (lastOption == opt_user_attendant) {
|
|
||||||
const queuesWhatsGreetingMessage = await queuesOutBot(
|
|
||||||
wbot,
|
|
||||||
botInfo.botQueueId
|
|
||||||
);
|
|
||||||
|
|
||||||
const queues = queuesWhatsGreetingMessage.queues;
|
|
||||||
|
|
||||||
// É numero
|
|
||||||
if (
|
|
||||||
!Number.isNaN(Number(msg.body.trim())) &&
|
|
||||||
+msg.body >= 0 &&
|
|
||||||
+msg.body <= queues.length
|
|
||||||
) {
|
|
||||||
await botTransferTicket(
|
|
||||||
queues[+msg.body - 1],
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot
|
|
||||||
);
|
|
||||||
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`${msg_client_transfer.msg}`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`Digite um número válido disponível no menu de opções de atendimento\n\n*0* - Voltar ao menu principal`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// É numero
|
|
||||||
if (!Number.isNaN(Number(msg.body.trim()))) {
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`Opção numérica inválida!\nDigite um dos números mostrados no menu de opções\n\n*0* - Voltar ao menu principal`
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
botSendMessage(
|
|
||||||
ticket,
|
|
||||||
contact,
|
|
||||||
wbot,
|
|
||||||
`Digite um número válido disponível no menu de opções\n\n*0* - Voltar ao menu principal`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (msg && !msg.fromMe && ticket.status == "pending") {
|
if (msg && !msg.fromMe && ticket.status == "pending") {
|
||||||
await setMessageAsRead(ticket);
|
await setMessageAsRead(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ticketHasQueue = false;
|
||||||
|
|
||||||
|
if (ticket?.queueId) {
|
||||||
|
ticketHasQueue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketHasQueue) {
|
||||||
|
// MESSAGE TO HOLIDAY
|
||||||
|
const holiday: any = await isHoliday();
|
||||||
|
|
||||||
|
if (holiday.set) {
|
||||||
|
if (msg.fromMe && holiday.msg == msg.body) {
|
||||||
|
console.log("HOLIDAY MESSAGE IGNORED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botSendMessage(ticket, holiday.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MESSAGES TO SATURDAY OR SUNDAY
|
||||||
|
const weekend: any = await isWeekend();
|
||||||
|
|
||||||
|
if (weekend.set) {
|
||||||
|
if (msg.fromMe && weekend.msg == msg.body) {
|
||||||
|
console.log("WEEKEND MESSAGE IGNORED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botSendMessage(ticket, weekend.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MESSAGE TO BUSINESS TIME
|
||||||
|
const businessTime = await isOutBusinessTime();
|
||||||
|
|
||||||
|
if (businessTime.set) {
|
||||||
|
if (msg.fromMe && businessTime.msg == msg.body) {
|
||||||
|
console.log("BUSINESS TIME MESSAGE IGNORED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botSendMessage(ticket, businessTime.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
|
console.log("Error handling whatsapp message: Err: ", err);
|
||||||
logger.error(`Error handling whatsapp message: Err: ${err}`);
|
logger.error(`Error handling whatsapp message: Err: ${err}`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-io/date-fns": "^1.3.13",
|
"@date-io/date-fns": "^1.3.13",
|
||||||
"@emotion/react": "^11.7.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/styled": "^11.6.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@material-ui/core": "^4.12.1",
|
"@material-ui/core": "^4.12.1",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.56",
|
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||||
"@material-ui/pickers": "^3.3.10",
|
"@material-ui/pickers": "^3.3.10",
|
||||||
"@mui/material": "^5.3.0",
|
"@mui/material": "^5.14.4",
|
||||||
"@mui/x-data-grid": "^5.3.0",
|
"@mui/x-data-grid": "^5.3.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.0.4",
|
"@testing-library/react": "^11.0.4",
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"emoji-mart": "^3.0.1",
|
"emoji-mart": "^3.0.1",
|
||||||
"formik": "^2.2.0",
|
"formik": "^2.2.0",
|
||||||
|
"formik-material-ui-pickers": "^1.0.0-alpha.1",
|
||||||
"i18next": "^19.8.2",
|
"i18next": "^19.8.2",
|
||||||
"i18next-browser-languagedetector": "^6.0.1",
|
"i18next-browser-languagedetector": "^6.0.1",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-csv": "^2.2.2",
|
"react-csv": "^2.2.2",
|
||||||
|
"react-datepicker": "^4.16.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-modal-image": "^2.5.0",
|
"react-modal-image": "^2.5.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
|
|
@ -0,0 +1,449 @@
|
||||||
|
import React, { useState, useEffect, } from 'react'
|
||||||
|
// import * as Yup from 'yup'
|
||||||
|
import { Formik, Form, Field, } from 'formik'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { green } from '@material-ui/core/colors'
|
||||||
|
|
||||||
|
import { TimePicker, DatePicker } from 'formik-material-ui-pickers'
|
||||||
|
|
||||||
|
import DateFnsUtils from '@date-io/date-fns'
|
||||||
|
|
||||||
|
import ptBrLocale from "date-fns/locale/pt-BR"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
MuiPickersUtilsProvider,
|
||||||
|
} from '@material-ui/pickers'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Button,
|
||||||
|
DialogActions,
|
||||||
|
CircularProgress,
|
||||||
|
TextField,
|
||||||
|
Switch,
|
||||||
|
FormControlLabel,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
|
import api from '../../services/api'
|
||||||
|
import { i18n } from '../../translate/i18n'
|
||||||
|
import toastError from '../../errors/toastError'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
|
||||||
|
multFieldLine: {
|
||||||
|
display: 'flex',
|
||||||
|
'& > *:not(:last-child)': {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
btnWrapper: {
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonProgress: {
|
||||||
|
color: green[500],
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
marginTop: -12,
|
||||||
|
marginLeft: -12,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// const SessionSchema = Yup.object().shape({
|
||||||
|
// name: Yup.string()
|
||||||
|
// .min(2, 'Too Short!')
|
||||||
|
// .max(100, 'Too Long!')
|
||||||
|
// .required('Required'),
|
||||||
|
// })
|
||||||
|
|
||||||
|
const ConfigModal = ({ open, onClose, change }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const initialState = {
|
||||||
|
startTimeBus: new Date(),
|
||||||
|
endTimeBus: new Date(),
|
||||||
|
messageBus: '',
|
||||||
|
businessTimeEnable: false,
|
||||||
|
ticketTimeExpiration: new Date(),
|
||||||
|
ticketExpirationMsg: '',
|
||||||
|
ticketExpirationEnable: false,
|
||||||
|
holidayDate: new Date(),
|
||||||
|
holidayDateEnable: false,
|
||||||
|
holidayDateMessage: '',
|
||||||
|
checkboxSundayValue: false,
|
||||||
|
checkboxSaturdayValue: false,
|
||||||
|
weekendMessage: '',
|
||||||
|
enableWeekendMessage: false
|
||||||
|
}
|
||||||
|
|
||||||
|
const [config, setConfig] = useState(initialState)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSession = async () => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await api.get('/settings')
|
||||||
|
|
||||||
|
const outBusinessHours = data.config.find((c) => c.key === "outBusinessHours")
|
||||||
|
const ticketExpiration = data.config.find((c) => c.key === "ticketExpiration")
|
||||||
|
const saturday = data.config.find((c) => c.key === "saturday")
|
||||||
|
const sunday = data.config.find((c) => c.key === "sunday")
|
||||||
|
const weekend = data.config.find((c) => c.key === "weekend")
|
||||||
|
const holiday = data.config.find((c) => c.key === "holiday")
|
||||||
|
|
||||||
|
setConfig({
|
||||||
|
startTimeBus: outBusinessHours.startTime,
|
||||||
|
endTimeBus: outBusinessHours.endTime,
|
||||||
|
messageBus: outBusinessHours.message,
|
||||||
|
businessTimeEnable: outBusinessHours.value === 'enabled' ? true : false,
|
||||||
|
|
||||||
|
ticketTimeExpiration: ticketExpiration.startTime,
|
||||||
|
ticketExpirationMsg: ticketExpiration.message,
|
||||||
|
ticketExpirationEnable: ticketExpiration.value === 'enabled' ? true : false,
|
||||||
|
|
||||||
|
checkboxSaturdayValue: saturday.value === 'enabled' ? true : false,
|
||||||
|
checkboxSundayValue: sunday.value === 'enabled' ? true : false,
|
||||||
|
weekendMessage: weekend.message,
|
||||||
|
enableWeekendMessage: weekend.value === 'enabled' ? true : false,
|
||||||
|
|
||||||
|
holidayDate: holiday.startTime,
|
||||||
|
holidayDateMessage: holiday.message,
|
||||||
|
holidayDateEnable: holiday.value === 'enabled' ? true : false,
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchSession()
|
||||||
|
}, [change])
|
||||||
|
|
||||||
|
const handleSaveConfig = async (values) => {
|
||||||
|
|
||||||
|
values = {
|
||||||
|
outBusinessHours: {
|
||||||
|
startTime: values.startTimeBus,
|
||||||
|
endTime: values.endTimeBus,
|
||||||
|
message: values.messageBus,
|
||||||
|
value: values.businessTimeEnable ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
ticketExpiration: {
|
||||||
|
startTime: values.ticketTimeExpiration,
|
||||||
|
message: values.ticketExpirationMsg,
|
||||||
|
value: values.ticketExpirationEnable ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
weekend: {
|
||||||
|
message: values.weekendMessage,
|
||||||
|
value: values.enableWeekendMessage ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
saturday:{
|
||||||
|
value: values.checkboxSaturdayValue ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
sunday: {
|
||||||
|
value: values.checkboxSundayValue ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
holiday: {
|
||||||
|
startTime: values.holidayDate,
|
||||||
|
message: values.holidayDateMessage,
|
||||||
|
value: values.holidayDateEnable ? 'enabled' : 'disabled'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
await api.put(`/settings/ticket`, values)
|
||||||
|
|
||||||
|
toast.success('Atualização realizada com sucesso!')
|
||||||
|
handleClose()
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose()
|
||||||
|
// setConfig(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
scroll="paper"
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
Configurações
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
|
||||||
|
<Formik
|
||||||
|
initialValues={config}
|
||||||
|
enableReinitialize={true}
|
||||||
|
// validationSchema={SessionSchema}
|
||||||
|
onSubmit={(values, actions) => {
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
handleSaveConfig(values)
|
||||||
|
actions.setSubmitting(false)
|
||||||
|
}, 100)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ values, touched, errors, isSubmitting }) => (
|
||||||
|
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}>
|
||||||
|
<Form>
|
||||||
|
|
||||||
|
<DialogContent dividers>
|
||||||
|
|
||||||
|
<div className={classes.multFieldLine}>
|
||||||
|
<Field
|
||||||
|
component={TimePicker}
|
||||||
|
name="startTimeBus"
|
||||||
|
label="Inicio atendimento"
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes',]}
|
||||||
|
format="HH:mm"
|
||||||
|
/>
|
||||||
|
{' '}
|
||||||
|
<Field
|
||||||
|
component={TimePicker}
|
||||||
|
name="endTimeBus"
|
||||||
|
label="Fim atendimento"
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes',]}
|
||||||
|
format="HH:mm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="businessTimeEnable"
|
||||||
|
checked={values.businessTimeEnable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem fora do horário de atendimento'}
|
||||||
|
type="messageBus"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="messageBus"
|
||||||
|
error={
|
||||||
|
touched.messageBus && Boolean(errors.messageBus)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.messageBus && errors.messageBus
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
{/* Saturday and Sunday date */}
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<div>
|
||||||
|
<label style={{ marginRight: '10px' }}>
|
||||||
|
<Field type="checkbox" name="checkboxSundayValue" />
|
||||||
|
Sábado
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<Field type="checkbox" name="checkboxSaturdayValue" />
|
||||||
|
Domingo
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="enableWeekendMessage"
|
||||||
|
checked={values.enableWeekendMessage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem para final de semana'}
|
||||||
|
type="weekendMessage"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="weekendMessage"
|
||||||
|
error={
|
||||||
|
touched.weekendMessage && Boolean(errors.weekendMessage)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.weekendMessage && errors.weekendMessage
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* Holiday date */}
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
component={DatePicker}
|
||||||
|
name="holidayDate"
|
||||||
|
label="Data do feriado"
|
||||||
|
format="dd/MM/yyyy"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="holidayDateEnable"
|
||||||
|
checked={values.holidayDateEnable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem para feriado'}
|
||||||
|
type="holidayDateMessage"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="holidayDateMessage"
|
||||||
|
error={
|
||||||
|
touched.holidayDateMessage && Boolean(errors.holidayDateMessage)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.holidayDateMessage && errors.holidayDateMessage
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Field
|
||||||
|
component={TimePicker}
|
||||||
|
name="ticketTimeExpiration"
|
||||||
|
label="Ticket expira em hh:mm"
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes',]}
|
||||||
|
format="HH:mm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="ticketExpirationEnable"
|
||||||
|
checked={values.ticketExpirationEnable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem por falta de atividade no atendimento'}
|
||||||
|
type="ticketExpirationMsg"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="ticketExpirationMsg"
|
||||||
|
error={
|
||||||
|
touched.ticketExpirationMsg && Boolean(errors.ticketExpirationMsg)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.ticketExpirationMsg && errors.ticketExpirationMsg
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={handleClose}
|
||||||
|
color="secondary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
{i18n.t('whatsappModal.buttons.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="contained"
|
||||||
|
className={classes.btnWrapper}
|
||||||
|
>
|
||||||
|
{isSubmitting ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
className={classes.buttonProgress}
|
||||||
|
/>
|
||||||
|
) : 'Salvar'}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Form>
|
||||||
|
</MuiPickersUtilsProvider>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default React.memo(ConfigModal)
|
|
@ -76,7 +76,7 @@ const useAuth = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSetting(data)
|
setSetting(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -6,6 +6,9 @@ import openSocket from 'socket.io-client'
|
||||||
|
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { green } from '@material-ui/core/colors'
|
import { green } from '@material-ui/core/colors'
|
||||||
|
|
||||||
|
import Settings from "@material-ui/icons/Settings";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
@ -47,6 +50,7 @@ import toastError from '../../errors/toastError'
|
||||||
//--------
|
//--------
|
||||||
import { AuthContext } from '../../context/Auth/AuthContext'
|
import { AuthContext } from '../../context/Auth/AuthContext'
|
||||||
import { Can } from '../../components/Can'
|
import { Can } from '../../components/Can'
|
||||||
|
import ConfigModal from '../../components/ConfigModal'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
mainPaper: {
|
mainPaper: {
|
||||||
|
@ -107,6 +111,7 @@ const Connections = () => {
|
||||||
|
|
||||||
const { whatsApps, loading } = useContext(WhatsAppsContext)
|
const { whatsApps, loading } = useContext(WhatsAppsContext)
|
||||||
const [whatsAppModalOpen, setWhatsAppModalOpen] = useState(false)
|
const [whatsAppModalOpen, setWhatsAppModalOpen] = useState(false)
|
||||||
|
const [configModalOpen, setConfigModalOpen] = useState(false)
|
||||||
const [qrModalOpen, setQrModalOpen] = useState(false)
|
const [qrModalOpen, setQrModalOpen] = useState(false)
|
||||||
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null)
|
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null)
|
||||||
const [confirmModalOpen, setConfirmModalOpen] = useState(false)
|
const [confirmModalOpen, setConfirmModalOpen] = useState(false)
|
||||||
|
@ -134,7 +139,7 @@ const Connections = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSettings(data)
|
setSettings(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
@ -205,6 +210,13 @@ const Connections = () => {
|
||||||
setWhatsAppModalOpen(true)
|
setWhatsAppModalOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOpenConfigModal = () => {
|
||||||
|
setConfigModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseConfigModal = () => {
|
||||||
|
setConfigModalOpen(false)
|
||||||
|
}
|
||||||
const handleCloseWhatsAppModal = useCallback(() => {
|
const handleCloseWhatsAppModal = useCallback(() => {
|
||||||
setWhatsAppModalOpen(false)
|
setWhatsAppModalOpen(false)
|
||||||
setSelectedWhatsApp(null)
|
setSelectedWhatsApp(null)
|
||||||
|
@ -307,17 +319,17 @@ const Connections = () => {
|
||||||
{(whatsApp.status === 'CONNECTED' ||
|
{(whatsApp.status === 'CONNECTED' ||
|
||||||
whatsApp.status === 'PAIRING' ||
|
whatsApp.status === 'PAIRING' ||
|
||||||
whatsApp.status === 'TIMEOUT') && (
|
whatsApp.status === 'TIMEOUT') && (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleOpenConfirmationModal('disconnect', whatsApp.id)
|
handleOpenConfirmationModal('disconnect', whatsApp.id)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18n.t('connections.buttons.disconnect')}
|
{i18n.t('connections.buttons.disconnect')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{whatsApp.status === 'OPENING' && (
|
{whatsApp.status === 'OPENING' && (
|
||||||
<Button size="small" variant="outlined" disabled color="default">
|
<Button size="small" variant="outlined" disabled color="default">
|
||||||
{i18n.t('connections.buttons.connecting')}
|
{i18n.t('connections.buttons.connecting')}
|
||||||
|
@ -454,10 +466,24 @@ const Connections = () => {
|
||||||
whatsAppId={!qrModalOpen && selectedWhatsApp?.id}
|
whatsAppId={!qrModalOpen && selectedWhatsApp?.id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ConfigModal
|
||||||
|
open={configModalOpen}
|
||||||
|
onClose={handleCloseConfigModal}
|
||||||
|
change={configModalOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
<MainHeader>
|
<MainHeader>
|
||||||
<Title>{i18n.t('connections.title')}</Title>
|
<Title>{i18n.t('connections.title')}</Title>
|
||||||
|
|
||||||
<MainHeaderButtonsWrapper>
|
<MainHeaderButtonsWrapper>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleOpenConfigModal}
|
||||||
|
>
|
||||||
|
<Settings/>
|
||||||
|
</Button>
|
||||||
<Can
|
<Can
|
||||||
role={user.profile}
|
role={user.profile}
|
||||||
perform="btn-add-whatsapp"
|
perform="btn-add-whatsapp"
|
||||||
|
@ -664,8 +690,8 @@ const Connections = () => {
|
||||||
settings.length > 0 &&
|
settings.length > 0 &&
|
||||||
getSettingValue('editURA') &&
|
getSettingValue('editURA') &&
|
||||||
getSettingValue('editURA') ===
|
getSettingValue('editURA') ===
|
||||||
'enabled') |
|
'enabled') |
|
||||||
(user.profile === 'master') ? (
|
(user.profile === 'master') ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
|
|
@ -121,7 +121,7 @@ const Queues = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSettings(data)
|
setSettings(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ const Settings = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSettings(data)
|
setSettings(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue