Atualização basica para envio de template do Whatsapp cloud API
parent
72c489a27b
commit
c151473d52
|
@ -19,6 +19,8 @@ import {
|
||||||
import CreateOrUpdateContactService from "../services/ContactServices/CreateOrUpdateContactService";
|
import CreateOrUpdateContactService from "../services/ContactServices/CreateOrUpdateContactService";
|
||||||
import sendWhatsAppMessageOfficialAPI from "../helpers/sendWhatsAppMessageOfficialAPI";
|
import sendWhatsAppMessageOfficialAPI from "../helpers/sendWhatsAppMessageOfficialAPI";
|
||||||
import Whatsapp from "../models/Whatsapp";
|
import Whatsapp from "../models/Whatsapp";
|
||||||
|
import checkLastClientMsg24hs from "../helpers/CheckLastClientMsg24hs";
|
||||||
|
import AppError from "../errors/AppError";
|
||||||
|
|
||||||
type IndexQuery = {
|
type IndexQuery = {
|
||||||
pageNumber: string;
|
pageNumber: string;
|
||||||
|
@ -29,7 +31,8 @@ type MessageData = {
|
||||||
fromMe: boolean;
|
fromMe: boolean;
|
||||||
read: boolean;
|
read: boolean;
|
||||||
quotedMsg?: Message;
|
quotedMsg?: Message;
|
||||||
mic_audio?: boolean
|
mic_audio?: boolean;
|
||||||
|
params: any;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const index = async (req: Request, res: Response): Promise<Response> => {
|
export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
@ -46,12 +49,92 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
|
|
||||||
export const store = async (req: Request, res: Response): Promise<Response> => {
|
export const store = async (req: Request, res: Response): Promise<Response> => {
|
||||||
const { ticketId } = req.params;
|
const { ticketId } = req.params;
|
||||||
const { body, quotedMsg, mic_audio }: MessageData = req.body;
|
const { body, quotedMsg, mic_audio, params }: MessageData = req.body;
|
||||||
const medias = req.files as Express.Multer.File[];
|
const medias = req.files as Express.Multer.File[];
|
||||||
const ticket = await ShowTicketService(ticketId);
|
const ticket = await ShowTicketService(ticketId);
|
||||||
|
|
||||||
const { queueId } = ticket;
|
const { queueId } = ticket;
|
||||||
console.log("-----------> queueId: ", queueId, " | quotedMsg: ", quotedMsg);
|
console.log(
|
||||||
|
"-----------> queueId: ",
|
||||||
|
queueId,
|
||||||
|
" | quotedMsg: ",
|
||||||
|
quotedMsg,
|
||||||
|
" | params: ",
|
||||||
|
params
|
||||||
|
);
|
||||||
|
|
||||||
|
const { phoneNumberId, whatsappId } = ticket;
|
||||||
|
|
||||||
|
if (phoneNumberId) {
|
||||||
|
const into24hs = await checkLastClientMsg24hs(ticket);
|
||||||
|
|
||||||
|
if (into24hs && into24hs.length == 0) {
|
||||||
|
if (params) {
|
||||||
|
console.log("SEND TEMPLATE PARAMS: ", params);
|
||||||
|
|
||||||
|
// return res.send()
|
||||||
|
|
||||||
|
let payloadComponents = [];
|
||||||
|
|
||||||
|
try {
|
||||||
|
for (let i in params) {
|
||||||
|
const { parameters, language, type } = params[i];
|
||||||
|
if (type == "BODY") {
|
||||||
|
if (parameters && parameters.length > 0) {
|
||||||
|
let components: any = [{ type: "body", parameters: [] }];
|
||||||
|
for (let x in parameters) {
|
||||||
|
const { type, text, index } = parameters[x];
|
||||||
|
console.log(text);
|
||||||
|
components[0].parameters.splice(index - 1, 0, {
|
||||||
|
type,
|
||||||
|
text
|
||||||
|
});
|
||||||
|
}
|
||||||
|
payloadComponents.push(components[0]);
|
||||||
|
}
|
||||||
|
} else if (type == "BUTTONS") {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const name = params.find((p: any) => p?.template_name);
|
||||||
|
const { language }: any = params.find((p: any) => p?.language);
|
||||||
|
|
||||||
|
const { template_name } = name;
|
||||||
|
|
||||||
|
if (template_name && language) {
|
||||||
|
const template: any = {
|
||||||
|
template: {
|
||||||
|
name: template_name,
|
||||||
|
language: { code: language },
|
||||||
|
components: payloadComponents
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
sendWhatsAppMessageOfficialAPI(ticket, body, null, template);
|
||||||
|
|
||||||
|
console.log("TEMPLATE: ", template);
|
||||||
|
return res.send();
|
||||||
|
}
|
||||||
|
} catch (error: any) {
|
||||||
|
throw new AppError(error.message);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
try {
|
||||||
|
const { wabaId }: any = await Whatsapp.findByPk(whatsappId);
|
||||||
|
|
||||||
|
const { data } = await whatsappOfficialAPI.get(
|
||||||
|
`/${process.env.VERSION}/${wabaId}/message_templates?language=pt_BR`
|
||||||
|
);
|
||||||
|
|
||||||
|
return res.status(200).json(data);
|
||||||
|
} catch (error) {
|
||||||
|
return res
|
||||||
|
.status(500)
|
||||||
|
.json({ message: "Não foi possível baixar os templates!" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
if (medias) {
|
if (medias) {
|
||||||
await Promise.all(
|
await Promise.all(
|
||||||
|
|
|
@ -14,6 +14,8 @@ export const wbotMonitorRemote = async (req: Request, res: Response): Promise<Re
|
||||||
|
|
||||||
const { action, whatsappId, reason } = req.body
|
const { action, whatsappId, reason } = req.body
|
||||||
|
|
||||||
|
console.log('action: ', action, ' | whatsappId: ', whatsappId, ' | reason: ', reason)
|
||||||
|
|
||||||
console.log('-----------> ACTION: ', req.body['action'])
|
console.log('-----------> ACTION: ', req.body['action'])
|
||||||
|
|
||||||
const whatsapp: any = await Whatsapp.findByPk(whatsappId, { raw: true })
|
const whatsapp: any = await Whatsapp.findByPk(whatsappId, { raw: true })
|
||||||
|
|
|
@ -80,7 +80,10 @@ export const index = async (req: Request, res: Response): Promise<Response> => {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.log('error on try update classification number from oficial whatsapp in WhatsappController.ts: ', error)
|
console.log(
|
||||||
|
"error on try update classification number from oficial whatsapp in WhatsappController.ts: ",
|
||||||
|
error
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -244,7 +247,8 @@ export const weebhook = async (
|
||||||
type = "chat";
|
type = "chat";
|
||||||
msg = {
|
msg = {
|
||||||
...msg,
|
...msg,
|
||||||
body: message.text.body // extract the message text from the webhook payload,
|
body: message.text.body, // extract the message text from the webhook payload,
|
||||||
|
type
|
||||||
};
|
};
|
||||||
} else {
|
} else {
|
||||||
const mediaId = message[message.type].id;
|
const mediaId = message[message.type].id;
|
||||||
|
@ -265,6 +269,8 @@ export const weebhook = async (
|
||||||
wbot = { ...wbot, media: { filename, mimetype } };
|
wbot = { ...wbot, media: { filename, mimetype } };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
msg = { ...msg, phoneNumberId: whatsapp.phoneNumberId };
|
||||||
|
|
||||||
console.log("from: ", contact_from);
|
console.log("from: ", contact_from);
|
||||||
console.log("to: ", contact_to);
|
console.log("to: ", contact_to);
|
||||||
console.log("msg type: ", type);
|
console.log("msg type: ", type);
|
||||||
|
|
|
@ -0,0 +1,22 @@
|
||||||
|
import { Op } from "sequelize";
|
||||||
|
import { sub, subHours } from "date-fns";
|
||||||
|
import Message from "../models/Message";
|
||||||
|
import Ticket from "../models/Ticket";
|
||||||
|
|
||||||
|
async function checkLastClientMsg24hs(ticket: Ticket) {
|
||||||
|
return await Message.findAll({
|
||||||
|
attributes: ["createdAt", "body"],
|
||||||
|
where: {
|
||||||
|
contactId: ticket.contactId,
|
||||||
|
phoneNumberId: ticket.phoneNumberId,
|
||||||
|
fromMe: false,
|
||||||
|
createdAt: {
|
||||||
|
[Op.between]: [+subHours(new Date(), 24), +new Date()]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
order: [["createdAt", "DESC"]],
|
||||||
|
limit: 1
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export default checkLastClientMsg24hs;
|
|
@ -11,7 +11,8 @@ import whatsappOfficialAPI from "./WhatsappOfficialAPI";
|
||||||
async function sendWhatsAppMessageOfficialAPI(
|
async function sendWhatsAppMessageOfficialAPI(
|
||||||
ticket: Ticket,
|
ticket: Ticket,
|
||||||
body: string,
|
body: string,
|
||||||
quotedMsgSerializedId?: any | undefined
|
quotedMsgSerializedId?: any | undefined,
|
||||||
|
_template?: any
|
||||||
) {
|
) {
|
||||||
const { contactId, phoneNumberId } = ticket;
|
const { contactId, phoneNumberId } = ticket;
|
||||||
|
|
||||||
|
@ -22,14 +23,27 @@ async function sendWhatsAppMessageOfficialAPI(
|
||||||
let data: any = {
|
let data: any = {
|
||||||
messaging_product: "whatsapp",
|
messaging_product: "whatsapp",
|
||||||
recipient_type: "individual",
|
recipient_type: "individual",
|
||||||
to: number,
|
to: number
|
||||||
type: "text",
|
|
||||||
text: {
|
|
||||||
preview_url: true,
|
|
||||||
body
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
if (_template) {
|
||||||
|
const { template } = _template;
|
||||||
|
data = {
|
||||||
|
...data,
|
||||||
|
type: "template",
|
||||||
|
template
|
||||||
|
};
|
||||||
|
} else {
|
||||||
|
data = {
|
||||||
|
...data,
|
||||||
|
type: "text",
|
||||||
|
text: {
|
||||||
|
preview_url: true,
|
||||||
|
body
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
if (quotedMsgSerializedId) {
|
if (quotedMsgSerializedId) {
|
||||||
data = { ...data, context: { message_id: quotedMsgSerializedId.id } };
|
data = { ...data, context: { message_id: quotedMsgSerializedId.id } };
|
||||||
}
|
}
|
||||||
|
@ -38,6 +52,8 @@ async function sendWhatsAppMessageOfficialAPI(
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
console.log("SEND MESSAGE: ", JSON.stringify(data, null,2));
|
||||||
|
|
||||||
whatsappOfficialAPI
|
whatsappOfficialAPI
|
||||||
.post(`/${process.env.VERSION}/${phoneNumberId}/messages`, data)
|
.post(`/${process.env.VERSION}/${phoneNumberId}/messages`, data)
|
||||||
.then(response => {
|
.then(response => {
|
||||||
|
|
|
@ -23,6 +23,7 @@ const isAuth = (req: Request, res: Response, next: NextFunction): void => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const decoded = verify(token, authConfig.secret);
|
const decoded = verify(token, authConfig.secret);
|
||||||
|
|
||||||
const { id, profile } = decoded as TokenPayload;
|
const { id, profile } = decoded as TokenPayload;
|
||||||
|
|
||||||
req.user = {
|
req.user = {
|
||||||
|
|
|
@ -116,9 +116,10 @@ const SendWhatsAppMessage = async ({
|
||||||
});
|
});
|
||||||
} else if (whatsapps && whatsapps.length == 1) {
|
} else if (whatsapps && whatsapps.length == 1) {
|
||||||
await ticket.update({ whatsappId: whatsapps[0].id });
|
await ticket.update({ whatsappId: whatsapps[0].id });
|
||||||
}
|
} else {
|
||||||
else{
|
throw new Error(
|
||||||
throw new Error('Sessão de Whatsapp desconectada! Entre em contato com o suporte.')
|
"Sessão de Whatsapp desconectada! Entre em contato com o suporte."
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -162,7 +162,8 @@ const verifyMediaMessage = async (
|
||||||
read: msg.fromMe,
|
read: msg.fromMe,
|
||||||
mediaUrl: media.filename,
|
mediaUrl: media.filename,
|
||||||
mediaType: media.mimetype.split("/")[0],
|
mediaType: media.mimetype.split("/")[0],
|
||||||
quotedMsgId: quotedMsg
|
quotedMsgId: quotedMsg,
|
||||||
|
phoneNumberId: msg?.phoneNumberId
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
@ -443,6 +444,7 @@ const mediaTypeWhatsappOfficial = (mimetype: string): object => {
|
||||||
const isValidMsg = (msg: any): boolean => {
|
const isValidMsg = (msg: any): boolean => {
|
||||||
if (msg.from === "status@broadcast") return false;
|
if (msg.from === "status@broadcast") return false;
|
||||||
if (
|
if (
|
||||||
|
msg.type === "template" ||
|
||||||
msg.type === "text" ||
|
msg.type === "text" ||
|
||||||
msg.type === "hsm" ||
|
msg.type === "hsm" ||
|
||||||
msg.type === "chat" ||
|
msg.type === "chat" ||
|
||||||
|
|
|
@ -178,7 +178,7 @@ const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => {
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
console.log('selectedWhatsId: ', selectedWhatsId)
|
console.log('selectedWhatsId: ', selectedWhatsId)
|
||||||
console.log('whatsQuee: ', whatsQueue)
|
console.log('whatsQuee: ', whatsQueue)
|
||||||
}, [whatsQueue])
|
}, [whatsQueue, selectedWhatsId])
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Dialog open={modalOpen} onClose={handleClose} maxWidth="xs" scroll="paper" classes={{ paper: classes.paper }}>
|
<Dialog open={modalOpen} onClose={handleClose} maxWidth="xs" scroll="paper" classes={{ paper: classes.paper }}>
|
||||||
|
|
|
@ -1,47 +1,50 @@
|
||||||
import React, { useState, useEffect, useContext, useRef } from "react";
|
import React, { useState, useEffect, useContext, useRef } from "react"
|
||||||
import "emoji-mart/css/emoji-mart.css";
|
import "emoji-mart/css/emoji-mart.css"
|
||||||
import { useParams } from "react-router-dom";
|
import { useParams } from "react-router-dom"
|
||||||
import { Picker } from "emoji-mart";
|
import { Picker } from "emoji-mart"
|
||||||
import MicRecorder from "mic-recorder-to-mp3";
|
import MicRecorder from "mic-recorder-to-mp3"
|
||||||
import clsx from "clsx";
|
import clsx from "clsx"
|
||||||
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles"
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper"
|
||||||
import InputBase from "@material-ui/core/InputBase";
|
import InputBase from "@material-ui/core/InputBase"
|
||||||
import CircularProgress from "@material-ui/core/CircularProgress";
|
import CircularProgress from "@material-ui/core/CircularProgress"
|
||||||
import { green } from "@material-ui/core/colors";
|
import { green } from "@material-ui/core/colors"
|
||||||
import AttachFileIcon from "@material-ui/icons/AttachFile";
|
import AttachFileIcon from "@material-ui/icons/AttachFile"
|
||||||
import IconButton from "@material-ui/core/IconButton";
|
import IconButton from "@material-ui/core/IconButton"
|
||||||
import MoreVert from "@material-ui/icons/MoreVert";
|
import MoreVert from "@material-ui/icons/MoreVert"
|
||||||
import MoodIcon from "@material-ui/icons/Mood";
|
import MoodIcon from "@material-ui/icons/Mood"
|
||||||
import SendIcon from "@material-ui/icons/Send";
|
import SendIcon from "@material-ui/icons/Send"
|
||||||
import CancelIcon from "@material-ui/icons/Cancel";
|
import CancelIcon from "@material-ui/icons/Cancel"
|
||||||
import ClearIcon from "@material-ui/icons/Clear";
|
import ClearIcon from "@material-ui/icons/Clear"
|
||||||
import MicIcon from "@material-ui/icons/Mic";
|
import MicIcon from "@material-ui/icons/Mic"
|
||||||
import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline";
|
import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline"
|
||||||
import HighlightOffIcon from "@material-ui/icons/HighlightOff";
|
import HighlightOffIcon from "@material-ui/icons/HighlightOff"
|
||||||
import {
|
import {
|
||||||
FormControlLabel,
|
FormControlLabel,
|
||||||
Hidden,
|
Hidden,
|
||||||
Menu,
|
Menu,
|
||||||
MenuItem,
|
MenuItem,
|
||||||
Switch,
|
Switch,
|
||||||
} from "@material-ui/core";
|
} from "@material-ui/core"
|
||||||
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
|
import ClickAwayListener from "@material-ui/core/ClickAwayListener"
|
||||||
|
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n"
|
||||||
import api from "../../services/api";
|
import api from "../../services/api"
|
||||||
import RecordingTimer from "./RecordingTimer";
|
import RecordingTimer from "./RecordingTimer"
|
||||||
import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext";
|
import { ReplyMessageContext } from "../../context/ReplyingMessage/ReplyingMessageContext"
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
import { useLocalStorage } from "../../hooks/useLocalStorage";
|
import { useLocalStorage } from "../../hooks/useLocalStorage"
|
||||||
import toastError from "../../errors/toastError";
|
import toastError from "../../errors/toastError"
|
||||||
|
|
||||||
// import TicketsManager from "../../components/TicketsManager/";
|
// import TicketsManager from "../../components/TicketsManager/";
|
||||||
|
|
||||||
import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption";
|
import { TabTicketContext } from "../../context/TabTicketHeaderOption/TabTicketHeaderOption"
|
||||||
|
import ModalTemplate from "../ModalTemplate"
|
||||||
|
|
||||||
const Mp3Recorder = new MicRecorder({ bitRate: 128 });
|
import { render } from '@testing-library/react'
|
||||||
|
|
||||||
|
const Mp3Recorder = new MicRecorder({ bitRate: 128 })
|
||||||
|
|
||||||
const useStyles = makeStyles((theme) => ({
|
const useStyles = makeStyles((theme) => ({
|
||||||
mainWrapper: {
|
mainWrapper: {
|
||||||
|
@ -203,78 +206,83 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
}));
|
}))
|
||||||
|
|
||||||
const MessageInput = ({ ticketStatus }) => {
|
const MessageInput = ({ ticketStatus }) => {
|
||||||
|
|
||||||
const { tabOption, setTabOption } = useContext(TabTicketContext);
|
const { tabOption, setTabOption } = useContext(TabTicketContext)
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
const { ticketId } = useParams();
|
const { ticketId } = useParams()
|
||||||
|
|
||||||
const [medias, setMedias] = useState([]);
|
const [medias, setMedias] = useState([])
|
||||||
const [inputMessage, setInputMessage] = useState("");
|
const [inputMessage, setInputMessage] = useState("")
|
||||||
const [showEmoji, setShowEmoji] = useState(false);
|
const [showEmoji, setShowEmoji] = useState(false)
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false)
|
||||||
const [recording, setRecording] = useState(false);
|
const [recording, setRecording] = useState(false)
|
||||||
const [quickAnswers, setQuickAnswer] = useState([]);
|
const [quickAnswers, setQuickAnswer] = useState([])
|
||||||
const [typeBar, setTypeBar] = useState(false);
|
const [typeBar, setTypeBar] = useState(false)
|
||||||
const inputRef = useRef();
|
const inputRef = useRef()
|
||||||
const [anchorEl, setAnchorEl] = useState(null);
|
const [anchorEl, setAnchorEl] = useState(null)
|
||||||
const { setReplyingMessage, replyingMessage } = useContext(ReplyMessageContext);
|
const { setReplyingMessage, replyingMessage } = useContext(ReplyMessageContext)
|
||||||
const { user } = useContext(AuthContext);
|
const { user } = useContext(AuthContext)
|
||||||
|
const [templates, setTemplates] = useState(null)
|
||||||
|
const [params, setParams] = useState(null)
|
||||||
|
|
||||||
|
const [signMessage, setSignMessage] = useLocalStorage("signOption", true)
|
||||||
|
|
||||||
|
const isRun = useRef(false)
|
||||||
|
|
||||||
const [signMessage, setSignMessage] = useLocalStorage("signOption", true);
|
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
inputRef.current.focus();
|
inputRef.current.focus()
|
||||||
}, [replyingMessage]);
|
}, [replyingMessage])
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
inputRef.current.focus();
|
inputRef.current.focus()
|
||||||
return () => {
|
return () => {
|
||||||
setInputMessage("");
|
setInputMessage("")
|
||||||
setShowEmoji(false);
|
setShowEmoji(false)
|
||||||
setMedias([]);
|
setMedias([])
|
||||||
setReplyingMessage(null);
|
setReplyingMessage(null)
|
||||||
};
|
}
|
||||||
}, [ticketId, setReplyingMessage]);
|
}, [ticketId, setReplyingMessage])
|
||||||
|
|
||||||
const handleChangeInput = (e) => {
|
const handleChangeInput = (e) => {
|
||||||
setInputMessage(e.target.value);
|
setInputMessage(e.target.value)
|
||||||
handleLoadQuickAnswer(e.target.value);
|
handleLoadQuickAnswer(e.target.value)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleQuickAnswersClick = (value) => {
|
const handleQuickAnswersClick = (value) => {
|
||||||
setInputMessage(value);
|
setInputMessage(value)
|
||||||
setTypeBar(false);
|
setTypeBar(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleAddEmoji = (e) => {
|
const handleAddEmoji = (e) => {
|
||||||
let emoji = e.native;
|
let emoji = e.native
|
||||||
setInputMessage((prevState) => prevState + emoji);
|
setInputMessage((prevState) => prevState + emoji)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleChangeMedias = (e) => {
|
const handleChangeMedias = (e) => {
|
||||||
if (!e.target.files) {
|
if (!e.target.files) {
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const selectedMedias = Array.from(e.target.files);
|
const selectedMedias = Array.from(e.target.files)
|
||||||
setMedias(selectedMedias);
|
setMedias(selectedMedias)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleInputPaste = (e) => {
|
const handleInputPaste = (e) => {
|
||||||
if (e.clipboardData.files[0]) {
|
if (e.clipboardData.files[0]) {
|
||||||
|
|
||||||
console.log('clipboardData: ', e.clipboardData.files[0])
|
console.log('clipboardData: ', e.clipboardData.files[0])
|
||||||
setMedias([e.clipboardData.files[0]]);
|
setMedias([e.clipboardData.files[0]])
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleUploadMedia = async (e) => {
|
const handleUploadMedia = async (e) => {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
e.preventDefault();
|
e.preventDefault()
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -282,28 +290,32 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
setTabOption('open')
|
setTabOption('open')
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
formData.append("fromMe", true);
|
formData.append("fromMe", true)
|
||||||
medias.forEach((media) => {
|
medias.forEach((media) => {
|
||||||
formData.append("medias", media);
|
formData.append("medias", media)
|
||||||
formData.append("body", media.name);
|
formData.append("body", media.name)
|
||||||
});
|
})
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await api.post(`/messages/${ticketId}`, formData);
|
const { data } = await api.post(`/messages/${ticketId}`, formData)
|
||||||
|
|
||||||
|
console.log('DATA FROM SEND MESSAGE MEDIA: ', data)
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
setMedias([]);
|
setMedias([])
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSendMessage = async () => {
|
const handleSendMessage = async (templateParams = null) => {
|
||||||
if (inputMessage.trim() === "") return;
|
|
||||||
setLoading(true);
|
|
||||||
|
|
||||||
|
console.log('templateParams: ', templateParams, ' | inputMessage: ', inputMessage)
|
||||||
|
|
||||||
|
if (inputMessage.trim() === "") return
|
||||||
|
setLoading(true)
|
||||||
|
|
||||||
if (tabOption === 'search') {
|
if (tabOption === 'search') {
|
||||||
setTabOption('open')
|
setTabOption('open')
|
||||||
|
@ -313,61 +325,119 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
read: 1,
|
read: 1,
|
||||||
fromMe: true,
|
fromMe: true,
|
||||||
mediaUrl: "",
|
mediaUrl: "",
|
||||||
body: signMessage
|
body: (signMessage && !templateParams)
|
||||||
? `*${user?.name}:*\n${inputMessage.trim()}`
|
? `*${user?.name}:*\n${inputMessage.trim()}`
|
||||||
: inputMessage.trim(),
|
: inputMessage.trim(),
|
||||||
quotedMsg: replyingMessage,
|
quotedMsg: replyingMessage,
|
||||||
};
|
params: templateParams
|
||||||
|
}
|
||||||
try {
|
try {
|
||||||
|
|
||||||
// console.log('message: ', message)
|
const { data } = await api.post(`/messages/${ticketId}`, message)
|
||||||
await api.post(`/messages/${ticketId}`, message);
|
setParams(null)
|
||||||
|
if (data && data?.data && Array.isArray(data.data)) {
|
||||||
|
setTemplates(data.data)
|
||||||
|
}
|
||||||
|
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
setInputMessage("");
|
setInputMessage("")
|
||||||
setShowEmoji(false);
|
setShowEmoji(false)
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
setReplyingMessage(null);
|
setReplyingMessage(null)
|
||||||
};
|
}
|
||||||
|
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (!params) return
|
||||||
|
|
||||||
|
const body_params = params.find(p => p?.type === 'BODY')
|
||||||
|
|
||||||
|
let { text } = body_params
|
||||||
|
|
||||||
|
console.log('PARAMS FROM MESSAGE INPUT: ', params, ' | text: ', text)
|
||||||
|
|
||||||
|
let body = text.match(/{{\d+}}/g)
|
||||||
|
|
||||||
|
if (body && body.length > 0) {
|
||||||
|
|
||||||
|
const { parameters } = body_params
|
||||||
|
|
||||||
|
for (const key in parameters) {
|
||||||
|
if (!isNaN(key)) {
|
||||||
|
const { index, text: body_text } = parameters[key]
|
||||||
|
text = text.replace(`{{${index}}}`, body_text)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
console.log('NEW TEXT: ', text)
|
||||||
|
setInputMessage(text)
|
||||||
|
|
||||||
|
}, [params])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (params) {
|
||||||
|
handleSendMessage(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [inputMessage, params])
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
if (!templates) return
|
||||||
|
|
||||||
|
return render(<ModalTemplate
|
||||||
|
modal_header={'Escolha um template para iniciar o Atendimento'}
|
||||||
|
func={setParams}
|
||||||
|
templates={templates.map(({ id, name, components, language, }) => {
|
||||||
|
return { id, name, components, language, }
|
||||||
|
})}
|
||||||
|
ticketId={ticketId}
|
||||||
|
/>)
|
||||||
|
|
||||||
|
}, [templates])
|
||||||
|
|
||||||
const handleStartRecording = async () => {
|
const handleStartRecording = async () => {
|
||||||
|
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
await navigator.mediaDevices.getUserMedia({ audio: true });
|
await navigator.mediaDevices.getUserMedia({ audio: true })
|
||||||
await Mp3Recorder.start();
|
await Mp3Recorder.start()
|
||||||
setRecording(true);
|
setRecording(true)
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleLoadQuickAnswer = async (value) => {
|
const handleLoadQuickAnswer = async (value) => {
|
||||||
if (value && value.indexOf("/") === 0) {
|
if (value && value.indexOf("/") === 0) {
|
||||||
try {
|
try {
|
||||||
const { data } = await api.get("/quickAnswers/", {
|
const { data } = await api.get("/quickAnswers/", {
|
||||||
params: { searchParam: inputMessage.substring(1) },
|
params: { searchParam: inputMessage.substring(1) },
|
||||||
});
|
})
|
||||||
setQuickAnswer(data.quickAnswers);
|
setQuickAnswer(data.quickAnswers)
|
||||||
if (data.quickAnswers.length > 0) {
|
if (data.quickAnswers.length > 0) {
|
||||||
setTypeBar(true);
|
setTypeBar(true)
|
||||||
} else {
|
} else {
|
||||||
setTypeBar(false);
|
setTypeBar(false)
|
||||||
}
|
}
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setTypeBar(false);
|
setTypeBar(false)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
setTypeBar(false);
|
setTypeBar(false)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleUploadAudio = async () => {
|
const handleUploadAudio = async () => {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -376,45 +446,45 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
const [, blob] = await Mp3Recorder.stop().getMp3();
|
const [, blob] = await Mp3Recorder.stop().getMp3()
|
||||||
if (blob.size < 10000) {
|
if (blob.size < 10000) {
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
setRecording(false);
|
setRecording(false)
|
||||||
return;
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const formData = new FormData();
|
const formData = new FormData()
|
||||||
const filename = `${new Date().getTime()}.mp3`;
|
const filename = `${new Date().getTime()}.mp3`
|
||||||
formData.append("medias", blob, filename);
|
formData.append("medias", blob, filename)
|
||||||
formData.append("body", filename);
|
formData.append("body", filename)
|
||||||
formData.append("fromMe", true);
|
formData.append("fromMe", true)
|
||||||
formData.append("mic_audio", true)
|
formData.append("mic_audio", true)
|
||||||
|
|
||||||
await api.post(`/messages/${ticketId}`, formData);
|
await api.post(`/messages/${ticketId}`, formData)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
|
|
||||||
setRecording(false);
|
setRecording(false)
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleCancelAudio = async () => {
|
const handleCancelAudio = async () => {
|
||||||
try {
|
try {
|
||||||
await Mp3Recorder.stop().getMp3();
|
await Mp3Recorder.stop().getMp3()
|
||||||
setRecording(false);
|
setRecording(false)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleOpenMenuClick = (event) => {
|
const handleOpenMenuClick = (event) => {
|
||||||
setAnchorEl(event.currentTarget);
|
setAnchorEl(event.currentTarget)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleMenuItemClick = (event) => {
|
const handleMenuItemClick = (event) => {
|
||||||
setAnchorEl(null);
|
setAnchorEl(null)
|
||||||
};
|
}
|
||||||
|
|
||||||
const renderReplyingMessage = (message) => {
|
const renderReplyingMessage = (message) => {
|
||||||
return (
|
return (
|
||||||
|
@ -443,8 +513,8 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
<ClearIcon className={classes.sendMessageIcons} />
|
<ClearIcon className={classes.sendMessageIcons} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</div>
|
</div>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
if (medias.length > 0)
|
if (medias.length > 0)
|
||||||
return (
|
return (
|
||||||
|
@ -476,7 +546,7 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
<SendIcon className={classes.sendMessageIcons} />
|
<SendIcon className={classes.sendMessageIcons} />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
)
|
||||||
else {
|
else {
|
||||||
return (
|
return (
|
||||||
<Paper square elevation={0} className={classes.mainWrapper}>
|
<Paper square elevation={0} className={classes.mainWrapper}>
|
||||||
|
@ -530,7 +600,7 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
size="small"
|
size="small"
|
||||||
checked={signMessage}
|
checked={signMessage}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSignMessage(e.target.checked);
|
setSignMessage(e.target.checked)
|
||||||
}}
|
}}
|
||||||
name="showAllTickets"
|
name="showAllTickets"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -592,7 +662,7 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
size="small"
|
size="small"
|
||||||
checked={signMessage}
|
checked={signMessage}
|
||||||
onChange={(e) => {
|
onChange={(e) => {
|
||||||
setSignMessage(e.target.checked);
|
setSignMessage(e.target.checked)
|
||||||
}}
|
}}
|
||||||
name="showAllTickets"
|
name="showAllTickets"
|
||||||
color="primary"
|
color="primary"
|
||||||
|
@ -605,8 +675,8 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
<div className={classes.messageInputWrapper}>
|
<div className={classes.messageInputWrapper}>
|
||||||
<InputBase
|
<InputBase
|
||||||
inputRef={(input) => {
|
inputRef={(input) => {
|
||||||
input && input.focus();
|
input && input.focus()
|
||||||
input && (inputRef.current = input);
|
input && (inputRef.current = input)
|
||||||
}}
|
}}
|
||||||
className={classes.messageInput}
|
className={classes.messageInput}
|
||||||
placeholder={
|
placeholder={
|
||||||
|
@ -621,12 +691,12 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
onChange={handleChangeInput}
|
onChange={handleChangeInput}
|
||||||
disabled={recording || loading || ticketStatus !== "open"}
|
disabled={recording || loading || ticketStatus !== "open"}
|
||||||
onPaste={(e) => {
|
onPaste={(e) => {
|
||||||
ticketStatus === "open" && handleInputPaste(e);
|
ticketStatus === "open" && handleInputPaste(e)
|
||||||
}}
|
}}
|
||||||
onKeyPress={(e) => {
|
onKeyPress={(e) => {
|
||||||
if (loading || e.shiftKey) return;
|
if (loading || e.shiftKey) return
|
||||||
else if (e.key === "Enter") {
|
else if (e.key === "Enter") {
|
||||||
handleSendMessage();
|
handleSendMessage()
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
@ -643,7 +713,7 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
{`${value.shortcut} - ${value.message}`}
|
{`${value.shortcut} - ${value.message}`}
|
||||||
</a>
|
</a>
|
||||||
</li>
|
</li>
|
||||||
);
|
)
|
||||||
})}
|
})}
|
||||||
</ul>
|
</ul>
|
||||||
) : (
|
) : (
|
||||||
|
@ -699,8 +769,8 @@ const MessageInput = ({ ticketStatus }) => {
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
</Paper>
|
</Paper>
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
export default MessageInput;
|
export default MessageInput
|
||||||
|
|
|
@ -0,0 +1,242 @@
|
||||||
|
|
||||||
|
import React, { useState, useEffect, useRef, } from 'react'
|
||||||
|
import Button from '@mui/material/Button'
|
||||||
|
import Dialog from '@mui/material/Dialog'
|
||||||
|
import DialogActions from '@mui/material/DialogActions'
|
||||||
|
import DialogContent from '@mui/material/DialogContent'
|
||||||
|
import DialogContentText from '@mui/material/DialogContentText'
|
||||||
|
import DialogTitle from '@mui/material/DialogTitle'
|
||||||
|
import SelectField from "../Report/SelectField"
|
||||||
|
|
||||||
|
import TextField from '@mui/material/TextField'
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
const ModalTemplate = ({ templates, modal_header, func }) => {
|
||||||
|
|
||||||
|
templates = [{}, ...templates]
|
||||||
|
|
||||||
|
const [open, setOpen] = useState(true)
|
||||||
|
const [scroll, /*setScroll*/] = useState('body')
|
||||||
|
const [templateId, setTemplateId] = useState(null)
|
||||||
|
const [templateComponents, setTemplateComponents] = useState(null)
|
||||||
|
const [language, setLanguage] = useState(null)
|
||||||
|
const [params, setParams] = useState([])
|
||||||
|
|
||||||
|
const handleCancel = (event, reason) => {
|
||||||
|
|
||||||
|
if (reason && reason === "backdropClick")
|
||||||
|
return
|
||||||
|
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleChatEnd = () => {
|
||||||
|
|
||||||
|
console.log('PARAMS TO SEND TO MESSAGE INPUT: ', params)
|
||||||
|
func(params)
|
||||||
|
setOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
|
const descriptionElementRef = useRef(null)
|
||||||
|
useEffect(() => {
|
||||||
|
if (open) {
|
||||||
|
const { current: descriptionElement } = descriptionElementRef
|
||||||
|
if (descriptionElement !== null) {
|
||||||
|
descriptionElement.focus()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}, [open])
|
||||||
|
|
||||||
|
|
||||||
|
// Get from child 1
|
||||||
|
const changedTextFieldSelect = (data) => {
|
||||||
|
setTemplateId(data)
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
|
||||||
|
const index = templates.findIndex(t => t.id === templateId)
|
||||||
|
setParams([])
|
||||||
|
if (index !== -1) {
|
||||||
|
|
||||||
|
const { components, language, name } = templates[index]
|
||||||
|
|
||||||
|
setParams((params) => [...params, { 'template_name': name }])
|
||||||
|
|
||||||
|
const buttons = components.find(c => c.type === 'BUTTONS')
|
||||||
|
|
||||||
|
if (buttons) {
|
||||||
|
handleButtons(buttons?.buttons)
|
||||||
|
}
|
||||||
|
|
||||||
|
setTemplateComponents(components)
|
||||||
|
setLanguage(language)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setTemplateComponents(null)
|
||||||
|
setLanguage(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
}, [templateId])
|
||||||
|
|
||||||
|
const handleButtons = (buttons) => {
|
||||||
|
|
||||||
|
let buttonsParams = {
|
||||||
|
type: 'BUTTONS',
|
||||||
|
parameters: []
|
||||||
|
}
|
||||||
|
|
||||||
|
if (buttons && buttons.length > 0) {
|
||||||
|
for (let i in buttons) {
|
||||||
|
const { text, type: sub_type } = buttons[i]
|
||||||
|
buttonsParams.parameters.push({ sub_type, text, index: i, })
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
setParams((params) => [...params, buttonsParams])
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTextChange = (value, index, type, text, language,) => {
|
||||||
|
|
||||||
|
if (!params) return
|
||||||
|
|
||||||
|
setParams((params) => {
|
||||||
|
|
||||||
|
const _index = params.findIndex(({ type }) => type === 'BODY')
|
||||||
|
|
||||||
|
if (_index !== -1) {
|
||||||
|
|
||||||
|
const indexParameter = params[_index].parameters.findIndex((param) => param.index === index)
|
||||||
|
|
||||||
|
if (indexParameter !== -1) {
|
||||||
|
params[_index].parameters[indexParameter] = { ...params[_index].parameters[indexParameter], type: 'text', text: value, index }
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
params[_index].parameters = [...params[_index].parameters, { type: 'text', text: value, index }]
|
||||||
|
}
|
||||||
|
|
||||||
|
return params
|
||||||
|
|
||||||
|
}
|
||||||
|
return [...params, { type, text, language, parameters: [{ type: 'text', text: value, index }] }]
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
console.log('---------> PARAMS: ', params)
|
||||||
|
}, [params])
|
||||||
|
|
||||||
|
const dinamicTextField = (replicateItems, func, type, text, language) => {
|
||||||
|
|
||||||
|
let textFields = Array.from({ length: replicateItems }, (_, index) => index)
|
||||||
|
|
||||||
|
return textFields.map((t) => {
|
||||||
|
return <TextField
|
||||||
|
key={t}
|
||||||
|
label={`{{${t + 1}}}`}
|
||||||
|
variant="outlined"
|
||||||
|
style={{ margin: '4px', }} // Adjust the height as needed
|
||||||
|
onChange={(e) => func(e.target.value, (t + 1), type, text, language)}
|
||||||
|
/>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
return (
|
||||||
|
|
||||||
|
<Dialog
|
||||||
|
open={open}
|
||||||
|
onClose={handleCancel}
|
||||||
|
// fullWidth={true}
|
||||||
|
// maxWidth={true}
|
||||||
|
disableEscapeKeyDown
|
||||||
|
|
||||||
|
scroll={scroll}
|
||||||
|
aria-labelledby="scroll-dialog-title"
|
||||||
|
aria-describedby="scroll-dialog-description"
|
||||||
|
>
|
||||||
|
|
||||||
|
<DialogTitle id="scroll-dialog-title">{modal_header}</DialogTitle>
|
||||||
|
<DialogContent dividers={scroll === 'body'}>
|
||||||
|
|
||||||
|
<DialogContentText
|
||||||
|
id="scroll-dialog-description"
|
||||||
|
ref={descriptionElementRef}
|
||||||
|
tabIndex={-1}
|
||||||
|
>
|
||||||
|
|
||||||
|
</DialogContentText>
|
||||||
|
|
||||||
|
<>
|
||||||
|
|
||||||
|
<SelectField func={changedTextFieldSelect}
|
||||||
|
emptyField={false}
|
||||||
|
textBoxFieldSelected={'1'}
|
||||||
|
header={'Selecione um template'}
|
||||||
|
currencies={templates.map((template,) => {
|
||||||
|
const { name, id } = template
|
||||||
|
return { 'value': id, 'label': name }
|
||||||
|
})} />
|
||||||
|
|
||||||
|
{templateComponents &&
|
||||||
|
templateComponents.map((components,) => {
|
||||||
|
const { type, format, text, buttons } = components
|
||||||
|
|
||||||
|
let body_params = 0
|
||||||
|
|
||||||
|
if (type === 'BODY') {
|
||||||
|
body_params = text.match(/{{\d+}}/g)
|
||||||
|
}
|
||||||
|
|
||||||
|
const titleCss = {
|
||||||
|
margin: 0,
|
||||||
|
fontSize: 12
|
||||||
|
}
|
||||||
|
const valueCss = { margin: 0, padding: 0, 'marginBottom': '15px', fontSize: 12 }
|
||||||
|
const valueCssButton = { margin: 0, padding: 0, fontSize: 11 }
|
||||||
|
|
||||||
|
return <div key={text}>
|
||||||
|
{type && <strong style={titleCss}>{type}</strong>}
|
||||||
|
{format && format !== 'TEXT' && <p style={valueCss}>TYPE {format}</p>}
|
||||||
|
{text &&
|
||||||
|
<div style={{ margin: 0, padding: 0, 'marginBottom': '15px' }}>
|
||||||
|
<p style={{ margin: 0, padding: 0, fontSize: 12 }}>{text}</p>
|
||||||
|
{type && (type === 'BODY') && dinamicTextField(body_params.length, handleTextChange, type, text, language)}
|
||||||
|
</div>}
|
||||||
|
{buttons && <div>{buttons.map((b) => {
|
||||||
|
const { type, text, url } = b
|
||||||
|
return <div style={{ margin: 0, padding: 0, 'marginBottom': '10px' }}>
|
||||||
|
{type && <p style={valueCssButton}>TYPE {type}</p>}
|
||||||
|
{text && <p style={valueCssButton}>{text}</p>}
|
||||||
|
{url && <p style={valueCssButton}>{url}</p>}
|
||||||
|
</div>
|
||||||
|
})} </div>}
|
||||||
|
|
||||||
|
</div>
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</>
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
</DialogContent>
|
||||||
|
|
||||||
|
|
||||||
|
<DialogActions>
|
||||||
|
<div style={{ marginRight: '50px' }}>
|
||||||
|
<Button onClick={handleCancel}>Cancelar</Button>
|
||||||
|
</div>
|
||||||
|
<Button onClick={handleChatEnd}>Ok</Button>
|
||||||
|
</DialogActions>
|
||||||
|
</Dialog >
|
||||||
|
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ModalTemplate
|
|
@ -1,27 +1,27 @@
|
||||||
import React, { useState, useEffect, useRef, useContext } from "react";
|
import React, { useState, useEffect, useRef, useContext } from "react"
|
||||||
|
|
||||||
import { useHistory, useParams } from "react-router-dom";
|
import { useHistory, useParams } from "react-router-dom"
|
||||||
import { parseISO, format, isSameDay } from "date-fns";
|
import { parseISO, format, isSameDay } from "date-fns"
|
||||||
import clsx from "clsx";
|
import clsx from "clsx"
|
||||||
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles"
|
||||||
import { green } from "@material-ui/core/colors";
|
import { green } from "@material-ui/core/colors"
|
||||||
import ListItem from "@material-ui/core/ListItem";
|
import ListItem from "@material-ui/core/ListItem"
|
||||||
import ListItemText from "@material-ui/core/ListItemText";
|
import ListItemText from "@material-ui/core/ListItemText"
|
||||||
import ListItemAvatar from "@material-ui/core/ListItemAvatar";
|
import ListItemAvatar from "@material-ui/core/ListItemAvatar"
|
||||||
import Typography from "@material-ui/core/Typography";
|
import Typography from "@material-ui/core/Typography"
|
||||||
import Avatar from "@material-ui/core/Avatar";
|
import Avatar from "@material-ui/core/Avatar"
|
||||||
import Divider from "@material-ui/core/Divider";
|
import Divider from "@material-ui/core/Divider"
|
||||||
import Badge from "@material-ui/core/Badge";
|
import Badge from "@material-ui/core/Badge"
|
||||||
|
|
||||||
import { i18n } from "../../translate/i18n";
|
import { i18n } from "../../translate/i18n"
|
||||||
|
|
||||||
import api from "../../services/api";
|
import api from "../../services/api"
|
||||||
import ButtonWithSpinner from "../ButtonWithSpinner";
|
import ButtonWithSpinner from "../ButtonWithSpinner"
|
||||||
import MarkdownWrapper from "../MarkdownWrapper";
|
import MarkdownWrapper from "../MarkdownWrapper"
|
||||||
import { Tooltip } from "@material-ui/core";
|
import { Tooltip } from "@material-ui/core"
|
||||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
import { AuthContext } from "../../context/Auth/AuthContext"
|
||||||
import toastError from "../../errors/toastError";
|
import toastError from "../../errors/toastError"
|
||||||
|
|
||||||
const useStyles = makeStyles(theme => ({
|
const useStyles = makeStyles(theme => ({
|
||||||
ticket: {
|
ticket: {
|
||||||
|
@ -99,46 +99,46 @@ const useStyles = makeStyles(theme => ({
|
||||||
top: "0%",
|
top: "0%",
|
||||||
left: "0%",
|
left: "0%",
|
||||||
},
|
},
|
||||||
}));
|
}))
|
||||||
|
|
||||||
const TicketListItem = ({ ticket }) => {
|
const TicketListItem = ({ ticket }) => {
|
||||||
const classes = useStyles();
|
const classes = useStyles()
|
||||||
const history = useHistory();
|
const history = useHistory()
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false)
|
||||||
const { ticketId } = useParams();
|
const { ticketId } = useParams()
|
||||||
const isMounted = useRef(true);
|
const isMounted = useRef(true)
|
||||||
const { user } = useContext(AuthContext);
|
const { user } = useContext(AuthContext)
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
return () => {
|
return () => {
|
||||||
isMounted.current = false;
|
isMounted.current = false
|
||||||
};
|
}
|
||||||
}, []);
|
}, [])
|
||||||
|
|
||||||
const handleAcepptTicket = async id => {
|
const handleAcepptTicket = async id => {
|
||||||
setLoading(true);
|
setLoading(true)
|
||||||
try {
|
try {
|
||||||
|
|
||||||
await api.put(`/tickets/${id}`, {
|
await api.put(`/tickets/${id}`, {
|
||||||
status: "open",
|
status: "open",
|
||||||
userId: user?.id,
|
userId: user?.id,
|
||||||
});
|
})
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
toastError(err);
|
toastError(err)
|
||||||
}
|
}
|
||||||
if (isMounted.current) {
|
if (isMounted.current) {
|
||||||
setLoading(false);
|
setLoading(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
history.push(`/tickets/${id}`);
|
history.push(`/tickets/${id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
const handleSelectTicket = id => {
|
const handleSelectTicket = id => {
|
||||||
history.push(`/tickets/${id}`);
|
history.push(`/tickets/${id}`)
|
||||||
};
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<React.Fragment key={ticket.id}>
|
<React.Fragment key={ticket.id}>
|
||||||
|
@ -146,8 +146,8 @@ const TicketListItem = ({ ticket }) => {
|
||||||
dense
|
dense
|
||||||
button
|
button
|
||||||
onClick={e => {
|
onClick={e => {
|
||||||
if (ticket.status === "pending") return;
|
if (ticket.status === "pending") return
|
||||||
handleSelectTicket(ticket.id);
|
handleSelectTicket(ticket.id)
|
||||||
}}
|
}}
|
||||||
selected={ticketId && +ticketId === ticket.id}
|
selected={ticketId && +ticketId === ticket.id}
|
||||||
className={clsx(classes.ticket, {
|
className={clsx(classes.ticket, {
|
||||||
|
@ -193,6 +193,7 @@ const TicketListItem = ({ ticket }) => {
|
||||||
variant="body2"
|
variant="body2"
|
||||||
color="textSecondary"
|
color="textSecondary"
|
||||||
>
|
>
|
||||||
|
{ticket?.phoneNumberId && <span style={{ 'fontWeight': 'bold' }}>Oficial</span>}{" "}
|
||||||
{isSameDay(parseISO(ticket.updatedAt), new Date()) ? (
|
{isSameDay(parseISO(ticket.updatedAt), new Date()) ? (
|
||||||
<>{format(parseISO(ticket.updatedAt), "HH:mm")}</>
|
<>{format(parseISO(ticket.updatedAt), "HH:mm")}</>
|
||||||
) : (
|
) : (
|
||||||
|
@ -251,7 +252,7 @@ const TicketListItem = ({ ticket }) => {
|
||||||
</ListItem>
|
</ListItem>
|
||||||
<Divider variant="inset" component="li" />
|
<Divider variant="inset" component="li" />
|
||||||
</React.Fragment>
|
</React.Fragment>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default TicketListItem;
|
export default TicketListItem
|
||||||
|
|
Loading…
Reference in New Issue