Compare commits
No commits in common. "76929c41ec4b31efb31ab1fec62ab604a87b5504" and "dc5a6945d294c48663e0f8a8067733e008e2b97d" have entirely different histories.
76929c41ec
...
dc5a6945d2
|
@ -262,7 +262,9 @@ export const reportMessagesUserByDateStartDateEnd = async (
|
||||||
data_query_messages[i].fromMe = "Cliente";
|
data_query_messages[i].fromMe = "Cliente";
|
||||||
}
|
}
|
||||||
|
|
||||||
data_query_messages[i].id = i + 1;
|
data_query_messages[i].id = i + 1;
|
||||||
|
|
||||||
|
console.log("data_query_messages: ", data_query_messages[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return res.status(200).json(data_query_messages);
|
return res.status(200).json(data_query_messages);
|
||||||
|
|
|
@ -18,7 +18,6 @@ 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";
|
||||||
import Query from "mysql2/typings/mysql/lib/protocol/sequences/Query";
|
import Query from "mysql2/typings/mysql/lib/protocol/sequences/Query";
|
||||||
import { te } from "date-fns/locale";
|
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
userId: string | number;
|
userId: string | number;
|
||||||
|
@ -44,56 +43,43 @@ const ShowTicketReport = async ({
|
||||||
createdOrUpdated = "created",
|
createdOrUpdated = "created",
|
||||||
queueId
|
queueId
|
||||||
}: Request): Promise<Response> => {
|
}: Request): Promise<Response> => {
|
||||||
// let where_clause: any = {};
|
let where_clause: any = {};
|
||||||
// let query = "";
|
let query = "";
|
||||||
|
|
||||||
// if (userId !== "0") {
|
if (userId !== "0") {
|
||||||
// where_clause.userid = userId;
|
where_clause.userid = userId;
|
||||||
// query = `AND t.userId = ${userId}`;
|
query = `AND t.userId = ${userId}`;
|
||||||
// }
|
}
|
||||||
|
|
||||||
// if (queueId) {
|
|
||||||
// where_clause.queueId = queueId;
|
|
||||||
// query = `AND t.queueId = ${queueId}`;
|
|
||||||
// }
|
|
||||||
|
|
||||||
const createdAtOrUpdatedAt =
|
|
||||||
createdOrUpdated == "created" ? "createdAt" : "updatedAt";
|
|
||||||
|
|
||||||
let where_clause = {};
|
|
||||||
|
|
||||||
if (queueId) {
|
if (queueId) {
|
||||||
where_clause = {
|
where_clause.queueId = queueId;
|
||||||
queueId: queueId,
|
query = `AND t.queueId = ${queueId}`;
|
||||||
[createdAtOrUpdatedAt]: {
|
|
||||||
[Op.gte]: startDate + " 00:00:00.000000",
|
|
||||||
[Op.lte]: endDate + " 23:59:59.999999"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (userId == "0") {
|
|
||||||
where_clause = {
|
|
||||||
[createdAtOrUpdatedAt]: {
|
|
||||||
[Op.gte]: startDate + " 00:00:00.000000",
|
|
||||||
[Op.lte]: endDate + " 23:59:59.999999"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
} else if (userId != "0") {
|
|
||||||
where_clause = {
|
|
||||||
userid: userId,
|
|
||||||
[createdAtOrUpdatedAt]: {
|
|
||||||
[Op.gte]: startDate + " 00:00:00.000000",
|
|
||||||
[Op.lte]: endDate + " 23:59:59.999999"
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const limit = 40;
|
const limit = 40;
|
||||||
const offset = limit * (+pageNumber - 1);
|
const offset = limit * (+pageNumber - 1);
|
||||||
|
|
||||||
|
const createdAtOrUpdatedAt =
|
||||||
|
createdOrUpdated == "created" ? "createdAt" : "updatedAt";
|
||||||
|
|
||||||
|
const _ticketsId = await sequelize.query(
|
||||||
|
`SELECT t.id
|
||||||
|
FROM Tickets t
|
||||||
|
INNER JOIN (
|
||||||
|
SELECT DISTINCT ticketId
|
||||||
|
FROM Messages
|
||||||
|
WHERE ${createdAtOrUpdatedAt} >= '${startDate} 00:00:00' AND ${createdAtOrUpdatedAt} <= '${endDate} 23:59:59'
|
||||||
|
) AS m ON m.ticketId = t.id ${query};`,
|
||||||
|
{ type: QueryTypes.SELECT }
|
||||||
|
);
|
||||||
|
|
||||||
let { count, rows: tickets }: any = await Ticket.findAndCountAll({
|
let { count, rows: tickets }: any = await Ticket.findAndCountAll({
|
||||||
where: where_clause,
|
where: {
|
||||||
|
id: { [Op.in]: _ticketsId.map((t: any) => t.id) }
|
||||||
|
},
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
|
|
||||||
attributes: [
|
attributes: [
|
||||||
"id",
|
"id",
|
||||||
"status",
|
"status",
|
||||||
|
@ -165,41 +151,43 @@ const ShowTicketReport = async ({
|
||||||
throw new AppError("ERR_NO_TICKET_FOUND", 404);
|
throw new AppError("ERR_NO_TICKET_FOUND", 404);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (tickets.length > 0) {
|
const ticketIds = tickets.map((t: any) => t.id);
|
||||||
|
|
||||||
|
if (ticketIds.length > 0) {
|
||||||
const waiting_time: any = await sequelize.query(
|
const waiting_time: any = await sequelize.query(
|
||||||
`SELECT t.id as ticketId, t.status, TIME_FORMAT(
|
`SELECT t.id as ticketId, t.status, TIME_FORMAT(
|
||||||
SEC_TO_TIME(
|
SEC_TO_TIME(
|
||||||
TIMESTAMPDIFF(
|
TIMESTAMPDIFF(
|
||||||
SECOND,
|
SECOND,
|
||||||
(
|
(
|
||||||
SELECT createdAt
|
SELECT createdAt
|
||||||
FROM Messages
|
FROM Messages
|
||||||
WHERE ticketId = m.ticketId
|
WHERE ticketId = m.ticketId
|
||||||
AND fromMe = 0
|
AND fromMe = 0
|
||||||
ORDER BY createdAt ASC
|
ORDER BY createdAt ASC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
),
|
),
|
||||||
(
|
(
|
||||||
SELECT createdAt
|
SELECT createdAt
|
||||||
FROM Messages
|
FROM Messages
|
||||||
WHERE ticketId = m.ticketId
|
WHERE ticketId = m.ticketId
|
||||||
AND fromAgent = 1
|
AND fromAgent = 1
|
||||||
ORDER BY createdAt ASC
|
ORDER BY createdAt ASC
|
||||||
LIMIT 1
|
LIMIT 1
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
), '%H:%i:%s') AS WAITING_TIME
|
), '%H:%i:%s') AS WAITING_TIME
|
||||||
FROM Tickets t
|
FROM Tickets t
|
||||||
JOIN Messages m ON t.id = m.ticketId
|
JOIN Messages m ON t.id = m.ticketId
|
||||||
JOIN Whatsapps w ON t.whatsappId = w.id
|
JOIN Whatsapps w ON t.whatsappId = w.id
|
||||||
JOIN Queues q ON q.id = t.queueId
|
JOIN Queues q ON q.id = t.queueId
|
||||||
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
AND t.id IN (${tickets.map((t: any) => t.id).join()})
|
AND t.id IN (${ticketIds.join()})
|
||||||
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
|
||||||
AND m.fromMe = 0
|
AND m.fromMe = 0
|
||||||
AND t.status IN ('open', 'closed')
|
AND t.status IN ('open', 'closed')
|
||||||
HAVING WAITING_TIME IS NOT NULL
|
HAVING WAITING_TIME IS NOT NULL
|
||||||
ORDER BY
|
ORDER BY
|
||||||
WAITING_TIME;`,
|
WAITING_TIME;`,
|
||||||
{ type: QueryTypes.SELECT }
|
{ type: QueryTypes.SELECT }
|
||||||
);
|
);
|
||||||
|
|
|
@ -1,127 +0,0 @@
|
||||||
import { Box } from '@material-ui/core';
|
|
||||||
import React from 'react';
|
|
||||||
import FiberManualRecordIcon from '@material-ui/icons/FiberManualRecord';
|
|
||||||
import { PieChart as RechartsPieChart, Pie, Sector, Cell, ResponsiveContainer } from 'recharts';
|
|
||||||
|
|
||||||
import Title from './Title';
|
|
||||||
|
|
||||||
const dataExample = [
|
|
||||||
{
|
|
||||||
"id": 3366,
|
|
||||||
"name": "FINALIZADO",
|
|
||||||
"count": 5
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3369,
|
|
||||||
"name": "LEMBRETE",
|
|
||||||
"count": 1
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3367,
|
|
||||||
"name": "EXEMPLO",
|
|
||||||
"count": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3364,
|
|
||||||
"name": "EXEMPLO 2",
|
|
||||||
"count": 3
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"id": 3364,
|
|
||||||
"name": "EXEMPLO 3",
|
|
||||||
"count": 6
|
|
||||||
},
|
|
||||||
]
|
|
||||||
|
|
||||||
const COLORS = [
|
|
||||||
'#0088FE', // Azul escuro
|
|
||||||
'#00C49F', // Verde menta
|
|
||||||
'#FFBB28', // Laranja escuro
|
|
||||||
'#FF8042', // Vermelho escuro
|
|
||||||
'#9D38BD', // Roxo escuro
|
|
||||||
'#FFD166', // Laranja claro
|
|
||||||
'#331F00', // Marrom escuro
|
|
||||||
'#C0FFC0', // Verde Claro
|
|
||||||
'#C4E538', // Verde-amarelo vibrante
|
|
||||||
'#A2A2A2', // Cinza claro
|
|
||||||
];;
|
|
||||||
|
|
||||||
const RADIAN = Math.PI / 180;
|
|
||||||
|
|
||||||
const renderCustomizedLabel = ({ cx, cy, midAngle, innerRadius, outerRadius, count }) => {
|
|
||||||
const radius = innerRadius + (outerRadius - innerRadius) * 0.75;
|
|
||||||
const x = cx + radius * Math.cos(-midAngle * RADIAN);
|
|
||||||
const y = cy + radius * Math.sin(-midAngle * RADIAN);
|
|
||||||
|
|
||||||
return (
|
|
||||||
<text x={x} y={y} fill="white" textAnchor={x > cx ? 'start' : 'end'} dominantBaseline="central">
|
|
||||||
{count}
|
|
||||||
</text>
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param data array de objetos no formato
|
|
||||||
* {
|
|
||||||
"id": number | string,
|
|
||||||
"name": string,
|
|
||||||
"count": number
|
|
||||||
* }
|
|
||||||
*/
|
|
||||||
const PieChart = ({ data = dataExample }) => {
|
|
||||||
return (
|
|
||||||
<Box width="100%" height="100%" position="relative" display="flex">
|
|
||||||
<Box sx={{ position: "absolute" }}>
|
|
||||||
<Title>Tickets status de encerramento</Title>
|
|
||||||
</Box>
|
|
||||||
<Box
|
|
||||||
component="ul"
|
|
||||||
sx={{
|
|
||||||
position: "absolute",
|
|
||||||
top: 0, right: 0,
|
|
||||||
display: "flex",
|
|
||||||
flexDirection: "column",
|
|
||||||
gap: "4px"
|
|
||||||
}}>
|
|
||||||
{data.map((entry, index) => {
|
|
||||||
return (
|
|
||||||
<Box
|
|
||||||
component="li"
|
|
||||||
key={entry.id}
|
|
||||||
sx={{
|
|
||||||
display: "flex", gap: "2px",
|
|
||||||
color: COLORS[index % COLORS.length],
|
|
||||||
alignItems: "center"
|
|
||||||
}}>
|
|
||||||
<FiberManualRecordIcon fill={COLORS[index % COLORS.length]} />
|
|
||||||
<text style={{ color: 'black' }}>{entry.name}</text>
|
|
||||||
</Box>
|
|
||||||
)
|
|
||||||
})}
|
|
||||||
</Box>
|
|
||||||
<Box width="100%" height="100%" alignSelf="flex-end">
|
|
||||||
<ResponsiveContainer width="100%" height="100%">
|
|
||||||
<RechartsPieChart width={400} height={400}>
|
|
||||||
<Pie
|
|
||||||
data={data}
|
|
||||||
cx="40%"
|
|
||||||
cy="60%"
|
|
||||||
labelLine={false}
|
|
||||||
label={renderCustomizedLabel}
|
|
||||||
outerRadius={100}
|
|
||||||
fill="#8884d8"
|
|
||||||
dataKey="count"
|
|
||||||
>
|
|
||||||
{data.map((entry, index) => (
|
|
||||||
<Cell key={`cell-${entry.id}`} fill={COLORS[index % COLORS.length]} />
|
|
||||||
))}
|
|
||||||
</Pie>
|
|
||||||
</RechartsPieChart>
|
|
||||||
</ResponsiveContainer>
|
|
||||||
</Box>
|
|
||||||
</Box >
|
|
||||||
);
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
export default PieChart
|
|
|
@ -15,7 +15,6 @@ import Info from "@material-ui/icons/Info"
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext"
|
import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
// import { i18n } from "../../translate/i18n";
|
// import { i18n } from "../../translate/i18n";
|
||||||
import Chart from "./Chart"
|
import Chart from "./Chart"
|
||||||
import PieChart from "./PieChart"
|
|
||||||
import openSocket from "socket.io-client"
|
import openSocket from "socket.io-client"
|
||||||
import api from "../../services/api"
|
import api from "../../services/api"
|
||||||
|
|
||||||
|
@ -32,7 +31,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
display: "flex",
|
display: "flex",
|
||||||
overflow: "auto",
|
overflow: "auto",
|
||||||
flexDirection: "column",
|
flexDirection: "column",
|
||||||
height: 280,
|
height: 240,
|
||||||
},
|
},
|
||||||
customFixedHeightPaper: {
|
customFixedHeightPaper: {
|
||||||
padding: theme.spacing(2),
|
padding: theme.spacing(2),
|
||||||
|
@ -109,7 +108,7 @@ const useStyles = makeStyles((theme) => ({
|
||||||
|
|
||||||
var _fifo
|
var _fifo
|
||||||
|
|
||||||
const sumOnlineTimeNow = (oldOnlineTimeSum) => {
|
const sumOnlineTimeNow = (oldOnlineTimeSum) => {
|
||||||
|
|
||||||
let onlineTime = new Date()
|
let onlineTime = new Date()
|
||||||
|
|
||||||
|
@ -139,7 +138,7 @@ const sumOnlineTimeNow = (oldOnlineTimeSum) => {
|
||||||
|
|
||||||
const isoDate = new Date(onlineTime)
|
const isoDate = new Date(onlineTime)
|
||||||
|
|
||||||
const newOnlinetime = isoDate.toJSON().slice(0, 19).replace('T', ' ')
|
const newOnlinetime = isoDate.toJSON().slice(0, 19).replace('T', ' ')
|
||||||
|
|
||||||
return newOnlinetime
|
return newOnlinetime
|
||||||
}
|
}
|
||||||
|
@ -208,9 +207,9 @@ const reducer = (state, action) => {
|
||||||
|
|
||||||
if ("onlineTime" in onlineUser) {
|
if ("onlineTime" in onlineUser) {
|
||||||
|
|
||||||
if ("sumOnlineTime" in state[index]) {
|
if ("sumOnlineTime" in state[index]) {
|
||||||
|
|
||||||
state[index].sumOnlineTime.sum = onlineUser.onlineTime.split(" ")[1]
|
state[index].sumOnlineTime.sum = onlineUser.onlineTime.split(" ")[1]
|
||||||
|
|
||||||
} else if (!("sumOnlineTime" in state[index])) {
|
} else if (!("sumOnlineTime" in state[index])) {
|
||||||
|
|
||||||
|
@ -258,7 +257,7 @@ const Dashboard = () => {
|
||||||
const [usersOnlineInfo, dispatch] = useReducer(reducer, [])
|
const [usersOnlineInfo, dispatch] = useReducer(reducer, [])
|
||||||
const [ticketStatusChange, setStatus] = useState()
|
const [ticketStatusChange, setStatus] = useState()
|
||||||
const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 })
|
const [ticketsStatus, setTicktsStatus] = useState({ open: 0, openAll: 0, pending: 0, closed: 0 })
|
||||||
const [ticketStatusChatEnd, setTicketStatusChatEnd] = useState([])
|
|
||||||
const { user } = useContext(AuthContext)
|
const { user } = useContext(AuthContext)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -284,20 +283,15 @@ const Dashboard = () => {
|
||||||
try {
|
try {
|
||||||
let date = new Date().toLocaleDateString("pt-BR").split("/")
|
let date = new Date().toLocaleDateString("pt-BR").split("/")
|
||||||
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
|
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
|
||||||
|
|
||||||
const { data } = await api.get("/reports/user/services", {
|
const { data } = await api.get("/reports/user/services", {
|
||||||
params: { userId: null, startDate: dateToday, endDate: dateToday },
|
params: { userId: null, startDate: dateToday, endDate: dateToday },
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// console.log('data.data: ', data.usersProfile)
|
||||||
|
|
||||||
dispatch({ type: "RESET" })
|
dispatch({ type: "RESET" })
|
||||||
dispatch({ type: "LOAD_QUERY", payload: data.usersProfile })
|
dispatch({ type: "LOAD_QUERY", payload: data.usersProfile })
|
||||||
|
|
||||||
const { data: ticketStatusChatEndData } = await api.get("/reports/count/statusChatEnd", {
|
|
||||||
params: { startDate: '2024-03-21', endDate: '2024-03-28' },
|
|
||||||
})
|
|
||||||
|
|
||||||
setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd)
|
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -325,7 +319,7 @@ const Dashboard = () => {
|
||||||
if (usersOnlineInfo[i].statusOnline && usersOnlineInfo[i].statusOnline.status === 'online') {
|
if (usersOnlineInfo[i].statusOnline && usersOnlineInfo[i].statusOnline.status === 'online') {
|
||||||
|
|
||||||
let onlineTimeCurrent = sumOnlineTimeNow({ onlineTime: usersOnlineInfo[i].statusOnline.onlineTime, updatedAt: usersOnlineInfo[i].statusOnline.updatedAt })
|
let onlineTimeCurrent = sumOnlineTimeNow({ onlineTime: usersOnlineInfo[i].statusOnline.onlineTime, updatedAt: usersOnlineInfo[i].statusOnline.updatedAt })
|
||||||
|
|
||||||
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: { userId: usersOnlineInfo[i].id, status: usersOnlineInfo[i].statusOnline.status, onlineTime: onlineTimeCurrent } })
|
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: { userId: usersOnlineInfo[i].id, status: usersOnlineInfo[i].statusOnline.status, onlineTime: onlineTimeCurrent } })
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -362,7 +356,7 @@ const Dashboard = () => {
|
||||||
})
|
})
|
||||||
|
|
||||||
socket.on("onlineStatus", (data) => {
|
socket.on("onlineStatus", (data) => {
|
||||||
if (data.action === "logout" || data.action === "update") {
|
if (data.action === "logout" || data.action === "update") {
|
||||||
|
|
||||||
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime })
|
dispatch({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime })
|
||||||
} else if (data.action === "delete") {
|
} else if (data.action === "delete") {
|
||||||
|
@ -503,17 +497,10 @@ const Dashboard = () => {
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item container spacing={3}>
|
<Grid item xs={12}>
|
||||||
<Grid item xs={12} sm={12} md={6} lg={6}>
|
<Paper className={classes.fixedHeightPaper} variant="outlined">
|
||||||
<Paper className={classes.fixedHeightPaper} variant="outlined">
|
<Chart allTickets={usersOnlineInfo} />
|
||||||
<Chart allTickets={usersOnlineInfo} />
|
</Paper>
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
<Grid item xs={12} sm={12} md={6} lg={6}>
|
|
||||||
<Paper className={classes.fixedHeightPaper} variant="outlined">
|
|
||||||
<PieChart data={ticketStatusChatEnd} />
|
|
||||||
</Paper>
|
|
||||||
</Grid>
|
|
||||||
</Grid>
|
</Grid>
|
||||||
</Grid>
|
</Grid>
|
||||||
</Paper>
|
</Paper>
|
||||||
|
|
|
@ -363,7 +363,7 @@ const Report = () => {
|
||||||
if (reportOption === '1') {
|
if (reportOption === '1') {
|
||||||
|
|
||||||
const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets, createdOrUpdated: selectedValue, queueId }, userQueues: userA.queues })
|
const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets, createdOrUpdated: selectedValue, queueId }, userQueues: userA.queues })
|
||||||
|
|
||||||
let ticketsQueue = data.tickets
|
let ticketsQueue = data.tickets
|
||||||
let userQueues = userA.queues
|
let userQueues = userA.queues
|
||||||
let filterQueuesTickets = []
|
let filterQueuesTickets = []
|
||||||
|
|
Loading…
Reference in New Issue