From 53d3b2b09e9c14279f95e93c729447053a44ae93 Mon Sep 17 00:00:00 2001 From: adriano Date: Fri, 2 Jun 2023 15:45:14 -0300 Subject: [PATCH] =?UTF-8?q?Altera=C3=A7=C3=B5es=20para=20atribuir=20chats?= =?UTF-8?q?=20iniciados=20pelo=20atendente=20a=20mesma=20fila=20que=20ele?= =?UTF-8?q?=20esta=20atribuido.=20Altera=C3=A7=C3=A3o=20para=20remover=20c?= =?UTF-8?q?aracteres=20especias=20ao=20inserir=20o=20numero=20do=20whatsap?= =?UTF-8?q?p.=20Cria=C3=A7=C3=A3o=20do=20nivel=20supervisor=20para=20geren?= =?UTF-8?q?ciar=20filas?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- TEST_SERVER1/api/db_conn.js | 2 +- backend/src/controllers/UserController.ts | 50 ++++++++++++++- .../helpers/whatsappQueueMatchingUserQueue.ts | 26 ++++++++ .../TicketServices/CreateTicketService.ts | 20 +++++- .../WbotServices/wbotMessageListener.ts | 2 +- frontend/src/components/ContactModal/index.js | 55 +++++++++++++++- .../src/components/NewTicketModal/index.js | 6 +- frontend/src/components/UserModal/index.js | 64 ++++++++++++++----- frontend/src/layout/MainListItems.js | 21 +++++- frontend/src/rules.js | 16 +++-- frontend/src/translate/languages/pt.js | 2 +- 11 files changed, 233 insertions(+), 31 deletions(-) create mode 100644 backend/src/helpers/whatsappQueueMatchingUserQueue.ts diff --git a/TEST_SERVER1/api/db_conn.js b/TEST_SERVER1/api/db_conn.js index 93c45dd..9d26734 100644 --- a/TEST_SERVER1/api/db_conn.js +++ b/TEST_SERVER1/api/db_conn.js @@ -3,7 +3,7 @@ const db = [ { client_url: "http://localhost:8080", db_conf: { - DB: "whaticket", + DB: "whaticket_recrutamento", DB_HOST: "localhost", DB_USER: "whaticket", DB_PASS: "strongpassword", diff --git a/backend/src/controllers/UserController.ts b/backend/src/controllers/UserController.ts index c41b912..0b53371 100644 --- a/backend/src/controllers/UserController.ts +++ b/backend/src/controllers/UserController.ts @@ -15,6 +15,10 @@ import User from "../models/User"; import { startWhoIsOnlineMonitor, stopWhoIsOnlineMonitor } from "../helpers/WhoIsOnlineMonitor" import UserOnlineTIme from '../models/UserOnlineTime' +import CountTicketsByUserQueue from "../services/UserServices/CountTicketsByUserQueue"; +import { splitDateTime } from "../helpers/SplitDateTime"; +import { format } from "date-fns"; +import { ptBR } from "date-fns/locale"; type IndexQuery = { searchParam: string; @@ -37,6 +41,9 @@ export const index = async (req: Request, res: Response): Promise => { for (var user of users) { if (user.profile !== 'master') { + + if (req.user.profile == 'supervisor' && (user.profile == 'admin')) continue + auxUsers.push(user) } } @@ -112,7 +119,7 @@ export const show = async (req: Request, res: Response): Promise => { export const logoutUser = async (req: Request, res: Response): Promise => { const { userId } = req.params; - + await stopWhoIsOnlineMonitor() let onlineTime = { @@ -137,14 +144,49 @@ export const update = async ( req: Request, res: Response ): Promise => { - if (req.user.profile !== "admin" && req.user.profile !== "master") { + if (req.user.profile !== "admin" && req.user.profile !== "master" && req.user.profile !== "supervisor") { throw new AppError("ERR_NO_PERMISSION", 403); } const { userId } = req.params; const userData = req.body; - const user = await UpdateUserService({ userData, userId }); + + + const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) + + const openByUserOnQueue: any[] = await CountTicketsByUserQueue({ startDate: dateToday.fullDate, endDate: dateToday.fullDate, status: 'open', clientChatStart: true, userId: userId }) + + // console.log('------> openByUserOnQueue: ', openByUserOnQueue) + // console.log() + // console.log('------> 1 userData.queueIds: ', userData.queueIds) + + let userQueuesAttendance = [] + + if ((openByUserOnQueue && openByUserOnQueue.length) > 0) { + + userQueuesAttendance = openByUserOnQueue.filter((e: any) => !userData.queueIds.includes(e.queueId)) + + if (userQueuesAttendance && userQueuesAttendance.length > 0) { + + const queueInAttendance = userQueuesAttendance.map((e) => e.queueId) + + const mergedSet = new Set([...userData.queueIds, ...queueInAttendance]) + + // Convert the Set back to an array + userData.queueIds = Array.from(mergedSet) + + // console.log('------> 2 userData.queueIds: ', userData.queueIds) + + } + + } + + // console.log('userQueuesAttendance: ', userQueuesAttendance) + + // return res.status(200).json({}); + + let user: any = await UpdateUserService({ userData, userId }); const io = getIO(); io.emit("user", { @@ -152,6 +194,8 @@ export const update = async ( user }); + user.userQueuesAttendance = userQueuesAttendance + return res.status(200).json(user); }; diff --git a/backend/src/helpers/whatsappQueueMatchingUserQueue.ts b/backend/src/helpers/whatsappQueueMatchingUserQueue.ts new file mode 100644 index 0000000..86a34fe --- /dev/null +++ b/backend/src/helpers/whatsappQueueMatchingUserQueue.ts @@ -0,0 +1,26 @@ +import Whatsapp from "../models/Whatsapp"; +import ShowQueuesByUser from "../services/UserServices/ShowQueuesByUser"; +import ShowWhatsAppService from "../services/WhatsappService/ShowWhatsAppService"; + +async function whatsappQueueMatchingUserQueue(userId: number, whatsapp: Whatsapp) { + + const userQueues = await ShowQueuesByUser({ profile: 'user', userId: userId }); + + if (!userQueues || userQueues && userQueues.length == 0) return + + console.log('-----> userQueues: ', userQueues); + + let whats: any = await ShowWhatsAppService(whatsapp.id); + + if (!whats.queues || whats.queues && whats.queues.length == 0) return + + const whatsappQueues = whats.queues.map((e: any) => e.dataValues.name); + + console.log('-----> whatsappQueues: ', whatsappQueues); + + const matchingQueue = userQueues.find(queue => whatsappQueues.includes(queue.name)); + + return matchingQueue +} + +export default whatsappQueueMatchingUserQueue \ No newline at end of file diff --git a/backend/src/services/TicketServices/CreateTicketService.ts b/backend/src/services/TicketServices/CreateTicketService.ts index f3bd64a..e26a094 100644 --- a/backend/src/services/TicketServices/CreateTicketService.ts +++ b/backend/src/services/TicketServices/CreateTicketService.ts @@ -14,6 +14,10 @@ import { splitDateTime } from "../../helpers/SplitDateTime"; import TicketEmiterSumOpenClosedByUser from "../../helpers/OnlineReporEmiterInfoByUser"; import { createOrUpdateTicketCache } from '../../helpers/TicketCache' +import ShowQueuesByUser from "../UserServices/ShowQueuesByUser"; +import ShowWhatsAppService from "../WhatsappService/ShowWhatsAppService"; +import Whatsapp from "../../models/Whatsapp"; +import whatsappQueueMatchingUserQueue from "../../helpers/whatsappQueueMatchingUserQueue"; let flatten = require('flat') @@ -32,8 +36,14 @@ const CreateTicketService = async ({ try { + console.log('Create contact service........') + const defaultWhatsapp = await GetDefaultWhatsApp(userId); + const matchingQueue = await whatsappQueueMatchingUserQueue(userId, defaultWhatsapp); + console.log('matchingQueue: ', matchingQueue) + const queueId = matchingQueue ? matchingQueue.queueId : undefined + await CheckContactOpenTickets(contactId); const { isGroup } = await ShowContactService(contactId); @@ -42,9 +52,12 @@ const CreateTicketService = async ({ contactId, status, isGroup, - userId + userId, + queueId }); + console.log('TICKET CREATED!') + const ticket = await Ticket.findByPk(id, { include: ["contact"] }); if (!ticket) { @@ -54,7 +67,6 @@ const CreateTicketService = async ({ // console.log('CONTACT ticket.id: ', ticket.id) - // TEST DEL try { let jsonString = JSON.stringify(ticket); //convert to string to remove the sequelize specific meta data @@ -68,7 +80,6 @@ const CreateTicketService = async ({ } catch (error) { console.log('There was an error on UpdateTicketService.ts on createTicketCache from user: ', error) } - // const dateToday = splitDateTime(new Date(format(new Date(), 'yyyy-MM-dd HH:mm:ss', { locale: ptBR }))) @@ -92,3 +103,6 @@ const CreateTicketService = async ({ }; export default CreateTicketService; + + + diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index 2c11f4a..cd97403 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -927,7 +927,7 @@ const handleMsgAck = async (msg_id: any, ack: any) => { } await messageToUpdate.update({ ack }); - console.log('ACK messageToUpdate: ', JSON.parse(JSON.stringify(messageToUpdate))) + // console.log('ACK messageToUpdate: ', JSON.parse(JSON.stringify(messageToUpdate))) io.to(messageToUpdate.ticketId.toString()).emit("appMessage", { action: "update", diff --git a/frontend/src/components/ContactModal/index.js b/frontend/src/components/ContactModal/index.js index 8305524..fb5b67b 100644 --- a/frontend/src/components/ContactModal/index.js +++ b/frontend/src/components/ContactModal/index.js @@ -60,11 +60,13 @@ const ContactSchema = Yup.object().shape({ number: Yup.string().min(8, "Too Short!").max(50, "Too Long!"), email: Yup.string().min(2, "Too Short!") - .max(50, "Too Long!"), + .max(50, "Too Long!"), // email: Yup.string().email("Invalid email"), }); + + const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { const classes = useStyles(); const isMounted = useRef(true); @@ -76,6 +78,8 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { }; const [contact, setContact] = useState(initialState); + const [phone, setPhone] = useState('') + useEffect(() => { return () => { @@ -83,9 +87,22 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { }; }, []); + // useEffect(() => { + // console.log('1 Contact: ', contact) + + // setContact({ + // ...contact, + // number: phone, + // }); + + // console.log('2 Contact: ', contact) + + // }, [phone]); + useEffect(() => { const fetchContact = async () => { if (initialValues) { + setContact(prevState => { return { ...prevState, ...initialValues }; }); @@ -95,9 +112,14 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { try { const { data } = await api.get(`/contacts/${contactId}`); + if (isMounted.current) { + + setPhone(data.number) + setContact(data); } + } catch (err) { toastError(err); } @@ -112,6 +134,11 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { }; const handleSaveContact = async values => { + + values = { ...values, number: phone }; + + console.log('submit values', values) + try { if (contactId) { await api.put(`/contacts/${contactId}`, values); @@ -129,6 +156,28 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { } }; + const handleChange = (event) => { + + const regex = /^[0-9\b]+$/; // Regular expression to match only numbers + + setPhone(event.target.value); + + setTimeout(() => { + + let newValue = '' + + for (const char of event.target.value) { + if (char.match(regex)) { + newValue += char + } + } + + setPhone(newValue) + + }, 100); + + }; + return (
@@ -169,6 +218,10 @@ const ContactModal = ({ open, onClose, contactId, initialValues, onSave }) => { as={TextField} label={i18n.t("contactModal.form.number")} name="number" + + value={phone} + onChange={handleChange} + error={touched.number && Boolean(errors.number)} helperText={touched.number && errors.number} placeholder="5513912344321" diff --git a/frontend/src/components/NewTicketModal/index.js b/frontend/src/components/NewTicketModal/index.js index 1d0a16a..490c71e 100644 --- a/frontend/src/components/NewTicketModal/index.js +++ b/frontend/src/components/NewTicketModal/index.js @@ -74,8 +74,12 @@ const NewTicketModal = ({ modalOpen, onClose }) => { contactId: contactId, userId: user.id, status: "open", - }); + }); + + window.location.reload(); history.push(`/tickets/${ticket.id}`); + window.location.reload(); + } catch (err) { toastError(err); } diff --git a/frontend/src/components/UserModal/index.js b/frontend/src/components/UserModal/index.js index d827ef8..50940cf 100644 --- a/frontend/src/components/UserModal/index.js +++ b/frontend/src/components/UserModal/index.js @@ -18,7 +18,7 @@ import { TextField, InputAdornment, IconButton - } from '@material-ui/core'; +} from '@material-ui/core'; import { Visibility, VisibilityOff } from '@material-ui/icons'; @@ -71,9 +71,9 @@ const UserSchema = Yup.object().shape({ password: Yup.string().min(5, "Too Short!").max(50, "Too Long!"), email: Yup.string().min(2, "Too Short!") - .max(50, "Too Long!") - .required("Required"), - + .max(50, "Too Long!") + .required("Required"), + // email: Yup.string().email("Invalid email").required("Required"), }); @@ -98,12 +98,20 @@ const UserModal = ({ open, onClose, userId }) => { const fetchUser = async () => { if (!userId) return; try { + + // console.log('window.location.href: ',window.location.href) + + const { data } = await api.get(`/users/${userId}`); setUser(prevState => { return { ...prevState, ...data }; }); const userQueueIds = data.queues?.map(queue => queue.id); setSelectedQueueIds(userQueueIds); + + // console.log('data: ', data) + // console.log('loggedInUser.email: ', loggedInUser.email) + } catch (err) { toastError(err); } @@ -121,7 +129,29 @@ const UserModal = ({ open, onClose, userId }) => { const userData = { ...values, queueIds: selectedQueueIds }; try { if (userId) { - await api.put(`/users/${userId}`, userData); + + const user = await api.put(`/users/${userId}`, userData); + + // console.log('USER: ', user.data) + + if (user && user.data.userQueuesAttendance.length > 0) { + + const userQueueInAttendance = user.data.userQueuesAttendance.map((e) => ({ "queue": e.queueName, "open": e.totAttendance })) + + console.log('userQueueInAttendance: ', userQueueInAttendance) + + let msg = '\nAVISO \n\nO atendente possui atendimento(s) em aberto na(s) fila(s) abaixo: \n\n' + + userQueueInAttendance.forEach((e) => { + msg += `Fila: ${e.queue}\nAberto: ${e.open}\n\n` + }) + + msg += 'Para remover o atendente da(s) fila(s) acima é necessário que o mesmo encerre os atendimentos aberto(s) nessas filas.\n' + + alert(msg) + + } + } else { await api.post("/users", userData); } @@ -166,6 +196,7 @@ const UserModal = ({ open, onClose, userId }) => { label={i18n.t("userModal.form.name")} autoFocus name="name" + disabled={loggedInUser.profile === 'admin' || loggedInUser.profile === 'master' ? false : true} error={touched.name && Boolean(errors.name)} helperText={touched.name && errors.name} variant="outlined" @@ -175,6 +206,7 @@ const UserModal = ({ open, onClose, userId }) => { { helperText={touched.password && errors.password} type={showPassword ? 'text' : 'password'} InputProps={{ - endAdornment: ( - - setShowPassword((e) => !e)} - > - {showPassword ? : } - - - ) + endAdornment: ( + + setShowPassword((e) => !e)} + > + {showPassword ? : } + + + ) }} fullWidth /> @@ -201,6 +233,7 @@ const UserModal = ({ open, onClose, userId }) => { as={TextField} label={i18n.t("userModal.form.email")} name="email" + disabled={loggedInUser.profile === 'admin' || loggedInUser.profile === 'master' ? false : true} error={touched.email && Boolean(errors.email)} helperText={touched.email && errors.email} variant="outlined" @@ -230,6 +263,7 @@ const UserModal = ({ open, onClose, userId }) => { required > Admin + Supervisor User diff --git a/frontend/src/layout/MainListItems.js b/frontend/src/layout/MainListItems.js index 1455c45..abab60f 100644 --- a/frontend/src/layout/MainListItems.js +++ b/frontend/src/layout/MainListItems.js @@ -94,9 +94,11 @@ const MainListItems = (props) => { primary={i18n.t("mainDrawer.listItems.quickAnswers")} icon={} /> + + ( <> @@ -106,6 +108,23 @@ const MainListItems = (props) => { primary={i18n.t("mainDrawer.listItems.users")} icon={} /> + + )} + /> + + + ( + <> + {/* + {i18n.t("mainDrawer.listItems.administration")} */} + {/* } + /> */}