Merge remote-tracking branch 'origin' into gertec

gertec
gustavo-gsp 2024-09-06 19:41:25 -03:00
commit 940a6c1f97
55 changed files with 1079 additions and 575 deletions
frontend
src
components
ChatEnd/TimerPickerSelect
ConfigModal
MessagesList
ModalUpdateScheduleReminder
DatePicker2
TimerPickerSelect2
NotificationsPopOver
PositionModal
QrcodeModal
QueueModal
QuickAnswersModal
Report
DatePicker
ReportModal
StatusChatEndModal
Ticket
TicketListItem
TicketsList
TicketsManager
TransferTicketModal
UserModal
hooks
useAuth.js
useWhatsApps
pages
Campaign
Connections
Contacts
Position
Queues
QuickAnswers
Report
SchedulesReminder
Settings
StatusChatEnd
services
translate/languages

View File

@ -17,10 +17,12 @@
"license": "MIT", "license": "MIT",
"dependencies": { "dependencies": {
"@sentry/node": "^5.29.2", "@sentry/node": "^5.29.2",
"@socket.io/redis-adapter": "^7.2.0",
"@types/fluent-ffmpeg": "^2.1.21", "@types/fluent-ffmpeg": "^2.1.21",
"@types/pino": "^6.3.4", "@types/pino": "^6.3.4",
"axios": "^1.2.3", "axios": "^1.2.3",
"bcryptjs": "^2.4.3", "bcryptjs": "^2.4.3",
"compression": "^1.7.4",
"cookie-parser": "^1.4.5", "cookie-parser": "^1.4.5",
"cors": "^2.8.5", "cors": "^2.8.5",
"cpf-cnpj-validator": "^1.0.3", "cpf-cnpj-validator": "^1.0.3",
@ -42,12 +44,13 @@
"pino": "^6.9.0", "pino": "^6.9.0",
"pino-pretty": "^9.1.1", "pino-pretty": "^9.1.1",
"qrcode-terminal": "^0.12.0", "qrcode-terminal": "^0.12.0",
"redis": "^4.6.13",
"reflect-metadata": "^0.1.13", "reflect-metadata": "^0.1.13",
"sequelize": "^5.22.3", "sequelize": "^5.22.3",
"sequelize-cli": "^5.5.1", "sequelize-cli": "^5.5.1",
"sequelize-typescript": "^1.1.0", "sequelize-typescript": "^1.1.0",
"sharp": "^0.32.5", "sharp": "^0.32.5",
"socket.io": "^3.0.5", "socket.io": "^4.7.5",
"socket.io-client": "^4.5.4", "socket.io-client": "^4.5.4",
"uuid": "^8.3.2", "uuid": "^8.3.2",
"whatsapp-web.js": "github:pedroslopez/whatsapp-web.js", "whatsapp-web.js": "github:pedroslopez/whatsapp-web.js",
@ -56,6 +59,7 @@
"devDependencies": { "devDependencies": {
"@types/bcryptjs": "^2.4.2", "@types/bcryptjs": "^2.4.2",
"@types/bluebird": "^3.5.32", "@types/bluebird": "^3.5.32",
"@types/compression": "^1.7.5",
"@types/cookie-parser": "^1.4.2", "@types/cookie-parser": "^1.4.2",
"@types/cors": "^2.8.7", "@types/cors": "^2.8.7",
"@types/express": "^4.17.13", "@types/express": "^4.17.13",
@ -63,6 +67,7 @@
"@types/faker": "^5.1.3", "@types/faker": "^5.1.3",
"@types/jest": "^26.0.15", "@types/jest": "^26.0.15",
"@types/jsonwebtoken": "^8.5.0", "@types/jsonwebtoken": "^8.5.0",
"@types/lodash": "4.14",
"@types/multer": "^1.4.4", "@types/multer": "^1.4.4",
"@types/node": "^14.11.8", "@types/node": "^14.11.8",
"@types/supertest": "^2.0.10", "@types/supertest": "^2.0.10",

View File

@ -11,6 +11,7 @@ import uploadConfig from "./config/upload";
import AppError from "./errors/AppError"; import AppError from "./errors/AppError";
import routes from "./routes"; import routes from "./routes";
import { logger } from "./utils/logger"; import { logger } from "./utils/logger";
import compression from 'compression';
Sentry.init({ dsn: process.env.SENTRY_DSN }); Sentry.init({ dsn: process.env.SENTRY_DSN });
@ -23,6 +24,7 @@ app.use(
}) })
); );
app.use(compression());
app.use(cookieParser()); app.use(cookieParser());
app.use(express.json()); app.use(express.json());
app.use(Sentry.Handlers.requestHandler()); app.use(Sentry.Handlers.requestHandler());

View File

