Finalização da Implementaçao do dialogflow
parent
9bf1850405
commit
1bdd4adaba
|
@ -15,6 +15,9 @@
|
||||||
"author": "",
|
"author": "",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"@google-cloud/dialogflow": "^4.6.0",
|
||||||
|
"actions-on-google": "^3.0.0",
|
||||||
|
"axios": "^0.27.2",
|
||||||
"@sentry/node": "^5.29.2",
|
"@sentry/node": "^5.29.2",
|
||||||
"@types/pino": "^6.3.4",
|
"@types/pino": "^6.3.4",
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
|
|
|
@ -13,10 +13,12 @@ import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact";
|
||||||
import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
|
import GetProfilePicUrl from "../services/WbotServices/GetProfilePicUrl";
|
||||||
import AppError from "../errors/AppError";
|
import AppError from "../errors/AppError";
|
||||||
|
|
||||||
|
|
||||||
import { searchContactCache } from '../helpers/ContactsCache'
|
import { searchContactCache } from '../helpers/ContactsCache'
|
||||||
import { off } from "process";
|
import { off } from "process";
|
||||||
|
|
||||||
|
import GetContactService from "../services/ContactServices/GetContactService";
|
||||||
|
import ToggleUseQueuesContactService from "../services/ContactServices/ToggleUseQueuesContactService";
|
||||||
|
import ToggleUseDialogflowContactService from "../services/ContactServices/ToggleUseDialogflowContactService";
|
||||||
|
|
||||||
|
|
||||||
type IndexQuery = {
|
type IndexQuery = {
|
||||||
|
@ -32,9 +34,15 @@ interface ContactData {
|
||||||
name: string;
|
name: string;
|
||||||
number: string;
|
number: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
useDialogflow: boolean;
|
||||||
extraInfo?: ExtraInfo[];
|
extraInfo?: ExtraInfo[];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type IndexGetContactQuery = {
|
||||||
|
name: string;
|
||||||
|
number: string;
|
||||||
|
};
|
||||||
|
|
||||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
let { searchParam, pageNumber } = req.query as IndexQuery;
|
let { searchParam, pageNumber } = req.query as IndexQuery;
|
||||||
|
|
||||||
|
@ -68,6 +76,17 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
return res.json({ contacts, count, hasMore });
|
return res.json({ contacts, count, hasMore });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const getContact = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
const { name, number } = req.body as IndexGetContactQuery;
|
||||||
|
|
||||||
|
const contact = await GetContactService({
|
||||||
|
name,
|
||||||
|
number
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(contact);
|
||||||
|
};
|
||||||
|
|
||||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const newContact: ContactData = req.body;
|
const newContact: ContactData = req.body;
|
||||||
newContact.number = newContact.number.replace("-", "").replace(" ", "");
|
newContact.number = newContact.number.replace("-", "").replace(" ", "");
|
||||||
|
@ -94,11 +113,13 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
let number = validNumber
|
let number = validNumber
|
||||||
let email = newContact.email
|
let email = newContact.email
|
||||||
let extraInfo = newContact.extraInfo
|
let extraInfo = newContact.extraInfo
|
||||||
|
let useDialogflow = newContact.useDialogflow
|
||||||
|
|
||||||
const contact = await CreateContactService({
|
const contact = await CreateContactService({
|
||||||
name,
|
name,
|
||||||
number,
|
number,
|
||||||
email,
|
email,
|
||||||
|
useDialogflow,
|
||||||
extraInfo,
|
extraInfo,
|
||||||
profilePicUrl
|
profilePicUrl
|
||||||
});
|
});
|
||||||
|
@ -173,6 +194,40 @@ export const remove = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
export const toggleUseQueue = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
const { contactId } = req.params;
|
||||||
|
|
||||||
|
const contact = await ToggleUseQueuesContactService({ contactId });
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
io.emit("contact", {
|
||||||
|
action: "update",
|
||||||
|
contact
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(contact);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const toggleUseDialogflow = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
const { contactId } = req.params;
|
||||||
|
|
||||||
|
const contact = await ToggleUseDialogflowContactService({ contactId });
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
io.emit("contact", {
|
||||||
|
action: "update",
|
||||||
|
contact
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(contact);
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
export const contacsBulkInsertOnQueue = async (req: Request, res: Response): Promise<Response> => {
|
export const contacsBulkInsertOnQueue = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,85 @@
|
||||||
|
import { Request, Response } from "express";
|
||||||
|
import { getIO } from "../libs/socket";
|
||||||
|
import CreateDialogflowService from "../services/DialogflowServices/CreateDialogflowService";
|
||||||
|
import DeleteDialogflowService from "../services/DialogflowServices/DeleteDialogflowService";
|
||||||
|
import ListDialogflowsService from "../services/DialogflowServices/ListDialogflowService";
|
||||||
|
import ShowDialogflowService from "../services/DialogflowServices/ShowDialogflowService";
|
||||||
|
import TestSessionDialogflowService from "../services/DialogflowServices/TestSessionDialogflowService";
|
||||||
|
import UpdateDialogflowService from "../services/DialogflowServices/UpdateDialogflowService";
|
||||||
|
|
||||||
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
const dialogflows = await ListDialogflowsService();
|
||||||
|
|
||||||
|
return res.status(200).json(dialogflows);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
const { name, projectName, jsonContent, language } = req.body;
|
||||||
|
|
||||||
|
const dialogflow = await CreateDialogflowService({ name, projectName, jsonContent, language });
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
io.emit("dialogflow", {
|
||||||
|
action: "update",
|
||||||
|
dialogflow
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(dialogflow);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
const { dialogflowId } = req.params;
|
||||||
|
|
||||||
|
const dialogflow = await ShowDialogflowService(dialogflowId);
|
||||||
|
|
||||||
|
return res.status(200).json(dialogflow);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const update = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
const { dialogflowId } = req.params;
|
||||||
|
const dialogflowData = req.body;
|
||||||
|
|
||||||
|
const dialogflow = await UpdateDialogflowService({dialogflowData, dialogflowId });
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
io.emit("dialogflow", {
|
||||||
|
action: "update",
|
||||||
|
dialogflow
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(201).json(dialogflow);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const remove = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
const { dialogflowId } = req.params;
|
||||||
|
|
||||||
|
await DeleteDialogflowService(dialogflowId);
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
io.emit("dialogflow", {
|
||||||
|
action: "delete",
|
||||||
|
dialogflowId: +dialogflowId
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).send();
|
||||||
|
};
|
||||||
|
|
||||||
|
export const testSession = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
const { projectName, jsonContent, language } = req.body;
|
||||||
|
|
||||||
|
const response = await TestSessionDialogflowService({ projectName, jsonContent, language });
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
io.emit("dialogflow", {
|
||||||
|
action: "testSession",
|
||||||
|
response
|
||||||
|
});
|
||||||
|
|
||||||
|
return res.status(200).json(response);
|
||||||
|
};
|
|
@ -14,6 +14,8 @@ 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 Dialogflow from "../models/Dialogflow";
|
||||||
// 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 +38,7 @@ const models = [
|
||||||
SchedulingNotify,
|
SchedulingNotify,
|
||||||
StatusChatEnd,
|
StatusChatEnd,
|
||||||
UserOnlineTime,
|
UserOnlineTime,
|
||||||
|
Dialogflow,
|
||||||
];
|
];
|
||||||
|
|
||||||
sequelize.addModels(models);
|
sequelize.addModels(models);
|
||||||
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.createTable("Dialogflows", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
name: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
projectName: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
unique: true
|
||||||
|
},
|
||||||
|
jsonContent: {
|
||||||
|
type: DataTypes.TEXT,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
language: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false,
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.dropTable("Dialogflows");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,16 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.addColumn("Queues", "dialogflowId", {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
references: { model: "Dialogflows", key: "id" },
|
||||||
|
onUpdate: "CASCADE",
|
||||||
|
onDelete: "SET NULL"
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.removeColumn("Queues", "dialogflowId");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.addColumn("Contacts", "useDialogflow", {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.removeColumn("Contacts", "useDialogflow");
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.addColumn("Contacts", "useQueues", {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: true
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.removeColumn("Contacts", "useQueues");
|
||||||
|
}
|
||||||
|
};
|
|
@ -1,37 +1,70 @@
|
||||||
const fsPromises = require("fs/promises");
|
const fsPromises = require("fs/promises");
|
||||||
const fs = require('fs')
|
import dir from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
import ListUsersService from "../services/UserServices/ListUsersService"
|
import ListUsersService from "../services/UserServices/ListUsersService"
|
||||||
|
|
||||||
const _botIsOnQueue = async (botName: string) => {
|
const _botIsOnQueue = async (botName: string) => {
|
||||||
|
|
||||||
const { users, count, hasMore } = await ListUsersService({searchParam:`${botName}`,pageNumber:1});
|
const botInfoFile = dir.join(os.tmpdir(), `botInfo.json`);
|
||||||
let botIsOnQueue = false
|
|
||||||
let userIdBot = null
|
console.log('The bot botInfoFile: ', botInfoFile)
|
||||||
let queueId = null
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
if (fs.existsSync(botInfoFile)) {
|
||||||
|
|
||||||
|
console.log('botInfo.json file exists');
|
||||||
|
|
||||||
if(users.length > 0){
|
const botInfo = fs.readFileSync(botInfoFile, {encoding:'utf8', flag:'r'});
|
||||||
|
|
||||||
try {
|
return JSON.parse(botInfo)
|
||||||
|
|
||||||
console.log('----------------- bot queue id: ', Object(users)[0]["queues"][0].id)
|
|
||||||
queueId = Object(users)[0]["queues"][0].id;
|
|
||||||
userIdBot = Object(users)[0].id
|
|
||||||
botIsOnQueue = true
|
|
||||||
|
|
||||||
}catch(err){
|
|
||||||
|
|
||||||
console.log('O usuário botqueue não está em nenhuma fila err: ',err)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
else{
|
|
||||||
console.log('Usuário botqueue não existe!')
|
|
||||||
}
|
|
||||||
|
|
||||||
return { userIdBot: userIdBot, botQueueId: queueId, isOnQueue: botIsOnQueue }
|
|
||||||
|
|
||||||
|
} else {
|
||||||
|
console.log('botInfo.json file not found!');
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log('There was an error on try to read the botInfo.json file: ',error)
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('XXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXXX ')
|
||||||
|
|
||||||
|
const { users, count, hasMore } = await ListUsersService({ searchParam: `${botName}`, pageNumber: 1 });
|
||||||
|
let botIsOnQueue = false
|
||||||
|
let userIdBot = null
|
||||||
|
let queueId = null
|
||||||
|
|
||||||
|
if (users.length > 0) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
console.log('----------------- bot queue id: ', Object(users)[0]["queues"][0].id)
|
||||||
|
queueId = Object(users)[0]["queues"][0].id;
|
||||||
|
userIdBot = Object(users)[0].id
|
||||||
|
botIsOnQueue = true
|
||||||
|
|
||||||
|
fs.writeFileSync(botInfoFile, JSON.stringify({ userIdBot: userIdBot, botQueueId: queueId, isOnQueue: botIsOnQueue }), "utf8");
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
|
||||||
|
console.log('O usuário botqueue não está em nenhuma fila err: ', err)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
console.log('Usuário botqueue não existe!')
|
||||||
|
|
||||||
|
fs.writeFileSync(botInfoFile, JSON.stringify({ isOnQueue: false, botQueueId: 0, userIdBot: 0 }), "utf8");
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export default _botIsOnQueue;
|
return { userIdBot: userIdBot, botQueueId: queueId, isOnQueue: botIsOnQueue }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default _botIsOnQueue;
|
|
@ -0,0 +1,33 @@
|
||||||
|
const fsPromises = require("fs/promises");
|
||||||
|
const fs = require('fs')
|
||||||
|
import axios from 'axios';
|
||||||
|
import * as https from "https";
|
||||||
|
|
||||||
|
const endPointQuery = async (url: string) => {
|
||||||
|
|
||||||
|
let response:any = null
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const httpsAgent = new https.Agent({ rejectUnauthorized: false, });
|
||||||
|
|
||||||
|
// const url = 'https://sos.espacolaser.com.br/api/whatsapp/ticket/R32656'
|
||||||
|
|
||||||
|
response = await axios.get(url, {
|
||||||
|
httpsAgent,
|
||||||
|
headers: {
|
||||||
|
'x-access-token': 'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOnsiaWQiOjEsInJvbGUiOiJjbGllbnQiLCJob3N0Ijoic29zLmVzcGFjb2xhc2VyLmNvbS5iciIsInRlbmFudCI6ImVzcGFjb2xhc2VyIiwibmFtZSI6IlNFTlNSLklUIiwiY29tcGFueSI6eyJpZCI6NDR9fSwiZGF0ZSI6MTY2MTI2MjY0MywiaWF0IjoxNjYxMjYyNjQzLCJleHAiOjE3NDc2NjI2NDN9.zf91OmRs4_C7B8OlVpLLrQMiRBYc7edP4qAdH_hqxpk',
|
||||||
|
'Origin': 'espacolaser'
|
||||||
|
}
|
||||||
|
});
|
||||||
|
console.log(`TEST URL CLIENT GET ROUTE: ${url} | STATUS CODE: ${response.status}`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error);
|
||||||
|
}
|
||||||
|
|
||||||
|
return response
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default endPointQuery;
|
|
@ -0,0 +1,20 @@
|
||||||
|
|
||||||
|
import dir from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
|
||||||
|
const deleteFileFromTMP = (name_ext: string) =>{
|
||||||
|
|
||||||
|
const botInfoFile = dir.join(os.tmpdir(), name_ext);
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
fs.unlinkSync(botInfoFile);
|
||||||
|
console.log(`${name_ext} file deleted!`);
|
||||||
|
|
||||||
|
} catch (error) {
|
||||||
|
console.log(`Can't delete ${name_ext} file: `,error)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default deleteFileFromTMP;
|
|
@ -41,6 +41,14 @@ class Contact extends Model<Contact> {
|
||||||
@Column
|
@Column
|
||||||
isGroup: boolean;
|
isGroup: boolean;
|
||||||
|
|
||||||
|
@Default(true)
|
||||||
|
@Column
|
||||||
|
useQueues: boolean;
|
||||||
|
|
||||||
|
@Default(true)
|
||||||
|
@Column
|
||||||
|
useDialogflow: boolean;
|
||||||
|
|
||||||
@CreatedAt
|
@CreatedAt
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,45 @@
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
Column,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
Model,
|
||||||
|
DataType,
|
||||||
|
PrimaryKey,
|
||||||
|
HasMany,
|
||||||
|
AutoIncrement
|
||||||
|
} from "sequelize-typescript";
|
||||||
|
import Queue from "./Queue";
|
||||||
|
|
||||||
|
@Table
|
||||||
|
class Dialogflow extends Model<Dialogflow> {
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column(DataType.TEXT)
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
@Column(DataType.TEXT)
|
||||||
|
projectName: string;
|
||||||
|
|
||||||
|
@Column(DataType.TEXT)
|
||||||
|
jsonContent: string;
|
||||||
|
|
||||||
|
@Column(DataType.TEXT)
|
||||||
|
language: string;
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
@Column(DataType.DATE(6))
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdatedAt
|
||||||
|
@Column(DataType.DATE(6))
|
||||||
|
updatedAt: Date;
|
||||||
|
|
||||||
|
@HasMany(() => Queue)
|
||||||
|
queues: Queue[]
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Dialogflow;
|
|
@ -8,13 +8,16 @@ import {
|
||||||
AutoIncrement,
|
AutoIncrement,
|
||||||
AllowNull,
|
AllowNull,
|
||||||
Unique,
|
Unique,
|
||||||
BelongsToMany
|
BelongsToMany,
|
||||||
|
BelongsTo,
|
||||||
|
ForeignKey
|
||||||
} from "sequelize-typescript";
|
} from "sequelize-typescript";
|
||||||
import User from "./User";
|
import User from "./User";
|
||||||
import UserQueue from "./UserQueue";
|
import UserQueue from "./UserQueue";
|
||||||
|
|
||||||
import Whatsapp from "./Whatsapp";
|
import Whatsapp from "./Whatsapp";
|
||||||
import WhatsappQueue from "./WhatsappQueue";
|
import WhatsappQueue from "./WhatsappQueue";
|
||||||
|
import Dialogflow from "./Dialogflow";
|
||||||
|
|
||||||
@Table
|
@Table
|
||||||
class Queue extends Model<Queue> {
|
class Queue extends Model<Queue> {
|
||||||
|
@ -36,6 +39,13 @@ class Queue extends Model<Queue> {
|
||||||
@Column
|
@Column
|
||||||
greetingMessage: string;
|
greetingMessage: string;
|
||||||
|
|
||||||
|
@ForeignKey(() => Dialogflow)
|
||||||
|
@Column
|
||||||
|
dialogflowId: number;
|
||||||
|
|
||||||
|
@BelongsTo(() => Dialogflow)
|
||||||
|
dialogflow: Dialogflow;
|
||||||
|
|
||||||
@CreatedAt
|
@CreatedAt
|
||||||
createdAt: Date;
|
createdAt: Date;
|
||||||
|
|
||||||
|
|
|
@ -18,6 +18,10 @@ contactRoutes.post("/contacts", isAuth, ContactController.store);
|
||||||
|
|
||||||
contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update);
|
contactRoutes.put("/contacts/:contactId", isAuth, ContactController.update);
|
||||||
|
|
||||||
|
contactRoutes.put("/contacts/toggleUseQueues/:contactId", isAuth, ContactController.toggleUseQueue);
|
||||||
|
|
||||||
|
contactRoutes.put("/contacts/toggleUseDialogflow/:contactId", isAuth, ContactController.toggleUseDialogflow);
|
||||||
|
|
||||||
contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove);
|
contactRoutes.delete("/contacts/:contactId", isAuth, ContactController.remove);
|
||||||
|
|
||||||
export default contactRoutes;
|
export default contactRoutes;
|
||||||
|
|
|
@ -0,0 +1,20 @@
|
||||||
|
import { Router } from "express";
|
||||||
|
import isAuth from "../middleware/isAuth";
|
||||||
|
|
||||||
|
import * as DialogflowController from "../controllers/DialogflowController";
|
||||||
|
|
||||||
|
const dialogflowRoutes = Router();
|
||||||
|
|
||||||
|
dialogflowRoutes.get("/dialogflow", isAuth, DialogflowController.index);
|
||||||
|
|
||||||
|
dialogflowRoutes.post("/dialogflow", isAuth, DialogflowController.store);
|
||||||
|
|
||||||
|
dialogflowRoutes.get("/dialogflow/:dialogflowId", isAuth, DialogflowController.show);
|
||||||
|
|
||||||
|
dialogflowRoutes.put("/dialogflow/:dialogflowId", isAuth, DialogflowController.update);
|
||||||
|
|
||||||
|
dialogflowRoutes.delete("/dialogflow/:dialogflowId", isAuth, DialogflowController.remove);
|
||||||
|
|
||||||
|
dialogflowRoutes.post("/dialogflow/testsession", isAuth, DialogflowController.testSession);
|
||||||
|
|
||||||
|
export default dialogflowRoutes;
|
|
@ -13,6 +13,7 @@ import quickAnswerRoutes from "./quickAnswerRoutes";
|
||||||
import reportRoutes from "./reportRoutes";
|
import reportRoutes from "./reportRoutes";
|
||||||
import schedulingNotifiyRoutes from "./SchedulingNotifyRoutes";
|
import schedulingNotifiyRoutes from "./SchedulingNotifyRoutes";
|
||||||
import statusChatEndRoutes from "./statusChatEndRoutes";
|
import statusChatEndRoutes from "./statusChatEndRoutes";
|
||||||
|
import dialogflowRoutes from "./dialogflowRoutes";
|
||||||
|
|
||||||
const routes = Router();
|
const routes = Router();
|
||||||
|
|
||||||
|
@ -31,5 +32,6 @@ routes.use(quickAnswerRoutes);
|
||||||
routes.use(schedulingNotifiyRoutes);
|
routes.use(schedulingNotifiyRoutes);
|
||||||
routes.use(reportRoutes);
|
routes.use(reportRoutes);
|
||||||
routes.use(statusChatEndRoutes);
|
routes.use(statusChatEndRoutes);
|
||||||
|
routes.use(dialogflowRoutes);
|
||||||
|
|
||||||
export default routes;
|
export default routes;
|
||||||
|
|
|
@ -1,7 +1,8 @@
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
import Contact from "../../models/Contact";
|
import Contact from "../../models/Contact";
|
||||||
|
|
||||||
import { createOrUpdateContactCache } from '../../helpers/ContactsCache'
|
import { createOrUpdateContactCache } from '../../helpers/ContactsCache'
|
||||||
|
|
||||||
|
|
||||||
interface ExtraInfo {
|
interface ExtraInfo {
|
||||||
name: string;
|
name: string;
|
||||||
|
@ -12,6 +13,7 @@ interface Request {
|
||||||
name: string;
|
name: string;
|
||||||
number: string;
|
number: string;
|
||||||
email?: string;
|
email?: string;
|
||||||
|
useDialogflow?: boolean;
|
||||||
profilePicUrl?: string;
|
profilePicUrl?: string;
|
||||||
extraInfo?: ExtraInfo[];
|
extraInfo?: ExtraInfo[];
|
||||||
}
|
}
|
||||||
|
@ -20,6 +22,7 @@ const CreateContactService = async ({
|
||||||
name,
|
name,
|
||||||
number,
|
number,
|
||||||
email = "",
|
email = "",
|
||||||
|
useDialogflow,
|
||||||
extraInfo = []
|
extraInfo = []
|
||||||
}: Request): Promise<Contact> => {
|
}: Request): Promise<Contact> => {
|
||||||
const numberExists = await Contact.findOne({
|
const numberExists = await Contact.findOne({
|
||||||
|
@ -35,6 +38,7 @@ const CreateContactService = async ({
|
||||||
name,
|
name,
|
||||||
number,
|
number,
|
||||||
email,
|
email,
|
||||||
|
useDialogflow,
|
||||||
extraInfo
|
extraInfo
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -45,8 +49,7 @@ const CreateContactService = async ({
|
||||||
|
|
||||||
// TEST DEL
|
// TEST DEL
|
||||||
await createOrUpdateContactCache(`contact:${contact.id}`, {id: contact.id, name, number, profilePicUrl:'', isGroup:'false', extraInfo, email })
|
await createOrUpdateContactCache(`contact:${contact.id}`, {id: contact.id, name, number, profilePicUrl:'', isGroup:'false', extraInfo, email })
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
return contact;
|
return contact;
|
||||||
};
|
};
|
||||||
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import Contact from "../../models/Contact";
|
||||||
|
import CreateContactService from "./CreateContactService";
|
||||||
|
|
||||||
|
interface ExtraInfo {
|
||||||
|
name: string;
|
||||||
|
value: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
name: string;
|
||||||
|
number: string;
|
||||||
|
email?: string;
|
||||||
|
profilePicUrl?: string;
|
||||||
|
extraInfo?: ExtraInfo[];
|
||||||
|
}
|
||||||
|
|
||||||
|
const GetContactService = async ({ name, number }: Request): Promise<Contact> => {
|
||||||
|
const numberExists = await Contact.findOne({
|
||||||
|
where: { number }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!numberExists) {
|
||||||
|
const contact = await CreateContactService({
|
||||||
|
name,
|
||||||
|
number,
|
||||||
|
})
|
||||||
|
|
||||||
|
if (contact == null)
|
||||||
|
throw new AppError("CONTACT_NOT_FOUND")
|
||||||
|
else
|
||||||
|
return contact
|
||||||
|
}
|
||||||
|
|
||||||
|
return numberExists
|
||||||
|
};
|
||||||
|
|
||||||
|
export default GetContactService;
|
|
@ -0,0 +1,34 @@
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import Contact from "../../models/Contact";
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
contactId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToggleUseDialogflowContactService = async ({
|
||||||
|
contactId
|
||||||
|
}: Request): Promise<Contact> => {
|
||||||
|
const contact = await Contact.findOne({
|
||||||
|
where: { id: contactId },
|
||||||
|
attributes: ["id", "useDialogflow"]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!contact) {
|
||||||
|
throw new AppError("ERR_NO_CONTACT_FOUND", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useDialogflow = contact.useDialogflow ? false : true;
|
||||||
|
|
||||||
|
await contact.update({
|
||||||
|
useDialogflow
|
||||||
|
});
|
||||||
|
|
||||||
|
await contact.reload({
|
||||||
|
attributes: ["id", "name", "number", "email", "profilePicUrl", "useQueues", "useDialogflow"],
|
||||||
|
include: ["extraInfo"]
|
||||||
|
});
|
||||||
|
|
||||||
|
return contact;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ToggleUseDialogflowContactService;
|
|
@ -0,0 +1,34 @@
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import Contact from "../../models/Contact";
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
contactId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const ToggleUseQueuesContactService = async ({
|
||||||
|
contactId
|
||||||
|
}: Request): Promise<Contact> => {
|
||||||
|
const contact = await Contact.findOne({
|
||||||
|
where: { id: contactId },
|
||||||
|
attributes: ["id", "useQueues"]
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!contact) {
|
||||||
|
throw new AppError("ERR_NO_CONTACT_FOUND", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
const useQueues = contact.useQueues ? false : true;
|
||||||
|
|
||||||
|
await contact.update({
|
||||||
|
useQueues
|
||||||
|
});
|
||||||
|
|
||||||
|
await contact.reload({
|
||||||
|
attributes: ["id", "name", "number", "email", "profilePicUrl", "useQueues", "useDialogflow"],
|
||||||
|
include: ["extraInfo"]
|
||||||
|
});
|
||||||
|
|
||||||
|
return contact;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ToggleUseQueuesContactService;
|
|
@ -0,0 +1,76 @@
|
||||||
|
import * as Yup from "yup";
|
||||||
|
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import Dialogflow from "../../models/Dialogflow";
|
||||||
|
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
name: string;
|
||||||
|
projectName: string;
|
||||||
|
jsonContent: string;
|
||||||
|
language: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const CreateDialogflowService = async ({
|
||||||
|
name,
|
||||||
|
projectName,
|
||||||
|
jsonContent,
|
||||||
|
language
|
||||||
|
}: Request): Promise<Dialogflow> => {
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
name: Yup.string()
|
||||||
|
.required()
|
||||||
|
.min(2)
|
||||||
|
.test(
|
||||||
|
"Check-name",
|
||||||
|
"This DialogFlow name is already used.",
|
||||||
|
async value => {
|
||||||
|
if (!value) return false;
|
||||||
|
const nameExists = await Dialogflow.findOne({
|
||||||
|
where: { name: value }
|
||||||
|
});
|
||||||
|
return !nameExists;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
projectName: Yup.string()
|
||||||
|
.required()
|
||||||
|
.min(2)
|
||||||
|
.test(
|
||||||
|
"Check-name",
|
||||||
|
"This DialogFlow projectName is already used.",
|
||||||
|
async value => {
|
||||||
|
if (!value) return false;
|
||||||
|
const nameExists = await Dialogflow.findOne({
|
||||||
|
where: { projectName: value }
|
||||||
|
});
|
||||||
|
return !nameExists;
|
||||||
|
}
|
||||||
|
),
|
||||||
|
jsonContent: Yup.string()
|
||||||
|
.required()
|
||||||
|
,
|
||||||
|
language: Yup.string()
|
||||||
|
.required()
|
||||||
|
.min(2)
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await schema.validate({ name, projectName, jsonContent, language });
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppError(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const dialogflow = await Dialogflow.create(
|
||||||
|
{
|
||||||
|
name,
|
||||||
|
projectName,
|
||||||
|
jsonContent,
|
||||||
|
language
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
|
return dialogflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CreateDialogflowService;
|
|
@ -0,0 +1,33 @@
|
||||||
|
import { SessionsClient } from "@google-cloud/dialogflow";
|
||||||
|
import Dialogflow from "../../models/Dialogflow";
|
||||||
|
import dir from 'path';
|
||||||
|
import fs from 'fs';
|
||||||
|
import os from 'os';
|
||||||
|
import { logger } from "../../utils/logger";
|
||||||
|
|
||||||
|
const sessions : Map<number, SessionsClient> = new Map<number, SessionsClient>();
|
||||||
|
|
||||||
|
const createDialogflowSession = async (id:number, projectName:string, jsonContent:string) : Promise<SessionsClient | undefined> => {
|
||||||
|
if(sessions.has(id)) {
|
||||||
|
return sessions.get(id);
|
||||||
|
}
|
||||||
|
|
||||||
|
const keyFilename = dir.join(os.tmpdir(), `whaticket_${id}.json`);
|
||||||
|
|
||||||
|
console.log('keyFilename: ',keyFilename)
|
||||||
|
|
||||||
|
logger.info(`Openig new dialogflow session #${projectName} in '${keyFilename}'`)
|
||||||
|
|
||||||
|
await fs.writeFileSync(keyFilename, jsonContent);
|
||||||
|
const session = new SessionsClient({ keyFilename });
|
||||||
|
|
||||||
|
sessions.set(id, session);
|
||||||
|
|
||||||
|
return session;
|
||||||
|
}
|
||||||
|
|
||||||
|
const createDialogflowSessionWithModel = async (model: Dialogflow) : Promise<SessionsClient | undefined> => {
|
||||||
|
return createDialogflowSession(model.id, model.projectName, model.jsonContent);
|
||||||
|
}
|
||||||
|
|
||||||
|
export { createDialogflowSession, createDialogflowSessionWithModel };
|
|
@ -0,0 +1,16 @@
|
||||||
|
import Dialogflow from "../../models/Dialogflow";
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
|
||||||
|
const DeleteDialogflowService = async (id: string): Promise<void> => {
|
||||||
|
const dialogflow = await Dialogflow.findOne({
|
||||||
|
where: { id }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (!dialogflow) {
|
||||||
|
throw new AppError("ERR_NO_DIALOG_FOUND", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await dialogflow.destroy();
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DeleteDialogflowService;
|
|
@ -0,0 +1,9 @@
|
||||||
|
import DialogFLow from "../../models/Dialogflow";
|
||||||
|
|
||||||
|
const ListDialogflowService = async (): Promise<DialogFLow[]> => {
|
||||||
|
const dialogFLows = await DialogFLow.findAll();
|
||||||
|
|
||||||
|
return dialogFLows;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListDialogflowService;
|
|
@ -0,0 +1,64 @@
|
||||||
|
import * as Sentry from "@sentry/node";
|
||||||
|
import { SessionsClient } from "@google-cloud/dialogflow";
|
||||||
|
import { logger } from "../../utils/logger";
|
||||||
|
|
||||||
|
async function detectIntent(
|
||||||
|
sessionClient:SessionsClient,
|
||||||
|
projectId:string,
|
||||||
|
sessionId:string,
|
||||||
|
query:string,
|
||||||
|
languageCode:string
|
||||||
|
) {
|
||||||
|
const sessionPath = sessionClient.projectAgentSessionPath(
|
||||||
|
projectId,
|
||||||
|
sessionId
|
||||||
|
);
|
||||||
|
|
||||||
|
const request = {
|
||||||
|
session: sessionPath,
|
||||||
|
queryInput: {
|
||||||
|
text: {
|
||||||
|
text: query,
|
||||||
|
languageCode: languageCode,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const responses = await sessionClient.detectIntent(request);
|
||||||
|
return responses[0];
|
||||||
|
}
|
||||||
|
|
||||||
|
async function queryDialogFlow(
|
||||||
|
sessionClient:SessionsClient,
|
||||||
|
projectId:string,
|
||||||
|
sessionId:string,
|
||||||
|
query:string,
|
||||||
|
languageCode:string
|
||||||
|
) : Promise<any | null> {
|
||||||
|
let intentResponse;
|
||||||
|
|
||||||
|
try {
|
||||||
|
intentResponse = await detectIntent(
|
||||||
|
sessionClient,
|
||||||
|
projectId,
|
||||||
|
sessionId,
|
||||||
|
query,
|
||||||
|
languageCode
|
||||||
|
);
|
||||||
|
|
||||||
|
const responses = intentResponse?.queryResult?.fulfillmentMessages;
|
||||||
|
|
||||||
|
if (responses?.length === 0) {
|
||||||
|
return null;
|
||||||
|
} else {
|
||||||
|
return responses;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
Sentry.captureException(error);
|
||||||
|
logger.error(`Error handling whatsapp message: Err: ${error}`);
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {queryDialogFlow}
|
|
@ -0,0 +1,15 @@
|
||||||
|
import Dialogflow from "../../models/Dialogflow";
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
|
||||||
|
|
||||||
|
const ShowDialogflowService = async (id: string | number): Promise<Dialogflow> => {
|
||||||
|
const dialogflow = await Dialogflow.findByPk(id);
|
||||||
|
|
||||||
|
if (!dialogflow) {
|
||||||
|
throw new AppError("ERR_NO_DIALOG_FOUND", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
return dialogflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ShowDialogflowService;
|
|
@ -0,0 +1,70 @@
|
||||||
|
import * as Yup from "yup";
|
||||||
|
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
|
||||||
|
import { queryDialogFlow } from "./QueryDialogflow";
|
||||||
|
import { createDialogflowSession } from "./CreateSessionDialogflow";
|
||||||
|
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
projectName: string;
|
||||||
|
jsonContent: string;
|
||||||
|
language: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Response {
|
||||||
|
messages: string[];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const TestDialogflowSession = async ({
|
||||||
|
projectName,
|
||||||
|
jsonContent,
|
||||||
|
language
|
||||||
|
}: Request): Promise<Response | null> => {
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
projectName: Yup.string()
|
||||||
|
.required()
|
||||||
|
.min(2),
|
||||||
|
jsonContent: Yup.string()
|
||||||
|
.required(),
|
||||||
|
language: Yup.string()
|
||||||
|
.required()
|
||||||
|
.min(2)
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
await schema.validate({ projectName, jsonContent, language });
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppError(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const session = await createDialogflowSession(999, projectName, jsonContent);
|
||||||
|
|
||||||
|
if (!session) {
|
||||||
|
throw new AppError("ERR_TEST_SESSION_DIALOG", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
let dialogFlowReply = await queryDialogFlow(
|
||||||
|
session,
|
||||||
|
projectName,
|
||||||
|
"TestSeesion",
|
||||||
|
"Ola",
|
||||||
|
language,
|
||||||
|
);
|
||||||
|
|
||||||
|
await session.close();
|
||||||
|
|
||||||
|
if (!dialogFlowReply) {
|
||||||
|
throw new AppError("ERR_TEST_REPLY_DIALOG", 400);
|
||||||
|
}
|
||||||
|
|
||||||
|
const messages = [];
|
||||||
|
for (let message of dialogFlowReply) {
|
||||||
|
messages.push(message.text.text[0]);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { messages };
|
||||||
|
}
|
||||||
|
|
||||||
|
export default TestDialogflowSession;
|
|
@ -0,0 +1,55 @@
|
||||||
|
import * as Yup from "yup";
|
||||||
|
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import Dialogflow from "../../models/Dialogflow";
|
||||||
|
import ShowDialogflowService from "./ShowDialogflowService";
|
||||||
|
|
||||||
|
interface DialogflowData {
|
||||||
|
name?: string;
|
||||||
|
projectName?: string;
|
||||||
|
jsonContent?: string;
|
||||||
|
language?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
dialogflowData: DialogflowData;
|
||||||
|
dialogflowId: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UpdateDialogflowService = async ({
|
||||||
|
dialogflowData,
|
||||||
|
dialogflowId
|
||||||
|
}: Request): Promise<Dialogflow> => {
|
||||||
|
const schema = Yup.object().shape({
|
||||||
|
name: Yup.string().min(2),
|
||||||
|
projectName: Yup.string().min(2),
|
||||||
|
jsonContent: Yup.string().min(2),
|
||||||
|
language: Yup.string().min(2)
|
||||||
|
});
|
||||||
|
|
||||||
|
const {
|
||||||
|
name,
|
||||||
|
projectName,
|
||||||
|
jsonContent,
|
||||||
|
language
|
||||||
|
} = dialogflowData;
|
||||||
|
|
||||||
|
try {
|
||||||
|
await schema.validate({ name, projectName, jsonContent, language });
|
||||||
|
} catch (err) {
|
||||||
|
throw new AppError(err.message);
|
||||||
|
}
|
||||||
|
|
||||||
|
const dialogflow = await ShowDialogflowService(dialogflowId);
|
||||||
|
|
||||||
|
await dialogflow.update({
|
||||||
|
name,
|
||||||
|
projectName,
|
||||||
|
jsonContent,
|
||||||
|
language
|
||||||
|
});
|
||||||
|
|
||||||
|
return dialogflow;
|
||||||
|
};
|
||||||
|
|
||||||
|
export default UpdateDialogflowService;
|
|
@ -1,5 +1,6 @@
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
|
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||||
import Queue from "../../models/Queue";
|
import Queue from "../../models/Queue";
|
||||||
|
|
||||||
interface QueueData {
|
interface QueueData {
|
||||||
|
@ -61,6 +62,8 @@ const CreateQueueService = async (queueData: QueueData): Promise<Queue> => {
|
||||||
|
|
||||||
const queue = await Queue.create(queueData);
|
const queue = await Queue.create(queueData);
|
||||||
|
|
||||||
|
deleteFileFromTMP(`botInfo.json`)
|
||||||
|
|
||||||
return queue;
|
return queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,7 @@ import UserQueue from "../../models/UserQueue";
|
||||||
import ListTicketsServiceCache from "../TicketServices/ListTicketServiceCache";
|
import ListTicketsServiceCache from "../TicketServices/ListTicketServiceCache";
|
||||||
|
|
||||||
import { deleteTicketsFieldsCache } from '../../helpers/TicketCache'
|
import { deleteTicketsFieldsCache } from '../../helpers/TicketCache'
|
||||||
|
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||||
|
|
||||||
const DeleteQueueService = async (queueId: number | string): Promise<void> => {
|
const DeleteQueueService = async (queueId: number | string): Promise<void> => {
|
||||||
|
|
||||||
|
@ -29,6 +30,8 @@ const DeleteQueueService = async (queueId: number | string): Promise<void> => {
|
||||||
}
|
}
|
||||||
|
|
||||||
await queue.destroy();
|
await queue.destroy();
|
||||||
|
|
||||||
|
deleteFileFromTMP(`botInfo.json`)
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeleteQueueService;
|
export default DeleteQueueService;
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import { Op } from "sequelize";
|
import { Op } from "sequelize";
|
||||||
import * as Yup from "yup";
|
import * as Yup from "yup";
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
|
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||||
import Queue from "../../models/Queue";
|
import Queue from "../../models/Queue";
|
||||||
import ShowQueueService from "./ShowQueueService";
|
import ShowQueueService from "./ShowQueueService";
|
||||||
|
|
||||||
|
@ -67,6 +68,8 @@ const UpdateQueueService = async (
|
||||||
|
|
||||||
await queue.update(queueData);
|
await queue.update(queueData);
|
||||||
|
|
||||||
|
deleteFileFromTMP(`botInfo.json`)
|
||||||
|
|
||||||
return queue;
|
return queue;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -26,8 +26,8 @@ const FindOrCreateTicketService = async (
|
||||||
|
|
||||||
|
|
||||||
//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 }
|
// const botInfo = { isOnQueue: false }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -10,7 +10,7 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
||||||
{
|
{
|
||||||
model: Contact,
|
model: Contact,
|
||||||
as: "contact",
|
as: "contact",
|
||||||
attributes: ["id", "name", "number", "profilePicUrl"],
|
attributes: ["id", "name", "number", "profilePicUrl", "useDialogflow", "useQueues"],
|
||||||
include: ["extraInfo"]
|
include: ["extraInfo"]
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
@ -21,7 +21,8 @@ const ShowTicketService = async (id: string | number): Promise<Ticket> => {
|
||||||
{
|
{
|
||||||
model: Queue,
|
model: Queue,
|
||||||
as: "queue",
|
as: "queue",
|
||||||
attributes: ["id", "name", "color"]
|
attributes: ["id", "name", "color"],
|
||||||
|
include: ["dialogflow"]
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,7 +2,9 @@ import * as Yup from "yup";
|
||||||
|
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
import { SerializeUser } from "../../helpers/SerializeUser";
|
import { SerializeUser } from "../../helpers/SerializeUser";
|
||||||
import User from "../../models/User";
|
import User from "../../models/User";
|
||||||
|
|
||||||
|
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
email: string;
|
email: string;
|
||||||
|
@ -78,6 +80,8 @@ const CreateUserService = async ({
|
||||||
|
|
||||||
const serializedUser = SerializeUser(user);
|
const serializedUser = SerializeUser(user);
|
||||||
|
|
||||||
|
deleteFileFromTMP(`botInfo.json`)
|
||||||
|
|
||||||
return serializedUser;
|
return serializedUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,7 @@ import AppError from "../../errors/AppError";
|
||||||
import Ticket from "../../models/Ticket";
|
import Ticket from "../../models/Ticket";
|
||||||
import UpdateDeletedUserOpenTicketsStatus from "../../helpers/UpdateDeletedUserOpenTicketsStatus";
|
import UpdateDeletedUserOpenTicketsStatus from "../../helpers/UpdateDeletedUserOpenTicketsStatus";
|
||||||
|
|
||||||
|
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||||
|
|
||||||
const DeleteUserService = async (id: string | number): Promise<void> => {
|
const DeleteUserService = async (id: string | number): Promise<void> => {
|
||||||
const user = await User.findOne({
|
const user = await User.findOne({
|
||||||
|
@ -24,6 +25,8 @@ const DeleteUserService = async (id: string | number): Promise<void> => {
|
||||||
|
|
||||||
await user.destroy();
|
await user.destroy();
|
||||||
|
|
||||||
|
deleteFileFromTMP(`botInfo.json`)
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default DeleteUserService;
|
export default DeleteUserService;
|
||||||
|
|
|
@ -3,6 +3,8 @@ import * as Yup from "yup";
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
import ShowUserService from "./ShowUserService";
|
import ShowUserService from "./ShowUserService";
|
||||||
|
|
||||||
|
import deleteFileFromTMP from "../../helpers/deleteFileFromTMP";
|
||||||
|
|
||||||
interface UserData {
|
interface UserData {
|
||||||
email?: string;
|
email?: string;
|
||||||
password?: string;
|
password?: string;
|
||||||
|
@ -67,6 +69,8 @@ const UpdateUserService = async ({
|
||||||
queues: user.queues
|
queues: user.queues
|
||||||
};
|
};
|
||||||
|
|
||||||
|
deleteFileFromTMP(`botInfo.json`)
|
||||||
|
|
||||||
return serializedUser;
|
return serializedUser;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
|
||||||
|
const data:any[] = [
|
||||||
|
{
|
||||||
|
"id":"1",
|
||||||
|
"action":"transfer_to_attendant",
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"id":"2",
|
||||||
|
"action":"request_endpoint",
|
||||||
|
},
|
||||||
|
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
export default data;
|
|
@ -16,7 +16,9 @@ import {
|
||||||
Contact as WbotContact,
|
Contact as WbotContact,
|
||||||
Message as WbotMessage,
|
Message as WbotMessage,
|
||||||
MessageAck,
|
MessageAck,
|
||||||
Client
|
Client,
|
||||||
|
Chat,
|
||||||
|
MessageMedia
|
||||||
} from "whatsapp-web.js";
|
} from "whatsapp-web.js";
|
||||||
|
|
||||||
import Contact from "../../models/Contact";
|
import Contact from "../../models/Contact";
|
||||||
|
@ -35,7 +37,7 @@ import { date } from "faker";
|
||||||
|
|
||||||
import ShowQueueService from "../QueueService/ShowQueueService";
|
import ShowQueueService from "../QueueService/ShowQueueService";
|
||||||
import ShowTicketMessage from "../TicketServices/ShowTicketMessage"
|
import ShowTicketMessage from "../TicketServices/ShowTicketMessage"
|
||||||
import BotIsOnQueue from "../../helpers/BotIsOnQueue"
|
import BotIsOnQueue from "../../helpers/BotIsOnQueue"
|
||||||
import Queue from "../../models/Queue";
|
import Queue from "../../models/Queue";
|
||||||
|
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
|
@ -44,16 +46,22 @@ import { StartWhatsAppSession } from "../../services/WbotServices/StartWhatsAppS
|
||||||
import { removeWbot } from '../../libs/wbot'
|
import { removeWbot } from '../../libs/wbot'
|
||||||
import { restartWhatsSession } from "../../helpers/RestartWhatsSession";
|
import { restartWhatsSession } from "../../helpers/RestartWhatsSession";
|
||||||
|
|
||||||
// test del
|
|
||||||
import data_ura from './ura'
|
import data_ura from './ura'
|
||||||
import msg_client_transfer from './ura_msg_transfer'
|
import msg_client_transfer from './ura_msg_transfer'
|
||||||
import final_message from "./ura_final_message";
|
import final_message from "./ura_final_message";
|
||||||
import SendWhatsAppMessage from "./SendWhatsAppMessage";
|
import SendWhatsAppMessage from "./SendWhatsAppMessage";
|
||||||
import Whatsapp from "../../models/Whatsapp";
|
import Whatsapp from "../../models/Whatsapp";
|
||||||
import { splitDateTime } from "../../helpers/SplitDateTime";
|
import { splitDateTime } from "../../helpers/SplitDateTime";
|
||||||
//
|
|
||||||
|
import { queryDialogFlow } from "../DialogflowServices/QueryDialogflow";
|
||||||
|
import { createDialogflowSessionWithModel } from "../DialogflowServices/CreateSessionDialogflow";
|
||||||
|
import bot_actions from './BotActions'
|
||||||
|
import ShowTicketService from "../TicketServices/ShowTicketService";
|
||||||
|
|
||||||
import { updateTicketCacheByTicketId } from '../../helpers/TicketCache'
|
import { updateTicketCacheByTicketId } from '../../helpers/TicketCache'
|
||||||
|
|
||||||
|
import endPointQuery from "../../helpers/EndpointQuery";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -171,6 +179,171 @@ const verifyMessage = async (
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
async function sendDelayedMessages(wbot: Session, ticket: Ticket, contact: Contact, message: string) {
|
||||||
|
const body = message.replace(/\\n/g, '\n');
|
||||||
|
|
||||||
|
if (body.search('dialog_actions') != -1) {
|
||||||
|
|
||||||
|
let msgAction = botMsgActions(body)
|
||||||
|
|
||||||
|
console.log('gggggggggggggggggggggggggggggggggg msgAction: ', msgAction)
|
||||||
|
|
||||||
|
if (msgAction.actions[0] == 'request_endpoint') {
|
||||||
|
|
||||||
|
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
|
||||||
|
await verifyMessage(sentMessage, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
// const url = 'https://sos.espacolaser.com.br/api/whatsapps/ticket/R32656'
|
||||||
|
const endPointResponse = await endPointQuery(msgAction.actions[1])
|
||||||
|
|
||||||
|
|
||||||
|
if (endPointResponse) {
|
||||||
|
|
||||||
|
const response = Object.entries(endPointResponse.data);
|
||||||
|
let msg_endpoint_response = ''
|
||||||
|
for (let i = 0; i < response.length; i++) {
|
||||||
|
|
||||||
|
msg_endpoint_response += `*${response[i][0]}*: ${response[i][1]}\n`
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
if (endPointResponse.data.status == 'EM ATENDIMENTO') {
|
||||||
|
|
||||||
|
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `Seu chamado está em atendimento pelo analista ${endPointResponse.data.tecnico} + Última informação do CHAT
|
||||||
|
// Verificar pelo “ID do solicitante” se existe chamado na HIT -> Se houver, informar status (Vamos alinhar os detalhes)`);
|
||||||
|
|
||||||
|
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Seu chamado está em atendimento\n\n *Analista:* ${endPointResponse.data.tecnico}\n *Chat:* ${endPointResponse.data.chat ? endPointResponse.data.chat : ""}\n\n_Digite *0* para voltar ao menu principal._`);
|
||||||
|
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Seu chamado está em atendimento\n\n${msg_endpoint_response}\n_Digite *0* para voltar ao menu principal._`);
|
||||||
|
await verifyMessage(msg, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (endPointResponse.data.categoria == 'ELOS' || (endPointResponse.data.subcategoria == 'VENDA' || endPointResponse.data.subcategoria == 'INDISPONIBILIDADE')) {
|
||||||
|
|
||||||
|
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Vi que está com um problema no ELOS\n\n *Status:* ${endPointResponse.data.status}\n\nSe seu caso for urgente para concluir uma venda, digite “URGENTE”\n_Digite *0* para voltar ao menu principal._`);
|
||||||
|
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n Vi que está com um problema no ELOS\n\n${msg_endpoint_response}\nSe seu caso for urgente para concluir uma venda, digite “URGENTE”\n_Digite *0* para voltar ao menu principal._`);
|
||||||
|
await verifyMessage(msg, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
}
|
||||||
|
else if ((endPointResponse.data.categoria == 'INFRAESTRUTURA' || endPointResponse.data.subcategoria == 'INTERNET' ||
|
||||||
|
endPointResponse.data.terceiro_nivel == 'QUEDA TOTAL' || endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO') ||
|
||||||
|
(endPointResponse.data.terceiro_nivel == 'PROBLEMA DE LENTIDÃO' || endPointResponse.data.terceiro_nivel == 'ABERTO')) {
|
||||||
|
|
||||||
|
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n${msg_endpoint_response}\n Estamos direcionando seu atendimento para o Suporte. Em breve você será atendido por um de nossos atendentes!`);
|
||||||
|
await transferTicket(0, wbot, ticket, contact)
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
|
// const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n *Status:* ${endPointResponse.data.status}\n *Data:* ${endPointResponse.data.data_chat ? endPointResponse.data.data_chat : ""}\n *Hora:* ${endPointResponse.data.hora_chat ? endPointResponse.data.hora_chat : ""} \n\n Por favor, aguarde atendimento e acompanhe sua solicitação no SOS.\n_Digite *0* para voltar ao menu principal._`);
|
||||||
|
const msg = await wbot.sendMessage(`${contact.number}@c.us`, `*Situação do chamado ${extractCallCode(msgAction.msgBody)}:*\n\n${msg_endpoint_response}\n Por favor, aguarde atendimento e acompanhe sua solicitação no SOS.\n_Digite *0* para voltar ao menu principal._`);
|
||||||
|
await verifyMessage(msg, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
botSendMessage(ticket, contact, wbot, `Desculpe, nao foi possível realizar a consulta!\n _Digite *0* para voltar ao menu principal._`)
|
||||||
|
}
|
||||||
|
|
||||||
|
} else if (msgAction.actions[0] == 'queue_transfer') {
|
||||||
|
|
||||||
|
console.log('>>>>>>>>>>>>>>> msgAction: ', msgAction, ' | msgAction.actions[1]: ', msgAction.actions[1])
|
||||||
|
|
||||||
|
const msg = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
|
||||||
|
await verifyMessage(msg, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
await transferTicket(+msgAction.actions[1], wbot, ticket, contact)
|
||||||
|
}
|
||||||
|
else if (msgAction.actions[0] == 'send_file'){
|
||||||
|
|
||||||
|
const sourcePath = path.join(__dirname,`../../../public/bot`)
|
||||||
|
|
||||||
|
const msg = await wbot.sendMessage(`${contact.number}@c.us`, msgAction.msgBody);
|
||||||
|
await verifyMessage(msg, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
await botSendMedia(ticket,contact,wbot,sourcePath, msgAction.actions[1])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// const linesOfBody = body.split('\n');
|
||||||
|
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, body);
|
||||||
|
await verifyMessage(sentMessage, ticket, contact);
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
// for(let message of linesOfBody) {
|
||||||
|
// const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, message);
|
||||||
|
// await verifyMessage(sentMessage, ticket, contact);
|
||||||
|
// await new Promise(f => setTimeout(f, 1000));
|
||||||
|
// }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const extractCallCode = (str: string) => {
|
||||||
|
if (str.includes('*') && str.indexOf('*') < str.lastIndexOf('*')) {
|
||||||
|
return (str.substring(str.indexOf('*'), str.lastIndexOf('*') + 1)).split('*').join('')
|
||||||
|
}
|
||||||
|
return ''
|
||||||
|
}
|
||||||
|
|
||||||
|
const sendDialogflowAwswer = async (
|
||||||
|
wbot: Session,
|
||||||
|
ticket: Ticket,
|
||||||
|
msg: WbotMessage,
|
||||||
|
contact: Contact,
|
||||||
|
chat: Chat
|
||||||
|
) => {
|
||||||
|
const session = await createDialogflowSessionWithModel(ticket.queue.dialogflow);
|
||||||
|
if (session === undefined) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
wbot.sendPresenceAvailable();
|
||||||
|
|
||||||
|
// console.log('typeof(msg.type): ', typeof (msg.type), ' | msg.type: ', msg.type)
|
||||||
|
|
||||||
|
if (msg.type != 'chat') {
|
||||||
|
botSendMessage(ticket, contact, wbot, `Desculpe, nao compreendi!\nEnvie apenas texto quando estiver interagindo com o bot!\n _Digite *0* para voltar ao menu principal._`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (msg.type == 'chat' && String(msg.body).length > 120) {
|
||||||
|
botSendMessage(ticket, contact, wbot, `Desculpe, nao compreendi!\nTexto acima de 120 caracteres!\n _Digite *0* para voltar ao menu principal._`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
let dialogFlowReply = await queryDialogFlow(
|
||||||
|
session,
|
||||||
|
ticket.queue.dialogflow.projectName,
|
||||||
|
msg.from,
|
||||||
|
msg.body,
|
||||||
|
ticket.queue.dialogflow.language
|
||||||
|
);
|
||||||
|
if (dialogFlowReply === null) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
chat.sendStateTyping();
|
||||||
|
|
||||||
|
await new Promise(f => setTimeout(f, 1000));
|
||||||
|
|
||||||
|
for (let message of dialogFlowReply) {
|
||||||
|
await sendDelayedMessages(wbot, ticket, contact, message.text.text[0]);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const verifyQueue = async (
|
const verifyQueue = async (
|
||||||
wbot: Session,
|
wbot: Session,
|
||||||
|
@ -195,8 +368,8 @@ const verifyQueue = async (
|
||||||
let choosenQueue = null
|
let choosenQueue = null
|
||||||
|
|
||||||
//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) {
|
if (botInfo.isOnQueue) {
|
||||||
|
|
||||||
|
@ -247,7 +420,12 @@ const verifyQueue = async (
|
||||||
ticketId: ticket.id
|
ticketId: ticket.id
|
||||||
});
|
});
|
||||||
|
|
||||||
data_ura.forEach((s, index) => { botOptions += `*${index + 1}* - ${s.option}\n` });
|
const _ticket = await ShowTicketService(ticket.id);
|
||||||
|
const chat = await msg.getChat();
|
||||||
|
await sendDialogflowAwswer(wbot, _ticket, msg, contact, chat);
|
||||||
|
return
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -302,6 +480,27 @@ const verifyQueue = async (
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const transferTicket = async (queueIndex: number, wbot: Session, ticket: Ticket, contact: Contact) => {
|
||||||
|
|
||||||
|
const botInfo = await BotIsOnQueue('botqueue')
|
||||||
|
|
||||||
|
const queuesWhatsGreetingMessage = await queuesOutBot(wbot, botInfo.botQueueId)
|
||||||
|
|
||||||
|
const queues = queuesWhatsGreetingMessage.queues
|
||||||
|
|
||||||
|
await botTransferTicket(queues[queueIndex], ticket, contact, wbot)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const botMsgActions = (params: string) => {
|
||||||
|
|
||||||
|
let lstActions = params.split('dialog_actions=')
|
||||||
|
let bodyMsg = lstActions[0].trim()
|
||||||
|
let actions = lstActions[1].split("=")
|
||||||
|
|
||||||
|
return { msgBody: bodyMsg, 'actions': [actions[0].trim(), actions[1].trim()] };
|
||||||
|
}
|
||||||
|
|
||||||
const isValidMsg = (msg: WbotMessage): boolean => {
|
const isValidMsg = (msg: WbotMessage): boolean => {
|
||||||
if (msg.from === "status@broadcast") return false;
|
if (msg.from === "status@broadcast") return false;
|
||||||
if (
|
if (
|
||||||
|
@ -341,6 +540,37 @@ const botTransferTicket = async (queues: Queue, ticket: Ticket, contact: Contact
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const botSendMedia = async (ticket: Ticket, contact: Contact, wbot: Session, mediaPath: string, fileNameExtension: string) => {
|
||||||
|
|
||||||
|
const debouncedSentMessage = debounce(
|
||||||
|
|
||||||
|
async () => {
|
||||||
|
|
||||||
|
// const sentMessage = await wbot.sendMessage(`${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us`, newMedia, { sendAudioAsVoice: true });
|
||||||
|
|
||||||
|
const newMedia = MessageMedia.fromFilePath(`${mediaPath}/${fileNameExtension}`);
|
||||||
|
|
||||||
|
const sentMessage = await wbot.sendMessage(`${contact.number}@c.us`, newMedia, {caption: 'this is my caption'});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// client.sendMessage("xxxxxxxx@c.us", media, {caption: "some caption"}); })();
|
||||||
|
|
||||||
|
// client.sendMessage(msg.from, attachmentData, { caption: 'Here\'s your requested media.' });
|
||||||
|
|
||||||
|
await ticket.update({ lastMessage: fileNameExtension });
|
||||||
|
|
||||||
|
verifyMessage(sentMessage, ticket, contact);
|
||||||
|
|
||||||
|
},
|
||||||
|
3000,
|
||||||
|
ticket.id
|
||||||
|
);
|
||||||
|
|
||||||
|
debouncedSentMessage();
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
const botSendMessage = (ticket: Ticket, contact: Contact, wbot: Session, msg: string) => {
|
const botSendMessage = (ticket: Ticket, contact: Contact, wbot: Session, msg: string) => {
|
||||||
|
|
||||||
|
@ -469,307 +699,14 @@ const handleMessage = async (
|
||||||
// O bot interage com o cliente e encaminha o atendimento para fila de atendende quando o usuário escolhe a opção falar com atendente
|
// O bot interage com o cliente e encaminha o atendimento para fila de atendende quando o usuário escolhe a opção falar com atendente
|
||||||
|
|
||||||
//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 (botInfo.isOnQueue && !msg.fromMe && ticket.userId == botInfo.userIdBot) {
|
||||||
|
|
||||||
|
await sendDialogflowAwswer(wbot, ticket, msg, contact, chat);
|
||||||
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`)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
//
|
|
||||||
|
|
||||||
|
|
||||||
// test del
|
|
||||||
|
|
||||||
// if (msg.body.trim() == 'broken') {
|
// if (msg.body.trim() == 'broken') {
|
||||||
// throw new Error('Throw makes it go boom!')
|
// throw new Error('Throw makes it go boom!')
|
||||||
|
|
|
@ -73,6 +73,7 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => {
|
||||||
name: "",
|
name: "",
|
||||||
number: "",
|
number: "",
|
||||||
email: "",
|
email: "",
|
||||||
|
useDialogflow: true,
|
||||||
};
|
};
|
||||||
|
|
||||||
const [contact, setContact] = useState(initialState);
|
const [contact, setContact] = useState(initialState);
|
||||||
|
|
|
@ -0,0 +1,285 @@
|
||||||
|
import React, { useState, useEffect } from "react";
|
||||||
|
|
||||||
|
import * as Yup from "yup";
|
||||||
|
import { Formik, Form, Field } from "formik";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
Dialog,
|
||||||
|
DialogActions,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
CircularProgress,
|
||||||
|
Select,
|
||||||
|
InputLabel,
|
||||||
|
MenuItem,
|
||||||
|
FormControl,
|
||||||
|
TextField,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
import { green } from "@material-ui/core/colors";
|
||||||
|
|
||||||
|
|
||||||
|
import { i18n } from "../../translate/i18n";
|
||||||
|
|
||||||
|
import api from "../../services/api";
|
||||||
|
import toastError from "../../errors/toastError";
|
||||||
|
|
||||||
|
const useStyles = makeStyles(theme => ({
|
||||||
|
root: {
|
||||||
|
display: "flex",
|
||||||
|
flexWrap: "wrap",
|
||||||
|
},
|
||||||
|
textField: {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
flex: 1,
|
||||||
|
},
|
||||||
|
|
||||||
|
btnWrapper: {
|
||||||
|
position: "relative",
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonProgress: {
|
||||||
|
color: green[500],
|
||||||
|
position: "absolute",
|
||||||
|
top: "50%",
|
||||||
|
left: "50%",
|
||||||
|
marginTop: -12,
|
||||||
|
marginLeft: -12,
|
||||||
|
},
|
||||||
|
btnLeft: {
|
||||||
|
display: "flex",
|
||||||
|
marginRight: "auto",
|
||||||
|
marginLeft: 12,
|
||||||
|
},
|
||||||
|
formControl: {
|
||||||
|
margin: theme.spacing(1),
|
||||||
|
minWidth: 240,
|
||||||
|
},
|
||||||
|
colorAdorment: {
|
||||||
|
width: 20,
|
||||||
|
height: 20,
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const DialogflowSchema = Yup.object().shape({
|
||||||
|
name: Yup.string()
|
||||||
|
.min(2, "Too Short!")
|
||||||
|
.max(50, "Too Long!")
|
||||||
|
.required("Required"),
|
||||||
|
projectName: Yup.string().min(3, "Too Short!").max(100, "Too Long!").required(),
|
||||||
|
jsonContent: Yup.string().min(3, "Too Short!").required(),
|
||||||
|
language: Yup.string().min(2, "Too Short!").max(50, "Too Long!").required(),
|
||||||
|
|
||||||
|
});
|
||||||
|
|
||||||
|
const DialogflowModal = ({ open, onClose, dialogflowId }) => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const initialState = {
|
||||||
|
name: "",
|
||||||
|
projectName: "",
|
||||||
|
jsonContent: "",
|
||||||
|
language: "",
|
||||||
|
};
|
||||||
|
|
||||||
|
const [dialogflow, setDialogflow] = useState(initialState);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
if (!dialogflowId) return;
|
||||||
|
try {
|
||||||
|
const { data } = await api.get(`/dialogflow/${dialogflowId}`);
|
||||||
|
setDialogflow(prevState => {
|
||||||
|
return { ...prevState, ...data };
|
||||||
|
});
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
setDialogflow({
|
||||||
|
name: "",
|
||||||
|
projectName: "",
|
||||||
|
jsonContent: "",
|
||||||
|
language: "",
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}, [dialogflowId, open]);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose();
|
||||||
|
setDialogflow(initialState);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleTestSession = async (event, values) => {
|
||||||
|
try {
|
||||||
|
const {projectName, jsonContent, language } = values
|
||||||
|
|
||||||
|
await api.post(`/dialogflow/testSession`, {projectName, jsonContent, language });
|
||||||
|
|
||||||
|
toast.success( i18n.t("dialogflowModal.messages.testSuccess") );
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveDialogflow = async values => {
|
||||||
|
try {
|
||||||
|
console.log(values)
|
||||||
|
if (dialogflowId) {
|
||||||
|
await api.put(`/dialogflow/${dialogflowId}`, values);
|
||||||
|
toast.success( i18n.t("dialogflowModal.messages.editSuccess") );
|
||||||
|
} else {
|
||||||
|
await api.post("/dialogflow", values);
|
||||||
|
toast.success( i18n.t("dialogflowModal.messages.addSuccess") );
|
||||||
|
}
|
||||||
|
handleClose();
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
|
||||||
|
<Dialog open={open} onClose={handleClose} scroll="paper">
|
||||||
|
<DialogTitle>
|
||||||
|
{dialogflowId
|
||||||
|
? `${i18n.t("dialogflowModal.title.edit")}`
|
||||||
|
: `${i18n.t("dialogflowModal.title.add")}`}
|
||||||
|
</DialogTitle>
|
||||||
|
<Formik
|
||||||
|
initialValues={dialogflow}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={DialogflowSchema}
|
||||||
|
onSubmit={(values, actions, event) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
console.log(actions);
|
||||||
|
handleSaveDialogflow(values);
|
||||||
|
actions.setSubmitting(false);
|
||||||
|
}, 400);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ touched, errors, isSubmitting, values }) => (
|
||||||
|
<Form>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={i18n.t("dialogflowModal.form.name")}
|
||||||
|
autoFocus
|
||||||
|
name="name"
|
||||||
|
error={touched.name && Boolean(errors.name)}
|
||||||
|
helperText={touched.name && errors.name}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
className={classes.textField}
|
||||||
|
/>
|
||||||
|
<FormControl
|
||||||
|
variant="outlined"
|
||||||
|
className={classes.formControl}
|
||||||
|
margin="dense"
|
||||||
|
>
|
||||||
|
<InputLabel id="language-selection-input-label">
|
||||||
|
{i18n.t("dialogflowModal.form.language")}
|
||||||
|
</InputLabel>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
as={Select}
|
||||||
|
label={i18n.t("dialogflowModal.form.language")}
|
||||||
|
name="language"
|
||||||
|
labelId="profile-selection-label"
|
||||||
|
error={touched.language && Boolean(errors.language)}
|
||||||
|
helperText={touched.language && errors.language}
|
||||||
|
id="language-selection"
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<MenuItem value="pt-BR">Portugues</MenuItem>
|
||||||
|
<MenuItem value="en">Inglês</MenuItem>
|
||||||
|
<MenuItem value="es">Espanhol</MenuItem>
|
||||||
|
</Field>
|
||||||
|
</FormControl>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={i18n.t("dialogflowModal.form.projectName")}
|
||||||
|
name="projectName"
|
||||||
|
error={touched.projectName && Boolean(errors.projectName)}
|
||||||
|
helperText={touched.projectName && errors.projectName}
|
||||||
|
fullWidth
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={i18n.t("dialogflowModal.form.jsonContent")}
|
||||||
|
type="jsonContent"
|
||||||
|
multiline
|
||||||
|
//inputRef={greetingRef}
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="jsonContent"
|
||||||
|
error={
|
||||||
|
touched.jsonContent && Boolean(errors.jsonContent)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.jsonContent && errors.jsonContent
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
//type="submit"
|
||||||
|
onClick={(e) => handleTestSession(e, values)}
|
||||||
|
color="inherit"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
name="testSession"
|
||||||
|
variant="outlined"
|
||||||
|
className={classes.btnLeft}
|
||||||
|
>
|
||||||
|
{i18n.t("dialogflowModal.buttons.test")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
onClick={handleClose}
|
||||||
|
color="secondary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
{i18n.t("dialogflowModal.buttons.cancel")}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="contained"
|
||||||
|
className={classes.btnWrapper}
|
||||||
|
>
|
||||||
|
{dialogflowId
|
||||||
|
? `${i18n.t("dialogflowModal.buttons.okEdit")}`
|
||||||
|
: `${i18n.t("dialogflowModal.buttons.okAdd")}`}
|
||||||
|
{isSubmitting && (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
className={classes.buttonProgress}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default DialogflowModal;
|
|
@ -13,14 +13,23 @@ import DialogActions from "@material-ui/core/DialogActions";
|
||||||
import DialogContent from "@material-ui/core/DialogContent";
|
import DialogContent from "@material-ui/core/DialogContent";
|
||||||
import DialogTitle from "@material-ui/core/DialogTitle";
|
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress";
|
||||||
|
import {
|
||||||
|
FormControl,
|
||||||
|
InputLabel,
|
||||||
|
Select,
|
||||||
|
MenuItem,
|
||||||
|
IconButton,
|
||||||
|
InputAdornment
|
||||||
|
} from "@material-ui/core"
|
||||||
|
|
||||||
|
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n";
|
||||||
|
|
||||||
import api from "../../services/api";
|
import api from "../../services/api";
|
||||||
import toastError from "../../errors/toastError";
|
import toastError from "../../errors/toastError";
|
||||||
import ColorPicker from "../ColorPicker";
|
import ColorPicker from "../ColorPicker";
|
||||||
import { IconButton, InputAdornment } from "@material-ui/core";
|
|
||||||
import { Colorize } from "@material-ui/icons";
|
import { Colorize } from "@material-ui/icons";
|
||||||
|
import useDialogflows from "../../hooks/useDialogflows";
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
root: {
|
root: {
|
||||||
|
@ -45,8 +54,8 @@ const useStyles = makeStyles(theme => ({
|
||||||
marginLeft: -12,
|
marginLeft: -12,
|
||||||
},
|
},
|
||||||
formControl: {
|
formControl: {
|
||||||
margin: theme.spacing(1),
|
marginRight: theme.spacing(1),
|
||||||
minWidth: 120,
|
minWidth: 200,
|
||||||
},
|
},
|
||||||
colorAdorment: {
|
colorAdorment: {
|
||||||
width: 20,
|
width: 20,
|
||||||
|
@ -61,6 +70,7 @@ const QueueSchema = Yup.object().shape({
|
||||||
.required("Required"),
|
.required("Required"),
|
||||||
color: Yup.string().min(3, "Too Short!").max(9, "Too Long!").required(),
|
color: Yup.string().min(3, "Too Short!").max(9, "Too Long!").required(),
|
||||||
greetingMessage: Yup.string(),
|
greetingMessage: Yup.string(),
|
||||||
|
dialogflowId: Yup.number(),
|
||||||
});
|
});
|
||||||
|
|
||||||
const QueueModal = ({ open, onClose, queueId }) => {
|
const QueueModal = ({ open, onClose, queueId }) => {
|
||||||
|
@ -70,10 +80,14 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
||||||
name: "",
|
name: "",
|
||||||
color: "",
|
color: "",
|
||||||
greetingMessage: "",
|
greetingMessage: "",
|
||||||
|
dialogflowId: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
const [colorPickerModalOpen, setColorPickerModalOpen] = useState(false);
|
const [colorPickerModalOpen, setColorPickerModalOpen] = useState(false);
|
||||||
const [queue, setQueue] = useState(initialState);
|
const [queue, setQueue] = useState(initialState);
|
||||||
|
const [dialogflows, setDialogflows] = useState([]);
|
||||||
|
const { findAll: findAllDialogflows } = useDialogflows();
|
||||||
|
|
||||||
const greetingRef = useRef();
|
const greetingRef = useRef();
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -81,6 +95,10 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
||||||
if (!queueId) return;
|
if (!queueId) return;
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get(`/queue/${queueId}`);
|
const { data } = await api.get(`/queue/${queueId}`);
|
||||||
|
if(data.dialogflowId === null) {
|
||||||
|
data.dialogflowId = "";
|
||||||
|
}
|
||||||
|
|
||||||
setQueue(prevState => {
|
setQueue(prevState => {
|
||||||
return { ...prevState, ...data };
|
return { ...prevState, ...data };
|
||||||
});
|
});
|
||||||
|
@ -94,10 +112,20 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
||||||
name: "",
|
name: "",
|
||||||
color: "",
|
color: "",
|
||||||
greetingMessage: "",
|
greetingMessage: "",
|
||||||
|
dialogflowId: "",
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
}, [queueId, open]);
|
}, [queueId, open]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const loadDialogflows = async () => {
|
||||||
|
const list = await findAllDialogflows();
|
||||||
|
setDialogflows(list);
|
||||||
|
}
|
||||||
|
loadDialogflows();
|
||||||
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||||
|
}, []);
|
||||||
|
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
onClose();
|
onClose();
|
||||||
setQueue(initialState);
|
setQueue(initialState);
|
||||||
|
@ -105,6 +133,10 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
||||||
|
|
||||||
const handleSaveQueue = async values => {
|
const handleSaveQueue = async values => {
|
||||||
try {
|
try {
|
||||||
|
if(values.dialogflowId === "") {
|
||||||
|
values.dialogflowId = null;
|
||||||
|
}
|
||||||
|
|
||||||
if (queueId) {
|
if (queueId) {
|
||||||
await api.put(`/queue/${queueId}`, values);
|
await api.put(`/queue/${queueId}`, values);
|
||||||
} else {
|
} else {
|
||||||
|
@ -213,6 +245,27 @@ const QueueModal = ({ open, onClose, queueId }) => {
|
||||||
margin="dense"
|
margin="dense"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
|
<FormControl
|
||||||
|
variant="outlined"
|
||||||
|
className={classes.formControl}
|
||||||
|
margin="dense"
|
||||||
|
>
|
||||||
|
<InputLabel id="dialogflow-selection-input-label">
|
||||||
|
{i18n.t("queueModal.form.dialogflow")}
|
||||||
|
</InputLabel>
|
||||||
|
<Field
|
||||||
|
as={Select}
|
||||||
|
label={i18n.t("queueModal.form.dialogflow")}
|
||||||
|
name="dialogflowId"
|
||||||
|
labelId="dialogflow-selection-label"
|
||||||
|
id="dialogflow-selection"
|
||||||
|
>
|
||||||
|
<MenuItem value={''}> </MenuItem>
|
||||||
|
{dialogflows.map((dialogflow) => (
|
||||||
|
<MenuItem key={dialogflow.id} value={dialogflow.id}>{dialogflow.name}</MenuItem>
|
||||||
|
))}
|
||||||
|
</Field>
|
||||||
|
</FormControl>
|
||||||
</DialogContent>
|
</DialogContent>
|
||||||
<DialogActions>
|
<DialogActions>
|
||||||
<Button
|
<Button
|
||||||
|
|
|
@ -12,7 +12,7 @@ import ButtonWithSpinner from "../ButtonWithSpinner";
|
||||||
import toastError from "../../errors/toastError";
|
import toastError from "../../errors/toastError";
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||||
|
|
||||||
import Modal from "../ChatEnd/ModalChatEnd";
|
import Modal from "../ChatEnd/ModalChatEnd";
|
||||||
import { render } from '@testing-library/react';
|
import { render } from '@testing-library/react';
|
||||||
|
|
||||||
import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption";
|
import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption";
|
||||||
|
@ -34,12 +34,13 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
||||||
const history = useHistory();
|
const history = useHistory();
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [useDialogflow, setUseDialogflow] = useState(ticket.contact.useDialogflow);
|
||||||
const ticketOptionsMenuOpen = Boolean(anchorEl);
|
const ticketOptionsMenuOpen = Boolean(anchorEl);
|
||||||
const { user } = useContext(AuthContext);
|
const { user } = useContext(AuthContext);
|
||||||
|
|
||||||
const { tabOption, setTabOption } = useContext(TabTicketContext);
|
const { tabOption, setTabOption } = useContext(TabTicketContext);
|
||||||
|
|
||||||
const handleOpenTicketOptionsMenu = e => {
|
const handleOpenTicketOptionsMenu = e => {
|
||||||
setAnchorEl(e.currentTarget);
|
setAnchorEl(e.currentTarget);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -47,83 +48,93 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const chatEndVal = (data) => {
|
|
||||||
|
|
||||||
if(data){
|
|
||||||
|
|
||||||
data = {...data, 'ticketId': ticket.id}
|
const chatEndVal = (data) => {
|
||||||
|
|
||||||
|
if (data) {
|
||||||
|
|
||||||
|
data = { ...data, 'ticketId': ticket.id }
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
handleUpdateTicketStatus(null, "closed", user?.id, data)
|
handleUpdateTicketStatus(null, "closed", user?.id, data)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleModal = (/*status, userId*/) => {
|
const handleModal = (/*status, userId*/) => {
|
||||||
|
|
||||||
|
render(<Modal
|
||||||
|
modal_header={'Finalização de Atendimento'}
|
||||||
|
func={chatEndVal}
|
||||||
|
statusChatEnd={statusChatEnd}
|
||||||
|
ticketId={ticket.id}
|
||||||
|
/>)
|
||||||
|
|
||||||
render(<Modal
|
|
||||||
modal_header={'Finalização de Atendimento'}
|
|
||||||
func={chatEndVal}
|
|
||||||
statusChatEnd={statusChatEnd}
|
|
||||||
ticketId={ticket.id}
|
|
||||||
/>)
|
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
|
|
||||||
|
|
||||||
setLoading(true);
|
|
||||||
try {
|
|
||||||
|
|
||||||
if(status==='closed'){
|
|
||||||
|
|
||||||
if (tabOption === 'search') {
|
const handleUpdateTicketStatus = async (e, status, userId, schedulingData = {}) => {
|
||||||
setTabOption('open')
|
|
||||||
}
|
|
||||||
|
|
||||||
await api.put(`/tickets/${ticket.id}`, {
|
setLoading(true);
|
||||||
status: status,
|
try {
|
||||||
userId: userId || null,
|
|
||||||
schedulingNotifyData: JSON.stringify(schedulingData)
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (status === 'closed') {
|
||||||
|
|
||||||
|
if (tabOption === 'search') {
|
||||||
|
setTabOption('open')
|
||||||
}
|
}
|
||||||
else{
|
|
||||||
|
|
||||||
if (tabOption === 'search') {
|
await api.put(`/tickets/${ticket.id}`, {
|
||||||
setTabOption('open')
|
status: status,
|
||||||
}
|
userId: userId || null,
|
||||||
|
schedulingNotifyData: JSON.stringify(schedulingData)
|
||||||
await api.put(`/tickets/${ticket.id}`, {
|
});
|
||||||
status: status,
|
|
||||||
userId: userId || null
|
|
||||||
});
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
|
||||||
setLoading(false);
|
if (tabOption === 'search') {
|
||||||
if (status === "open") {
|
setTabOption('open')
|
||||||
|
|
||||||
|
|
||||||
history.push(`/tickets/${ticket.id}`);
|
|
||||||
} else {
|
|
||||||
history.push("/tickets");
|
|
||||||
}
|
}
|
||||||
} catch (err) {
|
|
||||||
setLoading(false);
|
|
||||||
toastError(err);
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
await api.put(`/tickets/${ticket.id}`, {
|
||||||
|
status: status,
|
||||||
|
userId: userId || null
|
||||||
|
});
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
if (status === "open") {
|
||||||
|
|
||||||
|
|
||||||
|
history.push(`/tickets/${ticket.id}`);
|
||||||
|
} else {
|
||||||
|
history.push("/tickets");
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false);
|
||||||
|
toastError(err);
|
||||||
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleContactToggleUseDialogflow = async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const contact = await api.put(`/contacts/toggleUseDialogflow/${ticket.contact.id}`);
|
||||||
|
setUseDialogflow(contact.data.useDialogflow);
|
||||||
|
setLoading(false);
|
||||||
|
} catch (err) {
|
||||||
|
setLoading(false);
|
||||||
|
toastError(err);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div className={classes.actionButtons}>
|
<div className={classes.actionButtons}>
|
||||||
{ticket.status === "closed" && (
|
{ticket.status === "closed" && (
|
||||||
|
@ -138,7 +149,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
||||||
)}
|
)}
|
||||||
{ticket.status === "open" && (
|
{ticket.status === "open" && (
|
||||||
<>
|
<>
|
||||||
<ButtonWithSpinner style={{ marginRight: "70px" }}
|
<ButtonWithSpinner style={{ marginRight: "70px" }}
|
||||||
loading={loading}
|
loading={loading}
|
||||||
startIcon={<Replay />}
|
startIcon={<Replay />}
|
||||||
size="small"
|
size="small"
|
||||||
|
@ -146,16 +157,16 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
||||||
>
|
>
|
||||||
{i18n.t("messagesList.header.buttons.return")}
|
{i18n.t("messagesList.header.buttons.return")}
|
||||||
</ButtonWithSpinner>
|
</ButtonWithSpinner>
|
||||||
|
|
||||||
<ButtonWithSpinner
|
<ButtonWithSpinner
|
||||||
loading={loading}
|
loading={loading}
|
||||||
size="small"
|
size="small"
|
||||||
variant="contained"
|
variant="contained"
|
||||||
color="primary"
|
color="primary"
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
|
|
||||||
|
|
||||||
handleModal()
|
handleModal()
|
||||||
// handleUpdateTicketStatus(e, "closed", user?.id)
|
// handleUpdateTicketStatus(e, "closed", user?.id)
|
||||||
|
|
||||||
}}
|
}}
|
||||||
|
|
|
@ -0,0 +1,16 @@
|
||||||
|
function getConfig(name, defaultValue=null) {
|
||||||
|
// If inside a docker container, use window.ENV
|
||||||
|
if( window.ENV !== undefined ) {
|
||||||
|
return window.ENV[name] || defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
return process.env[name] || defaultValue;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getBackendUrl() {
|
||||||
|
return getConfig('REACT_APP_BACKEND_URL');
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getHoursCloseTicketsAuto() {
|
||||||
|
return getConfig('REACT_APP_HOURS_CLOSE_TICKETS_AUTO');
|
||||||
|
}
|
|
@ -0,0 +1,12 @@
|
||||||
|
import api from "../../services/api";
|
||||||
|
|
||||||
|
const useDialogflows = () => {
|
||||||
|
const findAll = async () => {
|
||||||
|
const { data } = await api.get("/dialogflow");
|
||||||
|
return data;
|
||||||
|
}
|
||||||
|
|
||||||
|
return { findAll };
|
||||||
|
};
|
||||||
|
|
||||||
|
export default useDialogflows;
|
|
@ -1,6 +1,7 @@
|
||||||
import React, { useContext, useEffect, useState } from "react";
|
import React, { useContext, useEffect, useState } from "react";
|
||||||
import { Link as RouterLink } from "react-router-dom";
|
import { Link as RouterLink } from "react-router-dom";
|
||||||
|
|
||||||
|
import DeviceHubOutlined from "@material-ui/icons/DeviceHubOutlined"
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from "@material-ui/core/ListItem";
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText";
|
||||||
|
@ -130,11 +131,18 @@ const MainListItems = (props) => {
|
||||||
role={user.profile}
|
role={user.profile}
|
||||||
perform="settings-view:show"
|
perform="settings-view:show"
|
||||||
yes={() => (
|
yes={() => (
|
||||||
<ListItemLink
|
<>
|
||||||
to="/settings"
|
<ListItemLink
|
||||||
primary={i18n.t("mainDrawer.listItems.settings")}
|
to="/settings"
|
||||||
icon={<SettingsOutlinedIcon />}
|
primary={i18n.t("mainDrawer.listItems.settings")}
|
||||||
/>
|
icon={<SettingsOutlinedIcon />}
|
||||||
|
/>
|
||||||
|
<ListItemLink
|
||||||
|
to="/Dialogflows"
|
||||||
|
primary={i18n.t("mainDrawer.listItems.dialogflow")}
|
||||||
|
icon={<DeviceHubOutlined />}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
|
@ -0,0 +1,221 @@
|
||||||
|
import { format, parseISO } from "date-fns";
|
||||||
|
import React, { useReducer, useState } from "react";
|
||||||
|
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
IconButton,
|
||||||
|
makeStyles,
|
||||||
|
Paper,
|
||||||
|
Table,
|
||||||
|
TableBody,
|
||||||
|
TableCell,
|
||||||
|
TableHead,
|
||||||
|
TableRow
|
||||||
|
} from "@material-ui/core";
|
||||||
|
|
||||||
|
import { DeleteOutline, Edit } from "@material-ui/icons";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
import ConfirmationModal from "../../components/ConfirmationModal";
|
||||||
|
import DialogflowModal from "../../components/DialogflowModal";
|
||||||
|
import MainContainer from "../../components/MainContainer";
|
||||||
|
import MainHeader from "../../components/MainHeader";
|
||||||
|
import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper";
|
||||||
|
import TableRowSkeleton from "../../components/TableRowSkeleton";
|
||||||
|
import Title from "../../components/Title";
|
||||||
|
import toastError from "../../errors/toastError";
|
||||||
|
import api from "../../services/api";
|
||||||
|
import { i18n } from "../../translate/i18n";
|
||||||
|
import useLoadData from "../Queues/useLoadData.js";
|
||||||
|
import useSocket from "../Queues/useSocket";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
mainPaper: {
|
||||||
|
flex: 1,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
overflowY: "scroll",
|
||||||
|
...theme.scrollbarStyles,
|
||||||
|
},
|
||||||
|
customTableCell: {
|
||||||
|
display: "flex",
|
||||||
|
alignItems: "center",
|
||||||
|
justifyContent: "center",
|
||||||
|
},
|
||||||
|
}));
|
||||||
|
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
if (action.type === "LOAD_DIALOGFLOWS") {
|
||||||
|
const dialoflows = action.payload;
|
||||||
|
const newDialogflows = [];
|
||||||
|
|
||||||
|
dialoflows.forEach((dialogflow) => {
|
||||||
|
const dialogflowIndex = state.findIndex((q) => q.id === dialogflow.id);
|
||||||
|
if (dialogflowIndex !== -1) {
|
||||||
|
state[dialogflowIndex] = dialogflow;
|
||||||
|
} else {
|
||||||
|
newDialogflows.push(dialogflow);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return [...state, ...newDialogflows];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === "UPDATE_DIALOGFLOWS") {
|
||||||
|
const dialogflow = action.payload;
|
||||||
|
const dialogflowIndex = state.findIndex((u) => u.id === dialogflow.id);
|
||||||
|
|
||||||
|
if (dialogflowIndex !== -1) {
|
||||||
|
state[dialogflowIndex] = dialogflow;
|
||||||
|
return [...state];
|
||||||
|
} else {
|
||||||
|
return [dialogflow, ...state];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === "DELETE_DIALOGFLOW") {
|
||||||
|
const dialogflowId = action.payload;
|
||||||
|
const dialogflowIndex = state.findIndex((q) => q.id === dialogflowId);
|
||||||
|
if (dialogflowIndex !== -1) {
|
||||||
|
state.splice(dialogflowIndex, 1);
|
||||||
|
}
|
||||||
|
return [...state];
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === "RESET") {
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const Dialogflows = () => {
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
const [dialogflows, dispatch] = useReducer(reducer, []);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
|
||||||
|
const [dialogflowModalOpen, setDialogflowModalOpen] = useState(false);
|
||||||
|
const [selectedDialogflow, setSelectedDialogflow] = useState(null);
|
||||||
|
const [confirmModalOpen, setConfirmModalOpen] = useState(false);
|
||||||
|
|
||||||
|
useLoadData(setLoading, dispatch, '/dialogflow', 'LOAD_DIALOGFLOWS');
|
||||||
|
useSocket(dispatch, 'dialogflow', 'UPDATE_DIALOGFLOWS', 'DELETE_DIALOGFLOW', 'dialogflow', 'dialogflowId');
|
||||||
|
|
||||||
|
const handleOpenDialogflowModal = () => {
|
||||||
|
setDialogflowModalOpen(true);
|
||||||
|
setSelectedDialogflow(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseDialogflowModal = () => {
|
||||||
|
setDialogflowModalOpen(false);
|
||||||
|
setSelectedDialogflow(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleEditDialogflow = (dialogflow) => {
|
||||||
|
setSelectedDialogflow(dialogflow);
|
||||||
|
setDialogflowModalOpen(true);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleCloseConfirmationModal = () => {
|
||||||
|
setConfirmModalOpen(false);
|
||||||
|
setSelectedDialogflow(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDeleteDialogflow = async (dialogflowId) => {
|
||||||
|
try {
|
||||||
|
await api.delete(`/dialogflow/${dialogflowId}`);
|
||||||
|
toast.success(i18n.t("Dialogflow deleted successfully!"));
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err);
|
||||||
|
}
|
||||||
|
setSelectedDialogflow(null);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<MainContainer>
|
||||||
|
<ConfirmationModal
|
||||||
|
title={
|
||||||
|
selectedDialogflow &&
|
||||||
|
`${i18n.t("dialogflows.confirmationModal.deleteTitle")} ${
|
||||||
|
selectedDialogflow.name
|
||||||
|
}?`
|
||||||
|
}
|
||||||
|
open={confirmModalOpen}
|
||||||
|
onClose={handleCloseConfirmationModal}
|
||||||
|
onConfirm={() => handleDeleteDialogflow(selectedDialogflow.id)}
|
||||||
|
>
|
||||||
|
{i18n.t("dialogflows.confirmationModal.deleteMessage")}
|
||||||
|
</ConfirmationModal>
|
||||||
|
<DialogflowModal
|
||||||
|
open={dialogflowModalOpen}
|
||||||
|
onClose={handleCloseDialogflowModal}
|
||||||
|
dialogflowId={selectedDialogflow?.id}
|
||||||
|
/>
|
||||||
|
<MainHeader>
|
||||||
|
<Title>{i18n.t("dialogflows.title")}</Title>
|
||||||
|
<MainHeaderButtonsWrapper>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleOpenDialogflowModal}
|
||||||
|
>
|
||||||
|
{i18n.t("dialogflows.buttons.add")}
|
||||||
|
</Button>
|
||||||
|
</MainHeaderButtonsWrapper>
|
||||||
|
</MainHeader>
|
||||||
|
<Paper className={classes.mainPaper} variant="outlined">
|
||||||
|
<Table size="small">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell align="center">
|
||||||
|
{i18n.t("dialogflows.table.name")}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{i18n.t("dialogflows.table.projectName")}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{i18n.t("dialogflows.table.language")}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{i18n.t("dialogflows.table.lastUpdate")}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{i18n.t("dialogflows.table.actions")}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
<>
|
||||||
|
{dialogflows.map((dialogflow) => (
|
||||||
|
<TableRow key={dialogflow.id}>
|
||||||
|
<TableCell align="center">{dialogflow.name}</TableCell>
|
||||||
|
<TableCell align="center">{ dialogflow.projectName}</TableCell>
|
||||||
|
<TableCell align="center">{dialogflow.language}</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
{format(parseISO(dialogflow.updatedAt), "dd/MM/yy HH:mm")}
|
||||||
|
</TableCell>
|
||||||
|
<TableCell align="center">
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => handleEditDialogflow(dialogflow)}
|
||||||
|
><Edit />
|
||||||
|
</IconButton>
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() => {
|
||||||
|
setSelectedDialogflow(dialogflow);
|
||||||
|
setConfirmModalOpen(true);
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteOutline />
|
||||||
|
</IconButton>
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
{loading && <TableRowSkeleton columns={4} />}
|
||||||
|
</>
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</Paper>
|
||||||
|
</MainContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default Dialogflows;
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import toastError from "../../errors/toastError";
|
||||||
|
import api from "../../services/api";
|
||||||
|
|
||||||
|
const useLoadData = (setLoading, dispatch, route, dispatchType) => {
|
||||||
|
useEffect(() => {
|
||||||
|
(async () => {
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data } = await api.get(route);
|
||||||
|
dispatch({ type: dispatchType, payload: data });
|
||||||
|
|
||||||
|
setLoading(false);
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err);
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
})();
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useLoadData;
|
|
@ -0,0 +1,24 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
|
import openSocket from "../../services/socket-io";
|
||||||
|
|
||||||
|
const useSocket = (dispatch, socketEvent, typeUpdate, typeDelete, payloadUpdate, payloadDelete) => {
|
||||||
|
useEffect(() => {
|
||||||
|
const socket = openSocket();
|
||||||
|
|
||||||
|
socket.on(socketEvent, (data) => {
|
||||||
|
if (data.action === "update" || data.action === "create") {
|
||||||
|
dispatch({ type: typeUpdate, payload: data[payloadUpdate] });
|
||||||
|
}
|
||||||
|
|
||||||
|
if (data.action === "delete") {
|
||||||
|
dispatch({ type: typeDelete, payload: data[payloadDelete] });
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.disconnect();
|
||||||
|
};
|
||||||
|
}, []);
|
||||||
|
}
|
||||||
|
|
||||||
|
export default useSocket;
|
|
@ -21,7 +21,7 @@ import { AuthProvider } from "../context/Auth/AuthContext";
|
||||||
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
|
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
|
||||||
import Route from "./Route";
|
import Route from "./Route";
|
||||||
|
|
||||||
|
import Dialogflows from "../pages/Dialogflow/";
|
||||||
|
|
||||||
const Routes = () => {
|
const Routes = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -47,6 +47,7 @@ const Routes = () => {
|
||||||
<Route exact path="/quickAnswers" component={QuickAnswers} isPrivate />
|
<Route exact path="/quickAnswers" component={QuickAnswers} isPrivate />
|
||||||
<Route exact path="/Settings" component={Settings} isPrivate />
|
<Route exact path="/Settings" component={Settings} isPrivate />
|
||||||
<Route exact path="/Queues" component={Queues} isPrivate />
|
<Route exact path="/Queues" component={Queues} isPrivate />
|
||||||
|
<Route exact path="/Dialogflows" component={Dialogflows} isPrivate />
|
||||||
</LoggedInLayout>
|
</LoggedInLayout>
|
||||||
</WhatsAppsProvider>
|
</WhatsAppsProvider>
|
||||||
</Switch>
|
</Switch>
|
||||||
|
|
|
@ -0,0 +1,8 @@
|
||||||
|
import openSocket from "socket.io-client";
|
||||||
|
import { getBackendUrl } from "../config";
|
||||||
|
|
||||||
|
function connectToSocket() {
|
||||||
|
return openSocket(getBackendUrl());
|
||||||
|
}
|
||||||
|
|
||||||
|
export default connectToSocket;
|
|
@ -155,6 +155,7 @@ const messages = {
|
||||||
email: "Email",
|
email: "Email",
|
||||||
extraName: "Field name",
|
extraName: "Field name",
|
||||||
extraValue: "Value",
|
extraValue: "Value",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
addExtraInfo: "Add information",
|
addExtraInfo: "Add information",
|
||||||
|
@ -188,6 +189,7 @@ const messages = {
|
||||||
form: {
|
form: {
|
||||||
name: "Name",
|
name: "Name",
|
||||||
color: "Color",
|
color: "Color",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
greetingMessage: "Greeting Message",
|
greetingMessage: "Greeting Message",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -196,6 +198,29 @@ const messages = {
|
||||||
cancel: "Cancel",
|
cancel: "Cancel",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialogflowModal: {
|
||||||
|
title: {
|
||||||
|
add: "Add Project",
|
||||||
|
edit: "Edit Project",
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
name: "Name",
|
||||||
|
projectName: "Project Name",
|
||||||
|
language: "Language",
|
||||||
|
jsonContent: "JsonContent"
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
okAdd: "Add",
|
||||||
|
okEdit: "Save",
|
||||||
|
cancel: "Cancel",
|
||||||
|
test: "Bot Test",
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
testSuccess: "Dialogflow test successfully",
|
||||||
|
addSuccess: "Dialogflow added successfully",
|
||||||
|
editSuccess: "Dialogflow edited successfully",
|
||||||
|
}
|
||||||
|
},
|
||||||
userModal: {
|
userModal: {
|
||||||
title: {
|
title: {
|
||||||
add: "Add user",
|
add: "Add user",
|
||||||
|
@ -206,6 +231,7 @@ const messages = {
|
||||||
email: "Email",
|
email: "Email",
|
||||||
password: "Password",
|
password: "Password",
|
||||||
profile: "Profile",
|
profile: "Profile",
|
||||||
|
whatsapp: "Default Connection",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
okAdd: "Add",
|
okAdd: "Add",
|
||||||
|
@ -248,7 +274,9 @@ const messages = {
|
||||||
title: "Transfer Ticket",
|
title: "Transfer Ticket",
|
||||||
fieldLabel: "Type to search for users",
|
fieldLabel: "Type to search for users",
|
||||||
fieldQueueLabel: "Transfer to queue",
|
fieldQueueLabel: "Transfer to queue",
|
||||||
|
fieldConnectionLabel: "Transfer to connection",
|
||||||
fieldQueuePlaceholder: "Please select a queue",
|
fieldQueuePlaceholder: "Please select a queue",
|
||||||
|
fieldConnectionPlaceholder: "Please select a connection",
|
||||||
noOptions: "No user found with this name",
|
noOptions: "No user found with this name",
|
||||||
buttons: {
|
buttons: {
|
||||||
ok: "Transfer",
|
ok: "Transfer",
|
||||||
|
@ -260,6 +288,7 @@ const messages = {
|
||||||
assignedHeader: "Working on",
|
assignedHeader: "Working on",
|
||||||
noTicketsTitle: "Nothing here!",
|
noTicketsTitle: "Nothing here!",
|
||||||
noTicketsMessage: "No tickets found with this status or search term.",
|
noTicketsMessage: "No tickets found with this status or search term.",
|
||||||
|
connectionTitle: "Connection that is currently being used.",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: "Accept",
|
accept: "Accept",
|
||||||
},
|
},
|
||||||
|
@ -284,6 +313,7 @@ const messages = {
|
||||||
administration: "Administration",
|
administration: "Administration",
|
||||||
users: "Users",
|
users: "Users",
|
||||||
settings: "Settings",
|
settings: "Settings",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
user: {
|
user: {
|
||||||
|
@ -312,6 +342,24 @@ const messages = {
|
||||||
"Are you sure? It cannot be reverted! Tickets in this queue will still exist, but will not have any queues assigned.",
|
"Are you sure? It cannot be reverted! Tickets in this queue will still exist, but will not have any queues assigned.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialogflows: {
|
||||||
|
title: "Dialogflow",
|
||||||
|
table: {
|
||||||
|
name: "Name",
|
||||||
|
projectName: "Project Name",
|
||||||
|
lamguage: "Language",
|
||||||
|
lastUpdate:"Last Update",
|
||||||
|
actions: "Actions",
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
add: "Add Project",
|
||||||
|
},
|
||||||
|
confirmationModal: {
|
||||||
|
deleteTitle: "Delete",
|
||||||
|
deleteMessage:
|
||||||
|
"Are you sure? It cannot be reverted!",
|
||||||
|
},
|
||||||
|
},
|
||||||
queueSelect: {
|
queueSelect: {
|
||||||
inputLabel: "Queues",
|
inputLabel: "Queues",
|
||||||
},
|
},
|
||||||
|
@ -340,6 +388,7 @@ const messages = {
|
||||||
name: "Name",
|
name: "Name",
|
||||||
email: "Email",
|
email: "Email",
|
||||||
profile: "Profile",
|
profile: "Profile",
|
||||||
|
whatsapp: "Default Connection",
|
||||||
actions: "Actions",
|
actions: "Actions",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -371,6 +420,7 @@ const messages = {
|
||||||
header: {
|
header: {
|
||||||
assignedTo: "Assigned to:",
|
assignedTo: "Assigned to:",
|
||||||
buttons: {
|
buttons: {
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
return: "Return",
|
return: "Return",
|
||||||
resolve: "Resolve",
|
resolve: "Resolve",
|
||||||
reopen: "Reopen",
|
reopen: "Reopen",
|
||||||
|
@ -392,6 +442,7 @@ const messages = {
|
||||||
},
|
},
|
||||||
ticketOptionsMenu: {
|
ticketOptionsMenu: {
|
||||||
delete: "Delete",
|
delete: "Delete",
|
||||||
|
useQueues: "¿Usar colas?",
|
||||||
transfer: "Transfer",
|
transfer: "Transfer",
|
||||||
confirmationModal: {
|
confirmationModal: {
|
||||||
title: "Delete ticket #",
|
title: "Delete ticket #",
|
||||||
|
@ -453,6 +504,9 @@ const messages = {
|
||||||
"This color is already in use, pick another one.",
|
"This color is already in use, pick another one.",
|
||||||
ERR_WAPP_GREETING_REQUIRED:
|
ERR_WAPP_GREETING_REQUIRED:
|
||||||
"Greeting message is required if there is more than one queue.",
|
"Greeting message is required if there is more than one queue.",
|
||||||
|
ERR_NO_DIALOG_FOUND: "No Dialogflow found with this ID.",
|
||||||
|
ERR_TEST_SESSION_DIALOG: "Error creating DialogFlow session",
|
||||||
|
ERR_TEST_REPLY_DIALOG: "Error testing DialogFlow configuration",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -158,6 +158,7 @@ const messages = {
|
||||||
email: "Correo Electrónico",
|
email: "Correo Electrónico",
|
||||||
extraName: "Nombre del Campo",
|
extraName: "Nombre del Campo",
|
||||||
extraValue: "Valor",
|
extraValue: "Valor",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
addExtraInfo: "Agregar información",
|
addExtraInfo: "Agregar información",
|
||||||
|
@ -191,6 +192,7 @@ const messages = {
|
||||||
form: {
|
form: {
|
||||||
name: "Nombre",
|
name: "Nombre",
|
||||||
color: "Color",
|
color: "Color",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
greetingMessage: "Mensaje de saludo",
|
greetingMessage: "Mensaje de saludo",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -199,6 +201,29 @@ const messages = {
|
||||||
cancel: "Cancelar",
|
cancel: "Cancelar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialogflowModal: {
|
||||||
|
title: {
|
||||||
|
add: "Agregar cola",
|
||||||
|
edit: "Editar cola",
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
name: "Nombre",
|
||||||
|
projectName: "Nombre del proyecto",
|
||||||
|
language: "Idioma",
|
||||||
|
jsonContent: "JsonContent",
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
okAdd: "Añadir",
|
||||||
|
okEdit: "Ahorrar",
|
||||||
|
cancel: "Cancelar",
|
||||||
|
test: "Testar Bot",
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
testSuccess: "Prueba Dialogflow con éxito",
|
||||||
|
addSuccess: "Dialogflow agregado con éxito",
|
||||||
|
editSuccess: "Dialogflow editado con éxito",
|
||||||
|
}
|
||||||
|
},
|
||||||
userModal: {
|
userModal: {
|
||||||
title: {
|
title: {
|
||||||
add: "Agregar usuario",
|
add: "Agregar usuario",
|
||||||
|
@ -209,6 +234,7 @@ const messages = {
|
||||||
email: "Correo Electrónico",
|
email: "Correo Electrónico",
|
||||||
password: "Contraseña",
|
password: "Contraseña",
|
||||||
profile: "Perfil",
|
profile: "Perfil",
|
||||||
|
whatsapp: "Conexión estándar",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
okAdd: "Agregar",
|
okAdd: "Agregar",
|
||||||
|
@ -251,7 +277,9 @@ const messages = {
|
||||||
title: "Transferir Ticket",
|
title: "Transferir Ticket",
|
||||||
fieldLabel: "Escriba para buscar usuarios",
|
fieldLabel: "Escriba para buscar usuarios",
|
||||||
fieldQueueLabel: "Transferir a la cola",
|
fieldQueueLabel: "Transferir a la cola",
|
||||||
|
fieldConnectionLabel: "Transferir to conexión",
|
||||||
fieldQueuePlaceholder: "Seleccione una cola",
|
fieldQueuePlaceholder: "Seleccione una cola",
|
||||||
|
fieldConnectionPlaceholder: "Seleccione una conexión",
|
||||||
noOptions: "No se encontraron usuarios con ese nombre",
|
noOptions: "No se encontraron usuarios con ese nombre",
|
||||||
buttons: {
|
buttons: {
|
||||||
ok: "Transferir",
|
ok: "Transferir",
|
||||||
|
@ -262,6 +290,7 @@ const messages = {
|
||||||
pendingHeader: "Cola",
|
pendingHeader: "Cola",
|
||||||
assignedHeader: "Trabajando en",
|
assignedHeader: "Trabajando en",
|
||||||
noTicketsTitle: "¡Nada acá!",
|
noTicketsTitle: "¡Nada acá!",
|
||||||
|
connectionTitle: "Conexión que se está utilizando actualmente.",
|
||||||
noTicketsMessage:
|
noTicketsMessage:
|
||||||
"No se encontraron tickets con este estado o término de búsqueda",
|
"No se encontraron tickets con este estado o término de búsqueda",
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -280,6 +309,7 @@ const messages = {
|
||||||
mainDrawer: {
|
mainDrawer: {
|
||||||
listItems: {
|
listItems: {
|
||||||
dashboard: "Dashboard",
|
dashboard: "Dashboard",
|
||||||
|
|
||||||
connections: "Conexiones",
|
connections: "Conexiones",
|
||||||
tickets: "Tickets",
|
tickets: "Tickets",
|
||||||
contacts: "Contactos",
|
contacts: "Contactos",
|
||||||
|
@ -288,6 +318,7 @@ const messages = {
|
||||||
administration: "Administración",
|
administration: "Administración",
|
||||||
users: "Usuarios",
|
users: "Usuarios",
|
||||||
settings: "Configuración",
|
settings: "Configuración",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
user: {
|
user: {
|
||||||
|
@ -316,6 +347,24 @@ const messages = {
|
||||||
"¿Estás seguro? ¡Esta acción no se puede revertir! Los tickets en esa cola seguirán existiendo, pero ya no tendrán ninguna cola asignada.",
|
"¿Estás seguro? ¡Esta acción no se puede revertir! Los tickets en esa cola seguirán existiendo, pero ya no tendrán ninguna cola asignada.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialoflows: {
|
||||||
|
title: "Dialogflow",
|
||||||
|
table: {
|
||||||
|
name: "Nombre",
|
||||||
|
projectName: "Nombre del proyecto",
|
||||||
|
language: "Idioma",
|
||||||
|
lastUpdate: "Última Actualización",
|
||||||
|
actions: "Comportamiento",
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
add: "Agregar proyecto",
|
||||||
|
},
|
||||||
|
confirmationModal: {
|
||||||
|
deleteTitle: "Eliminar",
|
||||||
|
deleteMessage:
|
||||||
|
"¿Estás seguro? ¡Esta acción no se puede revertir!",
|
||||||
|
},
|
||||||
|
},
|
||||||
queueSelect: {
|
queueSelect: {
|
||||||
inputLabel: "Linhas",
|
inputLabel: "Linhas",
|
||||||
},
|
},
|
||||||
|
@ -345,6 +394,7 @@ const messages = {
|
||||||
name: "Nombre",
|
name: "Nombre",
|
||||||
email: "Correo Electrónico",
|
email: "Correo Electrónico",
|
||||||
profile: "Perfil",
|
profile: "Perfil",
|
||||||
|
whatsapp: "Conexión estándar",
|
||||||
actions: "Acciones",
|
actions: "Acciones",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -376,6 +426,7 @@ const messages = {
|
||||||
header: {
|
header: {
|
||||||
assignedTo: "Asignado a:",
|
assignedTo: "Asignado a:",
|
||||||
buttons: {
|
buttons: {
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
return: "Devolver",
|
return: "Devolver",
|
||||||
resolve: "Resolver",
|
resolve: "Resolver",
|
||||||
reopen: "Reabrir",
|
reopen: "Reabrir",
|
||||||
|
@ -398,6 +449,7 @@ const messages = {
|
||||||
},
|
},
|
||||||
ticketOptionsMenu: {
|
ticketOptionsMenu: {
|
||||||
delete: "Borrar",
|
delete: "Borrar",
|
||||||
|
useQueues: "Use Queues",
|
||||||
transfer: "Transferir",
|
transfer: "Transferir",
|
||||||
confirmationModal: {
|
confirmationModal: {
|
||||||
title: "¿Borrar ticket #",
|
title: "¿Borrar ticket #",
|
||||||
|
@ -460,6 +512,9 @@ const messages = {
|
||||||
"Este color ya está en uso, elija otro.",
|
"Este color ya está en uso, elija otro.",
|
||||||
ERR_WAPP_GREETING_REQUIRED:
|
ERR_WAPP_GREETING_REQUIRED:
|
||||||
"El mensaje de saludo es obligatorio cuando hay más de una cola.",
|
"El mensaje de saludo es obligatorio cuando hay más de una cola.",
|
||||||
|
ERR_NO_DIALOG_FOUND: "No se encontró Dialogflow con este ID.",
|
||||||
|
ERR_TEST_SESSION_DIALOG: "Error al crear la sesión de dialogflow",
|
||||||
|
ERR_TEST_REPLY_DIALOG: "Error al probar la configuración de DialogFlow",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
|
@ -80,8 +80,7 @@ const messages = {
|
||||||
qrcode: {
|
qrcode: {
|
||||||
title: "Esperando leitura do QR Code",
|
title: "Esperando leitura do QR Code",
|
||||||
content:
|
content:
|
||||||
// "Clique no botão 'QR CODE' e leia o QR Code com o seu celular para iniciar a sessão",
|
"Clique no botão 'QR CODE' e leia o QR Code com o seu celular para iniciar a sessão",
|
||||||
"Entre em contato com o suporte para realizar a leitura do QR code",
|
|
||||||
},
|
},
|
||||||
connected: {
|
connected: {
|
||||||
title: "Conexão estabelecida!",
|
title: "Conexão estabelecida!",
|
||||||
|
@ -158,6 +157,7 @@ const messages = {
|
||||||
email: "Email",
|
email: "Email",
|
||||||
extraName: "Nome do campo",
|
extraName: "Nome do campo",
|
||||||
extraValue: "Valor",
|
extraValue: "Valor",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
addExtraInfo: "Adicionar informação",
|
addExtraInfo: "Adicionar informação",
|
||||||
|
@ -191,6 +191,7 @@ const messages = {
|
||||||
form: {
|
form: {
|
||||||
name: "Nome",
|
name: "Nome",
|
||||||
color: "Cor",
|
color: "Cor",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
greetingMessage: "Mensagem de saudação",
|
greetingMessage: "Mensagem de saudação",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -199,6 +200,29 @@ const messages = {
|
||||||
cancel: "Cancelar",
|
cancel: "Cancelar",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialogflowModal: {
|
||||||
|
title: {
|
||||||
|
add: "Adicionar projeto",
|
||||||
|
edit: "Editar projeto",
|
||||||
|
},
|
||||||
|
form: {
|
||||||
|
name: "Nome",
|
||||||
|
projectName: "Nome do Projeto",
|
||||||
|
language: "Linguagem",
|
||||||
|
jsonContent: "JsonContent",
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
okAdd: "Adicionar",
|
||||||
|
okEdit: "Salvar",
|
||||||
|
cancel: "Cancelar",
|
||||||
|
test: "Testar Bot",
|
||||||
|
},
|
||||||
|
messages: {
|
||||||
|
testSuccess: "Dialogflow testado com sucesso!",
|
||||||
|
addSuccess: "Dialogflow adicionado com sucesso.",
|
||||||
|
editSuccess: "Dialogflow editado com sucesso.",
|
||||||
|
}
|
||||||
|
},
|
||||||
userModal: {
|
userModal: {
|
||||||
title: {
|
title: {
|
||||||
add: "Adicionar usuário",
|
add: "Adicionar usuário",
|
||||||
|
@ -209,6 +233,7 @@ const messages = {
|
||||||
email: "Email",
|
email: "Email",
|
||||||
password: "Senha",
|
password: "Senha",
|
||||||
profile: "Perfil",
|
profile: "Perfil",
|
||||||
|
whatsapp: "Conexão Padrão",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
okAdd: "Adicionar",
|
okAdd: "Adicionar",
|
||||||
|
@ -241,7 +266,7 @@ const messages = {
|
||||||
search: { title: "Busca" },
|
search: { title: "Busca" },
|
||||||
},
|
},
|
||||||
search: {
|
search: {
|
||||||
placeholder: "Busca telefone/nome",
|
placeholder: "Buscar tickets e mensagens",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
showAll: "Todos",
|
showAll: "Todos",
|
||||||
|
@ -251,7 +276,9 @@ const messages = {
|
||||||
title: "Transferir Ticket",
|
title: "Transferir Ticket",
|
||||||
fieldLabel: "Digite para buscar usuários",
|
fieldLabel: "Digite para buscar usuários",
|
||||||
fieldQueueLabel: "Transferir para fila",
|
fieldQueueLabel: "Transferir para fila",
|
||||||
|
fieldConnectionLabel: "Transferir para conexão",
|
||||||
fieldQueuePlaceholder: "Selecione uma fila",
|
fieldQueuePlaceholder: "Selecione uma fila",
|
||||||
|
fieldConnectionPlaceholder: "Selecione uma conexão",
|
||||||
noOptions: "Nenhum usuário encontrado com esse nome",
|
noOptions: "Nenhum usuário encontrado com esse nome",
|
||||||
buttons: {
|
buttons: {
|
||||||
ok: "Transferir",
|
ok: "Transferir",
|
||||||
|
@ -264,6 +291,7 @@ const messages = {
|
||||||
noTicketsTitle: "Nada aqui!",
|
noTicketsTitle: "Nada aqui!",
|
||||||
noTicketsMessage:
|
noTicketsMessage:
|
||||||
"Nenhum ticket encontrado com esse status ou termo pesquisado",
|
"Nenhum ticket encontrado com esse status ou termo pesquisado",
|
||||||
|
connectionTitle: "Conexão que está sendo utilizada atualmente.",
|
||||||
buttons: {
|
buttons: {
|
||||||
accept: "Aceitar",
|
accept: "Aceitar",
|
||||||
},
|
},
|
||||||
|
@ -279,7 +307,7 @@ const messages = {
|
||||||
},
|
},
|
||||||
mainDrawer: {
|
mainDrawer: {
|
||||||
listItems: {
|
listItems: {
|
||||||
dashboard: "Dashboard",
|
dashboard: "Dashboard",
|
||||||
connections: "Conexões",
|
connections: "Conexões",
|
||||||
tickets: "Tickets",
|
tickets: "Tickets",
|
||||||
contacts: "Contatos",
|
contacts: "Contatos",
|
||||||
|
@ -288,6 +316,7 @@ const messages = {
|
||||||
administration: "Administração",
|
administration: "Administração",
|
||||||
users: "Usuários",
|
users: "Usuários",
|
||||||
settings: "Configurações",
|
settings: "Configurações",
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
},
|
},
|
||||||
appBar: {
|
appBar: {
|
||||||
user: {
|
user: {
|
||||||
|
@ -316,6 +345,24 @@ const messages = {
|
||||||
"Você tem certeza? Essa ação não pode ser revertida! Os tickets dessa fila continuarão existindo, mas não terão mais nenhuma fila atribuída.",
|
"Você tem certeza? Essa ação não pode ser revertida! Os tickets dessa fila continuarão existindo, mas não terão mais nenhuma fila atribuída.",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
dialogflows: {
|
||||||
|
title: "Dialogflow",
|
||||||
|
table: {
|
||||||
|
name: "Nome",
|
||||||
|
projectName: "Nome do Projeto",
|
||||||
|
language: "Linguagem",
|
||||||
|
lastUpdate: "Ultima atualização",
|
||||||
|
actions: "Ações",
|
||||||
|
},
|
||||||
|
buttons: {
|
||||||
|
add: "Adicionar Projeto",
|
||||||
|
},
|
||||||
|
confirmationModal: {
|
||||||
|
deleteTitle: "Excluir",
|
||||||
|
deleteMessage:
|
||||||
|
"Você tem certeza? Essa ação não pode ser revertida! e será removida das filas e conexões vinculadas",
|
||||||
|
},
|
||||||
|
},
|
||||||
queueSelect: {
|
queueSelect: {
|
||||||
inputLabel: "Filas",
|
inputLabel: "Filas",
|
||||||
},
|
},
|
||||||
|
@ -345,6 +392,7 @@ const messages = {
|
||||||
name: "Nome",
|
name: "Nome",
|
||||||
email: "Email",
|
email: "Email",
|
||||||
profile: "Perfil",
|
profile: "Perfil",
|
||||||
|
whatsapp: "Conexão Padrão",
|
||||||
actions: "Ações",
|
actions: "Ações",
|
||||||
},
|
},
|
||||||
buttons: {
|
buttons: {
|
||||||
|
@ -376,6 +424,7 @@ const messages = {
|
||||||
header: {
|
header: {
|
||||||
assignedTo: "Atribuído à:",
|
assignedTo: "Atribuído à:",
|
||||||
buttons: {
|
buttons: {
|
||||||
|
dialogflow: "Dialogflow",
|
||||||
return: "Retornar",
|
return: "Retornar",
|
||||||
resolve: "Resolver",
|
resolve: "Resolver",
|
||||||
reopen: "Reabrir",
|
reopen: "Reabrir",
|
||||||
|
@ -398,6 +447,7 @@ const messages = {
|
||||||
},
|
},
|
||||||
ticketOptionsMenu: {
|
ticketOptionsMenu: {
|
||||||
delete: "Deletar",
|
delete: "Deletar",
|
||||||
|
useQueues: "Usar fila?",
|
||||||
transfer: "Transferir",
|
transfer: "Transferir",
|
||||||
confirmationModal: {
|
confirmationModal: {
|
||||||
title: "Deletar o ticket do contato",
|
title: "Deletar o ticket do contato",
|
||||||
|
@ -458,6 +508,9 @@ const messages = {
|
||||||
"Esta cor já está em uso, escolha outra.",
|
"Esta cor já está em uso, escolha outra.",
|
||||||
ERR_WAPP_GREETING_REQUIRED:
|
ERR_WAPP_GREETING_REQUIRED:
|
||||||
"A mensagem de saudação é obrigatório quando há mais de uma fila.",
|
"A mensagem de saudação é obrigatório quando há mais de uma fila.",
|
||||||
|
ERR_NO_DIALOG_FOUND: "Nenhuma Dialogflow encontrado com este ID",
|
||||||
|
ERR_TEST_SESSION_DIALOG: "Erro ao criar sessão do DialogFlow",
|
||||||
|
ERR_TEST_REPLY_DIALOG: "Erro ao testar configuração do DialogFlow",
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
Loading…
Reference in New Issue