diff --git a/backend/src/controllers/QuickAnswerController.ts b/backend/src/controllers/QuickAnswerController.ts new file mode 100644 index 0000000..3828e91 --- /dev/null +++ b/backend/src/controllers/QuickAnswerController.ts @@ -0,0 +1,117 @@ +import * as Yup from "yup"; +import { Request, Response } from "express"; +import { getIO } from "../libs/socket"; + +import ListQuickAnswerService from "../services/QuickAnswerService/ListQuickAnswerService"; +import CreateQuickAnswerService from "../services/QuickAnswerService/CreateQuickAnswerService"; +import ShowQuickAnswerService from "../services/QuickAnswerService/ShowQuickAnswerService"; +import UpdateQuickAnswerService from "../services/QuickAnswerService/UpdateQuickAnswerService"; +import DeleteQuickAnswerService from "../services/QuickAnswerService/DeleteQuickAnswerService"; + +import AppError from "../errors/AppError"; + +type IndexQuery = { + searchParam: string; + pageNumber: string; +}; + +interface QuickAnswerData { + shortcut: string; + message: string; +} + +export const index = async (req: Request, res: Response): Promise => { + const { searchParam, pageNumber } = req.query as IndexQuery; + + const { quickAnswers, count, hasMore } = await ListQuickAnswerService({ + searchParam, + pageNumber + }); + + return res.json({ quickAnswers, count, hasMore }); +}; + +export const store = async (req: Request, res: Response): Promise => { + const newQuickAnswer: QuickAnswerData = req.body; + + const QuickAnswerSchema = Yup.object().shape({ + shortcut: Yup.string().required(), + message: Yup.string().required() + }); + + try { + await QuickAnswerSchema.validate(newQuickAnswer); + } catch (err) { + throw new AppError(err.message); + } + + const quickAnswer = await CreateQuickAnswerService({ + ...newQuickAnswer + }); + + const io = getIO(); + io.emit("quickAnswer", { + action: "create", + quickAnswer + }); + + return res.status(200).json(quickAnswer); +}; + +export const show = async (req: Request, res: Response): Promise => { + const { quickAnswerId } = req.params; + + const quickAnswer = await ShowQuickAnswerService(quickAnswerId); + + return res.status(200).json(quickAnswer); +}; + +export const update = async ( + req: Request, + res: Response +): Promise => { + const quickAnswerData: QuickAnswerData = req.body; + + const schema = Yup.object().shape({ + shortcut: Yup.string(), + message: Yup.string() + }); + + try { + await schema.validate(quickAnswerData); + } catch (err) { + throw new AppError(err.message); + } + + const { quickAnswerId } = req.params; + + const quickAnswer = await UpdateQuickAnswerService({ + quickAnswerData, + quickAnswerId + }); + + const io = getIO(); + io.emit("quickAnswer", { + action: "update", + quickAnswer + }); + + return res.status(200).json(quickAnswer); +}; + +export const remove = async ( + req: Request, + res: Response +): Promise => { + const { quickAnswerId } = req.params; + + await DeleteQuickAnswerService(quickAnswerId); + + const io = getIO(); + io.emit("quickAnswer", { + action: "delete", + quickAnswerId + }); + + return res.status(200).json({ message: "Quick Answer deleted" }); +}; diff --git a/backend/src/database/index.ts b/backend/src/database/index.ts index b62e840..4c230ac 100644 --- a/backend/src/database/index.ts +++ b/backend/src/database/index.ts @@ -9,6 +9,7 @@ import Message from "../models/Message"; import Queue from "../models/Queue"; import WhatsappQueue from "../models/WhatsappQueue"; import UserQueue from "../models/UserQueue"; +import QuickAnswer from "../models/QuickAnswer"; // eslint-disable-next-line const dbConfig = require("../config/database"); @@ -26,7 +27,8 @@ const models = [ Setting, Queue, WhatsappQueue, - UserQueue + UserQueue, + QuickAnswer ]; sequelize.addModels(models); diff --git a/backend/src/database/migrations/20210818102605-create-quickAnswers.ts b/backend/src/database/migrations/20210818102605-create-quickAnswers.ts new file mode 100644 index 0000000..479d5b3 --- /dev/null +++ b/backend/src/database/migrations/20210818102605-create-quickAnswers.ts @@ -0,0 +1,34 @@ +import { QueryInterface, DataTypes } from "sequelize"; + +module.exports = { + up: (queryInterface: QueryInterface) => { + return queryInterface.createTable("quickAnswers", { + id: { + type: DataTypes.INTEGER, + autoIncrement: true, + primaryKey: true, + allowNull: false + }, + shortcut: { + type: DataTypes.TEXT, + allowNull: false + }, + message: { + type: DataTypes.TEXT, + allowNull: false + }, + createdAt: { + type: DataTypes.DATE, + allowNull: false + }, + updatedAt: { + type: DataTypes.DATE, + allowNull: false + } + }); + }, + + down: (queryInterface: QueryInterface) => { + return queryInterface.dropTable("quickAnswers"); + } +}; diff --git a/backend/src/models/QuickAnswer.ts b/backend/src/models/QuickAnswer.ts new file mode 100644 index 0000000..3549734 --- /dev/null +++ b/backend/src/models/QuickAnswer.ts @@ -0,0 +1,32 @@ +import { + Table, + Column, + DataType, + CreatedAt, + UpdatedAt, + Model, + PrimaryKey, + AutoIncrement +} from "sequelize-typescript"; + +@Table +class QuickAnswer extends Model { + @PrimaryKey + @AutoIncrement + @Column + id: number; + + @Column(DataType.TEXT) + shortcut: string; + + @Column(DataType.TEXT) + message: string; + + @CreatedAt + createdAt: Date; + + @UpdatedAt + updatedAt: Date; +} + +export default QuickAnswer; diff --git a/backend/src/routes/index.ts b/backend/src/routes/index.ts index 7043f61..4109c7b 100644 --- a/backend/src/routes/index.ts +++ b/backend/src/routes/index.ts @@ -9,6 +9,7 @@ import whatsappRoutes from "./whatsappRoutes"; import messageRoutes from "./messageRoutes"; import whatsappSessionRoutes from "./whatsappSessionRoutes"; import queueRoutes from "./queueRoutes"; +import quickAnswerRoutes from "./quickAnswerRoutes"; const routes = Router(); @@ -22,5 +23,6 @@ routes.use(messageRoutes); routes.use(messageRoutes); routes.use(whatsappSessionRoutes); routes.use(queueRoutes); +routes.use(quickAnswerRoutes); export default routes; diff --git a/backend/src/routes/quickAnswerRoutes.ts b/backend/src/routes/quickAnswerRoutes.ts new file mode 100644 index 0000000..eab4557 --- /dev/null +++ b/backend/src/routes/quickAnswerRoutes.ts @@ -0,0 +1,30 @@ +import express from "express"; +import isAuth from "../middleware/isAuth"; + +import * as QuickAnswerController from "../controllers/QuickAnswerController"; + +const quickAnswerRoutes = express.Router(); + +quickAnswerRoutes.get("/quickAnswers", isAuth, QuickAnswerController.index); + +quickAnswerRoutes.get( + "/quickAnswers/:quickAnswerId", + isAuth, + QuickAnswerController.show +); + +quickAnswerRoutes.post("/quickAnswers", isAuth, QuickAnswerController.store); + +quickAnswerRoutes.put( + "/quickAnswers/:quickAnswerId", + isAuth, + QuickAnswerController.update +); + +quickAnswerRoutes.delete( + "/quickAnswers/:quickAnswerId", + isAuth, + QuickAnswerController.remove +); + +export default quickAnswerRoutes; diff --git a/backend/src/services/QuickAnswerService/CreateQuickAnswerService.ts b/backend/src/services/QuickAnswerService/CreateQuickAnswerService.ts new file mode 100644 index 0000000..80668e1 --- /dev/null +++ b/backend/src/services/QuickAnswerService/CreateQuickAnswerService.ts @@ -0,0 +1,26 @@ +import AppError from "../../errors/AppError"; +import QuickAnswer from "../../models/QuickAnswer"; + +interface Request { + shortcut: string; + message: string; +} + +const CreateQuickAnswerService = async ({ + shortcut, + message +}: Request): Promise => { + const nameExists = await QuickAnswer.findOne({ + where: { shortcut } + }); + + if (nameExists) { + throw new AppError("ERR__SHORTCUT_DUPLICATED"); + } + + const quickAnswer = await QuickAnswer.create({ shortcut, message }); + + return quickAnswer; +}; + +export default CreateQuickAnswerService; diff --git a/backend/src/services/QuickAnswerService/DeleteQuickAnswerService.ts b/backend/src/services/QuickAnswerService/DeleteQuickAnswerService.ts new file mode 100644 index 0000000..1cc21b2 --- /dev/null +++ b/backend/src/services/QuickAnswerService/DeleteQuickAnswerService.ts @@ -0,0 +1,16 @@ +import QuickAnswer from "../../models/QuickAnswer"; +import AppError from "../../errors/AppError"; + +const DeleteQuickAnswerService = async (id: string): Promise => { + const quickAnswer = await QuickAnswer.findOne({ + where: { id } + }); + + if (!quickAnswer) { + throw new AppError("ERR_NO_QUICK_ANSWER_FOUND", 404); + } + + await quickAnswer.destroy(); +}; + +export default DeleteQuickAnswerService; diff --git a/backend/src/services/QuickAnswerService/ListQuickAnswerService.ts b/backend/src/services/QuickAnswerService/ListQuickAnswerService.ts new file mode 100644 index 0000000..0ddcbcc --- /dev/null +++ b/backend/src/services/QuickAnswerService/ListQuickAnswerService.ts @@ -0,0 +1,45 @@ +import { Sequelize } from "sequelize"; +import QuickAnswer from "../../models/QuickAnswer"; + +interface Request { + searchParam?: string; + pageNumber?: string; +} + +interface Response { + quickAnswers: QuickAnswer[]; + count: number; + hasMore: boolean; +} + +const ListQuickAnswerService = async ({ + searchParam = "", + pageNumber = "1" +}: Request): Promise => { + const whereCondition = { + message: Sequelize.where( + Sequelize.fn("LOWER", Sequelize.col("message")), + "LIKE", + `%${searchParam.toLowerCase().trim()}%` + ) + }; + const limit = 20; + const offset = limit * (+pageNumber - 1); + + const { count, rows: quickAnswers } = await QuickAnswer.findAndCountAll({ + where: whereCondition, + limit, + offset, + order: [["message", "ASC"]] + }); + + const hasMore = count > offset + quickAnswers.length; + + return { + quickAnswers, + count, + hasMore + }; +}; + +export default ListQuickAnswerService; diff --git a/backend/src/services/QuickAnswerService/ShowQuickAnswerService.ts b/backend/src/services/QuickAnswerService/ShowQuickAnswerService.ts new file mode 100644 index 0000000..1ed3d2e --- /dev/null +++ b/backend/src/services/QuickAnswerService/ShowQuickAnswerService.ts @@ -0,0 +1,14 @@ +import QuickAnswer from "../../models/QuickAnswer"; +import AppError from "../../errors/AppError"; + +const ShowQuickAnswerService = async (id: string): Promise => { + const quickAnswer = await QuickAnswer.findByPk(id); + + if (!quickAnswer) { + throw new AppError("ERR_NO_QUICK_ANSWERS_FOUND", 404); + } + + return quickAnswer; +}; + +export default ShowQuickAnswerService; diff --git a/backend/src/services/QuickAnswerService/UpdateQuickAnswerService.ts b/backend/src/services/QuickAnswerService/UpdateQuickAnswerService.ts new file mode 100644 index 0000000..e50351b --- /dev/null +++ b/backend/src/services/QuickAnswerService/UpdateQuickAnswerService.ts @@ -0,0 +1,40 @@ +import QuickAnswer from "../../models/QuickAnswer"; +import AppError from "../../errors/AppError"; + +interface QuickAnswerData { + shortcut?: string; + message?: string; +} + +interface Request { + quickAnswerData: QuickAnswerData; + quickAnswerId: string; +} + +const UpdateQuickAnswerService = async ({ + quickAnswerData, + quickAnswerId +}: Request): Promise => { + const { shortcut, message } = quickAnswerData; + + const quickAnswer = await QuickAnswer.findOne({ + where: { id: quickAnswerId }, + attributes: ["id", "shortcut", "message"] + }); + + if (!quickAnswer) { + throw new AppError("ERR_NO_QUICK_ANSWERS_FOUND", 404); + } + await quickAnswer.update({ + shortcut, + message + }); + + await quickAnswer.reload({ + attributes: ["id", "shortcut", "message"] + }); + + return quickAnswer; +}; + +export default UpdateQuickAnswerService; diff --git a/frontend/src/components/MessageInput/index.js b/frontend/src/components/MessageInput/index.js index a6ce895..f297786 100644 --- a/frontend/src/components/MessageInput/index.js +++ b/frontend/src/components/MessageInput/index.js @@ -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 ( -
-
- -
- {!message.fromMe && ( - - {message.contact?.name} - - )} - {message.body} -
-
- setReplyingMessage(null)} - > - - -
- ); - }; + setRecording(false); + setLoading(false); + }; - if (medias.length > 0) - return ( - - setMedias([])} - > - - + const handleCancelAudio = async () => { + try { + await Mp3Recorder.stop().getMp3(); + setRecording(false); + } catch (err) { + toastError(err); + } + }; - {loading ? ( -
- -
- ) : ( - - {medias[0]?.name} - {/* */} - - )} - - - -
- ); - else { - return ( - - {replyingMessage && renderReplyingMessage(replyingMessage)} -
- setShowEmoji(prevState => !prevState)} - > - - - {showEmoji ? ( -
- -
- ) : null} + const renderReplyingMessage = (message) => { + return ( +
+
+ +
+ {!message.fromMe && ( + + {message.contact?.name} + + )} + {message.body} +
+
+ setReplyingMessage(null)} + > + + +
+ ); + }; - - - { - setSignMessage(e.target.checked); - }} - name="showAllTickets" - color="primary" - /> - } - /> -
- { - 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(); - } - }} - /> -
- {inputMessage ? ( - - - - ) : recording ? ( -
- - - - {loading ? ( -
- -
- ) : ( - - )} + if (medias.length > 0) + return ( + + setMedias([])} + > + + - - - -
- ) : ( - - - - )} -
-
- ); - } + {loading ? ( +
+ +
+ ) : ( + + {medias[0]?.name} + {/* */} + + )} + + + + + ); + else { + return ( + + {replyingMessage && renderReplyingMessage(replyingMessage)} +
+ setShowEmoji((prevState) => !prevState)} + > + + + {showEmoji ? ( +
+ setShowEmoji(false)}> + + +
+ ) : null} + + + + { + setSignMessage(e.target.checked); + }} + name="showAllTickets" + color="primary" + /> + } + /> +
+ { + 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 ? ( + + ) : ( +
+ )} +
+ {inputMessage ? ( + + + + ) : recording ? ( +
+ + + + {loading ? ( +
+ +
+ ) : ( + + )} + + + + +
+ ) : ( + + + + )} +
+
+ ); + } }; export default MessageInput; diff --git a/frontend/src/components/QuickAnswersModal/index.js b/frontend/src/components/QuickAnswersModal/index.js new file mode 100644 index 0000000..ba100c6 --- /dev/null +++ b/frontend/src/components/QuickAnswersModal/index.js @@ -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 ( +
+ + + {quickAnswerId + ? `${i18n.t("quickAnswersModal.title.edit")}` + : `${i18n.t("quickAnswersModal.title.add")}`} + + { + setTimeout(() => { + handleSaveQuickAnswer(values); + actions.setSubmitting(false); + }, 400); + }} + > + {({ values, errors, touched, isSubmitting }) => ( +
+ +
+ +
+
+ +
+
+ + + + +
+ )} +
+
+
+ ); +}; + +export default QuickAnswersModal; diff --git a/frontend/src/layout/MainListItems.js b/frontend/src/layout/MainListItems.js index ebffadc..879cc0e 100644 --- a/frontend/src/layout/MainListItems.js +++ b/frontend/src/layout/MainListItems.js @@ -14,6 +14,7 @@ import SettingsOutlinedIcon from "@material-ui/icons/SettingsOutlined"; import PeopleAltOutlinedIcon from "@material-ui/icons/PeopleAltOutlined"; import ContactPhoneOutlinedIcon from "@material-ui/icons/ContactPhoneOutlined"; import AccountTreeOutlinedIcon from "@material-ui/icons/AccountTreeOutlined"; +import QuestionAnswerOutlinedIcon from "@material-ui/icons/QuestionAnswerOutlined"; import { i18n } from "../translate/i18n"; import { WhatsAppsContext } from "../context/WhatsApp/WhatsAppsContext"; @@ -21,109 +22,114 @@ import { AuthContext } from "../context/Auth/AuthContext"; import { Can } from "../components/Can"; function ListItemLink(props) { - const { icon, primary, to, className } = props; + const { icon, primary, to, className } = props; - const renderLink = React.useMemo( - () => - React.forwardRef((itemProps, ref) => ( - - )), - [to] - ); + const renderLink = React.useMemo( + () => + React.forwardRef((itemProps, ref) => ( + + )), + [to] + ); - return ( -
  • - - {icon ? {icon} : null} - - -
  • - ); + return ( +
  • + + {icon ? {icon} : null} + + +
  • + ); } const MainListItems = () => { - const { whatsApps } = useContext(WhatsAppsContext); - const { user } = useContext(AuthContext); - const [connectionWarning, setConnectionWarning] = useState(false); + const { whatsApps } = useContext(WhatsAppsContext); + const { user } = useContext(AuthContext); + const [connectionWarning, setConnectionWarning] = useState(false); - useEffect(() => { - const delayDebounceFn = setTimeout(() => { - if (whatsApps.length > 0) { - const offlineWhats = whatsApps.filter(whats => { - return ( - whats.status === "qrcode" || - whats.status === "PAIRING" || - whats.status === "DISCONNECTED" || - whats.status === "TIMEOUT" || - whats.status === "OPENING" - ); - }); - if (offlineWhats.length > 0) { - setConnectionWarning(true); - } else { - setConnectionWarning(false); - } - } - }, 2000); - return () => clearTimeout(delayDebounceFn); - }, [whatsApps]); + useEffect(() => { + const delayDebounceFn = setTimeout(() => { + if (whatsApps.length > 0) { + const offlineWhats = whatsApps.filter((whats) => { + return ( + whats.status === "qrcode" || + whats.status === "PAIRING" || + whats.status === "DISCONNECTED" || + whats.status === "TIMEOUT" || + whats.status === "OPENING" + ); + }); + if (offlineWhats.length > 0) { + setConnectionWarning(true); + } else { + setConnectionWarning(false); + } + } + }, 2000); + return () => clearTimeout(delayDebounceFn); + }, [whatsApps]); - return ( -
    - } - /> - - - - } - /> - } - /> + return ( +
    + } + /> + + + + } + /> + } + /> - } - /> - ( - <> - - - {i18n.t("mainDrawer.listItems.administration")} - - } - /> - } - /> - } - /> - - )} - /> -
    - ); + } + /> + } + /> + ( + <> + + + {i18n.t("mainDrawer.listItems.administration")} + + } + /> + } + /> + } + /> + + )} + /> +
    + ); }; export default MainListItems; diff --git a/frontend/src/pages/QuickAnswers/index.js b/frontend/src/pages/QuickAnswers/index.js new file mode 100644 index 0000000..87958ea --- /dev/null +++ b/frontend/src/pages/QuickAnswers/index.js @@ -0,0 +1,288 @@ +import React, { useState, useEffect, useReducer } from "react"; +import openSocket from "socket.io-client"; + +import { + Button, + IconButton, + makeStyles, + Paper, + Table, + TableBody, + TableCell, + TableHead, + TableRow, + InputAdornment, + TextField, +} from "@material-ui/core"; +import { Edit, DeleteOutline } from "@material-ui/icons"; +import SearchIcon from "@material-ui/icons/Search"; + +import MainContainer from "../../components/MainContainer"; +import MainHeader from "../../components/MainHeader"; +import MainHeaderButtonsWrapper from "../../components/MainHeaderButtonsWrapper"; +import Title from "../../components/Title"; + +import api from "../../services/api"; +import { i18n } from "../../translate/i18n"; +import TableRowSkeleton from "../../components/TableRowSkeleton"; +import QuickAnswersModal from "../../components/QuickAnswersModal"; +import ConfirmationModal from "../../components/ConfirmationModal"; +import { toast } from "react-toastify"; +import toastError from "../../errors/toastError"; + +const reducer = (state, action) => { + if (action.type === "LOAD_QUICK_ANSWERS") { + const quickAnswers = action.payload; + const newQuickAnswers = []; + + quickAnswers.forEach((quickAnswer) => { + const quickAnswerIndex = state.findIndex((q) => q.id === quickAnswer.id); + if (quickAnswerIndex !== -1) { + state[quickAnswerIndex] = quickAnswer; + } else { + newQuickAnswers.push(quickAnswer); + } + }); + + return [...state, ...newQuickAnswers]; + } + + if (action.type === "UPDATE_QUICK_ANSWERS") { + const quickAnswer = action.payload; + const quickAnswerIndex = state.findIndex((q) => q.id === quickAnswer.id); + + if (quickAnswerIndex !== -1) { + state[quickAnswerIndex] = quickAnswer; + return [...state]; + } else { + return [quickAnswer, ...state]; + } + } + + if (action.type === "DELETE_QUICK_ANSWERS") { + const quickAnswerId = action.payload; + + const quickAnswerIndex = state.findIndex((q) => q.id === quickAnswerId); + if (quickAnswerIndex !== -1) { + state.splice(quickAnswerIndex, 1); + } + return [...state]; + } + + if (action.type === "RESET") { + return []; + } +}; + +const useStyles = makeStyles((theme) => ({ + mainPaper: { + flex: 1, + padding: theme.spacing(1), + overflowY: "scroll", + ...theme.scrollbarStyles, + }, +})); + +const QuickAnswers = () => { + const classes = useStyles(); + + const [loading, setLoading] = useState(false); + const [pageNumber, setPageNumber] = useState(1); + const [searchParam, setSearchParam] = useState(""); + const [quickAnswers, dispatch] = useReducer(reducer, []); + const [selectedQuickAnswers, setSelectedQuickAnswers] = useState(null); + const [quickAnswersModalOpen, setQuickAnswersModalOpen] = useState(false); + const [deletingQuickAnswers, setDeletingQuickAnswers] = useState(null); + const [confirmModalOpen, setConfirmModalOpen] = useState(false); + const [hasMore, setHasMore] = useState(false); + + useEffect(() => { + dispatch({ type: "RESET" }); + setPageNumber(1); + }, [searchParam]); + + useEffect(() => { + setLoading(true); + const delayDebounceFn = setTimeout(() => { + const fetchQuickAnswers = async () => { + try { + const { data } = await api.get("/quickAnswers/", { + params: { searchParam, pageNumber }, + }); + dispatch({ type: "LOAD_QUICK_ANSWERS", payload: data.quickAnswers }); + setHasMore(data.hasMore); + setLoading(false); + } catch (err) { + toastError(err); + } + }; + fetchQuickAnswers(); + }, 500); + return () => clearTimeout(delayDebounceFn); + }, [searchParam, pageNumber]); + + useEffect(() => { + const socket = openSocket(process.env.REACT_APP_BACKEND_URL); + + socket.on("quickAnswer", (data) => { + if (data.action === "update" || data.action === "create") { + dispatch({ type: "UPDATE_QUICK_ANSWERS", payload: data.quickAnswer }); + } + + if (data.action === "delete") { + dispatch({ + type: "DELETE_QUICK_ANSWERS", + payload: +data.quickAnswerId, + }); + } + }); + + return () => { + socket.disconnect(); + }; + }, []); + + const handleSearch = (event) => { + setSearchParam(event.target.value.toLowerCase()); + }; + + const handleOpenQuickAnswersModal = () => { + setSelectedQuickAnswers(null); + setQuickAnswersModalOpen(true); + }; + + const handleCloseQuickAnswersModal = () => { + setSelectedQuickAnswers(null); + setQuickAnswersModalOpen(false); + }; + + const handleEditQuickAnswers = (quickAnswer) => { + setSelectedQuickAnswers(quickAnswer); + setQuickAnswersModalOpen(true); + }; + + const handleDeleteQuickAnswers = async (quickAnswerId) => { + try { + await api.delete(`/quickAnswers/${quickAnswerId}`); + toast.success(i18n.t("quickAnswers.toasts.deleted")); + } catch (err) { + toastError(err); + } + setDeletingQuickAnswers(null); + setSearchParam(""); + setPageNumber(1); + }; + + const loadMore = () => { + setPageNumber((prevState) => prevState + 1); + }; + + const handleScroll = (e) => { + if (!hasMore || loading) return; + const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; + if (scrollHeight - (scrollTop + 100) < clientHeight) { + loadMore(); + } + }; + + return ( + + handleDeleteQuickAnswers(deletingQuickAnswers.id)} + > + {i18n.t("quickAnswers.confirmationModal.deleteMessage")} + + + + {i18n.t("quickAnswers.title")} + + + + + ), + }} + /> + + + + + + + + + {i18n.t("quickAnswers.table.shortcut")} + + + {i18n.t("quickAnswers.table.message")} + + + {i18n.t("quickAnswers.table.actions")} + + + + + <> + {quickAnswers.map((quickAnswer) => ( + + {quickAnswer.shortcut} + {quickAnswer.message} + + handleEditQuickAnswers(quickAnswer)} + > + + + + { + setConfirmModalOpen(true); + setDeletingQuickAnswers(quickAnswer); + }} + > + + + + + ))} + {loading && } + + +
    +
    +
    + ); +}; + +export default QuickAnswers; diff --git a/frontend/src/routes/index.js b/frontend/src/routes/index.js index 46b6cad..f303380 100644 --- a/frontend/src/routes/index.js +++ b/frontend/src/routes/index.js @@ -11,44 +11,51 @@ import Connections from "../pages/Connections/"; import Settings from "../pages/Settings/"; import Users from "../pages/Users"; import Contacts from "../pages/Contacts/"; +import QuickAnswers from "../pages/QuickAnswers/"; import Queues from "../pages/Queues/"; import { AuthProvider } from "../context/Auth/AuthContext"; import { WhatsAppsProvider } from "../context/WhatsApp/WhatsAppsContext"; import Route from "./Route"; const Routes = () => { - return ( - - - - - - - - - - - - - - - - - - - - - ); + return ( + + + + + + + + + + + + + + + + + + + + + + ); }; export default Routes; diff --git a/frontend/src/translate/languages/en.js b/frontend/src/translate/languages/en.js index 625fbce..eec8776 100644 --- a/frontend/src/translate/languages/en.js +++ b/frontend/src/translate/languages/en.js @@ -1,412 +1,448 @@ const messages = { - en: { - translations: { - signup: { - title: "Sign up", - toasts: { - success: "User created successfully! Please login!", - fail: "Error creating user. Check the reported data.", - }, - form: { - name: "Name", - email: "Email", - password: "Password", - }, - buttons: { - submit: "Register", - login: "Already have an account? Log in!", - }, - }, - login: { - title: "Login", - form: { - email: "Email", - password: "Password", - }, - buttons: { - submit: "Enter", - register: "Don't have an account? Register!", - }, - }, - auth: { - toasts: { - success: "Login successfully!", - }, - }, - dashboard: { - charts: { - perDay: { - title: "Tickets today: ", - }, - }, - }, - connections: { - title: "Connections", - toasts: { - deleted: "WhatsApp connection deleted sucessfully!", - }, - confirmationModal: { - deleteTitle: "Delete", - deleteMessage: "Are you sure? It cannot be reverted.", - disconnectTitle: "Disconnect", - disconnectMessage: "Are you sure? You'll need to read QR Code again.", - }, - buttons: { - add: "Add WhatsApp", - disconnect: "Disconnect", - tryAgain: "Try Again", - qrcode: "QR CODE", - newQr: "New QR CODE", - connecting: "Connectiing", - }, - toolTips: { - disconnected: { - title: "Failed to start WhatsApp session", - content: - "Make sure your cell phone is connected to the internet and try again, or request a new QR Code", - }, - qrcode: { - title: "Waiting for QR Code read", - content: - "Click on 'QR CODE' button and read the QR Code with your cell phone to start session", - }, - connected: { - title: "Connection established", - }, - timeout: { - title: "Connection with cell phone has been lost", - content: - "Make sure your cell phone is connected to the internet and WhatsApp is open, or click on 'Disconnect' button to get a new QRcode", - }, - }, - table: { - name: "Name", - status: "Status", - lastUpdate: "Last Update", - default: "Default", - actions: "Actions", - session: "Session", - }, - }, - whatsappModal: { - title: { - add: "Add WhatsApp", - edit: "Edit WhatsApp", - }, - form: { - name: "Name", - default: "Default", - }, - buttons: { - okAdd: "Add", - okEdit: "Save", - cancel: "Cancel", - }, - success: "WhatsApp saved successfully.", - }, - qrCode: { - message: "Read QrCode to start the session", - }, - contacts: { - title: "Contacts", - toasts: { - deleted: "Contact deleted sucessfully!", - }, - searchPlaceholder: "Search ...", - confirmationModal: { - deleteTitle: "Delete", - importTitlte: "Import contacts", - deleteMessage: - "Are you sure you want to delete this contact? All related tickets will be lost.", - importMessage: "Do you want to import all contacts from the phone?", - }, - buttons: { - import: "Import Contacts", - add: "Add Contact", - }, - table: { - name: "Name", - whatsapp: "WhatsApp", - email: "Email", - actions: "Actions", - }, - }, - contactModal: { - title: { - add: "Add contact", - edit: "Edit contact", - }, - form: { - mainInfo: "Contact details", - extraInfo: "Additional information", - name: "Name", - number: "Whatsapp number", - email: "Email", - extraName: "Field name", - extraValue: "Value", - }, - buttons: { - addExtraInfo: "Add information", - okAdd: "Add", - okEdit: "Save", - cancel: "Cancel", - }, - success: "Contact saved successfully.", - }, - queueModal: { - title: { - add: "Add queue", - edit: "Edit queue", - }, - form: { - name: "Name", - color: "Color", - greetingMessage: "Greeting Message", - }, - buttons: { - okAdd: "Add", - okEdit: "Save", - cancel: "Cancel", - }, - }, - userModal: { - title: { - add: "Add user", - edit: "Edit user", - }, - form: { - name: "Name", - email: "Email", - password: "Password", - profile: "Profile", - }, - buttons: { - okAdd: "Add", - okEdit: "Save", - cancel: "Cancel", - }, - success: "User saved successfully.", - }, - chat: { - noTicketMessage: "Select a ticket to start chatting.", - }, - ticketsManager: { - buttons: { - newTicket: "New", - }, - }, - ticketsQueueSelect: { - placeholder: "Queues", - }, - tickets: { - toasts: { - deleted: "The ticket you were on has been deleted.", - }, - notification: { - message: "Message from", - }, - tabs: { - open: { title: "Inbox" }, - closed: { title: "Resolved" }, - search: { title: "Search" }, - }, - search: { - placeholder: "Search tickets and messages.", - }, - buttons: { - showAll: "All", - }, - }, - transferTicketModal: { - title: "Transfer Ticket", - fieldLabel: "Type to search for users", - noOptions: "No user found with this name", - buttons: { - ok: "Transfer", - cancel: "Cancel", - }, - }, - ticketsList: { - pendingHeader: "Queue", - assignedHeader: "Working on", - noTicketsTitle: "Nothing here!", - noTicketsMessage: "No tickets found with this status or search term.", - buttons: { - accept: "Accept", - }, - }, - newTicketModal: { - title: "Create Ticket", - fieldLabel: "Type to search for a contact", - add: "Add", - buttons: { - ok: "Save", - cancel: "Cancel", - }, - }, - mainDrawer: { - listItems: { - dashboard: "Dashboard", - connections: "Connections", - tickets: "Tickets", - contacts: "Contacts", - queues: "Queues", - administration: "Administration", - users: "Users", - settings: "Settings", - }, - appBar: { - user: { - profile: "Profile", - logout: "Logout", - }, - }, - }, - notifications: { - noTickets: "No notifications.", - }, - queues: { - title: "Queues", - table: { - name: "Name", - color: "Color", - greeting: "Greeting message", - actions: "Actions", - }, - buttons: { - add: "Add queue", - }, - confirmationModal: { - deleteTitle: "Delete", - deleteMessage: - "Are you sure? It cannot be reverted! Tickets in this queue will still exist, but will not have any queues assigned.", - }, - }, - queueSelect: { - inputLabel: "Queues", - }, - users: { - title: "Users", - table: { - name: "Name", - email: "Email", - profile: "Profile", - actions: "Actions", - }, - buttons: { - add: "Add user", - }, - toasts: { - deleted: "User deleted sucessfully.", - }, - confirmationModal: { - deleteTitle: "Delete", - deleteMessage: - "All user data will be lost. Users' open tickets will be moved to queue.", - }, - }, - settings: { - success: "Settings saved successfully.", - title: "Settings", - settings: { - userCreation: { - name: "User creation", - options: { - enabled: "Enabled", - disabled: "Disabled", - }, - }, - }, - }, - messagesList: { - header: { - assignedTo: "Assigned to:", - buttons: { - return: "Return", - resolve: "Resolve", - reopen: "Reopen", - accept: "Accept", - }, - }, - }, - messagesInput: { - placeholderOpen: "Type a message", - placeholderClosed: "Reopen or accept this ticket to send a message.", - signMessage: "Sign", - }, - contactDrawer: { - header: "Contact details", - buttons: { - edit: "Edit contact", - }, - extraInfo: "Other information", - }, - ticketOptionsMenu: { - delete: "Delete", - transfer: "Transfer", - confirmationModal: { - title: "Delete ticket #", - titleFrom: "from contact ", - message: "Attention! All ticket's related messages will be lost.", - }, - buttons: { - delete: "Delete", - cancel: "Cancel", - }, - }, - confirmationModal: { - buttons: { - confirm: "Ok", - cancel: "Cancel", - }, - }, - messageOptionsMenu: { - delete: "Delete", - reply: "Reply", - confirmationModal: { - title: "Delete message?", - message: "This action cannot be reverted.", - }, - }, - backendErrors: { - ERR_NO_OTHER_WHATSAPP: - "There must be at lest one default WhatsApp connection.", - ERR_NO_DEF_WAPP_FOUND: - "No default WhatsApp found. Check connections page.", - ERR_WAPP_NOT_INITIALIZED: - "This WhatsApp session is not initialized. Check connections page.", - ERR_WAPP_CHECK_CONTACT: - "Could not check WhatsApp contact. Check connections page.", - ERR_WAPP_INVALID_CONTACT: "This is not a valid whatsapp number.", - ERR_WAPP_DOWNLOAD_MEDIA: - "Could not download media from WhatsApp. Check connections page.", - ERR_INVALID_CREDENTIALS: "Authentication error. Please try again.", - ERR_SENDING_WAPP_MSG: - "Error sending WhatsApp message. Check connections page.", - ERR_DELETE_WAPP_MSG: "Couldn't delete message from WhatsApp.", - ERR_OTHER_OPEN_TICKET: - "There's already an open ticket for this contact.", - ERR_SESSION_EXPIRED: "Session expired. Please login.", - ERR_USER_CREATION_DISABLED: - "User creation was disabled by administrator.", - ERR_NO_PERMISSION: "You don't have permission to access this resource.", - ERR_DUPLICATED_CONTACT: "A contact with this number already exists.", - ERR_NO_SETTING_FOUND: "No setting found with this ID.", - ERR_NO_CONTACT_FOUND: "No contact found with this ID.", - ERR_NO_TICKET_FOUND: "No ticket found with this ID.", - ERR_NO_USER_FOUND: "No user found with this ID.", - ERR_NO_WAPP_FOUND: "No WhatsApp found with this ID.", - ERR_CREATING_MESSAGE: "Error while creating message on database.", - ERR_CREATING_TICKET: "Error while creating ticket on database.", - ERR_FETCH_WAPP_MSG: - "Error fetching the message in WhtasApp, maybe it is too old.", - ERR_QUEUE_COLOR_ALREADY_EXISTS: - "This color is already in use, pick another one.", - ERR_WAPP_GREETING_REQUIRED: - "Greeting message is required if there is more than one queue.", - }, - }, - }, + en: { + translations: { + signup: { + title: "Sign up", + toasts: { + success: "User created successfully! Please login!", + fail: "Error creating user. Check the reported data.", + }, + form: { + name: "Name", + email: "Email", + password: "Password", + }, + buttons: { + submit: "Register", + login: "Already have an account? Log in!", + }, + }, + login: { + title: "Login", + form: { + email: "Email", + password: "Password", + }, + buttons: { + submit: "Enter", + register: "Don't have an account? Register!", + }, + }, + auth: { + toasts: { + success: "Login successfully!", + }, + }, + dashboard: { + charts: { + perDay: { + title: "Tickets today: ", + }, + }, + }, + connections: { + title: "Connections", + toasts: { + deleted: "WhatsApp connection deleted sucessfully!", + }, + confirmationModal: { + deleteTitle: "Delete", + deleteMessage: "Are you sure? It cannot be reverted.", + disconnectTitle: "Disconnect", + disconnectMessage: "Are you sure? You'll need to read QR Code again.", + }, + buttons: { + add: "Add WhatsApp", + disconnect: "Disconnect", + tryAgain: "Try Again", + qrcode: "QR CODE", + newQr: "New QR CODE", + connecting: "Connectiing", + }, + toolTips: { + disconnected: { + title: "Failed to start WhatsApp session", + content: + "Make sure your cell phone is connected to the internet and try again, or request a new QR Code", + }, + qrcode: { + title: "Waiting for QR Code read", + content: + "Click on 'QR CODE' button and read the QR Code with your cell phone to start session", + }, + connected: { + title: "Connection established", + }, + timeout: { + title: "Connection with cell phone has been lost", + content: + "Make sure your cell phone is connected to the internet and WhatsApp is open, or click on 'Disconnect' button to get a new QRcode", + }, + }, + table: { + name: "Name", + status: "Status", + lastUpdate: "Last Update", + default: "Default", + actions: "Actions", + session: "Session", + }, + }, + whatsappModal: { + title: { + add: "Add WhatsApp", + edit: "Edit WhatsApp", + }, + form: { + name: "Name", + default: "Default", + }, + buttons: { + okAdd: "Add", + okEdit: "Save", + cancel: "Cancel", + }, + success: "WhatsApp saved successfully.", + }, + qrCode: { + message: "Read QrCode to start the session", + }, + contacts: { + title: "Contacts", + toasts: { + deleted: "Contact deleted sucessfully!", + }, + searchPlaceholder: "Search ...", + confirmationModal: { + deleteTitle: "Delete", + importTitlte: "Import contacts", + deleteMessage: + "Are you sure you want to delete this contact? All related tickets will be lost.", + importMessage: "Do you want to import all contacts from the phone?", + }, + buttons: { + import: "Import Contacts", + add: "Add Contact", + }, + table: { + name: "Name", + whatsapp: "WhatsApp", + email: "Email", + actions: "Actions", + }, + }, + contactModal: { + title: { + add: "Add contact", + edit: "Edit contact", + }, + form: { + mainInfo: "Contact details", + extraInfo: "Additional information", + name: "Name", + number: "Whatsapp number", + email: "Email", + extraName: "Field name", + extraValue: "Value", + }, + buttons: { + addExtraInfo: "Add information", + okAdd: "Add", + okEdit: "Save", + cancel: "Cancel", + }, + success: "Contact saved successfully.", + }, + quickAnswersModal: { + title: { + add: "Add Quick Reply", + edit: "Edit Quick Answer", + }, + form: { + shortcut: "Shortcut", + message: "Quick Reply", + }, + buttons: { + okAdd: "Add", + okEdit: "Save", + cancel: "Cancel", + }, + success: "Quick Reply saved successfully.", + }, + queueModal: { + title: { + add: "Add queue", + edit: "Edit queue", + }, + form: { + name: "Name", + color: "Color", + greetingMessage: "Greeting Message", + }, + buttons: { + okAdd: "Add", + okEdit: "Save", + cancel: "Cancel", + }, + }, + userModal: { + title: { + add: "Add user", + edit: "Edit user", + }, + form: { + name: "Name", + email: "Email", + password: "Password", + profile: "Profile", + }, + buttons: { + okAdd: "Add", + okEdit: "Save", + cancel: "Cancel", + }, + success: "User saved successfully.", + }, + chat: { + noTicketMessage: "Select a ticket to start chatting.", + }, + ticketsManager: { + buttons: { + newTicket: "New", + }, + }, + ticketsQueueSelect: { + placeholder: "Queues", + }, + tickets: { + toasts: { + deleted: "The ticket you were on has been deleted.", + }, + notification: { + message: "Message from", + }, + tabs: { + open: { title: "Inbox" }, + closed: { title: "Resolved" }, + search: { title: "Search" }, + }, + search: { + placeholder: "Search tickets and messages.", + }, + buttons: { + showAll: "All", + }, + }, + transferTicketModal: { + title: "Transfer Ticket", + fieldLabel: "Type to search for users", + noOptions: "No user found with this name", + buttons: { + ok: "Transfer", + cancel: "Cancel", + }, + }, + ticketsList: { + pendingHeader: "Queue", + assignedHeader: "Working on", + noTicketsTitle: "Nothing here!", + noTicketsMessage: "No tickets found with this status or search term.", + buttons: { + accept: "Accept", + }, + }, + newTicketModal: { + title: "Create Ticket", + fieldLabel: "Type to search for a contact", + add: "Add", + buttons: { + ok: "Save", + cancel: "Cancel", + }, + }, + mainDrawer: { + listItems: { + dashboard: "Dashboard", + connections: "Connections", + tickets: "Tickets", + contacts: "Contacts", + quickAnswers: "Quick Answers", + queues: "Queues", + administration: "Administration", + users: "Users", + settings: "Settings", + }, + appBar: { + user: { + profile: "Profile", + logout: "Logout", + }, + }, + }, + notifications: { + noTickets: "No notifications.", + }, + queues: { + title: "Queues", + table: { + name: "Name", + color: "Color", + greeting: "Greeting message", + actions: "Actions", + }, + buttons: { + add: "Add queue", + }, + confirmationModal: { + deleteTitle: "Delete", + deleteMessage: + "Are you sure? It cannot be reverted! Tickets in this queue will still exist, but will not have any queues assigned.", + }, + }, + queueSelect: { + inputLabel: "Queues", + }, + quickAnswers: { + title: "Quick Answers", + table: { + shortcut: "Shortcut", + message: "Quick Reply", + actions: "Actions", + }, + buttons: { + add: "Add Quick Reply", + }, + toasts: { + deleted: "Quick Reply deleted successfully.", + }, + searchPlaceholder: "Search...", + confirmationModal: { + deleteTitle: "Are you sure you want to delete this Quick Reply: ", + deleteMessage: "This action cannot be undone.", + }, + }, + users: { + title: "Users", + table: { + name: "Name", + email: "Email", + profile: "Profile", + actions: "Actions", + }, + buttons: { + add: "Add user", + }, + toasts: { + deleted: "User deleted sucessfully.", + }, + confirmationModal: { + deleteTitle: "Delete", + deleteMessage: + "All user data will be lost. Users' open tickets will be moved to queue.", + }, + }, + settings: { + success: "Settings saved successfully.", + title: "Settings", + settings: { + userCreation: { + name: "User creation", + options: { + enabled: "Enabled", + disabled: "Disabled", + }, + }, + }, + }, + messagesList: { + header: { + assignedTo: "Assigned to:", + buttons: { + return: "Return", + resolve: "Resolve", + reopen: "Reopen", + accept: "Accept", + }, + }, + }, + messagesInput: { + placeholderOpen: "Type a message", + placeholderClosed: "Reopen or accept this ticket to send a message.", + signMessage: "Sign", + }, + contactDrawer: { + header: "Contact details", + buttons: { + edit: "Edit contact", + }, + extraInfo: "Other information", + }, + ticketOptionsMenu: { + delete: "Delete", + transfer: "Transfer", + confirmationModal: { + title: "Delete ticket #", + titleFrom: "from contact ", + message: "Attention! All ticket's related messages will be lost.", + }, + buttons: { + delete: "Delete", + cancel: "Cancel", + }, + }, + confirmationModal: { + buttons: { + confirm: "Ok", + cancel: "Cancel", + }, + }, + messageOptionsMenu: { + delete: "Delete", + reply: "Reply", + confirmationModal: { + title: "Delete message?", + message: "This action cannot be reverted.", + }, + }, + backendErrors: { + ERR_NO_OTHER_WHATSAPP: + "There must be at lest one default WhatsApp connection.", + ERR_NO_DEF_WAPP_FOUND: + "No default WhatsApp found. Check connections page.", + ERR_WAPP_NOT_INITIALIZED: + "This WhatsApp session is not initialized. Check connections page.", + ERR_WAPP_CHECK_CONTACT: + "Could not check WhatsApp contact. Check connections page.", + ERR_WAPP_INVALID_CONTACT: "This is not a valid whatsapp number.", + ERR_WAPP_DOWNLOAD_MEDIA: + "Could not download media from WhatsApp. Check connections page.", + ERR_INVALID_CREDENTIALS: "Authentication error. Please try again.", + ERR_SENDING_WAPP_MSG: + "Error sending WhatsApp message. Check connections page.", + ERR_DELETE_WAPP_MSG: "Couldn't delete message from WhatsApp.", + ERR_OTHER_OPEN_TICKET: + "There's already an open ticket for this contact.", + ERR_SESSION_EXPIRED: "Session expired. Please login.", + ERR_USER_CREATION_DISABLED: + "User creation was disabled by administrator.", + ERR_NO_PERMISSION: "You don't have permission to access this resource.", + ERR_DUPLICATED_CONTACT: "A contact with this number already exists.", + ERR_NO_SETTING_FOUND: "No setting found with this ID.", + ERR_NO_CONTACT_FOUND: "No contact found with this ID.", + ERR_NO_TICKET_FOUND: "No ticket found with this ID.", + ERR_NO_USER_FOUND: "No user found with this ID.", + ERR_NO_WAPP_FOUND: "No WhatsApp found with this ID.", + ERR_CREATING_MESSAGE: "Error while creating message on database.", + ERR_CREATING_TICKET: "Error while creating ticket on database.", + ERR_FETCH_WAPP_MSG: + "Error fetching the message in WhtasApp, maybe it is too old.", + ERR_QUEUE_COLOR_ALREADY_EXISTS: + "This color is already in use, pick another one.", + ERR_WAPP_GREETING_REQUIRED: + "Greeting message is required if there is more than one queue.", + }, + }, + }, }; export { messages }; diff --git a/frontend/src/translate/languages/es.js b/frontend/src/translate/languages/es.js index 7204966..7ed0c69 100644 --- a/frontend/src/translate/languages/es.js +++ b/frontend/src/translate/languages/es.js @@ -1,418 +1,455 @@ const messages = { - es: { - translations: { - signup: { - title: "Registro", - toasts: { - success: - "¡El usuario ha sido creado satisfactoriamente! ¡Ahora inicia sesión!", - fail: "Error creando el usuario. Verifica la data reportada.", - }, - form: { - name: "Nombre", - email: "Correo Electrónico", - password: "Contraseña", - }, - buttons: { - submit: "Regístrate", - login: "¿Ya tienes una cuenta? ¡Inicia sesión!", - }, - }, - login: { - title: "Inicio de Sesión", - form: { - email: "Correo Electrónico", - password: "Contraseña", - }, - buttons: { - submit: "Ingresa", - register: "¿No tienes cuenta? ¡Regístrate!", - }, - }, - auth: { - toasts: { - success: "¡Inicio de sesión exitoso!", - }, - }, - dashboard: { - charts: { - perDay: { - title: "Tickets hoy: ", - }, - }, - }, - connections: { - title: "Conexiones", - toasts: { - deleted: - "¡La conexión de WhatsApp ha sido borrada satisfactoriamente!", - }, - confirmationModal: { - deleteTitle: "Borrar", - deleteMessage: "¿Estás seguro? Este proceso no puede ser revertido.", - disconnectTitle: "Desconectar", - disconnectMessage: "Estás seguro? Deberá volver a leer el código QR", - }, - buttons: { - add: "Agrega WhatsApp", - disconnect: "Desconectar", - tryAgain: "Inténtalo de nuevo", - qrcode: "QR CODE", - newQr: "Nuevo QR CODE", - connecting: "Conectando", - }, - toolTips: { - disconnected: { - title: "No se pudo iniciar la sesión de WhatsApp", - content: - "Asegúrese de que su teléfono celular esté conectado a Internet y vuelva a intentarlo o solicite un nuevo código QR", - }, - qrcode: { - title: "Esperando la lectura del código QR", - content: - "Haga clic en el botón 'CÓDIGO QR' y lea el Código QR con su teléfono celular para iniciar la sesión", - }, - connected: { - title: "Conexión establecida", - }, - timeout: { - title: "Se perdió la conexión con el teléfono celular", - content: - "Asegúrese de que su teléfono celular esté conectado a Internet y que WhatsApp esté abierto, o haga clic en el botón 'Desconectar' para obtener un nuevo código QR", - }, - }, - table: { - name: "Nombre", - status: "Estado", - lastUpdate: "Última Actualización", - default: "Por Defecto", - actions: "Acciones", - session: "Sesión", - }, - }, - whatsappModal: { - title: { - add: "Agrega WhatsApp", - edit: "Edita WhatsApp", - }, - form: { - name: "Nombre", - default: "Por Defecto", - }, - buttons: { - okAdd: "Agregar", - okEdit: "Guardar", - cancel: "Cancelar", - }, - success: "WhatsApp guardado satisfactoriamente.", - }, - qrCode: { - message: "Lée el código QR para empezar la sesión.", - }, - contacts: { - title: "Contactos", - toasts: { - deleted: "¡Contacto borrado satisfactoriamente!", - }, - searchPlaceholder: "Buscar...", - confirmationModal: { - deleteTitle: "Borrar", - importTitlte: "Importar contactos", - deleteMessage: - "¿Estás seguro que deseas borrar este contacto? Todos los tickets relacionados se perderán.", - importMessage: - "¿Quieres importar todos los contactos desde tu teléfono?", - }, - buttons: { - import: "Importar Contactos", - add: "Agregar Contacto", - }, - table: { - name: "Nombre", - whatsapp: "WhatsApp", - email: "Correo Electrónico", - actions: "Acciones", - }, - }, - contactModal: { - title: { - add: "Agregar contacto", - edit: "Editar contacto", - }, - form: { - mainInfo: "Detalles del contacto", - extraInfo: "Información adicional", - name: "Nombre", - number: "Número de Whatsapp", - email: "Correo Electrónico", - extraName: "Nombre del Campo", - extraValue: "Valor", - }, - buttons: { - addExtraInfo: "Agregar información", - okAdd: "Agregar", - okEdit: "Guardar", - cancel: "Cancelar", - }, - success: "Contacto guardado satisfactoriamente.", - }, - queueModal: { - title: { - add: "Agregar cola", - edit: "Editar cola", - }, - form: { - name: "Nombre", - color: "Color", - greetingMessage: "Mensaje de saludo", - }, - buttons: { - okAdd: "Añadir", - okEdit: "Ahorrar", - cancel: "Cancelar", - }, - }, - userModal: { - title: { - add: "Agregar usuario", - edit: "Editar usuario", - }, - form: { - name: "Nombre", - email: "Correo Electrónico", - password: "Contraseña", - profile: "Perfil", - }, - buttons: { - okAdd: "Agregar", - okEdit: "Guardar", - cancel: "Cancelar", - }, - success: "Usuario guardado satisfactoriamente.", - }, - chat: { - noTicketMessage: "Selecciona un ticket para empezar a chatear.", - }, - ticketsManager: { - buttons: { - newTicket: "Nuevo", - }, - }, - ticketsQueueSelect: { - placeholder: "Linhas", - }, - tickets: { - toasts: { - deleted: "El ticket en el que estabas ha sido borrado.", - }, - notification: { - message: "Mensaje de", - }, - tabs: { - open: { title: "Bandeja" }, - closed: { title: "Resueltos" }, - search: { title: "Buscar" }, - }, - search: { - placeholder: "Buscar tickets y mensajes.", - }, - buttons: { - showAll: "Todos", - }, - }, - transferTicketModal: { - title: "Transferir Ticket", - fieldLabel: "Escriba para buscar usuarios", - noOptions: "No se encontraron usuarios con ese nombre", - buttons: { - ok: "Transferir", - cancel: "Cancelar", - }, - }, - ticketsList: { - pendingHeader: "Cola", - assignedHeader: "Trabajando en", - noTicketsTitle: "¡Nada acá!", - noTicketsMessage: - "No se encontraron tickets con este estado o término de búsqueda", - buttons: { - accept: "Acceptar", - }, - }, - newTicketModal: { - title: "Crear Ticket", - fieldLabel: "Escribe para buscar un contacto", - add: "Añadir", - buttons: { - ok: "Guardar", - cancel: "Cancelar", - }, - }, - mainDrawer: { - listItems: { - dashboard: "Dashboard", - connections: "Conexiones", - tickets: "Tickets", - contacts: "Contactos", - queues: "Linhas", - administration: "Administración", - users: "Usuarios", - settings: "Configuración", - }, - appBar: { - user: { - profile: "Perfil", - logout: "Cerrar Sesión", - }, - }, - }, - notifications: { - noTickets: "Sin notificaciones.", - }, - queues: { - title: "Linhas", - table: { - name: "Nombre", - color: "Color", - greeting: "Mensaje de saludo", - actions: "Comportamiento", - }, - buttons: { - add: "Agregar cola", - }, - confirmationModal: { - deleteTitle: "Eliminar", - deleteMessage: - "¿Estás seguro? ¡Esta acción no se puede revertir! Los tickets en esa cola seguirán existiendo, pero ya no tendrán ninguna cola asignada.", - }, - }, - queueSelect: { - inputLabel: "Linhas", - }, - users: { - title: "Usuarios", - table: { - name: "Nombre", - email: "Correo Electrónico", - profile: "Perfil", - actions: "Acciones", - }, - buttons: { - add: "Agregar usuario", - }, - toasts: { - deleted: "Usuario borrado satisfactoriamente.", - }, - confirmationModal: { - deleteTitle: "Borrar", - deleteMessage: - "Toda la información del usuario se perderá. Los tickets abiertos de los usuarios se moverán a la cola.", - }, - }, - settings: { - success: "Configuración guardada satisfactoriamente.", - title: "Configuración", - settings: { - userCreation: { - name: "Creación de usuarios", - options: { - enabled: "Habilitado", - disabled: "Deshabilitado", - }, - }, - }, - }, - messagesList: { - header: { - assignedTo: "Asignado a:", - buttons: { - return: "Devolver", - resolve: "Resolver", - reopen: "Reabrir", - accept: "Aceptar", - }, - }, - }, - messagesInput: { - placeholderOpen: "Escribe un mensaje", - placeholderClosed: - "Vuelva a abrir o acepte este ticket para enviar un mensaje.", - signMessage: "Firmar", - }, - contactDrawer: { - header: "Detalles del contacto", - buttons: { - edit: "Editar contacto", - }, - extraInfo: "Otra información", - }, - ticketOptionsMenu: { - delete: "Borrar", - transfer: "Transferir", - confirmationModal: { - title: "¿Borrar ticket #", - titleFrom: "del contacto ", - message: - "¡Atención! Todos los mensajes Todos los mensajes relacionados con el ticket se perderán.", - }, - buttons: { - delete: "Borrar", - cancel: "Cancelar", - }, - }, - confirmationModal: { - buttons: { - confirm: "Ok", - cancel: "Cancelar", - }, - }, - messageOptionsMenu: { - delete: "Borrar", - reply: "Responder", - confirmationModal: { - title: "¿Borrar mensaje?", - message: "Esta acción no puede ser revertida.", - }, - }, - backendErrors: { - ERR_NO_OTHER_WHATSAPP: - "Debe haber al menos una conexión de WhatsApp predeterminada.", - ERR_NO_DEF_WAPP_FOUND: - "No se encontró WhatsApp predeterminado. Verifique la página de conexiones.", - ERR_WAPP_NOT_INITIALIZED: - "Esta sesión de WhatsApp no ​​está inicializada. Verifique la página de conexiones.", - ERR_WAPP_CHECK_CONTACT: - "No se pudo verificar el contacto de WhatsApp. Verifique la página de conexiones.", - ERR_WAPP_INVALID_CONTACT: "Este no es un número de whatsapp válido.", - ERR_WAPP_DOWNLOAD_MEDIA: - "No se pudieron descargar los medios de WhatsApp. Verifique la página de conexiones.", - ERR_INVALID_CREDENTIALS: "Error de autenticación. Vuelva a intentarlo.", - ERR_SENDING_WAPP_MSG: - "Error al enviar el mensaje de WhatsApp. Verifique la página de conexiones.", - ERR_DELETE_WAPP_MSG: "No se pudo borrar el mensaje de WhatsApp.", - ERR_OTHER_OPEN_TICKET: "Ya hay un ticket abierto para este contacto.", - ERR_SESSION_EXPIRED: "Sesión caducada. Inicie sesión.", - ERR_USER_CREATION_DISABLED: - "La creación de usuarios fue deshabilitada por el administrador.", - ERR_NO_PERMISSION: "No tienes permiso para acceder a este recurso.", - ERR_DUPLICATED_CONTACT: "Ya existe un contacto con este número.", - ERR_NO_SETTING_FOUND: - "No se encontró ninguna configuración con este ID.", - ERR_NO_CONTACT_FOUND: "No se encontró ningún contacto con este ID.", - ERR_NO_TICKET_FOUND: "No se encontró ningún ticket con este ID.", - ERR_NO_USER_FOUND: "No se encontró ningún usuario con este ID.", - ERR_NO_WAPP_FOUND: "No se encontró WhatsApp con este ID.", - ERR_CREATING_MESSAGE: "Error al crear el mensaje en la base de datos.", - ERR_CREATING_TICKET: "Error al crear el ticket en la base de datos.", - ERR_FETCH_WAPP_MSG: - "Error al obtener el mensaje en WhtasApp, tal vez sea demasiado antiguo.", - ERR_QUEUE_COLOR_ALREADY_EXISTS: - "Este color ya está en uso, elija otro.", - ERR_WAPP_GREETING_REQUIRED: - "El mensaje de saludo es obligatorio cuando hay más de una cola.", - }, - }, - }, + es: { + translations: { + signup: { + title: "Registro", + toasts: { + success: + "¡El usuario ha sido creado satisfactoriamente! ¡Ahora inicia sesión!", + fail: "Error creando el usuario. Verifica la data reportada.", + }, + form: { + name: "Nombre", + email: "Correo Electrónico", + password: "Contraseña", + }, + buttons: { + submit: "Regístrate", + login: "¿Ya tienes una cuenta? ¡Inicia sesión!", + }, + }, + login: { + title: "Inicio de Sesión", + form: { + email: "Correo Electrónico", + password: "Contraseña", + }, + buttons: { + submit: "Ingresa", + register: "¿No tienes cuenta? ¡Regístrate!", + }, + }, + auth: { + toasts: { + success: "¡Inicio de sesión exitoso!", + }, + }, + dashboard: { + charts: { + perDay: { + title: "Tickets hoy: ", + }, + }, + }, + connections: { + title: "Conexiones", + toasts: { + deleted: + "¡La conexión de WhatsApp ha sido borrada satisfactoriamente!", + }, + confirmationModal: { + deleteTitle: "Borrar", + deleteMessage: "¿Estás seguro? Este proceso no puede ser revertido.", + disconnectTitle: "Desconectar", + disconnectMessage: "Estás seguro? Deberá volver a leer el código QR", + }, + buttons: { + add: "Agrega WhatsApp", + disconnect: "Desconectar", + tryAgain: "Inténtalo de nuevo", + qrcode: "QR CODE", + newQr: "Nuevo QR CODE", + connecting: "Conectando", + }, + toolTips: { + disconnected: { + title: "No se pudo iniciar la sesión de WhatsApp", + content: + "Asegúrese de que su teléfono celular esté conectado a Internet y vuelva a intentarlo o solicite un nuevo código QR", + }, + qrcode: { + title: "Esperando la lectura del código QR", + content: + "Haga clic en el botón 'CÓDIGO QR' y lea el Código QR con su teléfono celular para iniciar la sesión", + }, + connected: { + title: "Conexión establecida", + }, + timeout: { + title: "Se perdió la conexión con el teléfono celular", + content: + "Asegúrese de que su teléfono celular esté conectado a Internet y que WhatsApp esté abierto, o haga clic en el botón 'Desconectar' para obtener un nuevo código QR", + }, + }, + table: { + name: "Nombre", + status: "Estado", + lastUpdate: "Última Actualización", + default: "Por Defecto", + actions: "Acciones", + session: "Sesión", + }, + }, + whatsappModal: { + title: { + add: "Agrega WhatsApp", + edit: "Edita WhatsApp", + }, + form: { + name: "Nombre", + default: "Por Defecto", + }, + buttons: { + okAdd: "Agregar", + okEdit: "Guardar", + cancel: "Cancelar", + }, + success: "WhatsApp guardado satisfactoriamente.", + }, + qrCode: { + message: "Lée el código QR para empezar la sesión.", + }, + contacts: { + title: "Contactos", + toasts: { + deleted: "¡Contacto borrado satisfactoriamente!", + }, + searchPlaceholder: "Buscar...", + confirmationModal: { + deleteTitle: "Borrar", + importTitlte: "Importar contactos", + deleteMessage: + "¿Estás seguro que deseas borrar este contacto? Todos los tickets relacionados se perderán.", + importMessage: + "¿Quieres importar todos los contactos desde tu teléfono?", + }, + buttons: { + import: "Importar Contactos", + add: "Agregar Contacto", + }, + table: { + name: "Nombre", + whatsapp: "WhatsApp", + email: "Correo Electrónico", + actions: "Acciones", + }, + }, + contactModal: { + title: { + add: "Agregar contacto", + edit: "Editar contacto", + }, + form: { + mainInfo: "Detalles del contacto", + extraInfo: "Información adicional", + name: "Nombre", + number: "Número de Whatsapp", + email: "Correo Electrónico", + extraName: "Nombre del Campo", + extraValue: "Valor", + }, + buttons: { + addExtraInfo: "Agregar información", + okAdd: "Agregar", + okEdit: "Guardar", + cancel: "Cancelar", + }, + success: "Contacto guardado satisfactoriamente.", + }, + quickAnswersModal: { + title: { + add: "Agregar respuesta rápida", + edit: "Editar respuesta rápida", + }, + form: { + shortcut: "Atajo", + message: "Respuesta rápida", + }, + buttons: { + okAdd: "Agregar", + okEdit: "Guardar", + cancel: "Cancelar", + }, + success: "Respuesta rápida guardada correctamente.", + }, + queueModal: { + title: { + add: "Agregar cola", + edit: "Editar cola", + }, + form: { + name: "Nombre", + color: "Color", + greetingMessage: "Mensaje de saludo", + }, + buttons: { + okAdd: "Añadir", + okEdit: "Ahorrar", + cancel: "Cancelar", + }, + }, + userModal: { + title: { + add: "Agregar usuario", + edit: "Editar usuario", + }, + form: { + name: "Nombre", + email: "Correo Electrónico", + password: "Contraseña", + profile: "Perfil", + }, + buttons: { + okAdd: "Agregar", + okEdit: "Guardar", + cancel: "Cancelar", + }, + success: "Usuario guardado satisfactoriamente.", + }, + chat: { + noTicketMessage: "Selecciona un ticket para empezar a chatear.", + }, + ticketsManager: { + buttons: { + newTicket: "Nuevo", + }, + }, + ticketsQueueSelect: { + placeholder: "Linhas", + }, + tickets: { + toasts: { + deleted: "El ticket en el que estabas ha sido borrado.", + }, + notification: { + message: "Mensaje de", + }, + tabs: { + open: { title: "Bandeja" }, + closed: { title: "Resueltos" }, + search: { title: "Buscar" }, + }, + search: { + placeholder: "Buscar tickets y mensajes.", + }, + buttons: { + showAll: "Todos", + }, + }, + transferTicketModal: { + title: "Transferir Ticket", + fieldLabel: "Escriba para buscar usuarios", + noOptions: "No se encontraron usuarios con ese nombre", + buttons: { + ok: "Transferir", + cancel: "Cancelar", + }, + }, + ticketsList: { + pendingHeader: "Cola", + assignedHeader: "Trabajando en", + noTicketsTitle: "¡Nada acá!", + noTicketsMessage: + "No se encontraron tickets con este estado o término de búsqueda", + buttons: { + accept: "Acceptar", + }, + }, + newTicketModal: { + title: "Crear Ticket", + fieldLabel: "Escribe para buscar un contacto", + add: "Añadir", + buttons: { + ok: "Guardar", + cancel: "Cancelar", + }, + }, + mainDrawer: { + listItems: { + dashboard: "Dashboard", + connections: "Conexiones", + tickets: "Tickets", + contacts: "Contactos", + quickAnswers: "Respuestas rápidas", + queues: "Linhas", + administration: "Administración", + users: "Usuarios", + settings: "Configuración", + }, + appBar: { + user: { + profile: "Perfil", + logout: "Cerrar Sesión", + }, + }, + }, + notifications: { + noTickets: "Sin notificaciones.", + }, + queues: { + title: "Linhas", + table: { + name: "Nombre", + color: "Color", + greeting: "Mensaje de saludo", + actions: "Comportamiento", + }, + buttons: { + add: "Agregar cola", + }, + confirmationModal: { + deleteTitle: "Eliminar", + deleteMessage: + "¿Estás seguro? ¡Esta acción no se puede revertir! Los tickets en esa cola seguirán existiendo, pero ya no tendrán ninguna cola asignada.", + }, + }, + queueSelect: { + inputLabel: "Linhas", + }, + quickAnswers: { + title: "Respuestas rápidas", + table: { + shortcut: "Atajo", + message: "Respuesta rápida", + actions: "Acciones", + }, + buttons: { + add: "Agregar respuesta rápida", + }, + toasts: { + deleted: "Respuesta rápida eliminada correctamente", + }, + searchPlaceholder: "Buscar ...", + confirmationModal: { + deleteTitle: + "¿Está seguro de que desea eliminar esta respuesta rápida?", + deleteMessage: "Esta acción no se puede deshacer.", + }, + }, + users: { + title: "Usuarios", + table: { + name: "Nombre", + email: "Correo Electrónico", + profile: "Perfil", + actions: "Acciones", + }, + buttons: { + add: "Agregar usuario", + }, + toasts: { + deleted: "Usuario borrado satisfactoriamente.", + }, + confirmationModal: { + deleteTitle: "Borrar", + deleteMessage: + "Toda la información del usuario se perderá. Los tickets abiertos de los usuarios se moverán a la cola.", + }, + }, + settings: { + success: "Configuración guardada satisfactoriamente.", + title: "Configuración", + settings: { + userCreation: { + name: "Creación de usuarios", + options: { + enabled: "Habilitado", + disabled: "Deshabilitado", + }, + }, + }, + }, + messagesList: { + header: { + assignedTo: "Asignado a:", + buttons: { + return: "Devolver", + resolve: "Resolver", + reopen: "Reabrir", + accept: "Aceptar", + }, + }, + }, + messagesInput: { + placeholderOpen: "Escribe un mensaje", + placeholderClosed: + "Vuelva a abrir o acepte este ticket para enviar un mensaje.", + signMessage: "Firmar", + }, + contactDrawer: { + header: "Detalles del contacto", + buttons: { + edit: "Editar contacto", + }, + extraInfo: "Otra información", + }, + ticketOptionsMenu: { + delete: "Borrar", + transfer: "Transferir", + confirmationModal: { + title: "¿Borrar ticket #", + titleFrom: "del contacto ", + message: + "¡Atención! Todos los mensajes Todos los mensajes relacionados con el ticket se perderán.", + }, + buttons: { + delete: "Borrar", + cancel: "Cancelar", + }, + }, + confirmationModal: { + buttons: { + confirm: "Ok", + cancel: "Cancelar", + }, + }, + messageOptionsMenu: { + delete: "Borrar", + reply: "Responder", + confirmationModal: { + title: "¿Borrar mensaje?", + message: "Esta acción no puede ser revertida.", + }, + }, + backendErrors: { + ERR_NO_OTHER_WHATSAPP: + "Debe haber al menos una conexión de WhatsApp predeterminada.", + ERR_NO_DEF_WAPP_FOUND: + "No se encontró WhatsApp predeterminado. Verifique la página de conexiones.", + ERR_WAPP_NOT_INITIALIZED: + "Esta sesión de WhatsApp no ​​está inicializada. Verifique la página de conexiones.", + ERR_WAPP_CHECK_CONTACT: + "No se pudo verificar el contacto de WhatsApp. Verifique la página de conexiones.", + ERR_WAPP_INVALID_CONTACT: "Este no es un número de whatsapp válido.", + ERR_WAPP_DOWNLOAD_MEDIA: + "No se pudieron descargar los medios de WhatsApp. Verifique la página de conexiones.", + ERR_INVALID_CREDENTIALS: "Error de autenticación. Vuelva a intentarlo.", + ERR_SENDING_WAPP_MSG: + "Error al enviar el mensaje de WhatsApp. Verifique la página de conexiones.", + ERR_DELETE_WAPP_MSG: "No se pudo borrar el mensaje de WhatsApp.", + ERR_OTHER_OPEN_TICKET: "Ya hay un ticket abierto para este contacto.", + ERR_SESSION_EXPIRED: "Sesión caducada. Inicie sesión.", + ERR_USER_CREATION_DISABLED: + "La creación de usuarios fue deshabilitada por el administrador.", + ERR_NO_PERMISSION: "No tienes permiso para acceder a este recurso.", + ERR_DUPLICATED_CONTACT: "Ya existe un contacto con este número.", + ERR_NO_SETTING_FOUND: + "No se encontró ninguna configuración con este ID.", + ERR_NO_CONTACT_FOUND: "No se encontró ningún contacto con este ID.", + ERR_NO_TICKET_FOUND: "No se encontró ningún ticket con este ID.", + ERR_NO_USER_FOUND: "No se encontró ningún usuario con este ID.", + ERR_NO_WAPP_FOUND: "No se encontró WhatsApp con este ID.", + ERR_CREATING_MESSAGE: "Error al crear el mensaje en la base de datos.", + ERR_CREATING_TICKET: "Error al crear el ticket en la base de datos.", + ERR_FETCH_WAPP_MSG: + "Error al obtener el mensaje en WhtasApp, tal vez sea demasiado antiguo.", + ERR_QUEUE_COLOR_ALREADY_EXISTS: + "Este color ya está en uso, elija otro.", + ERR_WAPP_GREETING_REQUIRED: + "El mensaje de saludo es obligatorio cuando hay más de una cola.", + }, + }, + }, }; export { messages }; diff --git a/frontend/src/translate/languages/pt.js b/frontend/src/translate/languages/pt.js index f223ead..1c3e400 100644 --- a/frontend/src/translate/languages/pt.js +++ b/frontend/src/translate/languages/pt.js @@ -1,414 +1,451 @@ const messages = { - pt: { - translations: { - signup: { - title: "Cadastre-se", - toasts: { - success: "Usuário criado com sucesso! Faça seu login!!!.", - fail: "Erro ao criar usuário. Verifique os dados informados.", - }, - form: { - name: "Nome", - email: "Email", - password: "Senha", - }, - buttons: { - submit: "Cadastrar", - login: "Já tem uma conta? Entre!", - }, - }, - login: { - title: "Login", - form: { - email: "Email", - password: "Senha", - }, - buttons: { - submit: "Entrar", - register: "Não tem um conta? Cadastre-se!", - }, - }, - auth: { - toasts: { - success: "Login efetuado com sucesso!", - }, - }, - dashboard: { - charts: { - perDay: { - title: "Tickets hoje: ", - }, - }, - }, - connections: { - title: "Conexões", - toasts: { - deleted: "Conexão com o WhatsApp excluída com sucesso!", - }, - confirmationModal: { - deleteTitle: "Deletar", - deleteMessage: "Você tem certeza? Essa ação não pode ser revertida.", - disconnectTitle: "Desconectar", - disconnectMessage: - "Tem certeza? Você precisará ler o QR Code novamente.", - }, - buttons: { - add: "Adicionar WhatsApp", - disconnect: "desconectar", - tryAgain: "Tentar novamente", - qrcode: "QR CODE", - newQr: "Novo QR CODE", - connecting: "Conectando", - }, - toolTips: { - disconnected: { - title: "Falha ao iniciar sessão do WhatsApp", - content: - "Certifique-se de que seu celular esteja conectado à internet e tente novamente, ou solicite um novo QR Code", - }, - qrcode: { - title: "Esperando leitura do QR Code", - content: - "Clique no botão 'QR CODE' e leia o QR Code com o seu celular para iniciar a sessão", - }, - connected: { - title: "Conexão estabelecida!", - }, - timeout: { - title: "A conexão com o celular foi perdida", - content: - "Certifique-se de que seu celular esteja conectado à internet e o WhatsApp esteja aberto, ou clique no botão 'Desconectar' para obter um novo QR Code", - }, - }, - table: { - name: "Nome", - status: "Status", - lastUpdate: "Última atualização", - default: "Padrão", - actions: "Ações", - session: "Sessão", - }, - }, - whatsappModal: { - title: { - add: "Adicionar WhatsApp", - edit: "Editar WhatsApp", - }, - form: { - name: "Nome", - default: "Padrão", - }, - buttons: { - okAdd: "Adicionar", - okEdit: "Salvar", - cancel: "Cancelar", - }, - success: "WhatsApp salvo com sucesso.", - }, - qrCode: { - message: "Leia o QrCode para iniciar a sessão", - }, - contacts: { - title: "Contatos", - toasts: { - deleted: "Contato excluído com sucesso!", - }, - searchPlaceholder: "Pesquisar...", - confirmationModal: { - deleteTitle: "Deletar ", - importTitlte: "Importar contatos", - deleteMessage: - "Tem certeza que deseja deletar este contato? Todos os tickets relacionados serão perdidos.", - importMessage: "Deseja importas todos os contatos do telefone?", - }, - buttons: { - import: "Importar Contatos", - add: "Adicionar Contato", - }, - table: { - name: "Nome", - whatsapp: "WhatsApp", - email: "Email", - actions: "Ações", - }, - }, - contactModal: { - title: { - add: "Adicionar contato", - edit: "Editar contato", - }, - form: { - mainInfo: "Dados do contato", - extraInfo: "Informações adicionais", - name: "Nome", - number: "Número do Whatsapp", - email: "Email", - extraName: "Nome do campo", - extraValue: "Valor", - }, - buttons: { - addExtraInfo: "Adicionar informação", - okAdd: "Adicionar", - okEdit: "Salvar", - cancel: "Cancelar", - }, - success: "Contato salvo com sucesso.", - }, - queueModal: { - title: { - add: "Adicionar fila", - edit: "Editar fila", - }, - form: { - name: "Nome", - color: "Cor", - greetingMessage: "Mensagem de saudação", - }, - buttons: { - okAdd: "Adicionar", - okEdit: "Salvar", - cancel: "Cancelar", - }, - }, - userModal: { - title: { - add: "Adicionar usuário", - edit: "Editar usuário", - }, - form: { - name: "Nome", - email: "Email", - password: "Senha", - profile: "Perfil", - }, - buttons: { - okAdd: "Adicionar", - okEdit: "Salvar", - cancel: "Cancelar", - }, - success: "Usuário salvo com sucesso.", - }, - chat: { - noTicketMessage: "Selecione um ticket para começar a conversar.", - }, - ticketsManager: { - buttons: { - newTicket: "Novo", - }, - }, - ticketsQueueSelect: { - placeholder: "Filas", - }, - tickets: { - toasts: { - deleted: "O ticket que você estava foi deletado.", - }, - notification: { - message: "Mensagem de", - }, - tabs: { - open: { title: "Inbox" }, - closed: { title: "Resolvidos" }, - search: { title: "Busca" }, - }, - search: { - placeholder: "Buscar tickets e mensagens", - }, - buttons: { - showAll: "Todos", - }, - }, - transferTicketModal: { - title: "Transferir Ticket", - fieldLabel: "Digite para buscar usuários", - noOptions: "Nenhum usuário encontrado com esse nome", - buttons: { - ok: "Transferir", - cancel: "Cancelar", - }, - }, - ticketsList: { - pendingHeader: "Aguardando", - assignedHeader: "Atendendo", - noTicketsTitle: "Nada aqui!", - noTicketsMessage: - "Nenhum ticket encontrado com esse status ou termo pesquisado", - buttons: { - accept: "Aceitar", - }, - }, - newTicketModal: { - title: "Criar Ticket", - fieldLabel: "Digite para pesquisar o contato", - add: "Adicionar", - buttons: { - ok: "Salvar", - cancel: "Cancelar", - }, - }, - mainDrawer: { - listItems: { - dashboard: "Dashboard", - connections: "Conexões", - tickets: "Tickets", - contacts: "Contatos", - queues: "Filas", - administration: "Administração", - users: "Usuários", - settings: "Configurações", - }, - appBar: { - user: { - profile: "Perfil", - logout: "Sair", - }, - }, - }, - notifications: { - noTickets: "Nenhuma notificação.", - }, - queues: { - title: "Filas", - table: { - name: "Nome", - color: "Cor", - greeting: "Mensagem de saudação", - actions: "Ações", - }, - buttons: { - add: "Adicionar fila", - }, - confirmationModal: { - deleteTitle: "Excluir", - deleteMessage: - "Você tem certeza? Essa ação não pode ser revertida! Os tickets dessa fila continuarão existindo, mas não terão mais nenhuma fila atribuída.", - }, - }, - queueSelect: { - inputLabel: "Filas", - }, - users: { - title: "Usuários", - table: { - name: "Nome", - email: "Email", - profile: "Perfil", - actions: "Ações", - }, - buttons: { - add: "Adicionar usuário", - }, - toasts: { - deleted: "Usuário excluído com sucesso.", - }, - confirmationModal: { - deleteTitle: "Excluir", - deleteMessage: - "Todos os dados do usuário serão perdidos. Os tickets abertos deste usuário serão movidos para a fila.", - }, - }, - settings: { - success: "Configurações salvas com sucesso.", - title: "Configurações", - settings: { - userCreation: { - name: "Criação de usuário", - options: { - enabled: "Ativado", - disabled: "Desativado", - }, - }, - }, - }, - messagesList: { - header: { - assignedTo: "Atribuído à:", - buttons: { - return: "Retornar", - resolve: "Resolver", - reopen: "Reabrir", - accept: "Aceitar", - }, - }, - }, - messagesInput: { - placeholderOpen: "Digite uma mensagem", - placeholderClosed: - "Reabra ou aceite esse ticket para enviar uma mensagem.", - signMessage: "Assinar", - }, - contactDrawer: { - header: "Dados do contato", - buttons: { - edit: "Editar contato", - }, - extraInfo: "Outras informações", - }, - ticketOptionsMenu: { - delete: "Deletar", - transfer: "Transferir", - confirmationModal: { - title: "Deletar o ticket do contato", - message: - "Atenção! Todas as mensagens relacionadas ao ticket serão perdidas.", - }, - buttons: { - delete: "Excluir", - cancel: "Cancelar", - }, - }, - confirmationModal: { - buttons: { - confirm: "Ok", - cancel: "Cancelar", - }, - }, - messageOptionsMenu: { - delete: "Deletar", - reply: "Responder", - confirmationModal: { - title: "Apagar mensagem?", - message: "Esta ação não pode ser revertida.", - }, - }, - backendErrors: { - ERR_NO_OTHER_WHATSAPP: "Deve haver pelo menos um WhatsApp padrão.", - ERR_NO_DEF_WAPP_FOUND: - "Nenhum WhatsApp padrão encontrado. Verifique a página de conexões.", - ERR_WAPP_NOT_INITIALIZED: - "Esta sessão do WhatsApp não foi inicializada. Verifique a página de conexões.", - ERR_WAPP_CHECK_CONTACT: - "Não foi possível verificar o contato do WhatsApp. Verifique a página de conexões", - ERR_WAPP_INVALID_CONTACT: "Este não é um número de Whatsapp válido.", - ERR_WAPP_DOWNLOAD_MEDIA: - "Não foi possível baixar mídia do WhatsApp. Verifique a página de conexões.", - ERR_INVALID_CREDENTIALS: - "Erro de autenticação. Por favor, tente novamente.", - ERR_SENDING_WAPP_MSG: - "Erro ao enviar mensagem do WhatsApp. Verifique a página de conexões.", - ERR_DELETE_WAPP_MSG: "Não foi possível excluir a mensagem do WhatsApp.", - ERR_OTHER_OPEN_TICKET: "Já existe um tíquete aberto para este contato.", - ERR_SESSION_EXPIRED: "Sessão expirada. Por favor entre.", - ERR_USER_CREATION_DISABLED: - "A criação do usuário foi desabilitada pelo administrador.", - ERR_NO_PERMISSION: "Você não tem permissão para acessar este recurso.", - ERR_DUPLICATED_CONTACT: "Já existe um contato com este número.", - ERR_NO_SETTING_FOUND: "Nenhuma configuração encontrada com este ID.", - ERR_NO_CONTACT_FOUND: "Nenhum contato encontrado com este ID.", - ERR_NO_TICKET_FOUND: "Nenhum tíquete encontrado com este ID.", - ERR_NO_USER_FOUND: "Nenhum usuário encontrado com este ID.", - ERR_NO_WAPP_FOUND: "Nenhum WhatsApp encontrado com este ID.", - ERR_CREATING_MESSAGE: "Erro ao criar mensagem no banco de dados.", - ERR_CREATING_TICKET: "Erro ao criar tíquete no banco de dados.", - ERR_FETCH_WAPP_MSG: - "Erro ao buscar a mensagem no WhtasApp, talvez ela seja muito antiga.", - ERR_QUEUE_COLOR_ALREADY_EXISTS: - "Esta cor já está em uso, escolha outra.", - ERR_WAPP_GREETING_REQUIRED: - "A mensagem de saudação é obrigatório quando há mais de uma fila.", - }, - }, - }, + pt: { + translations: { + signup: { + title: "Cadastre-se", + toasts: { + success: "Usuário criado com sucesso! Faça seu login!!!.", + fail: "Erro ao criar usuário. Verifique os dados informados.", + }, + form: { + name: "Nome", + email: "Email", + password: "Senha", + }, + buttons: { + submit: "Cadastrar", + login: "Já tem uma conta? Entre!", + }, + }, + login: { + title: "Login", + form: { + email: "Email", + password: "Senha", + }, + buttons: { + submit: "Entrar", + register: "Não tem um conta? Cadastre-se!", + }, + }, + auth: { + toasts: { + success: "Login efetuado com sucesso!", + }, + }, + dashboard: { + charts: { + perDay: { + title: "Tickets hoje: ", + }, + }, + }, + connections: { + title: "Conexões", + toasts: { + deleted: "Conexão com o WhatsApp excluída com sucesso!", + }, + confirmationModal: { + deleteTitle: "Deletar", + deleteMessage: "Você tem certeza? Essa ação não pode ser revertida.", + disconnectTitle: "Desconectar", + disconnectMessage: + "Tem certeza? Você precisará ler o QR Code novamente.", + }, + buttons: { + add: "Adicionar WhatsApp", + disconnect: "desconectar", + tryAgain: "Tentar novamente", + qrcode: "QR CODE", + newQr: "Novo QR CODE", + connecting: "Conectando", + }, + toolTips: { + disconnected: { + title: "Falha ao iniciar sessão do WhatsApp", + content: + "Certifique-se de que seu celular esteja conectado à internet e tente novamente, ou solicite um novo QR Code", + }, + qrcode: { + title: "Esperando leitura do QR Code", + content: + "Clique no botão 'QR CODE' e leia o QR Code com o seu celular para iniciar a sessão", + }, + connected: { + title: "Conexão estabelecida!", + }, + timeout: { + title: "A conexão com o celular foi perdida", + content: + "Certifique-se de que seu celular esteja conectado à internet e o WhatsApp esteja aberto, ou clique no botão 'Desconectar' para obter um novo QR Code", + }, + }, + table: { + name: "Nome", + status: "Status", + lastUpdate: "Última atualização", + default: "Padrão", + actions: "Ações", + session: "Sessão", + }, + }, + whatsappModal: { + title: { + add: "Adicionar WhatsApp", + edit: "Editar WhatsApp", + }, + form: { + name: "Nome", + default: "Padrão", + }, + buttons: { + okAdd: "Adicionar", + okEdit: "Salvar", + cancel: "Cancelar", + }, + success: "WhatsApp salvo com sucesso.", + }, + qrCode: { + message: "Leia o QrCode para iniciar a sessão", + }, + contacts: { + title: "Contatos", + toasts: { + deleted: "Contato excluído com sucesso!", + }, + searchPlaceholder: "Pesquisar...", + confirmationModal: { + deleteTitle: "Deletar ", + importTitlte: "Importar contatos", + deleteMessage: + "Tem certeza que deseja deletar este contato? Todos os tickets relacionados serão perdidos.", + importMessage: "Deseja importas todos os contatos do telefone?", + }, + buttons: { + import: "Importar Contatos", + add: "Adicionar Contato", + }, + table: { + name: "Nome", + whatsapp: "WhatsApp", + email: "Email", + actions: "Ações", + }, + }, + contactModal: { + title: { + add: "Adicionar contato", + edit: "Editar contato", + }, + form: { + mainInfo: "Dados do contato", + extraInfo: "Informações adicionais", + name: "Nome", + number: "Número do Whatsapp", + email: "Email", + extraName: "Nome do campo", + extraValue: "Valor", + }, + buttons: { + addExtraInfo: "Adicionar informação", + okAdd: "Adicionar", + okEdit: "Salvar", + cancel: "Cancelar", + }, + success: "Contato salvo com sucesso.", + }, + quickAnswersModal: { + title: { + add: "Adicionar Resposta Rápida", + edit: "Editar Resposta Rápida", + }, + form: { + shortcut: "Atalho", + message: "Resposta Rápida", + }, + buttons: { + okAdd: "Adicionar", + okEdit: "Salvar", + cancel: "Cancelar", + }, + success: "Resposta Rápida salva com sucesso.", + }, + queueModal: { + title: { + add: "Adicionar fila", + edit: "Editar fila", + }, + form: { + name: "Nome", + color: "Cor", + greetingMessage: "Mensagem de saudação", + }, + buttons: { + okAdd: "Adicionar", + okEdit: "Salvar", + cancel: "Cancelar", + }, + }, + userModal: { + title: { + add: "Adicionar usuário", + edit: "Editar usuário", + }, + form: { + name: "Nome", + email: "Email", + password: "Senha", + profile: "Perfil", + }, + buttons: { + okAdd: "Adicionar", + okEdit: "Salvar", + cancel: "Cancelar", + }, + success: "Usuário salvo com sucesso.", + }, + chat: { + noTicketMessage: "Selecione um ticket para começar a conversar.", + }, + ticketsManager: { + buttons: { + newTicket: "Novo", + }, + }, + ticketsQueueSelect: { + placeholder: "Filas", + }, + tickets: { + toasts: { + deleted: "O ticket que você estava foi deletado.", + }, + notification: { + message: "Mensagem de", + }, + tabs: { + open: { title: "Inbox" }, + closed: { title: "Resolvidos" }, + search: { title: "Busca" }, + }, + search: { + placeholder: "Buscar tickets e mensagens", + }, + buttons: { + showAll: "Todos", + }, + }, + transferTicketModal: { + title: "Transferir Ticket", + fieldLabel: "Digite para buscar usuários", + noOptions: "Nenhum usuário encontrado com esse nome", + buttons: { + ok: "Transferir", + cancel: "Cancelar", + }, + }, + ticketsList: { + pendingHeader: "Aguardando", + assignedHeader: "Atendendo", + noTicketsTitle: "Nada aqui!", + noTicketsMessage: + "Nenhum ticket encontrado com esse status ou termo pesquisado", + buttons: { + accept: "Aceitar", + }, + }, + newTicketModal: { + title: "Criar Ticket", + fieldLabel: "Digite para pesquisar o contato", + add: "Adicionar", + buttons: { + ok: "Salvar", + cancel: "Cancelar", + }, + }, + mainDrawer: { + listItems: { + dashboard: "Dashboard", + connections: "Conexões", + tickets: "Tickets", + contacts: "Contatos", + quickAnswers: "Respostas Rápidas", + queues: "Filas", + administration: "Administração", + users: "Usuários", + settings: "Configurações", + }, + appBar: { + user: { + profile: "Perfil", + logout: "Sair", + }, + }, + }, + notifications: { + noTickets: "Nenhuma notificação.", + }, + queues: { + title: "Filas", + table: { + name: "Nome", + color: "Cor", + greeting: "Mensagem de saudação", + actions: "Ações", + }, + buttons: { + add: "Adicionar fila", + }, + confirmationModal: { + deleteTitle: "Excluir", + deleteMessage: + "Você tem certeza? Essa ação não pode ser revertida! Os tickets dessa fila continuarão existindo, mas não terão mais nenhuma fila atribuída.", + }, + }, + queueSelect: { + inputLabel: "Filas", + }, + quickAnswers: { + title: "Respostas Rápidas", + table: { + shortcut: "Atalho", + message: "Resposta Rápida", + actions: "Ações", + }, + buttons: { + add: "Adicionar Resposta Rápida", + }, + toasts: { + deleted: "Resposta Rápida excluída com sucesso.", + }, + searchPlaceholder: "Pesquisar...", + confirmationModal: { + deleteTitle: + "Você tem certeza que quer excluir esta Resposta Rápida: ", + deleteMessage: "Esta ação não pode ser revertida.", + }, + }, + users: { + title: "Usuários", + table: { + name: "Nome", + email: "Email", + profile: "Perfil", + actions: "Ações", + }, + buttons: { + add: "Adicionar usuário", + }, + toasts: { + deleted: "Usuário excluído com sucesso.", + }, + confirmationModal: { + deleteTitle: "Excluir", + deleteMessage: + "Todos os dados do usuário serão perdidos. Os tickets abertos deste usuário serão movidos para a fila.", + }, + }, + settings: { + success: "Configurações salvas com sucesso.", + title: "Configurações", + settings: { + userCreation: { + name: "Criação de usuário", + options: { + enabled: "Ativado", + disabled: "Desativado", + }, + }, + }, + }, + messagesList: { + header: { + assignedTo: "Atribuído à:", + buttons: { + return: "Retornar", + resolve: "Resolver", + reopen: "Reabrir", + accept: "Aceitar", + }, + }, + }, + messagesInput: { + placeholderOpen: "Digite uma mensagem", + placeholderClosed: + "Reabra ou aceite esse ticket para enviar uma mensagem.", + signMessage: "Assinar", + }, + contactDrawer: { + header: "Dados do contato", + buttons: { + edit: "Editar contato", + }, + extraInfo: "Outras informações", + }, + ticketOptionsMenu: { + delete: "Deletar", + transfer: "Transferir", + confirmationModal: { + title: "Deletar o ticket do contato", + message: + "Atenção! Todas as mensagens relacionadas ao ticket serão perdidas.", + }, + buttons: { + delete: "Excluir", + cancel: "Cancelar", + }, + }, + confirmationModal: { + buttons: { + confirm: "Ok", + cancel: "Cancelar", + }, + }, + messageOptionsMenu: { + delete: "Deletar", + reply: "Responder", + confirmationModal: { + title: "Apagar mensagem?", + message: "Esta ação não pode ser revertida.", + }, + }, + backendErrors: { + ERR_NO_OTHER_WHATSAPP: "Deve haver pelo menos um WhatsApp padrão.", + ERR_NO_DEF_WAPP_FOUND: + "Nenhum WhatsApp padrão encontrado. Verifique a página de conexões.", + ERR_WAPP_NOT_INITIALIZED: + "Esta sessão do WhatsApp não foi inicializada. Verifique a página de conexões.", + ERR_WAPP_CHECK_CONTACT: + "Não foi possível verificar o contato do WhatsApp. Verifique a página de conexões", + ERR_WAPP_INVALID_CONTACT: "Este não é um número de Whatsapp válido.", + ERR_WAPP_DOWNLOAD_MEDIA: + "Não foi possível baixar mídia do WhatsApp. Verifique a página de conexões.", + ERR_INVALID_CREDENTIALS: + "Erro de autenticação. Por favor, tente novamente.", + ERR_SENDING_WAPP_MSG: + "Erro ao enviar mensagem do WhatsApp. Verifique a página de conexões.", + ERR_DELETE_WAPP_MSG: "Não foi possível excluir a mensagem do WhatsApp.", + ERR_OTHER_OPEN_TICKET: "Já existe um tíquete aberto para este contato.", + ERR_SESSION_EXPIRED: "Sessão expirada. Por favor entre.", + ERR_USER_CREATION_DISABLED: + "A criação do usuário foi desabilitada pelo administrador.", + ERR_NO_PERMISSION: "Você não tem permissão para acessar este recurso.", + ERR_DUPLICATED_CONTACT: "Já existe um contato com este número.", + ERR_NO_SETTING_FOUND: "Nenhuma configuração encontrada com este ID.", + ERR_NO_CONTACT_FOUND: "Nenhum contato encontrado com este ID.", + ERR_NO_TICKET_FOUND: "Nenhum tíquete encontrado com este ID.", + ERR_NO_USER_FOUND: "Nenhum usuário encontrado com este ID.", + ERR_NO_WAPP_FOUND: "Nenhum WhatsApp encontrado com este ID.", + ERR_CREATING_MESSAGE: "Erro ao criar mensagem no banco de dados.", + ERR_CREATING_TICKET: "Erro ao criar tíquete no banco de dados.", + ERR_FETCH_WAPP_MSG: + "Erro ao buscar a mensagem no WhtasApp, talvez ela seja muito antiga.", + ERR_QUEUE_COLOR_ALREADY_EXISTS: + "Esta cor já está em uso, escolha outra.", + ERR_WAPP_GREETING_REQUIRED: + "A mensagem de saudação é obrigatório quando há mais de uma fila.", + }, + }, + }, }; export { messages };