@ -41,7 +41,10 @@ import { getSettingValue } from "../helpers/WhaticketSettings";
import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber"; import ListWhatsAppsNumber from "../services/WhatsappService/ListWhatsAppsNumber";
import SettingTicket from "../models/SettingTicket"; import SettingTicket from "../models/SettingTicket";
import { Op } from "sequelize"; import { Op } from "sequelize";
import { del, get, set } from "../helpers/RedisClient"; import { del, get, getKeysByPattern, set, setCBPWhatsappOfficial } from "../helpers/RedisClient";
import axios from "axios";
interface WhatsappData { interface WhatsappData {
name: string; name: string;
@ -214,6 +217,48 @@ export const weebhook = async (
return res.sendStatus(500); return res.sendStatus(500);
} }
if(
req?.body?.entry?.length > 0 &&
req?.body?.entry[0]?.changes?.length > 0 &&
req?.body?.entry[0]?.changes?.length > 0 &&
req?.body?.entry[0]?.changes[0]?.value?.statuses?.length>0 &&
req?.body?.entry[0]?.changes[0]?.value?.statuses[0]?.recipient_id &&
req?.body?.entry[0]?.changes[0]?.value?.statuses[0]?.conversation?.origin?.type){
const company_phone = req?.body?.entry[0]?.changes[0]?.value?.metadata?.display_phone_number
const client_phone = req?.body?.entry[0]?.changes[0]?.value?.statuses[0]?.recipient_id
const conversation_type = req?.body?.entry[0]?.changes[0]?.value?.statuses[0].conversation.origin.type
const billable = req?.body?.entry[0]?.changes[0]?.value?.statuses[0].pricing.billable
const pricing_model = req?.body?.entry[0]?.changes[0]?.value?.statuses[0].pricing.pricing_model
const conversation_type_category = req?.body?.entry[0]?.changes[0]?.value?.statuses[0].pricing.category
const msg_id = req?.body?.entry[0]?.changes[0]?.value?.statuses[0].id
const _contact_to_exist = await get({
key: "whatsapp:*",
value: `${company_phone}`
});
if(_contact_to_exist){
const lst_services_cbp = await getKeysByPattern(company_phone, client_phone, conversation_type_category)
if(lst_services_cbp && lst_services_cbp.length > 0){
for(const item of lst_services_cbp){
if(!item.split(':').includes(conversation_type_category)){
await setCBP(msg_id, company_phone, client_phone, conversation_type_category, billable, pricing_model)
}
}
}else{
await setCBP(msg_id, company_phone, client_phone, conversation_type_category, billable, pricing_model)
}
console.log('_contact_to_exist: ', _contact_to_exist)
}
}
// MESSAGE // MESSAGE
if (req.body.object) { if (req.body.object) {
if ( if (
@ -377,9 +422,11 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
} else if (!isOfficial) { } else if (!isOfficial) {
phoneNumberId = ""; phoneNumberId = "";
wabaId = ""; wabaId = "";
number = ""; //number = "";
}
if(!number){
number = getNumberFromName(name)
} }
let invalidPhoneName = validatePhoneName(name); let invalidPhoneName = validatePhoneName(name);
if (invalidPhoneName) { if (invalidPhoneName) {
@ -479,9 +526,11 @@ export const update = async (
} else if (!isOfficial) { } else if (!isOfficial) {
whatsappData.phoneNumberId = ""; whatsappData.phoneNumberId = "";
whatsappData.wabaId = ""; whatsappData.wabaId = "";
whatsappData.number = ""; //whatsappData.number = "";
}
if(!whatsappData?.number){
whatsappData.number = getNumberFromName(whatsappData.name)
} }
const { whatsapp, oldDefaultWhatsapp } = await UpdateWhatsAppService({ const { whatsapp, oldDefaultWhatsapp } = await UpdateWhatsAppService({
whatsappData, whatsappData,
whatsappId whatsappId
@ -626,3 +675,56 @@ const checkWhatsAppData = ({
return { message: "urlApi is required!" }; return { message: "urlApi is required!" };
} }
}; };
async function setCBP(msg_id: any, company_phone: any, client_phone: any, conversation_type_category: any, billable:string, pricing_model:string) {
const message = await Message.findByPk(msg_id)
if (message) {
await setCBPWhatsappOfficial(company_phone, client_phone, conversation_type_category, msg_id, `${message.ticketId}`)
await sendToAPIUsage(msg_id,
company_phone,
client_phone,
conversation_type_category,
`${message.ticketId}`,
billable,
pricing_model
)
}
}
async function sendToAPIUsage(msg_id: any, company_phone: any, client_phone: any, conversation_type_category: any, ticketId: any, billable:string, pricing_model:string) {
const data = JSON.stringify({
"companyId": process.env.COMPANY_ID || company_phone,
"companyPhone": company_phone,
"clientPhone": client_phone,
"provider": "meta",
"product": "whatsapp",
"type": conversation_type_category,
"msgId": msg_id,
"ticketId": `${ticketId}`,
"ticketUrl": `${process.env.FRONTEND_URL}/tickets/${ticketId}`,
"billable": billable,
"pricing_model": pricing_model
});
const config = {
method: 'post',
url: 'http://172.31.187.24:6008/api/v1/billing/usage-whatsapp',
headers: {
'Authorization': 'Bearer 2ivck10D3o9qAZi0pkKudVDl9bdEVXY2s8gdxZ0jYgL1DZWTgDz6wDiIjlWgYmJtVOoqf0b42ZTLBRrfo8WoAaScRsujz3jQUNXdchSg0o43YilZGmVhheGJNAeIQRknHEll4nRJ7avcFgmDGoYbEey7TSC8EHS4Z3gzeufYYSfnKNDBwwzBURIQrTOxYFe3tBHsGOzwnuD2lU5tnEx7tr2XRO4zRNYeNY4lMBOFM0mRuyAe4kuqTrKXmJ8As200',
'Content-Type': 'application/json'
},
data: data
};
try {
const response = await axios(config);
console.log('Response from whatsapp api usage: ',JSON.stringify(response.data));
} catch (error) {
console.log('Error on try register the whatsapp usage: ', error);
}
}

View File

@ -158,6 +158,46 @@ export async function findObject(
return result; return result;
} }
export async function setCBPWhatsappOfficial(
company_phone:string,
client_phone:string,
conversation_type:string,
msg_id: string,
ticketId?: string
) {
const key = `company_phone:${company_phone}:client_phone:${client_phone}:conversation_type:${conversation_type}`;
const result = await redis.hmset(
key,
"company_phone",
company_phone,
"client_phone",
client_phone,
"conversation_type",
conversation_type,
"msg_id",
msg_id,
"ticketId",
ticketId
);
await redis.expire(key, 86400);
}
export async function getKeysByPattern(company_phone:string, client_phone:string, conversation_type:string,) {
const pattern = `company_phone:${company_phone}:client_phone:${client_phone}:conversation_type:${conversation_type}*`;
const keys = [];
let cursor = "0";
do {
const result = await redis.scan(cursor, "MATCH", pattern, "COUNT", 100);
cursor = result[0];
keys.push(...result[1]);
} while (cursor !== "0");
return keys;
}
export async function deleteObject( export async function deleteObject(
whatsappId: string, whatsappId: string,
contactId: string, contactId: string,

View File

@ -29,23 +29,23 @@ async function controllByNumber() {
number = JSON.parse(number); number = JSON.parse(number);
ticketId = JSON.parse(ticketId); ticketId = JSON.parse(ticketId);
const index = controll.findIndex((c: any) => c.number == number); const index = controll?.findIndex((c: any) => c.number == number);
if (index == -1) { if (index == -1) {
controll.push({ ticketId, number }); controll?.push({ ticketId, number });
} }
} }
const ticketIds = controll.map((c: any) => c.ticketId); const ticketIds = controll?.map((c: any) => c.ticketId);
console.log("=======> ticketIds: ", ticketIds); //console.log("=======> ticketIds: ", ticketIds);
for (const ticketId of ticketIds) { for (const ticketId of ticketIds) {
const ticket: any = await Ticket.findByPk(ticketId); const ticket: any = await Ticket.findByPk(ticketId);
if(ticket){ if(ticket){
const { status } = ticket; const { status } = ticket;
if (status == "pending") { if (status && status == "pending") {
await UpdateTicketService({ await UpdateTicketService({
ticketData: { statusChatEnd: uuidv4() }, ticketData: { statusChatEnd: uuidv4() },
ticketId: ticket.id ticketId: ticket.id

View File

@ -3,6 +3,9 @@ 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 { createAdapter } from "@socket.io/redis-adapter";
import { createClient } from 'redis';
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 { import {
@ -27,6 +30,7 @@ import {
} from "../services/WbotServices/wbotMessageListener"; } from "../services/WbotServices/wbotMessageListener";
import { join } from "path"; import { join } from "path";
import Whatsapp from "../models/Whatsapp"; import Whatsapp from "../models/Whatsapp";
import { get } from "../helpers/RedisClient"
let count: number = 0; let count: number = 0;
let listOnline: any[] = []; let listOnline: any[] = [];
@ -42,16 +46,32 @@ let dateTime = 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 }))
); );
export const initIO = (httpServer: Server): SocketIO => {
const pubClient = createClient({ url: 'redis://172.31.187.29:6379' });
const subClient = pubClient.duplicate();
pubClient.connect().catch(console.error);
subClient.connect().catch(console.error);
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,
//allowedHeaders: ["my-custom-header"],
//credentials: true
}, },
maxHttpBufferSize: 1e8 maxHttpBufferSize: 1e8,
}); // pingInterval: 25000,
// pingTimeout: 60000,
// adapter: createAdapter(pubClient, subClient)
});
io.on("connection", socket => { io.on("connection", socket => {
//logger.info("Client Connected"); logger.info("Client Connected");
socket.on("joinWhatsSession", (whatsappId: string) => { socket.on("joinWhatsSession", (whatsappId: string) => {
//logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`); //logger.info(`A client joined a joinWhatsSession channel: ${whatsappId}`);
@ -107,6 +127,7 @@ export const initIO = (httpServer: Server): SocketIO => {
socket.on("online", (userId: any) => { socket.on("online", (userId: any) => {
// console.log('userId: ', userId) // console.log('userId: ', userId)
return
obj.uuid = uuidv4(); obj.uuid = uuidv4();
if (userId.logoutUserId) { if (userId.logoutUserId) {
@ -233,13 +254,18 @@ export const initIO = (httpServer: Server): SocketIO => {
if (rooms && rooms.size == 2 && ![...rooms][1].startsWith("session_")) if (rooms && rooms.size == 2 && ![...rooms][1].startsWith("session_"))
return; return;
let whatsappIds: any = await Whatsapp.findAll({ let whatsappIds = await get({
attributes: ["id"], key: "whatsapp:*",
raw: true parse: 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( console.log(
"whatsappIds whatsappIds whatsappIds whatsappIds whatsappIds: ", "whatsappIds whatsappIds whatsappIds whatsappIds whatsappIds: ",

View File

@ -1,5 +1,6 @@
import { getIO } from "../../libs/socket"; import { getIO } from "../../libs/socket";
import Contact from "../../models/Contact"; import Contact from "../../models/Contact";
const { Op } = require('sequelize');
import { createOrUpdateContactCache } from '../../helpers/ContactsCache' import { createOrUpdateContactCache } from '../../helpers/ContactsCache'
import { tr } from "date-fns/locale"; import { tr } from "date-fns/locale";
@ -35,15 +36,37 @@ const CreateOrUpdateContactService = async ({
const io = getIO(); const io = getIO();
let contact: Contact | null; let contact: Contact | null;
const firstFourDigits = number.slice(0, 4);
contact = await Contact.findOne({ where: { number } }); const lastEightDigits = number.slice(-8);
//const numberFormat = number?.length === 13 && number[4] == '9' ? number.slice(0, 4) + number.slice(0, 4) : number;
//contact = await Contact.findOne({ where: { number } });
contact = await Contact.findOne({
where: {
[Op.and]: [
{
number: {
[Op.like]: `%${firstFourDigits}%`
}
},
{
number: {
[Op.like]: `%${lastEightDigits}%`
}
}
]
}
});
if (contact) { if (contact) {
contact.update({ profilePicUrl });
// TEST DEL if(contact.number == number){
await createOrUpdateContactCache(`contact:${contact.id}`, { profilePicUrl }) contact.update({ profilePicUrl });
// await createOrUpdateContactCache(`contact:${contact.id}`, { profilePicUrl })
} else{
contact.update({ profilePicUrl, number });
await createOrUpdateContactCache(`contact:${contact.id}`, { profilePicUrl, number })
}
io.emit("contact", { io.emit("contact", {
action: "update", action: "update",

View File

@ -101,8 +101,6 @@ const FindOrCreateTicketServiceBot = async (
unreadMessages unreadMessages
}); });
console.log("lxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx");
await dialogFlowStartContext(contact, ticket, botInfo); await dialogFlowStartContext(contact, ticket, botInfo);
} }
} }
@ -128,8 +126,6 @@ const FindOrCreateTicketServiceBot = async (
phoneNumberId phoneNumberId
}); });
console.log("yyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy");
await dialogFlowStartContext(contact, ticket, botInfo); await dialogFlowStartContext(contact, ticket, botInfo);
} }

View File

@ -761,8 +761,6 @@ const handleMessage = async (
// console.log('----------> chat: ', JSON.parse(JSON.stringify(chat))) // console.log('----------> chat: ', JSON.parse(JSON.stringify(chat)))
console
if (chat.isGroup) { if (chat.isGroup) {
// let msgGroupContact; // let msgGroupContact;

View File

@ -43,7 +43,7 @@
"yup": "^0.32.8" "yup": "^0.32.8"
}, },
"scripts": { "scripts": {
"start": "react-scripts start", "start": "PORT=3331 react-scripts start",
"build": "react-scripts build", "build": "react-scripts build",
"test": "react-scripts test", "test": "react-scripts test",
"eject": "react-scripts eject" "eject": "react-scripts eject"

View File

@ -45,7 +45,7 @@ import {
import ptBrLocale from "date-fns/locale/pt-BR"; import ptBrLocale from "date-fns/locale/pt-BR";
import esLocale from 'date-fns/locale/es';
const ResponsiveTimePickers = (props) => { const ResponsiveTimePickers = (props) => {
@ -63,7 +63,7 @@ const ResponsiveTimePickers = (props) => {
<Fragment> <Fragment>
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}> <MuiPickersUtilsProvider utils={DateFnsUtils} locale={esLocale}>
<TimePicker <TimePicker
variant="outline" variant="outline"
label={props.title} label={props.title}

View File

@ -11,6 +11,7 @@ import { TimePicker, DatePicker } from 'formik-material-ui-pickers'
import DateFnsUtils from '@date-io/date-fns' import DateFnsUtils from '@date-io/date-fns'
import ptBrLocale from "date-fns/locale/pt-BR" import ptBrLocale from "date-fns/locale/pt-BR"
import esLocale from 'date-fns/locale/es';
import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext" import { WhatsAppsContext } from "../../context/WhatsApp/WhatsAppsContext"
@ -257,7 +258,7 @@ const ConfigModal = ({ open, onClose, change }) => {
}} }}
> >
{({ values, touched, errors, isSubmitting }) => ( {({ values, touched, errors, isSubmitting }) => (
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}> <MuiPickersUtilsProvider utils={DateFnsUtils} locale={esLocale}>
<Form> <Form>
<DialogContent dividers> <DialogContent dividers>
@ -299,7 +300,7 @@ const ConfigModal = ({ open, onClose, change }) => {
<Field <Field
component={TimePicker} component={TimePicker}
name="startTimeBus" name="startTimeBus"
label="Inicio atendimento" label={i18n.t('configModal.titles.startService')}
ampm={false} ampm={false}
openTo="hours" openTo="hours"
views={['hours', 'minutes',]} views={['hours', 'minutes',]}
@ -309,7 +310,7 @@ const ConfigModal = ({ open, onClose, change }) => {
<Field <Field
component={TimePicker} component={TimePicker}
name="endTimeBus" name="endTimeBus"
label="Fim atendimento" label={i18n.t('configModal.titles.endService')}
ampm={false} ampm={false}
openTo="hours" openTo="hours"
views={['hours', 'minutes',]} views={['hours', 'minutes',]}
@ -325,13 +326,13 @@ const ConfigModal = ({ open, onClose, change }) => {
checked={values.businessTimeEnable} checked={values.businessTimeEnable}
/> />
} }
label={'Ativar/Desativar'} /> label={i18n.t('configModal.titles.enableDisable')} />
</div> </div>
<div> <div>
<Field <Field
as={TextField} as={TextField}
label={'Mensagem fora do horário de atendimento'} label={i18n.t('configModal.titles.outOfHours')}
type="messageBus" type="messageBus"
multiline multiline
rows={5} rows={5}
@ -355,7 +356,7 @@ const ConfigModal = ({ open, onClose, change }) => {
<Field <Field
component={TimePicker} component={TimePicker}
name="startTimeBusSaturday" name="startTimeBusSaturday"
label="Inicio atendimentos" label={i18n.t('configModal.titles.startService')}
ampm={false} ampm={false}
openTo="hours" openTo="hours"
views={['hours', 'minutes',]} views={['hours', 'minutes',]}
@ -365,7 +366,7 @@ const ConfigModal = ({ open, onClose, change }) => {
<Field <Field
component={TimePicker} component={TimePicker}
name="endTimeBusSaturday" name="endTimeBusSaturday"
label="Fim atendimento" label={i18n.t('configModal.titles.endService')}
ampm={false} ampm={false}
openTo="hours" openTo="hours"
views={['hours', 'minutes',]} views={['hours', 'minutes',]}
@ -381,13 +382,13 @@ const ConfigModal = ({ open, onClose, change }) => {
checked={values.businessTimeEnableSaturday} checked={values.businessTimeEnableSaturday}
/> />
} }
label={'Ativar/Desativar'} /> label={i18n.t('configModal.titles.enableDisable')} />
</div> </div>
<div> <div>
<Field <Field
as={TextField} as={TextField}
label={'Mensagem fora do horário de atendimento sábado'} label={i18n.t('configModal.titles.outOfHoursSaturday')}
type="messageBusSaturday" type="messageBusSaturday"
multiline multiline
rows={5} rows={5}
@ -405,7 +406,7 @@ const ConfigModal = ({ open, onClose, change }) => {
</div> </div>
{/* SABADO FIM */} {/* SABADO FIM */}
<br /> <br />
{/* Saturday and Sunday date */} {/* Saturday and Sunday date */}
<div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}> <div className={classes.multFieldLine} style={{ display: 'flex', justifyContent: 'space-between', alignItems: 'center' }}>
@ -430,13 +431,13 @@ const ConfigModal = ({ open, onClose, change }) => {
checked={values.enableWeekendMessage} checked={values.enableWeekendMessage}
/> />
} }
label={'Ativar/Desativar'} label={i18n.t('configModal.titles.enableDisable')}
/> />
</div> </div>
<div> <div>
<Field <Field
as={TextField} as={TextField}
label={'Mensagem para final de semana'} label={i18n.t('configModal.titles.forTheWeekend')}
type="weekendMessage" type="weekendMessage"
multiline multiline
rows={5} rows={5}
@ -461,7 +462,7 @@ const ConfigModal = ({ open, onClose, change }) => {
<Field <Field
component={DatePicker} component={DatePicker}
name="holidayDate" name="holidayDate"
label="Data do feriado" label={i18n.t('configModal.titles.holiday')}
format="dd/MM/yyyy" format="dd/MM/yyyy"
fullWidth fullWidth
/> />
@ -475,13 +476,13 @@ const ConfigModal = ({ open, onClose, change }) => {
checked={values.holidayDateEnable} checked={values.holidayDateEnable}
/> />
} }
label={'Ativar/Desativar'} label={i18n.t('configModal.titles.enableDisable')}
/> />
</div> </div>
<div> <div>
<Field <Field
as={TextField} as={TextField}
label={'Mensagem para feriado'} label={i18n.t('configModal.titles.holidayMessage')}
type="holidayDateMessage" type="holidayDateMessage"
multiline multiline
rows={5} rows={5}
@ -524,13 +525,13 @@ const ConfigModal = ({ open, onClose, change }) => {
checked={values.ticketExpirationEnable} checked={values.ticketExpirationEnable}
/> />
} }
label={'Ativar/Desativar'} label={i18n.t('configModal.titles.enableDisable')}
/> />
</div> </div>
<div> <div>
<Field <Field
as={TextField} as={TextField}
label={'Mensagem por falta de atividade no atendimento'} label={i18n.t('configModal.titles.inactivityMessage')}
type="ticketExpirationMsg" type="ticketExpirationMsg"
multiline multiline
rows={5} rows={5}
@ -569,7 +570,7 @@ const ConfigModal = ({ open, onClose, change }) => {
size={24} size={24}
className={classes.buttonProgress} className={classes.buttonProgress}
/> />
) : 'Salvar'} ) : i18n.t('configModal.titles.save')}
</Button> </Button>
</DialogActions> </DialogActions>
</Form> </Form>

View File

@ -23,6 +23,8 @@ import CheckCircleIcon from "@material-ui/icons/CheckCircle";
import ErrorIcon from "@material-ui/icons/Error"; import ErrorIcon from "@material-ui/icons/Error";
import RemoveCircleIcon from "@material-ui/icons/RemoveCircle"; import RemoveCircleIcon from "@material-ui/icons/RemoveCircle";
import { i18n } from "../../translate/i18n";
const CardUser = ({ classes, usersOnlineInfo, logout }) => { const CardUser = ({ classes, usersOnlineInfo, logout }) => {
const [search, setSearch] = React.useState(""); const [search, setSearch] = React.useState("");
@ -46,14 +48,14 @@ const CardUser = ({ classes, usersOnlineInfo, logout }) => {
color="primary" color="primary"
style={{ marginBottom: "16px" }} style={{ marginBottom: "16px" }}
> >
Lista de Usuários {i18n.t('dashboard.titles.listUser')}
</Typography> </Typography>
</Grid> </Grid>
<Grid item sx={8} width="100%"> <Grid item sx={8} width="100%">
<Box sx={{ marginBottom: 2, display: "flex", gap: "12px" }}> <Box sx={{ marginBottom: 2, display: "flex", gap: "12px" }}>
<TextField <TextField
id="outlined-basic" id="outlined-basic"
label="Usuário" label={i18n.t('dashboard.titles.user')}
variant="standard" variant="standard"
value={search} value={search}
onChange={handlesearch} onChange={handlesearch}
@ -70,7 +72,7 @@ const CardUser = ({ classes, usersOnlineInfo, logout }) => {
<MenuItem value={null}>Todos</MenuItem> <MenuItem value={null}>Todos</MenuItem>
<MenuItem value={"online"}>Online</MenuItem> <MenuItem value={"online"}>Online</MenuItem>
<MenuItem value={"offline"}>Offline</MenuItem> <MenuItem value={"offline"}>Offline</MenuItem>
<MenuItem value={"not"}>Não entrou</MenuItem> <MenuItem value={"not"}>{i18n.t('dashboard.titles.notEnter')}</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
</Box> </Box>
@ -161,7 +163,7 @@ const CardUser = ({ classes, usersOnlineInfo, logout }) => {
<Typography component="p" color="textPrimary" paragraph> <Typography component="p" color="textPrimary" paragraph>
{user.sumOnlineTime && user.sumOnlineTime.sum {user.sumOnlineTime && user.sumOnlineTime.sum
? user.sumOnlineTime.sum ? user.sumOnlineTime.sum
: "Não entrou Hoje"} : i18n.t('dashboard.titles.notEnterToday')}
</Typography> </Typography>
</Typography> </Typography>
</CardContent> </CardContent>

View File

@ -75,7 +75,7 @@ const TableUser = ({ classes, usersOnlineInfo, logout }) => {
<MenuItem value={null}>Todos</MenuItem> <MenuItem value={null}>Todos</MenuItem>
<MenuItem value={"online"}>Online</MenuItem> <MenuItem value={"online"}>Online</MenuItem>
<MenuItem value={"offline"}>Offline</MenuItem> <MenuItem value={"offline"}>Offline</MenuItem>
<MenuItem value={"not"}>Não entrou</MenuItem> <MenuItem value={"not"}>{i18n.t('dashboard.titles.notEnter')}</MenuItem>
</Select> </Select>
</FormControl> </FormControl>
</Box> </Box>
@ -221,7 +221,7 @@ const TableUser = ({ classes, usersOnlineInfo, logout }) => {
</div> </div>
</TableCell> </TableCell>
<TableCell> <TableCell>
{user.sumOnlineTime ? user.sumOnlineTime.sum : "Não entrou"} {user.sumOnlineTime ? user.sumOnlineTime.sum : i18n.t('dashboard.titles.notEnter')}
</TableCell> </TableCell>
<TableCell> <TableCell>
{user.statusOnline && user.statusOnline.status === "online" ? ( {user.statusOnline && user.statusOnline.status === "online" ? (

View File

@ -1,7 +1,9 @@
import React, { useContext, useState, useEffect, useReducer, useRef } from "react" import React, { useContext, useState, useEffect, useReducer, useRef } from "react"
import { isSameDay, parseISO, format } from "date-fns" import { isSameDay, parseISO, format } from "date-fns"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import clsx from "clsx" import clsx from "clsx"
import { AuthContext } from "../../context/Auth/AuthContext" import { AuthContext } from "../../context/Auth/AuthContext"
@ -34,7 +36,7 @@ import whatsBackground from "../../assets/wa-background.png"
import api from "../../services/api" import api from "../../services/api"
import toastError from "../../errors/toastError" import toastError from "../../errors/toastError"
import { countTicketMsgContext } from "../../context/CountTicketMsgProvider/CountTicketMsgProvider" import { countTicketMsgContext } from "../../context/CountTicketMsgProvider/CountTicketMsgProvider"
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles((theme) => ({
messagesListWrapper: { messagesListWrapper: {
@ -438,11 +440,18 @@ const MessagesList = ({ ticketId, isGroup }) => {
}, [pageNumber, ticketId]) }, [pageNumber, ticketId])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("connect", () => socket.emit("joinChatBox", ticketId)) const onConnectMessagesList = () => {
socket.emit("joinChatBox", ticketId)
}
socket.on("appMessage", (data) => { onConnectMessagesList()
socket.on("connect", onConnectMessagesList)
const onAppMessageMessagesList = (data) => {
if (+data.message.ticketId !== +ticketId) return
if (data.action === "create") { if (data.action === "create") {
@ -454,10 +463,14 @@ const MessagesList = ({ ticketId, isGroup }) => {
if (data.action === "update") { if (data.action === "update") {
dispatch({ type: "UPDATE_MESSAGE", payload: data.message }) dispatch({ type: "UPDATE_MESSAGE", payload: data.message })
} }
}) }
socket.on("appMessage", onAppMessageMessagesList)
return () => { return () => {
socket.disconnect() socket.emit("leaveChatBox", ticketId)
socket.off("connect", onConnectMessagesList)
socket.off("appMessage", onAppMessageMessagesList)
} }
}, [ticketId]) }, [ticketId])
@ -949,4 +962,4 @@ const MessagesList = ({ ticketId, isGroup }) => {
) )
} }
export default MessagesList export default MessagesList

View File

@ -13,7 +13,7 @@ import {
import ptBrLocale from "date-fns/locale/pt-BR"; import ptBrLocale from "date-fns/locale/pt-BR";
import esLocale from 'date-fns/locale/es';
function formatDateDatePicker(data){ function formatDateDatePicker(data){
return String(new Date(data).getFullYear())+'-'+ return String(new Date(data).getFullYear())+'-'+
@ -50,7 +50,7 @@ function ResponsiveDatePickers(props) {
return ( return (
<Fragment> <Fragment>
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}> <MuiPickersUtilsProvider utils={DateFnsUtils} locale={esLocale}>
<KeyboardDatePicker <KeyboardDatePicker
// autoOk // autoOk

View File

@ -45,7 +45,7 @@ import {
import ptBrLocale from "date-fns/locale/pt-BR"; import ptBrLocale from "date-fns/locale/pt-BR";
import esLocale from 'date-fns/locale/es';
function formatDate(strDate){ function formatDate(strDate){
const date = strDate.split(' ')[0].split('/') const date = strDate.split(' ')[0].split('/')
@ -70,7 +70,7 @@ const ResponsiveTimePickers = (props) => {
<Fragment> <Fragment>
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}> <MuiPickersUtilsProvider utils={DateFnsUtils} locale={esLocale}>
<TimePicker <TimePicker
variant="outline" variant="outline"
label={props.title} label={props.title}

View File

@ -2,7 +2,8 @@ import React, { useState, useRef, useEffect, useContext } from "react"
import { useHistory } from "react-router-dom" import { useHistory } from "react-router-dom"
import { format } from "date-fns" import { format } from "date-fns"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import useSound from "use-sound" import useSound from "use-sound"
import Popover from "@material-ui/core/Popover" import Popover from "@material-ui/core/Popover"
@ -115,24 +116,24 @@ const NotificationsPopOver = () => {
useEffect(() => { useEffect(() => {
const fetchSession = async () => { const fetchSession = async () => {
try { try {
const { data } = await api.get('/settings') const { data } = await api.get('/settings')
setSettings(data.settings) setSettings(data.settings)
} catch (err) { } catch (err) {
toastError(err) toastError(err)
} }
} }
fetchSession() fetchSession()
}, []) }, [])
const getSettingValue = (key) => { const getSettingValue = (key) => {
const { value } = settings.find((s) => s.key === key) const { value } = settings.find((s) => s.key === key)
return value return value
} }
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("reload_page", (data) => { socket.on("reload_page", (data) => {
@ -193,7 +194,8 @@ const NotificationsPopOver = () => {
return () => { return () => {
socket.disconnect() socket.removeAllListeners('reload_page');
socket.removeAllListeners('onlineStatus');
} }
}, [user.id, handleLogout, user.profile]) }, [user.id, handleLogout, user.profile])
@ -201,12 +203,17 @@ const NotificationsPopOver = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("connect", () => socket.emit("joinNotification")) const onConnectNotifications = () => {
socket.emit("joinNotification")
}
onConnectNotifications()
socket.on("ticket", data => { socket.on("connect", onConnectNotifications)
const onTicketNotifications = data => {
if (data.action === "updateUnread" || data.action === "delete") { if (data.action === "updateUnread" || data.action === "delete") {
@ -232,25 +239,18 @@ const NotificationsPopOver = () => {
return prevState return prevState
}) })
} }
}) }
socket.on("appMessage", data => {
socket.on("ticket", onTicketNotifications)
const onAppMessageNotifications = data => {
if ( if (
data.action === "create" && data.action === "create" &&
!data.message.read && !data.message.read &&
(data.ticket.userId === user?.id || !data.ticket.userId) (data.ticket.userId === user?.id || !data.ticket.userId)
) { ) {
setNotifications(prevState => { setNotifications(prevState => {
// prevState.forEach((e)=>{ // prevState.forEach((e)=>{
// //
// }) // })
@ -265,8 +265,6 @@ const NotificationsPopOver = () => {
return [data.ticket, ...prevState] return [data.ticket, ...prevState]
}) })
const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") || const shouldNotNotificate = (data.message.ticketId === ticketIdRef.current && document.visibilityState === "visible") ||
(data.ticket.userId && data.ticket.userId !== user?.id) || (data.ticket.userId && data.ticket.userId !== user?.id) ||
data.ticket.isGroup || !data.ticket.userId data.ticket.isGroup || !data.ticket.userId
@ -275,20 +273,25 @@ const NotificationsPopOver = () => {
handleNotifications(data) handleNotifications(data)
} }
}) }
socket.on('notifyPeding', data =>{ socket.on("appMessage", onAppMessageNotifications)
if(settings?.length > 0 && getSettingValue('notificationTransferQueue') === 'enabled') handleNotifications("", data);
socket.on('notifyPeding', data => {
if (settings?.length > 0 && getSettingValue('notificationTransferQueue') === 'enabled') handleNotifications("", data);
}); });
return () => { return () => {
socket.disconnect() socket.off('connect', onConnectNotifications);
socket.off('ticket', onTicketNotifications);
socket.off('appMessage', onAppMessageNotifications);
socket.removeAllListeners('notifyPeding');
} }
}, [user, settings]) }, [user, settings])
const handleNotifications = (data, notify) => { const handleNotifications = (data, notify) => {
let isQueue = false; let isQueue = false;
if(!notify){ if (!notify) {
const { message, contact, ticket } = data const { message, contact, ticket } = data
const options = { const options = {
@ -301,14 +304,14 @@ const NotificationsPopOver = () => {
const notification = new Notification( const notification = new Notification(
`${i18n.t("tickets.notification.message")} ${contact.name}`, `${i18n.t("tickets.notification.message")} ${contact.name}`,
options options
) )
notification.onclick = e => { notification.onclick = e => {
e.preventDefault() e.preventDefault()
window.focus() window.focus()
historyRef.current.push(`/tickets/${ticket.id}`) historyRef.current.push(`/tickets/${ticket.id}`)
} }
setDesktopNotifications(prevState => { setDesktopNotifications(prevState => {
const notfiticationIndex = prevState.findIndex( const notfiticationIndex = prevState.findIndex(
n => n.tag === notification.tag n => n.tag === notification.tag
@ -319,15 +322,15 @@ const NotificationsPopOver = () => {
} }
return [notification, ...prevState] return [notification, ...prevState]
}) })
}else{ } else {
user.queues.forEach(queue =>{ user.queues.forEach(queue => {
if(queue.id === notify.data?.queue?.id){ if (queue.id === notify.data?.queue?.id) {
isQueue = true; isQueue = true;
} }
}) })
if(!isQueue){ if (!isQueue) {
return; return;
}else { } else {
const notification = new Notification(`${i18n.t("tickets.notification.messagePeding")} ${notify.data?.queue?.name}`); const notification = new Notification(`${i18n.t("tickets.notification.messagePeding")} ${notify.data?.queue?.name}`);
notification.onclick = e => { notification.onclick = e => {
e.preventDefault() e.preventDefault()
@ -346,7 +349,7 @@ const NotificationsPopOver = () => {
return [notification, ...prevState] return [notification, ...prevState]
}) })
} }
} }
soundAlertRef.current() soundAlertRef.current()
} }

View File

@ -3,8 +3,8 @@ import React, { useState, useEffect, useRef, useContext } from "react"
import * as Yup from "yup" import * as Yup from "yup"
import { Formik, Form, Field } from "formik" import { Formik, Form, Field } from "formik"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from "../../services/socket"
import { import {
makeStyles, makeStyles,
@ -51,8 +51,8 @@ const useStyles = makeStyles((theme) => ({
})) }))
const PositionSchema = Yup.object().shape({ const PositionSchema = Yup.object().shape({
name: Yup.string() name: Yup.string()
.required("Required"), .required("Required"),
}) })
const PositionModal = ({ const PositionModal = ({
@ -66,14 +66,14 @@ const PositionModal = ({
const isMounted = useRef(true) const isMounted = useRef(true)
const initialState = { const initialState = {
name: "", name: "",
} }
const [position, setPosition] = useState(initialState) const [position, setPosition] = useState(initialState)
// const [selectedQueueIds, setSelectedQueueIds] = useState([]) // const [selectedQueueIds, setSelectedQueueIds] = useState([])
const { setting } = useContext(AuthContext) const { setting } = useContext(AuthContext)
const [settings, setSettings] = useState(setting) const [settings, setSettings] = useState(setting)
useEffect(() => { useEffect(() => {
return () => { return () => {
@ -87,9 +87,9 @@ const PositionModal = ({
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsPosition = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -98,10 +98,12 @@ const PositionModal = ({
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsPosition)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsPosition)
} }
}, []) }, [])
@ -141,7 +143,7 @@ const PositionModal = ({
const handleSavePosition = async (values) => { const handleSavePosition = async (values) => {
try { try {
if (positionId) { if (positionId) {
await api.put(`/positions/${positionId}`, values) await api.put(`/positions/${positionId}`, values)
handleClose() handleClose()
@ -154,7 +156,7 @@ const PositionModal = ({
handleClose() handleClose()
toast.success("Cargo salvo com sucesso") toast.success("Cargo salvo com sucesso")
} }
} catch (err) { } catch (err) {
toastError(err) toastError(err)
} }
@ -191,7 +193,7 @@ const PositionModal = ({
<div className={classes.textQuickAnswerContainer}> <div className={classes.textQuickAnswerContainer}>
<Field <Field
as={TextField} as={TextField}
label={'Nome'} label={i18n.t('dashboard.titles.name')}
name="name" name="name"
autoFocus autoFocus
error={touched.name && Boolean(errors.name)} error={touched.name && Boolean(errors.name)}
@ -201,7 +203,7 @@ const PositionModal = ({
className={classes.textField} className={classes.textField}
fullWidth fullWidth
/> />
</div> </div>
{/* <div> {/* <div>
{ {
((settings && getSettingValue('quickAnswerByQueue') === 'enabled')) && ( ((settings && getSettingValue('quickAnswerByQueue') === 'enabled')) && (

View File

@ -1,6 +1,8 @@
import React, { useEffect, useState, useContext } from "react"; import React, { useEffect, useState, useContext } from "react";
import QRCode from "qrcode.react"; import QRCode from "qrcode.react";
import openSocket from "socket.io-client"; //import openSocket from "socket.io-client";
import { socket } from "../../services/socket";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
import { Dialog, DialogContent, Paper, Typography } from "@material-ui/core"; import { Dialog, DialogContent, Paper, Typography } from "@material-ui/core";
@ -31,9 +33,9 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => {
useEffect(() => { useEffect(() => {
if (!whatsAppId) return; if (!whatsAppId) return;
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); //const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("whatsappSession", data => { const onWhatsAppSessionQrCode = data => {
if (data.action === "update" && data.session.id === whatsAppId) { if (data.action === "update" && data.session.id === whatsAppId) {
setQrCode(data.session.qrcode); setQrCode(data.session.qrcode);
} }
@ -42,21 +44,23 @@ const QrcodeModal = ({ open, onClose, whatsAppId }) => {
onClose(); onClose();
} }
if (data.action === "error") { if (data.action === "error") {
console.log('user.profile: ', user.profile) console.log('user.profile: ', user.profile)
if(user.profile === 'master'){ if (user.profile === 'master') {
alert(data.msg) alert(data.msg)
} }
} }
}); }
socket.on("whatsappSession", onWhatsAppSessionQrCode);
return () => { return () => {
socket.disconnect(); socket.off("whatsappSession", onWhatsAppSessionQrCode);
}; };
}, [whatsAppId, onClose, user.profile]); }, [whatsAppId, onClose, user.profile]);

View File

@ -23,8 +23,8 @@ import { IconButton, InputAdornment } from "@material-ui/core"
import { Colorize } from "@material-ui/icons" import { Colorize } from "@material-ui/icons"
import { AuthContext } from '../../context/Auth/AuthContext' import { AuthContext } from '../../context/Auth/AuthContext'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from "../../services/socket"
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
@ -92,9 +92,9 @@ const QueueModal = ({ open, onClose, queueId }) => {
}, [setting]) }, [setting])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsQueueModal = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -103,10 +103,12 @@ const QueueModal = ({ open, onClose, queueId }) => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsQueueModal)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsQueueModal)
} }
}, []) }, [])

View File

@ -3,8 +3,8 @@ import React, { useState, useEffect, useRef, useContext } from "react"
import * as Yup from "yup" import * as Yup from "yup"
import { Formik, Form, Field } from "formik" import { Formik, Form, Field } from "formik"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from "../../services/socket"
import { import {
makeStyles, makeStyles,
@ -79,7 +79,7 @@ const QuickAnswersModal = ({
const [quickAnswer, setQuickAnswer] = useState(initialState) const [quickAnswer, setQuickAnswer] = useState(initialState)
const [selectedQueueIds, setSelectedQueueIds] = useState([]) const [selectedQueueIds, setSelectedQueueIds] = useState([])
const { user, setting, getSettingValue } = useContext(AuthContext) const { user, setting, getSettingValue } = useContext(AuthContext)
const [settings, setSettings] = useState(setting) const [settings, setSettings] = useState(setting)
useEffect(() => { useEffect(() => {
return () => { return () => {
@ -92,18 +92,18 @@ const QuickAnswersModal = ({
}, [setting]) }, [setting])
useEffect(() => { useEffect(() => {
setSelectedQueueIds([]) setSelectedQueueIds([])
if (open && selectedQueueIds.length === 0 && !quickAnswerId) { if (open && selectedQueueIds.length === 0 && !quickAnswerId) {
setSelectedQueueIds(user.queues.map(q => q.id)) setSelectedQueueIds(user.queues.map(q => q.id))
} }
}, [open,]) }, [open,])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsQuickAnswersModal = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -112,14 +112,16 @@ const QuickAnswersModal = ({
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsQuickAnswersModal)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsQuickAnswersModal)
} }
}, []) }, [])
useEffect(() => { useEffect(() => {
// setSelectedQueueIds([]) // setSelectedQueueIds([])
@ -139,7 +141,7 @@ const QuickAnswersModal = ({
if (isMounted.current) { if (isMounted.current) {
setQuickAnswer(data) setQuickAnswer(data)
if (data?.queues) { if (data?.queues) {
const quickQueueIds = data.queues?.map((queue) => queue.id) const quickQueueIds = data.queues?.map((queue) => queue.id)
setSelectedQueueIds(quickQueueIds) setSelectedQueueIds(quickQueueIds)
} }

View File

@ -15,7 +15,7 @@ import {
import ptBrLocale from "date-fns/locale/pt-BR"; import ptBrLocale from "date-fns/locale/pt-BR";
import esLocale from 'date-fns/locale/es';
function formatDateDatePicker(data){ function formatDateDatePicker(data){
return String(new Date(data).getFullYear())+'-'+ return String(new Date(data).getFullYear())+'-'+
@ -52,7 +52,7 @@ function ResponsiveDatePickers(props) {
return ( return (
<Fragment> <Fragment>
<MuiPickersUtilsProvider utils={DateFnsUtils} locale={ptBrLocale}> <MuiPickersUtilsProvider utils={DateFnsUtils} locale={esLocale}>
<KeyboardDatePicker <KeyboardDatePicker
// autoOk // autoOk

View File

@ -52,7 +52,7 @@ const Modal = (props) => {
const dataChat = props.data.map((dt) => { const dataChat = props.data.map((dt) => {
return { return {
'fromMe': dt.fromMe ? 'Atendente' : 'Cliente', 'fromMe': dt.fromMe ? i18n.t('dashboard.titles.attendant') : 'Cliente',
'body': dt.body, 'body': dt.body,
'createdAt': dt.createdAt 'createdAt': dt.createdAt
} }

View File

@ -11,7 +11,8 @@ import FormControl from '@mui/material/FormControl';
import InputLabel from '@mui/material/InputLabel'; import InputLabel from '@mui/material/InputLabel';
import MenuItem from '@mui/material/MenuItem'; import MenuItem from '@mui/material/MenuItem';
import Select from '@mui/material/Select'; import Select from '@mui/material/Select';
import { i18n } from "../../translate/i18n";
@ -56,11 +57,11 @@ useEffect(()=>{
open={open} open={open}
onClose={handleClose} onClose={handleClose}
> >
<DialogTitle>Relatórios</DialogTitle> <DialogTitle>{i18n.t('dashboard.titles.dialogTitle')}</DialogTitle>
<DialogContent> <DialogContent>
<DialogContentText> <DialogContentText>
Escolha uma opção de relatório abaixo {i18n.t('dashboard.titles.dialogContentText')}
</DialogContentText> </DialogContentText>
<Box <Box

View File

@ -3,8 +3,8 @@ import React, { useState, useEffect, useRef, useContext } from "react"
import * as Yup from "yup" import * as Yup from "yup"
import { Formik, Form, Field } from "formik" import { Formik, Form, Field } from "formik"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from "../../services/socket"
import { import {
makeStyles, makeStyles,
@ -91,9 +91,9 @@ const StatusChatEndModal = ({
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsStatusChatEndModal = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -102,10 +102,12 @@ const StatusChatEndModal = ({
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsStatusChatEndModal)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsStatusChatEndModal)
} }
}, []) }, [])

View File

@ -2,7 +2,9 @@ import React, { useState, useEffect } from "react"
import { useParams, useHistory } from "react-router-dom" import { useParams, useHistory } from "react-router-dom"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import clsx from "clsx" import clsx from "clsx"
import { Paper, makeStyles } from "@material-ui/core" import { Paper, makeStyles } from "@material-ui/core"
@ -132,22 +134,32 @@ const Ticket = () => {
}, []) }, [])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("connect", () => socket.emit("joinChatBox", ticketId)) const onConnectTicket = () => socket.emit("joinChatBox", ticketId)
onConnectTicket()
socket.on("connect", onConnectTicket)
const onTicketTicket = (data) => {
const isSameTicket = +data?.ticket?.id === +ticketId || +data.ticketId === +ticketId
if (!isSameTicket) return
socket.on("ticket", (data) => {
if (data.action === "update") { if (data.action === "update") {
setTicket(data.ticket) setTicket(data.ticket)
} }
if (data.action === "delete") { if (data.action === "deleteForever") {
console.log('delete forever')
toast.success("Ticket deleted sucessfully.") toast.success("Ticket deleted sucessfully.")
history.push("/tickets") history.push("/tickets")
} }
}) }
socket.on("contact", (data) => { socket.on("ticket", onTicketTicket)
const onContactTicket = (data) => {
if (data.action === "update") { if (data.action === "update") {
setContact((prevState) => { setContact((prevState) => {
if (prevState.id === data.contact?.id) { if (prevState.id === data.contact?.id) {
@ -156,9 +168,11 @@ const Ticket = () => {
return prevState return prevState
}) })
} }
}) }
socket.on("remoteTickesControllIdleOpen", (data) => { socket.on("contact", onContactTicket)
const onRemoteTicketsControllIdleOpenTicket = (data) => {
if (data.action === "update") { if (data.action === "update") {
let url_ticketId let url_ticketId
try { try {
@ -175,10 +189,15 @@ const Ticket = () => {
console.log('error on try do the send seen: ', error) console.log('error on try do the send seen: ', error)
} }
} }
}) }
socket.on("remoteTickesControllIdleOpen", onRemoteTicketsControllIdleOpenTicket)
return () => { return () => {
socket.disconnect() socket.off("connect", onConnectTicket)
socket.off("ticket", onTicketTicket)
socket.off("contact", onContactTicket)
socket.off("remoteTickesControllIdleOpen", onRemoteTicketsControllIdleOpenTicket)
} }
}, [ticketId, history]) }, [ticketId, history])

View File

@ -22,7 +22,8 @@ import MarkdownWrapper from "../MarkdownWrapper"
import { Tooltip } from "@material-ui/core" import { Tooltip } from "@material-ui/core"
import { AuthContext } from "../../context/Auth/AuthContext" import { AuthContext } from "../../context/Auth/AuthContext"
import toastError from "../../errors/toastError" import toastError from "../../errors/toastError"
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from "../../services/socket"
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles(theme => ({
ticket: { ticket: {
@ -151,17 +152,18 @@ const TicketListItem = ({ ticket, remoteTicketsControll, settings }) => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('remoteTickesControll', (data) => { const onRemoteTickesControllTicketListItem = (data) => {
console.log('REMOTE TICKETS CONTROLL UPDATE2: ', data.tickets) console.log('REMOTE TICKETS CONTROLL UPDATE2: ', data.tickets)
if (data.action === 'update') { if (data.action === 'update') {
setRemoteTicketsControll(data.tickets) setRemoteTicketsControll(data.tickets)
} }
}) }
socket.on('remoteTickesControll', onRemoteTickesControllTicketListItem)
socket.on('settings', (data) => { const onSettingsTicketListItem = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -170,11 +172,13 @@ const TicketListItem = ({ ticket, remoteTicketsControll, settings }) => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsTicketListItem)
return () => { return () => {
socket.disconnect() socket.off('remoteTickesControll', onRemoteTickesControllTicketListItem);
socket.off('settings', onSettingsTicketListItem);
} }
}, []) }, [])

View File

@ -1,7 +1,7 @@
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 { socket } from "../../services/socket"
import { makeStyles } from "@material-ui/core/styles" import { makeStyles } from "@material-ui/core/styles"
import List from "@material-ui/core/List" import List from "@material-ui/core/List"
@ -243,27 +243,29 @@ const TicketsList = (props) => {
// if (tab=='search')return // if (tab=='search')return
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
const shouldUpdateTicket = ticket => const shouldUpdateTicket = ticket =>
(status === ticket.status) &&
(!ticket.userId || ticket.userId === user?.id || showAll) && (!ticket.userId || ticket.userId === user?.id || showAll) &&
(!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1) (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1)
const notBelongsToUserQueues = ticket => const notBelongsToUserQueues = ticket =>
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1 ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1
socket.on("connect", () => { const onConnectTicketList = () => {
if (status) { if (status) {
socket.emit("joinTickets", status) socket.emit("joinTickets", status)
} else { } else {
socket.emit("joinNotification") socket.emit("joinNotification")
} }
}
}) onConnectTicketList()
socket.on("connect", onConnectTicketList)
const onTicketTicketList = data => {
socket.on("ticket", data => {
if (data.action === "updateUnread") { if (data.action === "updateUnread") {
@ -292,10 +294,11 @@ const TicketsList = (props) => {
if (data.action === "delete") { if (data.action === "delete") {
dispatch({ type: "DELETE_TICKET", payload: data.ticketId }) dispatch({ type: "DELETE_TICKET", payload: data.ticketId })
} }
}) }
socket.on("ticket", onTicketTicketList)
socket.on("appMessage", data => { const onAppMessageTicketList = data => {
if (data.action === "create" && shouldUpdateTicket(data.ticket)) { if (data.action === "create" && shouldUpdateTicket(data.ticket)) {
@ -307,7 +310,9 @@ const TicketsList = (props) => {
payload: data, payload: data,
}) })
} }
}) }
socket.on("appMessage", onAppMessageTicketList)
socket.on("contact", data => { socket.on("contact", data => {
if (data.action === "update") { if (data.action === "update") {
@ -318,16 +323,16 @@ const TicketsList = (props) => {
} }
}) })
const onRemoteTickesControllTicketList = (data) => {
socket.on('remoteTickesControll', (data) => {
console.log('REMOTE TICKETS CONTROLL UPDATE 1: ', data.tickets) console.log('REMOTE TICKETS CONTROLL UPDATE 1: ', data.tickets)
if (data.action === 'update') { if (data.action === 'update') {
setRemoteTicketsControll(data.tickets) setRemoteTicketsControll(data.tickets)
} }
}) }
socket.on('remoteTickesControll', onRemoteTickesControllTicketList)
socket.on('settings', (data) => { const onSettingsTicketList = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -336,11 +341,17 @@ const TicketsList = (props) => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsTicketList)
return () => { return () => {
socket.disconnect() socket.off("ticket", onTicketTicketList)
socket.off('appMessage', onAppMessageTicketList);
socket.removeAllListeners("contact")
socket.off('connect', onConnectTicketList);
socket.off('settings', onSettingsTicketList);
socket.off('remoteTickesControll', onRemoteTickesControllTicketList);
} }
}, [status, showAll, user, selectedQueueIds, tab]) }, [status, showAll, user, selectedQueueIds, tab])
@ -349,7 +360,7 @@ const TicketsList = (props) => {
if (typeof updateCount === "function") { if (typeof updateCount === "function") {
updateCount(ticketsList.length) updateCount(ticketsList.length)
} }
if (ticketsList && status === "pending"){ if (ticketsList && status === "pending") {
setTickets(ticketsList) setTickets(ticketsList)
} }
// else{ // else{

View File

@ -19,7 +19,6 @@ import FindInPageIcon from '@material-ui/icons/FindInPage'
import FormControlLabel from "@material-ui/core/FormControlLabel" import FormControlLabel from "@material-ui/core/FormControlLabel"
import Switch from "@material-ui/core/Switch" import Switch from "@material-ui/core/Switch"
import openSocket from "socket.io-client"
import NewTicketModal from "../NewTicketModal" import NewTicketModal from "../NewTicketModal"
import TicketsList from "../TicketsList" import TicketsList from "../TicketsList"
@ -153,7 +152,7 @@ const TicketsManager = () => {
const [openCount, setOpenCount] = useState(0) const [openCount, setOpenCount] = useState(0)
const [pendingCount, setPendingCount] = useState(0) const [pendingCount, setPendingCount] = useState(0)
const userQueueIds = user.queues.map((q) => q.id) const userQueueIds = user?.queues?.map((q) => q?.id)
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []) const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || [])
const [showContentSearch, setShowContentSearch] = useState(false) const [showContentSearch, setShowContentSearch] = useState(false)
@ -178,9 +177,9 @@ const TicketsManager = () => {
}, [setting]) }, [setting])
useEffect(() => { useEffect(() => {
if (user.profile.toUpperCase() === "ADMIN" || if (user?.profile?.toUpperCase() === "ADMIN" ||
user.profile.toUpperCase() === "SUPERVISOR" || user?.profile?.toUpperCase() === "SUPERVISOR" ||
user.profile.toUpperCase() === "MASTER") { user?.profile?.toUpperCase() === "MASTER") {
setShowAllTickets(true) setShowAllTickets(true)
} }
// eslint-disable-next-line react-hooks/exhaustive-deps // eslint-disable-next-line react-hooks/exhaustive-deps
@ -193,21 +192,21 @@ const TicketsManager = () => {
setTabOption(tab) setTabOption(tab)
}, [tab, setTabOption]) }, [tab, setTabOption])
useEffect(() => { useEffect(() => {
if (settings?.length > 0 && getSettingValue('waitingTimeTickets') !== 'enabled') return if (settings?.length > 0 && getSettingValue('waitingTimeTickets') !== 'enabled') return
const calculateAverageTime = () => { const calculateAverageTime = () => {
if (tickets.length > 0) { if (tickets.length > 0) {
const now = new Date() const now = new Date()
const differenceTime = tickets?.map(ticket => { const differenceTime = tickets?.map(ticket => {
const createdAt = new Date(ticket.createdAt) const createdAt = new Date(ticket?.createdAt)
const difference = now - createdAt const difference = now - createdAt
return difference return difference
}) })
const sumDifferences = differenceTime.reduce((total, difference) => total + difference, 0) const sumDifferences = differenceTime?.reduce((total, difference) => total + difference, 0)
const averageTimeMilliseconds = sumDifferences / tickets?.length const averageTimeMilliseconds = sumDifferences / tickets?.length
let hours = Math.floor(averageTimeMilliseconds / 3600000) let hours = Math.floor(averageTimeMilliseconds / 3600000)
const minutes = Math.floor((averageTimeMilliseconds % 3600000) / 60000) const minutes = Math.floor((averageTimeMilliseconds % 3600000) / 60000)
@ -222,10 +221,10 @@ const TicketsManager = () => {
} else return '00:00' } else return '00:00'
} }
setWaitingTime(calculateAverageTime()) setWaitingTime(calculateAverageTime())
const intervalId = setInterval(() => { const intervalId = setInterval(() => {
setWaitingTime(calculateAverageTime()) setWaitingTime(calculateAverageTime())
}, 10000) }, 10000)
return () => clearInterval(intervalId) return () => clearInterval(intervalId)

View File

@ -1,6 +1,7 @@
import React, { useState, useContext, useMemo, useEffect } from "react" import React, { useState, useContext, useMemo, useEffect } from "react"
import { useHistory } from "react-router-dom" import { useHistory } from "react-router-dom"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import Button from "@material-ui/core/Button" import Button from "@material-ui/core/Button"
import Dialog from "@material-ui/core/Dialog" import Dialog from "@material-ui/core/Dialog"
@ -120,9 +121,9 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsTransferTicketModal = (data) => {
console.log('settings updated ----------------------------xxxxxxxxxxxx') console.log('settings updated ----------------------------xxxxxxxxxxxx')
if (data.action === 'update') { if (data.action === 'update') {
@ -133,10 +134,12 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsTransferTicketModal)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsTransferTicketModal)
} }
}, []) }, [])
@ -253,7 +256,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
{i18n.t("transferTicketModal.title")} {i18n.t("transferTicketModal.title")}
</DialogTitle> </DialogTitle>
<DialogContent dividers > <DialogContent dividers >
<FormControl variant="outlined" className={classes.maxWidth} style={{marginBottom: '8px'}}> <FormControl variant="outlined" className={classes.maxWidth} style={{ marginBottom: '8px' }}>
{/* <InputLabel>{i18n.t("transferTicketModal.fieldQueueLabel")}</InputLabel> */} {/* <InputLabel>{i18n.t("transferTicketModal.fieldQueueLabel")}</InputLabel> */}
<InputLabel>{'Usuário'}</InputLabel> <InputLabel>{'Usuário'}</InputLabel>
@ -272,7 +275,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
</MenuItem> </MenuItem>
))} ))}
</Select> </Select>
</FormControl> </FormControl>
<FormControl variant="outlined" className={classes.maxWidth}> <FormControl variant="outlined" className={classes.maxWidth}>
<InputLabel>{i18n.t("transferTicketModal.fieldQueuePlaceholder")}</InputLabel> <InputLabel>{i18n.t("transferTicketModal.fieldQueuePlaceholder")}</InputLabel>
@ -284,7 +287,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
required required
> >
<MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem> <MenuItem style={{ background: "white", }} value={''}>&nbsp;</MenuItem>
{queues.map((queue) => ( {queues?.map((queue) => (
<MenuItem <MenuItem
key={queue.id} key={queue.id}
value={queue.id} value={queue.id}

View File

@ -261,8 +261,8 @@ const UserModal = ({ open, onClose, userId, }) => {
fullWidth fullWidth
/> />
<div className={classes.multFieldLine}> <div className={classes.multFieldLine}>
<label style={{display: 'flex', alignItems:'center'}}> <label style={{display: 'flex', alignItems:'center'}}>
Transferir para outras filas {i18n.t('dashboard.titles.transfer')}
<Switch <Switch
name= 'transferToOtherQueues' name= 'transferToOtherQueues'
checked={checked} checked={checked}

View File

@ -1,6 +1,7 @@
import { useState, useEffect } from 'react' import { useState, useEffect } from 'react'
import { useHistory } from 'react-router-dom' import { useHistory } from 'react-router-dom'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from '../../services/socket'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
@ -62,19 +63,19 @@ const useAuth = () => {
useEffect(() => { useEffect(() => {
const token = localStorage.getItem('token') const token = localStorage.getItem('token')
;(async () => { ; (async () => {
if (token) { if (token) {
try { try {
const { data } = await api.post('/auth/refresh_token') const { data } = await api.post('/auth/refresh_token')
api.defaults.headers.Authorization = `Bearer ${data.token}` api.defaults.headers.Authorization = `Bearer ${data.token}`
setIsAuth(true) setIsAuth(true)
setUser(data.user) setUser(data.user)
} catch (err) { } catch (err) {
toastError(err) toastError(err)
}
} }
} setLoading(false)
setLoading(false) })()
})()
}, []) }, [])
useEffect(() => { useEffect(() => {
@ -90,7 +91,7 @@ const useAuth = () => {
}, []) }, [])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('user', (data) => { socket.on('user', (data) => {
if (data.action === 'update' && data.user.id === user.id) { if (data.action === 'update' && data.user.id === user.id) {
@ -98,7 +99,7 @@ const useAuth = () => {
} }
}) })
socket.on('settings', (data) => { const onSettingsAuth = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSetting((prevState) => { setSetting((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -107,10 +108,13 @@ const useAuth = () => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsAuth)
return () => { return () => {
socket.disconnect() socket.removeAllListeners('user');
socket.off('settings', onSettingsAuth);
} }
}, [user]) }, [user])
@ -135,7 +139,7 @@ const useAuth = () => {
const handleLogout = async () => { const handleLogout = async () => {
setLoading(true) setLoading(true)
try { try {
await api.delete(`/auth/logout/${user.id}`); await api.delete(`/auth/logout/${user.id}`);
setIsAuth(false) setIsAuth(false)

View File

@ -1,5 +1,7 @@
import { useState, useEffect, useReducer } from "react"; import { useState, useEffect, useReducer } from "react";
import openSocket from "socket.io-client"; //import openSocket from "socket.io-client";
import { socket } from "../../services/socket";
import toastError from "../../errors/toastError"; import toastError from "../../errors/toastError";
@ -30,7 +32,7 @@ const reducer = (state, action) => {
if (whatsAppIndex !== -1) { if (whatsAppIndex !== -1) {
if ('disabled' in whatsApp) { if ('disabled' in whatsApp) {
state[whatsAppIndex].disabled = whatsApp.disabled state[whatsAppIndex].disabled = whatsApp.disabled
} }
else { else {
@ -126,7 +128,7 @@ const useWhatsApps = () => {
}, []); }, []);
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); //const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("whatsapp", data => { socket.on("whatsapp", data => {
if (data.action === "update") { if (data.action === "update") {
@ -158,7 +160,9 @@ const useWhatsApps = () => {
return () => { return () => {
socket.disconnect(); socket.removeAllListeners('whatsapp');
socket.removeAllListeners('whatsappSession');
socket.removeAllListeners('whatsappSessionMonit');
}; };
}, []); }, []);

View File

@ -31,7 +31,8 @@ import { i18n } from '../translate/i18n'
import { WhatsAppsContext } from '../context/WhatsApp/WhatsAppsContext' import { WhatsAppsContext } from '../context/WhatsApp/WhatsAppsContext'
import { AuthContext } from '../context/Auth/AuthContext' import { AuthContext } from '../context/Auth/AuthContext'
import { Can } from '../components/Can' import { Can } from '../components/Can'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from '../services/socket'
import api from '../services/api' import api from '../services/api'
@ -118,9 +119,9 @@ const MainListItems = (props) => {
} }
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsMainListItem = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -129,10 +130,12 @@ const MainListItems = (props) => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsMainListItem)
return () => { return () => {
socket.disconnect() socket.off('settings', onSettingsMainListItem);
} }
}, []) }, [])
@ -189,7 +192,7 @@ const MainListItems = (props) => {
/> />
<ListItemLink <ListItemLink
to="/report" to="/report"
primary="Relatório" primary={i18n.t("mainDrawer.listItems.reports")}
icon={<ReportOutlinedIcon />} icon={<ReportOutlinedIcon />}
/> />
</> </>
@ -242,7 +245,7 @@ const MainListItems = (props) => {
(getSettingValue('hasCampaign') === 'enabled' || user.profile === 'master') && ( (getSettingValue('hasCampaign') === 'enabled' || user.profile === 'master') && (
<ListItemLink <ListItemLink
to="/campaign" to="/campaign"
primary="Campanha" primary={i18n.t("mainDrawer.listItems.campaign") || 'Campaign'}
icon={<CampaignIcon />} icon={<CampaignIcon />}
/> />
) )

View File

@ -1,7 +1,8 @@
import React, { useState, useCallback, useEffect, useReducer, useContext } from 'react' import React, { useState, useCallback, useEffect, useReducer, useContext } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from '../../services/socket'
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'
@ -13,11 +14,11 @@ import {
IconButton, IconButton,
Table, Table,
TableHead, TableHead,
Paper, Paper,
} from '@material-ui/core' } from '@material-ui/core'
import { import {
Edit, Edit,
DeleteOutline, DeleteOutline,
// Restore // Restore
} from '@material-ui/icons' } from '@material-ui/icons'
@ -26,7 +27,7 @@ import MainHeader from '../../components/MainHeader'
import MainHeaderButtonsWrapper from '../../components/MainHeaderButtonsWrapper' import MainHeaderButtonsWrapper from '../../components/MainHeaderButtonsWrapper'
import Title from '../../components/Title' import Title from '../../components/Title'
import TableRowSkeleton from '../../components/TableRowSkeleton' import TableRowSkeleton from '../../components/TableRowSkeleton'
import CampaignModal from '../../components/CampaignModal' import CampaignModal from '../../components/CampaignModal'
import ConfirmationModal from '../../components/ConfirmationModal' import ConfirmationModal from '../../components/ConfirmationModal'
import QrcodeModal from '../../components/QrcodeModal' import QrcodeModal from '../../components/QrcodeModal'
@ -51,7 +52,7 @@ const reducer = (state, action) => {
const campaigns = action.payload const campaigns = action.payload
return [...state, ...campaigns] return [...state, ...campaigns]
} }
if (action.type === "UPDATE_CAMPAIGNS") { if (action.type === "UPDATE_CAMPAIGNS") {
const campaign = action.payload const campaign = action.payload
const campaignIndex = state.findIndex((c) => c.id === campaign.id) const campaignIndex = state.findIndex((c) => c.id === campaign.id)
@ -109,7 +110,7 @@ const useStyles = makeStyles((theme) => ({
buttonProgress: { buttonProgress: {
color: green[500], color: green[500],
}, },
})) }))
const Campaign = () => { const Campaign = () => {
//-------- //--------
@ -124,7 +125,7 @@ const Campaign = () => {
const [qrModalOpen, setQrModalOpen] = useState(false) const [qrModalOpen, setQrModalOpen] = useState(false)
const [selectedCampaign, setSelectedCampaign] = useState(null) const [selectedCampaign, setSelectedCampaign] = useState(null)
const [confirmModalOpen, setConfirmModalOpen] = useState(false) const [confirmModalOpen, setConfirmModalOpen] = useState(false)
const [campaigns, dispatch] = useReducer(reducer, []) const [campaigns, dispatch] = useReducer(reducer, [])
const [loading, setLoading] = useState(true) const [loading, setLoading] = useState(true)
@ -152,7 +153,7 @@ const Campaign = () => {
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchContacts = async () => { const fetchContacts = async () => {
try { try {
const { data } = await apiBroker.get('/campaign', { const { data } = await apiBroker.get('/campaign', {
params: { params: {
@ -160,7 +161,7 @@ const Campaign = () => {
baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE, baseURL: process.env.REACT_APP_BACKEND_URL_PRIVATE,
identifier: 'campaign' identifier: 'campaign'
} }
}) })
dispatch({ type: "LOAD_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "LOAD_CAMPAIGNS", payload: data.campaign })
setLoading(false) setLoading(false)
@ -174,7 +175,7 @@ const Campaign = () => {
}, 500) }, 500)
return () => clearTimeout(delayDebounceFn) return () => clearTimeout(delayDebounceFn)
}, [user.id]) }, [user.id])
const handleOpenCampaignModal = () => { const handleOpenCampaignModal = () => {
setSelectedCampaign(null) setSelectedCampaign(null)
@ -184,12 +185,12 @@ const Campaign = () => {
const handleCloseCampaignModal = useCallback(() => { const handleCloseCampaignModal = useCallback(() => {
setCampaignModalOpen(false) setCampaignModalOpen(false)
setSelectedCampaign(null) setSelectedCampaign(null)
}, [setSelectedCampaign, setCampaignModalOpen]) }, [setSelectedCampaign, setCampaignModalOpen])
const handleStart = async (campaign) => { const handleStart = async (campaign) => {
try { try {
const { data } = await apiBroker.post(`/campaign/start/${campaign.id}`) const { data } = await apiBroker.post(`/campaign/start/${campaign.id}`)
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
@ -200,10 +201,10 @@ const Campaign = () => {
} }
} }
const handleStop = async (campaign) => { const handleStop = async (campaign) => {
try { try {
const { data } = await apiBroker.post(`/campaign/stop/${campaign.id}`) const { data } = await apiBroker.post(`/campaign/stop/${campaign.id}`)
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
@ -245,7 +246,7 @@ const Campaign = () => {
setConfirmModalOpen(true) setConfirmModalOpen(true)
} }
const handleSubmitConfirmationModal = async () => { const handleSubmitConfirmationModal = async () => {
if (confirmModalInfo.action === 'delete') { if (confirmModalInfo.action === 'delete') {
try { try {
@ -293,13 +294,12 @@ const Campaign = () => {
)} )}
</> </>
) )
} }
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
const onContactsBulkCampaing = (data) => {
socket.on("contactsBulkInsertOnQueueStatus", (data) => {
if (data.action === 'update') { if (data.action === 'update') {
if (String(data.insertOnQueue.adminId) === String(user.id)) { if (String(data.insertOnQueue.adminId) === String(user.id)) {
@ -312,19 +312,24 @@ const Campaign = () => {
} }
} }
}) }
socket.on('campaign', (data) => { socket.on("contactsBulkInsertOnQueueStatus", onContactsBulkCampaing)
const onCampaignCampaign = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign }) dispatch({ type: "UPDATE_CAMPAIGNS", payload: data.campaign })
} }
}) }
socket.on('campaign', onCampaignCampaign)
return () => { return () => {
socket.disconnect() socket.off("campaign", onCampaignCampaign)
socket.off("contactsBulkInsertOnQueueStatus", onContactsBulkCampaing)
} }
}, [user.id]) }, [user.id])

View File

@ -2,7 +2,8 @@ import React, { useState, useCallback, useEffect, useContext } from 'react'
import { toast } from 'react-toastify' import { toast } from 'react-toastify'
import { format, parseISO } from 'date-fns' import { format, parseISO } from 'date-fns'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from '../../services/socket'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import { green, red, yellow, grey } from '@material-ui/core/colors' import { green, red, yellow, grey } from '@material-ui/core/colors'
@ -144,7 +145,7 @@ const Connections = () => {
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null) const [selectedWhatsApp, setSelectedWhatsApp] = useState(null)
const [confirmModalOpen, setConfirmModalOpen] = useState(false) const [confirmModalOpen, setConfirmModalOpen] = useState(false)
const [diskSpaceInfo, setDiskSpaceInfo] = useState({}) const [diskSpaceInfo, setDiskSpaceInfo] = useState({})
const [settings, setSettings] = useState([]) const [settings, setSettings] = useState([])
@ -388,7 +389,7 @@ const Connections = () => {
getSettingValue('whatsaAppCloudApi') === 'disabled') && ( getSettingValue('whatsaAppCloudApi') === 'disabled') && (
<CustomToolTip title={'Whatsapp Cloud API está desativado'}> <CustomToolTip title={'Whatsapp Cloud API está desativado'}>
<FiberManualRecord style={{ color: grey[500] }} /> <FiberManualRecord style={{ color: grey[500] }} />
</CustomToolTip> </CustomToolTip>
)} )}
{whatsApp.status === 'OPENING' && !whatsApp.isOfficial && ( {whatsApp.status === 'OPENING' && !whatsApp.isOfficial && (
<CircularProgress size={24} className={classes.buttonProgress} /> <CircularProgress size={24} className={classes.buttonProgress} />
@ -433,7 +434,7 @@ const Connections = () => {
try { try {
await api.post(`/restartwhatsappsession/0`, { await api.post(`/restartwhatsappsession/0`, {
params: { status: 'status' }, params: { status: 'status' },
}) })
setClicks((buttons) => setClicks((buttons) =>
buttons.map((e) => { buttons.map((e) => {
@ -451,15 +452,17 @@ const Connections = () => {
}, []) }, [])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('diskSpaceMonit', (data) => { const onDiskSpaceMonitConnections = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setDiskSpaceInfo(data.diskSpace) setDiskSpaceInfo(data.diskSpace)
} }
}) }
socket.on('settings', (data) => { socket.on('diskSpaceMonit', onDiskSpaceMonitConnections)
const onSettingsConnections = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -468,10 +471,13 @@ const Connections = () => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsConnections)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsConnections)
socket.off("diskSpaceMonit", onDiskSpaceMonitConnections)
} }
}, []) }, [])

View File

@ -1,5 +1,6 @@
import React, { useState, useEffect, useReducer, useCallback, useContext } from "react" import React, { useState, useEffect, useReducer, useCallback, useContext } from "react"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import { useHistory } from "react-router-dom" import { useHistory } from "react-router-dom"
@ -260,9 +261,9 @@ const Contacts = () => {
}, [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) => { const onContactsBulkContacts = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
if (String(data.insertOnQueue.adminId) === String(user.id)) { if (String(data.insertOnQueue.adminId) === String(user.id)) {
@ -278,9 +279,11 @@ const Contacts = () => {
} }
} }
}) }
socket.on("contact", (data) => { socket.on("contactsBulkInsertOnQueueStatus", onContactsBulkContacts)
const onContactContacts = (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 })
} }
@ -288,10 +291,13 @@ const Contacts = () => {
if (data.action === "delete") { if (data.action === "delete") {
dispatch({ type: "DELETE_CONTACT", payload: +data.contactId }) dispatch({ type: "DELETE_CONTACT", payload: +data.contactId })
} }
}) }
socket.on("contact", onContactContacts)
return () => { return () => {
socket.disconnect() socket.off("contact", onContactContacts)
socket.off("contactsBulkInsertOnQueueStatus", onContactsBulkContacts)
} }
}, [user, history]) }, [user, history])
@ -536,7 +542,7 @@ const Contacts = () => {
open={contactModalOpen} open={contactModalOpen}
onClose={handleCloseContactModal} onClose={handleCloseContactModal}
aria-labelledby="form-dialog-title" aria-labelledby="form-dialog-title"
contactId={selectedContactId} contactId={selectedContactId}
></ContactModal> ></ContactModal>
<ContactCreateTicketModal <ContactCreateTicketModal
modalOpen={isCreateTicketModalOpen} modalOpen={isCreateTicketModalOpen}

View File

@ -5,6 +5,8 @@ import { PieChart as RechartsPieChart, Pie, Cell, ResponsiveContainer, Tooltip }
import Title from './Title'; import Title from './Title';
import { i18n } from "../../translate/i18n";
const generateDataExample = (amount) => { const generateDataExample = (amount) => {
const arr = [] const arr = []
for (let i = 1; i <= amount; i++) { for (let i = 1; i <= amount; i++) {
@ -76,7 +78,7 @@ const PieChart = ({ data = dataExample }) => {
> >
<Box width="100%" height="100%" position="sticky" top="0" zIndex={1000}> <Box width="100%" height="100%" position="sticky" top="0" zIndex={1000}>
<Box sx={{ position: "absolute" }}> <Box sx={{ position: "absolute" }}>
<Title>Tickets encerramento</Title> <Title>{i18n.t('dashboard.titles.title')}</Title>
</Box> </Box>
<ResponsiveContainer width="100%" height="100%"> <ResponsiveContainer width="100%" height="100%">
<RechartsPieChart width={400} height={400}> <RechartsPieChart width={400} height={400}>

View File

@ -14,10 +14,11 @@ import Info from "@material-ui/icons/Info"
import SelectField from "../../components/Report/SelectField" import SelectField from "../../components/Report/SelectField"
import { AuthContext } from "../../context/Auth/AuthContext" import { AuthContext } from "../../context/Auth/AuthContext"
// import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import Chart from "./Chart" import Chart from "./Chart"
import PieChart from "./PieChart" import PieChart from "./PieChart"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import api from "../../services/api" import api from "../../services/api"
import { Can } from "../../components/Can" import { Can } from "../../components/Can"
@ -291,7 +292,7 @@ const Dashboard = () => {
const { data } = await api.get("/reports/user/services", { const { data } = await api.get("/reports/user/services", {
params: { userId: null, startDate: dateToday, endDate: dateToday, userQueues: selectedQueue }, params: { userId: null, startDate: dateToday, endDate: dateToday, userQueues: selectedQueue },
}) })
dispatch({ type: "RESET" }) dispatch({ type: "RESET" })
dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }) dispatch({ type: "LOAD_QUERY", payload: data.usersProfile })
@ -301,7 +302,7 @@ const Dashboard = () => {
}) })
setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd) setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd)
} catch (err) { } catch (err) {
} }
@ -356,46 +357,54 @@ const Dashboard = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("ticketStatus", (data) => { const onTicketsStatusDashboard = (data) => {
if (data.action === "update") { if (data.action === "update") {
setStatus("") setStatus("")
setStatus(data.ticketStatus.status) setStatus(data.ticketStatus.status)
} }
}) }
socket.on("onlineStatus", (data) => { socket.on("ticketStatus", onTicketsStatusDashboard)
const onOnlineStatusDashboard = (data) => {
if (data.action === "logout" || data.action === "update") { if (data.action === "logout" || data.action === "update") {
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }) dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime })
} else if (data.action === "delete") { } else if (data.action === "delete") {
dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }) dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime })
} }
}) }
socket.on("user", (data) => { socket.on("onlineStatus", onOnlineStatusDashboard)
const onUserDashboard = (data) => {
if (data.action === "delete") { if (data.action === "delete") {
dispatch({ type: "DELETE_USER", payload: +data.userId }) dispatch({ type: "DELETE_USER", payload: +data.userId })
} }
}) }
socket.on("user", onUserDashboard)
return () => { return () => {
socket.disconnect() socket.off("user", onUserDashboard)
socket.off("onlineStatus", onOnlineStatusDashboard)
socket.off("ticketStatus", onTicketsStatusDashboard)
} }
}, []) }, [])
const handleSelectedQueue = useCallback((queueSelected) => { const handleSelectedQueue = useCallback((queueSelected) => {
if(queueSelected !== 'All'){ if (queueSelected !== 'All') {
const queueIndex = user?.queues?.findIndex((q) => q.id === parseInt(queueSelected)); const queueIndex = user?.queues?.findIndex((q) => q.id === parseInt(queueSelected));
const queueIds = [] const queueIds = []
queueIds.push(user?.queues[queueIndex]?.id); queueIds.push(user?.queues[queueIndex]?.id);
setSelectedQueue(queueIds); setSelectedQueue(queueIds);
}else{ } else {
const queueIds = user?.queues?.map((queue) => queue.id); const queueIds = user?.queues?.map((queue) => queue.id);
setSelectedQueue(queueIds); setSelectedQueue(queueIds);
} }
},[user, setSelectedQueue]) }, [user, setSelectedQueue])
useEffect(() => { useEffect(() => {
if (ticketStatusChange === "") return if (ticketStatusChange === "") return
@ -472,7 +481,7 @@ const Dashboard = () => {
func={handleSelectedQueue} func={handleSelectedQueue}
textBoxFieldSelected={'All'} textBoxFieldSelected={'All'}
emptyField={false} emptyField={false}
header={'Filas'} header={i18n.t('dashboard.titles.selectQueues')}
currencies={user.queues.map((obj) => { currencies={user.queues.map((obj) => {
return { 'value': obj.id, 'label': obj.name } return { 'value': obj.id, 'label': obj.name }
})} /> })} />
@ -485,7 +494,7 @@ const Dashboard = () => {
variant="outlined" variant="outlined"
> >
<Typography component="h3" variant="h6" color="primary" paragraph> <Typography component="h3" variant="h6" color="primary" paragraph>
Aguardando {i18n.t('dashboard.titles.waiting')}
</Typography> </Typography>
<Grid item> <Grid item>
<Typography component="h1" variant="h4"> <Typography component="h1" variant="h4">
@ -501,7 +510,7 @@ const Dashboard = () => {
variant="outlined" variant="outlined"
> >
<Typography component="h3" variant="h6" color="primary" style={{ marginBottom: "0" }} paragraph> <Typography component="h3" variant="h6" color="primary" style={{ marginBottom: "0" }} paragraph>
Em Atendimento {i18n.t('dashboard.titles.inService')}
</Typography> </Typography>
<Typography paragraph style={{ fontSize: "12px", margin: "0px" }}>Hoje/Todo Periodo</Typography> <Typography paragraph style={{ fontSize: "12px", margin: "0px" }}>Hoje/Todo Periodo</Typography>
@ -520,7 +529,7 @@ const Dashboard = () => {
variant="outlined" variant="outlined"
> >
<Typography component="h3" variant="h6" color="primary" paragraph> <Typography component="h3" variant="h6" color="primary" paragraph>
Fechados {i18n.t('dashboard.titles.ticketsClosed')}
</Typography> </Typography>
<Grid item> <Grid item>
<Typography component="h1" variant="h4"> <Typography component="h1" variant="h4">
@ -532,7 +541,7 @@ const Dashboard = () => {
<Grid item container spacing={3}> <Grid item container spacing={3}>
<Grid item xs={12} sm={12} md={6} lg={6}> <Grid item xs={12} sm={12} md={6} lg={6}>
<Paper className={classes.fixedHeightPaper} variant="outlined"> <Paper className={classes.fixedHeightPaper} variant="outlined">
<Chart allTickets={usersOnlineInfo} selectedQueue = {selectedQueue}/> <Chart allTickets={usersOnlineInfo} selectedQueue={selectedQueue} />
</Paper> </Paper>
</Grid> </Grid>
<Grid item xs={12} sm={12} md={6} lg={6}> <Grid item xs={12} sm={12} md={6} lg={6}>
@ -551,7 +560,7 @@ const Dashboard = () => {
color="primary" color="primary"
style={{ marginBottom: "16px" }} style={{ marginBottom: "16px" }}
> >
Usuários {i18n.t('dashboard.titles.users')}
<Tooltip <Tooltip
title={`Os dados informados abaixo é baseado na data: ${new Date().toLocaleDateString( title={`Os dados informados abaixo é baseado na data: ${new Date().toLocaleDateString(
"pt-BR", "pt-BR",

View File

@ -1,5 +1,6 @@
import React, { useState, useContext, useEffect, useReducer } from "react" import React, { useState, useContext, useEffect, useReducer } from "react"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import { import {
Button, Button,
@ -125,9 +126,9 @@ const Position = () => {
}, [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("position", (data) => { const onPositionPosition = (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {
dispatch({ type: "UPDATE_POSITIONS", payload: data.position }) dispatch({ type: "UPDATE_POSITIONS", payload: data.position })
} }
@ -138,10 +139,12 @@ const Position = () => {
payload: +data.positionId, payload: +data.positionId,
}) })
} }
}) }
socket.on("position", onPositionPosition)
return () => { return () => {
socket.disconnect() socket.off("position", onPositionPosition)
} }
}, []) }, [])

View File

@ -1,6 +1,7 @@
import React, { useEffect, useReducer, useState, useContext } from 'react' import React, { useEffect, useReducer, useState, useContext } from 'react'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from '../../services/socket'
import { import {
Button, Button,
@ -91,7 +92,7 @@ const reducer = (state, action) => {
const Queues = () => { const Queues = () => {
const classes = useStyles() const classes = useStyles()
const { user, setting, getSettingValue } = useContext(AuthContext) const { user, setting, getSettingValue } = useContext(AuthContext)
const [queues, dispatch] = useReducer(reducer, []) const [queues, dispatch] = useReducer(reducer, [])
const [loading, setLoading] = useState(false) const [loading, setLoading] = useState(false)
@ -120,12 +121,12 @@ const Queues = () => {
setLoading(false) setLoading(false)
} }
})() })()
}, []) }, [])
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('queue', (data) => { const onQueueQueues = (data) => {
if (data.action === 'update' || data.action === 'create') { if (data.action === 'update' || data.action === 'create') {
dispatch({ type: 'UPDATE_QUEUES', payload: data.queue }) dispatch({ type: 'UPDATE_QUEUES', payload: data.queue })
} }
@ -133,9 +134,11 @@ const Queues = () => {
if (data.action === 'delete') { if (data.action === 'delete') {
dispatch({ type: 'DELETE_QUEUE', payload: data.queueId }) dispatch({ type: 'DELETE_QUEUE', payload: data.queueId })
} }
}) }
socket.on('settings', (data) => { socket.on('queue', onQueueQueues)
const onSettingsQueues = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -144,10 +147,13 @@ const Queues = () => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsQueues)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsQueues)
socket.off("queue", onQueueQueues)
} }
}, []) }, [])

View File

@ -1,5 +1,6 @@
import React, { useState, useContext, useEffect, useReducer } from "react" import React, { useState, useContext, useEffect, useReducer } from "react"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import { import {
Button, Button,
@ -125,9 +126,9 @@ const QuickAnswers = () => {
}, [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("quickAnswer", (data) => { const onQuickAnswerQuickAnswers = (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {
dispatch({ type: "UPDATE_QUICK_ANSWERS", payload: data.quickAnswer }) dispatch({ type: "UPDATE_QUICK_ANSWERS", payload: data.quickAnswer })
} }
@ -138,10 +139,11 @@ const QuickAnswers = () => {
payload: +data.quickAnswerId, payload: +data.quickAnswerId,
}) })
} }
}) }
socket.on("quickAnswer", onQuickAnswerQuickAnswers)
return () => { return () => {
socket.disconnect() socket.off("quickAnswer", onQuickAnswerQuickAnswers)
} }
}, []) }, [])

View File

@ -18,23 +18,24 @@ import MaterialTable from 'material-table'
import LogoutIcon from '@material-ui/icons/CancelOutlined' import LogoutIcon from '@material-ui/icons/CancelOutlined'
import apiBroker from "../../services/apiBroker" import apiBroker from "../../services/apiBroker"
import fileDownload from 'js-file-download' import fileDownload from 'js-file-download'
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import { i18n } from "../../translate/i18n" import { i18n } from "../../translate/i18n"
import Switch from '@mui/material/Switch' import Switch from '@mui/material/Switch'
const label = { inputProps: { 'aria-label': 'Size switch demo' } } const label = { inputProps: { 'aria-label': 'Size switch demo' } }
const report = [ const report = [
{ 'value': '1', 'label': 'Atendimento por atendentes' }, { 'value': '1', 'label': `${i18n.t("reports.listTitles.title1_1")}` },
{ 'value': '2', 'label': 'Usuários online/offline' }, { 'value': '2', 'label': `${i18n.t("reports.listTitles.title3_1")}` },
{ 'value': '3', 'label': 'Relatorio de atendimento por numeros' }, { 'value': '3', 'label': `${i18n.t("reports.listTitles.title4_1")}` },
{ 'value': '4', 'label': 'Relatorio de atendimento por filas' }, { 'value': '4', 'label': `${i18n.t("reports.listTitles.title5_1")}` },
] ]
const reportOptType = [ const reportOptType = [
{ 'value': '1', 'label': 'Padrão' }, { 'value': '1', 'label': i18n.t('reportOptType.listTitles.title1') },
{ 'value': '2', 'label': 'Sintético' }, { 'value': '2', 'label': i18n.t('reportOptType.listTitles.title2') },
{ 'value': '3', 'label': 'Analítico' } { 'value': '3', 'label': i18n.t('reportOptType.listTitles.title3') }
] ]
@ -239,7 +240,7 @@ let columnsData = [
{ title: `Espera`, field: 'waiting_time' }, { title: `Espera`, field: 'waiting_time' },
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true }, { title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
{ title: `Link`, field: 'link', searchable: false, hidden: true, export: true }, { title: `Link`, field: 'link', searchable: false, hidden: true, export: true },
] ]
let columnsDataSuper = [ let columnsDataSuper = [
{ title: `Tipo`, field: 'isRemote' }, { title: `Tipo`, field: 'isRemote' },
@ -381,10 +382,10 @@ const Report = () => {
const tickets = data.tickets.map(ticket => { const tickets = data.tickets.map(ticket => {
ticket.isRemote = ticket.isRemote ? 'Remoto' : 'Comum'; ticket.isRemote = ticket.isRemote ? 'Remoto' : 'Comum';
return ({ return ({
...ticket, ...ticket,
messagesToFilter: ticket.messages.map(message => message.body).join(' '), messagesToFilter: ticket.messages.map(message => message.body).join(' '),
link: `${process.env.REACT_APP_FRONTEND_URL}/tickets/${ticket.id}` link: `${process.env.REACT_APP_FRONTEND_URL}/tickets/${ticket.id}`
}) })
}) })
dispatchQ({ type: "LOAD_QUERY", payload: tickets }) dispatchQ({ type: "LOAD_QUERY", payload: tickets })
setHasMore(data.hasMore) setHasMore(data.hasMore)
@ -591,9 +592,9 @@ const Report = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("queryOnQueueStatus", (data) => { const onQueryOnQueueStatusReport = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
if (String(data.queryOnQueue.adminId) === String(userA.id)) { if (String(data.queryOnQueue.adminId) === String(userA.id)) {
@ -604,35 +605,39 @@ const Report = () => {
} }
} }
}) }
socket.on("queryOnQueueStatus", onQueryOnQueueStatusReport)
const onOnlineStatusReport = (data) => {
let date = new Date().toLocaleDateString('pt-BR').split('/')
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
if (data.action === "logout" || (data.action === "update" &&
((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) {
dispatchQ({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime })
}
else if (data.action === "delete") {
dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime })
}
}
const onUserReport = (data) => {
if (data.action === "delete") {
dispatch({ type: "DELETE_USER", payload: +data.userId })
}
}
if (reportOption === '2') { if (reportOption === '2') {
socket.on("onlineStatus", onOnlineStatusReport)
socket.on("onlineStatus", (data) => { socket.on("user", onUserReport)
let date = new Date().toLocaleDateString('pt-BR').split('/')
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
if (data.action === "logout" || (data.action === "update" &&
((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) {
dispatchQ({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime })
}
else if (data.action === "delete") {
dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime })
}
})
socket.on("user", (data) => {
if (data.action === "delete") {
dispatch({ type: "DELETE_USER", payload: +data.userId })
}
})
} }
else if (reportOption === "1") { else if (reportOption === "1") {
@ -641,7 +646,9 @@ const Report = () => {
} }
return () => { return () => {
socket.disconnect() socket.off("onlineStatus", onOnlineStatusReport)
socket.off("user", onUserReport)
socket.off("queryOnQueueStatus", onQueryOnQueueStatusReport)
} }
@ -687,7 +694,7 @@ const Report = () => {
const renderSwitch = (param) => { const renderSwitch = (param) => {
if(userA.profile !== 'supervisor'){ if (userA.profile !== 'supervisor') {
switch (param) { switch (param) {
case 'empty': case 'empty':
return ( return (
@ -838,11 +845,11 @@ const Report = () => {
<Box sx={{ display: 'flex', flexDirection: 'column', padding: '10px 0', }}> <Box sx={{ display: 'flex', flexDirection: 'column', padding: '10px 0', }}>
<FormControlLabel <FormControlLabel
control={<Checkbox checked={selectedValue === 'created'} onChange={() => handleCheckBoxChange('created')} />} control={<Checkbox checked={selectedValue === 'created'} onChange={() => handleCheckBoxChange('created')} />}
label="Criado" label="Creado"
/> />
<FormControlLabel <FormControlLabel
control={<Checkbox checked={selectedValue === 'updated'} onChange={() => handleCheckBoxChange('updated')} />} control={<Checkbox checked={selectedValue === 'updated'} onChange={() => handleCheckBoxChange('updated')} />}
label="Atualizado" label="Actualizado"
/> />
</Box> : </Box> :
@ -880,7 +887,7 @@ const Report = () => {
<> <>
<MTable data={query} <MTable data={query}
columns={userA.profile !== 'supervisor' ? columnsData : columnsDataSuper} columns={userA.profile !== 'supervisor' ? columnsData : columnsDataSuper}
hasChild={userA.profile !== 'supervisor' ? true :false} hasChild={userA.profile !== 'supervisor' ? true : false}
removeClickRow={false} removeClickRow={false}
handleScroll={handleScroll} handleScroll={handleScroll}
@ -896,7 +903,7 @@ const Report = () => {
localization={{ localization={{
header: { header: {
actions: 'Deslogar' actions: 'Unidad'
}, },
}} }}
@ -906,7 +913,7 @@ const Report = () => {
[ [
// { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => <img src={rowData['ticket.contact.profilePicUrl']} alt="imagem de perfil do whatsapp" style={{ width: 40, borderRadius: '50%' }} /> }, // { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => <img src={rowData['ticket.contact.profilePicUrl']} alt="imagem de perfil do whatsapp" style={{ width: 40, borderRadius: '50%' }} /> },
{ title: 'Nome', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, }, { title: 'Nombre', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ {
title: 'Status', field: 'statusOnline.status', title: 'Status', field: 'statusOnline.status',
@ -935,11 +942,11 @@ const Report = () => {
}, },
{ title: 'Tempo online', field: 'sumOnlineTime.sum' }, { title: `${i18n.t('reports.onlineTime')}`, field: 'sumOnlineTime.sum' },
{ title: `${i18n.t("reports.dateStart")}`, field: 'startDate' }, { title: `${i18n.t("reports.dateStart")}`, field: 'startDate' },
{ title: `${i18n.t("reports.dateEnd")}`, field: 'endDate' }, { title: `${i18n.t("reports.dateEnd")}`, field: 'endDate' },
{ title: 'Em atendimento', field: 'sumOpen.count' }, { title: `${i18n.t('reports.inService')}`, field: 'sumOpen.count' },
{ title: 'Finalizado', field: 'sumClosed.count' }, { title: `${i18n.t('reports.finished')}`, field: 'sumClosed.count' },
] ]
} }
@ -1001,20 +1008,20 @@ const Report = () => {
title={i18n.t("reports.listTitles.title4_1")} title={i18n.t("reports.listTitles.title4_1")}
columns={ columns={
!checkedRemote ? [ !checkedRemote ? [
{ title: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, }, { title: i18n.t('reports.listColumns.column3_1'), field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ title: 'Conversas iniciadas', field: 'startedByAgent', }, { title: i18n.t('reports.listColumns.column3_3'), field: 'startedByAgent', },
{ title: 'Conversas recebidas', field: 'startedByClient' }, { title: i18n.t('reports.listColumns.column3_4'), field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' }, { title: i18n.t('reports.listColumns.column3_5'), field: 'closedChat' },
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' }, { title: i18n.t('reports.listColumns.column3_6'), field: 'avgChatWaitingTime' },
{ title: 'Aguardando', field: 'pendingChat' } { title: i18n.t('reports.listColumns.column3_7'), field: 'pendingChat' }
] : ] :
[ [
{ title: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, }, { title: i18n.t('reports.listColumns.column3_1'), field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ title: 'Conversas iniciadas', field: 'startedByAgent', }, { title: i18n.t('reports.listColumns.column3_3'), field: 'startedByAgent', },
{ title: 'Conversas respondidas', field: 'startedByClient' }, { title: i18n.t('reports.listColumns.column3_4'), field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' }, { title: i18n.t('reports.listColumns.column3_5'), field: 'closedChat' },
{ title: 'Aguardando', field: 'pendingChat' } { title: i18n.t('reports.listColumns.column3_7'), field: 'pendingChat' }
] ]
} }
@ -1054,9 +1061,9 @@ const Report = () => {
columns={ columns={
!checkedRemote ? [ !checkedRemote ? [
{ title: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, }, { title: i18n.t('reports.listColumns.column3_1'), field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ {
title: 'Fila', field: 'queueName', title: i18n.t('reports.listColumns.column3_2'), field: 'queueName',
cellStyle: (evt, rowData) => { cellStyle: (evt, rowData) => {
return { return {
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
@ -1066,19 +1073,19 @@ const Report = () => {
} }
}, },
{ title: 'Conversas iniciadas', field: 'startedByAgent', }, { title: i18n.t('reports.listColumns.column3_3'), field: 'startedByAgent', },
{ title: 'Conversas recebidas', field: 'startedByClient' }, { title: i18n.t('reports.listColumns.column3_4'), field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' }, { title: i18n.t('reports.listColumns.column3_5'), field: 'closedChat' },
{ title: `Tempo médio de espera`, field: 'avgChatWaitingTime' }, { title: i18n.t('reports.listColumns.column3_6'), field: 'avgChatWaitingTime' },
{ title: 'Aguardando', field: 'pendingChat' } { title: i18n.t('reports.listColumns.column3_7'), field: 'pendingChat' }
] : ] :
[ [
{ title: 'Unidade', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, }, { title: i18n.t('reports.listColumns.column3_1'), field: 'name', cellStyle: { whiteSpace: 'nowrap' }, },
{ {
title: 'Fila', field: 'queueName', title: i18n.t('reports.listColumns.column3_2'), field: 'queueName',
cellStyle: (evt, rowData) => { cellStyle: (evt, rowData) => {
return { return {
whiteSpace: 'nowrap', whiteSpace: 'nowrap',
@ -1088,10 +1095,10 @@ const Report = () => {
} }
}, },
{ title: 'Conversas iniciadas', field: 'startedByAgent', }, { title: i18n.t('reports.listColumns.column3_3'), field: 'startedByAgent', },
{ title: 'Conversas respondidas', field: 'startedByClient' }, { title: i18n.t('reports.listColumns.column3_4'), field: 'startedByClient' },
{ title: `Conversas finalizadas`, field: 'closedChat' }, { title: i18n.t('reports.listColumns.column3_5'), field: 'closedChat' },
{ title: 'Aguardando', field: 'pendingChat' } { title: i18n.t('reports.listColumns.column3_7'), field: 'pendingChat' }
] ]
} }

View File

@ -28,8 +28,8 @@ import { render } from '@testing-library/react'
// import Modal from "../../../..ChatEnd/ModalChatEnd"; // import Modal from "../../../..ChatEnd/ModalChatEnd";
import Modal from "../../components/ModalUpdateScheduleReminder" import Modal from "../../components/ModalUpdateScheduleReminder"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
@ -204,11 +204,9 @@ const SchedulesReminder = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("schedulingNotify", (data) => {
const onSchedulingNotifySchedulesRemider = (data) => {
setLoading(true) setLoading(true)
@ -228,10 +226,12 @@ const SchedulesReminder = () => {
setLoading(false) setLoading(false)
}) }
socket.on("schedulingNotify", onSchedulingNotifySchedulesRemider)
return () => { return () => {
socket.disconnect() socket.off("schedulingNotify", onSchedulingNotifySchedulesRemider)
} }
}, []) }, [])
@ -300,9 +300,9 @@ const SchedulesReminder = () => {
useEffect(() => { useEffect(() => {
if (!loading) { if (!loading) {
setData(query.map(({ scheduleReminder, ...others }) => ( setData(query.map(({ scheduleReminder, ...others }) => (
{ {

View File

@ -1,5 +1,6 @@
import React, { useState, useEffect, useContext } from 'react' import React, { useState, useEffect, useContext } from 'react'
import openSocket from 'socket.io-client' //import openSocket from 'socket.io-client'
import { socket } from '../../services/socket.js'
import { makeStyles } from '@material-ui/core/styles' import { makeStyles } from '@material-ui/core/styles'
import Paper from '@material-ui/core/Paper' import Paper from '@material-ui/core/Paper'
@ -124,9 +125,9 @@ const Settings = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on('settings', (data) => { const onSettingsSettings = (data) => {
console.log('settings updated ----------------------------') console.log('settings updated ----------------------------')
if (data.action === 'update') { if (data.action === 'update') {
@ -137,10 +138,12 @@ const Settings = () => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsSettings)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsSettings)
} }
}, []) }, [])
@ -179,7 +182,11 @@ const Settings = () => {
} }
const getSettingValue = (key, _obj = false) => { const getSettingValue = (key, _obj = false) => {
const { value, obj } = settings.find((s) => s.key === key) //const { value, obj } = settings.find((s) => s.key === key)
const setting = settings.find((s) => s.key === key)
const value = setting?.value || ""
const obj = setting?.obj || null
if (_obj) if (_obj)
return obj return obj
@ -641,7 +648,7 @@ const Settings = () => {
</Select> </Select>
</Paper> </Paper>
</Container> </Container>
</div> </div>
</div> </div>
)} )}
/> />

View File

@ -1,5 +1,6 @@
import React, { useState, useContext, useEffect, useReducer } from "react" import React, { useState, useContext, useEffect, useReducer } from "react"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import { import {
Button, Button,
@ -115,7 +116,7 @@ const StatusChatEnd = () => {
const { data } = await api.get("/statusChatEnd", { const { data } = await api.get("/statusChatEnd", {
params: { searchParam, pageNumber }, params: { searchParam, pageNumber },
}) })
setChecked(data?.statusChatEnd?.map(s => s.isDefault ? true : false)) setChecked(data?.statusChatEnd?.map(s => s.isDefault ? true : false))
dispatch({ type: "LOAD_STATUS_CHAT_END", payload: data.statusChatEnd }) dispatch({ type: "LOAD_STATUS_CHAT_END", payload: data.statusChatEnd })
@ -131,9 +132,9 @@ const StatusChatEnd = () => {
}, [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("statusChatEnd", (data) => { const onStatusChatEndStatusChatEnd = (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {
dispatch({ type: "UPDATE_STATUS_CHAT_END", payload: data.statusChatEnd }) dispatch({ type: "UPDATE_STATUS_CHAT_END", payload: data.statusChatEnd })
} }
@ -144,10 +145,12 @@ const StatusChatEnd = () => {
payload: +data.statusChatEndId, payload: +data.statusChatEndId,
}) })
} }
}) }
socket.on("statusChatEnd", onStatusChatEndStatusChatEnd)
return () => { return () => {
socket.disconnect() socket.off("statusChatEnd", onStatusChatEndStatusChatEnd)
} }
}, []) }, [])
@ -187,14 +190,14 @@ const StatusChatEnd = () => {
} }
const handleChange = async (event, statusChatEnd, index) => { const handleChange = async (event, statusChatEnd, index) => {
const newChecked = new Array(statusChatEnds.length).fill(false) const newChecked = new Array(statusChatEnds.length).fill(false)
newChecked[index] = event.target.checked newChecked[index] = event.target.checked
setChecked(newChecked) setChecked(newChecked)
try { try {
const { id } = statusChatEnd const { id } = statusChatEnd
await api.put(`/statusChatEnd/${id}`, { isDefault: event.target.checked }) await api.put(`/statusChatEnd/${id}`, { isDefault: event.target.checked })
toast.success("Status de encerramento padrão salvo com sucesso") toast.success("Status de encerramento padrão salvo com sucesso")
} catch (error) { } catch (error) {
@ -217,7 +220,7 @@ const StatusChatEnd = () => {
<ConfirmationModal <ConfirmationModal
title={ title={
deletingStatusChatEnds && deletingStatusChatEnds &&
`Você tem certeza que quer excluir esta Status de encerramento: ${deletingStatusChatEnds.name `${i18n.t('dashboard.titles.confirmationModal')} ${deletingStatusChatEnds.name
}?` }?`
} }
open={confirmModalOpen} open={confirmModalOpen}
@ -233,7 +236,7 @@ const StatusChatEnd = () => {
statusChatEndId={selectedStatusChatEnd && selectedStatusChatEnd.id} statusChatEndId={selectedStatusChatEnd && selectedStatusChatEnd.id}
></StatusChatEndModal> ></StatusChatEndModal>
<MainHeader> <MainHeader>
<Title>{"Status de encerramento"}</Title> <Title>{i18n.t('dashboard.titles.status')}</Title>
<MainHeaderButtonsWrapper> <MainHeaderButtonsWrapper>
<TextField <TextField
placeholder={i18n.t("quickAnswers.searchPlaceholder")} placeholder={i18n.t("quickAnswers.searchPlaceholder")}
@ -266,13 +269,13 @@ const StatusChatEnd = () => {
<TableHead> <TableHead>
<TableRow> <TableRow>
<TableCell align="center"> <TableCell align="center">
{"Status de encerramento"} {i18n.t('dashboard.titles.status')}
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
{"Mensagem de despedida"} {i18n.t('quickAnswers.table.farewellMessage')}
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
{"Padrão"} {i18n.t('quickAnswers.table.standard')}
</TableCell> </TableCell>
<TableCell align="center"> <TableCell align="center">
{i18n.t("quickAnswers.table.actions")} {i18n.t("quickAnswers.table.actions")}

View File

@ -1,6 +1,7 @@
import React, { useState, useEffect, useReducer, useContext } from "react" import React, { useState, useEffect, useReducer, useContext } from "react"
import { toast } from "react-toastify" import { toast } from "react-toastify"
import openSocket from "socket.io-client" //import openSocket from "socket.io-client"
import { socket } from "../../services/socket"
import { makeStyles } from "@material-ui/core/styles" import { makeStyles } from "@material-ui/core/styles"
import Paper from "@material-ui/core/Paper" import Paper from "@material-ui/core/Paper"
@ -165,9 +166,9 @@ const Users = () => {
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL) //const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
socket.on("user", (data) => { const onUserUsers = (data) => {
if (data.action === "update" || data.action === "create") { if (data.action === "update" || data.action === "create") {
dispatch({ type: "UPDATE_USERS", payload: data.user }) dispatch({ type: "UPDATE_USERS", payload: data.user })
} }
@ -175,10 +176,11 @@ const Users = () => {
if (data.action === "delete") { if (data.action === "delete") {
dispatch({ type: "DELETE_USER", payload: +data.userId }) dispatch({ type: "DELETE_USER", payload: +data.userId })
} }
}) }
socket.on("user", onUserUsers)
socket.on('settings', (data) => { const onSettingsUsers = (data) => {
if (data.action === 'update') { if (data.action === 'update') {
setSettings((prevState) => { setSettings((prevState) => {
const aux = [...prevState] const aux = [...prevState]
@ -187,10 +189,12 @@ const Users = () => {
return aux return aux
}) })
} }
}) }
socket.on('settings', onSettingsUsers)
return () => { return () => {
socket.disconnect() socket.off("settings", onSettingsUsers)
socket.off("user", onUserUsers)
} }
}, []) }, [])

View File

@ -1,9 +1,14 @@
import React, { useContext } from "react"; import React, { useContext, Suspense, lazy } from "react";
import { Route as RouterRoute, Redirect } from "react-router-dom"; import { Route as RouterRoute, Redirect } from "react-router-dom";
import { AuthContext } from "../context/Auth/AuthContext"; import { AuthContext } from "../context/Auth/AuthContext";
import BackdropLoading from "../components/BackdropLoading"; import BackdropLoading from "../components/BackdropLoading";
// Exemplo de como você carregaria componentes de forma lazy
const Dashboard = lazy(() => import("../pages/Dashboard"));
const Login = lazy(() => import("../pages/Login"));
const Signup = lazy(() => import("../pages/Signup"));
const Route = ({ component: Component, isPrivate = false, ...rest }) => { const Route = ({ component: Component, isPrivate = false, ...rest }) => {
const { isAuth, loading } = useContext(AuthContext); const { isAuth, loading } = useContext(AuthContext);
@ -20,7 +25,7 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => {
return ( return (
<> <>
{loading && <BackdropLoading />} {loading && <BackdropLoading />}
<Redirect to={{ pathname: "/", state: { from: rest.location } }} />; <Redirect to={{ pathname: "/", state: { from: rest.location } }} />
</> </>
); );
} }
@ -28,9 +33,11 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => {
return ( return (
<> <>
{loading && <BackdropLoading />} {loading && <BackdropLoading />}
<RouterRoute {...rest} component={Component} /> <Suspense fallback={<BackdropLoading />}>
<RouterRoute {...rest} component={Component} />
</Suspense>
</> </>
); );
}; };
export default Route; export default Route;

View File

@ -1,94 +1,85 @@
import React from 'react' import React, { Suspense, lazy } 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 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 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 StatusChatEnd from '../pages/StatusChatEnd/'
import Position from '../pages/Position/'
import Queues from '../pages/Queues/'
import { AuthProvider } from '../context/Auth/AuthContext' import { AuthProvider } from '../context/Auth/AuthContext'
import { WhatsAppsProvider } from '../context/WhatsApp/WhatsAppsContext' import { WhatsAppsProvider } from '../context/WhatsApp/WhatsAppsContext'
import LoggedInLayout from '../layout'
import Route from './Route' import Route from './Route'
import BackdropLoading from "../components/BackdropLoading";
const Dashboard = lazy(() => import('../pages/Dashboard/'));
const Report = lazy(() => import('../pages/Report/'));
const SchedulesReminder = lazy(() => import('../pages/SchedulesReminder/'));
const Tickets = lazy(() => import('../pages/Tickets/'));
const Signup = lazy(() => import('../pages/Signup/'));
const Login = lazy(() => import('../pages/Login/'));
const Connections = lazy(() => import('../pages/Connections/'));
const Campaign = lazy(() => import('../pages/Campaign/'));
const Settings = lazy(() => import('../pages/Settings/'));
const Users = lazy(() => import('../pages/Users/'));
const Contacts = lazy(() => import('../pages/Contacts/'));
const QuickAnswers = lazy(() => import('../pages/QuickAnswers/'));
const StatusChatEnd = lazy(() => import('../pages/StatusChatEnd/'));
const Position = lazy(() => import('../pages/Position/'));
const Queues = lazy(() => import('../pages/Queues/'));
const Routes = () => { const Routes = () => {
return ( return (
<BrowserRouter> <BrowserRouter>
<AuthProvider> <AuthProvider>
<Switch> <Suspense fallback={<BackdropLoading />}>
<Route exact path="/login" component={Login} /> <Switch>
<Route exact path="/signup" component={Signup} /> <Route exact path="/login" component={Login} />
<WhatsAppsProvider> <Route exact path="/signup" component={Signup} />
<LoggedInLayout> <WhatsAppsProvider>
<Route exact path="/" component={Dashboard} isPrivate /> <LoggedInLayout>
<Route <Route exact path="/" component={Dashboard} isPrivate />
exact <Route
path="/tickets/:ticketId?" exact
component={Tickets} path="/tickets/:ticketId?"
isPrivate component={Tickets}
/> isPrivate
/>
<Route <Route
exact exact
path="/connections" path="/connections"
component={Connections} component={Connections}
isPrivate isPrivate
/> />
<Route exact path="/report" component={Report} isPrivate />
<Route exact path="/report" component={Report} isPrivate /> <Route exact path="/contacts" component={Contacts} isPrivate />
<Route
<Route exact path="/contacts" component={Contacts} isPrivate /> exact
path="/schedulesReminder"
<Route component={SchedulesReminder}
exact isPrivate
path="/schedulesReminder" />
component={SchedulesReminder} <Route exact path="/users" component={Users} isPrivate />
isPrivate <Route
/> exact
path="/quickAnswers"
<Route exact path="/users" component={Users} isPrivate /> component={QuickAnswers}
<Route isPrivate
exact />
path="/quickAnswers" <Route
component={QuickAnswers} exact
isPrivate path="/statusChatEnd"
/> component={StatusChatEnd}
<Route isPrivate
exact />
path="/statusChatEnd" <Route exact path="/position" component={Position} isPrivate />
component={StatusChatEnd} <Route exact path="/Settings" component={Settings} isPrivate />
isPrivate <Route exact path="/Queues" component={Queues} isPrivate />
/> <Route exact path="/campaign" component={Campaign} isPrivate />
<Route </LoggedInLayout>
exact </WhatsAppsProvider>
path="/position" </Switch>
component={Position} </Suspense>
isPrivate
/>
<Route exact path="/Settings" component={Settings} isPrivate />
<Route exact path="/Queues" component={Queues} isPrivate />
<Route exact path="/campaign" component={Campaign} isPrivate />
</LoggedInLayout>
</WhatsAppsProvider>
</Switch>
<ToastContainer autoClose={3000} /> <ToastContainer autoClose={3000} />
</AuthProvider> </AuthProvider>
</BrowserRouter> </BrowserRouter>
) );
} }
export default Routes export default Routes

View File

@ -0,0 +1,14 @@
import { io } from 'socket.io-client';
// "undefined" means the URL will be computed from the `window.location` object
const URL = process.env.REACT_APP_BACKEND_URL
export const socket = io(URL,
//{
// withCredentials: true,
// extraHeaders: {
// "my-custom-header": "abcd"
// },
// transports: ['websocket', 'polling']
//}
);

View File

@ -51,14 +51,53 @@ const messages = {
} }
}, },
table_users:{ table_users: {
title: 'User List', title: 'User List',
column0: 'Name', column0: 'Name',
column1: 'In Service/Finished', column1: 'In Service/Finished',
column2: 'Open by Queue', column2: 'Open by Queue',
column3: 'Closed by Queue', column3: 'Closed by Queue',
column4: 'Online time', column4: 'Online time',
column5: 'Actions', column5: 'Actions',
},
titles: {
selectQueues: 'Queues',
waiting: 'In Waiting',
inService: 'In Service',
users: 'Users',
name: 'Name',
attendant: 'Attendant',
dialogContentText: 'Choose a reporting option below.',
transfer: 'Transfer to other queues',
title: 'Closing tickets',
confirmationModal: 'Are you sure you want to remove this closing state: ',
status: 'Closing status',
listUser: 'List user',
user: 'user',
notEnter: 'Did not enter',
notEnterToday: 'Did not enter today',
ticketsClosed: 'Closed'
}
},
reportOptType: {
listTitles: {
title1: 'Standard',
title2: 'Synthetic',
title3: 'Analytical'
}
},
configModal: {
titles: {
startService: 'Start Service',
endService: 'End Service',
enableDisable: 'Enable/Disable',
outOfHours: 'Message outside opening hours',
outOfHoursSaturday: 'Message outside opening hours Saturday',
forTheWeekend: 'Weekend message',
holiday: 'Holiday date',
holidayMessage: 'Message for holidays',
inactivityMessage: 'Message due to lack of service activity',
save: 'Save'
} }
}, },
connections: { connections: {
@ -294,8 +333,9 @@ const messages = {
queues: "Queues", queues: "Queues",
administration: "Administration", administration: "Administration",
users: "Users", users: "Users",
settings: "Settings", settings: "Settings",
schedules: "Schedules" schedules: "Schedules",
reports: "Reports"
}, },
appBar: { appBar: {
user: { user: {
@ -309,9 +349,11 @@ const messages = {
title0_1: "Reminders/Schedulings", title0_1: "Reminders/Schedulings",
title1_1: "Calls by attendants", title1_1: "Calls by attendants",
title2_1: "Whatsapp chat", title2_1: "Whatsapp chat",
title3_1: "Users online/offline" title3_1: "Users online/offline",
title4_1: "Attendance report by numbers",
title5_1: "Queue service report"
}, },
listColumns:{ listColumns: {
column0_1: 'Actions', column0_1: 'Actions',
column0_2: 'Pic', column0_2: 'Pic',
column0_3: 'Name', column0_3: 'Name',
@ -322,7 +364,7 @@ const messages = {
column0_8: 'Message', column0_8: 'Message',
column1_1: 'Store', column1_1: 'Store',
column1_2: 'Attendant', column1_2: 'Attendant',
column1_5: 'Subject', column1_5: 'Subject',
column1_6: 'Status', column1_6: 'Status',
column1_7: 'Created', column1_7: 'Created',
@ -330,13 +372,22 @@ const messages = {
column1_9: 'Closing status', column1_9: 'Closing status',
column2_1: 'Attendant/Client', column2_1: 'Attendant/Client',
column3_1: 'Unit',
column3_2: 'Row',
column3_3: 'Conversations started',
column3_4: 'Incoming conversations',
column3_5: 'Finished conversations',
column3_6: 'Average waiting time',
column3_7: 'Waiting',
}, },
search: 'Number/Name...', search: 'Number/Name...',
dateStart: 'Start date', dateStart: 'Start date',
dateEnd: 'End date', dateEnd: 'End date',
user: 'User' user: 'User',
onlineTime: 'Online time',
inService: 'In service',
finished: 'Finished'
}, },
notifications: { notifications: {
noTickets: "No notifications.", noTickets: "No notifications.",
@ -367,6 +418,8 @@ const messages = {
shortcut: "Shortcut", shortcut: "Shortcut",
message: "Quick Reply", message: "Quick Reply",
actions: "Actions", actions: "Actions",
farewellMessage: "Farewell message",
standard: "Standard"
}, },
buttons: { buttons: {
add: "Add Quick Reply", add: "Add Quick Reply",

View File

@ -52,15 +52,55 @@ const messages = {
} }
}, },
table_users:{ table_users: {
title: 'Lista de usuarios', title: 'Lista de usuarios',
column0: 'Nombre', column0: 'Nombre',
column1: 'En servicio/Terminado(S)', column1: 'En servicio/Terminado(S)',
column2: 'Abrir por cola', column2: 'Abrir por cola',
column3: 'Cerrado por cola', column3: 'Cerrado por cola',
column4: 'Tiempo Online', column4: 'Tiempo Online',
column5: 'Actions', column5: 'Actions',
},
titles: {
selectQueues: 'Colas',
waiting: 'En espera',
inService: 'En Atendimiento',
users: 'Usuarios',
name: 'Nombre',
attendant: 'Agente',
dialogTitle: 'Reportes',
dialogContentText: 'Escoja una opción de reporte abajo.',
transfer: 'Transferir para otras Colas',
title: 'Cierre de Tickets',
confirmationModal: 'Está seguro de que desea eliminar este estado de cierre: ',
status: 'Status de cierre',
listUser: 'Lista de Usuarios',
user: 'Usuario',
notEnter: 'No ingresó',
notEnterToday: 'No ingresó hoy',
ticketsClosed: 'Cerrados'
}
},
reportOptType: {
listTitles: {
title1: 'Patrón',
title2: 'Sintético',
title3: 'Analítico'
}
},
configModal: {
titles: {
startService: 'Inicio atención',
endService: 'Fin atención',
enableDisable: 'Activar/Desactivar',
outOfHours: 'Mensaje fuera de horario de atención',
outOfHoursSaturday: 'Mensaje fuera del horario de atención sábado',
forTheWeekend: 'Mensaje fin de semana',
holiday: 'Día Festivo',
holidayMessage: 'Mensaje para Festivos',
inactivityMessage: 'Mensaje por falta de actividad',
save: 'Guardar'
} }
}, },
connections: { connections: {
@ -237,7 +277,7 @@ const messages = {
}, },
}, },
ticketsQueueSelect: { ticketsQueueSelect: {
placeholder: "Linhas", placeholder: "Líneas",
}, },
tickets: { tickets: {
toasts: { toasts: {
@ -296,11 +336,13 @@ const messages = {
tickets: "Tickets", tickets: "Tickets",
contacts: "Contactos", contacts: "Contactos",
quickAnswers: "Respuestas rápidas", quickAnswers: "Respuestas rápidas",
queues: "Linhas", queues: "Líneas",
administration: "Administración", administration: "Administración",
users: "Usuarios", users: "Usuarios",
settings: "Configuración", settings: "Configuración",
schedules: "Recordatorio" schedules: "Recordatorio",
reports: "Reportes",
campain: "Campañas"
}, },
appBar: { appBar: {
user: { user: {
@ -312,11 +354,13 @@ const messages = {
reports: { reports: {
listTitles: { listTitles: {
title0_1: "Recordatorios/Programación", title0_1: "Recordatorios/Programación",
title1_1: "Llamadas de asistentes", title1_1: "Atención por agentes",
title2_1: "Chat de whatsapp", title2_1: "Chat de whatsapp",
title3_1: "Usuarios online/offline" title3_1: "Usuarios online/offline",
title4_1: "Reporte de atención por números",
title5_1: "Reporte de atención por colas"
}, },
listColumns:{ listColumns: {
column0_1: 'Acción', column0_1: 'Acción',
column0_2: 'Pic', column0_2: 'Pic',
column0_3: 'Nombre', column0_3: 'Nombre',
@ -327,7 +371,7 @@ const messages = {
column0_8: 'Mensaje', column0_8: 'Mensaje',
column1_1: 'Almacenar', column1_1: 'Almacenar',
column1_2: 'Secretario', column1_2: 'Secretario',
column1_5: 'Tema', column1_5: 'Tema',
column1_6: 'Status', column1_6: 'Status',
column1_7: 'Creado', column1_7: 'Creado',
@ -335,18 +379,28 @@ const messages = {
column1_9: 'Estado de cierre', column1_9: 'Estado de cierre',
column2_1: 'Secretario/Cliente', column2_1: 'Secretario/Cliente',
column3_1: 'Unidad',
column3_2: 'Cola',
column3_3: 'Chats iniciados',
column3_4: 'Chats Recibidos',
column3_5: 'Chats Finalizados',
column3_6: 'Tiempo medio de espera',
column3_7: 'En Espera',
}, },
search: 'Número/Nombre...', search: 'Número/Nombre...',
dateStart: 'Fecha de inicio', dateStart: 'Fecha de inicio',
dateEnd: 'Fecha final', dateEnd: 'Fecha final',
user: 'Usuario' user: 'Usuario',
onlineTime: 'Tiempo online',
inService: 'En servicio',
finished: 'Finalizado'
}, },
notifications: { notifications: {
noTickets: "Sin notificaciones.", noTickets: "Sin notificaciones.",
}, },
queues: { queues: {
title: "Linhas", title: "Líneas",
table: { table: {
name: "Nombre", name: "Nombre",
color: "Color", color: "Color",
@ -363,7 +417,7 @@ const messages = {
}, },
}, },
queueSelect: { queueSelect: {
inputLabel: "Linhas", inputLabel: "Líneas",
}, },
quickAnswers: { quickAnswers: {
title: "Respuestas rápidas", title: "Respuestas rápidas",
@ -371,6 +425,8 @@ const messages = {
shortcut: "Atajo", shortcut: "Atajo",
message: "Respuesta rápida", message: "Respuesta rápida",
actions: "Acciones", actions: "Acciones",
farewellMessage: "Mensaje de despedida",
standard: "Patrón"
}, },
buttons: { buttons: {
add: "Agregar respuesta rápida", add: "Agregar respuesta rápida",

View File

@ -59,6 +59,46 @@ const messages = {
column3: 'Fechados Por Fila', column3: 'Fechados Por Fila',
column4: 'Tempo Online', column4: 'Tempo Online',
column5: 'Ações', column5: 'Ações',
},
titles: {
selectQueues: 'Filas',
waiting: 'Em espera',
inService: 'Em Atendimento',
users: 'Usuários',
name: 'Nome',
attendant: 'Atendente',
dialogTitle: 'Relatórios',
dialogContentText: 'Escolha uma opção de relatório abaixo.',
transfer: 'Transferir para outras filas',
title: 'Tickets encerramento',
confirmationModal: 'Você tem certeza que quer excluir esta Status de encerramento: ' ,
status: 'Status de encerramento',
listUser: 'Lista de Usuarios',
user: 'Usuario',
notEnter: 'Não entrou',
notEnterToday: 'Não entrou hoje',
ticketsClosed: 'Fechados'
}
},
reportOptType: {
listTitles: {
title1: 'Padrão',
title2: 'Sintético',
title3: 'Analítico'
}
},
configModal: {
titles: {
startService: 'Início atendimento',
endService: 'Fim atendimento',
enableDisable: 'Ativar/Desativar',
outOfHours: 'Mensagem fora do horário de atendimento',
outOfHoursSaturday: 'Mensagem fora do horário de atendimento sábado',
forTheWeekend: 'Mensagem fim de semana',
holiday: 'Data do feriado',
holidayMessage: 'Mensagem para feriados',
inactivityMessage: 'Mensagem por falta de atividade no atendimento',
save: 'Salvar'
} }
}, },
connections: { connections: {
@ -299,7 +339,9 @@ const messages = {
administration: "Administração", administration: "Administração",
users: "Usuários", users: "Usuários",
settings: "Configurações", settings: "Configurações",
schedules: "Lembretes" schedules: "Lembretes",
reports: "Relatórios",
campaign: "Campanha"
}, },
appBar: { appBar: {
user: { user: {
@ -336,14 +378,22 @@ const messages = {
column1_9: 'Status de encerramento', column1_9: 'Status de encerramento',
column2_1: 'Atendente/Cliente', column2_1: 'Atendente/Cliente',
column3_1: 'Unidade',
column3_2: 'Fila',
column3_3: 'Conversas iniciadas',
column3_4: 'Conversas recebidas',
column3_5: 'Conversas finalizadas',
column3_6: 'Tempo médio de espera',
column3_7: 'Aguardando',
}, },
search: 'Numer/Nome...', search: 'Numer/Nome...',
dateStart: 'Data início', dateStart: 'Data início',
dateEnd: 'Data fim', dateEnd: 'Data fim',
user: 'Usuário' user: 'Usuário',
onlineTime: 'Tempo online',
inService: 'Em atendimento',
finished: 'Finalizado'
}, },
notifications: { notifications: {
noTickets: "Nenhuma notificação.", noTickets: "Nenhuma notificação.",
@ -374,6 +424,8 @@ const messages = {
shortcut: "Atalho", shortcut: "Atalho",
message: "Resposta Rápida", message: "Resposta Rápida",
actions: "Ações", actions: "Ações",
farewellMessage: "Mensagem de despedida",
standard: "Padrão"
}, },
buttons: { buttons: {
add: "Adicionar Resposta Rápida", add: "Adicionar Resposta Rápida",

2
package-lock.json generated
View File

@ -1,5 +1,5 @@
{ {
"name": "whaticket", "name": "projeto-hit",
"lockfileVersion": 2, "lockfileVersion": 2,
"requires": true, "requires": true,
"packages": { "packages": {