Compare commits

..

No commits in common. "b05cd7d1d6c65be8d4f140e7ac4020725966dbea" and "9f040ce953d07393a951e0738b81ce94672cf240" have entirely different histories.

13 changed files with 42 additions and 337 deletions

View File

@ -20,7 +20,6 @@ import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser";
import { getIO } from "../libs/socket"; import { getIO } from "../libs/socket";
import { Json } from "sequelize/types/lib/utils"; import { Json } from "sequelize/types/lib/utils";
import ReportByNumberQueueService from "../services/ReportServices/ReportByNumberQueueService"; import ReportByNumberQueueService from "../services/ReportServices/ReportByNumberQueueService";
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService";
type IndexQuery = { type IndexQuery = {
userId: string; userId: string;
@ -332,6 +331,7 @@ export const reportServiceByQueue = async (
const { startDate, endDate, queueId } = req.query as IndexQuery; const { startDate, endDate, queueId } = req.query as IndexQuery;
const reportService = await ReportByNumberQueueService({ const reportService = await ReportByNumberQueueService({
startDate, startDate,
endDate, endDate,
@ -340,29 +340,3 @@ export const reportServiceByQueue = async (
return res.status(200).json({ reportService }); return res.status(200).json({ reportService });
}; };
export const reportTicksCountByStatusChatEnds = async (
req: Request,
res: Response
): Promise<Response> => {
if (
req.user.profile !== "master" &&
req.user.profile !== "admin" &&
req.user.profile !== "supervisor"
) {
throw new AppError("ERR_NO_PERMISSION", 403);
}
const { startDate, endDate } = req.query as IndexQuery;
const dateToday = splitDateTime(
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
);
const reportStatusChatEnd = await CountStatusChatEndService(
startDate || dateToday.fullDate,
endDate || dateToday.fullDate
);
return res.status(200).json({ reportStatusChatEnd });
};

View File

@ -76,7 +76,6 @@ import CreateContactService from "../services/ContactServices/CreateContactServi
import { botSendMessage } from "../services/WbotServices/wbotMessageListener"; import { botSendMessage } from "../services/WbotServices/wbotMessageListener";
import WhatsappQueue from "../models/WhatsappQueue"; import WhatsappQueue from "../models/WhatsappQueue";
import { get } from "../helpers/RedisClient" import { get } from "../helpers/RedisClient"
import CountStatusChatEndService from "../services/StatusChatEndService/CountStatusChatEndService"
export const index = async (req: Request, res: Response): Promise<Response> => { export const index = async (req: Request, res: Response): Promise<Response> => {
const { const {
@ -349,8 +348,7 @@ export const update = async (
ticketData: { ticketData: {
status: status, status: status,
userId: userId, userId: userId,
statusChatEnd: statusChatEndName.name, statusChatEnd: statusChatEndName.name
statusChatEndId: statusChatEndName.id
}, },
ticketId ticketId
}); });

View File

@ -1,17 +0,0 @@
import { QueryInterface, DataTypes } from "sequelize";
module.exports = {
up: (queryInterface: QueryInterface) => {
return queryInterface.addColumn("Tickets", "statusChatEndId", {
type: DataTypes.INTEGER,
references: { model: "StatusChatEnds", key: "id" },
onUpdate: "CASCADE",
onDelete: "SET NULL",
allowNull: true
});
},
down: (queryInterface: QueryInterface) => {
return queryInterface.removeColumn("Tickets", "statusChatEndId");
}
};

View File

@ -10,7 +10,6 @@ import {
} from "sequelize-typescript"; } from "sequelize-typescript";
import SchedulingNotify from "./SchedulingNotify"; import SchedulingNotify from "./SchedulingNotify";
import Ticket from "./Ticket"
@Table @Table
class StatusChatEnd extends Model<StatusChatEnd> { class StatusChatEnd extends Model<StatusChatEnd> {
@ -30,9 +29,6 @@ import Ticket from "./Ticket"
@HasMany(() => SchedulingNotify) @HasMany(() => SchedulingNotify)
SchedulingNotifies: SchedulingNotify[]; SchedulingNotifies: SchedulingNotify[];
@HasMany(() => Ticket)
tickets: Ticket[];
} }
export default StatusChatEnd; export default StatusChatEnd;

View File

@ -21,7 +21,6 @@ import User from "./User";
import Whatsapp from "./Whatsapp"; import Whatsapp from "./Whatsapp";
import SchedulingNotify from "./SchedulingNotify"; import SchedulingNotify from "./SchedulingNotify";
import StatusChatEnd from "./StatusChatEnd"
@Table @Table
class Ticket extends Model<Ticket> { class Ticket extends Model<Ticket> {
@ -43,10 +42,6 @@ class Ticket extends Model<Ticket> {
@Column @Column
isGroup: boolean; isGroup: boolean;
@ForeignKey(() => StatusChatEnd)
@Column
statusChatEndId: number;
@Column @Column
statusChatEnd: string; statusChatEnd: string;

View File

@ -7,19 +7,11 @@ import * as ReportController from "../controllers/ReportController";
const reportRoutes = express.Router(); const reportRoutes = express.Router();
reportRoutes.get( reportRoutes.get("/reports", isAuth, ReportController.reportUserByDateStartDateEnd);
"/reports",
isAuth,
ReportController.reportUserByDateStartDateEnd
);
reportRoutes.post("/reports/onqueue", ReportController.reportOnQueue); reportRoutes.post("/reports/onqueue", ReportController.reportOnQueue);
reportRoutes.get( reportRoutes.get("/reports/user/services", isAuth, ReportController.reportUserService);
"/reports/user/services",
isAuth,
ReportController.reportUserService
);
reportRoutes.get( reportRoutes.get(
"/reports/services/numbers", "/reports/services/numbers",
@ -27,22 +19,8 @@ reportRoutes.get(
ReportController.reportService ReportController.reportService
); );
reportRoutes.get( reportRoutes.get("/reports/services/queues", isAuth, ReportController.reportServiceByQueue);
"/reports/services/queues",
isAuth,
ReportController.reportServiceByQueue
);
reportRoutes.get( reportRoutes.get("/reports/messages", isAuth, ReportController.reportMessagesUserByDateStartDateEnd);
"/reports/messages",
isAuth,
ReportController.reportMessagesUserByDateStartDateEnd
);
reportRoutes.get(
"/reports/count/statusChatEnd",
isAuth,
ReportController.reportTicksCountByStatusChatEnds
);
export default reportRoutes; export default reportRoutes;

View File

@ -113,8 +113,7 @@ const ReportByNumberQueueService = async ({
AND m.fromMe = 0 AND m.fromMe = 0
-- AND q.id = 2 -- AND q.id = 2
AND w.number = ${number} AND w.number = ${number}
AND t.status IN ('open', 'closed') AND (t.status = 'open' OR t.status = 'closed')
HAVING WAITING_TIME IS NOT NULL
ORDER BY ORDER BY
WAITING_TIME;`, WAITING_TIME;`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
@ -238,8 +237,7 @@ const ReportByNumberQueueService = async ({
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 q.id = ${q.id} AND q.id = ${q.id}
AND t.status IN ('open', 'closed') AND (t.status = 'open' OR t.status = 'closed')
HAVING WAITING_TIME IS NOT NULL
ORDER BY ORDER BY
WAITING_TIME;`, WAITING_TIME;`,
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }

View File

@ -1,27 +0,0 @@
import StatusChatEnd from "../../models/StatusChatEnd";
import AppError from "../../errors/AppError";
import { Sequelize } from "sequelize";
import { splitDateTime } from "../../helpers/SplitDateTime"
import ptBR from "date-fns/locale/pt-BR";
import { format } from "date-fns"
const dbConfig = require("../../config/database");
const sequelize = new Sequelize(dbConfig);
const { QueryTypes } = require("sequelize");
const CountStatusChatEndService = async (
startDate: string,
endDate: string
) => {
const countStatusChatEnd: any = await sequelize.query(
`select t.id, s.name, count(t.id) as count from Tickets t join StatusChatEnds s on
t.statusChatEndId = s.id and DATE(t.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
group by s.id;`,
{ type: QueryTypes.SELECT }
);
return countStatusChatEnd;
};
export default CountStatusChatEndService;

View File

@ -7,7 +7,7 @@ import Queue from "../../models/Queue";
import Message from "../../models/Message"; import Message from "../../models/Message";
import { userInfo } from "os"; import { userInfo } from "os";
import { Op, QueryTypes, json, where } from "sequelize"; import { Op, QueryTypes, where } from "sequelize";
import { Sequelize } from "sequelize"; import { Sequelize } from "sequelize";
import moment from "moment"; import moment from "moment";
@ -17,7 +17,7 @@ const sequelize = new Sequelize(dbConfig);
import { startOfDay, endOfDay, parseISO, getDate } from "date-fns"; 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"
interface Request { interface Request {
userId: string | number; userId: string | number;
@ -73,7 +73,9 @@ const ShowTicketReport = async ({
{ type: QueryTypes.SELECT } { type: QueryTypes.SELECT }
); );
let { count, rows: tickets }: any = await Ticket.findAndCountAll({ console.log('QUERY: ', query)
const { count, rows: tickets } = await Ticket.findAndCountAll({
where: { where: {
id: { [Op.in]: _ticketsId.map((t: any) => t.id) } id: { [Op.in]: _ticketsId.map((t: any) => t.id) }
}, },
@ -151,58 +153,6 @@ const ShowTicketReport = async ({
throw new AppError("ERR_NO_TICKET_FOUND", 404); throw new AppError("ERR_NO_TICKET_FOUND", 404);
} }
const ticketIds = tickets.map((t: any) => t.id);
if (ticketIds.length > 0) {
const waiting_time: any = await sequelize.query(
`SELECT t.id as ticketId, t.status, TIME_FORMAT(
SEC_TO_TIME(
TIMESTAMPDIFF(
SECOND,
(
SELECT createdAt
FROM Messages
WHERE ticketId = m.ticketId
AND fromMe = 0
ORDER BY createdAt ASC
LIMIT 1
),
(
SELECT createdAt
FROM Messages
WHERE ticketId = m.ticketId
AND fromAgent = 1
ORDER BY createdAt ASC
LIMIT 1
)
)
), '%H:%i:%s') AS WAITING_TIME
FROM Tickets t
JOIN Messages m ON t.id = m.ticketId
JOIN Whatsapps w ON t.whatsappId = w.id
JOIN Queues q ON q.id = t.queueId
WHERE DATE(m.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
AND t.id IN (${ticketIds.join()})
AND m.createdAt = (SELECT MIN(createdAt) FROM Messages WHERE ticketId = t.id)
AND m.fromMe = 0
AND t.status IN ('open', 'closed')
HAVING WAITING_TIME IS NOT NULL
ORDER BY
WAITING_TIME;`,
{ type: QueryTypes.SELECT }
);
for (let w of waiting_time) {
const { ticketId, status, WAITING_TIME } = w;
const index = tickets.findIndex((t: any) => +t?.id == +ticketId);
if (index != -1) {
tickets[index].dataValues.waiting_time = WAITING_TIME;
}
}
}
return { tickets, count, hasMore }; return { tickets, count, hasMore };
}; };

View File

@ -10,7 +10,7 @@ import { createOrUpdateTicketCache } from "../../helpers/TicketCache";
import AppError from "../../errors/AppError"; import AppError from "../../errors/AppError";
import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket"; import sendWhatsAppMessageSocket from "../../helpers/SendWhatsappMessageSocket";
import BotIsOnQueue from "../../helpers/BotIsOnQueue"; import BotIsOnQueue from "../../helpers/BotIsOnQueue";
import { deleteObject } from "../../helpers/RedisClient"; import { deleteObject } from "../../helpers/RedisClient"
var flatten = require("flat"); var flatten = require("flat");
interface TicketData { interface TicketData {
@ -18,7 +18,6 @@ interface TicketData {
userId?: number; userId?: number;
queueId?: number; queueId?: number;
statusChatEnd?: string; statusChatEnd?: string;
statusChatEndId?: number;
unreadMessages?: number; unreadMessages?: number;
whatsappId?: string | number; whatsappId?: string | number;
} }
@ -47,7 +46,6 @@ const UpdateTicketService = async ({
queueId, queueId,
statusChatEnd, statusChatEnd,
unreadMessages, unreadMessages,
statusChatEndId,
whatsappId whatsappId
} = ticketData; } = ticketData;
@ -75,7 +73,6 @@ const UpdateTicketService = async ({
userId, userId,
unreadMessages, unreadMessages,
statusChatEnd, statusChatEnd,
statusChatEndId,
whatsappId whatsappId
}); });

View File

@ -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</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

View File

@ -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),
@ -498,18 +497,11 @@ 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>
<Grid item xs={12} sm={12} md={6} lg={6}>
<Paper className={classes.fixedHeightPaper} variant="outlined">
<PieChart data={[]} />
</Paper>
</Grid>
</Grid>
</Grid> </Grid>
</Paper> </Paper>
<Paper className={classes.containerPaperFix} style={{ marginTop: "21px" }} sx={12}> <Paper className={classes.containerPaperFix} style={{ marginTop: "21px" }} sx={12}>

View File

@ -235,7 +235,6 @@ let columnsData = [
{ title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' }, { title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' },
{ title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' }, { title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' },
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }, { title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' },
{ title: `Espera`, field: 'waiting_time' },
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true }, { title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
] ]
@ -250,7 +249,6 @@ let columnsDataSuper = [
{ title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' }, { title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' },
{ title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' }, { title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' },
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }, { title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' },
{ title: `Espera`, field: 'waiting_time' },
{ title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true }, { title: `Mensagens`, field: 'messagesToFilter', searchable: true, hidden: true },
] ]