add quick answers

This commit is contained in:
ertprs
2021-09-05 22:30:57 -03:00
parent 85fe927385
commit 258b5db4a1
19 changed files with 2853 additions and 1789 deletions

View File

@@ -20,6 +20,7 @@ import MicIcon from "@material-ui/icons/Mic";
import CheckCircleOutlineIcon from "@material-ui/icons/CheckCircleOutline";
import HighlightOffIcon from "@material-ui/icons/HighlightOff";
import { FormControlLabel, Switch } from "@material-ui/core";
import ClickAwayListener from "@material-ui/core/ClickAwayListener";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
@@ -31,483 +32,555 @@ import toastError from "../../errors/toastError";
const Mp3Recorder = new MicRecorder({ bitRate: 128 });
const useStyles = makeStyles(theme => ({
mainWrapper: {
background: "#eee",
display: "flex",
flexDirection: "column",
alignItems: "center",
borderTop: "1px solid rgba(0, 0, 0, 0.12)",
},
const useStyles = makeStyles((theme) => ({
mainWrapper: {
background: "#eee",
display: "flex",
flexDirection: "column",
alignItems: "center",
borderTop: "1px solid rgba(0, 0, 0, 0.12)",
},
newMessageBox: {
background: "#eee",
width: "100%",
display: "flex",
padding: "7px",
alignItems: "center",
},
newMessageBox: {
background: "#eee",
width: "100%",
display: "flex",
padding: "7px",
alignItems: "center",
},
messageInputWrapper: {
padding: 6,
marginRight: 7,
background: "#fff",
display: "flex",
borderRadius: 20,
flex: 1,
},
messageInputWrapper: {
padding: 6,
marginRight: 7,
background: "#fff",
display: "flex",
borderRadius: 20,
flex: 1,
},
messageInput: {
paddingLeft: 10,
flex: 1,
border: "none",
},
messageInput: {
paddingLeft: 10,
flex: 1,
border: "none",
},
sendMessageIcons: {
color: "grey",
},
sendMessageIcons: {
color: "grey",
},
uploadInput: {
display: "none",
},
uploadInput: {
display: "none",
},
viewMediaInputWrapper: {
display: "flex",
padding: "10px 13px",
position: "relative",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: "#eee",
borderTop: "1px solid rgba(0, 0, 0, 0.12)",
},
viewMediaInputWrapper: {
display: "flex",
padding: "10px 13px",
position: "relative",
justifyContent: "space-between",
alignItems: "center",
backgroundColor: "#eee",
borderTop: "1px solid rgba(0, 0, 0, 0.12)",
},
emojiBox: {
position: "absolute",
bottom: 63,
width: 40,
borderTop: "1px solid #e8e8e8",
},
emojiBox: {
position: "absolute",
bottom: 63,
width: 40,
borderTop: "1px solid #e8e8e8",
},
circleLoading: {
color: green[500],
opacity: "70%",
position: "absolute",
top: "20%",
left: "50%",
marginLeft: -12,
},
circleLoading: {
color: green[500],
opacity: "70%",
position: "absolute",
top: "20%",
left: "50%",
marginLeft: -12,
},
audioLoading: {
color: green[500],
opacity: "70%",
},
audioLoading: {
color: green[500],
opacity: "70%",
},
recorderWrapper: {
display: "flex",
alignItems: "center",
alignContent: "middle",
},
recorderWrapper: {
display: "flex",
alignItems: "center",
alignContent: "middle",
},
cancelAudioIcon: {
color: "red",
},
cancelAudioIcon: {
color: "red",
},
sendAudioIcon: {
color: "green",
},
sendAudioIcon: {
color: "green",
},
replyginMsgWrapper: {
display: "flex",
width: "100%",
alignItems: "center",
justifyContent: "center",
paddingTop: 8,
paddingLeft: 73,
paddingRight: 7,
},
replyginMsgWrapper: {
display: "flex",
width: "100%",
alignItems: "center",
justifyContent: "center",
paddingTop: 8,
paddingLeft: 73,
paddingRight: 7,
},
replyginMsgContainer: {
flex: 1,
marginRight: 5,
overflowY: "hidden",
backgroundColor: "rgba(0, 0, 0, 0.05)",
borderRadius: "7.5px",
display: "flex",
position: "relative",
},
replyginMsgContainer: {
flex: 1,
marginRight: 5,
overflowY: "hidden",
backgroundColor: "rgba(0, 0, 0, 0.05)",
borderRadius: "7.5px",
display: "flex",
position: "relative",
},
replyginMsgBody: {
padding: 10,
height: "auto",
display: "block",
whiteSpace: "pre-wrap",
overflow: "hidden",
},
replyginMsgBody: {
padding: 10,
height: "auto",
display: "block",
whiteSpace: "pre-wrap",
overflow: "hidden",
},
replyginContactMsgSideColor: {
flex: "none",
width: "4px",
backgroundColor: "#35cd96",
},
replyginContactMsgSideColor: {
flex: "none",
width: "4px",
backgroundColor: "#35cd96",
},
replyginSelfMsgSideColor: {
flex: "none",
width: "4px",
backgroundColor: "#6bcbef",
},
replyginSelfMsgSideColor: {
flex: "none",
width: "4px",
backgroundColor: "#6bcbef",
},
messageContactName: {
display: "flex",
color: "#6bcbef",
fontWeight: 500,
},
messageContactName: {
display: "flex",
color: "#6bcbef",
fontWeight: 500,
},
messageQuickAnswersWrapper: {
margin: 0,
position: "absolute",
bottom: "50px",
background: "#ffffff",
padding: "2px",
border: "1px solid #CCC",
left: 0,
width: "100%",
"& li": {
listStyle: "none",
"& a": {
display: "block",
padding: "8px",
textOverflow: "ellipsis",
overflow: "hidden",
maxHeight: "32px",
"&:hover": {
background: "#F1F1F1",
cursor: "pointer",
},
},
},
},
}));
const MessageInput = ({ ticketStatus }) => {
const classes = useStyles();
const { ticketId } = useParams();
const classes = useStyles();
const { ticketId } = useParams();
const [medias, setMedias] = useState([]);
const [inputMessage, setInputMessage] = useState("");
const [showEmoji, setShowEmoji] = useState(false);
const [loading, setLoading] = useState(false);
const [recording, setRecording] = useState(false);
const inputRef = useRef();
const { setReplyingMessage, replyingMessage } = useContext(
ReplyMessageContext
);
const { user } = useContext(AuthContext);
const [medias, setMedias] = useState([]);
const [inputMessage, setInputMessage] = useState("");
const [showEmoji, setShowEmoji] = useState(false);
const [loading, setLoading] = useState(false);
const [recording, setRecording] = useState(false);
const [quickAnswers, setQuickAnswer] = useState([]);
const [typeBar, setTypeBar] = useState(false);
const inputRef = useRef();
const { setReplyingMessage, replyingMessage } =
useContext(ReplyMessageContext);
const { user } = useContext(AuthContext);
const [signMessage, setSignMessage] = useLocalStorage("signOption", true);
const [signMessage, setSignMessage] = useLocalStorage("signOption", true);
useEffect(() => {
inputRef.current.focus();
}, [replyingMessage]);
useEffect(() => {
inputRef.current.focus();
}, [replyingMessage]);
useEffect(() => {
inputRef.current.focus();
return () => {
setInputMessage("");
setShowEmoji(false);
setMedias([]);
setReplyingMessage(null);
};
}, [ticketId, setReplyingMessage]);
useEffect(() => {
inputRef.current.focus();
return () => {
setInputMessage("");
setShowEmoji(false);
setMedias([]);
setReplyingMessage(null);
};
}, [ticketId, setReplyingMessage]);
const handleChangeInput = e => {
setInputMessage(e.target.value);
};
const handleChangeInput = (e) => {
setInputMessage(e.target.value);
handleLoadQuickAnswer(e.target.value);
};
const handleAddEmoji = e => {
let emoji = e.native;
setInputMessage(prevState => prevState + emoji);
};
const handleQuickAnswersClick = (value) => {
setInputMessage(value);
setTypeBar(false);
};
const handleChangeMedias = e => {
if (!e.target.files) {
return;
}
const handleAddEmoji = (e) => {
let emoji = e.native;
setInputMessage((prevState) => prevState + emoji);
};
const selectedMedias = Array.from(e.target.files);
setMedias(selectedMedias);
};
const handleChangeMedias = (e) => {
if (!e.target.files) {
return;
}
const handleInputPaste = e => {
if (e.clipboardData.files[0]) {
setMedias([e.clipboardData.files[0]]);
}
};
const selectedMedias = Array.from(e.target.files);
setMedias(selectedMedias);
};
const handleUploadMedia = async e => {
setLoading(true);
e.preventDefault();
const handleInputPaste = (e) => {
if (e.clipboardData.files[0]) {
setMedias([e.clipboardData.files[0]]);
}
};
const formData = new FormData();
formData.append("fromMe", true);
medias.forEach(media => {
formData.append("medias", media);
formData.append("body", media.name);
});
const handleUploadMedia = async (e) => {
setLoading(true);
e.preventDefault();
try {
await api.post(`/messages/${ticketId}`, formData);
} catch (err) {
toastError(err);
}
const formData = new FormData();
formData.append("fromMe", true);
medias.forEach((media) => {
formData.append("medias", media);
formData.append("body", media.name);
});
setLoading(false);
setMedias([]);
};
try {
await api.post(`/messages/${ticketId}`, formData);
} catch (err) {
toastError(err);
}
const handleSendMessage = async () => {
if (inputMessage.trim() === "") return;
setLoading(true);
setLoading(false);
setMedias([]);
};
const message = {
read: 1,
fromMe: true,
mediaUrl: "",
body: signMessage
? `*${user?.name}:*\n${inputMessage.trim()}`
: inputMessage.trim(),
quotedMsg: replyingMessage,
};
try {
await api.post(`/messages/${ticketId}`, message);
} catch (err) {
toastError(err);
}
const handleSendMessage = async () => {
if (inputMessage.trim() === "") return;
setLoading(true);
setInputMessage("");
setShowEmoji(false);
setLoading(false);
setReplyingMessage(null);
};
const message = {
read: 1,
fromMe: true,
mediaUrl: "",
body: signMessage
? `*${user?.name}:*\n${inputMessage.trim()}`
: inputMessage.trim(),
quotedMsg: replyingMessage,
};
try {
await api.post(`/messages/${ticketId}`, message);
} catch (err) {
toastError(err);
}
const handleStartRecording = async () => {
setLoading(true);
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
await Mp3Recorder.start();
setRecording(true);
setLoading(false);
} catch (err) {
toastError(err);
setLoading(false);
}
};
setInputMessage("");
setShowEmoji(false);
setLoading(false);
setReplyingMessage(null);
};
const handleUploadAudio = async () => {
setLoading(true);
try {
const [, blob] = await Mp3Recorder.stop().getMp3();
if (blob.size < 10000) {
setLoading(false);
setRecording(false);
return;
}
const handleStartRecording = async () => {
setLoading(true);
try {
await navigator.mediaDevices.getUserMedia({ audio: true });
await Mp3Recorder.start();
setRecording(true);
setLoading(false);
} catch (err) {
toastError(err);
setLoading(false);
}
};
const formData = new FormData();
const filename = `${new Date().getTime()}.mp3`;
formData.append("medias", blob, filename);
formData.append("body", filename);
formData.append("fromMe", true);
const handleLoadQuickAnswer = async (value) => {
if (value && value.indexOf("/") === 0) {
try {
const { data } = await api.get("/quickAnswers/", {
params: { searchParam: inputMessage.substring(1) },
});
setQuickAnswer(data.quickAnswers);
if (data.quickAnswers.length > 0) {
setTypeBar(true);
} else {
setTypeBar(false);
}
} catch (err) {
setTypeBar(false);
}
} else {
setTypeBar(false);
}
};
await api.post(`/messages/${ticketId}`, formData);
} catch (err) {
toastError(err);
}
const handleUploadAudio = async () => {
setLoading(true);
try {
const [, blob] = await Mp3Recorder.stop().getMp3();
if (blob.size < 10000) {
setLoading(false);
setRecording(false);
return;
}
setRecording(false);
setLoading(false);
};
const formData = new FormData();
const filename = `${new Date().getTime()}.mp3`;
formData.append("medias", blob, filename);
formData.append("body", filename);
formData.append("fromMe", true);
const handleCancelAudio = async () => {
try {
await Mp3Recorder.stop().getMp3();
setRecording(false);
} catch (err) {
toastError(err);
}
};
await api.post(`/messages/${ticketId}`, formData);
} catch (err) {
toastError(err);
}
const renderReplyingMessage = message => {
return (
<div className={classes.replyginMsgWrapper}>
<div className={classes.replyginMsgContainer}>
<span
className={clsx(classes.replyginContactMsgSideColor, {
[classes.replyginSelfMsgSideColor]: !message.fromMe,
})}
></span>
<div className={classes.replyginMsgBody}>
{!message.fromMe && (
<span className={classes.messageContactName}>
{message.contact?.name}
</span>
)}
{message.body}
</div>
</div>
<IconButton
aria-label="showRecorder"
component="span"
disabled={loading || ticketStatus !== "open"}
onClick={() => setReplyingMessage(null)}
>
<ClearIcon className={classes.sendMessageIcons} />
</IconButton>
</div>
);
};
setRecording(false);
setLoading(false);
};
if (medias.length > 0)
return (
<Paper elevation={0} square className={classes.viewMediaInputWrapper}>
<IconButton
aria-label="cancel-upload"
component="span"
onClick={e => setMedias([])}
>
<CancelIcon className={classes.sendMessageIcons} />
</IconButton>
const handleCancelAudio = async () => {
try {
await Mp3Recorder.stop().getMp3();
setRecording(false);
} catch (err) {
toastError(err);
}
};
{loading ? (
<div>
<CircularProgress className={classes.circleLoading} />
</div>
) : (
<span>
{medias[0]?.name}
{/* <img src={media.preview} alt=""></img> */}
</span>
)}
<IconButton
aria-label="send-upload"
component="span"
onClick={handleUploadMedia}
disabled={loading}
>
<SendIcon className={classes.sendMessageIcons} />
</IconButton>
</Paper>
);
else {
return (
<Paper square elevation={0} className={classes.mainWrapper}>
{replyingMessage && renderReplyingMessage(replyingMessage)}
<div className={classes.newMessageBox}>
<IconButton
aria-label="emojiPicker"
component="span"
disabled={loading || recording || ticketStatus !== "open"}
onClick={e => setShowEmoji(prevState => !prevState)}
>
<MoodIcon className={classes.sendMessageIcons} />
</IconButton>
{showEmoji ? (
<div className={classes.emojiBox}>
<Picker
perLine={16}
showPreview={false}
showSkinTones={false}
onSelect={handleAddEmoji}
/>
</div>
) : null}
const renderReplyingMessage = (message) => {
return (
<div className={classes.replyginMsgWrapper}>
<div className={classes.replyginMsgContainer}>
<span
className={clsx(classes.replyginContactMsgSideColor, {
[classes.replyginSelfMsgSideColor]: !message.fromMe,
})}
></span>
<div className={classes.replyginMsgBody}>
{!message.fromMe && (
<span className={classes.messageContactName}>
{message.contact?.name}
</span>
)}
{message.body}
</div>
</div>
<IconButton
aria-label="showRecorder"
component="span"
disabled={loading || ticketStatus !== "open"}
onClick={() => setReplyingMessage(null)}
>
<ClearIcon className={classes.sendMessageIcons} />
</IconButton>
</div>
);
};
<input
multiple
type="file"
id="upload-button"
disabled={loading || recording || ticketStatus !== "open"}
className={classes.uploadInput}
onChange={handleChangeMedias}
/>
<label htmlFor="upload-button">
<IconButton
aria-label="upload"
component="span"
disabled={loading || recording || ticketStatus !== "open"}
>
<AttachFileIcon className={classes.sendMessageIcons} />
</IconButton>
</label>
<FormControlLabel
style={{ marginRight: 7, color: "gray" }}
label={i18n.t("messagesInput.signMessage")}
labelPlacement="start"
control={
<Switch
size="small"
checked={signMessage}
onChange={e => {
setSignMessage(e.target.checked);
}}
name="showAllTickets"
color="primary"
/>
}
/>
<div className={classes.messageInputWrapper}>
<InputBase
inputRef={input => {
input && input.focus();
input && (inputRef.current = input);
}}
className={classes.messageInput}
placeholder={
ticketStatus === "open"
? i18n.t("messagesInput.placeholderOpen")
: i18n.t("messagesInput.placeholderClosed")
}
multiline
rowsMax={5}
value={inputMessage}
onChange={handleChangeInput}
disabled={recording || loading || ticketStatus !== "open"}
onPaste={e => {
ticketStatus === "open" && handleInputPaste(e);
}}
onKeyPress={e => {
if (loading || e.shiftKey) return;
else if (e.key === "Enter") {
handleSendMessage();
}
}}
/>
</div>
{inputMessage ? (
<IconButton
aria-label="sendMessage"
component="span"
onClick={handleSendMessage}
disabled={loading}
>
<SendIcon className={classes.sendMessageIcons} />
</IconButton>
) : recording ? (
<div className={classes.recorderWrapper}>
<IconButton
aria-label="cancelRecording"
component="span"
fontSize="large"
disabled={loading}
onClick={handleCancelAudio}
>
<HighlightOffIcon className={classes.cancelAudioIcon} />
</IconButton>
{loading ? (
<div>
<CircularProgress className={classes.audioLoading} />
</div>
) : (
<RecordingTimer />
)}
if (medias.length > 0)
return (
<Paper elevation={0} square className={classes.viewMediaInputWrapper}>
<IconButton
aria-label="cancel-upload"
component="span"
onClick={(e) => setMedias([])}
>
<CancelIcon className={classes.sendMessageIcons} />
</IconButton>
<IconButton
aria-label="sendRecordedAudio"
component="span"
onClick={handleUploadAudio}
disabled={loading}
>
<CheckCircleOutlineIcon className={classes.sendAudioIcon} />
</IconButton>
</div>
) : (
<IconButton
aria-label="showRecorder"
component="span"
disabled={loading || ticketStatus !== "open"}
onClick={handleStartRecording}
>
<MicIcon className={classes.sendMessageIcons} />
</IconButton>
)}
</div>
</Paper>
);
}
{loading ? (
<div>
<CircularProgress className={classes.circleLoading} />
</div>
) : (
<span>
{medias[0]?.name}
{/* <img src={media.preview} alt=""></img> */}
</span>
)}
<IconButton
aria-label="send-upload"
component="span"
onClick={handleUploadMedia}
disabled={loading}
>
<SendIcon className={classes.sendMessageIcons} />
</IconButton>
</Paper>
);
else {
return (
<Paper square elevation={0} className={classes.mainWrapper}>
{replyingMessage && renderReplyingMessage(replyingMessage)}
<div className={classes.newMessageBox}>
<IconButton
aria-label="emojiPicker"
component="span"
disabled={loading || recording || ticketStatus !== "open"}
onClick={(e) => setShowEmoji((prevState) => !prevState)}
>
<MoodIcon className={classes.sendMessageIcons} />
</IconButton>
{showEmoji ? (
<div className={classes.emojiBox}>
<ClickAwayListener onClickAway={(e) => setShowEmoji(false)}>
<Picker
perLine={16}
showPreview={false}
showSkinTones={false}
onSelect={handleAddEmoji}
/>
</ClickAwayListener>
</div>
) : null}
<input
multiple
type="file"
id="upload-button"
disabled={loading || recording || ticketStatus !== "open"}
className={classes.uploadInput}
onChange={handleChangeMedias}
/>
<label htmlFor="upload-button">
<IconButton
aria-label="upload"
component="span"
disabled={loading || recording || ticketStatus !== "open"}
>
<AttachFileIcon className={classes.sendMessageIcons} />
</IconButton>
</label>
<FormControlLabel
style={{ marginRight: 7, color: "gray" }}
label={i18n.t("messagesInput.signMessage")}
labelPlacement="start"
control={
<Switch
size="small"
checked={signMessage}
onChange={(e) => {
setSignMessage(e.target.checked);
}}
name="showAllTickets"
color="primary"
/>
}
/>
<div className={classes.messageInputWrapper}>
<InputBase
inputRef={(input) => {
input && input.focus();
input && (inputRef.current = input);
}}
className={classes.messageInput}
placeholder={
ticketStatus === "open"
? i18n.t("messagesInput.placeholderOpen")
: i18n.t("messagesInput.placeholderClosed")
}
multiline
rowsMax={5}
value={inputMessage}
onChange={handleChangeInput}
disabled={recording || loading || ticketStatus !== "open"}
onPaste={(e) => {
ticketStatus === "open" && handleInputPaste(e);
}}
onKeyPress={(e) => {
if (loading || e.shiftKey) return;
else if (e.key === "Enter") {
handleSendMessage();
}
}}
/>
{typeBar ? (
<ul className={classes.messageQuickAnswersWrapper}>
{quickAnswers.map((value, index) => {
return (
<li
className={classes.messageQuickAnswersWrapperItem}
key={index}
>
{/* eslint-disable-next-line jsx-a11y/anchor-is-valid */}
<a onClick={() => handleQuickAnswersClick(value.message)}>
{`${value.shortcut} - ${value.message}`}
</a>
</li>
);
})}
</ul>
) : (
<div></div>
)}
</div>
{inputMessage ? (
<IconButton
aria-label="sendMessage"
component="span"
onClick={handleSendMessage}
disabled={loading}
>
<SendIcon className={classes.sendMessageIcons} />
</IconButton>
) : recording ? (
<div className={classes.recorderWrapper}>
<IconButton
aria-label="cancelRecording"
component="span"
fontSize="large"
disabled={loading}
onClick={handleCancelAudio}
>
<HighlightOffIcon className={classes.cancelAudioIcon} />
</IconButton>
{loading ? (
<div>
<CircularProgress className={classes.audioLoading} />
</div>
) : (
<RecordingTimer />
)}
<IconButton
aria-label="sendRecordedAudio"
component="span"
onClick={handleUploadAudio}
disabled={loading}
>
<CheckCircleOutlineIcon className={classes.sendAudioIcon} />
</IconButton>
</div>
) : (
<IconButton
aria-label="showRecorder"
component="span"
disabled={loading || ticketStatus !== "open"}
onClick={handleStartRecording}
>
<MicIcon className={classes.sendMessageIcons} />
</IconButton>
)}
</div>
</Paper>
);
}
};
export default MessageInput;

View File

@@ -0,0 +1,222 @@
import React, { useState, useEffect, useRef } from "react";
import * as Yup from "yup";
import { Formik, Form, Field } from "formik";
import { toast } from "react-toastify";
import {
makeStyles,
Button,
TextField,
Dialog,
DialogActions,
DialogContent,
DialogTitle,
CircularProgress,
} from "@material-ui/core";
import { green } from "@material-ui/core/colors";
import { i18n } from "../../translate/i18n";
import api from "../../services/api";
import toastError from "../../errors/toastError";
const useStyles = makeStyles((theme) => ({
root: {
flexWrap: "wrap",
},
textField: {
marginRight: theme.spacing(1),
width: "100%",
},
btnWrapper: {
position: "relative",
},
buttonProgress: {
color: green[500],
position: "absolute",
top: "50%",
left: "50%",
marginTop: -12,
marginLeft: -12,
},
textQuickAnswerContainer: {
width: "100%",
},
}));
const QuickAnswerSchema = Yup.object().shape({
shortcut: Yup.string()
.min(2, "Too Short!")
.max(15, "Too Long!")
.required("Required"),
message: Yup.string()
.min(8, "Too Short!")
.max(30000, "Too Long!")
.required("Required"),
});
const QuickAnswersModal = ({
open,
onClose,
quickAnswerId,
initialValues,
onSave,
}) => {
const classes = useStyles();
const isMounted = useRef(true);
const initialState = {
shortcut: "",
message: "",
};
const [quickAnswer, setQuickAnswer] = useState(initialState);
useEffect(() => {
return () => {
isMounted.current = false;
};
}, []);
useEffect(() => {
const fetchQuickAnswer = async () => {
if (initialValues) {
setQuickAnswer((prevState) => {
return { ...prevState, ...initialValues };
});
}
if (!quickAnswerId) return;
try {
const { data } = await api.get(`/quickAnswers/${quickAnswerId}`);
if (isMounted.current) {
setQuickAnswer(data);
}
} catch (err) {
toastError(err);
}
};
fetchQuickAnswer();
}, [quickAnswerId, open, initialValues]);
const handleClose = () => {
onClose();
setQuickAnswer(initialState);
};
const handleSaveQuickAnswer = async (values) => {
try {
if (quickAnswerId) {
await api.put(`/quickAnswers/${quickAnswerId}`, values);
handleClose();
} else {
const { data } = await api.post("/quickAnswers", values);
if (onSave) {
onSave(data);
}
handleClose();
}
toast.success(i18n.t("quickAnswersModal.success"));
} catch (err) {
toastError(err);
}
};
return (
<div className={classes.root}>
<Dialog
open={open}
onClose={handleClose}
maxWidth="sm"
fullWidth
scroll="paper"
>
<DialogTitle id="form-dialog-title">
{quickAnswerId
? `${i18n.t("quickAnswersModal.title.edit")}`
: `${i18n.t("quickAnswersModal.title.add")}`}
</DialogTitle>
<Formik
initialValues={quickAnswer}
enableReinitialize={true}
validationSchema={QuickAnswerSchema}
onSubmit={(values, actions) => {
setTimeout(() => {
handleSaveQuickAnswer(values);
actions.setSubmitting(false);
}, 400);
}}
>
{({ values, errors, touched, isSubmitting }) => (
<Form>
<DialogContent dividers>
<div className={classes.textQuickAnswerContainer}>
<Field
as={TextField}
label={i18n.t("quickAnswersModal.form.shortcut")}
name="shortcut"
autoFocus
error={touched.shortcut && Boolean(errors.shortcut)}
helperText={touched.shortcut && errors.shortcut}
variant="outlined"
margin="dense"
className={classes.textField}
fullWidth
/>
</div>
<div className={classes.textQuickAnswerContainer}>
<Field
as={TextField}
label={i18n.t("quickAnswersModal.form.message")}
name="message"
error={touched.message && Boolean(errors.message)}
helperText={touched.message && errors.message}
variant="outlined"
margin="dense"
className={classes.textField}
multiline
rows={5}
fullWidth
/>
</div>
</DialogContent>
<DialogActions>
<Button
onClick={handleClose}
color="secondary"
disabled={isSubmitting}
variant="outlined"
>
{i18n.t("quickAnswersModal.buttons.cancel")}
</Button>
<Button
type="submit"
color="primary"
disabled={isSubmitting}
variant="contained"
className={classes.btnWrapper}
>
{quickAnswerId
? `${i18n.t("quickAnswersModal.buttons.okEdit")}`
: `${i18n.t("quickAnswersModal.buttons.okAdd")}`}
{isSubmitting && (
<CircularProgress
size={24}
className={classes.buttonProgress}
/>
)}
</Button>
</DialogActions>
</Form>
)}
</Formik>
</Dialog>
</div>
);
};
export default QuickAnswersModal;