diff --git a/backend/src/services/ContactServices/GetContactService.ts b/backend/src/services/ContactServices/GetContactService.ts index 93b12e3..3865bc5 100644 --- a/backend/src/services/ContactServices/GetContactService.ts +++ b/backend/src/services/ContactServices/GetContactService.ts @@ -1,5 +1,6 @@ import AppError from "../../errors/AppError"; import Contact from "../../models/Contact"; +import CreateContactService from "./CreateContactService"; interface ExtraInfo { name: string; @@ -20,10 +21,18 @@ const GetContactService = async ({ name, number }: Request): Promise => }); if (!numberExists) { - throw new AppError("CONTACT_NOT_FIND"); + const contact = await CreateContactService({ + name, + number, + }) + + if (contact == null) + throw new AppError("CONTACT_NOT_FIND") + else + return contact } - return numberExists; + return numberExists }; export default GetContactService; \ No newline at end of file diff --git a/backend/src/services/WbotServices/wbotMessageListener.ts b/backend/src/services/WbotServices/wbotMessageListener.ts index d8daaab..0345c21 100644 --- a/backend/src/services/WbotServices/wbotMessageListener.ts +++ b/backend/src/services/WbotServices/wbotMessageListener.ts @@ -2,6 +2,7 @@ import { join } from "path"; import { promisify } from "util"; import { writeFile } from "fs"; import * as Sentry from "@sentry/node"; + import { Contact as WbotContact, Message as WbotMessage, @@ -114,6 +115,10 @@ const verifyMessage = async ( ticket: Ticket, contact: Contact ) => { + + if (msg.type === 'location') + msg = prepareLocation(msg); + const quotedMsg = await verifyQuotedMessage(msg); const messageData = { id: msg.id.id, @@ -126,11 +131,21 @@ const verifyMessage = async ( quotedMsgId: quotedMsg?.id }; - await ticket.update({ lastMessage: msg.body }); + await ticket.update({ lastMessage: msg.type === "location" ? msg.location.description ? "Localization - " + msg.location.description.split('\\n')[0] : "Localization" : msg.body }); await CreateMessageService({ messageData }); }; +const prepareLocation = (msg: WbotMessage): WbotMessage => { + let gmapsUrl = "https://maps.google.com/maps?q=" + msg.location.latitude + "%2C" + msg.location.longitude + "&z=17&hl=pt-BR"; + + msg.body = "data:image/png;base64," + msg.body + "|" + gmapsUrl; + + msg.body += "|" + (msg.location.description ? msg.location.description : (msg.location.latitude + ", " + msg.location.longitude)) + + return msg; +}; + const verifyQueue = async ( wbot: Session, msg: WbotMessage, @@ -198,8 +213,9 @@ const isValidMsg = (msg: WbotMessage): boolean => { msg.type === "image" || msg.type === "document" || msg.type === "vcard" || - // msg.type === "multi_vcard" || - msg.type === "sticker" + //msg.type === "multi_vcard" || + msg.type === "sticker" || + msg.type === "location" ) return true; return false; @@ -225,13 +241,9 @@ const handleMessage = async ( // media messages sent from me from cell phone, first comes with "hasMedia = false" and type = "image/ptt/etc" // in this case, return and let this message be handled by "media_uploaded" event, when it will have "hasMedia = true" - if ( - !msg.hasMedia && - msg.type !== "chat" && - msg.type !== "vcard" - // && msg.type !== "multi_vcard" - ) - return; + if (!msg.hasMedia && msg.type !== "location" && msg.type !== "chat" && msg.type !== "vcard" + //&& msg.type !== "multi_vcard" + ) return; msgContact = await wbot.getContactById(msg.to); } else { diff --git a/frontend/.env.example b/frontend/.env.example index f890a22..b8d2f1d 100644 --- a/frontend/.env.example +++ b/frontend/.env.example @@ -1 +1,2 @@ -REACT_APP_BACKEND_URL = http://localhost:8080/ \ No newline at end of file +REACT_APP_BACKEND_URL = http://localhost:8080/ +REACT_APP_HOURS_CLOSE_TICKETS_AUTO = \ No newline at end of file diff --git a/frontend/src/components/LocationPreview/index.js b/frontend/src/components/LocationPreview/index.js new file mode 100644 index 0000000..9187faf --- /dev/null +++ b/frontend/src/components/LocationPreview/index.js @@ -0,0 +1,53 @@ +import React, { useEffect } from 'react'; +import toastError from "../../errors/toastError"; + +import Typography from "@material-ui/core/Typography"; +import Grid from "@material-ui/core/Grid"; + +import { Button, Divider, } from "@material-ui/core"; + +const LocationPreview = ({ image, link, description }) => { + useEffect(() => {}, [image, link, description]); + + const handleLocation = async() => { + try { + window.open(link); + } catch (err) { + toastError(err); + } + } + + return ( + <> +
+
+
+ +
+ { description && ( +
+ +
') }}>
+
+
+ )} +
+
+ + +
+
+
+ + ); + +}; + +export default LocationPreview; \ No newline at end of file diff --git a/frontend/src/components/MarkdownWrapper/index.js b/frontend/src/components/MarkdownWrapper/index.js index 29ea445..5a65ee1 100644 --- a/frontend/src/components/MarkdownWrapper/index.js +++ b/frontend/src/components/MarkdownWrapper/index.js @@ -153,10 +153,13 @@ const MarkdownWrapper = ({ children }) => { const boldRegex = /\*(.*?)\*/g; const tildaRegex = /~(.*?)~/g; - if(children.includes('BEGIN:VCARD')) + if(children && children.includes('BEGIN:VCARD')) //children = "Diga olá ao seu novo contato clicando em *conversar*!"; children = null; + if(children && children.includes('data:image/')) + children = null; + if (children && boldRegex.test(children)) { children = children.replace(boldRegex, "**$1**"); } diff --git a/frontend/src/components/MessagesList/index.js b/frontend/src/components/MessagesList/index.js index 5a9cf21..8e2a336 100644 --- a/frontend/src/components/MessagesList/index.js +++ b/frontend/src/components/MessagesList/index.js @@ -23,6 +23,7 @@ import { import MarkdownWrapper from "../MarkdownWrapper"; import VcardPreview from "../VcardPreview"; +import LocationPreview from "../LocationPreview"; import ModalImageCors from "../ModalImageCors"; import MessageOptionsMenu from "../MessageOptionsMenu"; import whatsBackground from "../../assets/wa-background.png"; @@ -414,7 +415,19 @@ const MessagesList = ({ ticketId, isGroup }) => { }; const checkMessageMedia = (message) => { - if (message.mediaType === "vcard") { + if(message.mediaType === "location" && message.body.split('|').length >= 2) { + let locationParts = message.body.split('|') + let imageLocation = locationParts[0] + let linkLocation = locationParts[1] + + let descriptionLocation = null + + if(locationParts.length > 2) + descriptionLocation = message.body.split('|')[2] + + return + } + else if (message.mediaType === "vcard") { //console.log("vcard") //console.log(message) let array = message.body.split("\n"); @@ -604,7 +617,7 @@ const MessagesList = ({ ticketId, isGroup }) => { {message.contact?.name} )} - {(message.mediaUrl || message.mediaType === "vcard" + {(message.mediaUrl || message.mediaType === "location" || message.mediaType === "vcard" //|| message.mediaType === "multi_vcard" ) && checkMessageMedia(message)}
@@ -633,7 +646,7 @@ const MessagesList = ({ ticketId, isGroup }) => { > - {(message.mediaUrl || message.mediaType === "vcard" + {(message.mediaUrl || message.mediaType === "location" || message.mediaType === "vcard" //|| message.mediaType === "multi_vcard" ) && checkMessageMedia(message)}
{ - const [loading, setLoading] = useState(true); - const [hasMore, setHasMore] = useState(false); - const [tickets, setTickets] = useState([]); + const [loading, setLoading] = useState(true); + const [hasMore, setHasMore] = useState(false); + const [tickets, setTickets] = useState([]); + const [count, setCount] = useState(0); - useEffect(() => { - setLoading(true); - const delayDebounceFn = setTimeout(() => { - const fetchTickets = async () => { - try { - const { data } = await api.get("/tickets", { - params: { - searchParam, - pageNumber, - status, - date, - showAll, - queueIds, - withUnreadMessages, - }, - }); - setTickets(data.tickets); - setHasMore(data.hasMore); - setLoading(false); - } catch (err) { - setLoading(false); - toastError(err); - } - }; - fetchTickets(); - }, 500); - return () => clearTimeout(delayDebounceFn); - }, [ - searchParam, - pageNumber, - status, - date, - showAll, - queueIds, - withUnreadMessages, - ]); + useEffect(() => { + setLoading(true); + const delayDebounceFn = setTimeout(() => { + const fetchTickets = async() => { + try { + const { data } = await api.get("/tickets", { + params: { + searchParam, + pageNumber, + status, + date, + showAll, + queueIds, + withUnreadMessages, + }, + }) + setTickets(data.tickets) - return { tickets, loading, hasMore }; + let horasFecharAutomaticamente = process.env.REACT_APP_HOURS_CLOSE_TICKETS_AUTO + + if (status === "open" && horasFecharAutomaticamente && horasFecharAutomaticamente !== "" && + horasFecharAutomaticamente !== "0" && Number(horasFecharAutomaticamente) > 0) { + + let dataLimite = new Date() + dataLimite.setHours(dataLimite.getHours() - Number(horasFecharAutomaticamente)) + + data.tickets.forEach(ticket => { + if (ticket.status !== "closed") { + let dataUltimaInteracaoChamado = new Date(ticket.updatedAt) + if (dataUltimaInteracaoChamado < dataLimite) + closeTicket(ticket) + } + }) + } + + setHasMore(data.hasMore) + setCount(data.count) + setLoading(false) + } catch (err) { + setLoading(false) + toastError(err) + } + } + + const closeTicket = async(ticket) => { + await api.put(`/tickets/${ticket.id}`, { + status: "closed", + userId: ticket.userId || null, + }) + } + + fetchTickets() + }, 500) + return () => clearTimeout(delayDebounceFn) + }, [ + searchParam, + pageNumber, + status, + date, + showAll, + queueIds, + withUnreadMessages, + ]) + + return { tickets, loading, hasMore, count }; }; -export default useTickets; +export default useTickets; \ No newline at end of file diff --git a/frontend/src/pages/Dashboard/index.js b/frontend/src/pages/Dashboard/index.js index 7d11703..eb11d19 100644 --- a/frontend/src/pages/Dashboard/index.js +++ b/frontend/src/pages/Dashboard/index.js @@ -54,13 +54,13 @@ const Dashboard = () => { const GetTickets = (status, showAll, withUnreadMessages) => { - const { tickets } = useTickets({ + const { count } = useTickets({ status: status, showAll: showAll, withUnreadMessages: withUnreadMessages, queueIds: JSON.stringify(userQueueIds) }); - return tickets.length; + return count; } return ( @@ -114,4 +114,4 @@ const Dashboard = () => { ) } -export default Dashboard +export default Dashboard \ No newline at end of file