feat: add new column 'transferToOtherQueues' to the Users table
Details: - Created a new column named 'transferToOtherQueues' in the Users table. feat: add field 'can transfer to other queues' in the user modal Details: - Added a field in the user modal to indicate if the user can transfer tickets to other queues. feat: implement functionality to allow users with 'transferToOtherTickets' set to true to transfer tickets to other queues, even when disabled in OmniHit Details: - Implemented functionality to allow users with 'transferToOtherTickets' set to true to transfer tickets to other queues, even when this feature is disabled in OmniHit. feat: add options to enable or disable functionalities: notify on new ticket arrival, block audio and video messages, show average waiting time of tickets in waiting status Details: - Added options to enable or disable the following functionalities: notification on new ticket arrival, blocking of audio and video messages, and displaying average waiting time of tickets in waiting status. feat: add filter in the dashboard to show data only from the queues the user is linked to, and allow the user to view data from a selected queue only Details: - Added a filter in the dashboard to display data only from the queues the user is linked to, and to allow the user to view data from a selected queue only.feat-scaling-ticket-remote-creation
parent
cc12cafb99
commit
0e8fbd8400
|
@ -65,7 +65,7 @@ export const reportUserByDateStartDateEnd = async (
|
||||||
endDate,
|
endDate,
|
||||||
pageNumber,
|
pageNumber,
|
||||||
createdOrUpdated,
|
createdOrUpdated,
|
||||||
queueId
|
queueId,
|
||||||
});
|
});
|
||||||
|
|
||||||
const queues = await Queue.findAll({ attributes: ["id", "name"] });
|
const queues = await Queue.findAll({ attributes: ["id", "name"] });
|
||||||
|
@ -84,11 +84,12 @@ export const reportUserService = async (
|
||||||
) {
|
) {
|
||||||
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, userQueues} = req.query as IndexQuery;
|
||||||
|
|
||||||
// let usersProfile = await ListUserParamiterService({ profile: 'user' })
|
// let usersProfile = await ListUserParamiterService({ profile: 'user' })
|
||||||
let usersProfile = await ListUserParamiterService({
|
let usersProfile = await ListUserParamiterService({
|
||||||
profiles: ["user", "supervisor"],
|
profiles: ["user", "supervisor"],
|
||||||
|
userQueues: userQueues ? userQueues : undefined,
|
||||||
raw: true
|
raw: true
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -351,15 +352,18 @@ export const reportTicksCountByStatusChatEnds = async (
|
||||||
throw new AppError("ERR_NO_PERMISSION", 403);
|
throw new AppError("ERR_NO_PERMISSION", 403);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { startDate, endDate } = req.query as IndexQuery;
|
const { startDate, endDate, userQueues } = req.query as IndexQuery;
|
||||||
|
|
||||||
const dateToday = splitDateTime(
|
const dateToday = splitDateTime(
|
||||||
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
|
new Date(format(new Date(), "yyyy-MM-dd HH:mm:ss", { locale: ptBR }))
|
||||||
);
|
);
|
||||||
|
|
||||||
|
const queueIds = userQueues ? userQueues.map(queue => parseInt(queue)) : [];
|
||||||
|
|
||||||
const reportStatusChatEnd = await CountStatusChatEndService(
|
const reportStatusChatEnd = await CountStatusChatEndService(
|
||||||
startDate || dateToday.fullDate,
|
startDate || dateToday.fullDate,
|
||||||
endDate || dateToday.fullDate
|
endDate || dateToday.fullDate,
|
||||||
|
queueIds
|
||||||
);
|
);
|
||||||
|
|
||||||
return res.status(200).json({ reportStatusChatEnd });
|
return res.status(200).json({ reportStatusChatEnd });
|
||||||
|
|
|
@ -362,9 +362,8 @@ export const show = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
export const count = async (req: Request, res: Response): Promise<Response> => {
|
export const count = async (req: Request, res: Response): Promise<Response> => {
|
||||||
// type indexQ = { status: string; date?: string; };
|
// type indexQ = { status: string; date?: string; };
|
||||||
const { status, date } = req.query as IndexQuery;
|
const { status, date, queueIds } = req.query as IndexQuery;
|
||||||
|
const ticketCount = await CountTicketService(status, date, queueIds);
|
||||||
const ticketCount = await CountTicketService(status, date);
|
|
||||||
|
|
||||||
return res.status(200).json(ticketCount);
|
return res.status(200).json(ticketCount);
|
||||||
};
|
};
|
||||||
|
|
|
@ -100,7 +100,7 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
// };
|
// };
|
||||||
|
|
||||||
export const all = async (req: Request, res: Response): Promise<Response> => {
|
export const all = async (req: Request, res: Response): Promise<Response> => {
|
||||||
let { userId, profile }: any = req.query as IndexQuery;
|
let { userId, profile, transferToOtherQueues }: any = req.query as IndexQuery;
|
||||||
|
|
||||||
console.log(
|
console.log(
|
||||||
"userId: ",
|
"userId: ",
|
||||||
|
@ -111,7 +111,7 @@ export const all = async (req: Request, res: Response): Promise<Response> => {
|
||||||
getSettingValue("queueTransferByWhatsappScope")?.value
|
getSettingValue("queueTransferByWhatsappScope")?.value
|
||||||
);
|
);
|
||||||
|
|
||||||
if (getSettingValue("queueTransferByWhatsappScope")?.value == "enabled") {
|
if (getSettingValue("queueTransferByWhatsappScope")?.value == "enabled" && !transferToOtherQueues) {
|
||||||
if (!userId) return res.json({ users: [], queues: [] });
|
if (!userId) return res.json({ users: [], queues: [] });
|
||||||
|
|
||||||
const obj = await ListUserByWhatsappQueuesService(
|
const obj = await ListUserByWhatsappQueuesService(
|
||||||
|
@ -145,7 +145,8 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
profile,
|
profile,
|
||||||
positionCompany,
|
positionCompany,
|
||||||
positionId,
|
positionId,
|
||||||
queueIds
|
queueIds,
|
||||||
|
transferToOtherQueues
|
||||||
} = req.body;
|
} = req.body;
|
||||||
|
|
||||||
console.log("===========> req.url: ", req.url);
|
console.log("===========> req.url: ", req.url);
|
||||||
|
@ -172,7 +173,8 @@ export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
positionCompany,
|
positionCompany,
|
||||||
positionId,
|
positionId,
|
||||||
profile,
|
profile,
|
||||||
queueIds
|
queueIds,
|
||||||
|
transferToOtherQueues
|
||||||
});
|
});
|
||||||
|
|
||||||
if (user) {
|
if (user) {
|
||||||
|
|
|
@ -0,0 +1,15 @@
|
||||||
|
import { QueryInterface, DataTypes } from "sequelize";
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
up: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.addColumn("Users", "transferToOtherQueues", {
|
||||||
|
type: DataTypes.BOOLEAN,
|
||||||
|
allowNull: false,
|
||||||
|
defaultValue: false
|
||||||
|
});
|
||||||
|
},
|
||||||
|
|
||||||
|
down: (queryInterface: QueryInterface) => {
|
||||||
|
return queryInterface.removeColumn("Users", "transferToOtherQueues");
|
||||||
|
}
|
||||||
|
};
|
|
@ -51,6 +51,9 @@ class User extends Model<User> {
|
||||||
@Column
|
@Column
|
||||||
secondaryId: string;
|
secondaryId: string;
|
||||||
|
|
||||||
|
@Column
|
||||||
|
transferToOtherQueues: boolean;
|
||||||
|
|
||||||
@Default("admin")
|
@Default("admin")
|
||||||
@Column
|
@Column
|
||||||
profile: string;
|
profile: string;
|
||||||
|
|
|
@ -11,13 +11,14 @@ const { QueryTypes } = require("sequelize");
|
||||||
|
|
||||||
const CountStatusChatEndService = async (
|
const CountStatusChatEndService = async (
|
||||||
startDate: string,
|
startDate: string,
|
||||||
endDate: string
|
endDate: string,
|
||||||
|
queueIds?: number[]
|
||||||
) => {
|
) => {
|
||||||
|
|
||||||
const countStatusChatEnd: any = await sequelize.query(
|
const countStatusChatEnd: any = await sequelize.query(
|
||||||
`select t.id, s.name, count(t.id) as count from Tickets t join StatusChatEnds s on
|
`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'
|
t.statusChatEndId = s.id and DATE(t.createdAt) BETWEEN '${startDate} 00:00:00.000000' AND '${endDate} 23:59:59.999999'
|
||||||
group by s.id;`,
|
AND t.queueId IN (${queueIds})
|
||||||
|
group by s.id;`,
|
||||||
{ type: QueryTypes.SELECT }
|
{ type: QueryTypes.SELECT }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
|
@ -9,7 +9,7 @@ import ptBR from 'date-fns/locale/pt-BR';
|
||||||
import { splitDateTime } from "../../helpers/SplitDateTime";
|
import { splitDateTime } from "../../helpers/SplitDateTime";
|
||||||
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
|
const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR })))
|
||||||
|
|
||||||
const CountTicketService = async (status: string, date?: string): Promise<any> => {
|
const CountTicketService = async (status: string, date?: string, queueIds?: string): Promise<any> => {
|
||||||
|
|
||||||
let where_clause = {}
|
let where_clause = {}
|
||||||
|
|
||||||
|
@ -30,8 +30,8 @@ const CountTicketService = async (status: string, date?: string): Promise<any> =
|
||||||
// }
|
// }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(queueIds) where_clause = { ...where_clause, status: status, queueId: { [Op.or]: [queueIds, null] } };
|
||||||
where_clause = { ...where_clause, status: status }
|
else where_clause = { ...where_clause, status: status};
|
||||||
|
|
||||||
const ticket = await Ticket.findAll({
|
const ticket = await Ticket.findAll({
|
||||||
where: where_clause,
|
where: where_clause,
|
||||||
|
|
|
@ -204,7 +204,8 @@ const ListTicketsService = async ({
|
||||||
whereCondition = {
|
whereCondition = {
|
||||||
createdAt: {
|
createdAt: {
|
||||||
[Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))]
|
[Op.between]: [+startOfDay(parseISO(date)), +endOfDay(parseISO(date))]
|
||||||
}
|
},
|
||||||
|
queueId: { [Op.or]: [queueIds, null] },
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -14,6 +14,7 @@ interface Request {
|
||||||
queueIds?: number[];
|
queueIds?: number[];
|
||||||
profile?: string;
|
profile?: string;
|
||||||
ignoreThrow?: boolean;
|
ignoreThrow?: boolean;
|
||||||
|
transferToOtherQueues?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Response {
|
interface Response {
|
||||||
|
@ -23,6 +24,7 @@ interface Response {
|
||||||
positionId: string;
|
positionId: string;
|
||||||
id: number;
|
id: number;
|
||||||
profile: string;
|
profile: string;
|
||||||
|
transferToOtherQueues: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateUserService = async ({
|
const CreateUserService = async ({
|
||||||
|
@ -33,7 +35,8 @@ const CreateUserService = async ({
|
||||||
positionId,
|
positionId,
|
||||||
queueIds = [],
|
queueIds = [],
|
||||||
profile = "master",
|
profile = "master",
|
||||||
ignoreThrow = false
|
ignoreThrow = false,
|
||||||
|
transferToOtherQueues
|
||||||
}: Request): Promise<Response | any> => {
|
}: Request): Promise<Response | any> => {
|
||||||
try {
|
try {
|
||||||
const schema = Yup.object().shape({
|
const schema = Yup.object().shape({
|
||||||
|
@ -84,7 +87,8 @@ const CreateUserService = async ({
|
||||||
name,
|
name,
|
||||||
positionCompany,
|
positionCompany,
|
||||||
positionId: !positionId ? null : positionId,
|
positionId: !positionId ? null : positionId,
|
||||||
profile
|
profile,
|
||||||
|
transferToOtherQueues: transferToOtherQueues? transferToOtherQueues : false
|
||||||
},
|
},
|
||||||
{ include: ["queues"] }
|
{ include: ["queues"] }
|
||||||
);
|
);
|
||||||
|
|
|
@ -10,6 +10,7 @@ interface Request {
|
||||||
profiles?: Array<string>;
|
profiles?: Array<string>;
|
||||||
raw?: boolean;
|
raw?: boolean;
|
||||||
userIds?: string | number;
|
userIds?: string | number;
|
||||||
|
userQueues?: string[];
|
||||||
}
|
}
|
||||||
|
|
||||||
const ListUser = async ({
|
const ListUser = async ({
|
||||||
|
@ -17,10 +18,31 @@ const ListUser = async ({
|
||||||
userId,
|
userId,
|
||||||
raw,
|
raw,
|
||||||
userIds,
|
userIds,
|
||||||
profiles
|
profiles,
|
||||||
|
userQueues: userQueuesToNumber
|
||||||
}: Request): Promise<User[]> => {
|
}: Request): Promise<User[]> => {
|
||||||
let where_clause = {};
|
let where_clause = {};
|
||||||
|
let userIdInQueues: number[] = [];
|
||||||
|
|
||||||
|
if(userQueuesToNumber !== undefined){
|
||||||
|
let userQueues = userQueuesToNumber.map(id => parseInt(id));
|
||||||
|
const userQueuesFiltered = await UserQueue.findAll({
|
||||||
|
where: { queueId: { [Op.or]: [userQueues, null] } },
|
||||||
|
order: [
|
||||||
|
['userId', 'ASC']
|
||||||
|
],
|
||||||
|
raw: true
|
||||||
|
});
|
||||||
|
if(userQueuesFiltered) for(let queueId of userQueues){
|
||||||
|
for(let userQueue of userQueuesFiltered){
|
||||||
|
if(queueId == userQueue.queueId){
|
||||||
|
const isAlready = userIdInQueues.indexOf(userQueue.userId);
|
||||||
|
|
||||||
|
if(isAlready === -1) userIdInQueues.push(userQueue.userId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
if (userId && profile) {
|
if (userId && profile) {
|
||||||
where_clause = {
|
where_clause = {
|
||||||
[Op.and]: [{ userId: userId }, { profile: profile }]
|
[Op.and]: [{ userId: userId }, { profile: profile }]
|
||||||
|
@ -39,14 +61,15 @@ const ListUser = async ({
|
||||||
};
|
};
|
||||||
} else if (profiles) {
|
} else if (profiles) {
|
||||||
where_clause = {
|
where_clause = {
|
||||||
profile: { [Op.in]: profiles }
|
profile: { [Op.in]: profiles },
|
||||||
|
id: {[Op.in]: userIdInQueues}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const users = await User.findAll({
|
const users = await User.findAll({
|
||||||
where: where_clause,
|
where: where_clause,
|
||||||
raw,
|
raw,
|
||||||
attributes: ["id", "name", "email", "positionCompany"],
|
attributes: ["id", "name", "email", "positionCompany", "transferToOtherQueues"],
|
||||||
|
|
||||||
include: [
|
include: [
|
||||||
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] }
|
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] }
|
||||||
|
|
|
@ -66,7 +66,8 @@ const ListUsersService = async ({
|
||||||
"email",
|
"email",
|
||||||
"positionCompany",
|
"positionCompany",
|
||||||
"profile",
|
"profile",
|
||||||
"createdAt"
|
"createdAt",
|
||||||
|
"transferToOtherQueues"
|
||||||
],
|
],
|
||||||
limit,
|
limit,
|
||||||
offset,
|
offset,
|
||||||
|
|
|
@ -12,7 +12,8 @@ const ShowUserService = async (id: string | number): Promise<User> => {
|
||||||
"profile",
|
"profile",
|
||||||
"positionCompany",
|
"positionCompany",
|
||||||
"positionId",
|
"positionId",
|
||||||
"tokenVersion"
|
"tokenVersion",
|
||||||
|
"transferToOtherQueues"
|
||||||
],
|
],
|
||||||
include: [
|
include: [
|
||||||
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] },
|
{ model: Queue, as: "queues", attributes: ["id", "name", "color"] },
|
||||||
|
|
|
@ -12,6 +12,7 @@ interface UserData {
|
||||||
positionId?: string;
|
positionId?: string;
|
||||||
profile?: string;
|
profile?: string;
|
||||||
queueIds?: number[];
|
queueIds?: number[];
|
||||||
|
transferToOtherQueues: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
interface Request {
|
interface Request {
|
||||||
|
@ -75,7 +76,8 @@ const UpdateUserService = async ({
|
||||||
name,
|
name,
|
||||||
positionCompany,
|
positionCompany,
|
||||||
positionId,
|
positionId,
|
||||||
queueIds = []
|
queueIds = [],
|
||||||
|
transferToOtherQueues
|
||||||
} = userData;
|
} = userData;
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -90,7 +92,8 @@ const UpdateUserService = async ({
|
||||||
profile,
|
profile,
|
||||||
positionCompany,
|
positionCompany,
|
||||||
positionId: !positionId ? null : positionId,
|
positionId: !positionId ? null : positionId,
|
||||||
name
|
name,
|
||||||
|
transferToOtherQueues
|
||||||
});
|
});
|
||||||
|
|
||||||
await user.$set("queues", queueIds);
|
await user.$set("queues", queueIds);
|
||||||
|
@ -117,7 +120,8 @@ const UpdateUserService = async ({
|
||||||
profile: _user.profile,
|
profile: _user.profile,
|
||||||
queues: _user.queues,
|
queues: _user.queues,
|
||||||
positionId: _user?.positionId,
|
positionId: _user?.positionId,
|
||||||
position: _user.position
|
position: _user.position,
|
||||||
|
transferToOtherQueues: _user.transferToOtherQueues
|
||||||
};
|
};
|
||||||
|
|
||||||
return serializedUser;
|
return serializedUser;
|
||||||
|
|
|
@ -10,11 +10,19 @@ const SelectTextFields = (props) => {
|
||||||
if (!props.textBoxFieldSelected) {
|
if (!props.textBoxFieldSelected) {
|
||||||
props.currencies.push({ 'value': 0, 'label': '' })
|
props.currencies.push({ 'value': 0, 'label': '' })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(props.textBoxFieldSelected === 'All'){
|
||||||
|
const already = props.currencies.findIndex(obj => obj.value === 'All');
|
||||||
|
if (already === -1) {
|
||||||
|
props.currencies.push({ 'value': 'All', 'label': 'All' })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
props.func(currency)
|
props.func(currency)
|
||||||
|
|
||||||
}, [currency, props])
|
}, [currency, props.textBoxFieldSelected])
|
||||||
|
|
||||||
const handleChange = (event) => {
|
const handleChange = (event) => {
|
||||||
|
|
||||||
|
|
|
@ -19,6 +19,7 @@ import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||||
|
|
||||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
import Switch from "@material-ui/core/Switch";
|
import Switch from "@material-ui/core/Switch";
|
||||||
|
import openSocket from "socket.io-client"
|
||||||
|
|
||||||
import NewTicketModal from "../NewTicketModal";
|
import NewTicketModal from "../NewTicketModal";
|
||||||
import TicketsList from "../TicketsList";
|
import TicketsList from "../TicketsList";
|
||||||
|
@ -219,12 +220,18 @@ const TicketsManager = () => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if(settings?.length > 0 && getSettingValue('waitingTimeTickets') === 'enabled') {
|
if(settings?.length > 0 && getSettingValue('waitingTimeTickets') === 'enabled') {
|
||||||
fetchTickets();
|
fetchTickets();
|
||||||
|
const intervalId = setInterval(fetchTickets, 55000);
|
||||||
|
const socket = openSocket(process.env.REACT_APP_BACKEND_URL)
|
||||||
|
|
||||||
const intervalId = setInterval(fetchTickets, 7000);
|
socket.on("ticketStatus", (data) => {
|
||||||
|
if (data.action === "update") {
|
||||||
return () => {
|
fetchTickets();
|
||||||
clearInterval(intervalId);
|
}
|
||||||
};
|
})
|
||||||
|
return () => {
|
||||||
|
socket.disconnect()
|
||||||
|
clearInterval(intervalId)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}, [selectedQueueIds, settings]);
|
}, [selectedQueueIds, settings]);
|
||||||
|
|
||||||
|
|
|
@ -97,7 +97,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
|
||||||
if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled') {
|
if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled' && !user.transferToOtherQueues) {
|
||||||
setQueues(_queues)
|
setQueues(_queues)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
|
@ -190,7 +190,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
|
||||||
try {
|
try {
|
||||||
|
|
||||||
|
|
||||||
if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled') {
|
if (settings?.find(e => e?.key === 'queueTransferByWhatsappScope')?.value === 'enabled' && !user.transferToOtherQueues) {
|
||||||
const { data } = await api.get(`/users/all`, {
|
const { data } = await api.get(`/users/all`, {
|
||||||
params: { userId: user.id },
|
params: { userId: user.id },
|
||||||
})
|
})
|
||||||
|
@ -202,7 +202,7 @@ const TransferTicketModal = ({ modalOpen, onClose, ticketid }) => {
|
||||||
else {
|
else {
|
||||||
|
|
||||||
const { data } = await api.get(`/users/all`, {
|
const { data } = await api.get(`/users/all`, {
|
||||||
params: { profile: 'user' },
|
params: { profile: 'user', transferToOtherQueues: user.transferToOtherQueues },
|
||||||
})
|
})
|
||||||
|
|
||||||
setUsers(data.users)
|
setUsers(data.users)
|
||||||
|
|
|
@ -32,6 +32,7 @@ import toastError from "../../errors/toastError"
|
||||||
import QueueSelect from "../QueueSelect"
|
import QueueSelect from "../QueueSelect"
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext"
|
import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
import { Can } from "../Can"
|
import { Can } from "../Can"
|
||||||
|
import Switch from '@mui/material/Switch'
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
root: {
|
root: {
|
||||||
|
@ -95,6 +96,7 @@ const UserModal = ({ open, onClose, userId, }) => {
|
||||||
const [showPassword, setShowPassword] = useState(false)
|
const [showPassword, setShowPassword] = useState(false)
|
||||||
const [positions, setPositions] = useState([])
|
const [positions, setPositions] = useState([])
|
||||||
const [selectedPosition, setSelectedPosition] = useState('')
|
const [selectedPosition, setSelectedPosition] = useState('')
|
||||||
|
const [checked, setChecked] = useState(false)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
const fetchUser = async () => {
|
const fetchUser = async () => {
|
||||||
|
@ -112,6 +114,9 @@ const UserModal = ({ open, onClose, userId, }) => {
|
||||||
setSelectedPosition(data.positionId)
|
setSelectedPosition(data.positionId)
|
||||||
else
|
else
|
||||||
setSelectedPosition('')
|
setSelectedPosition('')
|
||||||
|
|
||||||
|
|
||||||
|
if(data.transferToOtherQueues) setChecked(data.transferToOtherQueues);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err)
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
@ -136,10 +141,15 @@ const UserModal = ({ open, onClose, userId, }) => {
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
onClose()
|
onClose()
|
||||||
setUser(initialState)
|
setUser(initialState)
|
||||||
|
setChecked(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChange = (event) => {
|
||||||
|
setChecked(event.target.checked)
|
||||||
}
|
}
|
||||||
|
|
||||||
const handleSaveUser = async values => {
|
const handleSaveUser = async values => {
|
||||||
const userData = { ...values, queueIds: selectedQueueIds, positionId: selectedPosition }
|
const userData = { ...values, queueIds: selectedQueueIds, positionId: selectedPosition, transferToOtherQueues: checked}
|
||||||
try {
|
try {
|
||||||
if (userId) {
|
if (userId) {
|
||||||
|
|
||||||
|
@ -252,7 +262,7 @@ const UserModal = ({ open, onClose, userId, }) => {
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/>
|
||||||
<div className={classes.multFieldLine}>
|
<div className={classes.multFieldLine}>
|
||||||
<Field
|
{/* <Field
|
||||||
as={TextField}
|
as={TextField}
|
||||||
label="Cargo"
|
label="Cargo"
|
||||||
name="positionCompany"
|
name="positionCompany"
|
||||||
|
@ -261,7 +271,17 @@ const UserModal = ({ open, onClose, userId, }) => {
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
margin="dense"
|
margin="dense"
|
||||||
fullWidth
|
fullWidth
|
||||||
/>
|
/> */}
|
||||||
|
<label style={{display: 'flex', alignItems:'center'}}>
|
||||||
|
Transferir para outras filas
|
||||||
|
<Switch
|
||||||
|
name= 'transferToOtherQueues'
|
||||||
|
checked={checked}
|
||||||
|
onChange={handleChange}
|
||||||
|
inputProps={{ 'aria-label': 'controlled' }}
|
||||||
|
/>
|
||||||
|
</label>
|
||||||
|
|
||||||
<FormControl
|
<FormControl
|
||||||
variant="outlined"
|
variant="outlined"
|
||||||
className={classes.formControl}
|
className={classes.formControl}
|
||||||
|
|
|
@ -20,9 +20,10 @@ const Chart = (props) => {
|
||||||
const theme = useTheme();
|
const theme = useTheme();
|
||||||
|
|
||||||
const date = useRef(new Date().toISOString());
|
const date = useRef(new Date().toISOString());
|
||||||
let { tickets } = useTickets({ date: date.current, unlimited: "current" });
|
const queueIds = JSON.stringify( props.selectedQueue) || {};
|
||||||
|
let {tickets} = useTickets({ date: date.current, unlimited: "current", queueIds });
|
||||||
|
|
||||||
const [chartData, setChartData] = useState([
|
const modelChar = [
|
||||||
{ time: "08:00", amount: 0 },
|
{ time: "08:00", amount: 0 },
|
||||||
{ time: "09:00", amount: 0 },
|
{ time: "09:00", amount: 0 },
|
||||||
{ time: "10:00", amount: 0 },
|
{ time: "10:00", amount: 0 },
|
||||||
|
@ -35,11 +36,12 @@ const Chart = (props) => {
|
||||||
{ time: "17:00", amount: 0 },
|
{ time: "17:00", amount: 0 },
|
||||||
{ time: "18:00", amount: 0 },
|
{ time: "18:00", amount: 0 },
|
||||||
{ time: "19:00", amount: 0 },
|
{ time: "19:00", amount: 0 },
|
||||||
]);
|
]
|
||||||
|
const [chartData, setChartData] = useState(modelChar);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setChartData(prevState => {
|
setChartData(prevState => {
|
||||||
let aux = [...prevState];
|
let aux = modelChar;
|
||||||
|
|
||||||
aux.forEach(a => {
|
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++; });
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
import React, { useContext, useReducer, useEffect, useState } from "react"
|
import React, { useContext, useReducer, useEffect, useState, useCallback } from "react"
|
||||||
|
|
||||||
import { addHours, addMinutes, addSeconds, intervalToDuration } from "date-fns"
|
import { addHours, addMinutes, addSeconds, intervalToDuration } from "date-fns"
|
||||||
|
|
||||||
|
@ -11,6 +11,7 @@ import Tooltip from "@mui/material/Tooltip"
|
||||||
import Zoom from "@mui/material/Zoom"
|
import Zoom from "@mui/material/Zoom"
|
||||||
import IconButton from "@mui/material/IconButton"
|
import IconButton from "@mui/material/IconButton"
|
||||||
import Info from "@material-ui/icons/Info"
|
import Info from "@material-ui/icons/Info"
|
||||||
|
import SelectField from "../../components/Report/SelectField"
|
||||||
|
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext"
|
import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
// import { i18n } from "../../translate/i18n";
|
// import { i18n } from "../../translate/i18n";
|
||||||
|
@ -254,12 +255,15 @@ const reducer = (state, action) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
const Dashboard = () => {
|
const Dashboard = () => {
|
||||||
|
const { user } = useContext(AuthContext)
|
||||||
const classes = useStyles()
|
const classes = useStyles()
|
||||||
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 [ticketStatusChatEnd, setTicketStatusChatEnd] = useState([])
|
||||||
const { user } = useContext(AuthContext)
|
|
||||||
|
const userQueueIds = user.queues.map((q) => q.id);
|
||||||
|
const [selectedQueue, setSelectedQueue] = useState(userQueueIds || []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
dispatch({ type: "RESET" })
|
dispatch({ type: "RESET" })
|
||||||
|
@ -286,14 +290,14 @@ const Dashboard = () => {
|
||||||
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, userQueues: selectedQueue },
|
||||||
})
|
})
|
||||||
|
|
||||||
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", {
|
const { data: ticketStatusChatEndData } = await api.get("/reports/count/statusChatEnd", {
|
||||||
params: { startDate: dateToday, endDate: dateToday },
|
params: { startDate: dateToday, endDate: dateToday, userQueues: selectedQueue },
|
||||||
})
|
})
|
||||||
|
|
||||||
setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd)
|
setTicketStatusChatEnd(ticketStatusChatEndData.reportStatusChatEnd)
|
||||||
|
@ -306,7 +310,7 @@ const Dashboard = () => {
|
||||||
fetchQueries()
|
fetchQueries()
|
||||||
}, 500)
|
}, 500)
|
||||||
return () => clearTimeout(delayDebounceFn)
|
return () => clearTimeout(delayDebounceFn)
|
||||||
}, [])
|
}, [selectedQueue])
|
||||||
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
@ -381,6 +385,18 @@ const Dashboard = () => {
|
||||||
}
|
}
|
||||||
}, [])
|
}, [])
|
||||||
|
|
||||||
|
const handleSelectedQueue = useCallback((queueSelected) => {
|
||||||
|
if(queueSelected !== 'All'){
|
||||||
|
const queueIndex = user?.queues?.findIndex((q) => q.id === parseInt(queueSelected));
|
||||||
|
const queueIds = []
|
||||||
|
queueIds.push(user?.queues[queueIndex]?.id);
|
||||||
|
setSelectedQueue(queueIds);
|
||||||
|
}else{
|
||||||
|
const queueIds = user?.queues?.map((queue) => queue.id);
|
||||||
|
setSelectedQueue(queueIds);
|
||||||
|
}
|
||||||
|
},[user, setSelectedQueue])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (ticketStatusChange === "") return
|
if (ticketStatusChange === "") return
|
||||||
const delayDebounceFn = setTimeout(() => {
|
const delayDebounceFn = setTimeout(() => {
|
||||||
|
@ -390,17 +406,17 @@ const Dashboard = () => {
|
||||||
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
|
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
|
||||||
|
|
||||||
const _open = await api.get("/tickets/count", {
|
const _open = await api.get("/tickets/count", {
|
||||||
params: { status: "open", date: dateToday },
|
params: { status: "open", date: dateToday, queueIds: selectedQueue },
|
||||||
})
|
})
|
||||||
const _closed = await api.get("/tickets/count", {
|
const _closed = await api.get("/tickets/count", {
|
||||||
params: { status: "closed", date: dateToday },
|
params: { status: "closed", date: dateToday, queueIds: selectedQueue },
|
||||||
})
|
})
|
||||||
const _pending = await api.get("/tickets/count", {
|
const _pending = await api.get("/tickets/count", {
|
||||||
params: { status: "pending" },
|
params: { status: "pending", queueIds: selectedQueue },
|
||||||
})
|
})
|
||||||
|
|
||||||
const _openAll = await api.get("/tickets/count", {
|
const _openAll = await api.get("/tickets/count", {
|
||||||
params: { status: "open" },
|
params: { status: "open", queueIds: selectedQueue },
|
||||||
})
|
})
|
||||||
setTicktsStatus({
|
setTicktsStatus({
|
||||||
open: _open.data.count,
|
open: _open.data.count,
|
||||||
|
@ -419,7 +435,7 @@ const Dashboard = () => {
|
||||||
fetchQueries()
|
fetchQueries()
|
||||||
}, 500)
|
}, 500)
|
||||||
return () => clearTimeout(delayDebounceFn)
|
return () => clearTimeout(delayDebounceFn)
|
||||||
}, [ticketStatusChange])
|
}, [ticketStatusChange, selectedQueue])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Can
|
<Can
|
||||||
|
@ -451,6 +467,16 @@ const Dashboard = () => {
|
||||||
</Tooltip>
|
</Tooltip>
|
||||||
</Typography>
|
</Typography>
|
||||||
</Grid>
|
</Grid>
|
||||||
|
<Grid style={{ display: 'flex', flexDirection: 'column', padding: '10px 0', alignItems: 'start' }}>
|
||||||
|
<SelectField
|
||||||
|
func={handleSelectedQueue}
|
||||||
|
textBoxFieldSelected={'All'}
|
||||||
|
emptyField={false}
|
||||||
|
header={'Filas'}
|
||||||
|
currencies={user.queues.map((obj) => {
|
||||||
|
return { 'value': obj.id, 'label': obj.name }
|
||||||
|
})} />
|
||||||
|
</Grid>
|
||||||
<Grid container spacing={3}>
|
<Grid container spacing={3}>
|
||||||
<Grid item xs={12} sm={6} md={6} lg={4}>
|
<Grid item xs={12} sm={6} md={6} lg={4}>
|
||||||
<Paper
|
<Paper
|
||||||
|
@ -506,7 +532,7 @@ const Dashboard = () => {
|
||||||
<Grid item container spacing={3}>
|
<Grid item container spacing={3}>
|
||||||
<Grid item xs={12} sm={12} md={6} lg={6}>
|
<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} selectedQueue = {selectedQueue}/>
|
||||||
</Paper>
|
</Paper>
|
||||||
</Grid>
|
</Grid>
|
||||||
<Grid item xs={12} sm={12} md={6} lg={6}>
|
<Grid item xs={12} sm={12} md={6} lg={6}>
|
||||||
|
|
|
@ -446,6 +446,88 @@ const Settings = () => {
|
||||||
</Container>
|
</Container>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Container className={classes.container} maxWidth="sm">
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Noficar quando entrar novo ticket na fila
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
margin="dense"
|
||||||
|
variant="outlined"
|
||||||
|
native
|
||||||
|
id="notificationTransferQueue-setting"
|
||||||
|
name="notificationTransferQueue"
|
||||||
|
value={
|
||||||
|
settings &&
|
||||||
|
settings.length > 0 &&
|
||||||
|
getSettingValue('notificationTransferQueue')
|
||||||
|
}
|
||||||
|
className={classes.settingOption}
|
||||||
|
onChange={handleChangeSetting}
|
||||||
|
>
|
||||||
|
<option value="enabled">Ativado</option>
|
||||||
|
<option value="disabled">Desativado</option>
|
||||||
|
</Select>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Container className={classes.container} maxWidth="sm">
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Bloquear mídias de Audio e Video
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
margin="dense"
|
||||||
|
variant="outlined"
|
||||||
|
native
|
||||||
|
id="blockAudioVideoMedia-setting"
|
||||||
|
name="blockAudioVideoMedia"
|
||||||
|
value={
|
||||||
|
settings &&
|
||||||
|
settings.length > 0 &&
|
||||||
|
getSettingValue('blockAudioVideoMedia')
|
||||||
|
}
|
||||||
|
className={classes.settingOption}
|
||||||
|
onChange={handleChangeSetting}
|
||||||
|
>
|
||||||
|
<option value="enabled">Ativado</option>
|
||||||
|
<option value="disabled">Desativado</option>
|
||||||
|
</Select>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className={classes.root}>
|
||||||
|
<Container className={classes.container} maxWidth="sm">
|
||||||
|
<Paper className={classes.paper}>
|
||||||
|
<Typography variant="body1">
|
||||||
|
Mostrar tempo de espera dos tickets aguardando
|
||||||
|
</Typography>
|
||||||
|
|
||||||
|
<Select
|
||||||
|
margin="dense"
|
||||||
|
variant="outlined"
|
||||||
|
native
|
||||||
|
id="waitingTimeTickets-setting"
|
||||||
|
name="waitingTimeTickets"
|
||||||
|
value={
|
||||||
|
settings &&
|
||||||
|
settings.length > 0 &&
|
||||||
|
getSettingValue('waitingTimeTickets')
|
||||||
|
}
|
||||||
|
className={classes.settingOption}
|
||||||
|
onChange={handleChangeSetting}
|
||||||
|
>
|
||||||
|
<option value="enabled">Ativado</option>
|
||||||
|
<option value="disabled">Desativado</option>
|
||||||
|
</Select>
|
||||||
|
</Paper>
|
||||||
|
</Container>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
/>
|
/>
|
||||||
|
|
Loading…
Reference in New Issue