chore: update reports tab for supervisors, add reminders functionality, and include user roles

Details:
- Updated the reports tab to enhance functionality for supervisors.
- Implemented a reminders feature for improved user experience.
- Added user roles to enhance user management capabilities.
pull/22/head
gustavo.pinho 2024-02-01 17:46:23 -03:00
parent 0751374fb7
commit 6a5a51ff3f
14 changed files with 99 additions and 59 deletions

View File

@ -1,14 +0,0 @@
NODE_ENV=
BACKEND_URL=http://localhost
FRONTEND_URL=http://localhost:3000
PROXY_PORT=8080
PORT=8080
DB_DIALECT=
DB_HOST=
DB_USER=
DB_PASS=
DB_NAME=
JWT_SECRET=
JWT_REFRESH_SECRET=

View File

@ -34,6 +34,7 @@ type IndexQuery = {
startDate: string;
endDate: string;
pageNumber: string;
userQueues: [];
};
type ReportOnQueue = {
@ -52,12 +53,11 @@ export const reportUserByDateStartDateEnd = async (req: Request, res: Response):
throw new AppError("ERR_NO_PERMISSION", 403);
}
const { userId, startDate, endDate, pageNumber } = req.query as IndexQuery
const { userId, startDate, endDate, pageNumber, userQueues } = req.query as IndexQuery
console.log("userId, startDate, endDate, pageNumber: ", userId, startDate, endDate, pageNumber);
const { tickets, count, hasMore } = await ShowTicketReport({ userId, startDate, endDate, pageNumber });
// console.log('kkkkkkkkkkkkkkkkkk tickets: ', JSON.stringify(tickets, null, 6))
return res.status(200).json({ tickets, count, hasMore });

View File

@ -60,7 +60,7 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
auxUsers.push(user);
}
}
return res.json({ users: auxUsers, count, hasMore });
}
@ -134,7 +134,7 @@ export const all = async (req: Request, res: Response): Promise<Response> => {
};
export const store = async (req: Request, res: Response): Promise<Response> => {
const { email, password, name, profile, queueIds } = req.body;
const { email, password, name, profile, positionCompany, queueIds } = req.body;
if (
req.url === "/signup" &&
@ -149,6 +149,7 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
email,
password,
name,
positionCompany,
profile,
queueIds
});

View File

@ -11,6 +11,7 @@ import { convertBytes } from "./ConvertBytes";
import { deleteScheduleByTicketIdCache } from "./SchedulingNotifyCache";
import SchedulingNotify from "../models/SchedulingNotify";
import Ticket from "../models/Ticket";
import User from "../models/User";
import { Sequelize, Op } from "sequelize";
@ -57,20 +58,29 @@ const monitor = async () => {
status: { [Op.in]: ['open', 'pending'] }
}
})
await deleteScheduleByTicketIdCache(schedulingNotifies[i].ticketId)
await DeleteSchedulingNotifyService(schedulingNotifies[i].id)
if (_ticket) continue
if (ticket.dataValues.status == 'closed') {
await ticket.update({ status: 'pending' })
await ticket.update({ status: 'open' })
}
const userId: number = ticket.getDataValue('userId');
let userN = '';
if(userId){
const useer = await User.findByPk(userId);
if(useer){
const userName: string = useer.getDataValue('name');
userN = `*${userName}:* \n`;
}
}
await new Promise(f => setTimeout(f, 3000));
await SendWhatsAppMessage({
body: schedulingNotifies[i].message, ticket
body: userN+schedulingNotifies[i].message, ticket
});
@ -80,33 +90,33 @@ const monitor = async () => {
}
exec("df -h /", (error: any, stdout: any, stderr: any) => {
// 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;
}
// 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+/)
// 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]
}
});
// // DISK SPACE MONITORING
// const io = getIO();
// io.emit("diskSpaceMonit", {
// action: "update",
// diskSpace: {
// size: stdout[1],
// used: stdout[2],
// available: stdout[3],
// use: stdout[4]
// }
// });
});
// });

View File

