From ffcefe8f12dedc8b3d8f8fde7f80bbe2e5b2d016 Mon Sep 17 00:00:00 2001 From: adriano Date: Thu, 28 Jul 2022 14:55:23 -0300 Subject: [PATCH 1/3] =?UTF-8?q?Finaliza=C3=A7=C3=A3o=20do=20relatorio=20ca?= =?UTF-8?q?rregado=20sob=20demanda?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/controllers/ReportController.ts | 13 +- .../20220221193811-create-scheduling-data.ts | 2 +- .../helpers/SchedulingNotifySendMessage.ts | 31 ++ .../TicketServices/ShowTicketReport.ts | 42 +- .../WbotServices/wbotMessageListener.ts | 1 + frontend/src/components/MessagesList/index.js | 8 +- .../src/components/Report/MTable/index.js | 170 +++++-- .../components/TicketActionButtons/index.js | 2 +- frontend/src/components/TicketsList/index.js | 7 +- frontend/src/hooks/useWhatsApps/index.js | 25 +- frontend/src/pages/Connections/index.js | 429 ++++++++++-------- frontend/src/pages/Contacts/index.js | 9 + frontend/src/pages/Report/index.js | 177 +++++--- frontend/src/rules.js | 1 + 14 files changed, 588 insertions(+), 329 deletions(-) diff --git a/backend/src/controllers/ReportController.ts b/backend/src/controllers/ReportController.ts index bf59bbb..588e494 100644 --- a/backend/src/controllers/ReportController.ts +++ b/backend/src/controllers/ReportController.ts @@ -24,6 +24,7 @@ type IndexQuery = { userId: string; startDate: string; endDate: string; + pageNumber: string; }; @@ -33,13 +34,17 @@ export const reportUserByDateStartDateEnd = async (req: Request, res: Response): throw new AppError("ERR_NO_PERMISSION", 403); } - const { userId, startDate, endDate } = req.query as IndexQuery + const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery + + + console.log('PAGE NUMBER: ', pageNumber) - const data_query = await ShowTicketReport(userId, startDate, endDate); + const { tickets, count, hasMore } = await ShowTicketReport({userId, startDate, endDate, pageNumber}); - return res.status(200).json(data_query); - + // return res.status(200).json(data_query); + + return res.status(200).json({ tickets, count, hasMore }); }; diff --git a/backend/src/database/seeds/20220221193811-create-scheduling-data.ts b/backend/src/database/seeds/20220221193811-create-scheduling-data.ts index 34514f8..06d37d5 100644 --- a/backend/src/database/seeds/20220221193811-create-scheduling-data.ts +++ b/backend/src/database/seeds/20220221193811-create-scheduling-data.ts @@ -6,7 +6,7 @@ module.exports = { "StatusChatEnds", [ { - name: "SEM RETORNO DO CLIENTE", + name: "FINALIZADO", createdAt: new Date(), updatedAt: new Date() }, diff --git a/backend/src/helpers/SchedulingNotifySendMessage.ts b/backend/src/helpers/SchedulingNotifySendMessage.ts index 792281a..5a9ca55 100644 --- a/backend/src/helpers/SchedulingNotifySendMessage.ts +++ b/backend/src/helpers/SchedulingNotifySendMessage.ts @@ -13,6 +13,8 @@ const fastFolderSize = require('fast-folder-size') const { promisify } = require('util') const fs = require('fs') +const { exec } = require("child_process"); + let scheduler_monitor: any; let timeInterval = 5 @@ -53,6 +55,35 @@ const monitor = async () => { } + exec("df -h /", (error: any, stdout: any, stderr: any) => { + + if (error) { + console.log(`exec error: ${error.message}`); + return; + } + if (stderr) { + console.log(`exec stderr: ${stderr}`); + return; + } + + stdout = stdout.split(/\r?\n/) + stdout = stdout[1].trim().split(/\s+/) + + // DISK SPACE MONITORING + const io = getIO(); + io.emit("diskSpaceMonit", { + action: "update", + diskSpace: { + size: stdout[1], + used: stdout[2], + available: stdout[3], + use: stdout[4] + } + }); + + }); + + // WHATS SESSION SIZE MONITORING const whatsapps = await ListWhatsAppsService(); diff --git a/backend/src/services/TicketServices/ShowTicketReport.ts b/backend/src/services/TicketServices/ShowTicketReport.ts index 835922b..f11e41a 100644 --- a/backend/src/services/TicketServices/ShowTicketReport.ts +++ b/backend/src/services/TicketServices/ShowTicketReport.ts @@ -16,12 +16,31 @@ import { startOfDay, endOfDay, parseISO, getDate} from "date-fns"; import { string } from "yup/lib/locale"; import Whatsapp from "../../models/Whatsapp"; +interface Request { + userId: string | number; + startDate: string; + endDate: string; + pageNumber?: string; +} + + +interface Response { + tickets: Ticket[]; + count: number; + hasMore: boolean; +} + //Report by user, startDate, endDate -const ShowTicketReport = async (id: string | number, startDate: string, endDate: string): Promise => { +const ShowTicketReport = async ({ + userId, + startDate, + endDate, + pageNumber = "1" +}: Request): Promise => { let where_clause = {} - if(id=='0'){ + if(userId=='0'){ where_clause = { createdAt: { [Op.gte]: startDate+' 00:00:00.000000', @@ -31,7 +50,7 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate: } else{ where_clause = { - userid: id, + userid: userId, createdAt: { [Op.gte]: startDate+' 00:00:00.000000', [Op.lte]: endDate +' 23:59:59.999999' @@ -40,11 +59,14 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate: } - + const limit = 40; + const offset = limit * (+pageNumber - 1); - const ticket = await Ticket.findAll({ + const {count, rows: tickets} = await Ticket.findAndCountAll({ where: where_clause , + limit, + offset, //attributes: ['id', 'status', 'createdAt', 'updatedAt'], attributes: ['id', 'status', 'statusChatEnd', [Sequelize.fn("DATE_FORMAT",Sequelize.col("Ticket.createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"], @@ -81,15 +103,21 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate: attributes: ['name'] }, ], + + order: [ + ['id', 'ASC'] + ] }); + + const hasMore = count > offset + tickets.length; - if (!ticket) { + if (!tickets) { throw new AppError("ERR_NO_TICKET_FOUND", 404); } - return ticket; + return {tickets, count, hasMore}; }; export default ShowTicketReport; diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 961fb99..caeae3b 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -825,6 +825,7 @@ const handleMsgAck = async (msg: WbotMessage, ack: MessageAck) => { action: "update", message: messageToUpdate }); + } catch (err) { Sentry.captureException(err); logger.error(`Error handling message ack. Err: ${err}`); diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index 0344c7b..3f7fe46 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -362,15 +362,17 @@ const MessagesList = ({ ticketId, isGroup }) => { socket.on("connect", () => socket.emit("joinChatBox", ticketId)); socket.on("appMessage", (data) => { + if (data.action === "create") { - dispatch({ type: "ADD_MESSAGE", payload: data.message }); - - // console.log('* NOVA MENSAGEM CAP: ', data.message) + dispatch({ type: "ADD_MESSAGE", payload: data.message }); scrollToBottom(); } if (data.action === "update") { + + console.log('2 THIS IS THE DATA: ', data) + dispatch({ type: "UPDATE_MESSAGE", payload: data.message }); } }); diff --git a/frontend/src/components/Report/MTable/index.js b/frontend/src/components/Report/MTable/index.js index a2ca6b5..b3f5dbe 100644 --- a/frontend/src/components/Report/MTable/index.js +++ b/frontend/src/components/Report/MTable/index.js @@ -4,82 +4,154 @@ import MaterialTable from 'material-table'; import Modal from '../Modal' import { render } from '@testing-library/react'; +import chat from '@material-ui/icons/Chat'; + +// import Button from "@material-ui/core/Button"; + import React from 'react'; const MTable = (props) => { - const [selectedRow, setSelectedRow] = useState(null); - + const tableRef = React.useRef(); + + const openInNewTab = url => { + window.open(url, '_blank', 'noopener,noreferrer'); + }; + + const [selectedRow, setSelectedRow] = useState(null); //const dataLoad = props.data.map((dt) => { return { ...dt }}); const dataLoad = props.data.map(({ user, ...others }) => ({ ...others, 'user': user ? user : { name: 'Aguardando atendente', email: '' } })); const columnsLoad = props.columns.map((column) => { return { ...column } }); + useEffect(() => { console.log(`You have clicked the button ${selectedRow} times`) + // console.log('TABLE REF: ', tableRef) + }, [selectedRow]); + + useEffect(() => { + + if (!tableRef.current) return + + const element = tableRef.current.tableContainerDiv.current; + + element.addEventListener('scroll', props.handleScroll); + + return () => { + element.removeEventListener('scroll', props.handleScroll); + }; + + }, [props]); + + + + + + return ( - + {/* */} - maxWidth={true} - - onRowClick={(evt, selectedRow) => { + {/* */} - if(props.removeClickRow){ - return + { + + if (props.removeClickRow) { + return + } + + console.log(selectedRow.tableData.id); + console.log(selectedRow); + console.log(selectedRow.messages); + setSelectedRow(selectedRow.tableData.id) + + if (props.hasChild) { + render() + } + + console.log('props.hasChild: ', props.hasChild) + + //evt.stopPropagation() } - - console.log(selectedRow.tableData.id); - console.log(selectedRow); - console.log(selectedRow.messages); - setSelectedRow(selectedRow.tableData.id) - - if (props.hasChild) { - render() } - console.log('props.hasChild: ', props.hasChild) - //evt.stopPropagation() - } - } + actions={[ + (rowData) => { - options={{ - search: true, - selection: false, - paging: false, - padding: 'dense', - sorting: true ? props.hasChild : false, - //loadingType: 'linear', - searchFieldStyle: { - width: 300, - }, + if(props.hasChild){ + return { + icon: chat, + tooltip: `Ticket id ${rowData.id}`, + disable: false, + onClick: (event, rowData) => { + + openInNewTab(`/tickets/${rowData.id}`) + + } + } + } + } + ]} + + options={{ + search: true, + selection: false, + paging: false, + padding: 'dense', + + exportButton: props.hasChild ? true : null, + exportAllData: true, + + sorting: true ? props.hasChild : false, + loadingType: 'circular', + searchFieldStyle: { + width: 300, + }, + + pageSize: 20, + + headerStyle: { + position: "sticky", + top: "0" + }, + + maxBodyHeight: "400px", + // minBodyHeight: "85vh", //FIXME to calculate dynamic height, needed for correct scroll position identification + // maxBodyHeight: "85vh", + + + + rowStyle: rowData => ({ + fontSize: 12, + backgroundColor: selectedRow === rowData.tableData.id ? '#ec5114' : '#FFF' + }) + }} + /> + - pageSize: 20, - headerStyle: { - position: "sticky", - top: "0" - }, - maxBodyHeight: "400px", - rowStyle: rowData => ({ - fontSize: 12, - backgroundColor: selectedRow === rowData.tableData.id ? '#ec5114' : '#FFF' - }) - }} - /> ); }; diff --git a/frontend/src/components/TicketActionButtons/index.js b/frontend/src/components/TicketActionButtons/index.js index 3919503..e8a21e3 100644 --- a/frontend/src/components/TicketActionButtons/index.js +++ b/frontend/src/components/TicketActionButtons/index.js @@ -35,7 +35,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => { const ticketOptionsMenuOpen = Boolean(anchorEl); const { user } = useContext(AuthContext); - const handleOpenTicketOptionsMenu = e => { + const handleOpenTicketOptionsMenu = e => { setAnchorEl(e.currentTarget); }; diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 1fb3263..442cbd4 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -235,7 +235,7 @@ const TicketsList = (props) => { }); } - if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { + if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); } @@ -285,10 +285,11 @@ const TicketsList = (props) => { setPageNumber(prevState => prevState + 1); }; - const handleScroll = e => { + const handleScroll = e => { + if (!hasMore || loading) return; - const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; if (scrollHeight - (scrollTop + 100) < clientHeight) { loadMore(); diff --git a/frontend/src/hooks/useWhatsApps/index.js b/frontend/src/hooks/useWhatsApps/index.js index 19e8717..c7ed37b 100644 --- a/frontend/src/hooks/useWhatsApps/index.js +++ b/frontend/src/hooks/useWhatsApps/index.js @@ -39,6 +39,19 @@ const reducer = (state, action) => { } + // if (action.type === "UPDATE_DISK_SPACE_MONIT") { + // const whatsApp = action.payload; + // const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); + + // if (whatsAppIndex !== -1) { + // state[whatsAppIndex].sessionSize = whatsApp.sessionSize; + // return [...state]; + // } else { + // return [whatsApp, ...state]; + // } + // } + + if (action.type === "UPDATE_WHATSAPPS_SESSION_MONIT") { const whatsApp = action.payload; const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); @@ -106,11 +119,7 @@ const useWhatsApps = () => { dispatch({ type: "UPDATE_SESSION", payload: data.session }); } - }); - - - - //test del + }); socket.on("whatsappSessionMonit", data => { if (data.action === "update") { @@ -119,9 +128,9 @@ const useWhatsApps = () => { dispatch({ type: "UPDATE_WHATSAPPS_SESSION_MONIT", payload: data.whatsappSessionSize }); } - }); - - // + }); + + return () => { socket.disconnect(); diff --git a/frontend/src/pages/Connections/index.js b/frontend/src/pages/Connections/index.js index 1cf53f6..2efc13d 100644 --- a/frontend/src/pages/Connections/index.js +++ b/frontend/src/pages/Connections/index.js @@ -1,7 +1,9 @@ -import React, { useState, useCallback, useContext } from "react"; +import React, { useState, useCallback, useEffect, useContext } from "react"; import { toast } from "react-toastify"; import { format, parseISO } from "date-fns"; +import openSocket from "socket.io-client"; + import { makeStyles } from "@material-ui/core/styles"; import { green } from "@material-ui/core/colors"; import { @@ -111,6 +113,8 @@ const Connections = () => { const [selectedWhatsApp, setSelectedWhatsApp] = useState(null); const [confirmModalOpen, setConfirmModalOpen] = useState(false); + const [diskSpaceInfo, setDiskSpaceInfo] = useState({}); + @@ -324,6 +328,23 @@ const Connections = () => { ); }; + + useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + + socket.on("diskSpaceMonit", data => { + if (data.action === "update") { + + setDiskSpaceInfo(data.diskSpace) + + } + }); + + return () => { + socket.disconnect(); + }; + }, []); + return ( { /> + + {i18n.t("connections.title")} + { )} /> + + - - - + <> - - {i18n.t("connections.table.name")} - - - - {i18n.t("connections.table.status")} - - - ( - - {i18n.t("connections.table.session")} - - )} - /> - - - - - ( - - Restore - - )} - /> - - - - ( - - Session MB - - )} - /> - - - - - - {i18n.t("connections.table.lastUpdate")} - - - {i18n.t("connections.table.default")} - - - {i18n.t("connections.table.actions")} - - - - - {loading ? ( - - ) : ( + ( <> - {whatsApps?.length > 0 && - whatsApps.map(whatsApp => ( - - {whatsApp.name} - - - {renderStatusToolTips(whatsApp)} - - - ( - - {renderActionButtons(whatsApp)} - - )} - /> - - - - - - - ( - - - - - - )} - /> - - - - ( - - -
{whatsApp.sessionSize}
-
-
- )} - /> - - - - - - - {format(parseISO(whatsApp.updatedAt), "dd/MM/yy HH:mm")} - - - - - {whatsApp.isDefault && ( -
- -
- )} -
- - - - - ( - handleEditWhatsApp(whatsApp)} - > - - - )} - /> - - - ( - { - handleOpenConfirmationModal("delete", whatsApp.id); - }} - > - - - )} - /> - - -
- ))} - +
+ + + + Size + + + Used + + + Available + + + Use% + + + + + + + {diskSpaceInfo.size} + {diskSpaceInfo.used} + {diskSpaceInfo.available} + {diskSpaceInfo.use} + + +
+
+ )} - - + /> + + + + + + + {i18n.t("connections.table.name")} + + + + {i18n.t("connections.table.status")} + + + ( + + {i18n.t("connections.table.session")} + + )} + /> + + + + + ( + + Restore + + )} + /> + + + + ( + + Session MB + + )} + /> + + + + + + {i18n.t("connections.table.lastUpdate")} + + + {i18n.t("connections.table.default")} + + + {i18n.t("connections.table.actions")} + + + + + {loading ? ( + + ) : ( + <> + {whatsApps?.length > 0 && + whatsApps.map(whatsApp => ( + + {whatsApp.name} + + + {renderStatusToolTips(whatsApp)} + + + ( + + {renderActionButtons(whatsApp)} + + )} + /> + + + + + + + ( + + + + + + )} + /> + + + + ( + + +
{whatsApp.sessionSize}
+
+
+ )} + /> + + + + + + + {format(parseISO(whatsApp.updatedAt), "dd/MM/yy HH:mm")} + + + + + {whatsApp.isDefault && ( +
+ +
+ )} +
+ + + + + ( + handleEditWhatsApp(whatsApp)} + > + + + )} + /> + + + ( + { + handleOpenConfirmationModal("delete", whatsApp.id); + }} + > + + + )} + /> + + +
+ ))} + + )} +
+
+ +
)} diff --git a/frontend/src/pages/Contacts/index.js b/frontend/src/pages/Contacts/index.js index e2df88d..da21442 100644 --- a/frontend/src/pages/Contacts/index.js +++ b/frontend/src/pages/Contacts/index.js @@ -36,17 +36,23 @@ import { AuthContext } from "../../context/Auth/AuthContext"; import { Can } from "../../components/Can"; const reducer = (state, action) => { + + if (action.type === "LOAD_CONTACTS") { + const contacts = action.payload; const newContacts = []; contacts.forEach((contact) => { + const contactIndex = state.findIndex((c) => c.id === contact.id); + if (contactIndex !== -1) { state[contactIndex] = contact; } else { newContacts.push(contact); } + }); return [...state, ...newContacts]; @@ -210,6 +216,9 @@ const Contacts = () => { const handleScroll = (e) => { if (!hasMore || loading) return; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + + // console.log('scrollTop: ', scrollTop, ' | scrollHeight: ', scrollHeight, ' | clientHeight: ', clientHeight) + if (scrollHeight - (scrollTop + 100) < clientHeight) { loadMore(); } diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index 4b5d94d..cd59b8b 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -18,10 +18,13 @@ import MaterialTable from 'material-table'; import LogoutIcon from '@material-ui/icons/CancelOutlined'; + + import { CSVLink } from "react-csv"; +// import CircularProgress from '@mui/material/CircularProgress'; -import openSocket from "socket.io-client"; +import openSocket from "socket.io-client"; const report = [{ 'value': '1', 'label': 'Atendimento por atendentes' }, { 'value': '2', 'label': 'Usuários online/offline' }] @@ -77,7 +80,7 @@ let columns = [ }, { key: 'ticket.statusChatEnd', - label: 'Status de encerramento', + label: 'Status de encerramento', } ] @@ -104,7 +107,7 @@ const reducerQ = (state, action) => { const userIndex = state.findIndex((u) => `${u.id}` === `${userId}`); - // console.log('>>>>>>>>>>>>>>>>>>>>> userIndex: ', userIndex) + // console.log('>>>>>>>>>>>>>>>>>>>>> userIndex: ', userIndex) if (userIndex !== -1) { state.splice(userIndex, 1); @@ -117,7 +120,6 @@ const reducerQ = (state, action) => { if (action.type === 'LOAD_QUERY') { - const queries = action.payload const newQueries = [] @@ -143,7 +145,7 @@ const reducerQ = (state, action) => { let onlineUser = action.payload let index = -1 - // console.log('sssssssssstate: ', state, ' | ONLINE USERS: onlineUser.userId ', onlineUser.userId) + // console.log('sssssssssstate: ', state, ' | ONLINE USERS: onlineUser.userId ', onlineUser.userId) if (onlineUser.sumOpen || onlineUser.sumClosed) { index = state.findIndex((e) => ((onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) || (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId))) @@ -152,13 +154,13 @@ const reducerQ = (state, action) => { index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`) } - //console.log(' *********************** index: ', index) + //console.log(' *********************** index: ', index) if (index !== -1) { - // console.log('ENTROU NO INDEX') + // console.log('ENTROU NO INDEX') if (!("statusOnline" in state[index])) { @@ -183,10 +185,10 @@ const reducerQ = (state, action) => { if (onlineUser.sumOpen) { if ("sumOpen" in state[index]) { - // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1 | state[index].sumOpen["count"]: ', state[index].sumOpen['count'], ' | onlineUser.sumOpen.count: ', onlineUser.sumOpen.count) + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1 | state[index].sumOpen["count"]: ', state[index].sumOpen['count'], ' | onlineUser.sumOpen.count: ', onlineUser.sumOpen.count) state[index].sumOpen['count'] = onlineUser.sumOpen.count } else if (!("sumOpen" in state[index])) { - // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') state[index].sumOpen = onlineUser.sumOpen } @@ -195,10 +197,10 @@ const reducerQ = (state, action) => { if (onlineUser.sumClosed) { if ("sumClosed" in state[index]) { - // console.log(' >>>>>>>>>>>>>>>>>> sumClosed 1 | state[index].sumClosed["count"]: ', state[index].sumClosed['count'], ' | onlineUser.sumClosed.count: ', onlineUser.sumClosed.count) + // console.log(' >>>>>>>>>>>>>>>>>> sumClosed 1 | state[index].sumClosed["count"]: ', state[index].sumClosed['count'], ' | onlineUser.sumClosed.count: ', onlineUser.sumClosed.count) state[index].sumClosed['count'] = onlineUser.sumClosed.count } else if (!("sumClosed" in state[index])) { - // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') state[index].sumClosed = onlineUser.sumClosed } @@ -298,22 +300,29 @@ let columnsData = [ { title: 'Assunto', field: 'queue.name' }, { title: 'Status', field: 'status' }, - + { title: 'Criado', field: 'createdAt' }, //{title: 'Atualizado', field: 'updatedAt'}, - {title: 'Status de encerramento', field: 'statusChatEnd'}]; + { title: 'Status de encerramento', field: 'statusChatEnd' }]; const Report = () => { const csvLink = useRef() + const { user: userA } = useContext(AuthContext); //-------- const [searchParam] = useState(""); - //const [loading, setLoading] = useState(false); - //const [hasMore, setHasMore] = useState(false); + + const [loading, setLoading] = useState(false); + const [hasMore, setHasMore] = useState(false); + const [pageNumberTickets, setTicketsPageNumber] = useState(1); + const [totalCountTickets, setTotalCountTickets] = useState(0); + + + const [pageNumber, setPageNumber] = useState(1); const [users, dispatch] = useReducer(reducer, []); //const [columns, setColums] = useState([]) @@ -329,14 +338,14 @@ const Report = () => { const [reporList,] = useState(report) const [profile, setProfile] = useState('') const [dataRows, setData] = useState([]); - + useEffect(() => { dispatch({ type: "RESET" }); dispatchQ({ type: "RESET" }) - + setTicketsPageNumber(1) setPageNumber(1); }, [searchParam, profile]); @@ -380,20 +389,24 @@ const Report = () => { const delayDebounceFn = setTimeout(() => { + setLoading(true); const fetchQueries = async () => { try { if (reportOption === '1') { - const dataQuery = await api.get("/reports/", { params: { userId, startDate, endDate }, }); - dispatchQ({ type: "RESET" }) - dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data }); + const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets }, }); - //setLoading(false); + console.log('dataQuery: ', data) + console.log('pageNumberTickets: ', pageNumberTickets) - // console.log('dataQuery: ', dataQuery.data) + // dispatchQ({ type: "RESET" }) + dispatchQ({ type: "LOAD_QUERY", payload: data.tickets }); + + setHasMore(data.hasMore); + setTotalCountTickets(data.count) + setLoading(false); - // console.log() } else if (reportOption === '2') { @@ -404,7 +417,7 @@ const Report = () => { //setLoading(false); - // console.log('REPORT 2 dataQuery : ', dataQuery.data) + // console.log('REPORT 2 dataQuery : ', dataQuery.data) //console.log() @@ -420,7 +433,7 @@ const Report = () => { }, 500); return () => clearTimeout(delayDebounceFn); - }, [userId, startDate, endDate, reportOption]); + }, [userId, startDate, endDate, reportOption, pageNumberTickets, totalCountTickets]); // Get from child 1 @@ -449,7 +462,7 @@ const Report = () => { setReport(data) - // console.log(' data: ', data) + // console.log(' data: ', data) } useEffect(() => { @@ -461,22 +474,10 @@ const Report = () => { setProfile('user') } - }, [reportOption]) + }, [reportOption]) + - - // useEffect(() => { - - // console.log('>>>>>>>>>>>>>>>>>> New query: ', query) - - // }, [query]) - - - - // test del - - const handleCSVMessages = () => { - - // setLoading(true); + const handleCSVMessages = () => { const fetchQueries = async () => { @@ -484,7 +485,7 @@ const Report = () => { const dataQuery = await api.get("/reports/messages", { params: { userId, startDate, endDate }, }); - // console.log('dataQuery messages: ', dataQuery.data) + console.log('dataQuery messages: ', dataQuery.data) if (dataQuery.data.length > 0) { @@ -497,16 +498,11 @@ const Report = () => { else { dataCSVFormat[i].fromMe = 'Cliente' } - } - - // console.log('dataCSVFormat: ', dataCSVFormat) + } setDataCSV(dataCSVFormat) - setIsMount(false); - // setDataCSV(dataQuery.data) - } - - // setLoading(false); + setIsMount(false); + } } catch (err) { console.log(err); @@ -548,8 +544,8 @@ const Report = () => { let date = new Date().toLocaleDateString('pt-BR').split('/') let dateToday = `${date[2]}-${date[1]}-${date[0]}` - // console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR')) - // console.log('date2: ', startDate) + // console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR')) + // console.log('date2: ', startDate) if (data.action === "logout" || (data.action === "update" && @@ -571,7 +567,7 @@ const Report = () => { socket.on("user", (data) => { if (data.action === "delete") { - // console.log(' entrou no delete user: ', data) + // console.log(' entrou no delete user: ', data) dispatch({ type: "DELETE_USER", payload: +data.userId }); } }); @@ -581,9 +577,13 @@ const Report = () => { }; } + else if (reportOption === "1") { + dispatchQ({ type: "RESET" }) + setTicketsPageNumber(1) + } - }, [reportOption, startDate, endDate]); + }, [reportOption, startDate, endDate, userId]); // const handleDeleteRows = (id) => { @@ -621,7 +621,31 @@ const Report = () => { // toastError(err); } - + + }; + + + const loadMore = () => { + setTicketsPageNumber((prevState) => prevState + 1); + }; + + const handleScroll = (e) => { + + if (!hasMore || loading) return; + + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + + // console.log('scrollTop: ', scrollTop, + // ' | scrollHeight: ', scrollHeight, + // ' | clientHeight: ', clientHeight, + // ' | scrollHeight - (scrollTop + 1): ', scrollHeight - (scrollTop + 1)) + + if (scrollHeight - (scrollTop + 1) < clientHeight) { + + loadMore(); + + // e.currentTarget.scrollTo(0, 200); + } }; @@ -639,7 +663,7 @@ const Report = () => { return { 'value': obj.id, 'label': obj.name } })} /> - + @@ -650,14 +674,16 @@ const Report = () => { {reportOption === '1' && -
- {/* */} +
{ } - - - - - + @@ -688,11 +710,22 @@ const Report = () => { {reportOption === '1' && - + //
+ //
+ + <> + + + + + } {reportOption === '2' && @@ -711,7 +744,7 @@ const Report = () => { [ // { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => imagem de perfil do whatsapp }, - { title: 'Nome', field: 'name', cellStyle: {whiteSpace: 'nowrap'}, }, + { title: 'Nome', field: 'name', cellStyle: { whiteSpace: 'nowrap' }, }, { title: 'Status', field: 'statusOnline.status', @@ -765,7 +798,7 @@ const Report = () => { disable: false, onClick: (event, rowData) => { - // console.log(' ROW DATA INFO: ', rowData, ' | rowData: ', rowData.id) + // console.log(' ROW DATA INFO: ', rowData, ' | rowData: ', rowData.id) handleLogouOnlineUser(rowData.id) } } diff --git a/frontend/src/rules.js b/frontend/src/rules.js index fec35f1..9d42bf0 100644 --- a/frontend/src/rules.js +++ b/frontend/src/rules.js @@ -29,6 +29,7 @@ const rules = { "show-icon-add-queue", "show-icon-edit-queue", "show-icon-delete-queue", + "space-disk-info:show", "drawer-admin-items:view", From 2d95bfe20d3214b402e64a7818fccbba369ada64 Mon Sep 17 00:00:00 2001 From: adriano Date: Thu, 28 Jul 2022 18:21:14 -0300 Subject: [PATCH 2/3] =?UTF-8?q?Pequena=20modifica=C3=A7=C3=A3o=20no=20dasb?= =?UTF-8?q?oard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../TicketServices/ListTicketsService.ts | 55 ++++++++----------- .../src/components/Report/MTable/index.js | 2 +- frontend/src/components/TicketsList/index.js | 3 + .../src/components/TicketsManager/index.js | 2 +- frontend/src/pages/Dashboard/Chart.js | 2 +- frontend/src/pages/Dashboard/index.js | 7 ++- frontend/src/pages/Report/index.js | 4 +- frontend/src/pages/Tickets/index.js | 2 +- 8 files changed, 38 insertions(+), 39 deletions(-) diff --git a/backend/src/services/TicketServices/ListTicketsService.ts b/backend/src/services/TicketServices/ListTicketsService.ts index 91bca39..c20154c 100644 --- a/backend/src/services/TicketServices/ListTicketsService.ts +++ b/backend/src/services/TicketServices/ListTicketsService.ts @@ -39,7 +39,7 @@ const ListTicketsService = async ({ showAll, userId, withUnreadMessages, - unlimited='false' + unlimited = 'false' }: Request): Promise => { let whereCondition: Filterable["where"] = { [Op.or]: [{ userId }, { status: "pending" }], @@ -47,6 +47,8 @@ const ListTicketsService = async ({ }; let includeCondition: Includeable[]; + + includeCondition = [ { model: Contact, @@ -65,20 +67,21 @@ const ListTicketsService = async ({ } if (status) { - whereCondition = { - ...whereCondition, - status - }; - // if (unlimited) { - // whereCondition = { - // ...whereCondition, - // createdAt: { - // [Op.gte]: dateToday.fullDate + ' 00:00:00.000000', - // [Op.lte]: dateToday.fullDate + ' 23:59:59.999999' - // } - // } - // } + whereCondition = { ...whereCondition, status }; + + // console.log('TEST unlimited: ', unlimited) + + if (unlimited === 'true' && status !== 'pending') { + + whereCondition = { + ...whereCondition, + createdAt: { + [Op.gte]: dateToday.fullDate + ' 00:00:00.000000', + [Op.lte]: dateToday.fullDate + ' 23:59:59.999999' + } + } + } } @@ -92,11 +95,7 @@ const ListTicketsService = async ({ as: "messages", attributes: ["id", "body"], where: { - body: where( - fn("LOWER", col("body")), - "LIKE", - `%${sanitizedSearchParam}%` - ) + body: where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`) }, required: false, duplicating: false @@ -107,19 +106,13 @@ const ListTicketsService = async ({ ...whereCondition, [Op.or]: [ { - "$contact.name$": where( - fn("LOWER", col("contact.name")), - "LIKE", - `%${sanitizedSearchParam}%` - ) + "$contact.name$": where(fn("LOWER", col("contact.name")), "LIKE", `%${sanitizedSearchParam}%`) }, + { "$contact.number$": { [Op.like]: `%${sanitizedSearchParam}%` } }, - { - "$message.body$": where( - fn("LOWER", col("body")), - "LIKE", - `%${sanitizedSearchParam}%` - ) + + { + "$message.body$": where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`) } ] }; @@ -151,7 +144,7 @@ const ListTicketsService = async ({ where: whereCondition, include: includeCondition, distinct: true, - limit, + limit, offset, order: [["updatedAt", "DESC"]] }); diff --git a/frontend/src/components/Report/MTable/index.js b/frontend/src/components/Report/MTable/index.js index b3f5dbe..d2f7c55 100644 --- a/frontend/src/components/Report/MTable/index.js +++ b/frontend/src/components/Report/MTable/index.js @@ -125,7 +125,7 @@ const MTable = (props) => { exportAllData: true, sorting: true ? props.hasChild : false, - loadingType: 'circular', + // loadingType: 'circular', searchFieldStyle: { width: 300, }, diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 442cbd4..3a1d336 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -195,6 +195,9 @@ const TicketsList = (props) => { useEffect(() => { if (!status && !searchParam) return; + + // console.log('lllllllllllllllllllllllllllllllll') + dispatch({ type: "LOAD_TICKETS", payload: tickets, diff --git a/frontend/src/components/TicketsManager/index.js b/frontend/src/components/TicketsManager/index.js index 88e6ea0..2da1d18 100644 --- a/frontend/src/components/TicketsManager/index.js +++ b/frontend/src/components/TicketsManager/index.js @@ -124,7 +124,7 @@ const TicketsManager = () => { let searchTimeout; const handleSearch = (e) => { - const searchedTerm = e.target.value.toLowerCase(); + const searchedTerm = e.target.value.toLowerCase(); clearTimeout(searchTimeout); diff --git a/frontend/src/pages/Dashboard/Chart.js b/frontend/src/pages/Dashboard/Chart.js index a1480f8..d6207f5 100644 --- a/frontend/src/pages/Dashboard/Chart.js +++ b/frontend/src/pages/Dashboard/Chart.js @@ -20,7 +20,7 @@ const Chart = () => { const theme = useTheme(); const date = useRef(new Date().toISOString()); - const { tickets } = useTickets({ date: date.current }); + const { tickets } = useTickets({ date: date.current, unlimited: "true" }); const [chartData, setChartData] = useState([ { time: "08:00", amount: 0 }, diff --git a/frontend/src/pages/Dashboard/index.js b/frontend/src/pages/Dashboard/index.js index 7ca5e15..674b040 100644 --- a/frontend/src/pages/Dashboard/index.js +++ b/frontend/src/pages/Dashboard/index.js @@ -75,6 +75,7 @@ const Dashboard = () => {
+ @@ -107,15 +108,17 @@ const Dashboard = () => { {GetTickets("closed", "true", "false", "true")} - - + + + +
diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index cd59b8b..c2467c6 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -397,8 +397,8 @@ const Report = () => { const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets }, }); - console.log('dataQuery: ', data) - console.log('pageNumberTickets: ', pageNumberTickets) + // console.log('dataQuery: ', data) + // console.log('pageNumberTickets: ', pageNumberTickets) // dispatchQ({ type: "RESET" }) dispatchQ({ type: "LOAD_QUERY", payload: data.tickets }); diff --git a/frontend/src/pages/Tickets/index.js b/frontend/src/pages/Tickets/index.js index 80eb909..fc4be5a 100644 --- a/frontend/src/pages/Tickets/index.js +++ b/frontend/src/pages/Tickets/index.js @@ -78,7 +78,7 @@ const Chat = () => { className={ ticketId ? classes.contactsWrapperSmall : classes.contactsWrapper } - > + > From 71d69990e25e3c18d7da7954a6d237e3760c7763 Mon Sep 17 00:00:00 2001 From: adriano Date: Mon, 8 Aug 2022 13:47:28 -0300 Subject: [PATCH 3/3] =?UTF-8?q?Modifica=C3=A7=C3=B5es=20para=20exibir=20in?= =?UTF-8?q?forma=C3=A7=C3=A3os=20em=20tempo=20real=20no=20dasboard?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- backend/src/controllers/ReportController.ts | 289 ++++++---- backend/src/controllers/TicketController.ts | 39 +- .../helpers/OnlineReporEmiterInfoByUser.ts | 71 ++- backend/src/routes/ticketRoutes.ts | 6 +- backend/src/server.ts | 2 +- .../TicketServices/CountTicketService.ts | 50 ++ .../UserServices/CountTicketsByUserQueue.ts | 73 +++ .../CreateOrUpdateOnlineUserService.ts | 5 +- .../UserServices/ListUserParamiterService.ts | 87 +-- .../ListUsersOnlineOfflineService.ts | 4 +- .../services/UserServices/ShowQueuesByUser.ts | 40 ++ .../UserServices/ShowUserServiceReport.ts | 2 + frontend/src/components/TicketsList/index.js | 34 +- frontend/src/hooks/useTickets/index.js | 2 +- frontend/src/pages/Dashboard/Chart.js | 19 +- frontend/src/pages/Dashboard/index.js | 497 +++++++++++++++++- frontend/src/pages/Report/index.js | 20 +- 17 files changed, 1025 insertions(+), 215 deletions(-) create mode 100644 backend/src/services/TicketServices/CountTicketService.ts create mode 100644 backend/src/services/UserServices/CountTicketsByUserQueue.ts create mode 100644 backend/src/services/UserServices/ShowQueuesByUser.ts diff --git a/backend/src/controllers/ReportController.ts b/backend/src/controllers/ReportController.ts index 588e494..7f63d19 100644 --- a/backend/src/controllers/ReportController.ts +++ b/backend/src/controllers/ReportController.ts @@ -1,132 +1,241 @@ //relatorio -import { Request, Response } from "express"; -import AppError from "../errors/AppError"; +import { Request, Response } from "express"; +import AppError from "../errors/AppError"; import ShowTicketReport from "../services/TicketServices/ShowTicketReport"; import ShowMessageReport from "../services/MessageServices/ShowMessageReport"; -import onlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService"; +import onlineUserService from "../services/UserServices/CreateOrUpdateOnlineUserService"; import User from "../models/User"; import Queue from "../models/Queue"; import UserOnlineTime from "../models/UserOnlineTime"; -import { Op, Sequelize,literal } from "sequelize"; +import { Op, Sequelize, literal } from "sequelize"; import format from 'date-fns/format'; -import ptBR from 'date-fns/locale/pt-BR'; +import ptBR from 'date-fns/locale/pt-BR'; import { splitDateTime } from "../helpers/SplitDateTime"; import ListUserOnlineOffline from "../services/UserServices/ListUsersOnlineOfflineService"; import ListUserParamiterService from "../services/UserServices/ListUserParamiterService"; import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport"; +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; + +import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser"; +import { filter } from "bluebird"; + + type IndexQuery = { - userId: string; - startDate: string; - endDate: string; - pageNumber: string; - }; - - -export const reportUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { - - if (req.user.profile !== "master" && req.user.profile !== "admin") { - throw new AppError("ERR_NO_PERMISSION", 403); - } - - const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery - - - console.log('PAGE NUMBER: ', pageNumber) - - - const { tickets, count, hasMore } = await ShowTicketReport({userId, startDate, endDate, pageNumber}); - - // return res.status(200).json(data_query); - - return res.status(200).json({ tickets, count, hasMore }); + userId: string; + startDate: string; + endDate: string; + pageNumber: string; }; -export const reportUserService= async (req: Request, res: Response): Promise => { +export const reportUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { - if (req.user.profile !== "master" && req.user.profile !== "admin") { - throw new AppError("ERR_NO_PERMISSION", 403); - } - const { userId, startDate, endDate } = req.query as IndexQuery - + if (req.user.profile !== "master" && req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } - let usersProfile = await ListUserParamiterService({profile: 'user'}) - - const sumUserOlineTime = await ShowUserServiceReport({startDate, endDate, userId}); - const closedByUser = await ShowUserServiceReport({startDate, endDate, ticketStatus: 'closed', userId}); - const openByUser = await ShowUserServiceReport({startDate, endDate, ticketStatus: 'open', userId}); + const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery - let dateTime = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) - const onlineUsers = await ListUserOnlineOffline({ date: dateTime.fullDate }) - - usersProfile.map((user:any) => { + console.log('PAGE NUMBER: ', pageNumber) - let index = sumUserOlineTime.findIndex((e:any) => e.userId == user.id) - if (index != -1) { - user.sumOnlineTime = sumUserOlineTime[index]; - } + const { tickets, count, hasMore } = await ShowTicketReport({ userId, startDate, endDate, pageNumber }); - index = closedByUser.findIndex((e:any) => e.userId == user.id) + // return res.status(200).json(data_query); - if (index != -1) { - user.sumClosed = closedByUser[index]; - } - - index = openByUser.findIndex((e:any) => e.userId == user.id) - - if (index != -1) { - user.sumOpen = openByUser[index] - } - - - index = onlineUsers.findIndex((e:any) => e.userId == user.id) - - if (index != -1) { - user.statusOnline = onlineUsers[index] - } - - if(startDate.length>0 && startDate.split('-').length == 3){ - let date = startDate.split('-') - user.startDate = `${date[2]}/${date[1]}/${date[0]}` - } - - if(endDate.length>0 && endDate.split('-').length == 3){ - let date = endDate.split('-') - user.endDate = `${date[2]}/${date[1]}/${date[0]}` - } - - }) - - return res.status(200).json(usersProfile); - + return res.status(200).json({ tickets, count, hasMore }); }; +export const reportUserService = async (req: Request, res: Response): Promise => { -export const reportMessagesUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { + if (req.user.profile !== "master" && req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + const { userId, startDate, endDate } = req.query as IndexQuery - if (req.user.profile !== "master" && req.user.profile !== "admin") { - throw new AppError("ERR_NO_PERMISSION", 403); + + let usersProfile = await ListUserParamiterService({ profile: 'user' }) + + const sumUserOlineTime = await ShowUserServiceReport({ startDate, endDate, userId }); + const closedByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'closed', userId }); + const openByUser = await ShowUserServiceReport({ startDate, endDate, ticketStatus: 'open', userId }); + + let dateTime = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + const onlineUsers = await ListUserOnlineOffline({ date: dateTime.fullDate }) + + const openByUserOnQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: true }) + const openByUserOutQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: false }) + + const closedByUserOnQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: true }) + const closedUserOutQueue = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: false }) + + // let openQueueInOut = openByUserOnQueue.concat(openByUserOutQueue) + // let closedQueueInOut = closedByUserOnQueue.concat(closedUserOutQueue) + + + const queuesByUser = await ShowQueuesByUser({ profile: 'user' }) + + let openCloseOnQueue = openByUserOnQueue.concat(closedByUserOnQueue) + let openCloseOutQueue = openByUserOutQueue.concat(closedUserOutQueue) + + for (let i = 0; i < queuesByUser.length; i++) { + + queuesByUser[i].countOpen = 0 + queuesByUser[i].countClosed = 0 + + for (let x = 0; x < openCloseOnQueue.length; x++) { + if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'open')) { + queuesByUser[i].countOpen = openCloseOnQueue[x].totAttendance + } + else if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'closed')) { + queuesByUser[i].countClosed = openCloseOnQueue[x].totAttendance + } + + } + } + + + // console.log('queuesByUser: ', queuesByUser) + + + + // console.log('queuesByUser: ',queuesByUser) + // console.log() + // console.log('CLIENT START TRUE openByUserOnQueue: ', openByUserOnQueue) + // console.log() + // console.log('CLIENT START FALSE openByUserOutQueue: ', openByUserOutQueue) + // console.log() + // console.log('CLIENT START TRUE closedByUserOnQueue: ', closedByUserOnQueue) + // console.log() + // console.log('CLIENT START FALSE closedUserOutQueue: ', closedUserOutQueue) + + + usersProfile.map((user: any) => { + + let index = sumUserOlineTime.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.sumOnlineTime = sumUserOlineTime[index]; } - const { userId, startDate, endDate } = req.query as IndexQuery + index = closedByUser.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.sumClosed = closedByUser[index]; + } + + index = openByUser.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.sumOpen = openByUser[index] + } + + + + + + // let openByUserOut = openQueueInOut.filter((e: any) => e.userId == user.id && !e.queueName) + // let openByUserIn = openQueueInOut.filter((e: any) => e.userId == user.id && e.queueName) + + // if (openByUserOut && openByUserOut.length > 0) { + // user.openTicketByUserOut = openByUserOut + // } + // if (openByUserIn && openByUserIn.length > 0) { + // user.openTicketByUserIn = openByUserIn + // } + + // let closedByUserOut = closedQueueInOut.filter((e: any) => e.userId == user.id && !e.queueName) + // let closedByUserIn = closedQueueInOut.filter((e: any) => e.userId == user.id && e.queueName) + + // if (closedByUserOut && closedByUserOut.length > 0) { + // user.closedTicketByUserOut = closedByUserOut + // } + // if (closedByUserIn && closedByUserIn.length > 0) { + // user.closedTicketByUserIn = closedByUserIn + // } + + + + + + // OPEN, CLOSED TICKETS STARTED BY USERS + let openClosedOutQueue = {} + let open = openCloseOutQueue.filter((e) => e.userId == user.id && e.status == 'open') + let closed = openCloseOutQueue.filter((e) => e.userId == user.id && e.status == 'closed') + + openClosedOutQueue = { + ...openClosedOutQueue, + userId: user.id, + countOpen: open && open.length > 0 ? open[0].totAttendance : 0, + countClosed: closed && closed.length > 0 ? closed[0].totAttendance : 0 + } + + user.openClosedOutQueue = openClosedOutQueue + + + // OPEN, CLOSED TICKETS STARTED BY CLIENTS + let openClosedInQueue = queuesByUser.filter((e) => e.userId == user.id) + + if (openClosedInQueue && openClosedInQueue.length > 0) { + user.openClosedInQueue = openClosedInQueue + } + + + + + + + + + index = onlineUsers.findIndex((e: any) => e.userId == user.id) + + if (index != -1) { + user.statusOnline = onlineUsers[index] + } + + if (startDate.length > 0 && startDate.split('-').length == 3) { + let date = startDate.split('-') + user.startDate = `${date[2]}/${date[1]}/${date[0]}` + } + + if (endDate.length > 0 && endDate.split('-').length == 3) { + let date = endDate.split('-') + user.endDate = `${date[2]}/${date[1]}/${date[0]}` + } + + }) + + return res.status(200).json(usersProfile); - const data_query_messages = await ShowMessageReport(userId, startDate, endDate); - - return res.status(200).json(data_query_messages); - }; - + +export const reportMessagesUserByDateStartDateEnd = async (req: Request, res: Response): Promise => { + + if (req.user.profile !== "master" && req.user.profile !== "admin") { + throw new AppError("ERR_NO_PERMISSION", 403); + } + + const { userId, startDate, endDate } = req.query as IndexQuery + + const data_query_messages = await ShowMessageReport(userId, startDate, endDate); + + return res.status(200).json(data_query_messages); + +}; + + + - \ No newline at end of file diff --git a/backend/src/controllers/TicketController.ts b/backend/src/controllers/TicketController.ts index 128b10c..e68af61 100644 --- a/backend/src/controllers/TicketController.ts +++ b/backend/src/controllers/TicketController.ts @@ -21,6 +21,8 @@ import format from 'date-fns/format'; + + type IndexQuery = { searchParam: string; pageNumber: string; @@ -44,8 +46,12 @@ import ListStatusChatEndService from "../services/StatusChatEndService/ListStatu import Ticket from "../models/Ticket"; import ShowUserServiceReport from "../services/UserServices/ShowUserServiceReport"; import TicketEmiterSumOpenClosedByUser from "../helpers/OnlineReporEmiterInfoByUser"; +import CountTicketService from "../services/TicketServices/CountTicketService"; +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; +import ShowUserService from "../services/UserServices/ShowUserService"; + +export const index = async (req: Request, res: Response): Promise => { -export const index = async (req: Request, res: Response): Promise => { const { pageNumber, status, @@ -55,8 +61,7 @@ export const index = async (req: Request, res: Response): Promise => { queueIds: queueIdsStringified, withUnreadMessages, unlimited - } = req.query as IndexQuery; - + } = req.query as IndexQuery; const userId = req.user.id; @@ -99,7 +104,7 @@ export const store = async (req: Request, res: Response): Promise => { console.log('TICKET QUEUE CHOICE !!!!!!!') } else { - ticket = await CreateTicketService({ contactId, status, userId }); + ticket = await CreateTicketService({ contactId, status, userId }); } const io = getIO(); @@ -138,6 +143,17 @@ 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); + + return res.status(200).json(ticketCount); +}; + + @@ -214,19 +230,22 @@ export const update = async (req: Request, res: Response): Promise => const ticketData: TicketData = req.body; - //ticketData: { status: 'open', userId: 4 } , ticketId + //ticketData: { status: 'open', userId: 4 } , ticketId const { ticket } = await UpdateTicketService({ ticketData, ticketId }); + const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + TicketEmiterSumOpenClosedByUser(ticketData.userId.toString(), dateToday.fullDate, dateToday.fullDate) + ticket2 = ticket } - // test del + if (userOldInfo) { @@ -234,12 +253,15 @@ export const update = async (req: Request, res: Response): Promise => if (userOldInfo.userId) { - TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate) + // console.log('FECHOU...') + + TicketEmiterSumOpenClosedByUser(userOldInfo.userId.toString(), dateToday.fullDate, dateToday.fullDate) + } } - // + return res.status(200).json(ticket2); }; @@ -248,7 +270,6 @@ export const update = async (req: Request, res: Response): Promise => - // export const update = async ( // req: Request, // res: Response diff --git a/backend/src/helpers/OnlineReporEmiterInfoByUser.ts b/backend/src/helpers/OnlineReporEmiterInfoByUser.ts index 382d001..7b2abbd 100644 --- a/backend/src/helpers/OnlineReporEmiterInfoByUser.ts +++ b/backend/src/helpers/OnlineReporEmiterInfoByUser.ts @@ -3,23 +3,78 @@ import { splitDateTime } from "../helpers/SplitDateTime"; import format from 'date-fns/format'; import ShowUserServiceReport from '../services/UserServices/ShowUserServiceReport'; import { getIO } from "../libs/socket"; +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; +import ShowQueuesByUser from '../services/UserServices/ShowQueuesByUser'; + +// import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; -const TicketEmiterSumOpenClosedByUser = async (userId: string, startDate: string, endDate: string) => { +const TicketEmiterSumOpenClosedByUser = async (userId: string, startDate: string, endDate: string) => { - const openByUser: any[] = await ShowUserServiceReport({ startDate: startDate, endDate:endDate, ticketStatus: 'open', userId: userId.toString() }); - const closedByUser: any[] = await ShowUserServiceReport({ startDate: endDate, endDate:endDate, ticketStatus: 'closed', userId: userId.toString() }); + const openByUser: any[] = await ShowUserServiceReport({ startDate: startDate, endDate: endDate, ticketStatus: 'open', userId: userId.toString() }); + const closedByUser: any[] = await ShowUserServiceReport({ startDate: endDate, endDate: endDate, ticketStatus: 'closed', userId: userId.toString() }); + + + const openByUserOnQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: true, userId: userId }) + const openByUserOutQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'open', clientChatStart: false, userId: userId }) + + const closedByUserOnQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: true, userId: userId }) + const closedUserOutQueue: any[] = await CountTicketsByUserQueue({ startDate: startDate, endDate: endDate, status: 'closed', clientChatStart: false, userId: userId }) + + + + const queuesByUser = await ShowQueuesByUser({ profile: 'user', userId: userId }) + + let openCloseOnQueue = openByUserOnQueue.concat(closedByUserOnQueue) + let openCloseOutQueue = openByUserOutQueue.concat(closedUserOutQueue) + + + // OPEN, CLOSED TICKETS STARTED BY CLIENTS + for (let i = 0; i < queuesByUser.length; i++) { + + queuesByUser[i].countOpen = 0 + queuesByUser[i].countClosed = 0 + + for (let x = 0; x < openCloseOnQueue.length; x++) { + if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'open')) { + queuesByUser[i].countOpen = openCloseOnQueue[x].totAttendance + } + else if ((queuesByUser[i].userId == openCloseOnQueue[x].userId) && + (queuesByUser[i].queueId == openCloseOnQueue[x].queueId && openCloseOnQueue[x].status == 'closed')) { + queuesByUser[i].countClosed = openCloseOnQueue[x].totAttendance + } + + } + } + + + // OPEN, CLOSED TICKETS STARTED BY USERS + let openClosedOutQueue = {} + let open = openCloseOutQueue.filter((e) => e.status == 'open') + let closed = openCloseOutQueue.filter((e) => e.status == 'closed') + + openClosedOutQueue = { + ...openClosedOutQueue, + userId: userId, + countOpen: open && open.length > 0 ? open[0].totAttendance : 0, + countClosed: closed && closed.length > 0 ? closed[0].totAttendance : 0 + } - //openByUser : [ { id: 13, status: 'online' } ] const io = getIO(); io.emit("onlineStatus", { action: "update", - userOnlineTime: { - sumOpen: openByUser.length > 0 ? openByUser[0] : { userId: userId, count: '' }, - sumClosed: closedByUser.length > 0 ? closedByUser[0] : { userId: userId, count: '' }, + userOnlineTime: { + sumOpen: openByUser.length > 0 ? openByUser[0] : { userId: userId, count: '' }, + sumClosed: closedByUser.length > 0 ? closedByUser[0] : { userId: userId, count: '' }, + + openClosedOutQueue: openClosedOutQueue, + openClosedInQueue: queuesByUser + + } - }); + }); }; diff --git a/backend/src/routes/ticketRoutes.ts b/backend/src/routes/ticketRoutes.ts index 742d375..e2c0a76 100644 --- a/backend/src/routes/ticketRoutes.ts +++ b/backend/src/routes/ticketRoutes.ts @@ -4,9 +4,11 @@ import isAuth from "../middleware/isAuth"; import * as TicketController from "../controllers/TicketController"; const ticketRoutes = express.Router(); - -ticketRoutes.get("/tickets", isAuth, TicketController.index); + +ticketRoutes.get("/tickets/count", isAuth, TicketController.count); + +ticketRoutes.get("/tickets", isAuth, TicketController.index); ticketRoutes.get("/tickets/:ticketId", isAuth, TicketController.show); diff --git a/backend/src/server.ts b/backend/src/server.ts index 103fa46..ac9bccc 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -16,4 +16,4 @@ StartAllWhatsAppsSessions(); gracefulShutdown(server); startSchedulingMonitor(5000) -startWhoIsOnlineMonitor(5000) +startWhoIsOnlineMonitor(2000) diff --git a/backend/src/services/TicketServices/CountTicketService.ts b/backend/src/services/TicketServices/CountTicketService.ts new file mode 100644 index 0000000..2cdde93 --- /dev/null +++ b/backend/src/services/TicketServices/CountTicketService.ts @@ -0,0 +1,50 @@ +import Ticket from "../../models/Ticket"; +import AppError from "../../errors/AppError"; +import { Op, where } from "sequelize"; +import { Sequelize } from "sequelize"; + +import { format } from "date-fns"; +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 => { + + let where_clause = {} + + if (date) { + where_clause = { + createdAt: { + [Op.gte]: date + ' 00:00:00.000000', + [Op.lte]: date + ' 23:59:59.999999' + } + } + } + else { + // where_clause = { + // createdAt: { + // [Op.gte]: dateToday.fullDate + ' 00:00:00.000000', + // [Op.lte]: dateToday.fullDate + ' 23:59:59.999999' + // } + // } + } + + + where_clause = { ...where_clause, status: status } + + const ticket = await Ticket.findAll({ + where: where_clause, + raw: true, + attributes: [[Sequelize.fn("COUNT", Sequelize.col("Ticket.status")), "count"]], + }); + + + if (!ticket) { + throw new AppError("ERR_NO_TICKET_FOUND", 404); + } + + return ticket[0]; +}; + +export default CountTicketService; \ No newline at end of file diff --git a/backend/src/services/UserServices/CountTicketsByUserQueue.ts b/backend/src/services/UserServices/CountTicketsByUserQueue.ts new file mode 100644 index 0000000..dc13108 --- /dev/null +++ b/backend/src/services/UserServices/CountTicketsByUserQueue.ts @@ -0,0 +1,73 @@ + +import { Sequelize, } from "sequelize"; + +const dbConfig = require("../../config/database"); +const sequelize = new Sequelize(dbConfig); + +const { QueryTypes } = require('sequelize'); + +interface Request { + startDate: string; + endDate: string; + status: string; + clientChatStart: boolean; + userId?: string | number; +} + +const CountTicketsByUserQueue = async ({ startDate, endDate, status, clientChatStart, userId }: Request): Promise => { + + let usersQueueInfo = [] + + if (clientChatStart) { + + if (userId) { + // CONSULTANDO POR CONVERSAS INICIADAS PELO CLIENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, Queues.id as queueId, Queues.name as queueName, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId = Queues.id \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users inner join Queues on Tickets.queueId = Queues.id and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') and Users.id = '${userId}' group by Users.email, Queues.name;`, { type: QueryTypes.SELECT }); + + } else { + + // CONSULTANDO POR CONVERSAS INICIADAS PELO CLIENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, Queues.id as queueId, Queues.name as queueName, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId = Queues.id \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users inner join Queues on Tickets.queueId = Queues.id and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') group by Users.email, Queues.name;`, { type: QueryTypes.SELECT }); + + } + } + else { + + if (userId) { + // CONSULTANDO POR CONVERSAS INICIADAS PELO ATENDENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId is null \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users on \ + Tickets.queueId is null and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') and Users.id = '${userId}' group by Users.email;`, { type: QueryTypes.SELECT }); + } + else { + // CONSULTANDO POR CONVERSAS INICIADAS PELO ATENDENTE + usersQueueInfo = await sequelize.query(`select Users.id as userId, Tickets.status, \ + (select count(id) from Tickets where userId = Users.id and Tickets.status = '${status}' and Tickets.queueId is null \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999')) as totAttendance \ + from Tickets inner join Users on \ + Tickets.queueId is null and Tickets.userId = Users.id and Tickets.status = '${status}' \ + and (date(Tickets.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999') group by Users.email;`, { type: QueryTypes.SELECT }); + } + + + } + + return usersQueueInfo; +}; + +export default CountTicketsByUserQueue; + + + + diff --git a/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts b/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts index b7fbe80..3075da3 100644 --- a/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts +++ b/backend/src/services/UserServices/CreateOrUpdateOnlineUserService.ts @@ -116,7 +116,8 @@ const CreateOrUpdateUserOnlineTime = async ({ console.log('CreatedAt string: ', createdAtString) console.log('UpdatedAt string: ', updatedAtString) - // + // + io.emit("onlineStatus", { action: "update", @@ -132,7 +133,7 @@ const CreateOrUpdateUserOnlineTime = async ({ } else if (oldStatus == 'offline' && status === 'online') { await userOnlineTime.update({ status }) - + io.emit("onlineStatus", { action: "update", userOnlineTime: { diff --git a/backend/src/services/UserServices/ListUserParamiterService.ts b/backend/src/services/UserServices/ListUserParamiterService.ts index 70f985a..e8ff0bd 100644 --- a/backend/src/services/UserServices/ListUserParamiterService.ts +++ b/backend/src/services/UserServices/ListUserParamiterService.ts @@ -1,48 +1,66 @@ - -import { Op, Sequelize } from "sequelize"; + +import { Op, Sequelize } from "sequelize"; +import Queue from "../../models/Queue"; import User from "../../models/User"; +import UserQueue from "../../models/UserQueue"; interface Request { - userId?: string | number; - profile?: string; + userId?: string | number; + profile?: string; +} + + +const ListUser = async ({ profile, userId }: Request): Promise => { + + let where_clause = {} + + if (userId && profile) { + where_clause = { + [Op.and]: [ + { userId: userId }, + { profile: profile } + ] + } } - - -const ListUser = async ({profile, userId}: Request): Promise => { - - let where_clause = {} - - if(userId && profile){ - where_clause = { - [Op.and]: [ - {userId: userId}, - {profile: profile} - ] - } + else if (userId) { + where_clause = { + [Op.and]: [ + { userId: userId }, + ] } - else if(userId){ - where_clause = { - [Op.and]: [ - {userId: userId}, - ] - } - } - else if(profile){ - where_clause = { - profile: profile - } + } + else if (profile) { + where_clause = { + profile: profile } - + } - const users = await User.findAll({ + + const users = await User.findAll({ where: where_clause, - raw:true, + raw: true, attributes: ['id', 'name', 'email'], - - order: [["id", "ASC"]], - }) + // include: [ + // { + // model: UserQueue, + // separate: true, + + // attributes: ['id',], + + // order: [ + // ['createdAt', 'ASC'] + // ] + // }, + // ], + + + + order: [["id", "ASC"]], + }) + + return users; }; @@ -51,4 +69,3 @@ export default ListUser; - \ No newline at end of file diff --git a/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts b/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts index 1fe4205..04f3cb0 100644 --- a/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts +++ b/backend/src/services/UserServices/ListUsersOnlineOfflineService.ts @@ -61,11 +61,11 @@ const ListUserOnlineOffline = async ({date, userId, status}: Request): Promise => { + + let queueByUsersInfo = [] + + if (userId) { + // CONSULTANDO FILAS PELO ID DO USUARIO + queueByUsersInfo = await sequelize.query(`select UserQueues.userId, UserQueues.queueId, Users.name, + Queues.name, Queues.color from UserQueues inner join Users inner join Queues on + UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.id = '${userId}' and Users.profile = '${profile}' order by userId, queueId;`, { type: QueryTypes.SELECT }); + + } else { + + // CONSULTANDO FILAS PELO USUARIO + queueByUsersInfo = await sequelize.query(`select UserQueues.userId, UserQueues.queueId, Users.name, + Queues.name, Queues.color from UserQueues inner join Users inner join Queues on + UserQueues.queueId = Queues.id and UserQueues.userId = Users.id and Users.profile = '${profile}' order by userId, queueId;`, { type: QueryTypes.SELECT }); + + } + + return queueByUsersInfo; +}; + +export default QueuesByUser; + + + + diff --git a/backend/src/services/UserServices/ShowUserServiceReport.ts b/backend/src/services/UserServices/ShowUserServiceReport.ts index c6129ab..f9de30d 100644 --- a/backend/src/services/UserServices/ShowUserServiceReport.ts +++ b/backend/src/services/UserServices/ShowUserServiceReport.ts @@ -106,6 +106,8 @@ const ShowUserServiceReport = async ({ } + // console.log('>>>>>>>>>>>>>> objQuery: ', objQuery) + if (!objQuery) { diff --git a/frontend/src/components/TicketsList/index.js b/frontend/src/components/TicketsList/index.js index 3a1d336..2699cd5 100644 --- a/frontend/src/components/TicketsList/index.js +++ b/frontend/src/components/TicketsList/index.js @@ -10,7 +10,8 @@ import TicketsListSkeleton from "../TicketsListSkeleton"; import useTickets from "../../hooks/useTickets"; import { i18n } from "../../translate/i18n"; -import { AuthContext } from "../../context/Auth/AuthContext"; +import { AuthContext } from "../../context/Auth/AuthContext"; + const useStyles = makeStyles(theme => ({ ticketsListWrapper: { @@ -73,9 +74,9 @@ const useStyles = makeStyles(theme => ({ const reducer = (state, action) => { if (action.type === "LOAD_TICKETS") { - const newTickets = action.payload; - - + const newTickets = action.payload; + + newTickets.forEach(ticket => { // console.log('* ticket.unreadMessages: ',ticket.unreadMessages) @@ -108,7 +109,7 @@ const reducer = (state, action) => { if (action.type === "UPDATE_TICKET") { const ticket = action.payload; - // console.log('++++++++++++ UPDATE_TICKET: ',ticket) + // console.log('++++++++++++ UPDATE_TICKET: ',ticket) const ticketIndex = state.findIndex(t => t.id === ticket.id); if (ticketIndex !== -1) { @@ -124,23 +125,23 @@ const reducer = (state, action) => { const message = action.payload.message - const ticket = action.payload.ticket; + const ticket = action.payload.ticket; const ticketIndex = state.findIndex(t => t.id === ticket.id); if (ticketIndex !== -1) { - + // console.log('>>>>>> ticketIndex: ', ticketIndex) // console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message) - - if(!message.fromMe){ - ticket.unreadMessages +=1 + + if (!message.fromMe) { + ticket.unreadMessages += 1 } state[ticketIndex] = ticket; state.unshift(state.splice(ticketIndex, 1)[0]); - + } else { state.unshift(ticket); } @@ -223,7 +224,8 @@ const TicketsList = (props) => { }); - socket.on("ticket", data => { + socket.on("ticket", data => { + if (data.action === "updateUnread") { dispatch({ type: "RESET_UNREAD", @@ -238,13 +240,13 @@ const TicketsList = (props) => { }); } - if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { + if (data.action === "update" && notBelongsToUserQueues(data.ticket)) { dispatch({ type: "DELETE_TICKET", payload: data.ticket.id }); } if (data.action === "delete") { dispatch({ type: "DELETE_TICKET", payload: data.ticketId }); - } + } }); socket.on("appMessage", data => { @@ -288,11 +290,11 @@ const TicketsList = (props) => { setPageNumber(prevState => prevState + 1); }; - const handleScroll = e => { + const handleScroll = e => { if (!hasMore || loading) return; - const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; if (scrollHeight - (scrollTop + 100) < clientHeight) { loadMore(); diff --git a/frontend/src/hooks/useTickets/index.js b/frontend/src/hooks/useTickets/index.js index c1250b3..e56e38f 100644 --- a/frontend/src/hooks/useTickets/index.js +++ b/frontend/src/hooks/useTickets/index.js @@ -15,7 +15,7 @@ const useTickets = ({ }) => { const [loading, setLoading] = useState(true); const [hasMore, setHasMore] = useState(false); - const [tickets, setTickets] = useState([]); + const [tickets, setTickets] = useState([]); useEffect(() => { setLoading(true); diff --git a/frontend/src/pages/Dashboard/Chart.js b/frontend/src/pages/Dashboard/Chart.js index d6207f5..77671b0 100644 --- a/frontend/src/pages/Dashboard/Chart.js +++ b/frontend/src/pages/Dashboard/Chart.js @@ -16,12 +16,19 @@ import { i18n } from "../../translate/i18n"; import Title from "./Title"; import useTickets from "../../hooks/useTickets"; -const Chart = () => { +const Chart = (props) => { const theme = useTheme(); const date = useRef(new Date().toISOString()); const { tickets } = useTickets({ date: date.current, unlimited: "true" }); + + + // useEffect(()=>{ + // console.log('kkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkkk') + // },[props.change]) + + const [chartData, setChartData] = useState([ { time: "08:00", amount: 0 }, { time: "09:00", amount: 0 }, @@ -42,10 +49,7 @@ const Chart = () => { let aux = [...prevState]; aux.forEach(a => { - tickets.forEach(ticket => { - format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time && - a.amount++; - }); + tickets.forEach(ticket => { format(startOfHour(parseISO(ticket.createdAt)), "HH:mm") === a.time && a.amount++; }); }); return aux; @@ -54,9 +58,8 @@ const Chart = () => { return ( - {`${i18n.t("dashboard.charts.perDay.title")}${ - tickets.length - }`} + {`${i18n.t("dashboard.charts.perDay.title")}${tickets.length + }`} ({ container: { paddingTop: theme.spacing(4), @@ -44,29 +50,314 @@ const useStyles = makeStyles(theme => ({ }, })) + + + +const reducer = (state, action) => { + + if (action.type === "DELETE_USER_STATUS") { + + const userId = action.payload; + + const userIndex = state.findIndex((u) => `${u.id}` === `${userId}`); + + if (userIndex !== -1) { + state.splice(userIndex, 1); + } + + return [...state]; + } + + + + if (action.type === 'LOAD_QUERY') { + + const queries = action.payload + const newQueries = [] + + queries.forEach((query) => { + + const queryIndex = state.findIndex((q) => q.id === query.id) + + if (queryIndex !== -1) { + state[queryIndex] = query + } + else { + newQueries.push(query) + } + + }) + + return [...state, ...newQueries] + } + + if (action.type === "UPDATE_STATUS_ONLINE") { + + let onlineUser = action.payload + let index = -1 + + let onlySumOpenClosed = false + + if (onlineUser.sumOpen || onlineUser.sumClosed) { + index = state.findIndex((e) => ((onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) || (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId))) + + onlySumOpenClosed = true + } + else { + index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`) + } + + + if (index !== -1) { + + if (!onlySumOpenClosed) { + + if (!("statusOnline" in state[index])) { + + state[index].statusOnline = onlineUser + + } + else if ("statusOnline" in state[index]) { + + state[index].statusOnline['status'] = onlineUser.status + + } + + } + + + if ("onlineTime" in onlineUser) { + + if ("sumOnlineTime" in state[index]) { + state[index].sumOnlineTime['sum'] = (onlineUser.onlineTime).split(" ")[1] + } + else if (!("sumOnlineTime" in state[index])) { + state[index].sumOnlineTime = { userId: onlineUser.userId, sum: (onlineUser.onlineTime).split(" ")[1] } + } + } + + + if (onlineUser.sumOpen) { + + if ("sumOpen" in state[index]) { + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1 | state[index].sumOpen["count"]: ', state[index].sumOpen['count'], ' | onlineUser.sumOpen.count: ', onlineUser.sumOpen.count) + state[index].sumOpen['count'] = onlineUser.sumOpen.count + } else if (!("sumOpen" in state[index])) { + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + state[index].sumOpen = onlineUser.sumOpen + } + + } + + if (onlineUser.sumClosed) { + + if ("sumClosed" in state[index]) { + // console.log(' >>>>>>>>>>>>>>>>>> sumClosed 1 | state[index].sumClosed["count"]: ', state[index].sumClosed['count'], ' | onlineUser.sumClosed.count: ', onlineUser.sumClosed.count) + state[index].sumClosed['count'] = onlineUser.sumClosed.count + } else if (!("sumClosed" in state[index])) { + // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') + state[index].sumClosed = onlineUser.sumClosed + } + + } + + + + if(onlineUser.openClosedInQueue){ + state[index].openClosedInQueue = onlineUser.openClosedInQueue + } + if(onlineUser.openClosedOutQueue){ + state[index].openClosedOutQueue = onlineUser.openClosedOutQueue + } + + + + // if(onlineUser.closedTicketByUserIn){ + // state[index].closedTicketByUserIn = onlineUser.closedTicketByUserIn + // } + // if(onlineUser.closedTicketByUserOut){ + // state[index].closedTicketByUserOut = onlineUser.closedTicketByUserOut + // } + // if(onlineUser.openTicketByUserIn){ + // state[index].openTicketByUserIn = onlineUser.openTicketByUserIn + // } + // if(onlineUser.openTicketByUserOut){ + // state[index].openTicketByUserOut = onlineUser.openTicketByUserOut + // } + + } + return [...state] + + } + + if (action.type === "RESET") { + return []; + } + +} + + + const Dashboard = () => { const classes = useStyles() + const [usersOnlineInfo, dispatch] = useReducer(reducer, []) + + const [open, setOpen] = useState(0) + const [closed, setClosed] = useState(0) + const [pending, setPending] = useState(0) const { user } = useContext(AuthContext); - var userQueueIds = []; + // var userQueueIds = []; - if (user.queues && user.queues.length > 0) { - userQueueIds = user.queues.map(q => q.id); - } + // if (user.queues && user.queues.length > 0) { + // userQueueIds = user.queues.map(q => q.id); + // } - const GetTickets = (status, showAll, withUnreadMessages, unlimited) => { + // const GetTickets = (status, showAll, withUnreadMessages, unlimited) => { + + // const { tickets } = useTickets({ + // status: status, + // showAll: showAll, + // withUnreadMessages: withUnreadMessages, + // queueIds: JSON.stringify(userQueueIds), + // unlimited: unlimited + // }); + // return tickets.length; + // } + + useEffect(() => { + + dispatch({ type: "RESET" }); + + }, []); + + + + + const handleLogouOnlineUser = async (userId) => { + try { + await api.get(`/users/logout/${userId}`); + //toast.success(("Desloged!")); + //handleDeleteRows(scheduleId) + } catch (err) { + // toastError(err); + } + + + }; + + + useEffect(() => { + + //setLoading(true); + + const delayDebounceFn = setTimeout(() => { + + // setLoading(true); + const fetchQueries = async () => { + try { + + let date = new Date().toLocaleDateString('pt-BR').split('/') + let dateToday = `${date[2]}-${date[1]}-${date[0]}` + + const dataQuery = await api.get("/reports/user/services", { params: { userId: null, startDate: dateToday, endDate: dateToday }, }); + dispatch({ type: "RESET" }) + dispatch({ type: "LOAD_QUERY", payload: dataQuery.data }); + + console.log('xxx REPORT 2 dataQuery : ', dataQuery.data) + + //console.log() + + } catch (err) { + console.log(err); + } + }; + + fetchQueries(); + + }, 500); + return () => clearTimeout(delayDebounceFn); + + }, []); + + + + useEffect(() => { + + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + + // socket.on("ticket", (data) => { + // console.log('OK') + // }); + + socket.on("onlineStatus", (data) => { + + if (data.action === "logout" || (data.action === "update")) { + + // console.log('>>>>>>> data.userOnlineTime: ', data.userOnlineTime) + + + dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }); + + } + else if (data.action === "delete") { + dispatch({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); + } - const { tickets } = useTickets({ - status: status, - showAll: showAll, - withUnreadMessages: withUnreadMessages, - queueIds: JSON.stringify(userQueueIds), - unlimited: unlimited }); - return tickets.length; - } - return ( + socket.on("user", (data) => { + + if (data.action === "delete") { + // console.log(' entrou no delete user: ', data) + dispatch({ type: "DELETE_USER", payload: +data.userId }); + } + }); + + return () => { + socket.disconnect(); + }; + + + }, []); + + + useEffect(() => { + + const delayDebounceFn = setTimeout(() => { + + const fetchQueries = async () => { + + try { + + let date = new Date().toLocaleDateString('pt-BR').split('/') + let dateToday = `${date[2]}-${date[1]}-${date[0]}` + + const _open = await api.get("/tickets/count", { params: { status: 'open', date: dateToday } }); + const _closed = await api.get("/tickets/count", { params: { status: 'closed', date: dateToday } }); + const _pending = await api.get("/tickets/count", { params: { status: 'pending', date: dateToday } }); + + setOpen(_open.data.count) + setClosed(_closed.data.count) + setPending(_pending.data.count) + + + } catch (err) { + console.log(err); + } + }; + + fetchQueries(); + + }, 500); + return () => clearTimeout(delayDebounceFn); + + }, [usersOnlineInfo]); + + + + + return ( {
- + @@ -83,7 +374,8 @@ const Dashboard = () => { - {GetTickets("open", "true", "false", "true")} + {/* {GetTickets("open", "true", "false", "true")} */} + {open} @@ -95,7 +387,8 @@ const Dashboard = () => { - {GetTickets("pending", "true", "false", "true")} + {/* {GetTickets("pending", "true", "false", "true")} */} + {pending} @@ -107,25 +400,175 @@ const Dashboard = () => { - {GetTickets("closed", "true", "false", "true")} - - + {/* {GetTickets("closed", "true", "false", "true")} */} + {closed} + + - + + + + + + + + {'Total online'} + + + + {usersOnlineInfo && + usersOnlineInfo.filter((user) => user.statusOnline && user.statusOnline.status === 'online').length + } + + + + + + + + {'Total offline'} + + + + {usersOnlineInfo && + usersOnlineInfo.filter((user) => !user.statusOnline || user.statusOnline.status === 'offline').length + } + + + + + + + + + + + + + + + {usersOnlineInfo && + + usersOnlineInfo.map((userInfo, index) => ( + + // {option.label} + + + <> + {userInfo.statusOnline && + + + + + + + {userInfo.name} + + + + + {userInfo.statusOnline && + userInfo.statusOnline.status + } + + + + Em atendimento: {userInfo.sumOpen && userInfo.sumOpen.count} + + + + Finalizado: {userInfo.sumClosed && userInfo.sumClosed.count} + + + + Tempo online: {userInfo.sumOnlineTime && userInfo.sumOnlineTime.sum} + + + +
+ +
Em atendimento(open/closed) por fila, conversas iniciadas pelos clientes:
+ + + {userInfo.openClosedInQueue && + + userInfo.openClosedInQueue.map((info, index) => ( + <> + + {info.name}: OPEN {info.countOpen} | CLOSED {info.countClosed} + + + )) + + } + +
+ +
+ +
Em atendimento(open/closed) sem fila, conversas iniciadas por atendentes:
+ + {userInfo.openClosedOutQueue && + + + SEM FILA: OPEN {userInfo.openClosedOutQueue.countOpen} | CLOSED {userInfo.openClosedOutQueue.countClosed} + + + } + +
+ + + {userInfo.statusOnline && userInfo.statusOnline.status === "online" && + + userInfo.statusOnline && + + + + + + + + } + + + + +
+
+
+ } + + + + )) + } + + + -
-
+ +
)} /> - - + + diff --git a/frontend/src/pages/Report/index.js b/frontend/src/pages/Report/index.js index c2467c6..8476a78 100644 --- a/frontend/src/pages/Report/index.js +++ b/frontend/src/pages/Report/index.js @@ -417,7 +417,7 @@ const Report = () => { //setLoading(false); - // console.log('REPORT 2 dataQuery : ', dataQuery.data) + console.log('REPORT 2 dataQuery : ', dataQuery.data) //console.log() @@ -536,17 +536,10 @@ const Report = () => { const socket = openSocket(process.env.REACT_APP_BACKEND_URL); - socket.on("onlineStatus", (data) => { - - // setLoading(true); - + socket.on("onlineStatus", (data) => { let date = new Date().toLocaleDateString('pt-BR').split('/') - let dateToday = `${date[2]}-${date[1]}-${date[0]}` - - // console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR')) - // console.log('date2: ', startDate) - + let dateToday = `${date[2]}-${date[1]}-${date[0]}` if (data.action === "logout" || (data.action === "update" && ((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) { @@ -558,9 +551,7 @@ const Report = () => { } else if (data.action === "delete") { dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); - } - - // setLoading(false); + } }); @@ -608,7 +599,8 @@ const Report = () => { // ))) // } - setData(query.map((column) => { return { ...column } })) + setData(query.map((column) => { return { ...column } })) + }, [query])