Finalização do relatorio carregado sob demanda

pull/18/head
adriano 2022-07-28 14:55:23 -03:00
parent 75fd79fc8d
commit ffcefe8f12
14 changed files with 588 additions and 329 deletions

View File

@ -24,6 +24,7 @@ type IndexQuery = {
userId: string; userId: string;
startDate: string; startDate: string;
endDate: string; endDate: string;
pageNumber: string;
}; };
@ -33,13 +34,17 @@ export const reportUserByDateStartDateEnd = async (req: Request, res: Response):
throw new AppError("ERR_NO_PERMISSION", 403); throw new AppError("ERR_NO_PERMISSION", 403);
} }
const { userId, startDate, endDate } = req.query as IndexQuery const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery
const data_query = await ShowTicketReport(userId, startDate, endDate); console.log('PAGE NUMBER: ', pageNumber)
return res.status(200).json(data_query);
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 });
}; };

View File

@ -6,7 +6,7 @@ module.exports = {
"StatusChatEnds", "StatusChatEnds",
[ [
{ {
name: "SEM RETORNO DO CLIENTE", name: "FINALIZADO",
createdAt: new Date(), createdAt: new Date(),
updatedAt: new Date() updatedAt: new Date()
}, },

View File

@ -13,6 +13,8 @@ const fastFolderSize = require('fast-folder-size')
const { promisify } = require('util') const { promisify } = require('util')
const fs = require('fs') const fs = require('fs')
const { exec } = require("child_process");
let scheduler_monitor: any; let scheduler_monitor: any;
let timeInterval = 5 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 // WHATS SESSION SIZE MONITORING
const whatsapps = await ListWhatsAppsService(); const whatsapps = await ListWhatsAppsService();

View File

@ -16,12 +16,31 @@ import { startOfDay, endOfDay, parseISO, getDate} from "date-fns";
import { string } from "yup/lib/locale"; import { string } from "yup/lib/locale";
import Whatsapp from "../../models/Whatsapp"; 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 //Report by user, startDate, endDate
const ShowTicketReport = async (id: string | number, startDate: string, endDate: string): Promise<Ticket[]> => { const ShowTicketReport = async ({
userId,
startDate,
endDate,
pageNumber = "1"
}: Request): Promise<Response> => {
let where_clause = {} let where_clause = {}
if(id=='0'){ if(userId=='0'){
where_clause = { where_clause = {
createdAt: { createdAt: {
[Op.gte]: startDate+' 00:00:00.000000', [Op.gte]: startDate+' 00:00:00.000000',
@ -31,7 +50,7 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate:
} }
else{ else{
where_clause = { where_clause = {
userid: id, userid: userId,
createdAt: { createdAt: {
[Op.gte]: startDate+' 00:00:00.000000', [Op.gte]: startDate+' 00:00:00.000000',
[Op.lte]: endDate +' 23:59:59.999999' [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 {count, rows: tickets} = await Ticket.findAndCountAll({
const ticket = await Ticket.findAll({
where: where_clause , where: where_clause ,
limit,
offset,
//attributes: ['id', 'status', 'createdAt', 'updatedAt'], //attributes: ['id', 'status', 'createdAt', 'updatedAt'],
attributes: ['id', 'status', 'statusChatEnd', [Sequelize.fn("DATE_FORMAT",Sequelize.col("Ticket.createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"], attributes: ['id', 'status', 'statusChatEnd', [Sequelize.fn("DATE_FORMAT",Sequelize.col("Ticket.createdAt"),"%d/%m/%Y %H:%i:%s"),"createdAt"],
@ -82,14 +104,20 @@ const ShowTicketReport = async (id: string | number, startDate: string, endDate:
}, },
], ],
order: [
['id', 'ASC']
]
}); });
const hasMore = count > offset + tickets.length;
if (!ticket) {
if (!tickets) {
throw new AppError("ERR_NO_TICKET_FOUND", 404); throw new AppError("ERR_NO_TICKET_FOUND", 404);
} }
return ticket; return {tickets, count, hasMore};
}; };
export default ShowTicketReport; export default ShowTicketReport;

View File

@ -825,6 +825,7 @@ const handleMsgAck = async (msg: WbotMessage, ack: MessageAck) => {
action: "update", action: "update",
message: messageToUpdate message: messageToUpdate
}); });
} catch (err) { } catch (err) {
Sentry.captureException(err); Sentry.captureException(err);
logger.error(`Error handling message ack. Err: ${err}`); logger.error(`Error handling message ack. Err: ${err}`);

View File

@ -362,15 +362,17 @@ const MessagesList = ({ ticketId, isGroup }) => {
socket.on("connect", () => socket.emit("joinChatBox", ticketId)); socket.on("connect", () => socket.emit("joinChatBox", ticketId));
socket.on("appMessage", (data) => { socket.on("appMessage", (data) => {
if (data.action === "create") { if (data.action === "create") {
dispatch({ type: "ADD_MESSAGE", payload: data.message }); dispatch({ type: "ADD_MESSAGE", payload: data.message });
// console.log('* NOVA MENSAGEM CAP: ', data.message)
scrollToBottom(); scrollToBottom();
} }
if (data.action === "update") { if (data.action === "update") {
console.log('2 THIS IS THE DATA: ', data)
dispatch({ type: "UPDATE_MESSAGE", payload: data.message }); dispatch({ type: "UPDATE_MESSAGE", payload: data.message });
} }
}); });

View File

@ -4,33 +4,73 @@ import MaterialTable from 'material-table';
import Modal from '../Modal' import Modal from '../Modal'
import { render } from '@testing-library/react'; import { render } from '@testing-library/react';
import chat from '@material-ui/icons/Chat';
// import Button from "@material-ui/core/Button";
import React from 'react'; import React from 'react';
const MTable = (props) => { 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((dt) => { return { ...dt }});
const dataLoad = props.data.map(({ user, ...others }) => ({ ...others, 'user': user ? user : { name: 'Aguardando atendente', email: '' } })); const dataLoad = props.data.map(({ user, ...others }) => ({ ...others, 'user': user ? user : { name: 'Aguardando atendente', email: '' } }));
const columnsLoad = props.columns.map((column) => { return { ...column } }); const columnsLoad = props.columns.map((column) => { return { ...column } });
useEffect(() => { useEffect(() => {
console.log(`You have clicked the button ${selectedRow} times`) console.log(`You have clicked the button ${selectedRow} times`)
// console.log('TABLE REF: ', tableRef)
}, [selectedRow]); }, [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 ( return (
<>
{/* <Button onClick={handleTest}>Toggle</Button> */}
{/* <CircularProgress /> */}
<MaterialTable <MaterialTable
title={props.table_title} title={props.table_title}
columns={columnsLoad} columns={columnsLoad}
data={dataLoad} data={dataLoad}
maxWidth={true} maxWidth={true}
tableRef={tableRef}
localization={{ header: { actions: props.hasChild ? 'Ticket' : null },}}
onRowClick={(evt, selectedRow) => { onRowClick={(evt, selectedRow) => {
if (props.removeClickRow) { if (props.removeClickRow) {
@ -56,23 +96,52 @@ const MTable = (props) => {
} }
} }
actions={[
(rowData) => {
if(props.hasChild){
return {
icon: chat,
tooltip: `Ticket id ${rowData.id}`,
disable: false,
onClick: (event, rowData) => {
openInNewTab(`/tickets/${rowData.id}`)
}
}
}
}
]}
options={{ options={{
search: true, search: true,
selection: false, selection: false,
paging: false, paging: false,
padding: 'dense', padding: 'dense',
exportButton: props.hasChild ? true : null,
exportAllData: true,
sorting: true ? props.hasChild : false, sorting: true ? props.hasChild : false,
//loadingType: 'linear', loadingType: 'circular',
searchFieldStyle: { searchFieldStyle: {
width: 300, width: 300,
}, },
pageSize: 20, pageSize: 20,
headerStyle: { headerStyle: {
position: "sticky", position: "sticky",
top: "0" top: "0"
}, },
maxBodyHeight: "400px", maxBodyHeight: "400px",
// minBodyHeight: "85vh", //FIXME to calculate dynamic height, needed for correct scroll position identification
// maxBodyHeight: "85vh",
rowStyle: rowData => ({ rowStyle: rowData => ({
fontSize: 12, fontSize: 12,
@ -80,6 +149,9 @@ const MTable = (props) => {
}) })
}} }}
/> />
</>
); );
}; };

View File

@ -286,6 +286,7 @@ const TicketsList = (props) => {
}; };
const handleScroll = e => { const handleScroll = e => {
if (!hasMore || loading) return; if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;

View File

@ -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") { if (action.type === "UPDATE_WHATSAPPS_SESSION_MONIT") {
const whatsApp = action.payload; const whatsApp = action.payload;
const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id); const whatsAppIndex = state.findIndex(s => s.id === whatsApp.id);
@ -108,10 +121,6 @@ const useWhatsApps = () => {
} }
}); });
//test del
socket.on("whatsappSessionMonit", data => { socket.on("whatsappSessionMonit", data => {
if (data.action === "update") { if (data.action === "update") {
@ -121,7 +130,7 @@ const useWhatsApps = () => {
} }
}); });
//
return () => { return () => {
socket.disconnect(); socket.disconnect();

View File

@ -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 { toast } from "react-toastify";
import { format, parseISO } from "date-fns"; import { format, parseISO } from "date-fns";
import openSocket from "socket.io-client";
import { makeStyles } from "@material-ui/core/styles"; import { makeStyles } from "@material-ui/core/styles";
import { green } from "@material-ui/core/colors"; import { green } from "@material-ui/core/colors";
import { import {
@ -111,6 +113,8 @@ const Connections = () => {
const [selectedWhatsApp, setSelectedWhatsApp] = useState(null); const [selectedWhatsApp, setSelectedWhatsApp] = useState(null);
const [confirmModalOpen, setConfirmModalOpen] = useState(false); const [confirmModalOpen, setConfirmModalOpen] = useState(false);
const [diskSpaceInfo, setDiskSpaceInfo] = useState({});
@ -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 ( return (
<Can <Can
@ -355,7 +376,10 @@ const Connections = () => {
/> />
<MainHeader> <MainHeader>
<Title>{i18n.t("connections.title")}</Title> <Title>{i18n.t("connections.title")}</Title>
<MainHeaderButtonsWrapper> <MainHeaderButtonsWrapper>
<Can <Can
role={user.profile} role={user.profile}
@ -370,10 +394,51 @@ const Connections = () => {
)} )}
/> />
</MainHeaderButtonsWrapper> </MainHeaderButtonsWrapper>
</MainHeader> </MainHeader>
<Paper className={classes.mainPaper} variant="outlined"> <Paper className={classes.mainPaper} variant="outlined">
<>
<Can
role={user.profile}
perform="space-disk-info:show"
yes={() => (
<>
<Table size="small">
<TableHead>
<TableRow>
<TableCell align="center">
Size
</TableCell>
<TableCell align="center">
Used
</TableCell>
<TableCell align="center">
Available
</TableCell>
<TableCell align="center">
Use%
</TableCell>
</TableRow>
</TableHead>
<TableBody>
<TableRow>
<TableCell align="center">{diskSpaceInfo.size}</TableCell>
<TableCell align="center">{diskSpaceInfo.used}</TableCell>
<TableCell align="center">{diskSpaceInfo.available}</TableCell>
<TableCell align="center">{diskSpaceInfo.use}</TableCell>
</TableRow>
</TableBody>
</Table>
<br />
</>
)}
/>
<Table size="small"> <Table size="small">
<TableHead> <TableHead>
<TableRow> <TableRow>
@ -557,6 +622,8 @@ const Connections = () => {
)} )}
</TableBody> </TableBody>
</Table> </Table>
</>
</Paper> </Paper>
</MainContainer> </MainContainer>
)} )}

View File

@ -36,17 +36,23 @@ import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../../components/Can"; import { Can } from "../../components/Can";
const reducer = (state, action) => { const reducer = (state, action) => {
if (action.type === "LOAD_CONTACTS") { if (action.type === "LOAD_CONTACTS") {
const contacts = action.payload; const contacts = action.payload;
const newContacts = []; const newContacts = [];
contacts.forEach((contact) => { contacts.forEach((contact) => {
const contactIndex = state.findIndex((c) => c.id === contact.id); const contactIndex = state.findIndex((c) => c.id === contact.id);
if (contactIndex !== -1) { if (contactIndex !== -1) {
state[contactIndex] = contact; state[contactIndex] = contact;
} else { } else {
newContacts.push(contact); newContacts.push(contact);
} }
}); });
return [...state, ...newContacts]; return [...state, ...newContacts];
@ -210,6 +216,9 @@ const Contacts = () => {
const handleScroll = (e) => { const handleScroll = (e) => {
if (!hasMore || loading) return; if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
// console.log('scrollTop: ', scrollTop, ' | scrollHeight: ', scrollHeight, ' | clientHeight: ', clientHeight)
if (scrollHeight - (scrollTop + 100) < clientHeight) { if (scrollHeight - (scrollTop + 100) < clientHeight) {
loadMore(); loadMore();
} }

View File

@ -18,8 +18,11 @@ import MaterialTable from 'material-table';
import LogoutIcon from '@material-ui/icons/CancelOutlined'; import LogoutIcon from '@material-ui/icons/CancelOutlined';
import { CSVLink } from "react-csv"; import { CSVLink } from "react-csv";
// import CircularProgress from '@mui/material/CircularProgress';
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
@ -117,7 +120,6 @@ const reducerQ = (state, action) => {
if (action.type === 'LOAD_QUERY') { if (action.type === 'LOAD_QUERY') {
const queries = action.payload const queries = action.payload
const newQueries = [] const newQueries = []
@ -308,12 +310,19 @@ const Report = () => {
const csvLink = useRef() const csvLink = useRef()
const { user: userA } = useContext(AuthContext); const { user: userA } = useContext(AuthContext);
//-------- //--------
const [searchParam] = useState(""); 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 [pageNumber, setPageNumber] = useState(1);
const [users, dispatch] = useReducer(reducer, []); const [users, dispatch] = useReducer(reducer, []);
//const [columns, setColums] = useState([]) //const [columns, setColums] = useState([])
@ -336,7 +345,7 @@ const Report = () => {
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
dispatchQ({ type: "RESET" }) dispatchQ({ type: "RESET" })
setTicketsPageNumber(1)
setPageNumber(1); setPageNumber(1);
}, [searchParam, profile]); }, [searchParam, profile]);
@ -380,20 +389,24 @@ const Report = () => {
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
setLoading(true);
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
if (reportOption === '1') { if (reportOption === '1') {
const dataQuery = await api.get("/reports/", { params: { userId, startDate, endDate }, }); const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets }, });
dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data });
//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') { else if (reportOption === '2') {
@ -420,7 +433,7 @@ const Report = () => {
}, 500); }, 500);
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn);
}, [userId, startDate, endDate, reportOption]); }, [userId, startDate, endDate, reportOption, pageNumberTickets, totalCountTickets]);
// Get from child 1 // Get from child 1
@ -464,27 +477,15 @@ const Report = () => {
}, [reportOption]) }, [reportOption])
// useEffect(() => {
// console.log('>>>>>>>>>>>>>>>>>> New query: ', query)
// }, [query])
// test del
const handleCSVMessages = () => { const handleCSVMessages = () => {
// setLoading(true);
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
const dataQuery = await api.get("/reports/messages", { params: { userId, startDate, endDate }, }); 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) { if (dataQuery.data.length > 0) {
@ -499,15 +500,10 @@ const Report = () => {
} }
} }
// console.log('dataCSVFormat: ', dataCSVFormat)
setDataCSV(dataCSVFormat) setDataCSV(dataCSVFormat)
setIsMount(false); setIsMount(false);
// setDataCSV(dataQuery.data)
} }
// setLoading(false);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
@ -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) => { // const handleDeleteRows = (id) => {
@ -625,6 +625,30 @@ const Report = () => {
}; };
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);
}
};
return ( return (
<Can <Can
@ -639,7 +663,7 @@ const Report = () => {
return { 'value': obj.id, 'label': obj.name } return { 'value': obj.id, 'label': obj.name }
})} /></Item> })} /></Item>
<Item><DatePicker1 func={datePicker1Value} minDate={true} startEmpty={false} title={'Data inicio'} /></Item> <Item><DatePicker1 func={datePicker1Value} minDate={false} startEmpty={false} title={'Data inicio'} /></Item>
<Item><DatePicker2 func={datePicker2Value} minDate={false} startEmpty={false} title={'Data fim'} /></Item> <Item><DatePicker2 func={datePicker2Value} minDate={false} startEmpty={false} title={'Data fim'} /></Item>
<Item sx={{ display: 'grid', gridColumn: '4 / 5', }}> <Item sx={{ display: 'grid', gridColumn: '4 / 5', }}>
@ -651,13 +675,15 @@ const Report = () => {
{reportOption === '1' && {reportOption === '1' &&
<div> <div>
{/* <Button <Button style={{display: "none"}}
variant="contained" variant="contained"
color="primary" color="primary"
onClick={(e) => handleCSVMessages()} onClick={(e) => {
handleCSVMessages()
}}
> >
{"CSV ALL"} {"CSV ALL"}
</Button> */} </Button>
<div> <div>
<CSVLink <CSVLink
@ -674,10 +700,6 @@ const Report = () => {
</Item> </Item>
</Box> </Box>
<Box sx={{ <Box sx={{
@ -688,11 +710,22 @@ const Report = () => {
{reportOption === '1' && {reportOption === '1' &&
// <div onScroll={handleScroll} style={{ height: 400, overflowY: "scroll" }}>
// </div>
<>
<MTable data={query} <MTable data={query}
columns={columnsData} columns={columnsData}
hasChild={true} hasChild={true}
removeClickRow={false} removeClickRow={false}
handleScroll={handleScroll}
table_title={'Atendimento por atendentes'} /> table_title={'Atendimento por atendentes'} />
</>
} }
{reportOption === '2' && {reportOption === '2' &&

View File

@ -29,6 +29,7 @@ const rules = {
"show-icon-add-queue", "show-icon-add-queue",
"show-icon-edit-queue", "show-icon-edit-queue",
"show-icon-delete-queue", "show-icon-delete-queue",
"space-disk-info:show",
"drawer-admin-items:view", "drawer-admin-items:view",