diff --git a/backend/package.json b/backend/package.json index 1c8646e..1e5bd13 100644 --- a/backend/package.json +++ b/backend/package.json @@ -8,6 +8,7 @@ "watch": "tsc -w", "start": "nodemon --expose-gc dist/server.js", "dev:server": "ts-node-dev --respawn --transpile-only --ignore node_modules src/server.ts", + "dev": "nodemon --exec npm run dev:server", "pretest": "NODE_ENV=test sequelize db:migrate && NODE_ENV=test sequelize db:seed:all", "test": "NODE_ENV=test jest", "posttest": "NODE_ENV=test sequelize db:migrate:undo:all" diff --git a/backend/src/controllers/ReportController.ts b/backend/src/controllers/ReportController.ts index 13ef50a..c7e975b 100644 --- a/backend/src/controllers/ReportController.ts +++ b/backend/src/controllers/ReportController.ts @@ -66,7 +66,7 @@ export const reportUserByDateStartDateEnd = async ( endDate, pageNumber, createdOrUpdated, - queueId + queueId, }); const queues = await Queue.findAll({ attributes: ["id", "name"] }); @@ -85,11 +85,12 @@ export const reportUserService = async ( ) { throw new AppError("ERR_NO_PERMISSION", 403); } - const { userId, startDate, endDate } = req.query as IndexQuery; + const { userId, startDate, endDate, userQueues} = req.query as IndexQuery; // let usersProfile = await ListUserParamiterService({ profile: 'user' }) let usersProfile = await ListUserParamiterService({ profiles: ["user", "supervisor"], + userQueues: userQueues ? userQueues : undefined, raw: true }); @@ -357,15 +358,18 @@ export const reportTicksCountByStatusChatEnds = async ( throw new AppError("ERR_NO_PERMISSION", 403); } - const { startDate, endDate } = req.query as IndexQuery; + const { startDate, endDate, userQueues } = req.query as IndexQuery; const dateToday = splitDateTime( new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR })) ); + + const queueIds = userQueues ? userQueues.map(queue => parseInt(queue)) : []; const reportStatusChatEnd = await CountStatusChatEndService( startDate || dateToday.fullDate, - endDate || dateToday.fullDate + endDate || dateToday.fullDate, + queueIds ); return res.status(200).json({ reportStatusChatEnd }); diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 367275c..57d62d3 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -409,9 +409,8 @@ export const show = async (req: Request, res: Response): Promise => { export const count = async (req: Request, res: Response): Promise => { // type indexQ = { status: string; date?: string; }; - const { status, date } = req.query as IndexQuery; - - const ticketCount = await CountTicketService(status, date); + const { status, date, queueIds } = req.query as IndexQuery; + const ticketCount = await CountTicketService(status, date, queueIds); return res.status(200).json(ticketCount); }; diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index 7bfcf5f..20dc4ee 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -100,7 +100,7 @@ export const index = async (req: Request, res: Response): Promise => { // }; export const all = async (req: Request, res: Response): Promise => { - let { userId, profile }: any = req.query as IndexQuery; + let { userId, profile, transferToOtherQueues }: any = req.query as IndexQuery; console.log( "userId: ", @@ -111,7 +111,7 @@ export const all = async (req: Request, res: Response): Promise => { getSettingValue("queueTransferByWhatsappScope")?.value ); - if (getSettingValue("queueTransferByWhatsappScope")?.value == "enabled") { + if (getSettingValue("queueTransferByWhatsappScope")?.value == "enabled" && !transferToOtherQueues) { if (!userId) return res.json({ users: [], queues: [] }); const obj = await ListUserByWhatsappQueuesService( @@ -145,7 +145,8 @@ export const store = async (req: Request, res: Response): Promise => { profile, positionCompany, positionId, - queueIds + queueIds, + transferToOtherQueues } = req.body; console.log("===========> req.url: ", req.url); @@ -172,7 +173,8 @@ export const store = async (req: Request, res: Response): Promise => { positionCompany, positionId, profile, - queueIds + queueIds, + transferToOtherQueues }); if (user) { diff --git a/backend/src/database/migrations/20240425190416-add-transfer-to-other-queues-to-users.ts b/backend/src/database/migrations/20240425190416-add-transfer-to-other-queues-to-users.ts new file mode 100644 index 0000000..8a653a8 --- /dev/null +++ b/backend/src/database/migrations/20240425190416-add-transfer-to-other-queues-to-users.ts @@ -0,0 +1,15 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.addColumn("Users", "transferToOtherQueues", { + type: DataTypes.BOOLEAN, + allowNull: false, + defaultValue: false + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.removeColumn("Users", "transferToOtherQueues"); + } +}; \ No newline at end of file diff --git a/backend/src/database/seeds/20240416141716-add-enable-block-audio-video-media-settings.ts b/backend/src/database/seeds/20240416141716-add-enable-block-audio-video-media-settings.ts new file mode 100644 index 0000000..511de98 --- /dev/null +++ b/backend/src/database/seeds/20240416141716-add-enable-block-audio-video-media-settings.ts @@ -0,0 +1,22 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.bulkInsert( + "Settings", + [ + { + key: "blockAudioVideoMedia", + value: "disabled", + createdAt: new Date(), + updatedAt: new Date() + } + ], + {} + ); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Settings", {}); + } +}; diff --git a/backend/src/database/seeds/20240417151300-add-enable-waiting-time-tickets-settings.ts b/backend/src/database/seeds/20240417151300-add-enable-waiting-time-tickets-settings.ts new file mode 100644 index 0000000..8bb6828 --- /dev/null +++ b/backend/src/database/seeds/20240417151300-add-enable-waiting-time-tickets-settings.ts @@ -0,0 +1,22 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.bulkInsert( + "Settings", + [ + { + key: "waitingTimeTickets", + value: "disabled", + createdAt: new Date(), + updatedAt: new Date() + } + ], + {} + ); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Settings", {}); + } +}; diff --git a/backend/src/database/seeds/20240417185331-add-enable-notification-transfer-queue.ts b/backend/src/database/seeds/20240417185331-add-enable-notification-transfer-queue.ts new file mode 100644 index 0000000..00c4069 --- /dev/null +++ b/backend/src/database/seeds/20240417185331-add-enable-notification-transfer-queue.ts @@ -0,0 +1,22 @@ +import { QueryInterface } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.bulkInsert( + "Settings", + [ + { + key: "notificationTransferQueue", + value: "disabled", + createdAt: new Date(), + updatedAt: new Date() + } + ], + {} + ); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.bulkDelete("Settings", {}); + } +}; diff --git a/backend/src/models/User.ts b/backend/src/models/User.ts index 1729d96..f4db2f7 100644 --- a/backend/src/models/User.ts +++ b/backend/src/models/User.ts @@ -51,6 +51,9 @@ class User extends Model { @Column secondaryId: string; + @Column + transferToOtherQueues: boolean; + @Default("admin") @Column profile: string; diff --git a/backend/src/services/StatusChatEndService/CountStatusChatEndService.ts b/backend/src/services/StatusChatEndService/CountStatusChatEndService.ts index bffbb5d..d6b28db 100644 --- a/backend/src/services/StatusChatEndService/CountStatusChatEndService.ts +++ b/backend/src/services/StatusChatEndService/CountStatusChatEndService.ts @@ -11,15 +11,29 @@ const { QueryTypes } = require("sequelize"); const CountStatusChatEndService = async ( startDate: string, - endDate: string + endDate: string, + queueIds?: number[] ) => { + let countStatusChatEnd: any - const countStatusChatEnd: any = await sequelize.query( + if(queueIds && queueIds.length > 0){ + countStatusChatEnd = await sequelize.query( + `select t.id, s.name, count(t.id) as count from Tickets t join StatusChatEnds s on + t.statusChatEndId = s.id and DATE(t.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999' + AND t.queueId IN (${queueIds}) + group by s.id;`, + { type: QueryTypes.SELECT } + ); + } + else{ + countStatusChatEnd = await sequelize.query( `select t.id, s.name, count(t.id) as count from Tickets t join StatusChatEnds s on -t.statusChatEndId = s.id and DATE(t.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999' -group by s.id;`, - { type: QueryTypes.SELECT } - ); + t.statusChatEndId = s.id and DATE(t.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999' + group by s.id;`, + { type: QueryTypes.SELECT } + ); + } + return countStatusChatEnd; }; diff --git a/backend/src/services/TicketServices/CountTicketService.ts b/backend/src/services/TicketServices/CountTicketService.ts index 2cdde93..08f9de3 100644 --- a/backend/src/services/TicketServices/CountTicketService.ts +++ b/backend/src/services/TicketServices/CountTicketService.ts @@ -9,7 +9,7 @@ import ptBR from 'date-fns/locale/pt-BR'; import { splitDateTime } from "../../helpers/SplitDateTime"; const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) -const CountTicketService = async (status: string, date?: string): Promise => { +const CountTicketService = async (status: string, date?: string, queueIds?: string): Promise => { let where_clause = {} @@ -30,8 +30,8 @@ const CountTicketService = async (status: string, date?: string): Promise = // } } - - where_clause = { ...where_clause, status: status } + if(queueIds) where_clause = { ...where_clause, status: status, queueId: { [Op.or]: [queueIds, null] } }; + else where_clause = { ...where_clause, status: status}; const ticket = await Ticket.findAll({ where: where_clause, diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts index 6068b97..4a04a80 100644 --- a/backend/src/services/TicketServices/ListTicketsService.ts +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -257,7 +257,8 @@ const ListTicketsService = async ({ whereCondition = { createdAt: { [Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))] - } + }, + queueId: { [Op.or]: [queueIds, null] }, }; } diff --git a/backend/src/services/TicketServices/ShowTicketReport.ts b/backend/src/services/TicketServices/ShowTicketReport.ts index 1c4b1ea..a3aa41e 100644 --- a/backend/src/services/TicketServices/ShowTicketReport.ts +++ b/backend/src/services/TicketServices/ShowTicketReport.ts @@ -98,6 +98,7 @@ const ShowTicketReport = async ({ "id", "status", "statusChatEnd", + "isRemote", [ Sequelize.fn( "DATE_FORMAT", diff --git a/backend/src/services/UserServices/CreateUserService.ts b/backend/src/services/UserServices/CreateUserService.ts index f866a9b..9e4117c 100644 --- a/backend/src/services/UserServices/CreateUserService.ts +++ b/backend/src/services/UserServices/CreateUserService.ts @@ -14,6 +14,7 @@ interface Request { queueIds?: number[]; profile?: string; ignoreThrow?: boolean; + transferToOtherQueues?: boolean; } interface Response { @@ -23,6 +24,7 @@ interface Response { positionId: string; id: number; profile: string; + transferToOtherQueues: boolean; } const CreateUserService = async ({ @@ -33,7 +35,8 @@ const CreateUserService = async ({ positionId, queueIds = [], profile = "master", - ignoreThrow = false + ignoreThrow = false, + transferToOtherQueues }: Request): Promise => { try { const schema = Yup.object().shape({ @@ -84,7 +87,8 @@ const CreateUserService = async ({ name, positionCompany, positionId: !positionId ? null : positionId, - profile + profile, + transferToOtherQueues: transferToOtherQueues? transferToOtherQueues : false }, { include: ["queues"] } ); diff --git a/backend/src/services/UserServices/ListUserParamiterService.ts b/backend/src/services/UserServices/ListUserParamiterService.ts index 2d92498..de476cd 100644 --- a/backend/src/services/UserServices/ListUserParamiterService.ts +++ b/backend/src/services/UserServices/ListUserParamiterService.ts @@ -10,6 +10,7 @@ interface Request { profiles?: Array; raw?: boolean; userIds?: string | number; + userQueues?: string[]; } const ListUser = async ({ @@ -17,10 +18,31 @@ const ListUser = async ({ userId, raw, userIds, - profiles + profiles, + userQueues: userQueuesToNumber }: Request): Promise => { let where_clause = {}; + let userIdInQueues: number[] = []; + + if(userQueuesToNumber !== undefined){ + let userQueues = userQueuesToNumber.map(id => parseInt(id)); + const userQueuesFiltered = await UserQueue.findAll({ + where: { queueId: { [Op.or]: [userQueues, null] } }, + order: [ + ['userId', 'ASC'] + ], + raw: true + }); + if(userQueuesFiltered) for(let queueId of userQueues){ + for(let userQueue of userQueuesFiltered){ + if(queueId == userQueue.queueId){ + const isAlready = userIdInQueues.indexOf(userQueue.userId); + if(isAlready === -1) userIdInQueues.push(userQueue.userId); + } + } + } + } if (userId && profile) { where_clause = { [Op.and]: [{ userId: userId }, { profile: profile }] @@ -37,16 +59,23 @@ const ListUser = async ({ where_clause = { id: { [Op.in]: userIds } }; - } else if (profiles) { + } + else if (profiles && userIdInQueues.length > 0) { where_clause = { - profile: { [Op.in]: profiles } + profile: { [Op.in]: profiles }, + id: {[Op.in]: userIdInQueues} + }; + } + else if (profiles) { + where_clause = { + profile: { [Op.in]: profiles }, }; } const users = await User.findAll({ where: where_clause, raw, - attributes: ["id", "name", "email", "positionCompany"], + attributes: ["id", "name", "email", "positionCompany", "transferToOtherQueues"], include: [ { model: Queue, as: "queues", attributes: ["id", "name", "color"] } diff --git a/backend/src/services/UserServices/ListUsersService.ts b/backend/src/services/UserServices/ListUsersService.ts index a55ca5c..bfcb4d3 100644 --- a/backend/src/services/UserServices/ListUsersService.ts +++ b/backend/src/services/UserServices/ListUsersService.ts @@ -66,7 +66,8 @@ const ListUsersService = async ({ "email", "positionCompany", "profile", - "createdAt" + "createdAt", + "transferToOtherQueues" ], limit, offset, diff --git a/backend/src/services/UserServices/ShowUserService.ts b/backend/src/services/UserServices/ShowUserService.ts index 1f3d272..8b9d6c8 100644 --- a/backend/src/services/UserServices/ShowUserService.ts +++ b/backend/src/services/UserServices/ShowUserService.ts @@ -12,7 +12,8 @@ const ShowUserService = async (id: string | number): Promise => { "profile", "positionCompany", "positionId", - "tokenVersion" + "tokenVersion", + "transferToOtherQueues" ], include: [ { model: Queue, as: "queues", attributes: ["id", "name", "color"] }, diff --git a/backend/src/services/UserServices/UpdateUserService.ts b/backend/src/services/UserServices/UpdateUserService.ts index 78a5fd2..2f721bb 100644 --- a/backend/src/services/UserServices/UpdateUserService.ts +++ b/backend/src/services/UserServices/UpdateUserService.ts @@ -12,6 +12,7 @@ interface UserData { positionId?: string; profile?: string; queueIds?: number[]; + transferToOtherQueues?: boolean; } interface Request { @@ -75,7 +76,8 @@ const UpdateUserService = async ({ name, positionCompany, positionId, - queueIds = [] + queueIds = [], + transferToOtherQueues } = userData; try { @@ -90,7 +92,8 @@ const UpdateUserService = async ({ profile, positionCompany, positionId: !positionId ? null : positionId, - name + name, + transferToOtherQueues }); await user.$set("queues", queueIds); @@ -117,7 +120,8 @@ const UpdateUserService = async ({ profile: _user.profile, queues: _user.queues, positionId: _user?.positionId, - position: _user.position + position: _user.position, + transferToOtherQueues: _user.transferToOtherQueues }; return serializedUser; diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index cc4e801..c667c20 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -170,7 +170,7 @@ const verifyMediaMessage = async ( if (!media) { throw new Error("ERR_WAPP_DOWNLOAD_MEDIA"); } - + let mediaAuthorized = true; let messageData = { id: msg.id.id, ticketId: ticket.id, @@ -184,7 +184,9 @@ const verifyMediaMessage = async ( phoneNumberId: msg?.phoneNumberId, fromAgent: false }; - + if(messageData.mediaType === 'video' || messageData.mediaType === 'audio' && getSettingValue('blockAudioVideoMedia')?.value === 'enabled'){ + mediaAuthorized = false; + } if (msg?.fromMe) { messageData = { ...messageData, fromAgent: true }; } @@ -199,23 +201,36 @@ const verifyMediaMessage = async ( body: media.filename }; } - - try { - await writeFileAsync( - join(__dirname, "..", "..", "..", "..", "..", "public", media.filename), - media.data, - "base64" - ); - } catch (err) { - Sentry.captureException(err); - logger.error(`There was an error: wbotMessageLitener.ts: ${err}`); + if(mediaAuthorized){ + try { + await writeFileAsync( + join(__dirname, "..", "..", "..", "..", "..", "public", media.filename), + media.data, + "base64" + ); + } catch (err) { + Sentry.captureException(err); + logger.error(`There was an error: wbotMessageLitener.ts: ${err}`); + } } } - - await ticket.update({ lastMessage: msg.body || media.filename }); - const newMessage = await CreateMessageService({ messageData }); - - return newMessage; + if(mediaAuthorized){ + await ticket.update({ lastMessage: msg.body || media.filename }); + const newMessage = await CreateMessageService({ messageData }); + return newMessage; + }else{ + if (ticket.status !== "queueChoice") { + botSendMessage( + ticket, + `Atenção! Mensagem ignorada, tipo de mídia não suportado.` + ); + } + messageData.body = `Mensagem de *${messageData.mediaType}* ignorada, tipo de mídia não suportado.`; + messageData.mediaUrl = ''; + await ticket.update({ lastMessage: `Mensagem de *${messageData.mediaType}* ignorada, tipo de mídia não suportado.`}); + const newMessage = await CreateMessageService({ messageData }); + console.log(`--------->>> Mensagem do tipo: ${messageData.mediaType}, ignorada!`) + } }; // const verifyMediaMessage = async ( @@ -413,13 +428,15 @@ const verifyQueue = async ( } let body = ""; - + const io = getIO(); if (botOptions.length > 0) { body = `\u200e${choosenQueue.greetingMessage}\n\n${botOptions}\n${final_message.msg}`; } else { body = `\u200e${choosenQueue.greetingMessage}`; } + io.emit('notifyPeding', {data: {ticket, queue: choosenQueue}}); + sendWhatsAppMessageSocket(ticket, body); } else { //test del transfere o atendimento se entrar na ura infinita diff --git a/frontend/src/components/NotificationsPopOver/index.js b/frontend/src/components/NotificationsPopOver/index.js index 7840796..1e46dd1 100644 --- a/frontend/src/components/NotificationsPopOver/index.js +++ b/frontend/src/components/NotificationsPopOver/index.js @@ -20,6 +20,9 @@ import useTickets from "../../hooks/useTickets" import alertSound from "../../assets/sound.mp3" import { AuthContext } from "../../context/Auth/AuthContext" +import api from "../../services/api"; +import toastError from "../../errors/toastError"; + const useStyles = makeStyles(theme => ({ tabContainer: { overflowY: "auto", @@ -83,7 +86,7 @@ const NotificationsPopOver = () => { const historyRef = useRef(history) const { handleLogout } = useContext(AuthContext) - + const [settings, setSettings] = useState([]); // const [lastRef] = useState(+history.location.pathname.split("/")[2]) @@ -110,7 +113,22 @@ const NotificationsPopOver = () => { ticketIdRef.current = ticketIdUrl }, [ticketIdUrl]) + useEffect(() => { + const fetchSession = async () => { + try { + const { data } = await api.get('/settings') + setSettings(data.settings) + } catch (err) { + toastError(err) + } + } + fetchSession() + }, []) + const getSettingValue = (key) => { + const { value } = settings.find((s) => s.key === key) + return value + } useEffect(() => { @@ -255,49 +273,80 @@ const NotificationsPopOver = () => { if (shouldNotNotificate) return - - handleNotifications(data) } }) + socket.on('notifyPeding', data =>{ + if(settings?.length > 0 && getSettingValue('notificationTransferQueue') === 'enabled') handleNotifications("", data); + }); + return () => { socket.disconnect() } - }, [user]) + }, [user, settings]) - const handleNotifications = data => { - const { message, contact, ticket } = data + const handleNotifications = (data, notify) => { + let isQueue = false; + if(!notify){ + const { message, contact, ticket } = data - const options = { - body: `${message.body} - ${format(new Date(), "HH:mm")}`, - icon: contact.profilePicUrl, - tag: ticket.id, - renotify: true, - } - - const notification = new Notification( - `${i18n.t("tickets.notification.message")} ${contact.name}`, - options - ) - - notification.onclick = e => { - e.preventDefault() - window.focus() - historyRef.current.push(`/tickets/${ticket.id}`) - } - - setDesktopNotifications(prevState => { - const notfiticationIndex = prevState.findIndex( - n => n.tag === notification.tag - ) - if (notfiticationIndex !== -1) { - prevState[notfiticationIndex] = notification - return [...prevState] + const options = { + body: `${message.body} - ${format(new Date(), "HH:mm")}`, + icon: contact.profilePicUrl, + tag: ticket.id, + renotify: true, } - return [notification, ...prevState] - }) + const notification = new Notification( + `${i18n.t("tickets.notification.message")} ${contact.name}`, + options + ) + + notification.onclick = e => { + e.preventDefault() + window.focus() + historyRef.current.push(`/tickets/${ticket.id}`) + } + + setDesktopNotifications(prevState => { + const notfiticationIndex = prevState.findIndex( + n => n.tag === notification.tag + ) + if (notfiticationIndex !== -1) { + prevState[notfiticationIndex] = notification + return [...prevState] + } + return [notification, ...prevState] + }) + }else{ + user.queues.forEach(queue =>{ + if(queue.id === notify.data?.queue?.id){ + isQueue = true; + } + }) + if(!isQueue){ + return; + }else { + const notification = new Notification(`${i18n.t("tickets.notification.messagePeding")} ${notify.data?.queue?.name}`); + notification.onclick = e => { + e.preventDefault() + window.focus() + historyRef.current.push(`/tickets`) + } + + setDesktopNotifications(prevState => { + const notfiticationIndex = prevState.findIndex( + n => n.tag === notification.tag + ) + if (notfiticationIndex !== -1) { + prevState[notfiticationIndex] = notification + return [...prevState] + } + return [notification, ...prevState] + }) + } + } soundAlertRef.current() } diff --git a/frontend/src/components/Report/SelectField/index.js b/frontend/src/components/Report/SelectField/index.js index ef14f72..b8b48c0 100644 --- a/frontend/src/components/Report/SelectField/index.js +++ b/frontend/src/components/Report/SelectField/index.js @@ -10,11 +10,19 @@ const SelectTextFields = (props) => { if (!props.textBoxFieldSelected) { props.currencies.push({ 'value': 0, 'label': '' }) } + + if(props.textBoxFieldSelected === 'All'){ + const already = props.currencies.findIndex(obj => obj.value === 'All'); + if (already === -1) { + props.currencies.push({ 'value': 'All', 'label': 'All' }) + } + } + useEffect(() => { props.func(currency) - }, [currency, props]) + }, [currency, props.textBoxFieldSelected]) const handleChange = (event) => { diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index b591312..a481700 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -16,6 +16,7 @@ import { AuthContext } from "../../context/Auth/AuthContext" import { SearchTicketContext } from "../../context/SearchTicket/SearchTicket" import { Divider } from "@material-ui/core" +import { ticketsContext } from "../../context/TicketsProvider/TicketsProvider" const useStyles = makeStyles(theme => ({ ticketsListWrapper: { @@ -193,6 +194,8 @@ const TicketsList = (props) => { useEffect(() => { setSettings(setting) }, [setting]) + const { setTickets } = useContext(ticketsContext) + useEffect(() => { @@ -346,6 +349,12 @@ const TicketsList = (props) => { if (typeof updateCount === "function") { updateCount(ticketsList.length) } + if (ticketsList && status === "pending"){ + setTickets(ticketsList) + } + // else{ + // setTickets([]) + // } // eslint-disable-next-line react-hooks/exhaustive-deps }, [ticketsList]) diff --git a/frontend/src/components/TicketsManager/index.js b/frontend/src/components/TicketsManager/index.js index 9c2b71d..19b922b 100644 --- a/frontend/src/components/TicketsManager/index.js +++ b/frontend/src/components/TicketsManager/index.js @@ -1,38 +1,45 @@ -import React, { useContext, useEffect, useRef, useState } from "react"; +import React, { useContext, useEffect, useRef, useState } from "react" -import { makeStyles } from "@material-ui/core/styles"; -import { IconButton } from "@mui/material"; -import Paper from "@material-ui/core/Paper"; -import InputBase from "@material-ui/core/InputBase"; -import Tabs from "@material-ui/core/Tabs"; -import Tab from "@material-ui/core/Tab"; -import Badge from "@material-ui/core/Badge"; +import { makeStyles } from "@material-ui/core/styles" +import { IconButton } from "@mui/material" +import Paper from "@material-ui/core/Paper" +import InputBase from "@material-ui/core/InputBase" +import Tabs from "@material-ui/core/Tabs" +import Tab from "@material-ui/core/Tab" +import Badge from "@material-ui/core/Badge" -import Tooltip from "@material-ui/core/Tooltip"; +import Tooltip from "@material-ui/core/Tooltip" -import SearchIcon from "@material-ui/icons/Search"; -import MoveToInboxIcon from "@material-ui/icons/MoveToInbox"; -import CheckBoxIcon from "@material-ui/icons/CheckBox"; -import MenuIcon from "@material-ui/icons/Menu"; -import FindInPageIcon from '@material-ui/icons/FindInPage'; +import SearchIcon from "@material-ui/icons/Search" +import MoveToInboxIcon from "@material-ui/icons/MoveToInbox" +import CheckBoxIcon from "@material-ui/icons/CheckBox" +import MenuIcon from "@material-ui/icons/Menu" +import FindInPageIcon from '@material-ui/icons/FindInPage' -import FormControlLabel from "@material-ui/core/FormControlLabel"; -import Switch from "@material-ui/core/Switch"; +import FormControlLabel from "@material-ui/core/FormControlLabel" +import Switch from "@material-ui/core/Switch" +import openSocket from "socket.io-client" -import NewTicketModal from "../NewTicketModal"; -import TicketsList from "../TicketsList"; -import TabPanel from "../TabPanel"; +import NewTicketModal from "../NewTicketModal" +import TicketsList from "../TicketsList" +import TabPanel from "../TabPanel" -import { i18n } from "../../translate/i18n"; -import { AuthContext } from "../../context/Auth/AuthContext"; -import { Can } from "../Can"; -import TicketsQueueSelect from "../TicketsQueueSelect"; -import { Button } from "@material-ui/core"; +import { i18n } from "../../translate/i18n" +import { AuthContext } from "../../context/Auth/AuthContext" +import { Can } from "../Can" +import TicketsQueueSelect from "../TicketsQueueSelect" +import { Button } from "@material-ui/core" -import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption"; +import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption" + +import { SearchTicketContext } from "../../context/SearchTicket/SearchTicket" +import useTickets from "../../hooks/useTickets" +import api from "../../services/api" +import toastError from "../../errors/toastError" + +import { ticketsContext } from "../../context/TicketsProvider/TicketsProvider" -import { SearchTicketContext } from "../../context/SearchTicket/SearchTicket"; const useStyles = makeStyles((theme) => ({ ticketsWrapper: { @@ -124,59 +131,106 @@ const useStyles = makeStyles((theme) => ({ hide: { display: "none !important", }, -})); +})) const DEFAULT_SEARCH_PARAM = { searchParam: "", searchParamContent: "" } const TicketsManager = () => { - const { tabOption, setTabOption } = useContext(TabTicketContext); + const { tabOption, setTabOption } = useContext(TabTicketContext) const { setSearchTicket } = useContext(SearchTicketContext) - const classes = useStyles(); + const classes = useStyles() - const [searchParam, setSearchParam] = useState(DEFAULT_SEARCH_PARAM); - const [tab, setTab] = useState("open"); - const [tabOpen, setTabOpen] = useState("open"); - const [newTicketModalOpen, setNewTicketModalOpen] = useState(false); - const [showAllTickets, setShowAllTickets] = useState(false); - const { user } = useContext(AuthContext); + const [searchParam, setSearchParam] = useState(DEFAULT_SEARCH_PARAM) + const [tab, setTab] = useState("open") + const [tabOpen, setTabOpen] = useState("open") + const [newTicketModalOpen, setNewTicketModalOpen] = useState(false) + const [showAllTickets, setShowAllTickets] = useState(false) + const { user, setting, getSettingValue } = useContext(AuthContext) - const [openCount, setOpenCount] = useState(0); - const [pendingCount, setPendingCount] = useState(0); + const [openCount, setOpenCount] = useState(0) + const [pendingCount, setPendingCount] = useState(0) - const userQueueIds = user.queues.map((q) => q.id); - const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []); + const userQueueIds = user.queues.map((q) => q.id) + const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []) const [showContentSearch, setShowContentSearch] = useState(false) - const searchInputRef = useRef(); - const searchContentInputRef = useRef(); - const [inputSearch, setInputSearch] = useState(''); + const searchInputRef = useRef() + const searchContentInputRef = useRef() + const [inputSearch, setInputSearch] = useState('') const [inputContentSearch, setInputContentSearch] = useState("") const [openTooltipSearch, setOpenTooltipSearch] = useState(false) - let searchTimeout; - let searchContentTimeout; + const [waitingTime, setWaitingTime] = useState('00:00') + // const [tickets, setTickets] = useState([]); + const [settings, setSettings] = useState([]) + + let searchTimeout + let searchContentTimeout + + const { tickets, } = useContext(ticketsContext) useEffect(() => { - if (user.profile.toUpperCase() === "ADMIN" || - user.profile.toUpperCase() === "SUPERVISOR" || - user.profile.toUpperCase() === "MASTER") { - setShowAllTickets(true); + setSettings(setting) + }, [setting]) + + useEffect(() => { + if (user.profile.toUpperCase() === "ADMIN" || + user.profile.toUpperCase() === "SUPERVISOR" || + user.profile.toUpperCase() === "MASTER") { + setShowAllTickets(true) } // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); + }, []) useEffect(() => { if (tab === "search") { - searchInputRef.current.focus(); + searchInputRef.current.focus() } setTabOption(tab) - }, [tab, setTabOption]); + }, [tab, setTabOption]) + + useEffect(() => { + + if (settings?.length > 0 && getSettingValue('waitingTimeTickets') !== 'enabled') return + + const calculateAverageTime = () => { + if (tickets.length > 0) { + const now = new Date() + const differenceTime = tickets?.map(ticket => { + const createdAt = new Date(ticket.createdAt) + const difference = now - createdAt + return difference + }) + const sumDifferences = differenceTime.reduce((total, difference) => total + difference, 0) + const averageTimeMilliseconds = sumDifferences / tickets?.length + let hours = Math.floor(averageTimeMilliseconds / 3600000) + const minutes = Math.floor((averageTimeMilliseconds % 3600000) / 60000) + + let days = hours >= 24 ? parseInt(hours / 24) : '' + + if (days != '') hours = hours - (24 * days) + + const averageTimeFormated = `${days != '' ? `${days}d ` : days}${hours.toString().padStart(2, '0')}h${minutes.toString().padStart(2, '0')}` + + return averageTimeFormated + } else return '00:00' + } + + setWaitingTime(calculateAverageTime()) + + const intervalId = setInterval(() => { + setWaitingTime(calculateAverageTime()) + }, 10000) + + return () => clearInterval(intervalId) + + }, [tickets]) useEffect(() => { @@ -184,7 +238,7 @@ const TicketsManager = () => { // setSearchParam(prev => ({ ...prev, searchParamContent: "" })) - if (!inputContentSearch) return + if (!inputContentSearch) return if (!searchContentTimeout) return @@ -194,29 +248,29 @@ const TicketsManager = () => { // }, 500); - clearTimeout(searchContentTimeout); + clearTimeout(searchContentTimeout) - setSearchParam(prev => ({ ...prev, searchParamContent: "" })) + setSearchParam(prev => ({ ...prev, searchParamContent: "" })) - }, [inputContentSearch, searchContentTimeout]); + }, [inputContentSearch, searchContentTimeout]) useEffect(() => { - + //console.log(selectedQueueIds); if (tabOption === 'open') { setTabOption('') - setSearchParam(DEFAULT_SEARCH_PARAM); - setInputSearch(''); + setSearchParam(DEFAULT_SEARCH_PARAM) + setInputSearch('') setInputContentSearch('') - setTab("open"); - return; + setTab("open") + return } }, [tabOption, setTabOption]) - + const removeExtraSpace = (str) => { str = str.replace(/^\s+/g, '') @@ -231,14 +285,14 @@ const TicketsManager = () => { setSearchTicket(searchParam.searchParam) - clearTimeout(searchTimeout); + clearTimeout(searchTimeout) if (searchedTerm === "") { setSearchParam(prev => ({ ...prev, searchParam: searchedTerm })) setInputSearch(searchedTerm) setShowContentSearch(false) - setTab("open"); - return; + setTab("open") + return } if (searchedTerm.length < 4) { @@ -249,22 +303,22 @@ const TicketsManager = () => { searchTimeout = setTimeout(() => { - setSearchParam(prev => ({ ...prev, searchParam: searchedTerm })); + setSearchParam(prev => ({ ...prev, searchParam: searchedTerm })) - }, 500); - }; + }, 500) + } const handleContentSearch = e => { let searchedContentText = removeExtraSpace(e.target.value.toLowerCase()) - setInputContentSearch(searchedContentText) - + setInputContentSearch(searchedContentText) + searchContentTimeout = setTimeout(() => { - setSearchParam(prev => ({ ...prev, searchParamContent: searchedContentText })); + setSearchParam(prev => ({ ...prev, searchParamContent: searchedContentText })) - }, 500); + }, 500) } @@ -282,18 +336,18 @@ const TicketsManager = () => { } const handleChangeTab = (e, newValue) => { - setTab(newValue); - }; + setTab(newValue) + } const handleChangeTabOpen = (e, newValue) => { - setTabOpen(newValue); - }; + setTabOpen(newValue) + } const applyPanelStyle = (status) => { if (tabOpen !== status) { - return { width: 0, height: 0 }; + return { width: 0, height: 0 } } - }; + } return ( @@ -448,7 +502,25 @@ const TicketsManager = () => { } value={"pending"} - /> + />{ + (settings?.length > 0 && getSettingValue('waitingTimeTickets') === 'enabled') && + + + + {/* */} + + + + + } { - ); -}; + ) +} -export default TicketsManager; \ No newline at end of file +export default TicketsManager \ No newline at end of file diff --git a/frontend/src/components/TransferTicketModal/index.js b/frontend/src/components/TransferTicketModal/index.js index 472565b..02acdfc 100644 --- a/frontend/src/components/TransferTicketModal/index.js +++ b/frontend/src/components/TransferTicketModal/index.js @@ -97,7 +97,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => { } else { - if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled') { + if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled' && !user.transferToOtherQueues) { setQueues(_queues) } else { @@ -190,7 +190,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => { try { - if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled') { + if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled' && !user.transferToOtherQueues) { const { data } = await api.get(`/users/all`, { params: { userId: user.id }, }) @@ -202,7 +202,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => { else { const { data } = await api.get(`/users/all`, { - params: { profile: 'user' }, + params: { profile: 'user', transferToOtherQueues: user.transferToOtherQueues }, }) setUsers(data.users) diff --git a/frontend/src/components/UserModal/index.js b/frontend/src/components/UserModal/index.js index 51d660d..976fc8c 100644 --- a/frontend/src/components/UserModal/index.js +++ b/frontend/src/components/UserModal/index.js @@ -32,6 +32,7 @@ import toastError from "../../errors/toastError" import QueueSelect from "../QueueSelect" import { AuthContext } from "../../context/Auth/AuthContext" import { Can } from "../Can" +import Switch from '@mui/material/Switch' const useStyles = makeStyles(theme => ({ root: { @@ -95,6 +96,7 @@ const UserModal = ({ open, onClose, userId, }) => { const [showPassword, setShowPassword] = useState(false) const [positions, setPositions] = useState([]) const [selectedPosition, setSelectedPosition] = useState('') + const [checked, setChecked] = useState(false) useEffect(() => { const fetchUser = async () => { @@ -112,6 +114,9 @@ const UserModal = ({ open, onClose, userId, }) => { setSelectedPosition(data.positionId) else setSelectedPosition('') + + + if(data.transferToOtherQueues) setChecked(data.transferToOtherQueues); } catch (err) { toastError(err) } @@ -136,10 +141,15 @@ const UserModal = ({ open, onClose, userId, }) => { const handleClose = () => { onClose() setUser(initialState) + setChecked(false); + } + + const handleChange = (event) => { + setChecked(event.target.checked) } const handleSaveUser = async values => { - const userData = { ...values, queueIds: selectedQueueIds, positionId: selectedPosition } + const userData = { ...values, queueIds: selectedQueueIds, positionId: selectedPosition, transferToOtherQueues: checked} try { if (userId) { @@ -252,7 +262,7 @@ const UserModal = ({ open, onClose, userId, }) => { fullWidth />
- { variant="outlined" margin="dense" fullWidth - /> + /> */} + + { + + const [tickets, setTickets] = useState(0) + + return ( + + {children} + + ) +} + +export { ticketsContext, TicketsProvider } \ No newline at end of file diff --git a/frontend/src/pages/Dashboard/Chart.js b/frontend/src/pages/Dashboard/Chart.js index 5d0de34..651347a 100644 --- a/frontend/src/pages/Dashboard/Chart.js +++ b/frontend/src/pages/Dashboard/Chart.js @@ -20,9 +20,10 @@ const Chart = (props) => { const theme = useTheme(); const date = useRef(new Date().toISOString()); - let { tickets } = useTickets({ date: date.current, unlimited: "current" }); + const queueIds = JSON.stringify( props.selectedQueue) || {}; + let {tickets} = useTickets({ date: date.current, unlimited: "current", queueIds }); - const [chartData, setChartData] = useState([ + const modelChar = [ { time: "08:00", amount: 0 }, { time: "09:00", amount: 0 }, { time: "10:00", amount: 0 }, @@ -35,11 +36,12 @@ const Chart = (props) => { { time: "17:00", amount: 0 }, { time: "18:00", amount: 0 }, { time: "19:00", amount: 0 }, - ]); + ] + const [chartData, setChartData] = useState(modelChar); useEffect(() => { setChartData(prevState => { - let aux = [...prevState]; + let aux = modelChar; aux.forEach(a => { tickets.forEach(ticket => { format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time && a.amount++; }); diff --git a/frontend/src/pages/Dashboard/index.js b/frontend/src/pages/Dashboard/index.js index ca6fe4e..3fa4e2d 100644 --- a/frontend/src/pages/Dashboard/index.js +++ b/frontend/src/pages/Dashboard/index.js @@ -1,4 +1,4 @@ -import React, { useContext, useReducer, useEffect, useState } from "react" +import React, { useContext, useReducer, useEffect, useState, useCallback } from "react" import { addHours, addMinutes, addSeconds, intervalToDuration } from "date-fns" @@ -11,6 +11,7 @@ import Tooltip from "@mui/material/Tooltip" import Zoom from "@mui/material/Zoom" import IconButton from "@mui/material/IconButton" import Info from "@material-ui/icons/Info" +import SelectField from "../../components/Report/SelectField" import { AuthContext } from "../../context/Auth/AuthContext" // import { i18n } from "../../translate/i18n"; @@ -254,12 +255,15 @@ const reducer = (state, action) => { } const Dashboard = () => { + const { user } = useContext(AuthContext) const classes = useStyles() const [usersOnlineInfo, dispatch] = useReducer(reducer, []) const [ticketStatusChange, setStatus] = useState() const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 }) const [ticketStatusChatEnd, setTicketStatusChatEnd] = useState([]) - const { user } = useContext(AuthContext) + + const userQueueIds = user.queues?.map((q) => q.id); + const [selectedQueue, setSelectedQueue] = useState(userQueueIds || []); useEffect(() => { dispatch({ type: "RESET" }) @@ -286,14 +290,14 @@ const Dashboard = () => { let dateToday = `${date[2]}-${date[1]}-${date[0]}` const { data } = await api.get("/reports/user/services", { - params: { userId: null, startDate: dateToday, endDate: dateToday }, + params: { userId: null, startDate: dateToday, endDate: dateToday, userQueues: selectedQueue }, }) dispatch({ type: "RESET" }) dispatch({ type: "LOAD_QUERY", payload: data.usersProfile }) const { data: ticketStatusChatEndData } = await api.get("/reports/count/statusChatEnd", { - params: { startDate: dateToday, endDate: dateToday }, + params: { startDate: dateToday, endDate: dateToday, userQueues: selectedQueue }, }) setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd) @@ -306,7 +310,7 @@ const Dashboard = () => { fetchQueries() }, 500) return () => clearTimeout(delayDebounceFn) - }, []) + }, [selectedQueue]) useEffect(() => { @@ -380,6 +384,18 @@ const Dashboard = () => { socket.disconnect() } }, []) + + const handleSelectedQueue = useCallback((queueSelected) => { + if(queueSelected !== 'All'){ + const queueIndex = user?.queues?.findIndex((q) => q.id === parseInt(queueSelected)); + const queueIds = [] + queueIds.push(user?.queues[queueIndex]?.id); + setSelectedQueue(queueIds); + }else{ + const queueIds = user?.queues?.map((queue) => queue.id); + setSelectedQueue(queueIds); + } + },[user, setSelectedQueue]) useEffect(() => { if (ticketStatusChange === "") return @@ -390,17 +406,17 @@ const Dashboard = () => { let dateToday = `${date[2]}-${date[1]}-${date[0]}` const _open = await api.get("/tickets/count", { - params: { status: "open", date: dateToday }, + params: { status: "open", date: dateToday, queueIds: selectedQueue }, }) const _closed = await api.get("/tickets/count", { - params: { status: "closed", date: dateToday }, + params: { status: "closed", date: dateToday, queueIds: selectedQueue }, }) const _pending = await api.get("/tickets/count", { - params: { status: "pending" }, + params: { status: "pending", queueIds: selectedQueue }, }) const _openAll = await api.get("/tickets/count", { - params: { status: "open" }, + params: { status: "open", queueIds: selectedQueue }, }) setTicktsStatus({ open: _open.data.count, @@ -419,7 +435,7 @@ const Dashboard = () => { fetchQueries() }, 500) return () => clearTimeout(delayDebounceFn) - }, [ticketStatusChange]) + }, [ticketStatusChange, selectedQueue]) return ( { + + { + return { 'value': obj.id, 'label': obj.name } + })} /> + { - + diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index c57242d..1a18d50 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -224,6 +224,7 @@ Item.propTypes = { let columnsData = [ + { title: `Tipo`, field: 'isRemote' }, { title: `${i18n.t("reports.listColumns.column1_1")}`, field: 'whatsapp.name' }, { title: `${i18n.t("reports.listColumns.column1_2")}`, field: 'user.name' }, { title: `${i18n.t("reports.listColumns.column0_4")}`, field: 'contact.number' }, @@ -237,9 +238,12 @@ let columnsData = [ { title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }, { title: `Espera`, field: 'waiting_time' }, { title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true }, -] + { title: `Link`, field: 'link', searchable: false, hidden: true, export: true }, +] let columnsDataSuper = [ + { title: `Tipo`, field: 'isRemote' }, + { title: `${i18n.t("reports.listColumns.column1_0")}`, field: 'isRemote' }, { title: `${i18n.t("reports.listColumns.column1_1")}`, field: 'whatsapp.name' }, { title: `${i18n.t("reports.listColumns.column1_2")}`, field: 'user.name' }, { title: `${i18n.t("reports.listColumns.column0_3")}`, field: 'contact.name' }, @@ -252,6 +256,7 @@ let columnsDataSuper = [ { title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }, { title: `Espera`, field: 'waiting_time' }, { title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true }, + { title: `Link`, field: 'link', searchable: false, hidden: true, export: true }, ] @@ -374,12 +379,15 @@ const Report = () => { filterQueuesTickets = ticketsQueue.filter(ticket => ticket?.queue?.name === userQueues[0]?.name) } data.tickets = filterQueuesTickets - const tickets = data.tickets.map(ticket => ({ + const tickets = data.tickets.map(ticket => { + ticket.isRemote = ticket.isRemote ? 'Remoto' : 'Comum'; + return ({ ...ticket, messagesToFilter: ticket.messages.map(message => message.body).join(' '), - })) + link: `${process.env.REACT_APP_FRONTEND_URL}/tickets/${ticket.id}` + }) + }) dispatchQ({ type: "LOAD_QUERY", payload: tickets }) - // console.log(tickets) setHasMore(data.hasMore) setTotalCountTickets(data.count) setLoading(false) @@ -680,54 +688,56 @@ const Report = () => { const renderSwitch = (param) => { - switch (param) { - case 'empty': - return ( - <> - {query && query.length > 0 && - - } - {/* */} - ) + if(userA.profile !== 'supervisor'){ + switch (param) { + case 'empty': + return ( + <> + {query && query.length > 0 && + + } + {/* */} + ) - case 'pending' || 'processing': - return ( - <> - PROCESSING... - ) + case 'pending' || 'processing': + return ( + <> + PROCESSING... + ) - case 'success': - return ( - <> - - ) - case 'downloading': - return ( - <> - DOWNLOADING... - ) + case 'success': + return ( + <> + + ) + case 'downloading': + return ( + <> + DOWNLOADING... + ) - default: - return (<>WAITING...) + default: + return (<>WAITING...) + } } } @@ -871,7 +881,7 @@ const Report = () => { <> {
+
+ + + + Noficar quando entrar novo ticket na fila + + + + + +
+
+ + + + Bloquear mídias de Audio e Video + + + + + +
+ +
+ + + + Mostrar tempo de espera dos tickets aguardando + + + + + +
)} /> diff --git a/frontend/src/pages/Tickets/index.js b/frontend/src/pages/Tickets/index.js index 21a254d..0900471 100644 --- a/frontend/src/pages/Tickets/index.js +++ b/frontend/src/pages/Tickets/index.js @@ -11,6 +11,7 @@ import { i18n } from "../../translate/i18n"; import Hidden from "@material-ui/core/Hidden"; import { SearchTicketProvider } from "../../context/SearchTicket/SearchTicket"; +import { TicketsProvider } from "../../context/TicketsProvider/TicketsProvider" const useStyles = makeStyles((theme) => ({ chatContainer: { @@ -82,7 +83,9 @@ const Chat = () => { } > - + + +