@ -4,6 +4,7 @@ import User from "../models/User";
interface SerializedUser {
id: number;
name: string;
positionCompany: string;
email: string;
profile: string;
queues: Queue[];
@ -13,6 +14,7 @@ export const SerializeUser = (user: User): SerializedUser => {
return {
id: user.id,
name: user.name,
positionCompany: user.positionCompany,
email: user.email,
profile: user.profile,
queues: user.queues

View File

@ -41,7 +41,10 @@ class User extends Model<User> {
@Default(0)
@Column
tokenVersion: number;
@Column
positionCompany: string;
@Default("admin")
@Column
profile: string;
@ -51,7 +54,6 @@ class User extends Model<User> {
@UpdatedAt
updatedAt: Date;
@HasMany(() => Ticket)
tickets: Ticket[];

View File

@ -10,6 +10,7 @@ import Queue from "../../models/Queue";
interface SerializedUser {
id: number;
name: string;
positionCompany: string;
email: string;
profile: string;
queues: Queue[];

View File

@ -8,6 +8,7 @@ interface Request {
email: string;
password: string;
name: string;
positionCompany?: string;
queueIds?: number[];
profile?: string;
}
@ -15,6 +16,7 @@ interface Request {
interface Response {
email: string;
name: string;
positionCompany: string;
id: number;
profile: string;
}
@ -23,6 +25,7 @@ const CreateUserService = async ({
email,
password,
name,
positionCompany,
queueIds = [],
profile = "master"
}: Request): Promise<Response> => {
@ -70,6 +73,7 @@ const CreateUserService = async ({
email,
password,
name,
positionCompany,
profile
},
{ include: ["queues"] }

View File

@ -40,7 +40,7 @@ const ListUser = async ({ profile, userId, raw, userIds, profiles }: Request): P
const users = await User.findAll({
where: where_clause,
raw,
attributes: ["id", "name", "email"],
attributes: ["id", "name", "email", "positionCompany"],
include: [
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] }

View File

@ -63,7 +63,7 @@ const ListUsersService = async ({
const { count, rows: users } = await User.findAndCountAll({
where: whereCondition,
attributes: ["name", "id", "email", "profile", "createdAt"],
attributes: ["name", "id", "email","positionCompany", "profile", "createdAt"],
limit,
offset,
order: [["createdAt", "DESC"]],

View File

@ -1 +0,0 @@
REACT_APP_BACKEND_URL = http://localhost:8080/

View File

@ -84,6 +84,7 @@ const UserModal = ({ open, onClose, userId }) => {
name: "",
email: "",
password: "",
position: "",
profile: "user",
};
@ -220,10 +221,9 @@ const UserModal = ({ open, onClose, userId }) => {
fullWidth
/>
</div>
<div className={classes.multFieldLine}>
<Field
as={TextField}
label={i18n.t("userModal.form.email")}
label='Login'
name="email"
error={touched.email && Boolean(errors.email)}
helperText={touched.email && errors.email}
@ -231,6 +231,18 @@ const UserModal = ({ open, onClose, userId }) => {
margin="dense"
fullWidth
/>
<div className={classes.multFieldLine}>
<Field
as={TextField}
label="Cargo"
autoFocus
name="positionCompany"
error={touched.name && Boolean(errors.name)}
helperText={touched.name && errors.name}
variant="outlined"
margin="dense"
fullWidth
/>
<FormControl
variant="outlined"
className={classes.formControl}
@ -262,6 +274,7 @@ const UserModal = ({ open, onClose, userId }) => {
/>
</FormControl>
</div>
<Can
role={loggedInUser.profile}
perform="user-modal:editQueues"

View File

@ -221,6 +221,19 @@ let columnsData = [
{ title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' },
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }]
let columnsDataSuper = [
{ title: `${i18n.t("reports.listColumns.column1_1")}`, field: 'whatsapp.name' },
{ title: `${i18n.t("reports.listColumns.column1_2")}`, field: 'user.name' },
{ title: `${i18n.t("reports.listColumns.column0_3")}`, field: 'contact.name' },
{ title: `${i18n.t("reports.listColumns.column1_5")}`, field: 'queue.name' },
{ title: 'Status', field: 'status' },
{ title: `${i18n.t("reports.listColumns.column1_7")}`, field: 'createdAt' },
{ title: `${i18n.t("reports.listColumns.column1_8")}`, field: 'updatedAt' },
{ title: `${i18n.t("reports.listColumns.column1_9")}`, field: 'statusChatEnd' }
]
// function convertAndFormatDate(dateString) {
@ -245,7 +258,6 @@ let columnsData = [
const Report = () => {
const { user: userA } = useContext(AuthContext)
//--------
const [searchParam] = useState("")
@ -316,21 +328,28 @@ const Report = () => {
setLoading(true)
const fetchQueries = async () => {
try {
if (reportOption === '1') {
// const { data } = await api.get("/reports/", { params: { userId: userId ? userId : 0, startDate: convertAndFormatDate(startDate), endDate: convertAndFormatDate(endDate), pageNumber: pageNumberTickets }, })
const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets }, })
const { data } = await api.get("/reports/", { params: { userId, startDate, endDate, pageNumber: pageNumberTickets }, userQueues: userA.queues})
let ticketsQueue = data.tickets;
let userQueues = userA.queues;
let filterQueuesTickets = [];
if(userQueues.length > 1){
filterQueuesTickets = ticketsQueue.filter(ticket => userQueues.some(queue => queue.name === ticket.queue.name));
}else if(userQueues.length > 0) {
filterQueuesTickets = ticketsQueue.filter(ticket => ticket.queue.name === userQueues[0].name);
}
data.tickets = filterQueuesTickets;
dispatchQ({ type: "LOAD_QUERY", payload: data.tickets })
setHasMore(data.hasMore)
setTotalCountTickets(data.count)
setLoading(false)
}
else if (reportOption === '2') {
@ -684,7 +703,7 @@ const Report = () => {
<>
<MTable data={query}
columns={columnsData}
columns={userA.profile !== 'supervisor' ?columnsData:columnsDataSuper}
hasChild={true}
removeClickRow={false}

View File

@ -267,7 +267,9 @@ const Users = () => {
<TableCell align="center">
{i18n.t("users.table.profile")}
</TableCell>
<TableCell align="center">
Cargo
</TableCell>
<TableCell align="center">
{i18n.t("users.table.actions")}
</TableCell>
@ -281,6 +283,7 @@ const Users = () => {
<TableCell align="center">{user.name}</TableCell>
<TableCell align="center">{user.email}</TableCell>
<TableCell align="center">{user.profile}</TableCell>
<TableCell align="center">{user.positionCompany}</TableCell>
<TableCell align="center">