Merge branch 'new_features' into _dialogflow_omnihit_hit_new_features
commit
763d707ca5
|
@ -23,6 +23,11 @@ WWebJS
|
||||||
*/WWebJS/*
|
*/WWebJS/*
|
||||||
**WWebJS
|
**WWebJS
|
||||||
|
|
||||||
|
.wwebjs_auth
|
||||||
|
*/wwebjs_auth/*
|
||||||
|
.wwebjs_cache
|
||||||
|
*/wwebjs_cache/*
|
||||||
|
|
||||||
# testing
|
# testing
|
||||||
/coverage
|
/coverage
|
||||||
|
|
||||||
|
|
|
@ -1,3 +1,8 @@
|
||||||
PORT=8019
|
PORT=8019
|
||||||
PORT_START=8020
|
PORT_START=8020
|
||||||
BASE_URL=http://localhost
|
BASE_URL=http://localhost
|
||||||
|
PASS="strongpassword, strongpassword32"
|
||||||
|
DB_MONGO_URL=mongodb://localhost:27017
|
||||||
|
DB_MONGO_NAME=session_out_omnihit_db
|
||||||
|
DB_MONGO_NAME_CAMPAIGN=broker_omnihit
|
||||||
|
|
|
@ -1,486 +1,518 @@
|
||||||
const express = require('express');
|
const express = require('express')
|
||||||
const bodyparser = require('body-parser');
|
const bodyparser = require('body-parser')
|
||||||
const dotenv = require('dotenv');
|
const dotenv = require('dotenv')
|
||||||
dotenv.config({ path: '.env' });
|
dotenv.config({ path: '.env' })
|
||||||
const copyFolder = require('./helpers/copyFolder')
|
const copyFolder = require('./helpers/copyFolder')
|
||||||
const createDir = require('./helpers/createDir')
|
const createDir = require('./helpers/createDir')
|
||||||
const createFile = require('./helpers/createFile')
|
const createFile = require('./helpers/createFile')
|
||||||
const path = require('path');
|
const path = require('path')
|
||||||
const db_info = require('./db_conn')
|
const db_info = require('./db_conn')
|
||||||
const fs = require('fs');
|
const fs = require('fs')
|
||||||
let mysql_conn = require('./helpers/mysql_conn.js');
|
let mysql_conn = require('./helpers/mysql_conn.js')
|
||||||
const { exec, execSync, spawn } = require("child_process");
|
const { exec, execSync, spawn } = require('child_process')
|
||||||
|
|
||||||
const startPm2Process = require('./helpers/startPm2Process');
|
const startPm2Process = require('./helpers/startPm2Process')
|
||||||
const removeDir = require('./helpers/remove_dir');
|
const removeDir = require('./helpers/remove_dir')
|
||||||
const setSessionName = require('./helpers/setSessionNumber')
|
const setSessionName = require('./helpers/setSessionNumber')
|
||||||
const getNumberFromName = require('./helpers/getNumberSequence')
|
const getNumberFromName = require('./helpers/getNumberSequence')
|
||||||
const pm2 = require('pm2');
|
const pm2 = require('pm2')
|
||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
const OmnihitDBConn = require('./model/db_conn')
|
||||||
|
|
||||||
const app = express();
|
const app = express()
|
||||||
|
|
||||||
app.use(bodyparser.json());
|
app.use(bodyparser.json())
|
||||||
|
|
||||||
app.get('/', function (req, res) { return res.send('Express + TypeScript Server'); });
|
app.get('/', function (req, res) {
|
||||||
|
return res.send('Express + TypeScript Server')
|
||||||
|
})
|
||||||
|
|
||||||
app.post('/api/session', async function (req, res) {
|
app.post('/api/session', async function (req, res) {
|
||||||
|
let { app_name, whatsappId, client_url, number } = req.body
|
||||||
|
|
||||||
let { app_name, whatsappId, client_url, number } = req.body
|
if (app_name) {
|
||||||
|
app_name = app_name.trim()
|
||||||
|
}
|
||||||
|
|
||||||
if(app_name){
|
console.log('__dirname: ', path.join(__dirname, '..', app_name))
|
||||||
app_name = app_name.trim()
|
|
||||||
|
console.log(
|
||||||
|
'app_name: ',
|
||||||
|
app_name,
|
||||||
|
' | whatsappId: ',
|
||||||
|
whatsappId,
|
||||||
|
' | client_url: ',
|
||||||
|
client_url
|
||||||
|
)
|
||||||
|
|
||||||
|
const sessionsPath = path.join(__dirname, '..', 'sessions')
|
||||||
|
|
||||||
|
const directoriesInDIrectory = fs
|
||||||
|
.readdirSync(sessionsPath, { withFileTypes: true })
|
||||||
|
.filter((item) => item.isDirectory())
|
||||||
|
.map((item) => item.name)
|
||||||
|
|
||||||
|
console.log('directoriesInDIrectory: ', directoriesInDIrectory)
|
||||||
|
|
||||||
|
const dirExist = directoriesInDIrectory.filter((e) => e.trim() == app_name)
|
||||||
|
|
||||||
|
let dirSessionsApp = path.join(sessionsPath, app_name)
|
||||||
|
|
||||||
|
if (dirExist.length == 0) {
|
||||||
|
let create = createDir(dirSessionsApp)
|
||||||
|
|
||||||
|
if (!create) {
|
||||||
|
res.status(500).json({ message: 'Cannot create the directory path!' })
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
console.log('__dirname: ', path.join(__dirname, '..', app_name))
|
let appPort = []
|
||||||
|
|
||||||
console.log('app_name: ', app_name, ' | whatsappId: ', whatsappId, ' | client_url: ',client_url)
|
let existSubDir = false
|
||||||
|
|
||||||
const sessionsPath = path.join(__dirname, '..', 'sessions')
|
for (let i = 0; i < directoriesInDIrectory.length; i++) {
|
||||||
|
console.log('directoriesInDIrectory[i]', directoriesInDIrectory[i])
|
||||||
|
|
||||||
const directoriesInDIrectory = fs.readdirSync(sessionsPath, { withFileTypes: true })
|
const subDir = fs
|
||||||
.filter((item) => item.isDirectory())
|
.readdirSync(path.join(sessionsPath, directoriesInDIrectory[i]), {
|
||||||
.map((item) => item.name);
|
withFileTypes: true,
|
||||||
|
})
|
||||||
|
.filter((item) => item.isDirectory())
|
||||||
|
.map((item) => item.name)
|
||||||
|
|
||||||
console.log('directoriesInDIrectory: ', directoriesInDIrectory)
|
for (let x = 0; x < subDir.length; x++) {
|
||||||
|
console.log('subdir: ', subDir[x])
|
||||||
|
|
||||||
const dirExist = directoriesInDIrectory.filter((e) => e.trim() == app_name)
|
let whatsId = subDir[x].split('_')[0]
|
||||||
|
|
||||||
let dirSessionsApp = path.join(sessionsPath, app_name)
|
if (whatsId == whatsappId && app_name == directoriesInDIrectory[i]) {
|
||||||
|
let currPath = path.join(
|
||||||
|
sessionsPath,
|
||||||
|
directoriesInDIrectory[i],
|
||||||
|
subDir[x]
|
||||||
|
)
|
||||||
|
|
||||||
if (dirExist.length == 0) {
|
console.log(
|
||||||
|
'PATH: ',
|
||||||
|
path.join(sessionsPath, directoriesInDIrectory[i], subDir[x])
|
||||||
|
)
|
||||||
|
|
||||||
let create = createDir(dirSessionsApp)
|
let oldNumber = subDir[x].split('_')[1]
|
||||||
|
|
||||||
if (!create) {
|
if (oldNumber != number) {
|
||||||
|
deletePm2Process(subDir[x], currPath)
|
||||||
res.status(500).json({ message: 'Cannot create the directory path!' })
|
|
||||||
return
|
|
||||||
|
|
||||||
|
removeDir(currPath)
|
||||||
|
} else {
|
||||||
|
res.send('ok')
|
||||||
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let appPort = []
|
let auxPort = subDir[x].split('_')[3]
|
||||||
|
|
||||||
|
console.log('---------> auxPort: ' + auxPort)
|
||||||
|
|
||||||
|
if (auxPort) {
|
||||||
|
auxPort = +auxPort.trim()
|
||||||
|
|
||||||
let existSubDir = false
|
if (!isNaN(auxPort)) {
|
||||||
|
appPort.push(auxPort)
|
||||||
for (let i = 0; i < directoriesInDIrectory.length; i++) {
|
|
||||||
|
|
||||||
console.log('directoriesInDIrectory[i]', directoriesInDIrectory[i])
|
|
||||||
|
|
||||||
const subDir = fs.readdirSync(path.join(sessionsPath, directoriesInDIrectory[i]), { withFileTypes: true })
|
|
||||||
.filter((item) => item.isDirectory())
|
|
||||||
.map((item) => item.name);
|
|
||||||
|
|
||||||
for (let x = 0; x < subDir.length; x++) {
|
|
||||||
|
|
||||||
console.log('subdir: ', subDir[x])
|
|
||||||
|
|
||||||
let whatsId = subDir[x].split('_')[0]
|
|
||||||
|
|
||||||
if (whatsId == whatsappId && app_name == directoriesInDIrectory[i]) {
|
|
||||||
|
|
||||||
let currPath = path.join(sessionsPath, directoriesInDIrectory[i], subDir[x])
|
|
||||||
|
|
||||||
console.log('PATH: ', path.join(sessionsPath, directoriesInDIrectory[i], subDir[x]))
|
|
||||||
|
|
||||||
let oldNumber = subDir[x].split('_')[1]
|
|
||||||
// let sessionNum = subDir[x].split('_')[2]
|
|
||||||
// let sessionPort = subDir[x].split('_')[3]
|
|
||||||
// let newSessionAppName = `${whatsId}_${number}_${sessionNum}_${sessionPort}`
|
|
||||||
|
|
||||||
// let newPath = path.join(sessionsPath, directoriesInDIrectory[i], newSessionAppName)
|
|
||||||
|
|
||||||
// console.log(`number: ${number}\noldNumber: ${oldNumber}\nsessionNum: ${sessionNum}\nsessionPort: ${sessionPort}\nnewSessionAppName:${newSessionAppName}`)
|
|
||||||
|
|
||||||
if (oldNumber != number) {
|
|
||||||
|
|
||||||
deletePm2Process(subDir[x], currPath)
|
|
||||||
|
|
||||||
removeDir(currPath)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
res.send('ok')
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// try {
|
|
||||||
|
|
||||||
// //
|
|
||||||
|
|
||||||
// fs.renameSync(currPath, newPath)
|
|
||||||
|
|
||||||
// console.log("Successfully renamed the directory.")
|
|
||||||
|
|
||||||
// const data = fs.readFileSync(path.join(`${newPath}`, '.env'), 'utf-8');
|
|
||||||
|
|
||||||
// // console.log('Data: ', data)
|
|
||||||
|
|
||||||
// const newValue = data.replace(`MOBILEUID=${oldNumber}`, `MOBILEUID=${number}`)
|
|
||||||
|
|
||||||
// fs.writeFileSync(path.join(`${newPath}`, '.env'), newValue, 'utf-8');
|
|
||||||
|
|
||||||
// if (oldNumber != number) {
|
|
||||||
// removeDir(path.join(newPath, '.wwebjs_auth'))
|
|
||||||
// }
|
|
||||||
|
|
||||||
// startPm2Process(newSessionAppName, 'app.js', newPath, sessionPort)
|
|
||||||
|
|
||||||
|
|
||||||
// } catch (err) {
|
|
||||||
// console.log(err)
|
|
||||||
// }
|
|
||||||
|
|
||||||
|
|
||||||
// res.send('ok')
|
|
||||||
// return
|
|
||||||
}
|
|
||||||
|
|
||||||
appPort.push(+subDir[x].split('_')[3])
|
|
||||||
|
|
||||||
console.log('---------> appPort: '+appPort)
|
|
||||||
|
|
||||||
existSubDir = true
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
existSubDir = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
appPort = existSubDir ? Math.max(...appPort) + 1 : process.env.PORT_START
|
||||||
|
|
||||||
|
console.log('new port: ', appPort)
|
||||||
|
|
||||||
|
let dirSessionAppName
|
||||||
|
|
||||||
|
let numberSession = 1
|
||||||
|
|
||||||
|
let lstPass = process.env.PASS
|
||||||
|
|
||||||
|
if (!lstPass) {
|
||||||
|
console.log('PASS VARIABLE NOT FOUND INTO .ENV!')
|
||||||
|
return res.send('OK')
|
||||||
|
}
|
||||||
|
|
||||||
|
let db_credentials
|
||||||
|
try {
|
||||||
|
db_credentials = await OmnihitDBConn.findOne({ client_url })
|
||||||
|
|
||||||
|
if (!db_credentials) {
|
||||||
|
db_credentials = new OmnihitDBConn({
|
||||||
|
client_url: client_url,
|
||||||
|
db_conf: {
|
||||||
|
DB: '',
|
||||||
|
DB_HOST: '',
|
||||||
|
DB_USER: '',
|
||||||
|
DB_PASS: '',
|
||||||
|
DB_PORT: '',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
await db_credentials.save()
|
||||||
|
return res.send('ok')
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log(error)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (db_credentials && db_credentials.db_conf.DB.trim().length > 0) {
|
||||||
|
lstPass = lstPass.split(',')
|
||||||
|
let password = null
|
||||||
|
|
||||||
|
// password = await lstPass.find(
|
||||||
|
// async (pass) =>
|
||||||
|
// await bcrypt.compare(pass.trim(), db_credentials.db_conf.DB_PASS)
|
||||||
|
// )
|
||||||
|
|
||||||
|
for (let i = 0; i < lstPass.length; i++) {
|
||||||
|
const hasPass = await bcrypt.compare(
|
||||||
|
lstPass[i].trim(),
|
||||||
|
db_credentials.db_conf.DB_PASS
|
||||||
|
)
|
||||||
|
|
||||||
|
if (hasPass) {
|
||||||
|
password = lstPass[i].trim()
|
||||||
|
break
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
appPort = existSubDir ? Math.max(...appPort) + 1 : process.env.PORT_START
|
if (password) {
|
||||||
|
db_credentials.db_conf.DB_PASS = password
|
||||||
console.log('new port: ', appPort)
|
} else {
|
||||||
|
return res.send('ok')
|
||||||
|
|
||||||
|
|
||||||
let dirSessionAppName
|
|
||||||
|
|
||||||
let numberSession = 1
|
|
||||||
|
|
||||||
// const dirSessionsNumberAppDirectories = fs.readdirSync(dirSessionsApp, { withFileTypes: true })
|
|
||||||
// .filter((item) => item.isDirectory() && item.name.includes(`${number}`))
|
|
||||||
// .map((item) => item.name);
|
|
||||||
|
|
||||||
// console.log('dirSessionsNumberAppDirectories', dirSessionsNumberAppDirectories, ' | dirSessionsApp: ', dirSessionsApp)
|
|
||||||
|
|
||||||
console.log('client_url: ', client_url)
|
|
||||||
|
|
||||||
let db = db_info.filter((e) => e.client_url == client_url)
|
|
||||||
|
|
||||||
if (db && db.length > 0) {
|
|
||||||
|
|
||||||
db = db[0].db_conf
|
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// if (dirSessionsNumberAppDirectories.length > 0) {
|
if (
|
||||||
|
db_credentials.db_conf &&
|
||||||
if (db && Object.keys(db).length > 0) {
|
Object.keys(db_credentials.db_conf).length > 0
|
||||||
|
) {
|
||||||
const whatsapp_numbers = await new Promise((resolve, reject) => {
|
const whatsapp_numbers = await new Promise((resolve, reject) => {
|
||||||
mysql_conn(db).query('SELECT name FROM Whatsapps WHERE name LIKE ?', [`%${number}%`], (err, result) => {
|
mysql_conn(db_credentials.db_conf).query(
|
||||||
if (err) {
|
'SELECT name FROM Whatsapps WHERE name LIKE ?',
|
||||||
reject(err)
|
[`%${number}%`],
|
||||||
}
|
(err, result) => {
|
||||||
else {
|
if (err) {
|
||||||
resolve(result)
|
reject(err)
|
||||||
}
|
} else {
|
||||||
});
|
resolve(result)
|
||||||
})
|
}
|
||||||
|
|
||||||
console.log('whatsapp_numbers: ', whatsapp_numbers)
|
|
||||||
|
|
||||||
let session_num = []
|
|
||||||
|
|
||||||
if (whatsapp_numbers && whatsapp_numbers.length > 0) {
|
|
||||||
|
|
||||||
console.log('whatsapp_numbers.length: ', whatsapp_numbers.length)
|
|
||||||
|
|
||||||
if (whatsapp_numbers.length == 5) {
|
|
||||||
res.status(400).json({ message: 'Cannot create more than 4 sessions from the same number' })
|
|
||||||
return
|
|
||||||
}
|
|
||||||
|
|
||||||
let regex = /-> [a-zA-Z]\d$/
|
|
||||||
|
|
||||||
let numbered_sessions = whatsapp_numbers.filter((e) => regex.test(e.name))
|
|
||||||
|
|
||||||
console.log('numbered_sessions: ', numbered_sessions)
|
|
||||||
|
|
||||||
session_num = numbered_sessions.map((e) => parseInt((e.name.split('->')[e.name.split('->').length - 1]).trim().match(/\d+/)[0]))
|
|
||||||
|
|
||||||
console.log('session_num', session_num)
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
let index = 1;
|
|
||||||
|
|
||||||
while (index <= 4) {
|
|
||||||
|
|
||||||
if (!session_num.includes(index)) {
|
|
||||||
console.log(index)
|
|
||||||
numberSession = index
|
|
||||||
break
|
|
||||||
}
|
|
||||||
index++
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('whatsapp_numbers: ', whatsapp_numbers)
|
||||||
|
|
||||||
// numberSession = Math.max(...session_number) + 1
|
let session_num = []
|
||||||
|
|
||||||
console.log('Number session: ', numberSession)
|
if (whatsapp_numbers && whatsapp_numbers.length > 0) {
|
||||||
|
console.log('whatsapp_numbers.length: ', whatsapp_numbers.length)
|
||||||
// }
|
|
||||||
|
|
||||||
dirSessionAppName = `${whatsappId}_${number}_${numberSession}_${appPort}`
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
destDir = path.join(dirSessionsApp, dirSessionAppName)
|
|
||||||
|
|
||||||
originDir = path.join(__dirname, '..', 'whats')
|
|
||||||
|
|
||||||
copyFolder(originDir, destDir)
|
|
||||||
|
|
||||||
|
|
||||||
if (db && Object.keys(db).length > 0) {
|
|
||||||
|
|
||||||
console.log('kkkkkkkkkkkkkkk')
|
|
||||||
|
|
||||||
let whatsName
|
|
||||||
|
|
||||||
const whatsapp = await new Promise((resolve, reject) => {
|
|
||||||
|
|
||||||
mysql_conn(db).query("SELECT name from Whatsapps where id = ?", [whatsappId], (err, result) => {
|
|
||||||
|
|
||||||
if (err) {
|
|
||||||
reject(err)
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
resolve(result)
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
|
if (whatsapp_numbers.length == 5) {
|
||||||
|
res.status(400).json({
|
||||||
|
message: 'Cannot create more than 4 sessions from the same number',
|
||||||
})
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
if (whatsapp[0]["name"].split('->').length > 0) {
|
let regex = /-> [a-zA-Z]\d$/
|
||||||
whatsName = `${whatsapp[0]["name"].split('->')[0]} -> S${numberSession}`
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
whatsName = `${whatsapp[0]["name"]} -> S${numberSession}`
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('whatsName: ', whatsName)
|
let numbered_sessions = whatsapp_numbers.filter((e) => regex.test(e.name))
|
||||||
|
|
||||||
console.log(`url: ${process.env.BASE_URL}:${appPort}\n whatsname: ${whatsName}\n whatsappId: ${whatsappId}`)
|
console.log('numbered_sessions: ', numbered_sessions)
|
||||||
|
|
||||||
await new Promise((resolve, reject) => {
|
session_num = numbered_sessions.map((e) =>
|
||||||
mysql_conn(db).query("UPDATE Whatsapps SET url = ?, name = ? where id = ?", [`${process.env.BASE_URL}:${appPort}`, `${whatsName}`, whatsappId],
|
parseInt(
|
||||||
|
e.name
|
||||||
function (err, result) {
|
.split('->')
|
||||||
if (err) {
|
[e.name.split('->').length - 1].trim()
|
||||||
reject(err)
|
.match(/\d+/)[0]
|
||||||
console.log("===> ERROR: " + err);
|
)
|
||||||
}
|
)
|
||||||
else {
|
|
||||||
resolve(result)
|
|
||||||
// console.log('RESULT: ', result)
|
|
||||||
}
|
|
||||||
// else
|
|
||||||
// console.log('myslq result: ', result);
|
|
||||||
});
|
|
||||||
})
|
|
||||||
|
|
||||||
let whatsappName = `${number} - s${numberSession}`
|
|
||||||
|
|
||||||
console.log('-------------- numberSession', numberSession)
|
|
||||||
|
|
||||||
if (whatsapp.length > 0) {
|
|
||||||
|
|
||||||
if (whatsapp[0]['name'].split(' ').length > 0) {
|
|
||||||
|
|
||||||
whatsappName = `${whatsapp[0]['name'].split(' ')[0]} - S${numberSession}`
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('whatsapp: ', whatsapp)
|
|
||||||
console.log("whatsapp[0]['name']: ", whatsapp[0]['name'])
|
|
||||||
|
|
||||||
const keys = Object.keys(db);
|
|
||||||
|
|
||||||
var stream = fs.createWriteStream(path.join(destDir, '.env'));
|
|
||||||
stream.once('open', function (fd) {
|
|
||||||
stream.write("# NUMBER AND NAME THAT WILL BE DISPLAYED ON CONSOLE\n");
|
|
||||||
stream.write(`MOBILEUID=${number}\n`);
|
|
||||||
stream.write(`MOBILENAME=${whatsappName}\n`);
|
|
||||||
stream.write("\n");
|
|
||||||
|
|
||||||
stream.write("# PORT NUMBER FOR THIS API\n");
|
|
||||||
stream.write(`PORT=${appPort}\n`);
|
|
||||||
stream.write("\n");
|
|
||||||
|
|
||||||
stream.write("# URL FROM THE OMNIHIT BACKEND API\n");
|
|
||||||
stream.write(`CLIENT_URL=${client_url}\n`);
|
|
||||||
stream.write("\n");
|
|
||||||
|
|
||||||
stream.write("# OMNIHIT DATABASE\n");
|
|
||||||
keys.forEach((key, index) => {
|
|
||||||
stream.write(`${key}=${db[key]}\n`);
|
|
||||||
});
|
|
||||||
stream.write("\n");
|
|
||||||
|
|
||||||
stream.write(`# WHATSAPP ID OF THE TABLE Whatsapps FROM THE OMNIHIT DATABASE\n`);
|
|
||||||
stream.write(`WHATSAPP_ID=${whatsappId}`);
|
|
||||||
|
|
||||||
stream.end();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
console.log('----------------destDir: ', destDir)
|
|
||||||
|
|
||||||
|
|
||||||
execSync(`npm install`, { cwd: destDir }, (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
console.log(`error: ${error.message}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stderr) {
|
|
||||||
console.log(`stderr: ${stderr}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`stdout: ${stdout}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
startPm2Process(dirSessionAppName, 'app.js', destDir, appPort)
|
|
||||||
|
|
||||||
|
console.log('session_num', session_num)
|
||||||
}
|
}
|
||||||
|
|
||||||
res.send("OK");
|
let index = 1
|
||||||
|
|
||||||
});
|
while (index <= 4) {
|
||||||
|
if (!session_num.includes(index)) {
|
||||||
|
console.log(index)
|
||||||
|
numberSession = index
|
||||||
|
break
|
||||||
|
}
|
||||||
|
index++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// numberSession = Math.max(...session_number) + 1
|
||||||
|
|
||||||
|
console.log('Number session: ', numberSession)
|
||||||
|
|
||||||
|
// }
|
||||||
|
|
||||||
|
dirSessionAppName = `${whatsappId}_${number}_${numberSession}_${appPort}`
|
||||||
|
|
||||||
|
destDir = path.join(dirSessionsApp, dirSessionAppName)
|
||||||
|
|
||||||
|
originDir = path.join(__dirname, '..', 'whats')
|
||||||
|
|
||||||
|
copyFolder(originDir, destDir)
|
||||||
|
|
||||||
|
if (
|
||||||
|
db_credentials.db_conf &&
|
||||||
|
Object.keys(db_credentials.db_conf).length > 0
|
||||||
|
) {
|
||||||
|
console.log('***SUCCESS SEED DIRECTORY CREATED***')
|
||||||
|
|
||||||
|
let whatsName
|
||||||
|
|
||||||
|
const whatsapp = await new Promise((resolve, reject) => {
|
||||||
|
mysql_conn(db_credentials.db_conf).query(
|
||||||
|
'SELECT name from Whatsapps where id = ?',
|
||||||
|
[whatsappId],
|
||||||
|
(err, result) => {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
} else {
|
||||||
|
resolve(result)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
if (whatsapp[0]['name'].split('->').length > 0) {
|
||||||
|
whatsName = `${whatsapp[0]['name'].split('->')[0]} -> S${numberSession}`
|
||||||
|
} else {
|
||||||
|
whatsName = `${whatsapp[0]['name']} -> S${numberSession}`
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('whatsName: ', whatsName)
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
`url: ${process.env.BASE_URL}:${appPort}\n whatsname: ${whatsName}\n whatsappId: ${whatsappId}`
|
||||||
|
)
|
||||||
|
|
||||||
|
await new Promise((resolve, reject) => {
|
||||||
|
mysql_conn(db_credentials.db_conf).query(
|
||||||
|
'UPDATE Whatsapps SET url = ?, name = ? where id = ?',
|
||||||
|
[`${process.env.BASE_URL}:${appPort}`, `${whatsName}`, whatsappId],
|
||||||
|
|
||||||
|
function (err, result) {
|
||||||
|
if (err) {
|
||||||
|
reject(err)
|
||||||
|
console.log('===> ERROR: ' + err)
|
||||||
|
} else {
|
||||||
|
resolve(result)
|
||||||
|
// console.log('RESULT: ', result)
|
||||||
|
}
|
||||||
|
// else
|
||||||
|
// console.log('myslq result: ', result);
|
||||||
|
}
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
|
let whatsappName = `${number} - s${numberSession}`
|
||||||
|
|
||||||
|
console.log('-------------- numberSession', numberSession)
|
||||||
|
|
||||||
|
if (whatsapp.length > 0) {
|
||||||
|
if (whatsapp[0]['name'].split(' ').length > 0) {
|
||||||
|
whatsappName = `${whatsapp[0]['name'].split(' ')[0]
|
||||||
|
} - S${numberSession}`
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
console.log('whatsapp: ', whatsapp)
|
||||||
|
console.log("whatsapp[0]['name']: ", whatsapp[0]['name'])
|
||||||
|
|
||||||
|
const keys = Object.keys(db_credentials.db_conf)
|
||||||
|
|
||||||
|
var stream = fs.createWriteStream(path.join(destDir, '.env'))
|
||||||
|
stream.once('open', function (fd) {
|
||||||
|
stream.write('# NUMBER AND NAME THAT WILL BE DISPLAYED ON CONSOLE\n')
|
||||||
|
stream.write(`MOBILEUID=${number}\n`)
|
||||||
|
stream.write(`MOBILENAME=${whatsappName}\n`)
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.write('# PORT NUMBER FOR THIS API\n')
|
||||||
|
stream.write(`PORT=${appPort}\n`)
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.write('# URL FROM THE OMNIHIT BACKEND API\n')
|
||||||
|
stream.write(`CLIENT_URL=${client_url}\n`)
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.write('# OMNIHIT DATABASE\n')
|
||||||
|
keys.forEach((key, index) => {
|
||||||
|
stream.write(`${key}=${db_credentials.db_conf[key]}\n`)
|
||||||
|
})
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.write(
|
||||||
|
`# WHATSAPP ID OF THE TABLE Whatsapps FROM THE OMNIHIT DATABASE\n`
|
||||||
|
)
|
||||||
|
stream.write(`WHATSAPP_ID=${whatsappId}\n`)
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.write('# MONGO CONNECTION\n')
|
||||||
|
stream.write(`DB_MONGO_URL=${process.env.DB_MONGO_URL}\n`)
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.write('# MONGO COLLECTION\n')
|
||||||
|
stream.write(`DB_MONGO_NAME=${process.env.DB_MONGO_NAME_CAMPAIGN}\n`)
|
||||||
|
stream.write('\n')
|
||||||
|
|
||||||
|
stream.end()
|
||||||
|
})
|
||||||
|
|
||||||
|
console.log('----------------destDir: ', destDir)
|
||||||
|
|
||||||
|
execSync(`npm install`, { cwd: destDir }, (error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.log(`error: ${error.message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.log(`stderr: ${stderr}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(`stdout: ${stdout}`)
|
||||||
|
})
|
||||||
|
|
||||||
|
const env = {
|
||||||
|
PORT: appPort,
|
||||||
|
DB_MONGO_URL: process.env.DB_MONGO_URL,
|
||||||
|
DB_MONGO_NAME: process.env.DB_MONGO_NAME_CAMPAIGN
|
||||||
|
}
|
||||||
|
|
||||||
|
startPm2Process(dirSessionAppName, 'app.js', destDir, env)
|
||||||
|
}
|
||||||
|
|
||||||
|
res.send('OK')
|
||||||
|
})
|
||||||
|
|
||||||
app.post('/api/session/edit', async function (req, res) {
|
app.post('/api/session/edit', async function (req, res) {
|
||||||
|
const { app_name, whatsappId, client_url, number } = req.body
|
||||||
const { app_name, whatsappId, client_url, number } = req.body
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
app.post('/api/session/del', async function (req, res) {
|
app.post('/api/session/del', async function (req, res) {
|
||||||
|
let { whatsappId, app_name } = req.body
|
||||||
|
|
||||||
let { whatsappId, app_name } = req.body
|
if (app_name) {
|
||||||
|
app_name = app_name.trim()
|
||||||
|
}
|
||||||
|
|
||||||
if(app_name){
|
const sessionsPath = path.join(__dirname, '..', 'sessions')
|
||||||
app_name = app_name.trim()
|
|
||||||
|
const directoriesInDIrectory = fs
|
||||||
|
.readdirSync(sessionsPath, { withFileTypes: true })
|
||||||
|
.filter((item) => item.isDirectory())
|
||||||
|
.map((item) => item.name)
|
||||||
|
|
||||||
|
console.log('directoriesInDIrectory: ', directoriesInDIrectory)
|
||||||
|
|
||||||
|
const dirExist = directoriesInDIrectory.filter((e) => e.trim() == app_name)
|
||||||
|
|
||||||
|
console.log('dirExist: ', dirExist)
|
||||||
|
|
||||||
|
if (dirExist.length == 0) res.send('ok')
|
||||||
|
|
||||||
|
for (let i = 0; i < directoriesInDIrectory.length; i++) {
|
||||||
|
console.log('directoriesInDIrectory[i]', directoriesInDIrectory[i])
|
||||||
|
|
||||||
|
const subDir = fs
|
||||||
|
.readdirSync(path.join(sessionsPath, directoriesInDIrectory[i]), {
|
||||||
|
withFileTypes: true,
|
||||||
|
})
|
||||||
|
.filter((item) => item.isDirectory())
|
||||||
|
.map((item) => item.name)
|
||||||
|
|
||||||
|
for (let x = 0; x < subDir.length; x++) {
|
||||||
|
console.log('subdir: ', subDir[x])
|
||||||
|
|
||||||
|
let whatsId = subDir[x].split('_')[0]
|
||||||
|
|
||||||
|
if (whatsId == whatsappId && app_name == directoriesInDIrectory[i]) {
|
||||||
|
let currPath = path.join(
|
||||||
|
sessionsPath,
|
||||||
|
directoriesInDIrectory[i],
|
||||||
|
subDir[x]
|
||||||
|
)
|
||||||
|
|
||||||
|
deletePm2Process(subDir[x], currPath)
|
||||||
|
|
||||||
|
console.log('currPath: ', currPath)
|
||||||
|
|
||||||
|
removeDir(currPath)
|
||||||
|
|
||||||
|
return res.send('ok')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const sessionsPath = path.join(__dirname, '..', 'sessions')
|
res.send('ok')
|
||||||
|
|
||||||
const directoriesInDIrectory = fs.readdirSync(sessionsPath, { withFileTypes: true })
|
|
||||||
.filter((item) => item.isDirectory())
|
|
||||||
.map((item) => item.name);
|
|
||||||
|
|
||||||
console.log('directoriesInDIrectory: ', directoriesInDIrectory)
|
|
||||||
|
|
||||||
const dirExist = directoriesInDIrectory.filter((e) => e.trim() == app_name)
|
|
||||||
|
|
||||||
console.log('dirExist: ', dirExist)
|
|
||||||
|
|
||||||
if (dirExist.length == 0)
|
|
||||||
res.send('ok')
|
|
||||||
|
|
||||||
|
|
||||||
for (let i = 0; i < directoriesInDIrectory.length; i++) {
|
|
||||||
|
|
||||||
console.log('directoriesInDIrectory[i]', directoriesInDIrectory[i])
|
|
||||||
|
|
||||||
const subDir = fs.readdirSync(path.join(sessionsPath, directoriesInDIrectory[i]), { withFileTypes: true })
|
|
||||||
.filter((item) => item.isDirectory())
|
|
||||||
.map((item) => item.name);
|
|
||||||
|
|
||||||
for (let x = 0; x < subDir.length; x++) {
|
|
||||||
|
|
||||||
console.log('subdir: ', subDir[x])
|
|
||||||
|
|
||||||
let whatsId = subDir[x].split('_')[0]
|
|
||||||
|
|
||||||
if (whatsId == whatsappId && app_name == directoriesInDIrectory[i]) {
|
|
||||||
|
|
||||||
let currPath = path.join(sessionsPath, directoriesInDIrectory[i], subDir[x])
|
|
||||||
|
|
||||||
deletePm2Process(subDir[x], currPath);
|
|
||||||
|
|
||||||
console.log('currPath: ', currPath)
|
|
||||||
|
|
||||||
removeDir(currPath)
|
|
||||||
|
|
||||||
return res.send('ok')
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
res.send('ok')
|
|
||||||
|
|
||||||
})
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
app.listen(process.env.PORT || 8003, function () {
|
app.listen(process.env.PORT || 8003, function () {
|
||||||
console.log("\u26A1[server]: Server is running at Port ::: " + process.env.PORT || 8003);
|
console.log(
|
||||||
});
|
'\u26A1[server]: Server is running at Port ::: ' + process.env.PORT || 8003
|
||||||
|
)
|
||||||
|
})
|
||||||
|
|
||||||
process.on('uncaughtException', function (err) {
|
process.on('uncaughtException', function (err) {
|
||||||
console.error(' ');
|
console.error(' ')
|
||||||
console.error('----- ' + (new Date).toUTCString() + ' ----------------------------------')
|
console.error(
|
||||||
console.error('Erro uncaughtException: ', err.message)
|
'----- ' + new Date().toUTCString() + ' ----------------------------------'
|
||||||
console.error(err.stack)
|
)
|
||||||
console.error(' ');
|
console.error('Erro uncaughtException: ', err.message)
|
||||||
return
|
console.error(err.stack)
|
||||||
});
|
console.error(' ')
|
||||||
|
return
|
||||||
|
})
|
||||||
|
|
||||||
function deletePm2Process(process_name, currPath) {
|
function deletePm2Process(process_name, currPath) {
|
||||||
pm2.connect(function (err) {
|
pm2.connect(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
pm2.list(function (err, processes) {
|
pm2.list(function (err, processes) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
processes.forEach(function (process) {
|
||||||
|
console.log('.........process.name: ', process.name)
|
||||||
|
|
||||||
|
if (process.name === process_name) {
|
||||||
|
execSync(
|
||||||
|
`pm2 delete ${process_name} && pm2 save --force`,
|
||||||
|
{ cwd: currPath },
|
||||||
|
(error, stdout, stderr) => {
|
||||||
|
if (error) {
|
||||||
|
console.log(`error: ${error.message}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (stderr) {
|
||||||
|
console.log(`stderr: ${stderr}`)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
console.log(`stdout: ${stdout}`)
|
||||||
}
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
processes.forEach(function (process) {
|
pm2.disconnect()
|
||||||
|
})
|
||||||
console.log(".........process.name: ", process.name);
|
})
|
||||||
|
|
||||||
if (process.name === process_name) {
|
|
||||||
execSync(`pm2 delete ${process_name} && pm2 save --force`, { cwd: currPath }, (error, stdout, stderr) => {
|
|
||||||
if (error) {
|
|
||||||
console.log(`error: ${error.message}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (stderr) {
|
|
||||||
console.log(`stderr: ${stderr}`);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
console.log(`stdout: ${stdout}`);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
});
|
|
||||||
|
|
||||||
pm2.disconnect();
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,11 @@
|
||||||
|
const mongoose = require('mongoose')
|
||||||
|
require('dotenv').config({ path: `${process.cwd()}/.env` })
|
||||||
|
|
||||||
|
async function main(){
|
||||||
|
await mongoose.connect(process.env.DB_MONGO_URL, { dbName: process.env.DB_MONGO_NAME })
|
||||||
|
console.log('Conectou ao Mongoose!')
|
||||||
|
}
|
||||||
|
|
||||||
|
main().catch((err)=>console.log(err))
|
||||||
|
|
||||||
|
module.exports = mongoose
|
|
@ -0,0 +1,20 @@
|
||||||
|
const bcrypt = require('bcrypt')
|
||||||
|
|
||||||
|
// const pass = async () => {
|
||||||
|
// // create a password
|
||||||
|
// const salt = await bcrypt.genSalt(12)
|
||||||
|
// const passwordHash = await bcrypt.hash('7901228899', salt)
|
||||||
|
// console.log(passwordHash)
|
||||||
|
// }
|
||||||
|
// pass()
|
||||||
|
|
||||||
|
const passDec = async () => {
|
||||||
|
const _pass = await bcrypt.compare(
|
||||||
|
'strongpassword',
|
||||||
|
'$2b$12$PZ8N1jU77nnNUCCGyKTMNOi2QI7X/SgPsISVQfr.cQ/jgdx5Z7AqC'
|
||||||
|
)
|
||||||
|
console.log('_pass: ', _pass)
|
||||||
|
}
|
||||||
|
passDec()
|
||||||
|
|
||||||
|
console.log('process.cwd(): ', process.cwd())
|
|
@ -1,44 +1,49 @@
|
||||||
const pm2 = require('pm2');
|
const pm2 = require('pm2')
|
||||||
const { execSync } = require("child_process");
|
const { execSync } = require("child_process")
|
||||||
|
|
||||||
function startPm2Process(process_name, file, path, port) {
|
function startPm2Process(process_name, file, path, env) {
|
||||||
|
|
||||||
pm2.connect(function (err) {
|
pm2.connect(function (err) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
// process.exit(2);
|
// process.exit(2);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log('ENV PM2: ', env)
|
||||||
|
|
||||||
pm2.start({
|
pm2.start({
|
||||||
name: process_name,
|
name: process_name,
|
||||||
script: file,
|
script: file,
|
||||||
cwd: path,
|
cwd: path,
|
||||||
env: {
|
env
|
||||||
PORT: port
|
// env: {
|
||||||
}
|
// NODE_ENV: 'production',
|
||||||
|
|
||||||
|
// PORT: port,
|
||||||
|
// }
|
||||||
// additional options here if needed
|
// additional options here if needed
|
||||||
}, function (err, apps) {
|
}, function (err, apps) {
|
||||||
if (err) {
|
if (err) {
|
||||||
console.error(err);
|
console.error(err)
|
||||||
// process.exit(2);
|
// process.exit(2);
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
execSync(`pm2 save --force`, { cwd: path }, (error, stdout, stderr) => {
|
execSync(`pm2 save --force`, { cwd: path }, (error, stdout, stderr) => {
|
||||||
if (error) {
|
if (error) {
|
||||||
console.log(`error: ${error.message}`);
|
console.log(`error: ${error.message}`)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
if (stderr) {
|
if (stderr) {
|
||||||
console.log(`stderr: ${stderr}`);
|
console.log(`stderr: ${stderr}`)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
console.log(`stdout: ${stdout}`);
|
console.log(`stdout: ${stdout}`)
|
||||||
});
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
pm2.disconnect();
|
pm2.disconnect()
|
||||||
});
|
})
|
||||||
});
|
})
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,33 @@
|
||||||
|
const mongoose = require('../db/connMongo')
|
||||||
|
const { Schema } = mongoose
|
||||||
|
|
||||||
|
const OmnihitDBConn = mongoose.model(
|
||||||
|
'Omnihit_db_conn',
|
||||||
|
new Schema(
|
||||||
|
{
|
||||||
|
client_url: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
db_conf: {
|
||||||
|
DB: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
DB_HOST: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
DB_USER: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
DB_PASS: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
DB_PORT: {
|
||||||
|
type: String,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{ timestamps: true }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
module.exports = OmnihitDBConn
|
File diff suppressed because it is too large
Load Diff
|
@ -11,10 +11,12 @@
|
||||||
"author": "Adriano <adriano08andrade@hotmail.com>",
|
"author": "Adriano <adriano08andrade@hotmail.com>",
|
||||||
"license": "MIT",
|
"license": "MIT",
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"bcrypt": "^5.1.0",
|
||||||
"body-parser": "^1.20.1",
|
"body-parser": "^1.20.1",
|
||||||
"dotenv": "^16.0.3",
|
"dotenv": "^16.0.3",
|
||||||
"express": "^4.18.2",
|
"express": "^4.18.2",
|
||||||
"fs-extra": "^11.1.0",
|
"fs-extra": "^11.1.0",
|
||||||
|
"mongoose": "^7.4.0",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"nodemon": "^2.0.20",
|
"nodemon": "^2.0.20",
|
||||||
"socket.io": "^4.5.4"
|
"socket.io": "^4.5.4"
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
# NUMBER AND NAME THAT WILL BE DISPLAYED ON CONSOLE
|
# NUMBER AND NAME THAT WILL BE DISPLAYED ON CONSOLE
|
||||||
MOBILEUID=5517988310949
|
MOBILEUID=5517988310949
|
||||||
MOBILENAME=Numero de teste
|
MOBILENAME=test - S1
|
||||||
|
|
||||||
# PORT NUMBER FOR THIS API
|
# PORT NUMBER FOR THIS API
|
||||||
PORT=8020
|
PORT=8029
|
||||||
|
|
||||||
# URL FROM THE OMNIHIT BACKEND API
|
# URL FROM THE OMNIHIT BACKEND API
|
||||||
CLIENT_URL=http://localhost:8080
|
CLIENT_URL=http://localhost:8080
|
||||||
|
@ -13,8 +13,13 @@ DB=whaticket
|
||||||
DB_HOST=localhost
|
DB_HOST=localhost
|
||||||
DB_USER=whaticket
|
DB_USER=whaticket
|
||||||
DB_PASS=strongpassword
|
DB_PASS=strongpassword
|
||||||
DB_PORT=3306
|
DB_PORT=3306
|
||||||
|
|
||||||
# WHATSAPP ID OF THE TABLE Whatsapps FROM THE OMNIHIT DATABASE
|
# WHATSAPP ID OF THE TABLE Whatsapps FROM THE OMNIHIT DATABASE
|
||||||
WHATSAPP_ID=46
|
WHATSAPP_ID=223
|
||||||
|
|
||||||
|
# MONGO CONNECTION
|
||||||
|
DB_MONGO_URL=mongodb://localhost:27017
|
||||||
|
|
||||||
|
# MONGO COLLECTION
|
||||||
|
DB_MONGO_NAME=broker_omnihit
|
File diff suppressed because it is too large
Load Diff
|
@ -0,0 +1,18 @@
|
||||||
|
const { MongoClient } = require('mongodb')
|
||||||
|
|
||||||
|
const uri = process.env.DB_MONGO_URL
|
||||||
|
|
||||||
|
const mongo = new MongoClient(uri)
|
||||||
|
|
||||||
|
async function run() {
|
||||||
|
try {
|
||||||
|
await mongo.connect()
|
||||||
|
console.log('Connectado ao mongo db')
|
||||||
|
} catch (err) {
|
||||||
|
console.log(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
run()
|
||||||
|
|
||||||
|
module.exports = mongo
|
|
@ -0,0 +1,41 @@
|
||||||
|
const http = require('http')
|
||||||
|
|
||||||
|
const checkInternetConnection = async () => {
|
||||||
|
const options = {
|
||||||
|
hostname: 'www.google.com',
|
||||||
|
port: 80,
|
||||||
|
method: 'HEAD'
|
||||||
|
}
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
const req = http.request(options, (res) => {
|
||||||
|
if (res.statusCode === 200) {
|
||||||
|
resolve(true)
|
||||||
|
} else {
|
||||||
|
resolve(false)
|
||||||
|
}
|
||||||
|
req.abort()
|
||||||
|
})
|
||||||
|
|
||||||
|
req.on('error', (err) => {
|
||||||
|
resolve(false)
|
||||||
|
})
|
||||||
|
|
||||||
|
req.end()
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
|
// (async () => {
|
||||||
|
// try {
|
||||||
|
// const isConnected = await checkInternetConnection()
|
||||||
|
// if (isConnected) {
|
||||||
|
// console.log('Internet connection is available.')
|
||||||
|
// } else {
|
||||||
|
// console.log('Internet connection is not available.')
|
||||||
|
// }
|
||||||
|
// } catch (error) {
|
||||||
|
// console.error('Error checking internet connection:', error)
|
||||||
|
// }
|
||||||
|
// })()
|
||||||
|
|
||||||
|
module.exports = checkInternetConnection
|
File diff suppressed because it is too large
Load Diff
|
@ -15,17 +15,18 @@
|
||||||
"dotenv": "^16.0.0",
|
"dotenv": "^16.0.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"form-data": "^4.0.0",
|
"form-data": "^4.0.0",
|
||||||
|
"fs-extra": "^11.1.0",
|
||||||
"logger": "^0.0.1",
|
"logger": "^0.0.1",
|
||||||
"mime": "^2.4.5",
|
"mime": "^2.4.5",
|
||||||
"mongodb": "^4.1.1",
|
"mongodb": "^4.1.1",
|
||||||
|
"mongoose": "^7.4.3",
|
||||||
"multer": "^1.4.4",
|
"multer": "^1.4.4",
|
||||||
"mysql": "^2.18.1",
|
"mysql": "^2.18.1",
|
||||||
"node-os-utils": "^1.3.5",
|
"node-os-utils": "^1.3.5",
|
||||||
"qr-encode": "^0.3.0",
|
"qr-encode": "^0.3.0",
|
||||||
"qrcode-terminal": "^0.12.0",
|
"qrcode-terminal": "^0.12.0",
|
||||||
"socket.io": "^4.5.4",
|
"socket.io": "^4.5.4",
|
||||||
"socket.io-client": "^4.5.4",
|
"socket.io-client": "^4.5.4"
|
||||||
"fs-extra": "^11.1.0"
|
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"nodemon": "^2.0.20"
|
"nodemon": "^2.0.20"
|
||||||
|
|
|
@ -23,8 +23,8 @@
|
||||||
"bcryptjs": "^2.4.3",
|
"bcryptjs": "^2.4.3",
|
||||||
"cookie-parser": "^1.4.5",
|
"cookie-parser": "^1.4.5",
|
||||||
"cors": "^2.8.5",
|
"cors": "^2.8.5",
|
||||||
"date-fns": "^2.16.1",
|
"date-fns": "^2.30.0",
|
||||||
"date-fns-tz": "^1.3.4",
|
"date-fns-tz": "^1.3.8",
|
||||||
"dotenv": "^8.2.0",
|
"dotenv": "^8.2.0",
|
||||||
"express": "^4.17.1",
|
"express": "^4.17.1",
|
||||||
"express-async-errors": "^3.1.1",
|
"express-async-errors": "^3.1.1",
|
||||||
|
|
|
@ -8,7 +8,7 @@ import ShowContactService from "../services/ContactServices/ShowContactService";
|
||||||
import UpdateContactService from "../services/ContactServices/UpdateContactService";
|
import UpdateContactService from "../services/ContactServices/UpdateContactService";
|
||||||
import DeleteContactService from "../services/ContactServices/DeleteContactService";
|
import DeleteContactService from "../services/ContactServices/DeleteContactService";
|
||||||
|
|
||||||
import CheckContactNumber from "../services/WbotServices/CheckNumber"
|
import CheckContactNumber from "../services/WbotServices/CheckNumber";
|
||||||
import CheckIsValidContact from "../services/WbotServices/CheckIsValidContact";
|
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";
|
||||||
|
@ -47,42 +47,46 @@ type IndexGetContactQuery = {
|
||||||
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;
|
||||||
|
|
||||||
console.log('PAGE NUMBER CONTACT: ', pageNumber)
|
console.log("PAGE NUMBER CONTACT: ", pageNumber);
|
||||||
|
|
||||||
if (pageNumber === undefined || pageNumber.trim().length == 0) {
|
if (pageNumber === undefined || pageNumber.trim().length == 0) {
|
||||||
pageNumber = '1'
|
pageNumber = "1";
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST DEL
|
// TEST DEL
|
||||||
if (searchParam && searchParam.trim().length > 0 && process.env.CACHE) {
|
if (searchParam && searchParam.trim().length > 0 && process.env.CACHE) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
const offset = 20 * (+pageNumber - 1);
|
const offset = 20 * (+pageNumber - 1);
|
||||||
|
|
||||||
searchParam = searchParam.replace(/\s+/g, ' ').trim().toLowerCase();
|
searchParam = searchParam.replace(/\s+/g, " ").trim().toLowerCase();
|
||||||
|
|
||||||
const data = await searchContactCache(searchParam, offset, 20)
|
const data = await searchContactCache(searchParam, offset, 20);
|
||||||
|
|
||||||
if (data) {
|
if (data) {
|
||||||
|
console.log("QUERY CONTACTS FROM CACHE SEARCH PARAM: ", searchParam);
|
||||||
|
|
||||||
console.log('QUERY CONTACTS FROM CACHE SEARCH PARAM: ', searchParam)
|
console.log("QUERY CONTACTS FROM CACHE QUERY LENGTH: ", data.length);
|
||||||
|
|
||||||
console.log('QUERY CONTACTS FROM CACHE QUERY LENGTH: ', data.length)
|
return res.json({
|
||||||
|
contacts: data,
|
||||||
return res.json({ contacts: data, count: data.length, hasMore: data.length > 0 ? true : false });
|
count: data.length,
|
||||||
|
hasMore: data.length > 0 ? true : false
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('There was an error on search ContactController.ts search cache: ', error)
|
console.log(
|
||||||
|
"There was an error on search ContactController.ts search cache: ",
|
||||||
|
error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('QUERY CONTACTS FROM DATABASE SEARCH PARAM: ', searchParam)
|
console.log("QUERY CONTACTS FROM DATABASE SEARCH PARAM: ", searchParam);
|
||||||
|
|
||||||
const { contacts, count, hasMore } = await ListContactsService({ searchParam, pageNumber });
|
const { contacts, count, hasMore } = await ListContactsService({
|
||||||
|
searchParam,
|
||||||
|
pageNumber
|
||||||
|
});
|
||||||
|
|
||||||
return res.json({ contacts, count, hasMore });
|
return res.json({ contacts, count, hasMore });
|
||||||
};
|
};
|
||||||
|
@ -118,16 +122,15 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
const validNumber = await CheckIsValidContact(newContact.number);
|
const validNumber = await CheckIsValidContact(newContact.number);
|
||||||
|
|
||||||
// const validNumber: any = await CheckContactNumber(newContact.number)
|
// const validNumber: any = await CheckContactNumber(newContact.number)
|
||||||
|
|
||||||
if(!validNumber){
|
if (!validNumber) {
|
||||||
throw new AppError("ERR_WAPP_CHECK_CONTACT");
|
throw new AppError("ERR_WAPP_CHECK_CONTACT");
|
||||||
}
|
}
|
||||||
|
|
||||||
const profilePicUrl = await GetProfilePicUrl(validNumber);
|
const profilePicUrl = await GetProfilePicUrl(validNumber);
|
||||||
|
|
||||||
console.log('xxxxxxxxxxx profilePicUrl: ',profilePicUrl)
|
console.log("xxxxxxxxxxx profilePicUrl: ", profilePicUrl);
|
||||||
|
|
||||||
|
|
||||||
// console.log(`newContact.name: ${newContact.name}\n
|
// console.log(`newContact.name: ${newContact.name}\n
|
||||||
// newContact.number: ${newContact.number}\n
|
// newContact.number: ${newContact.number}\n
|
||||||
|
@ -146,7 +149,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
email,
|
email,
|
||||||
useDialogflow,
|
useDialogflow,
|
||||||
profilePicUrl: profilePicUrl,
|
profilePicUrl: profilePicUrl,
|
||||||
extraInfo,
|
extraInfo
|
||||||
});
|
});
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
|
@ -175,8 +178,8 @@ export const update = async (
|
||||||
const schema = Yup.object().shape({
|
const schema = Yup.object().shape({
|
||||||
name: Yup.string(),
|
name: Yup.string(),
|
||||||
number: Yup.string()
|
number: Yup.string()
|
||||||
.matches(/^\d+$/,"Invalid number format. Only numbers is allowed.")
|
.matches(/^\d+$/, "Invalid number format. Only numbers is allowed.")
|
||||||
.matches(/^55\d+$/, "The number must start with 55.")
|
.matches(/^55\d+$/, "The number must start with 55.")
|
||||||
});
|
});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -257,7 +260,14 @@ export const contacsBulkInsertOnQueue = async (req: Request, res: Response): Pro
|
||||||
|
|
||||||
// console.log('THE BODY: ', req.body)
|
// console.log('THE BODY: ', req.body)
|
||||||
|
|
||||||
const { adminId, identifier, queueStatus, file, contacts_inserted } = req.body
|
const {
|
||||||
|
adminId,
|
||||||
|
identifier,
|
||||||
|
queueStatus,
|
||||||
|
file,
|
||||||
|
contacts_inserted,
|
||||||
|
campaign
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.emit("contactsBulkInsertOnQueueStatus", {
|
io.emit("contactsBulkInsertOnQueueStatus", {
|
||||||
|
@ -266,17 +276,14 @@ export const contacsBulkInsertOnQueue = async (req: Request, res: Response): Pro
|
||||||
adminId: adminId,
|
adminId: adminId,
|
||||||
identifier: identifier,
|
identifier: identifier,
|
||||||
queueStatus: queueStatus,
|
queueStatus: queueStatus,
|
||||||
file: file
|
file: file,
|
||||||
|
campaign
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (process.env.CACHE && contacts_inserted) {
|
if (process.env.CACHE && contacts_inserted) {
|
||||||
|
await insertContactsCache(contacts_inserted);
|
||||||
await insertContactsCache(contacts_inserted)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json({ message: 'ok' })
|
return res.status(200).json({ message: "ok" });
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -5,6 +5,9 @@ import AppError from "../errors/AppError";
|
||||||
|
|
||||||
import UpdateSettingService from "../services/SettingServices/UpdateSettingService";
|
import UpdateSettingService from "../services/SettingServices/UpdateSettingService";
|
||||||
import ListSettingsService from "../services/SettingServices/ListSettingsService";
|
import ListSettingsService from "../services/SettingServices/ListSettingsService";
|
||||||
|
import loadSettings from "../helpers/LoadSettings";
|
||||||
|
import updateSettingTicket from "../services/SettingServices/UpdateSettingTicket";
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
|
|
||||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
// if (req.user.profile !== "master") {
|
// if (req.user.profile !== "master") {
|
||||||
|
@ -13,7 +16,76 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
const settings = await ListSettingsService();
|
const settings = await ListSettingsService();
|
||||||
|
|
||||||
return res.status(200).json(settings);
|
const config = await SettingTicket.findAll();
|
||||||
|
|
||||||
|
return res.status(200).json({ settings, config });
|
||||||
|
};
|
||||||
|
|
||||||
|
export const updateTicketSettings = async (
|
||||||
|
req: Request,
|
||||||
|
res: Response
|
||||||
|
): Promise<Response> => {
|
||||||
|
const {
|
||||||
|
outBusinessHours,
|
||||||
|
ticketExpiration,
|
||||||
|
weekend,
|
||||||
|
saturday,
|
||||||
|
sunday,
|
||||||
|
holiday
|
||||||
|
} = req.body;
|
||||||
|
|
||||||
|
if (outBusinessHours && Object.keys(outBusinessHours).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...outBusinessHours,
|
||||||
|
key: "outBusinessHours"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketExpiration && Object.keys(ticketExpiration).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...ticketExpiration,
|
||||||
|
key: "ticketExpiration"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (weekend && Object.keys(weekend).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...weekend,
|
||||||
|
key: "weekend"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (saturday && Object.keys(saturday).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...saturday,
|
||||||
|
key: "saturday"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sunday && Object.keys(sunday).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...sunday,
|
||||||
|
key: "sunday"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
if (holiday && Object.keys(holiday).length > 0) {
|
||||||
|
await updateSettingTicket({
|
||||||
|
...holiday,
|
||||||
|
key: "holiday"
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
return res
|
||||||
|
.status(200)
|
||||||
|
.json({
|
||||||
|
outBusinessHours,
|
||||||
|
ticketExpiration,
|
||||||
|
weekend,
|
||||||
|
saturday,
|
||||||
|
sunday,
|
||||||
|
holiday
|
||||||
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
export const update = async (
|
export const update = async (
|
||||||
|
@ -31,6 +103,8 @@ export const update = async (
|
||||||
value
|
value
|
||||||
});
|
});
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.emit("settings", {
|
io.emit("settings", {
|
||||||
action: "update",
|
action: "update",
|
||||||
|
|
|
@ -8,24 +8,22 @@ import ShowTicketService from "../services/TicketServices/ShowTicketService";
|
||||||
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
||||||
import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
|
import SendWhatsAppMessage from "../services/WbotServices/SendWhatsAppMessage";
|
||||||
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
|
import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService";
|
||||||
import ShowStatusChatEndService from '../services/StatusChatEndService/ShowStatusChatEndService'
|
import ShowStatusChatEndService from "../services/StatusChatEndService/ShowStatusChatEndService";
|
||||||
|
|
||||||
import CreateSchedulingNotifyService from "../services/SchedulingNotifyServices/CreateSchedulingNotifyService";
|
import CreateSchedulingNotifyService from "../services/SchedulingNotifyServices/CreateSchedulingNotifyService";
|
||||||
import ListSchedulingNotifyContactService from "../services/SchedulingNotifyServices/ListSchedulingNotifyContactService";
|
import ListSchedulingNotifyContactService from "../services/SchedulingNotifyServices/ListSchedulingNotifyContactService";
|
||||||
|
|
||||||
import { isScheduling } from "../helpers/CheckSchedulingReminderNotify"
|
import { isScheduling } from "../helpers/CheckSchedulingReminderNotify";
|
||||||
|
|
||||||
import ptBR from 'date-fns/locale/pt-BR';
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
import { splitDateTime } from "../helpers/SplitDateTime";
|
import { splitDateTime } from "../helpers/SplitDateTime";
|
||||||
import format from 'date-fns/format';
|
import format from "date-fns/format";
|
||||||
|
|
||||||
import ListTicketsServiceCache from "../services/TicketServices/ListTicketServiceCache";
|
import ListTicketsServiceCache from "../services/TicketServices/ListTicketServiceCache";
|
||||||
|
|
||||||
import { searchTicketCache, loadTicketsCache, } from '../helpers/TicketCache'
|
import { searchTicketCache, loadTicketsCache } from "../helpers/TicketCache";
|
||||||
import { Op } from "sequelize";
|
import { Op } from "sequelize";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
type IndexQuery = {
|
type IndexQuery = {
|
||||||
searchParam: string;
|
searchParam: string;
|
||||||
pageNumber: string;
|
pageNumber: string;
|
||||||
|
@ -35,7 +33,7 @@ type IndexQuery = {
|
||||||
withUnreadMessages: string;
|
withUnreadMessages: string;
|
||||||
queueIds: string;
|
queueIds: string;
|
||||||
unlimited?: string;
|
unlimited?: string;
|
||||||
searchParamContent?: string
|
searchParamContent?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
interface TicketData {
|
interface TicketData {
|
||||||
|
@ -43,13 +41,12 @@ interface TicketData {
|
||||||
status: string;
|
status: string;
|
||||||
queueId: number;
|
queueId: number;
|
||||||
userId: number;
|
userId: number;
|
||||||
whatsappId?: string | number
|
whatsappId?: string | number;
|
||||||
msg?: string,
|
msg?: string;
|
||||||
transfer?: boolean | undefined,
|
transfer?: boolean | undefined;
|
||||||
fromMe?: boolean
|
fromMe?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
import ListStatusChatEndService from "../services/StatusChatEndService/ListStatusChatEndService";
|
import ListStatusChatEndService from "../services/StatusChatEndService/ListStatusChatEndService";
|
||||||
import Ticket from "../models/Ticket";
|
import Ticket from "../models/Ticket";
|
||||||
import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport";
|
import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport";
|
||||||
|
@ -60,16 +57,15 @@ import ShowUserService from "../services/UserServices/ShowUserService";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import User from "../models/User";
|
import User from "../models/User";
|
||||||
import CheckContactOpenTickets from "../helpers/CheckContactOpenTickets";
|
import CheckContactOpenTickets from "../helpers/CheckContactOpenTickets";
|
||||||
import QueuesByUser from "../services/UserServices/ShowQueuesByUser";
|
|
||||||
import GetDefaultWhatsApp from "../helpers/GetDefaultWhatsApp";
|
import GetDefaultWhatsApp from "../helpers/GetDefaultWhatsApp";
|
||||||
import { getWbot } from "../libs/wbot";
|
import { getWbot } from "../libs/wbot";
|
||||||
import endPointQuery from "../helpers/old_EndPointQuery";
|
import endPointQuery from "../helpers/old_EndPointQuery";
|
||||||
import Contact from "../models/Contact";
|
import Contact from "../models/Contact";
|
||||||
import BotIsOnQueue from "../helpers/BotIsOnQueue";
|
import BotIsOnQueue from "../helpers/BotIsOnQueue";
|
||||||
import { setMessageAsRead } from "../helpers/SetMessageAsRead";
|
import { setMessageAsRead } from "../helpers/SetMessageAsRead";
|
||||||
|
import { getSettingValue } from "../helpers/WhaticketSettings";
|
||||||
|
|
||||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
const {
|
const {
|
||||||
pageNumber,
|
pageNumber,
|
||||||
status,
|
status,
|
||||||
|
@ -82,7 +78,6 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
searchParamContent
|
searchParamContent
|
||||||
} = req.query as IndexQuery;
|
} = req.query as IndexQuery;
|
||||||
|
|
||||||
|
|
||||||
const userId = req.user.id;
|
const userId = req.user.id;
|
||||||
|
|
||||||
let queueIds: number[] = [];
|
let queueIds: number[] = [];
|
||||||
|
@ -110,22 +105,23 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const { contactId, status, userId, msg, queueId }: TicketData = req.body;
|
const { contactId, status, userId, msg, queueId }: TicketData = req.body;
|
||||||
|
|
||||||
const botInfo = await BotIsOnQueue('botqueue')
|
// const botInfo = await BotIsOnQueue("botqueue");
|
||||||
|
|
||||||
let ticket = await Ticket.findOne({
|
let ticket = await Ticket.findOne({
|
||||||
where: {
|
where: {
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
{ contactId, status: 'queueChoice' },
|
{ contactId, status: "queueChoice" }
|
||||||
{ contactId, status: 'open', userId: botInfo.userIdBot }
|
// { contactId, status: "open", userId: botInfo.userIdBot }
|
||||||
]
|
]
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ticket) {
|
if (ticket) {
|
||||||
await UpdateTicketService({ ticketData: { status: 'open', userId: userId, queueId }, ticketId: ticket.id });
|
await UpdateTicketService({
|
||||||
|
ticketData: { status: "open", userId: userId, queueId },
|
||||||
}
|
ticketId: ticket.id
|
||||||
else {
|
});
|
||||||
|
} else {
|
||||||
ticket = await CreateTicketService({ contactId, status, userId, queueId });
|
ticket = await CreateTicketService({ contactId, status, userId, queueId });
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -134,8 +130,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
action: "update",
|
action: "update",
|
||||||
ticket
|
ticket
|
||||||
});
|
});
|
||||||
//
|
//
|
||||||
|
|
||||||
|
|
||||||
// const ticket = await CreateTicketService({ contactId, status, userId });
|
// const ticket = await CreateTicketService({ contactId, status, userId });
|
||||||
|
|
||||||
|
@ -148,58 +143,65 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
return res.status(200).json(ticket);
|
return res.status(200).json(ticket);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
export const show = async (req: Request, res: Response): Promise<Response> => {
|
export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const { ticketId } = req.params;
|
const { ticketId } = req.params;
|
||||||
|
|
||||||
const contact = await ShowTicketService(ticketId);
|
const contact = await ShowTicketService(ticketId);
|
||||||
|
|
||||||
const { statusChatEnd, count, hasMore } = await ListStatusChatEndService({ searchParam: "", pageNumber: "1" });
|
const { statusChatEnd, count, hasMore } = await ListStatusChatEndService({
|
||||||
|
searchParam: "",
|
||||||
|
pageNumber: "1"
|
||||||
|
});
|
||||||
|
|
||||||
//////////////////
|
//////////////////
|
||||||
const schedulesContact = await ListSchedulingNotifyContactService(contact.contact.number);
|
const schedulesContact = await ListSchedulingNotifyContactService(
|
||||||
|
contact.contact.number
|
||||||
|
);
|
||||||
/////////////////
|
/////////////////
|
||||||
|
|
||||||
|
|
||||||
return res.status(200).json({ contact, statusChatEnd, schedulesContact });
|
return res.status(200).json({ contact, statusChatEnd, schedulesContact });
|
||||||
};
|
};
|
||||||
|
|
||||||
export const count = async (req: Request, res: Response): Promise<Response> => {
|
export const count = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
// type indexQ = { status: string; date?: string; };
|
// type indexQ = { status: string; date?: string; };
|
||||||
const { status, date } = req.query as IndexQuery
|
const { status, date } = req.query as IndexQuery;
|
||||||
|
|
||||||
const ticketCount = await CountTicketService(status, date);
|
const ticketCount = await CountTicketService(status, date);
|
||||||
|
|
||||||
return res.status(200).json(ticketCount);
|
return res.status(200).json(ticketCount);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export const update = async (
|
||||||
export const update = async (req: Request, res: Response): Promise<Response> => {
|
req: Request,
|
||||||
|
res: Response
|
||||||
console.log('ENTROU NO UPDATE TICKET CONTROLLER')
|
): Promise<Response> => {
|
||||||
|
console.log("ENTROU NO UPDATE TICKET CONTROLLER");
|
||||||
|
|
||||||
const { ticketId } = req.params;
|
const { ticketId } = req.params;
|
||||||
|
|
||||||
const userOldInfo = await Ticket.findByPk(ticketId)
|
const userOldInfo = await Ticket.findByPk(ticketId);
|
||||||
|
|
||||||
let ticket2 = {}
|
let ticket2 = {};
|
||||||
|
|
||||||
if (req.body['status'] === "closed") {
|
|
||||||
|
|
||||||
|
if (req.body["status"] === "closed") {
|
||||||
const { status, userId, schedulingNotifyData } = req.body;
|
const { status, userId, schedulingNotifyData } = req.body;
|
||||||
|
|
||||||
// lembrete
|
// lembrete
|
||||||
const scheduleData = JSON.parse(schedulingNotifyData)
|
const scheduleData = JSON.parse(schedulingNotifyData);
|
||||||
|
|
||||||
const statusChatEndName = await ShowStatusChatEndService(scheduleData.statusChatEndId)
|
const statusChatEndName = await ShowStatusChatEndService(
|
||||||
|
scheduleData.statusChatEndId
|
||||||
|
);
|
||||||
|
|
||||||
const { ticket } = await UpdateTicketService({
|
const { ticket } = await UpdateTicketService({
|
||||||
ticketData: { 'status': status, 'userId': userId, 'statusChatEnd': statusChatEndName.name },
|
ticketData: {
|
||||||
|
status: status,
|
||||||
|
userId: userId,
|
||||||
|
statusChatEnd: statusChatEndName.name
|
||||||
|
},
|
||||||
ticketId
|
ticketId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
if (scheduleData.farewellMessage) {
|
if (scheduleData.farewellMessage) {
|
||||||
const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
|
const whatsapp = await ShowWhatsAppService(ticket.whatsappId);
|
||||||
|
|
||||||
|
@ -210,115 +212,105 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// lembrete // agendamento
|
// lembrete // agendamento
|
||||||
if (scheduleData.statusChatEndId === '2' || scheduleData.statusChatEndId === '3') {
|
if (
|
||||||
|
scheduleData.statusChatEndId === "2" ||
|
||||||
|
scheduleData.statusChatEndId === "3"
|
||||||
if (isScheduling(scheduleData.schedulingDate, scheduleData.schedulingTime)) {
|
) {
|
||||||
console.log('*** É AGENDAMENTO!')
|
if (
|
||||||
}
|
isScheduling(scheduleData.schedulingDate, scheduleData.schedulingTime)
|
||||||
else {
|
) {
|
||||||
console.log('*** É LEMBRETE!')
|
console.log("*** É AGENDAMENTO!");
|
||||||
|
} else {
|
||||||
|
console.log("*** É LEMBRETE!");
|
||||||
}
|
}
|
||||||
|
|
||||||
const schedulingNotifyCreate = await CreateSchedulingNotifyService(
|
const schedulingNotifyCreate = await CreateSchedulingNotifyService({
|
||||||
{
|
ticketId: scheduleData.ticketId,
|
||||||
ticketId: scheduleData.ticketId,
|
statusChatEndId: scheduleData.statusChatEndId,
|
||||||
statusChatEndId: scheduleData.statusChatEndId,
|
schedulingDate: scheduleData.schedulingDate,
|
||||||
schedulingDate: scheduleData.schedulingDate,
|
schedulingTime: scheduleData.schedulingTime,
|
||||||
schedulingTime: scheduleData.schedulingTime,
|
message: scheduleData.message
|
||||||
message: scheduleData.message
|
});
|
||||||
}
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket2 = ticket
|
ticket2 = ticket;
|
||||||
|
} else {
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
// Para aparecer pendente para todos usuarios que estao na fila
|
// Para aparecer pendente para todos usuarios que estao na fila
|
||||||
if (req.body.transfer) {
|
if (req.body.transfer) {
|
||||||
req.body.userId = null
|
req.body.userId = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
let ticketData: TicketData = req.body;
|
let ticketData: TicketData = req.body;
|
||||||
|
|
||||||
// console.log('ticketData: ', ticketData)
|
if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") {
|
||||||
// console.log('ticketData.transfer', ticketData.transfer)
|
if (ticketData.transfer) {
|
||||||
|
const defaultWhatsapp: any = await GetDefaultWhatsApp(
|
||||||
|
ticketData.userId
|
||||||
|
);
|
||||||
|
|
||||||
// return res.send()
|
const _ticket: any = await Ticket.findByPk(ticketId);
|
||||||
|
|
||||||
|
if (defaultWhatsapp && ticketData.status != "open") {
|
||||||
|
await CheckContactOpenTickets(
|
||||||
|
_ticket.dataValues.contactId,
|
||||||
|
defaultWhatsapp.dataValues.id
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
// if (ticketData.transfer) {
|
ticketData.whatsappId = defaultWhatsapp.dataValues.id;
|
||||||
|
}
|
||||||
// const defaultWhatsapp: any = await GetDefaultWhatsApp(ticketData.userId);
|
}
|
||||||
|
|
||||||
// const _ticket: any = await Ticket.findByPk(ticketId)
|
|
||||||
|
|
||||||
// if (defaultWhatsapp && ticketData.status != 'open') {
|
|
||||||
|
|
||||||
// await CheckContactOpenTickets(_ticket.dataValues.contactId, defaultWhatsapp.dataValues.id)
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
// ticketData.whatsappId = defaultWhatsapp.dataValues.id
|
|
||||||
|
|
||||||
// }
|
|
||||||
|
|
||||||
console.log('--------> ticketData.status: ', ticketData.status, ' | ticketData.fromMe: ', ticketData.fromMe)
|
|
||||||
|
|
||||||
|
console.log(
|
||||||
|
"--------> ticketData.status: ",
|
||||||
|
ticketData.status,
|
||||||
|
" | ticketData.fromMe: ",
|
||||||
|
ticketData.fromMe
|
||||||
|
);
|
||||||
|
|
||||||
const { ticket } = await UpdateTicketService({
|
const { ticket } = await UpdateTicketService({
|
||||||
ticketData,
|
ticketData,
|
||||||
ticketId,
|
ticketId
|
||||||
});
|
});
|
||||||
|
|
||||||
|
if (ticketData.status == "open" && !ticketData.fromMe) {
|
||||||
if (ticketData.status == 'open' && !ticketData.fromMe) {
|
|
||||||
|
|
||||||
await setMessageAsRead(ticket);
|
await setMessageAsRead(ticket);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
console.log('ticket.unreadMessages: ', ticket.unreadMessages)
|
console.log("ticket.unreadMessages: ", ticket.unreadMessages);
|
||||||
|
|
||||||
if (ticketData.userId) {
|
if (ticketData.userId) {
|
||||||
|
const dateToday = splitDateTime(
|
||||||
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
|
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
|
||||||
TicketEmiterSumOpenClosedByUser(ticketData.userId.toString(), dateToday.fullDate, dateToday.fullDate)
|
);
|
||||||
|
TicketEmiterSumOpenClosedByUser(
|
||||||
|
ticketData.userId.toString(),
|
||||||
|
dateToday.fullDate,
|
||||||
|
dateToday.fullDate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket2 = ticket
|
ticket2 = ticket;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (userOldInfo) {
|
if (userOldInfo) {
|
||||||
|
const dateToday = splitDateTime(
|
||||||
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
|
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
|
||||||
|
);
|
||||||
|
|
||||||
if (userOldInfo.userId) {
|
if (userOldInfo.userId) {
|
||||||
|
TicketEmiterSumOpenClosedByUser(
|
||||||
TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate)
|
userOldInfo.userId.toString(),
|
||||||
|
dateToday.fullDate,
|
||||||
|
dateToday.fullDate
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
return res.status(200).json(ticket2);
|
return res.status(200).json(ticket2);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
// export const update = async (
|
// export const update = async (
|
||||||
// req: Request,
|
// req: Request,
|
||||||
// res: Response
|
// res: Response
|
||||||
|
@ -341,7 +333,6 @@ export const update = async (req: Request, res: Response): Promise<Response> =>
|
||||||
// }
|
// }
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
||||||
// return res.status(200).json(ticket);
|
// return res.status(200).json(ticket);
|
||||||
// };
|
// };
|
||||||
|
|
||||||
|
@ -354,13 +345,10 @@ export const remove = async (
|
||||||
const ticket = await DeleteTicketService(ticketId);
|
const ticket = await DeleteTicketService(ticketId);
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
io.to(ticket.status)
|
io.to(ticket.status).to(ticketId).to("notification").emit("ticket", {
|
||||||
.to(ticketId)
|
action: "delete",
|
||||||
.to("notification")
|
ticketId: +ticketId
|
||||||
.emit("ticket", {
|
});
|
||||||
action: "delete",
|
|
||||||
ticketId: +ticketId
|
|
||||||
});
|
|
||||||
|
|
||||||
return res.status(200).json({ message: "ticket deleted" });
|
return res.status(200).json({ message: "ticket deleted" });
|
||||||
};
|
};
|
||||||
|
@ -371,4 +359,3 @@ export const remove = async (
|
||||||
|
|
||||||
// await endPointQuery(`${wbot_url}/api/sendSeen`, { number: `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us` });
|
// await endPointQuery(`${wbot_url}/api/sendSeen`, { number: `${ticket.contact.number}@${ticket.isGroup ? "g" : "c"}.us` });
|
||||||
// }
|
// }
|
||||||
|
|
||||||
|
|
|
@ -17,6 +17,7 @@ import UserOnlineTime from "../models/UserOnlineTime";
|
||||||
|
|
||||||
import Dialogflow from "../models/Dialogflow";
|
import Dialogflow from "../models/Dialogflow";
|
||||||
import QueryItem from "../models/QueryItem";
|
import QueryItem from "../models/QueryItem";
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
// eslint-disable-next-line
|
// eslint-disable-next-line
|
||||||
const dbConfig = require("../config/database");
|
const dbConfig = require("../config/database");
|
||||||
// import dbConfig from "../config/database";
|
// import dbConfig from "../config/database";
|
||||||
|
@ -40,7 +41,8 @@ const models = [
|
||||||
StatusChatEnd,
|
StatusChatEnd,
|
||||||
UserOnlineTime,
|
UserOnlineTime,
|
||||||
Dialogflow,
|
Dialogflow,
|
||||||
QueryItem
|
QueryItem,
|
||||||
|
SettingTicket
|
||||||
];
|
];
|
||||||
|
|
||||||
sequelize.addModels(models);
|
sequelize.addModels(models);
|
||||||
|
|
|
@ -0,0 +1,46 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.createTable("SettingTickets", {
|
||||||
|
id: {
|
||||||
|
type: DataTypes.INTEGER,
|
||||||
|
autoIncrement: true,
|
||||||
|
primaryKey: true,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
message: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
startTime: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
endTime: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: true
|
||||||
|
},
|
||||||
|
value: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
key: {
|
||||||
|
type: DataTypes.STRING,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
createdAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false
|
||||||
|
},
|
||||||
|
updatedAt: {
|
||||||
|
type: DataTypes.DATE,
|
||||||
|
allowNull: false
|
||||||
|
}
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.dropTable("SettingTickets");
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"Settings",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
key: "oneContactChatWithManyWhats",
|
||||||
|
value: "enabled",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("Settings", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"SettingTickets",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "outBusinessHours",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "ticketExpiration",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,34 @@
|
||||||
|
import { QueryInterface } from "sequelize"
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"SettingTickets",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "saturday",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "sunday",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {})
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"SettingTickets",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "holiday",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
},
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,25 @@
|
||||||
|
import { QueryInterface } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkInsert(
|
||||||
|
"SettingTickets",
|
||||||
|
[
|
||||||
|
{
|
||||||
|
message: "",
|
||||||
|
startTime: new Date(),
|
||||||
|
endTime: new Date(),
|
||||||
|
value: "disabled",
|
||||||
|
key: "weekend",
|
||||||
|
createdAt: new Date(),
|
||||||
|
updatedAt: new Date()
|
||||||
|
}
|
||||||
|
],
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.bulkDelete("SettingTickets", {});
|
||||||
|
}
|
||||||
|
};
|
|
@ -0,0 +1,84 @@
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
|
import ListTicketTimeLife from "../services/TicketServices/ListTicketTimeLife";
|
||||||
|
import UpdateTicketService from "../services/TicketServices/UpdateTicketService";
|
||||||
|
import BotIsOnQueue from "./BotIsOnQueue";
|
||||||
|
|
||||||
|
import {
|
||||||
|
format as _format,
|
||||||
|
isWithinInterval,
|
||||||
|
parse,
|
||||||
|
subMinutes
|
||||||
|
} from "date-fns";
|
||||||
|
|
||||||
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
import { splitDateTime } from "./SplitDateTime";
|
||||||
|
|
||||||
|
const fsPromises = require("fs/promises");
|
||||||
|
const fs = require("fs");
|
||||||
|
|
||||||
|
let timer: any;
|
||||||
|
|
||||||
|
const AutoCloseTickets = async () => {
|
||||||
|
try {
|
||||||
|
// const botInfo = await BotIsOnQueue('botqueue')
|
||||||
|
|
||||||
|
// if (!botInfo.userIdBot) return
|
||||||
|
|
||||||
|
const ticketExpiration = await SettingTicket.findOne({
|
||||||
|
where: { key: "ticketExpiration" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (ticketExpiration && ticketExpiration.value == "enabled") {
|
||||||
|
const startTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(ticketExpiration.startTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const seconds = timeStringToSeconds(startTime.fullTime);
|
||||||
|
|
||||||
|
// console.log("Ticket seconds: ", seconds);
|
||||||
|
|
||||||
|
let tickets: any = await ListTicketTimeLife({
|
||||||
|
timeseconds: seconds,
|
||||||
|
status: "open"
|
||||||
|
});
|
||||||
|
|
||||||
|
// console.log("tickets: ", tickets);
|
||||||
|
|
||||||
|
for (let i = 0; i < tickets.length; i++) {
|
||||||
|
|
||||||
|
await UpdateTicketService({
|
||||||
|
ticketData: { status: "closed", statusChatEnd: "FINALIZADO" },
|
||||||
|
ticketId: tickets[i].ticket_id,
|
||||||
|
msg: ticketExpiration.message
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.log("There was an error on try close the bot tickets: ", error);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
function timeStringToSeconds(timeString: any) {
|
||||||
|
const [hours, minutes, seconds] = timeString.split(":").map(Number);
|
||||||
|
return hours * 3600 + minutes * 60 + seconds;
|
||||||
|
}
|
||||||
|
|
||||||
|
const schedule = async () => {
|
||||||
|
try {
|
||||||
|
clearInterval(timer);
|
||||||
|
|
||||||
|
await AutoCloseTickets();
|
||||||
|
} catch (error) {
|
||||||
|
console.log("error on schedule: ", error);
|
||||||
|
} finally {
|
||||||
|
timer = setInterval(schedule, 60000);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
timer = setInterval(schedule, 60000);
|
||||||
|
|
||||||
|
export default schedule;
|
|
@ -1,11 +1,32 @@
|
||||||
import { Op } from "sequelize";
|
import { Op } from "sequelize";
|
||||||
import AppError from "../errors/AppError";
|
import AppError from "../errors/AppError";
|
||||||
import Ticket from "../models/Ticket";
|
import Ticket from "../models/Ticket";
|
||||||
|
import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber";
|
||||||
|
import { getSettingValue } from "./WhaticketSettings";
|
||||||
|
|
||||||
const CheckContactOpenTickets = async (contactId: number): Promise<void> => {
|
const CheckContactOpenTickets = async (
|
||||||
const ticket = await Ticket.findOne({
|
contactId: number,
|
||||||
where: { contactId, status: { [Op.or]: ["open", "pending"] } }
|
whatsappId: number | string
|
||||||
});
|
): Promise<void> => {
|
||||||
|
let ticket;
|
||||||
|
|
||||||
|
if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") {
|
||||||
|
let whats = await ListWhatsAppsNumber(whatsappId);
|
||||||
|
|
||||||
|
ticket = await Ticket.findOne({
|
||||||
|
where: {
|
||||||
|
[Op.and]: [
|
||||||
|
{ contactId: contactId },
|
||||||
|
{ whatsappId: { [Op.in]: whats.whatsapps.map((w: any) => w.id) } },
|
||||||
|
{ status: { [Op.or]: ["open", "pending"] } }
|
||||||
|
]
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ticket = await Ticket.findOne({
|
||||||
|
where: { contactId, status: { [Op.or]: ["open", "pending"] } }
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
if (ticket) {
|
if (ticket) {
|
||||||
throw new AppError("ERR_OTHER_OPEN_TICKET");
|
throw new AppError("ERR_OTHER_OPEN_TICKET");
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
import Setting from "../models/Setting";
|
||||||
|
import fs from "fs";
|
||||||
|
import dir from "path";
|
||||||
|
import os from "os";
|
||||||
|
|
||||||
|
async function loadSettings() {
|
||||||
|
const setting = await Setting.findAll({});
|
||||||
|
|
||||||
|
if (setting) {
|
||||||
|
let sett = setting.map((s: any) => {
|
||||||
|
return { key: s.key, value: s.value };
|
||||||
|
});
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keyFilename = dir.join(os.tmpdir(), `whaticket_settings.json`);
|
||||||
|
|
||||||
|
const jsonSettings = JSON.stringify(sett, null, 2);
|
||||||
|
|
||||||
|
fs.writeFileSync(keyFilename, jsonSettings);
|
||||||
|
console.log(`Settings saved to ${keyFilename}`);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error saving Settings to file:", err);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export default loadSettings;
|
|
@ -55,4 +55,4 @@ const mostRepeatedPhrase = async (ticketId: number | string, fromMe: boolean = f
|
||||||
return { body: '', occurrences: 0 }
|
return { body: '', occurrences: 0 }
|
||||||
}
|
}
|
||||||
|
|
||||||
export default mostRepeatedPhrase;
|
export default mostRepeatedPhrase;
|
||||||
|
|
|
@ -0,0 +1,182 @@
|
||||||
|
import SettingTicket from "../models/SettingTicket";
|
||||||
|
import { splitDateTime } from "./SplitDateTime";
|
||||||
|
import { utcToZonedTime, zonedTimeToUtc } from "date-fns-tz";
|
||||||
|
|
||||||
|
import {
|
||||||
|
format as _format,
|
||||||
|
isWithinInterval,
|
||||||
|
parse,
|
||||||
|
subMinutes,
|
||||||
|
isSaturday,
|
||||||
|
isSunday,
|
||||||
|
parseISO
|
||||||
|
} from "date-fns";
|
||||||
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
|
||||||
|
const isHoliday = async () => {
|
||||||
|
let obj = { set: false, msg: "" };
|
||||||
|
|
||||||
|
const holiday = await SettingTicket.findOne({
|
||||||
|
where: { key: "holiday" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
holiday &&
|
||||||
|
holiday.value == "enabled" &&
|
||||||
|
holiday.message?.trim()?.length > 0
|
||||||
|
) {
|
||||||
|
const startTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(holiday.startTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentDate = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
if (currentDate.fullDate == startTime.fullDate) {
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = holiday.message.trim();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
};
|
||||||
|
|
||||||
|
const isWeekend = async () => {
|
||||||
|
let obj = { set: false, msg: "" };
|
||||||
|
|
||||||
|
const weekend = await SettingTicket.findOne({
|
||||||
|
where: { key: "weekend" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
weekend &&
|
||||||
|
weekend.value == "enabled" &&
|
||||||
|
weekend.message?.trim()?.length > 0
|
||||||
|
) {
|
||||||
|
|
||||||
|
// Specify your desired timezone
|
||||||
|
const brazilTimeZone = "America/Sao_Paulo";
|
||||||
|
|
||||||
|
const currentDateUtc = new Date();
|
||||||
|
|
||||||
|
// Convert UTC date to Brazil time zone
|
||||||
|
const currentDate = utcToZonedTime(currentDateUtc, brazilTimeZone);
|
||||||
|
|
||||||
|
// Format the date using the desired format
|
||||||
|
const formattedDate = _format(currentDate, "yyyy-MM-dd HH:mm:ssXXX");
|
||||||
|
|
||||||
|
const parsedDate = parseISO(formattedDate);
|
||||||
|
|
||||||
|
// Convert parsed date to Brazil time zone
|
||||||
|
const localDate = utcToZonedTime(parsedDate, brazilTimeZone);
|
||||||
|
|
||||||
|
// Check if it's Saturday or Sunday
|
||||||
|
if (isSaturday(localDate)) {
|
||||||
|
const saturday = await SettingTicket.findOne({
|
||||||
|
where: { key: "saturday" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (saturday && saturday.value == "enabled") {
|
||||||
|
// botSendMessage(ticket, weekend.message);
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = weekend.message;
|
||||||
|
}
|
||||||
|
} else if (isSunday(localDate)) {
|
||||||
|
const sunday = await SettingTicket.findOne({
|
||||||
|
where: { key: "sunday" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (sunday && sunday.value == "enabled") {
|
||||||
|
// botSendMessage(ticket, weekend.message);
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = weekend.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
// obj.set = true;
|
||||||
|
// obj.msg = weekend.message;
|
||||||
|
}
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
async function isOutBusinessTime() {
|
||||||
|
let obj = { set: false, msg: "" };
|
||||||
|
|
||||||
|
const outBusinessHours = await SettingTicket.findOne({
|
||||||
|
where: { key: "outBusinessHours" }
|
||||||
|
});
|
||||||
|
|
||||||
|
let isWithinRange = false;
|
||||||
|
|
||||||
|
if (
|
||||||
|
outBusinessHours &&
|
||||||
|
outBusinessHours.value == "enabled" &&
|
||||||
|
outBusinessHours?.message?.trim()?.length > 0
|
||||||
|
) {
|
||||||
|
const ticketDateTimeUpdate = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const startTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(outBusinessHours.startTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const endTime = splitDateTime(
|
||||||
|
new Date(
|
||||||
|
_format(new Date(outBusinessHours.endTime), "yyyy-MM-dd HH:mm:ss", {
|
||||||
|
locale: ptBR
|
||||||
|
})
|
||||||
|
)
|
||||||
|
);
|
||||||
|
|
||||||
|
const format = "HH:mm:ss";
|
||||||
|
const parsedStartTime = parse(
|
||||||
|
ticketDateTimeUpdate.fullTime,
|
||||||
|
format,
|
||||||
|
new Date()
|
||||||
|
);
|
||||||
|
const parsedEndTime = parse(startTime.fullTime, format, new Date());
|
||||||
|
const parsedTimeToCheck = parse(endTime.fullTime, format, new Date());
|
||||||
|
const timeInterval = { start: parsedStartTime, end: parsedEndTime };
|
||||||
|
|
||||||
|
// If the time range spans across different days, handle the date part
|
||||||
|
if (parsedEndTime < parsedStartTime) {
|
||||||
|
const nextDay = new Date(parsedStartTime);
|
||||||
|
nextDay.setDate(nextDay.getDate() + 1);
|
||||||
|
timeInterval.end = nextDay;
|
||||||
|
}
|
||||||
|
|
||||||
|
isWithinRange = isWithinInterval(parsedTimeToCheck, timeInterval);
|
||||||
|
|
||||||
|
if (!isWithinRange) {
|
||||||
|
obj.set = true;
|
||||||
|
obj.msg = outBusinessHours.message;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return obj;
|
||||||
|
}
|
||||||
|
|
||||||
|
export {
|
||||||
|
isWeekend,
|
||||||
|
isHoliday,
|
||||||
|
isOutBusinessTime
|
||||||
|
};
|
|
@ -0,0 +1,18 @@
|
||||||
|
import fs from "fs";
|
||||||
|
import dir from "path";
|
||||||
|
import os from "os";
|
||||||
|
|
||||||
|
export function getSettingValue(key: any) {
|
||||||
|
let value: any = {};
|
||||||
|
|
||||||
|
try {
|
||||||
|
const keyFilename = dir.join(os.tmpdir(), `whaticket_settings.json`);
|
||||||
|
const jsonData = fs.readFileSync(keyFilename, "utf8");
|
||||||
|
const settings = JSON.parse(jsonData);
|
||||||
|
value = settings.find((s: any) => s.key === key);
|
||||||
|
} catch (err) {
|
||||||
|
console.error("Error reading or parsing data from file:", err);
|
||||||
|
}
|
||||||
|
|
||||||
|
return value;
|
||||||
|
}
|
|
@ -23,4 +23,4 @@ async function whatsappQueueMatchingUserQueue(userId: number, whatsapp: Whatsapp
|
||||||
return matchingQueue
|
return matchingQueue
|
||||||
}
|
}
|
||||||
|
|
||||||
export default whatsappQueueMatchingUserQueue
|
export default whatsappQueueMatchingUserQueue
|
||||||
|
|
|
@ -3,47 +3,53 @@ import { Server } from "http";
|
||||||
import AppError from "../errors/AppError";
|
import AppError from "../errors/AppError";
|
||||||
import { logger } from "../utils/logger";
|
import { logger } from "../utils/logger";
|
||||||
|
|
||||||
import { v4 as uuidv4 } from 'uuid';
|
import { v4 as uuidv4 } from "uuid";
|
||||||
import ListUserParamiterService from "../services/UserServices/ListUserParamiterService";
|
import ListUserParamiterService from "../services/UserServices/ListUserParamiterService";
|
||||||
import { addHours, addMinutes, addSeconds, intervalToDuration, add } from "date-fns";
|
import {
|
||||||
|
addHours,
|
||||||
|
addMinutes,
|
||||||
|
addSeconds,
|
||||||
|
intervalToDuration,
|
||||||
|
add
|
||||||
|
} from "date-fns";
|
||||||
|
|
||||||
let io: SocketIO;
|
let io: SocketIO;
|
||||||
|
|
||||||
//test del
|
//test del
|
||||||
import createOrUpdateOnlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService";
|
import createOrUpdateOnlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService";
|
||||||
import { splitDateTime } from "../helpers/SplitDateTime";
|
import { splitDateTime } from "../helpers/SplitDateTime";
|
||||||
import format from 'date-fns/format';
|
import format from "date-fns/format";
|
||||||
import ptBR from 'date-fns/locale/pt-BR';
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
import ListUserOnlineOffline from "../services/UserServices/ListUsersOnlineOfflineService";
|
import ListUserOnlineOffline from "../services/UserServices/ListUsersOnlineOfflineService";
|
||||||
import { handleMessage, handleMsgAck } from "../services/WbotServices/wbotMessageListener";
|
import {
|
||||||
|
handleMessage,
|
||||||
|
handleMsgAck
|
||||||
|
} from "../services/WbotServices/wbotMessageListener";
|
||||||
import { join } from "path";
|
import { join } from "path";
|
||||||
import Whatsapp from "../models/Whatsapp";
|
import Whatsapp from "../models/Whatsapp";
|
||||||
|
|
||||||
let count: number = 0
|
let count: number = 0;
|
||||||
let listOnline: any[] = []
|
let listOnline: any[] = [];
|
||||||
let listOnlineAux: any[] = []
|
let listOnlineAux: any[] = [];
|
||||||
let countOnline: number = 0
|
let countOnline: number = 0;
|
||||||
let obj: any = { listOnline: [], uuid: null, listOnlineAux: [] }
|
let obj: any = { listOnline: [], uuid: null, listOnlineAux: [] };
|
||||||
|
|
||||||
|
let lstOnline: any[] = [];
|
||||||
|
let lstOnlineAux: any[] = [];
|
||||||
|
let lstTry: any[] = [];
|
||||||
|
|
||||||
|
let dateTime = splitDateTime(
|
||||||
|
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
|
||||||
let lstOnline: any[] = []
|
);
|
||||||
let lstOnlineAux: any[] = []
|
|
||||||
let lstTry: any[] = []
|
|
||||||
|
|
||||||
let dateTime = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
|
|
||||||
|
|
||||||
|
|
||||||
export const initIO = (httpServer: Server): SocketIO => {
|
export const initIO = (httpServer: Server): SocketIO => {
|
||||||
io = new SocketIO(httpServer, {
|
io = new SocketIO(httpServer, {
|
||||||
cors: {
|
cors: {
|
||||||
origin: process.env.FRONTEND_URL
|
origin: process.env.FRONTEND_URL
|
||||||
},
|
},
|
||||||
maxHttpBufferSize: 1e8
|
maxHttpBufferSize: 1e8
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
io.on("connection", socket => {
|
io.on("connection", socket => {
|
||||||
logger.info("Client Connected");
|
logger.info("Client Connected");
|
||||||
|
|
||||||
|
@ -53,9 +59,8 @@ export const initIO = (httpServer: Server): SocketIO => {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("message_from_client", () => {
|
socket.on("message_from_client", () => {
|
||||||
|
socket.emit("message_from_server", "Sent an event from the server!");
|
||||||
socket.emit('message_from_server', 'Sent an event from the server!');
|
});
|
||||||
})
|
|
||||||
|
|
||||||
socket.on("message_create", async (data: any) => {
|
socket.on("message_create", async (data: any) => {
|
||||||
|
|
||||||
|
@ -65,154 +70,141 @@ export const initIO = (httpServer: Server): SocketIO => {
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("media_uploaded", async (data: any) => {
|
||||||
socket.on("media_uploaded", async (data: any) => {
|
|
||||||
|
|
||||||
handleMessage(data.msg, data);
|
handleMessage(data.msg, data);
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("message_ack", async (data: any) => {
|
socket.on("message_ack", async (data: any) => {
|
||||||
|
handleMsgAck(data.id, data.ack);
|
||||||
handleMsgAck(data.id, data.ack)
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
socket.on("campaign_message_sent", async (data: any) => {
|
||||||
|
// console.log("campaign_message_sent: ", data);
|
||||||
|
|
||||||
|
const io = getIO();
|
||||||
|
|
||||||
|
let campaign = {};
|
||||||
|
|
||||||
|
if (data?.read) {
|
||||||
|
campaign = {
|
||||||
|
id: data.id,
|
||||||
|
read: data.read
|
||||||
|
};
|
||||||
|
} else if (data?.sent) {
|
||||||
|
campaign = {
|
||||||
|
id: data.id,
|
||||||
|
sent: data.sent
|
||||||
|
};
|
||||||
|
} else if (data?.status) {
|
||||||
|
campaign = {
|
||||||
|
id: data.id,
|
||||||
|
status: data.status
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
io.emit("campaign", {
|
||||||
|
action: "update",
|
||||||
|
campaign
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
socket.on("online", (userId: any) => {
|
socket.on("online", (userId: any) => {
|
||||||
|
|
||||||
// console.log('userId: ', userId)
|
// console.log('userId: ', userId)
|
||||||
|
|
||||||
obj.uuid = uuidv4()
|
obj.uuid = uuidv4();
|
||||||
|
|
||||||
if (userId.logoutUserId) {
|
if (userId.logoutUserId) {
|
||||||
|
let index = lstOnline.findIndex(
|
||||||
let index = lstOnline.findIndex((x: any) => x.id == userId.logoutUserId)
|
(x: any) => x.id == userId.logoutUserId
|
||||||
|
);
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
|
lstOnline.splice(index, 1);
|
||||||
lstOnline.splice(index, 1)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
index = lstOnlineAux.findIndex((x: any) => x.id == userId.logoutUserId);
|
||||||
index = lstOnlineAux.findIndex((x: any) => x.id == userId.logoutUserId)
|
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
|
lstOnlineAux.splice(index, 1);
|
||||||
lstOnlineAux.splice(index, 1)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lstOnline.length == 0) {
|
if (lstOnline.length == 0) {
|
||||||
|
const index = listOnlineAux.findIndex((e: any) => e.id == userId);
|
||||||
const index = listOnlineAux.findIndex((e: any) => e.id == userId)
|
|
||||||
|
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
listOnlineAux.push({ 'id': userId })
|
listOnlineAux.push({ id: userId });
|
||||||
}
|
} else {
|
||||||
else {
|
return;
|
||||||
return
|
|
||||||
}
|
}
|
||||||
|
|
||||||
lstOnline.push({ 'id': userId, 'status': 'online', 'try': 0 })
|
lstOnline.push({ id: userId, status: "online", try: 0 });
|
||||||
|
|
||||||
lstOnlineAux.push({ 'id': userId })
|
lstOnlineAux.push({ id: userId });
|
||||||
console.log(' 1 PUSHED NEW USER ID 1: ', userId)
|
console.log(" 1 PUSHED NEW USER ID 1: ", userId);
|
||||||
|
|
||||||
obj.listOnline = lstOnline
|
obj.listOnline = lstOnline;
|
||||||
|
} else {
|
||||||
}
|
const indexAux = lstOnlineAux.findIndex((e: any) => e.id == userId);
|
||||||
else {
|
|
||||||
|
|
||||||
const indexAux = lstOnlineAux.findIndex((e: any) => e.id == userId)
|
|
||||||
|
|
||||||
if (indexAux == -1) {
|
if (indexAux == -1) {
|
||||||
lstOnlineAux.push({ 'id': userId })
|
lstOnlineAux.push({ id: userId });
|
||||||
}
|
}
|
||||||
|
|
||||||
const index = lstOnline.findIndex((e: any) => e.id == userId)
|
const index = lstOnline.findIndex((e: any) => e.id == userId);
|
||||||
|
|
||||||
if (index == -1) {
|
if (index == -1) {
|
||||||
|
lstOnline.push({ id: userId, status: "online", try: 0 });
|
||||||
|
|
||||||
|
console.log(" 2 PUSHED NEW USER ID: ", userId);
|
||||||
|
|
||||||
lstOnline.push({ 'id': userId, 'status': 'online', 'try': 0 })
|
obj.listOnline = lstOnline;
|
||||||
|
} else {
|
||||||
|
if (countOnline > lstOnline.length - 1) {
|
||||||
console.log(' 2 PUSHED NEW USER ID: ', userId)
|
|
||||||
|
|
||||||
obj.listOnline = lstOnline
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
|
|
||||||
if (countOnline > (lstOnline.length - 1)) {
|
|
||||||
|
|
||||||
lstOnline.forEach((x: any) => {
|
lstOnline.forEach((x: any) => {
|
||||||
if (lstOnlineAux.map((e: any) => e.id).includes(x.id)) {
|
if (lstOnlineAux.map((e: any) => e.id).includes(x.id)) {
|
||||||
x.try = 0
|
x.try = 0;
|
||||||
x.status = 'online'
|
x.status = "online";
|
||||||
}
|
}
|
||||||
})
|
});
|
||||||
|
|
||||||
|
|
||||||
var difference = lstOnline.filter((x: any) => !lstOnlineAux.map((e: any) => e.id).includes(x.id));
|
|
||||||
|
|
||||||
|
var difference = lstOnline.filter(
|
||||||
|
(x: any) => !lstOnlineAux.map((e: any) => e.id).includes(x.id)
|
||||||
|
);
|
||||||
|
|
||||||
if (difference.length > 0) {
|
if (difference.length > 0) {
|
||||||
|
difference.forEach(e => {
|
||||||
difference.forEach((e) => {
|
e.try += 1;
|
||||||
|
|
||||||
e.try += 1
|
|
||||||
|
|
||||||
if (e.try > 1) {
|
if (e.try > 1) {
|
||||||
e.status = 'waiting...'
|
e.status = "waiting...";
|
||||||
}
|
}
|
||||||
|
|
||||||
if (e.try > 3) {
|
if (e.try > 3) {
|
||||||
|
const index = lstOnline.findIndex((x: any) => x.id == e.id);
|
||||||
const index = lstOnline.findIndex((x: any) => x.id == e.id)
|
|
||||||
|
|
||||||
if (index != -1) {
|
if (index != -1) {
|
||||||
|
lstOnline.splice(index, 1);
|
||||||
lstOnline.splice(index, 1)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
});
|
||||||
})
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
obj.listOnline = lstOnline
|
obj.listOnline = lstOnline;
|
||||||
obj.listOnlineAux = lstOnlineAux
|
obj.listOnlineAux = lstOnlineAux;
|
||||||
|
|
||||||
|
lstOnlineAux = [];
|
||||||
|
listOnlineAux = [];
|
||||||
|
|
||||||
lstOnlineAux = []
|
countOnline = -1;
|
||||||
listOnlineAux = []
|
|
||||||
|
|
||||||
countOnline = -1
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
countOnline++;
|
||||||
countOnline++
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
exports.ob = obj
|
exports.ob = obj;
|
||||||
|
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("joinChatBox", (ticketId: string) => {
|
socket.on("joinChatBox", (ticketId: string) => {
|
||||||
|
@ -235,49 +227,51 @@ export const initIO = (httpServer: Server): SocketIO => {
|
||||||
});
|
});
|
||||||
|
|
||||||
socket.on("disconnecting", async () => {
|
socket.on("disconnecting", async () => {
|
||||||
console.log('socket.rooms: ', socket.rooms); // the Set contains at least the socket ID
|
console.log("socket.rooms: ", socket.rooms); // the Set contains at least the socket ID
|
||||||
|
|
||||||
let rooms = socket.rooms
|
let rooms = socket.rooms;
|
||||||
|
|
||||||
console.log('rooms: ', rooms, ' | rooms.size: ', rooms.size)
|
console.log("rooms: ", rooms, " | rooms.size: ", rooms.size);
|
||||||
|
|
||||||
if(rooms && rooms.size==1) return
|
if (rooms && rooms.size == 1) return;
|
||||||
if(rooms && rooms.size==2 && !([...rooms][1].startsWith('session_'))) return
|
if (rooms && rooms.size == 2 && ![...rooms][1].startsWith("session_"))
|
||||||
|
return;
|
||||||
|
|
||||||
let whatsappIds: any = await Whatsapp.findAll({ attributes: ['id'], raw: true })
|
let whatsappIds: any = await Whatsapp.findAll({
|
||||||
|
attributes: ["id"],
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
|
||||||
if (whatsappIds && whatsappIds.length > 0) {
|
if (whatsappIds && whatsappIds.length > 0) {
|
||||||
|
whatsappIds = whatsappIds.map((e: any) => `${e.id}`);
|
||||||
|
|
||||||
whatsappIds = whatsappIds.map((e: any) => `${e.id}`)
|
console.log(
|
||||||
|
"whatsappIds whatsappIds whatsappIds whatsappIds whatsappIds: ",
|
||||||
|
whatsappIds
|
||||||
|
);
|
||||||
|
|
||||||
console.log('whatsappIds whatsappIds whatsappIds whatsappIds whatsappIds: ',whatsappIds)
|
if (
|
||||||
|
rooms &&
|
||||||
|
rooms.size == 2 &&
|
||||||
|
[...rooms][1].startsWith("session_") &&
|
||||||
|
whatsappIds.includes([...rooms][1].replace("session_", ""))
|
||||||
|
) {
|
||||||
|
console.log([...rooms][1]);
|
||||||
|
|
||||||
if (rooms && rooms.size == 2 &&
|
let whatsappId = [...rooms][1].replace("session_", "");
|
||||||
[...rooms][1].startsWith('session_') &&
|
|
||||||
whatsappIds.includes([...rooms][1].replace('session_', ''))) {
|
|
||||||
|
|
||||||
console.log([...rooms][1])
|
const whatsapp = await Whatsapp.findByPk(whatsappId, {});
|
||||||
|
|
||||||
let whatsappId = [...rooms][1].replace('session_', '')
|
|
||||||
|
|
||||||
const whatsapp = await Whatsapp.findByPk(whatsappId, {})
|
|
||||||
|
|
||||||
if (whatsapp) {
|
if (whatsapp) {
|
||||||
|
await whatsapp.update({ status: "OPENING" });
|
||||||
await whatsapp.update({ status: 'OPENING' });
|
|
||||||
|
|
||||||
io.emit("whatsappSession", {
|
io.emit("whatsappSession", {
|
||||||
action: "update",
|
action: "update",
|
||||||
session: whatsapp
|
session: whatsapp
|
||||||
});
|
});
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
return io;
|
return io;
|
||||||
|
@ -290,12 +284,9 @@ export const getIO = (): SocketIO => {
|
||||||
return io;
|
return io;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
function writeFileAsync(arg0: any, data: any, arg2: string) {
|
function writeFileAsync(arg0: any, data: any, arg2: string) {
|
||||||
throw new Error("Function not implemented.");
|
throw new Error("Function not implemented.");
|
||||||
}
|
}
|
||||||
|
|
||||||
// exports.listOnlineUsers = listUserId
|
// exports.listOnlineUsers = listUserId
|
||||||
// exports.listUserId
|
// exports.listUserId
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,40 @@
|
||||||
|
import {
|
||||||
|
Table,
|
||||||
|
Column,
|
||||||
|
CreatedAt,
|
||||||
|
UpdatedAt,
|
||||||
|
Model,
|
||||||
|
PrimaryKey,
|
||||||
|
AutoIncrement
|
||||||
|
} from "sequelize-typescript";
|
||||||
|
|
||||||
|
@Table
|
||||||
|
class SettingTicket extends Model<SettingTicket> {
|
||||||
|
@PrimaryKey
|
||||||
|
@AutoIncrement
|
||||||
|
@Column
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
message: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
startTime: Date;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
endTime: Date;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
value: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
key: string;
|
||||||
|
|
||||||
|
@CreatedAt
|
||||||
|
createdAt: Date;
|
||||||
|
|
||||||
|
@UpdatedAt
|
||||||
|
updatedAt: Date;
|
||||||
|
}
|
||||||
|
|
||||||
|
export default SettingTicket;
|
|
@ -3,13 +3,21 @@ import isAuth from "../middleware/isAuth";
|
||||||
|
|
||||||
import * as SettingController from "../controllers/SettingController";
|
import * as SettingController from "../controllers/SettingController";
|
||||||
|
|
||||||
const settingRoutes = Router();
|
const settingRoutes = Router();
|
||||||
|
|
||||||
settingRoutes.get("/settings", SettingController.index);
|
settingRoutes.get("/settings", SettingController.index);
|
||||||
|
|
||||||
// routes.get("/settings/:settingKey", isAuth, SettingsController.show);
|
// routes.get("/settings/:settingKey", isAuth, SettingsController.show);
|
||||||
|
|
||||||
|
settingRoutes.put(
|
||||||
|
"/settings/ticket",
|
||||||
|
isAuth,
|
||||||
|
SettingController.updateTicketSettings
|
||||||
|
);
|
||||||
|
|
||||||
|
|
||||||
// change setting key to key in future
|
// change setting key to key in future
|
||||||
settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update);
|
settingRoutes.put("/settings/:settingKey", isAuth, SettingController.update);
|
||||||
|
|
||||||
|
|
||||||
export default settingRoutes;
|
export default settingRoutes;
|
||||||
|
|
|
@ -12,16 +12,22 @@ import { loadContactsCache } from "./helpers/ContactsCache";
|
||||||
import "./helpers/CloseBotTickets";
|
import "./helpers/CloseBotTickets";
|
||||||
import { loadSchedulesCache } from "./helpers/SchedulingNotifyCache";
|
import { loadSchedulesCache } from "./helpers/SchedulingNotifyCache";
|
||||||
import { delRestoreControllFile } from "./helpers/RestoreControll";
|
import { delRestoreControllFile } from "./helpers/RestoreControll";
|
||||||
|
import "./helpers/AutoCloseTickets";
|
||||||
|
|
||||||
import "./helpers/SchedulingNotifySendMessage"
|
import "./helpers/SchedulingNotifySendMessage";
|
||||||
import axios from "axios";
|
import axios from "axios";
|
||||||
import os from 'os';
|
import os from "os";
|
||||||
|
import Setting from "./models/Setting";
|
||||||
|
|
||||||
|
import fs from "fs";
|
||||||
|
import dir from "path";
|
||||||
|
import { getSettingValue } from "./helpers/WhaticketSettings";
|
||||||
|
import loadSettings from "./helpers/LoadSettings";
|
||||||
|
|
||||||
const server = app.listen(process.env.PORT, () => {
|
const server = app.listen(process.env.PORT, () => {
|
||||||
logger.info(`Server started on port: ${process.env.PORT}`);
|
logger.info(`Server started on port: ${process.env.PORT}`);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
// if (global.gc) {
|
// if (global.gc) {
|
||||||
// console.log(">> Starting Garbage Collector...");
|
// console.log(">> Starting Garbage Collector...");
|
||||||
// global.gc();
|
// global.gc();
|
||||||
|
@ -34,99 +40,84 @@ initIO(server);
|
||||||
// StartAllWhatsAppsSessions();
|
// StartAllWhatsAppsSessions();
|
||||||
gracefulShutdown(server);
|
gracefulShutdown(server);
|
||||||
|
|
||||||
|
(async () => {
|
||||||
|
console.log("os.tmpdir(): ", os.tmpdir());
|
||||||
|
|
||||||
|
loadSettings();
|
||||||
|
|
||||||
(async()=>{
|
let whatsapps: any = await Whatsapp.findAll({ attributes: ["id", "url"] });
|
||||||
|
|
||||||
console.log('os.tmpdir(): ', os.tmpdir())
|
|
||||||
|
|
||||||
let whatsapps: any = await Whatsapp.findAll({ attributes: ['id', 'url'] })
|
|
||||||
|
|
||||||
// console.log('whatsapps: ', whatsapps)
|
// console.log('whatsapps: ', whatsapps)
|
||||||
|
|
||||||
if (whatsapps && whatsapps.length > 0) {
|
if (whatsapps && whatsapps.length > 0) {
|
||||||
|
|
||||||
for (let i = 0; i < whatsapps.length; i++) {
|
for (let i = 0; i < whatsapps.length; i++) {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
console.log(
|
||||||
|
`API URL: ${whatsapps[i].dataValues.url}/api/connection/status`
|
||||||
|
);
|
||||||
|
|
||||||
console.log(`API URL: ${whatsapps[i].dataValues.url}/api/connection/status`)
|
const response = await axios.get(
|
||||||
|
`${whatsapps[i].dataValues.url}/api/connection/status`,
|
||||||
|
{}
|
||||||
|
);
|
||||||
|
|
||||||
const response = await axios.get(`${whatsapps[i].dataValues.url}/api/connection/status`, {});
|
console.log(`-------> Response: ${response.data.data}`);
|
||||||
|
|
||||||
console.log(`-------> Response: ${response.data.data}`)
|
|
||||||
|
|
||||||
if (!response) {
|
if (!response) {
|
||||||
throw new Error('Response null');
|
throw new Error("Response null");
|
||||||
}
|
}
|
||||||
|
|
||||||
if (response.data.data && response.data.data == 'CONNECTED') {
|
if (response.data.data && response.data.data == "CONNECTED") {
|
||||||
await whatsapps[i].update({ status: 'CONNECTED' });
|
await whatsapps[i].update({ status: "CONNECTED" });
|
||||||
}
|
}
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
|
await whatsapps[i].update({ status: "OPENING" });
|
||||||
|
|
||||||
await whatsapps[i].update({ status: 'OPENING' });
|
console.log(
|
||||||
|
`There was an error on try acess the api sessions ${whatsapps[i].dataValues.url}`
|
||||||
console.log(`There was an error on try acess the api sessions ${whatsapps[i].dataValues.url}`)
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
await new Promise(f => setTimeout(f, 100));
|
await new Promise(f => setTimeout(f, 100));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
if (process.env.CACHE) {
|
if (process.env.CACHE) {
|
||||||
|
const cacheLength = await cacheSize();
|
||||||
|
|
||||||
const cacheLength = await cacheSize()
|
console.log("cacheSize: ", cacheLength);
|
||||||
|
|
||||||
console.log('cacheSize: ', cacheLength)
|
|
||||||
|
|
||||||
if (cacheLength == 0) {
|
if (cacheLength == 0) {
|
||||||
console.log('Loading from cache...')
|
console.log("Loading from cache...");
|
||||||
await flushCache()
|
await flushCache();
|
||||||
await loadContactsCache()
|
await loadContactsCache();
|
||||||
await loadTicketsCache()
|
await loadTicketsCache();
|
||||||
}
|
}
|
||||||
|
|
||||||
await loadSchedulesCache()
|
await loadSchedulesCache();
|
||||||
// await loadWhatsappCache()
|
// await loadWhatsappCache()
|
||||||
}
|
}
|
||||||
|
|
||||||
delRestoreControllFile()
|
delRestoreControllFile();
|
||||||
})()
|
})();
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
|
||||||
const io = getIO();
|
const io = getIO();
|
||||||
|
|
||||||
console.log('Triggered socket!')
|
console.log("Triggered socket!");
|
||||||
|
|
||||||
let users = await User.findAll({ raw: true, attributes: ["id"], })
|
let users = await User.findAll({ raw: true, attributes: ["id"] });
|
||||||
|
|
||||||
if (users && users.length > 0) {
|
if (users && users.length > 0) {
|
||||||
|
|
||||||
for (let i = 0; i < users.length; i++) {
|
for (let i = 0; i < users.length; i++) {
|
||||||
|
|
||||||
io.emit("reload_page", {
|
io.emit("reload_page", {
|
||||||
action: "update",
|
action: "update",
|
||||||
userId: users[i].id
|
userId: users[i].id
|
||||||
});
|
});
|
||||||
|
|
||||||
console.log('USER ID: ', users[i].id)
|
console.log("USER ID: ", users[i].id);
|
||||||
|
|
||||||
await new Promise(f => setTimeout(f, 100));
|
await new Promise(f => setTimeout(f, 100));
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, 5000);
|
||||||
|
|
||||||
}, 5000)
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,35 @@
|
||||||
|
import AppError from "../../errors/AppError";
|
||||||
|
import SettingTicket from "../../models/SettingTicket";
|
||||||
|
|
||||||
|
interface Request {
|
||||||
|
key: string;
|
||||||
|
startTime: string;
|
||||||
|
endTime: string;
|
||||||
|
value: string;
|
||||||
|
message: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
const updateSettingTicket = async ({
|
||||||
|
key,
|
||||||
|
startTime,
|
||||||
|
endTime,
|
||||||
|
value,
|
||||||
|
message
|
||||||
|
}: Request): Promise<SettingTicket | undefined> => {
|
||||||
|
try {
|
||||||
|
const businessHours = await SettingTicket.findOne({ where: { key } });
|
||||||
|
|
||||||
|
if (!businessHours) {
|
||||||
|
throw new AppError("ERR_NO_SETTING_FOUND", 404);
|
||||||
|
}
|
||||||
|
|
||||||
|
await businessHours.update({ startTime, endTime, message, value });
|
||||||
|
|
||||||
|
return businessHours;
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error("===> Error on UpdateSettingService.ts file: \n", error);
|
||||||
|
throw new AppError(error.message);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export default updateSettingTicket;
|
|
@ -35,8 +35,9 @@ const CreateTicketService = async ({
|
||||||
try {
|
try {
|
||||||
const defaultWhatsapp = await GetDefaultWhatsApp(userId);
|
const defaultWhatsapp = await GetDefaultWhatsApp(userId);
|
||||||
|
|
||||||
|
const user = await User.findByPk(userId, { raw: true });
|
||||||
|
|
||||||
if (!queueId) {
|
if (!queueId) {
|
||||||
const user = await User.findByPk(userId, { raw: true });
|
|
||||||
const matchingQueue = await whatsappQueueMatchingUserQueue(
|
const matchingQueue = await whatsappQueueMatchingUserQueue(
|
||||||
userId,
|
userId,
|
||||||
defaultWhatsapp,
|
defaultWhatsapp,
|
||||||
|
@ -45,7 +46,7 @@ const CreateTicketService = async ({
|
||||||
queueId = matchingQueue ? matchingQueue.queueId : undefined;
|
queueId = matchingQueue ? matchingQueue.queueId : undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
await CheckContactOpenTickets(contactId);
|
await CheckContactOpenTickets(contactId, defaultWhatsapp.id);
|
||||||
|
|
||||||
const { isGroup } = await ShowContactService(contactId);
|
const { isGroup } = await ShowContactService(contactId);
|
||||||
|
|
||||||
|
|
|
@ -6,7 +6,8 @@ import Ticket from "../../models/Ticket";
|
||||||
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
|
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
|
||||||
import ShowTicketService from "./ShowTicketService";
|
import ShowTicketService from "./ShowTicketService";
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
|
import ListWhatsAppsNumber from "../WhatsappService/ListWhatsAppsNumber";
|
||||||
|
import { getSettingValue } from "../../helpers/WhaticketSettings";
|
||||||
|
|
||||||
const FindOrCreateTicketService = async (
|
const FindOrCreateTicketService = async (
|
||||||
contact: Contact,
|
contact: Contact,
|
||||||
|
@ -14,21 +15,34 @@ const FindOrCreateTicketService = async (
|
||||||
unreadMessages: number,
|
unreadMessages: number,
|
||||||
groupContact?: Contact
|
groupContact?: Contact
|
||||||
): Promise<Ticket> => {
|
): Promise<Ticket> => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
let ticket;
|
||||||
|
|
||||||
let ticket = await Ticket.findOne({
|
if (getSettingValue("oneContactChatWithManyWhats")?.value == "enabled") {
|
||||||
where: {
|
let whats = await ListWhatsAppsNumber(whatsappId);
|
||||||
status: {
|
|
||||||
[Op.or]: ["open", "pending", "queueChoice"]
|
ticket = await Ticket.findOne({
|
||||||
},
|
where: {
|
||||||
contactId: groupContact ? groupContact.id : contact.id
|
status: {
|
||||||
}
|
[Op.or]: ["open", "pending", "queueChoice"]
|
||||||
});
|
},
|
||||||
|
contactId: groupContact ? groupContact.id : contact.id,
|
||||||
|
whatsappId: { [Op.in]: whats.whatsapps.map((w: any) => w.id) }
|
||||||
|
}
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
ticket = await Ticket.findOne({
|
||||||
|
where: {
|
||||||
|
status: {
|
||||||
|
[Op.or]: ["open", "pending", "queueChoice"]
|
||||||
|
},
|
||||||
|
contactId: groupContact ? groupContact.id : contact.id
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
const { queues, greetingMessage } = await ShowWhatsAppService(whatsappId);
|
const { queues, greetingMessage } = await ShowWhatsAppService(whatsappId);
|
||||||
|
|
||||||
|
|
||||||
//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 }
|
||||||
|
@ -46,10 +60,7 @@ const FindOrCreateTicketService = async (
|
||||||
order: [["updatedAt", "DESC"]]
|
order: [["updatedAt", "DESC"]]
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (ticket) {
|
if (ticket) {
|
||||||
|
|
||||||
await ticket.update({
|
await ticket.update({
|
||||||
status: "pending",
|
status: "pending",
|
||||||
userId: null,
|
userId: null,
|
||||||
|
@ -59,7 +70,6 @@ const FindOrCreateTicketService = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ticket && !groupContact) {
|
if (!ticket && !groupContact) {
|
||||||
|
|
||||||
ticket = await Ticket.findOne({
|
ticket = await Ticket.findOne({
|
||||||
where: {
|
where: {
|
||||||
updatedAt: {
|
updatedAt: {
|
||||||
|
@ -77,7 +87,6 @@ const FindOrCreateTicketService = async (
|
||||||
});
|
});
|
||||||
|
|
||||||
if (ticket) {
|
if (ticket) {
|
||||||
|
|
||||||
await ticket.update({
|
await ticket.update({
|
||||||
status: "pending",
|
status: "pending",
|
||||||
userId: null,
|
userId: null,
|
||||||
|
@ -87,11 +96,10 @@ const FindOrCreateTicketService = async (
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!ticket) {
|
if (!ticket) {
|
||||||
|
let status = "pending";
|
||||||
let status = "pending"
|
|
||||||
|
|
||||||
if (queues.length > 1 && !botInfo.isOnQueue) {
|
if (queues.length > 1 && !botInfo.isOnQueue) {
|
||||||
status = "queueChoice"
|
status = "queueChoice";
|
||||||
}
|
}
|
||||||
|
|
||||||
ticket = await Ticket.create({
|
ticket = await Ticket.create({
|
||||||
|
@ -113,9 +121,8 @@ const FindOrCreateTicketService = async (
|
||||||
ticket = await ShowTicketService(ticket.id);
|
ticket = await ShowTicketService(ticket.id);
|
||||||
|
|
||||||
return ticket;
|
return ticket;
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('===> Error on FindOrCreateTicketService.ts file: \n', error)
|
console.error("===> Error on FindOrCreateTicketService.ts file: \n", error);
|
||||||
throw new AppError(error.message);
|
throw new AppError(error.message);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -6,25 +6,24 @@ import SendWhatsAppMessage from "../WbotServices/SendWhatsAppMessage";
|
||||||
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
|
import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService";
|
||||||
import ShowTicketService from "./ShowTicketService";
|
import ShowTicketService from "./ShowTicketService";
|
||||||
|
|
||||||
import { createOrUpdateTicketCache } from '../../helpers/TicketCache'
|
import { createOrUpdateTicketCache } from "../../helpers/TicketCache";
|
||||||
import AppError from "../../errors/AppError";
|
import AppError from "../../errors/AppError";
|
||||||
import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
|
import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
|
||||||
var flatten = require('flat')
|
var flatten = require("flat");
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
interface TicketData {
|
interface TicketData {
|
||||||
status?: string;
|
status?: string;
|
||||||
userId?: number;
|
userId?: number;
|
||||||
queueId?: number;
|
queueId?: number;
|
||||||
statusChatEnd?: string;
|
statusChatEnd?: string;
|
||||||
unreadMessages?: number;
|
unreadMessages?: number;
|
||||||
|
whatsappId?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
ticketData: TicketData;
|
ticketData: TicketData;
|
||||||
ticketId: string | number;
|
ticketId: string | number;
|
||||||
msg?: string
|
msg?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
|
@ -35,13 +34,18 @@ interface Response {
|
||||||
|
|
||||||
const UpdateTicketService = async ({
|
const UpdateTicketService = async ({
|
||||||
ticketData,
|
ticketData,
|
||||||
ticketId,
|
ticketId,
|
||||||
msg=''
|
msg = ""
|
||||||
}: Request): Promise<Response> => {
|
}: Request): Promise<Response> => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
const {
|
||||||
const { status, userId, queueId, statusChatEnd, unreadMessages } = ticketData;
|
status,
|
||||||
|
userId,
|
||||||
|
queueId,
|
||||||
|
statusChatEnd,
|
||||||
|
unreadMessages,
|
||||||
|
whatsappId
|
||||||
|
} = ticketData;
|
||||||
|
|
||||||
const ticket = await ShowTicketService(ticketId);
|
const ticket = await ShowTicketService(ticketId);
|
||||||
// await SetTicketMessagesAsRead(ticket);
|
// await SetTicketMessagesAsRead(ticket);
|
||||||
|
@ -50,7 +54,7 @@ const UpdateTicketService = async ({
|
||||||
const oldUserId = ticket.user?.id;
|
const oldUserId = ticket.user?.id;
|
||||||
|
|
||||||
if (oldStatus === "closed") {
|
if (oldStatus === "closed") {
|
||||||
await CheckContactOpenTickets(ticket.contact.id);
|
await CheckContactOpenTickets(ticket.contact.id, ticket.whatsappId);
|
||||||
}
|
}
|
||||||
|
|
||||||
await ticket.update({
|
await ticket.update({
|
||||||
|
@ -58,36 +62,36 @@ const UpdateTicketService = async ({
|
||||||
queueId,
|
queueId,
|
||||||
userId,
|
userId,
|
||||||
unreadMessages,
|
unreadMessages,
|
||||||
statusChatEnd
|
statusChatEnd,
|
||||||
|
whatsappId
|
||||||
});
|
});
|
||||||
|
|
||||||
await ticket.reload();
|
await ticket.reload();
|
||||||
|
|
||||||
if (msg.length > 0) {
|
if (msg?.trim().length > 0) {
|
||||||
|
|
||||||
setTimeout(async () => {
|
setTimeout(async () => {
|
||||||
|
sendWhatsAppMessageSocket(ticket, msg);
|
||||||
sendWhatsAppMessageSocket(ticket, msg)
|
}, 2000);
|
||||||
|
|
||||||
}, 2000)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// TEST DEL
|
// TEST DEL
|
||||||
try {
|
try {
|
||||||
|
// const { name, number } = await ShowContactService(ticket.contactId)
|
||||||
|
|
||||||
// const { name, number } = await ShowContactService(ticket.contactId)
|
let jsonString = JSON.stringify(ticket); //convert to string to remove the sequelize specific meta data
|
||||||
|
|
||||||
let jsonString = JSON.stringify(ticket); //convert to string to remove the sequelize specific meta data
|
|
||||||
let ticket_obj = JSON.parse(jsonString); //to make plain json
|
let ticket_obj = JSON.parse(jsonString); //to make plain json
|
||||||
delete ticket_obj['contact']['extraInfo']
|
delete ticket_obj["contact"]["extraInfo"];
|
||||||
delete ticket_obj['user']
|
delete ticket_obj["user"];
|
||||||
|
|
||||||
ticket_obj = flatten(ticket_obj)
|
ticket_obj = flatten(ticket_obj);
|
||||||
|
|
||||||
await createOrUpdateTicketCache(`ticket:${ticket.id}`, ticket_obj)
|
|
||||||
|
|
||||||
|
await createOrUpdateTicketCache(`ticket:${ticket.id}`, ticket_obj);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('There was an error on UpdateTicketService.ts on createTicketCache: ', error)
|
console.log(
|
||||||
|
"There was an error on UpdateTicketService.ts on createTicketCache: ",
|
||||||
|
error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
//
|
//
|
||||||
|
|
||||||
|
@ -100,7 +104,6 @@ const UpdateTicketService = async ({
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
io.to(ticket.status)
|
io.to(ticket.status)
|
||||||
.to("notification")
|
.to("notification")
|
||||||
.to(ticketId.toString())
|
.to(ticketId.toString())
|
||||||
|
@ -109,20 +112,16 @@ const UpdateTicketService = async ({
|
||||||
ticket
|
ticket
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
io.emit("ticketStatus", {
|
io.emit("ticketStatus", {
|
||||||
action: "update",
|
action: "update",
|
||||||
ticketStatus: { ticketId: ticket.id, status: ticket.status }
|
ticketStatus: { ticketId: ticket.id, status: ticket.status }
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
return { ticket, oldStatus, oldUserId };
|
return { ticket, oldStatus, oldUserId };
|
||||||
|
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('===> Error on UpdateTicketService.ts file: \n', error)
|
console.error("===> Error on UpdateTicketService.ts file: \n", error);
|
||||||
throw new AppError(error.message);
|
throw new AppError(error.message);
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
export default UpdateTicketService;
|
export default UpdateTicketService;
|
||||||
|
|
|
@ -1,40 +1,62 @@
|
||||||
|
import { Sequelize } from "sequelize";
|
||||||
import { Sequelize, } from "sequelize";
|
|
||||||
|
|
||||||
const dbConfig = require("../../config/database");
|
const dbConfig = require("../../config/database");
|
||||||
const sequelize = new Sequelize(dbConfig);
|
const sequelize = new Sequelize(dbConfig);
|
||||||
|
|
||||||
const { QueryTypes } = require('sequelize');
|
const { QueryTypes } = require("sequelize");
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
profile: string;
|
profile?: string;
|
||||||
userId?: string | number;
|
userId?: string | number;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QueuesByUser = async ({ profile, userId }: Request): Promise<any[]> => {
|
const QueuesByUser = async ({ profile, userId }: Request): Promise<any[]> => {
|
||||||
|
let queueByUsersInfo: any = [];
|
||||||
|
|
||||||
let queueByUsersInfo = []
|
if (userId && profile) {
|
||||||
|
// CONSULTANDO FILAS PELO ID DO USUARIO
|
||||||
if (userId) {
|
queueByUsersInfo = await sequelize.query(
|
||||||
// CONSULTANDO FILAS PELO ID DO USUARIO
|
`select UserQueues.userId, UserQueues.queueId, Users.name,
|
||||||
queueByUsersInfo = await sequelize.query(`select UserQueues.userId, UserQueues.queueId, Users.name,
|
|
||||||
Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
||||||
UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.id = '${userId}' and Users.profile = '${profile}' order by userId, queueId;`, { type: QueryTypes.SELECT });
|
UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.id = '${userId}' and Users.profile = '${profile}' order by userId, queueId;`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
} else {
|
);
|
||||||
|
} else if (profile) {
|
||||||
// CONSULTANDO FILAS PELO USUARIO
|
// CONSULTANDO FILAS PELO USUARIO
|
||||||
queueByUsersInfo = await sequelize.query(`select UserQueues.userId, UserQueues.queueId, Users.name,
|
queueByUsersInfo = await sequelize.query(
|
||||||
|
`select UserQueues.userId, UserQueues.queueId, Users.name,
|
||||||
Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
||||||
UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.profile = '${profile}' order by userId, queueId;`, { type: QueryTypes.SELECT });
|
UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.profile = '${profile}' order by userId, queueId;`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
} else if (userId) {
|
||||||
|
queueByUsersInfo = await sequelize.query(
|
||||||
|
`select UserQueues.userId, UserQueues.queueId, Users.name,
|
||||||
|
Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
||||||
|
UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.id = '${userId}' order by userId, queueId;`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
// if (userId) {
|
||||||
|
// // CONSULTANDO FILAS PELO ID DO USUARIO
|
||||||
|
// queueByUsersInfo = await sequelize.query(
|
||||||
|
// `select UserQueues.userId, UserQueues.queueId, Users.name,
|
||||||
|
// Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
||||||
|
// UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.id = '${userId}' and Users.profile = '${profile}' order by userId, queueId;`,
|
||||||
|
// { type: QueryTypes.SELECT }
|
||||||
|
// );
|
||||||
|
// } else {
|
||||||
|
// // CONSULTANDO FILAS PELO USUARIO
|
||||||
|
// queueByUsersInfo = await sequelize.query(
|
||||||
|
// `select UserQueues.userId, UserQueues.queueId, Users.name,
|
||||||
|
// Queues.name, Queues.color from UserQueues inner join Users inner join Queues on
|
||||||
|
// UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.profile = '${profile}' order by userId, queueId;`,
|
||||||
|
// { type: QueryTypes.SELECT }
|
||||||
|
// );
|
||||||
|
// }
|
||||||
|
|
||||||
return queueByUsersInfo;
|
return queueByUsersInfo;
|
||||||
};
|
};
|
||||||
|
|
||||||
export default QueuesByUser;
|
export default QueuesByUser;
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -7,7 +7,21 @@ import { copyFolder } from "../../helpers/CopyFolder";
|
||||||
import { removeDir } from "../../helpers/DeleteDirectory";
|
import { removeDir } from "../../helpers/DeleteDirectory";
|
||||||
import path from "path";
|
import path from "path";
|
||||||
|
|
||||||
import { format } from "date-fns";
|
import {
|
||||||
|
isHoliday,
|
||||||
|
isOutBusinessTime,
|
||||||
|
isWeekend
|
||||||
|
} from "../../helpers/TicketConfig";
|
||||||
|
|
||||||
|
import {
|
||||||
|
format as _format,
|
||||||
|
isWithinInterval,
|
||||||
|
parse,
|
||||||
|
subMinutes,
|
||||||
|
isSaturday,
|
||||||
|
isSunday,
|
||||||
|
parseISO
|
||||||
|
} from "date-fns";
|
||||||
import ptBR from "date-fns/locale/pt-BR";
|
import ptBR from "date-fns/locale/pt-BR";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
|
@ -37,6 +51,8 @@ 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 SettingTicket from "../../models/SettingTicket";
|
||||||
|
|
||||||
|
|
||||||
import fs from "fs";
|
import fs from "fs";
|
||||||
|
|
||||||
|
@ -914,6 +930,19 @@ const handleMessage = async (msg: any, wbot: any): Promise<void> => {
|
||||||
// let groupContact: Contact | undefined;
|
// let groupContact: Contact | undefined;
|
||||||
|
|
||||||
if (msg.fromMe) {
|
if (msg.fromMe) {
|
||||||
|
const ticketExpiration = await SettingTicket.findOne({
|
||||||
|
where: { key: "ticketExpiration" }
|
||||||
|
});
|
||||||
|
|
||||||
|
if (
|
||||||
|
ticketExpiration &&
|
||||||
|
ticketExpiration.value == "enabled" &&
|
||||||
|
ticketExpiration?.message.trim() == msg.body.trim()
|
||||||
|
) {
|
||||||
|
console.log("*********** TICKET EXPIRATION");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// console.log('FROM ME: ', msg.fromMe, ' | /\u200e/.test(msg.body[0]: ', (/\u200e/.test(msg.body[0])))
|
// console.log('FROM ME: ', msg.fromMe, ' | /\u200e/.test(msg.body[0]: ', (/\u200e/.test(msg.body[0])))
|
||||||
|
|
||||||
// messages sent automatically by wbot have a special character in front of it
|
// messages sent automatically by wbot have a special character in front of it
|
||||||
|
@ -1206,6 +1235,54 @@ const handleMessage = async (msg: any, wbot: any): Promise<void> => {
|
||||||
if (msg && !msg.fromMe && ticket.status == "pending") {
|
if (msg && !msg.fromMe && ticket.status == "pending") {
|
||||||
await setMessageAsRead(ticket);
|
await setMessageAsRead(ticket);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let ticketHasQueue = false;
|
||||||
|
|
||||||
|
if (ticket?.queueId) {
|
||||||
|
ticketHasQueue = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ticketHasQueue) {
|
||||||
|
// MESSAGE TO HOLIDAY
|
||||||
|
const holiday: any = await isHoliday();
|
||||||
|
|
||||||
|
if (holiday && holiday.set) {
|
||||||
|
if (msg.fromMe && holiday.msg == msg.body) {
|
||||||
|
console.log("HOLIDAY MESSAGE IGNORED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botSendMessage(ticket, holiday.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MESSAGES TO SATURDAY OR SUNDAY
|
||||||
|
const weekend: any = await isWeekend();
|
||||||
|
|
||||||
|
if (weekend && weekend.set) {
|
||||||
|
if (msg.fromMe && weekend.msg == msg.body) {
|
||||||
|
console.log("WEEKEND MESSAGE IGNORED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botSendMessage(ticket, weekend.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// MESSAGE TO BUSINESS TIME
|
||||||
|
const businessTime = await isOutBusinessTime();
|
||||||
|
|
||||||
|
if (businessTime && businessTime.set) {
|
||||||
|
if (msg.fromMe && businessTime.msg == msg.body) {
|
||||||
|
console.log("BUSINESS TIME MESSAGE IGNORED");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
botSendMessage(ticket, businessTime.msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
Sentry.captureException(err);
|
Sentry.captureException(err);
|
||||||
console.log("xxxxxxxxxxxxx err: ", err);
|
console.log("xxxxxxxxxxxxx err: ", err);
|
||||||
|
|
|
@ -1,30 +1,32 @@
|
||||||
|
|
||||||
import Whatsapp from "../../models/Whatsapp";
|
import Whatsapp from "../../models/Whatsapp";
|
||||||
|
|
||||||
const ListWhatsAppsNumber = async (whatsappId: string | number, status: string): Promise<Whatsapp[] | any> => {
|
const ListWhatsAppsNumber = async (
|
||||||
|
whatsappId: string | number,
|
||||||
|
status?: string
|
||||||
|
): Promise<Whatsapp[] | any> => {
|
||||||
|
const whatsapp = await Whatsapp.findByPk(whatsappId, { raw: true });
|
||||||
|
|
||||||
// const whatsapp = await Whatsapp.findOne({
|
let whatsapps: any = [];
|
||||||
// raw: true,
|
|
||||||
// where: { id: whatsappId }
|
|
||||||
// })
|
|
||||||
|
|
||||||
const whatsapp = await Whatsapp.findByPk(whatsappId, { raw: true })
|
|
||||||
|
|
||||||
if (whatsapp) {
|
|
||||||
|
|
||||||
const whatsapps = await Whatsapp.findAll({
|
|
||||||
raw: true,
|
|
||||||
where: { number: whatsapp.number, status: status },
|
|
||||||
attributes: ['id', 'number', 'status', 'isDefault', 'url']
|
|
||||||
});
|
|
||||||
|
|
||||||
return { whatsapps, whatsapp };
|
|
||||||
|
|
||||||
|
if (whatsapp) {
|
||||||
|
if (status) {
|
||||||
|
whatsapps = await Whatsapp.findAll({
|
||||||
|
raw: true,
|
||||||
|
where: { number: whatsapp.number, status: status },
|
||||||
|
attributes: ["id", "number", "status", "isDefault", "url"]
|
||||||
|
});
|
||||||
|
} else {
|
||||||
|
whatsapps = await Whatsapp.findAll({
|
||||||
|
raw: true,
|
||||||
|
where: { number: whatsapp.number },
|
||||||
|
attributes: ["id", "number", "status", "isDefault", "url"]
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
return { whatsapps: [], whatsapp: null }
|
return { whatsapps, whatsapp };
|
||||||
|
}
|
||||||
|
|
||||||
|
return { whatsapps: [], whatsapp: null };
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ListWhatsAppsNumber;
|
export default ListWhatsAppsNumber;
|
||||||
|
|
|
@ -4,13 +4,13 @@
|
||||||
"private": true,
|
"private": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"@date-io/date-fns": "^1.3.13",
|
"@date-io/date-fns": "^1.3.13",
|
||||||
"@emotion/react": "^11.7.1",
|
"@emotion/react": "^11.11.1",
|
||||||
"@emotion/styled": "^11.6.0",
|
"@emotion/styled": "^11.11.0",
|
||||||
"@material-ui/core": "^4.12.1",
|
"@material-ui/core": "^4.12.1",
|
||||||
"@material-ui/icons": "^4.9.1",
|
"@material-ui/icons": "^4.9.1",
|
||||||
"@material-ui/lab": "^4.0.0-alpha.56",
|
"@material-ui/lab": "^4.0.0-alpha.56",
|
||||||
"@material-ui/pickers": "^3.3.10",
|
"@material-ui/pickers": "^3.3.10",
|
||||||
"@mui/material": "^5.3.0",
|
"@mui/material": "^5.14.4",
|
||||||
"@mui/x-data-grid": "^5.3.0",
|
"@mui/x-data-grid": "^5.3.0",
|
||||||
"@testing-library/jest-dom": "^5.11.4",
|
"@testing-library/jest-dom": "^5.11.4",
|
||||||
"@testing-library/react": "^11.0.4",
|
"@testing-library/react": "^11.0.4",
|
||||||
|
@ -20,6 +20,7 @@
|
||||||
"dotenv": "^16.0.1",
|
"dotenv": "^16.0.1",
|
||||||
"emoji-mart": "^3.0.1",
|
"emoji-mart": "^3.0.1",
|
||||||
"formik": "^2.2.0",
|
"formik": "^2.2.0",
|
||||||
|
"formik-material-ui-pickers": "^1.0.0-alpha.1",
|
||||||
"i18next": "^19.8.2",
|
"i18next": "^19.8.2",
|
||||||
"i18next-browser-languagedetector": "^6.0.1",
|
"i18next-browser-languagedetector": "^6.0.1",
|
||||||
"js-file-download": "^0.4.12",
|
"js-file-download": "^0.4.12",
|
||||||
|
@ -30,6 +31,7 @@
|
||||||
"react": "^17.0.2",
|
"react": "^17.0.2",
|
||||||
"react-color": "^2.19.3",
|
"react-color": "^2.19.3",
|
||||||
"react-csv": "^2.2.2",
|
"react-csv": "^2.2.2",
|
||||||
|
"react-datepicker": "^4.16.0",
|
||||||
"react-dom": "^17.0.2",
|
"react-dom": "^17.0.2",
|
||||||
"react-modal-image": "^2.5.0",
|
"react-modal-image": "^2.5.0",
|
||||||
"react-router-dom": "^5.2.0",
|
"react-router-dom": "^5.2.0",
|
||||||
|
|
|
@ -0,0 +1,379 @@
|
||||||
|
import React, { useState, useEffect, useContext, } from 'react'
|
||||||
|
import * as Yup from 'yup'
|
||||||
|
import { Formik, Form, Field, } from 'formik'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { green } from '@material-ui/core/colors'
|
||||||
|
|
||||||
|
import { AuthContext } from '../../context/Auth/AuthContext'
|
||||||
|
|
||||||
|
|
||||||
|
import apiBroker from '../../services/apiBroker'
|
||||||
|
|
||||||
|
import Select from "@material-ui/core/Select"
|
||||||
|
import MenuItem from "@material-ui/core/MenuItem"
|
||||||
|
|
||||||
|
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext"
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Button,
|
||||||
|
DialogActions,
|
||||||
|
CircularProgress,
|
||||||
|
TextField,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
|
import { i18n } from '../../translate/i18n'
|
||||||
|
import toastError from '../../errors/toastError'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
|
||||||
|
multFieldLine: {
|
||||||
|
display: 'flex',
|
||||||
|
'& > *:not(:last-child)': {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
btnWrapper: {
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonProgress: {
|
||||||
|
color: green[500],
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
marginTop: -12,
|
||||||
|
marginLeft: -12,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const SessionSchema = Yup.object().shape({
|
||||||
|
name: Yup.string()
|
||||||
|
.min(2, 'Too Short!')
|
||||||
|
.max(100, 'Too Long!')
|
||||||
|
.required('Required'),
|
||||||
|
|
||||||
|
secondStart: Yup.number()
|
||||||
|
.required('Min time is required')
|
||||||
|
.min(3, 'Min time must be 3')
|
||||||
|
.max(3600, 'Min must be less than 3600'),
|
||||||
|
|
||||||
|
secondEnd: Yup.number()
|
||||||
|
.required('Max time is required')
|
||||||
|
.min(3, 'Min time must be 3')
|
||||||
|
.max(3600, 'Max must be less than 3600')
|
||||||
|
.test('higher-than-lower', 'Tempo final deve ser maior que tempo inicio!', function (
|
||||||
|
secondEnd
|
||||||
|
) {
|
||||||
|
const secondStart = this.resolve(Yup.ref('secondStart'))
|
||||||
|
return secondStart === undefined || secondEnd === undefined || secondEnd > secondStart
|
||||||
|
}),
|
||||||
|
|
||||||
|
})
|
||||||
|
|
||||||
|
const CampaignModal = ({ open, onClose, campaignId, dispatch }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const initialState = {
|
||||||
|
name: '',
|
||||||
|
message: '',
|
||||||
|
status: 'pending',
|
||||||
|
whatsapp_sender: '',
|
||||||
|
csv_original_file_name: '',
|
||||||
|
secondStart: '3',
|
||||||
|
secondEnd: '15',
|
||||||
|
textToSeconds: true,
|
||||||
|
}
|
||||||
|
|
||||||
|
const { user } = useContext(AuthContext)
|
||||||
|
|
||||||
|
const { whatsApps } = useContext(WhatsAppsContext)
|
||||||
|
|
||||||
|
const [campaign, setCampaign] = useState(initialState)
|
||||||
|
// const [selectedQueueIds, setSelectedQueueIds] = useState([])
|
||||||
|
const [file, setFile] = useState()
|
||||||
|
|
||||||
|
const [selectedNumber, setSelectedNumber] = useState('')
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSession = async () => {
|
||||||
|
if (!campaignId) return
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const { data } = await apiBroker.get('/campaign', {
|
||||||
|
params: {
|
||||||
|
adminId: user.id,
|
||||||
|
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
||||||
|
identifier: 'campaign',
|
||||||
|
id: campaignId
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
setCampaign(data.campaign)
|
||||||
|
setSelectedNumber(data.campaign.whatsapp_sender)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchSession()
|
||||||
|
}, [campaignId, user.id])
|
||||||
|
|
||||||
|
const handleSaveCampaign = async (values) => {
|
||||||
|
let response = null
|
||||||
|
|
||||||
|
const formData = new FormData()
|
||||||
|
formData.append('adminId', user.id)
|
||||||
|
formData.append('baseURL', process.env.REACT_APP_BACKEND_URL_PRIVATE)
|
||||||
|
formData.append('frontURL', process.env.REACT_APP_FRONTEND_URL)
|
||||||
|
formData.append('identifier', 'campaign')
|
||||||
|
formData.append('file', file)
|
||||||
|
formData.append('name', values.name)
|
||||||
|
formData.append('whatsapp_sender', selectedNumber)
|
||||||
|
formData.append('secondStart', values.secondStart)
|
||||||
|
formData.append('secondEnd', values.secondEnd)
|
||||||
|
formData.append('textToSeconds', values.textToSeconds)
|
||||||
|
formData.append('message', values.message)
|
||||||
|
formData.append('csv_original_file_name', file?.name)
|
||||||
|
|
||||||
|
|
||||||
|
const config = {
|
||||||
|
headers: {
|
||||||
|
'content-type': 'multipart/form-data',
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
if (campaignId) {
|
||||||
|
|
||||||
|
response = await apiBroker.patch(`/campaign/${campaignId}`,
|
||||||
|
formData,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
toast.success('Campanha atualizada com sucesso!')
|
||||||
|
|
||||||
|
} else {
|
||||||
|
|
||||||
|
response = await apiBroker.post('/campaign',
|
||||||
|
formData,
|
||||||
|
config
|
||||||
|
)
|
||||||
|
toast.success('Campanha criada com sucesso!')
|
||||||
|
}
|
||||||
|
|
||||||
|
dispatch({ type: "UPDATE_CAMPAIGNS", payload: response.data.campaign })
|
||||||
|
|
||||||
|
handleClose()
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose()
|
||||||
|
setCampaign(initialState)
|
||||||
|
setSelectedNumber('')
|
||||||
|
setFile(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleChange(event) {
|
||||||
|
try {
|
||||||
|
if (event.target.files[0].size > 1024 * 1024 * 4) {
|
||||||
|
alert('Arquivo não pode ser maior que 4 MB!')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
setFile(event.target.files[0])
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
scroll="paper"
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
{campaignId ? 'Editar campanha' : 'Adicionar campanha'}
|
||||||
|
</DialogTitle>
|
||||||
|
<Formik
|
||||||
|
initialValues={campaign}
|
||||||
|
enableReinitialize={true}
|
||||||
|
validationSchema={SessionSchema}
|
||||||
|
onSubmit={(values, actions) => {
|
||||||
|
setTimeout(() => {
|
||||||
|
handleSaveCampaign(values)
|
||||||
|
actions.setSubmitting(false)
|
||||||
|
}, 400)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ values, touched, errors, isSubmitting }) => (
|
||||||
|
|
||||||
|
<Form>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<div>
|
||||||
|
<div className={classes.multFieldLine}>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={i18n.t('whatsappModal.form.name')}
|
||||||
|
autoFocus
|
||||||
|
name="name"
|
||||||
|
error={touched.name && Boolean(errors.name)}
|
||||||
|
helperText={touched.name && errors.name}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
className={classes.textField}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
value={selectedNumber}
|
||||||
|
onChange={(e) => setSelectedNumber(e.target.value)}
|
||||||
|
label={i18n.t("transferTicketModal.fieldQueuePlaceholder")}
|
||||||
|
required
|
||||||
|
>
|
||||||
|
<MenuItem style={{ background: "white", }} value={''}> </MenuItem>
|
||||||
|
{whatsApps.map((whatsapp) => (
|
||||||
|
<MenuItem
|
||||||
|
key={whatsapp.id}
|
||||||
|
value={whatsapp.number}
|
||||||
|
>{whatsapp.number}
|
||||||
|
</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.multFieldLine}>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label="Mensagem"
|
||||||
|
type="message"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="message"
|
||||||
|
error={touched.message && Boolean(errors.message)}
|
||||||
|
helperText={touched.message && errors.message}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
gap: '10px',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<input
|
||||||
|
type="file"
|
||||||
|
accept=".csv"
|
||||||
|
style={{ display: 'none' }}
|
||||||
|
onChange={handleChange}
|
||||||
|
id="contained-button-file"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label htmlFor="contained-button-file">
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
component="span"
|
||||||
|
>
|
||||||
|
CSV UPLOAD
|
||||||
|
</Button>
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<h3>{file?.name || campaign?.csv_original_file_name}</h3>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label='Inicio em segundos'
|
||||||
|
autoFocus
|
||||||
|
name="secondStart"
|
||||||
|
error={touched.secondStart && Boolean(errors.secondStart)}
|
||||||
|
helperText={touched.secondStart && errors.secondStart}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
className={classes.textField}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label='Fim em segundos'
|
||||||
|
autoFocus
|
||||||
|
name="secondEnd"
|
||||||
|
error={touched.secondEnd && Boolean(errors.secondEnd)}
|
||||||
|
helperText={touched.secondEnd && errors.secondEnd}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
className={classes.textField}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<Field type="checkbox" name="textToSeconds"/>
|
||||||
|
Tamanho do texto para segundos
|
||||||
|
</label>
|
||||||
|
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={handleClose}
|
||||||
|
color="secondary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
{i18n.t('whatsappModal.buttons.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="contained"
|
||||||
|
className={classes.btnWrapper}
|
||||||
|
>
|
||||||
|
{campaignId
|
||||||
|
? i18n.t('whatsappModal.buttons.okEdit')
|
||||||
|
: i18n.t('whatsappModal.buttons.okAdd')}
|
||||||
|
{isSubmitting && (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
className={classes.buttonProgress}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Form>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default React.memo(CampaignModal)
|
|
@ -0,0 +1,449 @@
|
||||||
|
import React, { useState, useEffect, } from 'react'
|
||||||
|
// import * as Yup from 'yup'
|
||||||
|
import { Formik, Form, Field, } from 'formik'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { green } from '@material-ui/core/colors'
|
||||||
|
|
||||||
|
import { TimePicker, DatePicker } from 'formik-material-ui-pickers'
|
||||||
|
|
||||||
|
import DateFnsUtils from '@date-io/date-fns'
|
||||||
|
|
||||||
|
import ptBrLocale from "date-fns/locale/pt-BR"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
import {
|
||||||
|
MuiPickersUtilsProvider,
|
||||||
|
} from '@material-ui/pickers'
|
||||||
|
|
||||||
|
import {
|
||||||
|
Dialog,
|
||||||
|
DialogContent,
|
||||||
|
DialogTitle,
|
||||||
|
Button,
|
||||||
|
DialogActions,
|
||||||
|
CircularProgress,
|
||||||
|
TextField,
|
||||||
|
Switch,
|
||||||
|
FormControlLabel,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
|
||||||
|
import api from '../../services/api'
|
||||||
|
import { i18n } from '../../translate/i18n'
|
||||||
|
import toastError from '../../errors/toastError'
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
root: {
|
||||||
|
display: 'flex',
|
||||||
|
flexWrap: 'wrap',
|
||||||
|
},
|
||||||
|
|
||||||
|
multFieldLine: {
|
||||||
|
display: 'flex',
|
||||||
|
'& > *:not(:last-child)': {
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
},
|
||||||
|
|
||||||
|
btnWrapper: {
|
||||||
|
position: 'relative',
|
||||||
|
},
|
||||||
|
|
||||||
|
buttonProgress: {
|
||||||
|
color: green[500],
|
||||||
|
position: 'absolute',
|
||||||
|
top: '50%',
|
||||||
|
left: '50%',
|
||||||
|
marginTop: -12,
|
||||||
|
marginLeft: -12,
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
// const SessionSchema = Yup.object().shape({
|
||||||
|
// name: Yup.string()
|
||||||
|
// .min(2, 'Too Short!')
|
||||||
|
// .max(100, 'Too Long!')
|
||||||
|
// .required('Required'),
|
||||||
|
// })
|
||||||
|
|
||||||
|
const ConfigModal = ({ open, onClose, change }) => {
|
||||||
|
const classes = useStyles()
|
||||||
|
const initialState = {
|
||||||
|
startTimeBus: new Date(),
|
||||||
|
endTimeBus: new Date(),
|
||||||
|
messageBus: '',
|
||||||
|
businessTimeEnable: false,
|
||||||
|
ticketTimeExpiration: new Date(),
|
||||||
|
ticketExpirationMsg: '',
|
||||||
|
ticketExpirationEnable: false,
|
||||||
|
holidayDate: new Date(),
|
||||||
|
holidayDateEnable: false,
|
||||||
|
holidayDateMessage: '',
|
||||||
|
checkboxSundayValue: false,
|
||||||
|
checkboxSaturdayValue: false,
|
||||||
|
weekendMessage: '',
|
||||||
|
enableWeekendMessage: false
|
||||||
|
}
|
||||||
|
|
||||||
|
const [config, setConfig] = useState(initialState)
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchSession = async () => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await api.get('/settings')
|
||||||
|
|
||||||
|
const outBusinessHours = data.config.find((c) => c.key === "outBusinessHours")
|
||||||
|
const ticketExpiration = data.config.find((c) => c.key === "ticketExpiration")
|
||||||
|
const saturday = data.config.find((c) => c.key === "saturday")
|
||||||
|
const sunday = data.config.find((c) => c.key === "sunday")
|
||||||
|
const weekend = data.config.find((c) => c.key === "weekend")
|
||||||
|
const holiday = data.config.find((c) => c.key === "holiday")
|
||||||
|
|
||||||
|
setConfig({
|
||||||
|
startTimeBus: outBusinessHours.startTime,
|
||||||
|
endTimeBus: outBusinessHours.endTime,
|
||||||
|
messageBus: outBusinessHours.message,
|
||||||
|
businessTimeEnable: outBusinessHours.value === 'enabled' ? true : false,
|
||||||
|
|
||||||
|
ticketTimeExpiration: ticketExpiration.startTime,
|
||||||
|
ticketExpirationMsg: ticketExpiration.message,
|
||||||
|
ticketExpirationEnable: ticketExpiration.value === 'enabled' ? true : false,
|
||||||
|
|
||||||
|
checkboxSaturdayValue: saturday.value === 'enabled' ? true : false,
|
||||||
|
checkboxSundayValue: sunday.value === 'enabled' ? true : false,
|
||||||
|
weekendMessage: weekend.message,
|
||||||
|
enableWeekendMessage: weekend.value === 'enabled' ? true : false,
|
||||||
|
|
||||||
|
holidayDate: holiday.startTime,
|
||||||
|
holidayDateMessage: holiday.message,
|
||||||
|
holidayDateEnable: holiday.value === 'enabled' ? true : false,
|
||||||
|
})
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fetchSession()
|
||||||
|
}, [change])
|
||||||
|
|
||||||
|
const handleSaveConfig = async (values) => {
|
||||||
|
|
||||||
|
values = {
|
||||||
|
outBusinessHours: {
|
||||||
|
startTime: values.startTimeBus,
|
||||||
|
endTime: values.endTimeBus,
|
||||||
|
message: values.messageBus,
|
||||||
|
value: values.businessTimeEnable ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
ticketExpiration: {
|
||||||
|
startTime: values.ticketTimeExpiration,
|
||||||
|
message: values.ticketExpirationMsg,
|
||||||
|
value: values.ticketExpirationEnable ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
weekend: {
|
||||||
|
message: values.weekendMessage,
|
||||||
|
value: values.enableWeekendMessage ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
saturday:{
|
||||||
|
value: values.checkboxSaturdayValue ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
sunday: {
|
||||||
|
value: values.checkboxSundayValue ? 'enabled' : 'disabled'
|
||||||
|
},
|
||||||
|
holiday: {
|
||||||
|
startTime: values.holidayDate,
|
||||||
|
message: values.holidayDateMessage,
|
||||||
|
value: values.holidayDateEnable ? 'enabled' : 'disabled'
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
await api.put(`/settings/ticket`, values)
|
||||||
|
|
||||||
|
toast.success('Atualização realizada com sucesso!')
|
||||||
|
handleClose()
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose()
|
||||||
|
// setConfig(initialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleClose}
|
||||||
|
maxWidth="sm"
|
||||||
|
fullWidth
|
||||||
|
scroll="paper"
|
||||||
|
>
|
||||||
|
<DialogTitle>
|
||||||
|
Configurações
|
||||||
|
</DialogTitle>
|
||||||
|
|
||||||
|
|
||||||
|
<Formik
|
||||||
|
initialValues={config}
|
||||||
|
enableReinitialize={true}
|
||||||
|
// validationSchema={SessionSchema}
|
||||||
|
onSubmit={(values, actions) => {
|
||||||
|
|
||||||
|
setTimeout(() => {
|
||||||
|
handleSaveConfig(values)
|
||||||
|
actions.setSubmitting(false)
|
||||||
|
}, 100)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{({ values, touched, errors, isSubmitting }) => (
|
||||||
|
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}>
|
||||||
|
<Form>
|
||||||
|
|
||||||
|
<DialogContent dividers>
|
||||||
|
|
||||||
|
<div className={classes.multFieldLine}>
|
||||||
|
<Field
|
||||||
|
component={TimePicker}
|
||||||
|
name="startTimeBus"
|
||||||
|
label="Inicio atendimento"
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes',]}
|
||||||
|
format="HH:mm"
|
||||||
|
/>
|
||||||
|
{' '}
|
||||||
|
<Field
|
||||||
|
component={TimePicker}
|
||||||
|
name="endTimeBus"
|
||||||
|
label="Fim atendimento"
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes',]}
|
||||||
|
format="HH:mm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="businessTimeEnable"
|
||||||
|
checked={values.businessTimeEnable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'} />
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem fora do horário de atendimento'}
|
||||||
|
type="messageBus"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="messageBus"
|
||||||
|
error={
|
||||||
|
touched.messageBus && Boolean(errors.messageBus)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.messageBus && errors.messageBus
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
|
||||||
|
{/* Saturday and Sunday date */}
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<div>
|
||||||
|
<label style={{ marginRight: '10px' }}>
|
||||||
|
<Field type="checkbox" name="checkboxSundayValue" />
|
||||||
|
Sábado
|
||||||
|
</label>
|
||||||
|
|
||||||
|
<label>
|
||||||
|
<Field type="checkbox" name="checkboxSaturdayValue" />
|
||||||
|
Domingo
|
||||||
|
</label>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="enableWeekendMessage"
|
||||||
|
checked={values.enableWeekendMessage}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem para final de semana'}
|
||||||
|
type="weekendMessage"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="weekendMessage"
|
||||||
|
error={
|
||||||
|
touched.weekendMessage && Boolean(errors.weekendMessage)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.weekendMessage && errors.weekendMessage
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<br />
|
||||||
|
|
||||||
|
{/* Holiday date */}
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
|
||||||
|
<Field
|
||||||
|
component={DatePicker}
|
||||||
|
name="holidayDate"
|
||||||
|
label="Data do feriado"
|
||||||
|
format="dd/MM/yyyy"
|
||||||
|
fullWidth
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="holidayDateEnable"
|
||||||
|
checked={values.holidayDateEnable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem para feriado'}
|
||||||
|
type="holidayDateMessage"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="holidayDateMessage"
|
||||||
|
error={
|
||||||
|
touched.holidayDateMessage && Boolean(errors.holidayDateMessage)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.holidayDateMessage && errors.holidayDateMessage
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
<br />
|
||||||
|
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
|
||||||
|
<Field
|
||||||
|
component={TimePicker}
|
||||||
|
name="ticketTimeExpiration"
|
||||||
|
label="Ticket expira em hh:mm"
|
||||||
|
ampm={false}
|
||||||
|
openTo="hours"
|
||||||
|
views={['hours', 'minutes',]}
|
||||||
|
format="HH:mm"
|
||||||
|
/>
|
||||||
|
|
||||||
|
<FormControlLabel
|
||||||
|
control={
|
||||||
|
<Field
|
||||||
|
as={Switch}
|
||||||
|
color="primary"
|
||||||
|
name="ticketExpirationEnable"
|
||||||
|
checked={values.ticketExpirationEnable}
|
||||||
|
/>
|
||||||
|
}
|
||||||
|
label={'Ativar/Desativar'}
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<Field
|
||||||
|
as={TextField}
|
||||||
|
label={'Mensagem por falta de atividade no atendimento'}
|
||||||
|
type="ticketExpirationMsg"
|
||||||
|
multiline
|
||||||
|
rows={5}
|
||||||
|
fullWidth
|
||||||
|
name="ticketExpirationMsg"
|
||||||
|
error={
|
||||||
|
touched.ticketExpirationMsg && Boolean(errors.ticketExpirationMsg)
|
||||||
|
}
|
||||||
|
helperText={
|
||||||
|
touched.ticketExpirationMsg && errors.ticketExpirationMsg
|
||||||
|
}
|
||||||
|
variant="outlined"
|
||||||
|
margin="dense"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={handleClose}
|
||||||
|
color="secondary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
{i18n.t('whatsappModal.buttons.cancel')}
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
disabled={isSubmitting}
|
||||||
|
variant="contained"
|
||||||
|
className={classes.btnWrapper}
|
||||||
|
>
|
||||||
|
{isSubmitting ? (
|
||||||
|
<CircularProgress
|
||||||
|
size={24}
|
||||||
|
className={classes.buttonProgress}
|
||||||
|
/>
|
||||||
|
) : 'Salvar'}
|
||||||
|
</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Form>
|
||||||
|
</MuiPickersUtilsProvider>
|
||||||
|
)}
|
||||||
|
</Formik>
|
||||||
|
</Dialog>
|
||||||
|
</div>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
export default React.memo(ConfigModal)
|
|
@ -1,8 +1,8 @@
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify";
|
||||||
import { i18n } from "../translate/i18n";
|
import { i18n } from "../translate/i18n";
|
||||||
|
|
||||||
const toastError = err => {
|
const toastError = err => {
|
||||||
const errorMsg = err.response?.data?.message || err.response.data.error;
|
const errorMsg = err.response?.data?.message || err?.response?.data?.error || `${err?.message}`;
|
||||||
if (errorMsg) {
|
if (errorMsg) {
|
||||||
if (i18n.exists(`backendErrors.${errorMsg}`)) {
|
if (i18n.exists(`backendErrors.${errorMsg}`)) {
|
||||||
toast.error(i18n.t(`backendErrors.${errorMsg}`), {
|
toast.error(i18n.t(`backendErrors.${errorMsg}`), {
|
||||||
|
|
|
@ -76,7 +76,7 @@ const useAuth = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSetting(data)
|
setSetting(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,41 +1,46 @@
|
||||||
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 DeviceHubOutlined from "@material-ui/icons/DeviceHubOutlined"
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItemText from "@material-ui/core/ListItemText"
|
||||||
import ListItemIcon from "@material-ui/core/ListItemIcon";
|
import ListSubheader from "@material-ui/core/ListSubheader"
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import Divider from "@material-ui/core/Divider"
|
||||||
import ListSubheader from "@material-ui/core/ListSubheader";
|
import { Badge } from "@material-ui/core"
|
||||||
import Divider from "@material-ui/core/Divider";
|
import DashboardOutlinedIcon from "@material-ui/icons/DashboardOutlined"
|
||||||
import { Badge } from "@material-ui/core";
|
import ListItem from '@material-ui/core/ListItem'
|
||||||
import DashboardOutlinedIcon from "@material-ui/icons/DashboardOutlined";
|
import ListItemIcon from '@material-ui/core/ListItemIcon'
|
||||||
|
|
||||||
import ReportOutlinedIcon from "@material-ui/icons/ReportOutlined";
|
import ReportOutlinedIcon from '@material-ui/icons/ReportOutlined'
|
||||||
import SendOutlined from "@material-ui/icons/SendOutlined";
|
import CampaignIcon from '@material-ui/icons/Send'
|
||||||
|
|
||||||
|
|
||||||
|
import SendOutlined from '@material-ui/icons/SendOutlined'
|
||||||
|
|
||||||
//import ReportOutlined from "@bit/mui-org.material-ui-icons.report-outlined";
|
//import ReportOutlined from "@bit/mui-org.material-ui-icons.report-outlined";
|
||||||
|
|
||||||
|
import WhatsAppIcon from '@material-ui/icons/WhatsApp'
|
||||||
|
import SyncAltIcon from '@material-ui/icons/SyncAlt'
|
||||||
|
import SettingsOutlinedIcon from '@material-ui/icons/SettingsOutlined'
|
||||||
|
import PeopleAltOutlinedIcon from '@material-ui/icons/PeopleAltOutlined'
|
||||||
|
import ContactPhoneOutlinedIcon from '@material-ui/icons/ContactPhoneOutlined'
|
||||||
|
import AccountTreeOutlinedIcon from '@material-ui/icons/AccountTreeOutlined'
|
||||||
|
import QuestionAnswerOutlinedIcon from '@material-ui/icons/QuestionAnswerOutlined'
|
||||||
|
|
||||||
import WhatsAppIcon from "@material-ui/icons/WhatsApp";
|
import { i18n } from '../translate/i18n'
|
||||||
import SyncAltIcon from "@material-ui/icons/SyncAlt";
|
import { WhatsAppsContext } from '../context/WhatsApp/WhatsAppsContext'
|
||||||
import SettingsOutlinedIcon from "@material-ui/icons/SettingsOutlined";
|
import { AuthContext } from '../context/Auth/AuthContext'
|
||||||
import PeopleAltOutlinedIcon from "@material-ui/icons/PeopleAltOutlined";
|
import { Can } from '../components/Can'
|
||||||
import ContactPhoneOutlinedIcon from "@material-ui/icons/ContactPhoneOutlined";
|
|
||||||
import AccountTreeOutlinedIcon from "@material-ui/icons/AccountTreeOutlined";
|
|
||||||
import QuestionAnswerOutlinedIcon from "@material-ui/icons/QuestionAnswerOutlined";
|
|
||||||
|
|
||||||
import { i18n } from "../translate/i18n";
|
|
||||||
import { WhatsAppsContext } from "../context/WhatsApp/WhatsAppsContext";
|
|
||||||
import { AuthContext } from "../context/Auth/AuthContext";
|
|
||||||
import { Can } from "../components/Can";
|
|
||||||
|
|
||||||
function ListItemLink(props) {
|
function ListItemLink(props) {
|
||||||
const { icon, primary, to, className } = props;
|
const { icon, primary, to, className } = props
|
||||||
|
|
||||||
const renderLink = React.useMemo(
|
const renderLink = React.useMemo(
|
||||||
() => React.forwardRef((itemProps, ref) => <RouterLink to={to} ref={ref} {...itemProps} />),
|
() =>
|
||||||
|
React.forwardRef((itemProps, ref) => (
|
||||||
|
<RouterLink to={to} ref={ref} {...itemProps} />
|
||||||
|
)),
|
||||||
[to]
|
[to]
|
||||||
);
|
)
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<li>
|
<li>
|
||||||
|
@ -44,60 +49,59 @@ function ListItemLink(props) {
|
||||||
<ListItemText primary={primary} />
|
<ListItemText primary={primary} />
|
||||||
</ListItem>
|
</ListItem>
|
||||||
</li>
|
</li>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
const MainListItems = (props) => {
|
const MainListItems = (props) => {
|
||||||
const { setDrawerOpen } = props;
|
const { setDrawerOpen } = props
|
||||||
const { whatsApps } = useContext(WhatsAppsContext);
|
const { whatsApps } = useContext(WhatsAppsContext)
|
||||||
const { user } = useContext(AuthContext);
|
const { user } = useContext(AuthContext)
|
||||||
const [connectionWarning, setConnectionWarning] = useState(false);
|
const [connectionWarning, setConnectionWarning] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const delayDebounceFn = setTimeout(() => {
|
const delayDebounceFn = setTimeout(() => {
|
||||||
if (whatsApps.length > 0) {
|
if (whatsApps.length > 0) {
|
||||||
const offlineWhats = whatsApps.filter((whats) => {
|
const offlineWhats = whatsApps.filter((whats) => {
|
||||||
return (
|
return (
|
||||||
whats.status === "qrcode" ||
|
whats.status === 'qrcode' ||
|
||||||
whats.status === "PAIRING" ||
|
whats.status === 'PAIRING' ||
|
||||||
whats.status === "DISCONNECTED" ||
|
whats.status === 'DISCONNECTED' ||
|
||||||
whats.status === "TIMEOUT" ||
|
whats.status === 'TIMEOUT' ||
|
||||||
whats.status === "OPENING"
|
whats.status === 'OPENING'
|
||||||
);
|
)
|
||||||
});
|
})
|
||||||
if (offlineWhats.length > 0) {
|
if (offlineWhats.length > 0) {
|
||||||
setConnectionWarning(true);
|
setConnectionWarning(true)
|
||||||
} else {
|
} else {
|
||||||
setConnectionWarning(false);
|
setConnectionWarning(false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, 2000);
|
}, 2000)
|
||||||
return () => clearTimeout(delayDebounceFn);
|
return () => clearTimeout(delayDebounceFn)
|
||||||
}, [whatsApps]);
|
}, [whatsApps])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
//Solicitado pelo Adriano: Click no LinkItem e fechar o menu!
|
//Solicitado pelo Adriano: Click no LinkItem e fechar o menu!
|
||||||
<div onClick={() => setDrawerOpen(false)}>
|
<div onClick={() => setDrawerOpen(false)}>
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/tickets"
|
to="/tickets"
|
||||||
primary={i18n.t("mainDrawer.listItems.tickets")}
|
primary={i18n.t('mainDrawer.listItems.tickets')}
|
||||||
icon={<WhatsAppIcon />}
|
icon={<WhatsAppIcon />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/contacts"
|
to="/contacts"
|
||||||
primary={i18n.t("mainDrawer.listItems.contacts")}
|
primary={i18n.t('mainDrawer.listItems.contacts')}
|
||||||
icon={<ContactPhoneOutlinedIcon />}
|
icon={<ContactPhoneOutlinedIcon />}
|
||||||
/>
|
/>
|
||||||
|
<ListItemLink
|
||||||
<ListItemLink to="/schedulesReminder"
|
to="/schedulesReminder"
|
||||||
primary={i18n.t("mainDrawer.listItems.reminders")}
|
primary={i18n.t('mainDrawer.listItems.schedules')}
|
||||||
icon={<SendOutlined />}
|
icon={<SendOutlined />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/quickAnswers"
|
to="/quickAnswers"
|
||||||
primary={i18n.t("mainDrawer.listItems.quickAnswers")}
|
primary={i18n.t('mainDrawer.listItems.quickAnswers')}
|
||||||
icon={<QuestionAnswerOutlinedIcon />}
|
icon={<QuestionAnswerOutlinedIcon />}
|
||||||
/>
|
/>
|
||||||
<Can
|
<Can
|
||||||
|
@ -106,31 +110,47 @@ const MainListItems = (props) => {
|
||||||
yes={() => (
|
yes={() => (
|
||||||
<>
|
<>
|
||||||
<Divider />
|
<Divider />
|
||||||
<ListSubheader inset>{i18n.t("mainDrawer.listItems.administration")}</ListSubheader>
|
<ListSubheader inset>
|
||||||
|
{i18n.t('mainDrawer.listItems.administration')}
|
||||||
|
</ListSubheader>
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/users"
|
to="/users"
|
||||||
primary={i18n.t("mainDrawer.listItems.users")}
|
primary={i18n.t('mainDrawer.listItems.users')}
|
||||||
icon={<PeopleAltOutlinedIcon />}
|
icon={<PeopleAltOutlinedIcon />}
|
||||||
/>
|
/>
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/queues"
|
to="/queues"
|
||||||
primary={i18n.t("mainDrawer.listItems.queues")}
|
primary={i18n.t('mainDrawer.listItems.queues')}
|
||||||
icon={<AccountTreeOutlinedIcon />}
|
icon={<AccountTreeOutlinedIcon />}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItemLink
|
<ListItemLink
|
||||||
to="/connections"
|
to="/connections"
|
||||||
primary={i18n.t("mainDrawer.listItems.connections")}
|
primary={i18n.t('mainDrawer.listItems.connections')}
|
||||||
icon={
|
icon={
|
||||||
<Badge badgeContent={connectionWarning ? "!" : 0} color="error">
|
<Badge badgeContent={connectionWarning ? '!' : 0} color="error">
|
||||||
<SyncAltIcon />
|
<SyncAltIcon />
|
||||||
</Badge>
|
</Badge>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<ListItemLink to="/" primary="Dashboard" icon={<DashboardOutlinedIcon />} />
|
<ListItemLink
|
||||||
|
to="/"
|
||||||
|
primary="Dashboard"
|
||||||
|
icon={<DashboardOutlinedIcon />}
|
||||||
|
/>
|
||||||
|
|
||||||
<ListItemLink to="/report" primary="Relatório" icon={<ReportOutlinedIcon />} />
|
<ListItemLink
|
||||||
|
to="/report"
|
||||||
|
primary="Relatório"
|
||||||
|
icon={<ReportOutlinedIcon />}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<ListItemLink
|
||||||
|
to="/campaign"
|
||||||
|
primary="Campanha"
|
||||||
|
icon={<CampaignIcon />}
|
||||||
|
/>
|
||||||
|
|
||||||
<Can
|
<Can
|
||||||
role={user.profile}
|
role={user.profile}
|
||||||
|
@ -148,13 +168,14 @@ const MainListItems = (props) => {
|
||||||
icon={<DeviceHubOutlined />}
|
icon={<DeviceHubOutlined />}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
|
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default MainListItems;
|
export default MainListItems
|
||||||
|
|
|
@ -0,0 +1,490 @@
|
||||||
|
import React, { useState, useCallback, useEffect, useReducer, useContext } from 'react'
|
||||||
|
import { toast } from 'react-toastify'
|
||||||
|
|
||||||
|
import openSocket from 'socket.io-client'
|
||||||
|
|
||||||
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
|
import { green } from '@material-ui/core/colors'
|
||||||
|
import {
|
||||||
|
Button,
|
||||||
|
TableBody,
|
||||||
|
TableRow,
|
||||||
|
TableCell,
|
||||||
|
IconButton,
|
||||||
|
Table,
|
||||||
|
TableHead,
|
||||||
|
Paper,
|
||||||
|
} from '@material-ui/core'
|
||||||
|
import {
|
||||||
|
Edit,
|
||||||
|
DeleteOutline,
|
||||||
|
// Restore
|
||||||
|
} from '@material-ui/icons'
|
||||||
|
|
||||||
|
import MainContainer from '../../components/MainContainer'
|
||||||
|
import MainHeader from '../../components/MainHeader'
|
||||||
|
import MainHeaderButtonsWrapper from '../../components/MainHeaderButtonsWrapper'
|
||||||
|
import Title from '../../components/Title'
|
||||||
|
import TableRowSkeleton from '../../components/TableRowSkeleton'
|
||||||
|
|
||||||
|
import CampaignModal from '../../components/CampaignModal'
|
||||||
|
import ConfirmationModal from '../../components/ConfirmationModal'
|
||||||
|
import QrcodeModal from '../../components/QrcodeModal'
|
||||||
|
import { i18n } from '../../translate/i18n'
|
||||||
|
// import { WhatsAppsContext } from '../../context/WhatsApp/WhatsAppsContext'
|
||||||
|
import toastError from '../../errors/toastError'
|
||||||
|
|
||||||
|
//--------
|
||||||
|
import { AuthContext } from '../../context/Auth/AuthContext'
|
||||||
|
import { Can } from '../../components/Can'
|
||||||
|
|
||||||
|
import apiBroker from '../../services/apiBroker'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const reducer = (state, action) => {
|
||||||
|
|
||||||
|
|
||||||
|
if (action.type === "LOAD_CAMPAIGNS") {
|
||||||
|
|
||||||
|
const campaigns = action.payload
|
||||||
|
return [...state, ...campaigns]
|
||||||
|
}
|
||||||
|
if (action.type === "UPDATE_CAMPAIGNS") {
|
||||||
|
|
||||||
|
const campaign = action.payload
|
||||||
|
const campaignIndex = state.findIndex((c) => c.id === campaign.id)
|
||||||
|
|
||||||
|
if (campaignIndex !== -1) {
|
||||||
|
|
||||||
|
state[campaignIndex] = { ...state[campaignIndex], ...campaign }
|
||||||
|
|
||||||
|
return [...state]
|
||||||
|
|
||||||
|
// state[campaignIndex] = campaign
|
||||||
|
|
||||||
|
// return [...state]
|
||||||
|
} else {
|
||||||
|
return [campaign, ...state]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (action.type === "DELETE_CAMPAIGN") {
|
||||||
|
const campaignId = action.payload
|
||||||
|
|
||||||
|
const campaignIndex = state.findIndex((c) => c.id === campaignId)
|
||||||
|
if (campaignIndex !== -1) {
|
||||||
|
state.splice(campaignIndex, 1)
|
||||||
|
}
|
||||||
|
return [...state]
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action.type === "RESET") {
|
||||||
|
return []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
mainPaper: {
|
||||||
|
flex: 1,
|
||||||
|
padding: theme.spacing(1),
|
||||||
|
overflowY: 'scroll',
|
||||||
|
...theme.scrollbarStyles,
|
||||||
|
},
|
||||||
|
customTableCell: {
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
justifyContent: 'center',
|
||||||
|
},
|
||||||
|
tooltip: {
|
||||||
|
backgroundColor: '#f5f5f9',
|
||||||
|
color: 'rgba(0, 0, 0, 0.87)',
|
||||||
|
fontSize: theme.typography.pxToRem(14),
|
||||||
|
border: '1px solid #dadde9',
|
||||||
|
maxWidth: 450,
|
||||||
|
},
|
||||||
|
tooltipPopper: {
|
||||||
|
textAlign: 'center',
|
||||||
|
},
|
||||||
|
buttonProgress: {
|
||||||
|
color: green[500],
|
||||||
|
},
|
||||||
|
}))
|
||||||
|
|
||||||
|
const Campaign = () => {
|
||||||
|
//--------
|
||||||
|
const { user } = useContext(AuthContext)
|
||||||
|
|
||||||
|
const classes = useStyles()
|
||||||
|
|
||||||
|
// const { whatsApps } = useContext(WhatsAppsContext)
|
||||||
|
|
||||||
|
// const { campaigns, loading } = useContext(WhatsAppsContext)
|
||||||
|
const [campaignModalOpen, setCampaignModalOpen] = useState(false)
|
||||||
|
const [qrModalOpen, setQrModalOpen] = useState(false)
|
||||||
|
const [selectedCampaign, setSelectedCampaign] = useState(null)
|
||||||
|
const [confirmModalOpen, setConfirmModalOpen] = useState(false)
|
||||||
|
|
||||||
|
const [campaigns, dispatch] = useReducer(reducer, [])
|
||||||
|
|
||||||
|
const [loading, setLoading] = useState(true)
|
||||||
|
|
||||||
|
const confirmationModalInitialState = {
|
||||||
|
action: '',
|
||||||
|
title: '',
|
||||||
|
message: '',
|
||||||
|
campaignId: '',
|
||||||
|
csv_original_file_name: '',
|
||||||
|
open: false,
|
||||||
|
}
|
||||||
|
const [confirmModalInfo, setConfirmModalInfo] = useState(
|
||||||
|
confirmationModalInitialState
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
dispatch({ type: "RESET" })
|
||||||
|
}, [])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
|
const delayDebounceFn = setTimeout(() => {
|
||||||
|
const fetchContacts = async () => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
const { data } = await apiBroker.get('/campaign', {
|
||||||
|
params: {
|
||||||
|
adminId: user.id,
|
||||||
|
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
||||||
|
identifier: 'campaign'
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch({ type: "LOAD_CAMPAIGNS", payload: data.campaign })
|
||||||
|
setLoading(false)
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
fetchContacts()
|
||||||
|
}, 500)
|
||||||
|
return () => clearTimeout(delayDebounceFn)
|
||||||
|
}, [user.id])
|
||||||
|
|
||||||
|
|
||||||
|
const handleOpenCampaignModal = () => {
|
||||||
|
setSelectedCampaign(null)
|
||||||
|
setCampaignModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseCampaignModal = useCallback(() => {
|
||||||
|
setCampaignModalOpen(false)
|
||||||
|
setSelectedCampaign(null)
|
||||||
|
}, [setSelectedCampaign, setCampaignModalOpen])
|
||||||
|
|
||||||
|
const handleStart = async (campaign) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await apiBroker.post(`/campaign/start/${campaign.id}`)
|
||||||
|
|
||||||
|
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
|
||||||
|
|
||||||
|
toast.success('Campanha iniciada com sucesso')
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStop = async (campaign) => {
|
||||||
|
|
||||||
|
try {
|
||||||
|
const { data } = await apiBroker.post(`/campaign/stop/${campaign.id}`)
|
||||||
|
|
||||||
|
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
|
||||||
|
|
||||||
|
toast.success('Campanha parada com sucesso')
|
||||||
|
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseQrModal = useCallback(() => {
|
||||||
|
setSelectedCampaign(null)
|
||||||
|
setQrModalOpen(false)
|
||||||
|
}, [setQrModalOpen, setSelectedCampaign])
|
||||||
|
|
||||||
|
const handleEditCampaign = (campaign) => {
|
||||||
|
setSelectedCampaign(campaign)
|
||||||
|
setCampaignModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleOpenConfirmationModal = (action, campaignId) => {
|
||||||
|
if (action === 'disconnect') {
|
||||||
|
setConfirmModalInfo({
|
||||||
|
action: action,
|
||||||
|
title: i18n.t('connections.confirmationModal.disconnectTitle'),
|
||||||
|
message: i18n.t('connections.confirmationModal.disconnectMessage'),
|
||||||
|
campaignId: campaignId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (action === 'delete') {
|
||||||
|
setConfirmModalInfo({
|
||||||
|
action: action,
|
||||||
|
title: i18n.t('connections.confirmationModal.deleteTitle'),
|
||||||
|
message: i18n.t('connections.confirmationModal.deleteMessage'),
|
||||||
|
campaignId: campaignId,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
setConfirmModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSubmitConfirmationModal = async () => {
|
||||||
|
|
||||||
|
if (confirmModalInfo.action === 'delete') {
|
||||||
|
try {
|
||||||
|
await apiBroker.delete(`/campaign/${confirmModalInfo.campaignId}`, {
|
||||||
|
params: {
|
||||||
|
adminId: user.id,
|
||||||
|
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
||||||
|
identifier: 'campaign',
|
||||||
|
},
|
||||||
|
})
|
||||||
|
|
||||||
|
dispatch({ type: "DELETE_CAMPAIGN", payload: confirmModalInfo.campaignId })
|
||||||
|
|
||||||
|
toast.success('Campanha deletada com sucesso')
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setConfirmModalInfo(confirmationModalInitialState)
|
||||||
|
}
|
||||||
|
|
||||||
|
const renderActionButtons = (campaign) => {
|
||||||
|
return (
|
||||||
|
<>
|
||||||
|
{campaign.status === 'stopped' && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => handleStart(campaign)}
|
||||||
|
>
|
||||||
|
Start
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
{campaign.status === 'running' && (
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={() => handleStop(campaign)}
|
||||||
|
>
|
||||||
|
Stop
|
||||||
|
</Button>
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
|
||||||
|
|
||||||
|
|
||||||
|
socket.on("contactsBulkInsertOnQueueStatus", (data) => {
|
||||||
|
if (data.action === 'update') {
|
||||||
|
|
||||||
|
if (String(data.insertOnQueue.adminId) === String(user.id)) {
|
||||||
|
|
||||||
|
if (data?.insertOnQueue?.campaign?.status === "stopped") {
|
||||||
|
|
||||||
|
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.insertOnQueue.campaign })
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
socket.on('campaign', (data) => {
|
||||||
|
|
||||||
|
if (data.action === 'update') {
|
||||||
|
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
return () => {
|
||||||
|
socket.disconnect()
|
||||||
|
}
|
||||||
|
}, [user.id])
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Can
|
||||||
|
role={user.profile}
|
||||||
|
perform="connections-view:show"
|
||||||
|
yes={() => (
|
||||||
|
<MainContainer>
|
||||||
|
<ConfirmationModal
|
||||||
|
title={confirmModalInfo.title}
|
||||||
|
open={confirmModalOpen}
|
||||||
|
onClose={setConfirmModalOpen}
|
||||||
|
onConfirm={handleSubmitConfirmationModal}
|
||||||
|
>
|
||||||
|
{confirmModalInfo.message}
|
||||||
|
</ConfirmationModal>
|
||||||
|
|
||||||
|
<QrcodeModal
|
||||||
|
open={qrModalOpen}
|
||||||
|
onClose={handleCloseQrModal}
|
||||||
|
campaignId={!campaignModalOpen && selectedCampaign?.id}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<CampaignModal
|
||||||
|
open={campaignModalOpen}
|
||||||
|
onClose={handleCloseCampaignModal}
|
||||||
|
campaignId={selectedCampaign?.id}
|
||||||
|
dispatch={dispatch}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<MainHeader>
|
||||||
|
<Title>Campanhas</Title>
|
||||||
|
|
||||||
|
<MainHeaderButtonsWrapper>
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleOpenCampaignModal}
|
||||||
|
>
|
||||||
|
criar campanha
|
||||||
|
</Button>
|
||||||
|
</MainHeaderButtonsWrapper>
|
||||||
|
</MainHeader>
|
||||||
|
|
||||||
|
<Paper className={classes.mainPaper} variant="outlined">
|
||||||
|
<>
|
||||||
|
<Table size="small">
|
||||||
|
<TableHead>
|
||||||
|
<TableRow>
|
||||||
|
<TableCell align="center">
|
||||||
|
Name
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
Status
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
Sent
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
Read
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
Start/stop
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
Sender
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
Message
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{i18n.t('connections.table.actions')}
|
||||||
|
</TableCell>
|
||||||
|
</TableRow>
|
||||||
|
</TableHead>
|
||||||
|
<TableBody>
|
||||||
|
{loading ? (
|
||||||
|
<TableRowSkeleton />
|
||||||
|
) : (
|
||||||
|
<>
|
||||||
|
{campaigns?.length > 0 &&
|
||||||
|
campaigns.map((campaign) => (
|
||||||
|
<TableRow key={campaign.id}>
|
||||||
|
<TableCell align="center">
|
||||||
|
{campaign.name}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{campaign.status}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{campaign.status === 'stopped' || campaign.status === 'running' || campaign.status === 'success' ? `${campaign.sent}/${campaign.all}` : '0/0'}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{campaign.status === 'stopped' || campaign.status === 'running' || campaign.status === 'success' ? `${campaign.read}` : '0'}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{renderActionButtons(campaign)}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{campaign.whatsapp_sender}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
<TableCell align="center">
|
||||||
|
{campaign.message}
|
||||||
|
</TableCell>
|
||||||
|
|
||||||
|
{campaign.status !== 'running' ? <TableCell align="center">
|
||||||
|
|
||||||
|
{campaign.status !== 'success' && (
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={() =>
|
||||||
|
handleEditCampaign(campaign)
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<Edit />
|
||||||
|
</IconButton>)}
|
||||||
|
|
||||||
|
|
||||||
|
<IconButton
|
||||||
|
size="small"
|
||||||
|
onClick={(e) => {
|
||||||
|
handleOpenConfirmationModal(
|
||||||
|
'delete',
|
||||||
|
campaign.id
|
||||||
|
)
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteOutline />
|
||||||
|
</IconButton>
|
||||||
|
</TableCell> : <TableCell align="center"></TableCell>}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</TableRow>
|
||||||
|
))}
|
||||||
|
</>
|
||||||
|
)}
|
||||||
|
</TableBody>
|
||||||
|
</Table>
|
||||||
|
</>
|
||||||
|
</Paper>
|
||||||
|
</MainContainer>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default Campaign
|
|
@ -6,6 +6,9 @@ import openSocket from 'socket.io-client'
|
||||||
|
|
||||||
import { makeStyles } from '@material-ui/core/styles'
|
import { makeStyles } from '@material-ui/core/styles'
|
||||||
import { green } from '@material-ui/core/colors'
|
import { green } from '@material-ui/core/colors'
|
||||||
|
|
||||||
|
import Settings from "@material-ui/icons/Settings";
|
||||||
|
|
||||||
import {
|
import {
|
||||||
Button,
|
Button,
|
||||||
TableBody,
|
TableBody,
|
||||||
|
@ -47,6 +50,7 @@ import toastError from '../../errors/toastError'
|
||||||
//--------
|
//--------
|
||||||
import { AuthContext } from '../../context/Auth/AuthContext'
|
import { AuthContext } from '../../context/Auth/AuthContext'
|
||||||
import { Can } from '../../components/Can'
|
import { Can } from '../../components/Can'
|
||||||
|
import ConfigModal from '../../components/ConfigModal'
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
mainPaper: {
|
mainPaper: {
|
||||||
|
@ -107,6 +111,7 @@ const Connections = () => {
|
||||||
|
|
||||||
const { whatsApps, loading } = useContext(WhatsAppsContext)
|
const { whatsApps, loading } = useContext(WhatsAppsContext)
|
||||||
const [whatsAppModalOpen, setWhatsAppModalOpen] = useState(false)
|
const [whatsAppModalOpen, setWhatsAppModalOpen] = useState(false)
|
||||||
|
const [configModalOpen, setConfigModalOpen] = useState(false)
|
||||||
const [qrModalOpen, setQrModalOpen] = useState(false)
|
const [qrModalOpen, setQrModalOpen] = useState(false)
|
||||||
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null)
|
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null)
|
||||||
const [confirmModalOpen, setConfirmModalOpen] = useState(false)
|
const [confirmModalOpen, setConfirmModalOpen] = useState(false)
|
||||||
|
@ -134,7 +139,7 @@ const Connections = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSettings(data)
|
setSettings(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
@ -205,6 +210,13 @@ const Connections = () => {
|
||||||
setWhatsAppModalOpen(true)
|
setWhatsAppModalOpen(true)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleOpenConfigModal = () => {
|
||||||
|
setConfigModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseConfigModal = () => {
|
||||||
|
setConfigModalOpen(false)
|
||||||
|
}
|
||||||
const handleCloseWhatsAppModal = useCallback(() => {
|
const handleCloseWhatsAppModal = useCallback(() => {
|
||||||
setWhatsAppModalOpen(false)
|
setWhatsAppModalOpen(false)
|
||||||
setSelectedWhatsApp(null)
|
setSelectedWhatsApp(null)
|
||||||
|
@ -307,17 +319,17 @@ const Connections = () => {
|
||||||
{(whatsApp.status === 'CONNECTED' ||
|
{(whatsApp.status === 'CONNECTED' ||
|
||||||
whatsApp.status === 'PAIRING' ||
|
whatsApp.status === 'PAIRING' ||
|
||||||
whatsApp.status === 'TIMEOUT') && (
|
whatsApp.status === 'TIMEOUT') && (
|
||||||
<Button
|
<Button
|
||||||
size="small"
|
size="small"
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
color="secondary"
|
color="secondary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
handleOpenConfirmationModal('disconnect', whatsApp.id)
|
handleOpenConfirmationModal('disconnect', whatsApp.id)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
{i18n.t('connections.buttons.disconnect')}
|
{i18n.t('connections.buttons.disconnect')}
|
||||||
</Button>
|
</Button>
|
||||||
)}
|
)}
|
||||||
{whatsApp.status === 'OPENING' && (
|
{whatsApp.status === 'OPENING' && (
|
||||||
<Button size="small" variant="outlined" disabled color="default">
|
<Button size="small" variant="outlined" disabled color="default">
|
||||||
{i18n.t('connections.buttons.connecting')}
|
{i18n.t('connections.buttons.connecting')}
|
||||||
|
@ -454,10 +466,24 @@ const Connections = () => {
|
||||||
whatsAppId={!qrModalOpen && selectedWhatsApp?.id}
|
whatsAppId={!qrModalOpen && selectedWhatsApp?.id}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
|
<ConfigModal
|
||||||
|
open={configModalOpen}
|
||||||
|
onClose={handleCloseConfigModal}
|
||||||
|
change={configModalOpen}
|
||||||
|
/>
|
||||||
|
|
||||||
<MainHeader>
|
<MainHeader>
|
||||||
<Title>{i18n.t('connections.title')}</Title>
|
<Title>{i18n.t('connections.title')}</Title>
|
||||||
|
|
||||||
<MainHeaderButtonsWrapper>
|
<MainHeaderButtonsWrapper>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
variant="contained"
|
||||||
|
color="primary"
|
||||||
|
onClick={handleOpenConfigModal}
|
||||||
|
>
|
||||||
|
<Settings/>
|
||||||
|
</Button>
|
||||||
<Can
|
<Can
|
||||||
role={user.profile}
|
role={user.profile}
|
||||||
perform="btn-add-whatsapp"
|
perform="btn-add-whatsapp"
|
||||||
|
@ -664,8 +690,8 @@ const Connections = () => {
|
||||||
settings.length > 0 &&
|
settings.length > 0 &&
|
||||||
getSettingValue('editURA') &&
|
getSettingValue('editURA') &&
|
||||||
getSettingValue('editURA') ===
|
getSettingValue('editURA') ===
|
||||||
'enabled') |
|
'enabled') |
|
||||||
(user.profile === 'master') ? (
|
(user.profile === 'master') ? (
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() =>
|
onClick={() =>
|
||||||
|
|
|
@ -1,43 +1,43 @@
|
||||||
import React, { useState, useEffect, useReducer, useContext } from "react";
|
import React, { useState, useEffect, useReducer, useContext } from "react"
|
||||||
import openSocket from "socket.io-client";
|
import openSocket from "socket.io-client"
|
||||||
import { toast } from "react-toastify";
|
import { toast } from "react-toastify"
|
||||||
import { useHistory } from "react-router-dom";
|
import { useHistory } from "react-router-dom"
|
||||||
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles"
|
||||||
import Table from "@material-ui/core/Table";
|
import Table from "@material-ui/core/Table"
|
||||||
import TableBody from "@material-ui/core/TableBody";
|
import TableBody from "@material-ui/core/TableBody"
|
||||||
import TableCell from "@material-ui/core/TableCell";
|
import TableCell from "@material-ui/core/TableCell"
|
||||||
import TableHead from "@material-ui/core/TableHead";
|
import TableHead from "@material-ui/core/TableHead"
|
||||||
import TableRow from "@material-ui/core/TableRow";
|
import TableRow from "@material-ui/core/TableRow"
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper"
|
||||||
import Button from "@material-ui/core/Button";
|
import Button from "@material-ui/core/Button"
|
||||||
import Avatar from "@material-ui/core/Avatar";
|
import Avatar from "@material-ui/core/Avatar"
|
||||||
import WhatsAppIcon from "@material-ui/icons/WhatsApp";
|
import WhatsAppIcon from "@material-ui/icons/WhatsApp"
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
import SearchIcon from "@material-ui/icons/Search"
|
||||||
import TextField from "@material-ui/core/TextField";
|
import TextField from "@material-ui/core/TextField"
|
||||||
import InputAdornment from "@material-ui/core/InputAdornment";
|
import InputAdornment from "@material-ui/core/InputAdornment"
|
||||||
|
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton"
|
||||||
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline";
|
import DeleteOutlineIcon from "@material-ui/icons/DeleteOutline"
|
||||||
import EditIcon from "@material-ui/icons/Edit";
|
import EditIcon from "@material-ui/icons/Edit"
|
||||||
|
|
||||||
import api from "../../services/api";
|
import api from "../../services/api"
|
||||||
import TableRowSkeleton from "../../components/TableRowSkeleton";
|
import TableRowSkeleton from "../../components/TableRowSkeleton"
|
||||||
import ContactModal from "../../components/ContactModal";
|
import ContactModal from "../../components/ContactModal"
|
||||||
import ConfirmationModal from "../../components/ConfirmationModal/";
|
import ConfirmationModal from "../../components/ConfirmationModal/"
|
||||||
|
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n"
|
||||||
import MainHeader from "../../components/MainHeader";
|
import MainHeader from "../../components/MainHeader"
|
||||||
import Title from "../../components/Title";
|
import Title from "../../components/Title"
|
||||||
import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper";
|
import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper"
|
||||||
import MainContainer from "../../components/MainContainer";
|
import MainContainer from "../../components/MainContainer"
|
||||||
import toastError from "../../errors/toastError";
|
import toastError from "../../errors/toastError"
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
import { Can } from "../../components/Can";
|
import { Can } from "../../components/Can"
|
||||||
|
|
||||||
import apiBroker from "../../services/apiBroker";
|
import apiBroker from "../../services/apiBroker"
|
||||||
import fileDownload from 'js-file-download'
|
import fileDownload from 'js-file-download'
|
||||||
import ContactCreateTicketModal from "../../components/ContactCreateTicketModal";
|
import ContactCreateTicketModal from "../../components/ContactCreateTicketModal"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -46,50 +46,50 @@ const reducer = (state, action) => {
|
||||||
|
|
||||||
if (action.type === "LOAD_CONTACTS") {
|
if (action.type === "LOAD_CONTACTS") {
|
||||||
|
|
||||||
const contacts = action.payload;
|
const contacts = action.payload
|
||||||
const newContacts = [];
|
const newContacts = []
|
||||||
|
|
||||||
contacts.forEach((contact) => {
|
contacts.forEach((contact) => {
|
||||||
|
|
||||||
const contactIndex = state.findIndex((c) => +c.id === +contact.id);
|
const contactIndex = state.findIndex((c) => +c.id === +contact.id)
|
||||||
|
|
||||||
if (contactIndex !== -1) {
|
if (contactIndex !== -1) {
|
||||||
state[contactIndex] = contact;
|
state[contactIndex] = contact
|
||||||
} else {
|
} else {
|
||||||
newContacts.push(contact);
|
newContacts.push(contact)
|
||||||
}
|
}
|
||||||
|
|
||||||
});
|
})
|
||||||
|
|
||||||
return [...state, ...newContacts];
|
return [...state, ...newContacts]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === "UPDATE_CONTACTS") {
|
if (action.type === "UPDATE_CONTACTS") {
|
||||||
const contact = action.payload;
|
const contact = action.payload
|
||||||
const contactIndex = state.findIndex((c) => +c.id === +contact.id);
|
const contactIndex = state.findIndex((c) => +c.id === +contact.id)
|
||||||
|
|
||||||
if (contactIndex !== -1) {
|
if (contactIndex !== -1) {
|
||||||
state[contactIndex] = contact;
|
state[contactIndex] = contact
|
||||||
return [...state];
|
return [...state]
|
||||||
} else {
|
} else {
|
||||||
return [contact, ...state];
|
return [contact, ...state]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === "DELETE_CONTACT") {
|
if (action.type === "DELETE_CONTACT") {
|
||||||
const contactId = action.payload;
|
const contactId = action.payload
|
||||||
|
|
||||||
const contactIndex = state.findIndex((c) => +c.id === +contactId);
|
const contactIndex = state.findIndex((c) => +c.id === +contactId)
|
||||||
if (contactIndex !== -1) {
|
if (contactIndex !== -1) {
|
||||||
state.splice(contactIndex, 1);
|
state.splice(contactIndex, 1)
|
||||||
}
|
}
|
||||||
return [...state];
|
return [...state]
|
||||||
}
|
}
|
||||||
|
|
||||||
if (action.type === "RESET") {
|
if (action.type === "RESET") {
|
||||||
return [];
|
return []
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
mainPaper: {
|
mainPaper: {
|
||||||
|
@ -98,24 +98,24 @@ const useStyles = makeStyles((theme) => ({
|
||||||
overflowY: "scroll",
|
overflowY: "scroll",
|
||||||
...theme.scrollbarStyles,
|
...theme.scrollbarStyles,
|
||||||
},
|
},
|
||||||
}));
|
}))
|
||||||
|
|
||||||
const Contacts = () => {
|
const Contacts = () => {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
const history = useHistory();
|
const history = useHistory()
|
||||||
|
|
||||||
const { user } = useContext(AuthContext);
|
const { user } = useContext(AuthContext)
|
||||||
|
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false)
|
||||||
const [pageNumber, setPageNumber] = useState(1);
|
const [pageNumber, setPageNumber] = useState(1)
|
||||||
const [searchParam, setSearchParam] = useState("");
|
const [searchParam, setSearchParam] = useState("")
|
||||||
const [contacts, dispatch] = useReducer(reducer, []);
|
const [contacts, dispatch] = useReducer(reducer, [])
|
||||||
const [selectedContactId, setSelectedContactId] = useState(null);
|
const [selectedContactId, setSelectedContactId] = useState(null)
|
||||||
const [contactModalOpen, setContactModalOpen] = useState(false);
|
const [contactModalOpen, setContactModalOpen] = useState(false)
|
||||||
const [isCreateTicketModalOpen, setIsCreateTicketModalOpen] = useState(false)
|
const [isCreateTicketModalOpen, setIsCreateTicketModalOpen] = useState(false)
|
||||||
const [deletingContact, setDeletingContact] = useState(null);
|
const [deletingContact, setDeletingContact] = useState(null)
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
const [confirmOpen, setConfirmOpen] = useState(false)
|
||||||
const [hasMore, setHasMore] = useState(false);
|
const [hasMore, setHasMore] = useState(false)
|
||||||
|
|
||||||
|
|
||||||
const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined)
|
const [onQueueStatus, setOnQueueProcessStatus] = useState(undefined)
|
||||||
|
@ -132,20 +132,20 @@ const Contacts = () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
formData.append("adminId", user.id);
|
formData.append("adminId", user.id)
|
||||||
formData.append("baseURL", process.env.REACT_APP_BACKEND_URL_PRIVATE,);
|
formData.append("baseURL", process.env.REACT_APP_BACKEND_URL_PRIVATE,)
|
||||||
formData.append("frontURL", process.env.REACT_APP_FRONTEND_URL);
|
formData.append("frontURL", process.env.REACT_APP_FRONTEND_URL)
|
||||||
formData.append("identifier", 'contacts_insert_csv');
|
formData.append("identifier", 'contacts_insert_csv')
|
||||||
formData.append("file", event.target.files[0]);
|
formData.append("file", event.target.files[0])
|
||||||
|
|
||||||
const config = {
|
const config = {
|
||||||
headers: {
|
headers: {
|
||||||
'content-type': 'multipart/form-data',
|
'content-type': 'multipart/form-data',
|
||||||
},
|
},
|
||||||
};
|
}
|
||||||
|
|
||||||
const bulk_contact_insert = await apiBroker.post("/contacts/bulk/insert", formData, config);
|
const bulk_contact_insert = await apiBroker.post("/contacts/bulk/insert", formData, config)
|
||||||
|
|
||||||
console.log(bulk_contact_insert.data)
|
console.log(bulk_contact_insert.data)
|
||||||
|
|
||||||
|
@ -156,7 +156,7 @@ const Contacts = () => {
|
||||||
|
|
||||||
// history.go(0);
|
// history.go(0);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -177,12 +177,9 @@ const Contacts = () => {
|
||||||
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
||||||
identifier: 'contacts_insert_csv'
|
identifier: 'contacts_insert_csv'
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
if (insertOnQueue && insertOnQueue.data) {
|
if (insertOnQueue && insertOnQueue.data) {
|
||||||
|
|
||||||
console.log('insertOnQueue: ', insertOnQueue.data)
|
|
||||||
console.log('data.app.file: ', insertOnQueue.data.app.file)
|
|
||||||
|
|
||||||
setZipFile(insertOnQueue.data.app.file)
|
setZipFile(insertOnQueue.data.app.file)
|
||||||
setOnQueueProcessStatus(insertOnQueue.data.app.status)
|
setOnQueueProcessStatus(insertOnQueue.data.app.status)
|
||||||
|
@ -193,23 +190,23 @@ const Contacts = () => {
|
||||||
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
fetchReportOnQueue();
|
fetchReportOnQueue()
|
||||||
|
|
||||||
}, 500);
|
}, 500)
|
||||||
return () => clearTimeout(delayDebounceFn);
|
return () => clearTimeout(delayDebounceFn)
|
||||||
|
|
||||||
}, [user])
|
}, [user])
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({ type: "RESET" });
|
dispatch({ type: "RESET" })
|
||||||
setPageNumber(1);
|
setPageNumber(1)
|
||||||
}, [searchParam]);
|
}, [searchParam])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
|
@ -220,7 +217,7 @@ const Contacts = () => {
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
const delayDebounceFn = setTimeout(() => {
|
const delayDebounceFn = setTimeout(() => {
|
||||||
const fetchContacts = async () => {
|
const fetchContacts = async () => {
|
||||||
|
|
||||||
|
@ -230,25 +227,25 @@ const Contacts = () => {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const { data } = await api.get("/contacts/", { params: { searchParam, pageNumber }, });
|
const { data } = await api.get("/contacts/", { params: { searchParam, pageNumber }, })
|
||||||
|
|
||||||
|
|
||||||
dispatch({ type: "LOAD_CONTACTS", payload: data.contacts });
|
dispatch({ type: "LOAD_CONTACTS", payload: data.contacts })
|
||||||
setHasMore(data.hasMore);
|
setHasMore(data.hasMore)
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
}
|
||||||
fetchContacts();
|
fetchContacts()
|
||||||
}, 500);
|
}, 500)
|
||||||
return () => clearTimeout(delayDebounceFn);
|
return () => clearTimeout(delayDebounceFn)
|
||||||
}, [searchParam, pageNumber]);
|
}, [searchParam, pageNumber])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
|
||||||
|
|
||||||
socket.on("contactsBulkInsertOnQueueStatus", (data) => {
|
socket.on("contactsBulkInsertOnQueueStatus", (data) => {
|
||||||
if (data.action === 'update') {
|
if (data.action === 'update') {
|
||||||
|
@ -261,7 +258,7 @@ const Contacts = () => {
|
||||||
setOnQueueProcessStatus(data.insertOnQueue.queueStatus)
|
setOnQueueProcessStatus(data.insertOnQueue.queueStatus)
|
||||||
|
|
||||||
if (data.insertOnQueue.queueStatus === "success") {
|
if (data.insertOnQueue.queueStatus === "success") {
|
||||||
history.go(0);
|
history.go(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -270,18 +267,18 @@ const Contacts = () => {
|
||||||
|
|
||||||
socket.on("contact", (data) => {
|
socket.on("contact", (data) => {
|
||||||
if (data.action === "update" || data.action === "create") {
|
if (data.action === "update" || data.action === "create") {
|
||||||
dispatch({ type: "UPDATE_CONTACTS", payload: data.contact });
|
dispatch({ type: "UPDATE_CONTACTS", payload: data.contact })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (data.action === "delete") {
|
if (data.action === "delete") {
|
||||||
dispatch({ type: "DELETE_CONTACT", payload: +data.contactId });
|
dispatch({ type: "DELETE_CONTACT", payload: +data.contactId })
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
return () => {
|
return () => {
|
||||||
socket.disconnect();
|
socket.disconnect()
|
||||||
};
|
}
|
||||||
}, [user, history]);
|
}, [user, history])
|
||||||
|
|
||||||
const removeExtraSpace = (str) => {
|
const removeExtraSpace = (str) => {
|
||||||
|
|
||||||
|
@ -291,18 +288,18 @@ const Contacts = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSearch = (event) => {
|
const handleSearch = (event) => {
|
||||||
setSearchParam(removeExtraSpace(event.target.value.toLowerCase()));
|
setSearchParam(removeExtraSpace(event.target.value.toLowerCase()))
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOpenContactModal = () => {
|
const handleOpenContactModal = () => {
|
||||||
setSelectedContactId(null);
|
setSelectedContactId(null)
|
||||||
setContactModalOpen(true);
|
setContactModalOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCloseContactModal = () => {
|
const handleCloseContactModal = () => {
|
||||||
setSelectedContactId(null);
|
setSelectedContactId(null)
|
||||||
setContactModalOpen(false);
|
setContactModalOpen(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOpenCreateTicketModal = (contactId) => {
|
const handleOpenCreateTicketModal = (contactId) => {
|
||||||
setSelectedContactId(contactId)
|
setSelectedContactId(contactId)
|
||||||
|
@ -330,46 +327,46 @@ const Contacts = () => {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
const hadleEditContact = (contactId) => {
|
const hadleEditContact = (contactId) => {
|
||||||
setSelectedContactId(contactId);
|
setSelectedContactId(contactId)
|
||||||
setContactModalOpen(true);
|
setContactModalOpen(true)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleDeleteContact = async (contactId) => {
|
const handleDeleteContact = async (contactId) => {
|
||||||
try {
|
try {
|
||||||
await api.delete(`/contacts/${contactId}`);
|
await api.delete(`/contacts/${contactId}`)
|
||||||
toast.success(i18n.t("contacts.toasts.deleted"));
|
toast.success(i18n.t("contacts.toasts.deleted"))
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
setDeletingContact(null);
|
setDeletingContact(null)
|
||||||
setSearchParam("");
|
setSearchParam("")
|
||||||
setPageNumber(1);
|
setPageNumber(1)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleimportContact = async () => {
|
const handleimportContact = async () => {
|
||||||
try {
|
try {
|
||||||
await api.post("/contacts/import");
|
await api.post("/contacts/import")
|
||||||
history.go(0);
|
history.go(0)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const loadMore = () => {
|
const loadMore = () => {
|
||||||
setPageNumber((prevState) => prevState + 1);
|
setPageNumber((prevState) => prevState + 1)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleScroll = (e) => {
|
const handleScroll = (e) => {
|
||||||
if (!hasMore || loading) return;
|
if (!hasMore || loading) return
|
||||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
if (scrollHeight - (scrollTop + 100) < clientHeight) {
|
if (scrollHeight - (scrollTop + 100) < clientHeight) {
|
||||||
loadMore();
|
loadMore()
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
const handleDownload = async () => {
|
const handleDownload = async () => {
|
||||||
|
@ -378,16 +375,16 @@ const Contacts = () => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
let res = await apiBroker.get(`/contacts/download/${zipfile}`, { responseType: 'blob' });
|
let res = await apiBroker.get(`/contacts/download/${zipfile}`, { responseType: 'blob' })
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
fileDownload(res.data, `${zipfile}`);
|
fileDownload(res.data, `${zipfile}`)
|
||||||
setOnQueueProcessStatus('empty')
|
setOnQueueProcessStatus('empty')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -406,7 +403,7 @@ const Contacts = () => {
|
||||||
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
|
||||||
identifier: 'contacts_insert_csv'
|
identifier: 'contacts_insert_csv'
|
||||||
}
|
}
|
||||||
});
|
})
|
||||||
|
|
||||||
if (res) {
|
if (res) {
|
||||||
setOnQueueProcessStatus('empty')
|
setOnQueueProcessStatus('empty')
|
||||||
|
@ -414,7 +411,7 @@ const Contacts = () => {
|
||||||
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.log(err);
|
console.log(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -452,13 +449,13 @@ const Contacts = () => {
|
||||||
>
|
>
|
||||||
{"CSV ALL"}
|
{"CSV ALL"}
|
||||||
</Button> */}
|
</Button> */}
|
||||||
</>);
|
</>)
|
||||||
|
|
||||||
case 'pending' || 'processing':
|
case 'pending' || 'processing':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>PROCESSING...</span>
|
<span>PROCESSING...</span>
|
||||||
</>);
|
</>)
|
||||||
|
|
||||||
case 'success':
|
case 'success':
|
||||||
return (
|
return (
|
||||||
|
@ -472,7 +469,7 @@ const Contacts = () => {
|
||||||
>
|
>
|
||||||
{'DOWNLOAD'}
|
{'DOWNLOAD'}
|
||||||
</Button>
|
</Button>
|
||||||
</>);
|
</>)
|
||||||
case 'error':
|
case 'error':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
|
@ -485,16 +482,16 @@ const Contacts = () => {
|
||||||
>
|
>
|
||||||
{'ERROR'}
|
{'ERROR'}
|
||||||
</Button>
|
</Button>
|
||||||
</>);
|
</>)
|
||||||
case 'downloading':
|
case 'downloading':
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<span>DOWNLOADING...</span>
|
<span>DOWNLOADING...</span>
|
||||||
</>);
|
</>)
|
||||||
|
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return (<><span>WAITING...</span></>);
|
return (<><span>WAITING...</span></>)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -642,8 +639,8 @@ const Contacts = () => {
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={(e) => {
|
onClick={(e) => {
|
||||||
setConfirmOpen(true);
|
setConfirmOpen(true)
|
||||||
setDeletingContact(contact);
|
setDeletingContact(contact)
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<DeleteOutlineIcon />
|
<DeleteOutlineIcon />
|
||||||
|
@ -659,7 +656,7 @@ const Contacts = () => {
|
||||||
</Table>
|
</Table>
|
||||||
</Paper>
|
</Paper>
|
||||||
</MainContainer>
|
</MainContainer>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Contacts;
|
export default Contacts
|
|
@ -121,7 +121,7 @@ const Queues = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSettings(data)
|
setSettings(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
|
@ -52,7 +52,7 @@ const Settings = () => {
|
||||||
const fetchSession = async () => {
|
const fetchSession = async () => {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get('/settings')
|
const { data } = await api.get('/settings')
|
||||||
setSettings(data)
|
setSettings(data.settings)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
@ -198,6 +198,34 @@ const Settings = () => {
|
||||||
</Paper>
|
</Paper>
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Container className={classes.container} maxWidth="sm">
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Contato conversar com whatsapps distintos no omnihit
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
margin="dense"
|
||||||
|
variant="outlined"
|
||||||
|
native
|
||||||
|
id="oneContactChatWithManyWhats-setting"
|
||||||
|
name="oneContactChatWithManyWhats"
|
||||||
|
value={
|
||||||
|
settings &&
|
||||||
|
settings.length > 0 &&
|
||||||
|
getSettingValue('oneContactChatWithManyWhats')
|
||||||
|
}
|
||||||
|
className={classes.settingOption}
|
||||||
|
onChange={handleChangeSetting}
|
||||||
|
>
|
||||||
|
<option value="enabled">Ativado</option>
|
||||||
|
<option value="disabled">Desativado</option>
|
||||||
|
</Select>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
|
@ -1,27 +1,27 @@
|
||||||
import React from "react";
|
import React from 'react'
|
||||||
import { BrowserRouter, Switch } from "react-router-dom";
|
import { BrowserRouter, Switch } from 'react-router-dom'
|
||||||
import { ToastContainer } from "react-toastify";
|
import { ToastContainer } from 'react-toastify'
|
||||||
|
|
||||||
import LoggedInLayout from "../layout";
|
import LoggedInLayout from '../layout'
|
||||||
import Dashboard from "../pages/Dashboard/";
|
import Dashboard from '../pages/Dashboard/'
|
||||||
|
|
||||||
import Report from "../pages/Report/";
|
|
||||||
import SchedulesReminder from "../pages/SchedulesReminder/";
|
|
||||||
|
|
||||||
import Tickets from "../pages/Tickets/";
|
|
||||||
import Signup from "../pages/Signup/";
|
|
||||||
import Login from "../pages/Login/";
|
|
||||||
import Connections from "../pages/Connections/";
|
|
||||||
import Settings from "../pages/Settings/";
|
|
||||||
import Users from "../pages/Users";
|
|
||||||
import Contacts from "../pages/Contacts/";
|
|
||||||
import QuickAnswers from "../pages/QuickAnswers/";
|
|
||||||
import Queues from "../pages/Queues/";
|
|
||||||
import { AuthProvider } from "../context/Auth/AuthContext";
|
|
||||||
import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext";
|
|
||||||
import Route from "./Route";
|
|
||||||
|
|
||||||
import Dialogflows from "../pages/Dialogflow/";
|
import Dialogflows from "../pages/Dialogflow/";
|
||||||
|
import Report from '../pages/Report/'
|
||||||
|
import SchedulesReminder from '../pages/SchedulesReminder/'
|
||||||
|
|
||||||
|
import Tickets from '../pages/Tickets/'
|
||||||
|
import Signup from '../pages/Signup/'
|
||||||
|
import Login from '../pages/Login/'
|
||||||
|
import Connections from '../pages/Connections/'
|
||||||
|
import Campaign from '../pages/Campaign'
|
||||||
|
import Settings from '../pages/Settings/'
|
||||||
|
import Users from '../pages/Users'
|
||||||
|
import Contacts from '../pages/Contacts/'
|
||||||
|
import QuickAnswers from '../pages/QuickAnswers/'
|
||||||
|
import Queues from '../pages/Queues/'
|
||||||
|
import { AuthProvider } from '../context/Auth/AuthContext'
|
||||||
|
import { WhatsAppsProvider } from '../context/WhatsApp/WhatsAppsContext'
|
||||||
|
import Route from './Route'
|
||||||
|
|
||||||
const Routes = () => {
|
const Routes = () => {
|
||||||
return (
|
return (
|
||||||
|
@ -33,28 +33,49 @@ const Routes = () => {
|
||||||
<WhatsAppsProvider>
|
<WhatsAppsProvider>
|
||||||
<LoggedInLayout>
|
<LoggedInLayout>
|
||||||
<Route exact path="/" component={Dashboard} isPrivate />
|
<Route exact path="/" component={Dashboard} isPrivate />
|
||||||
<Route exact path="/tickets/:ticketId?" component={Tickets} isPrivate />
|
<Route
|
||||||
|
exact
|
||||||
|
path="/tickets/:ticketId?"
|
||||||
|
component={Tickets}
|
||||||
|
isPrivate
|
||||||
|
/>
|
||||||
|
|
||||||
<Route exact path="/connections" component={Connections} isPrivate />
|
<Route
|
||||||
|
exact
|
||||||
|
path="/connections"
|
||||||
|
component={Connections}
|
||||||
|
isPrivate
|
||||||
|
/>
|
||||||
|
|
||||||
<Route exact path="/report" component={Report} isPrivate />
|
<Route exact path="/report" component={Report} isPrivate />
|
||||||
|
|
||||||
<Route exact path="/contacts" component={Contacts} isPrivate />
|
<Route exact path="/contacts" component={Contacts} isPrivate />
|
||||||
|
|
||||||
<Route exact path="/schedulesReminder" component={SchedulesReminder} isPrivate />
|
<Route
|
||||||
|
exact
|
||||||
|
path="/schedulesReminder"
|
||||||
|
component={SchedulesReminder}
|
||||||
|
isPrivate
|
||||||
|
/>
|
||||||
|
|
||||||
<Route exact path="/users" component={Users} isPrivate />
|
<Route exact path="/users" component={Users} isPrivate />
|
||||||
<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 />
|
<Route exact path="/Dialogflows" component={Dialogflows} isPrivate />
|
||||||
|
<Route exact path="/campaign" component={Campaign} isPrivate />
|
||||||
</LoggedInLayout>
|
</LoggedInLayout>
|
||||||
</WhatsAppsProvider>
|
</WhatsAppsProvider>
|
||||||
</Switch>
|
</Switch>
|
||||||
<ToastContainer autoClose={3000} />
|
<ToastContainer autoClose={3000} />
|
||||||
</AuthProvider>
|
</AuthProvider>
|
||||||
</BrowserRouter>
|
</BrowserRouter>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default Routes;
|
export default Routes
|
||||||
|
|
Loading…
Reference in New Issue