Em andamento. Ajustes no frontend para que o atendende possa escolher fila. Alteração na busca por ticket pelo conteudo.
parent
34a0f72ac6
commit
4b23aad804
|
@ -28,14 +28,16 @@ interface Request {
|
||||||
contactId: number;
|
contactId: number;
|
||||||
status: string;
|
status: string;
|
||||||
userId: number;
|
userId: number;
|
||||||
msg?: string
|
msg?: string,
|
||||||
|
queueId?: string | undefined
|
||||||
}
|
}
|
||||||
|
|
||||||
const CreateTicketService = async ({
|
const CreateTicketService = async ({
|
||||||
contactId,
|
contactId,
|
||||||
status,
|
status,
|
||||||
userId,
|
userId,
|
||||||
msg = ''
|
msg = '',
|
||||||
|
queueId = undefined
|
||||||
}: Request): Promise<Ticket> => {
|
}: Request): Promise<Ticket> => {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
@ -46,9 +48,13 @@ const CreateTicketService = async ({
|
||||||
|
|
||||||
const user = await User.findByPk(userId, { raw: true, })
|
const user = await User.findByPk(userId, { raw: true, })
|
||||||
|
|
||||||
|
if (!queueId) {
|
||||||
|
|
||||||
const matchingQueue = await whatsappQueueMatchingUserQueue(userId, defaultWhatsapp, user?.profile);
|
const matchingQueue = await whatsappQueueMatchingUserQueue(userId, defaultWhatsapp, user?.profile);
|
||||||
|
|
||||||
const queueId = matchingQueue ? matchingQueue.queueId : undefined
|
queueId = matchingQueue ? matchingQueue.queueId : undefined
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
await CheckContactOpenTickets(contactId, defaultWhatsapp.id);
|
await CheckContactOpenTickets(contactId, defaultWhatsapp.id);
|
||||||
|
|
||||||
|
|
|
@ -161,6 +161,21 @@ const ListTicketsService = async ({
|
||||||
// }
|
// }
|
||||||
// ];
|
// ];
|
||||||
|
|
||||||
|
|
||||||
|
includeCondition = [
|
||||||
|
...includeCondition,
|
||||||
|
{
|
||||||
|
model: Message,
|
||||||
|
as: "messages",
|
||||||
|
attributes: ["id", "body"],
|
||||||
|
where: {
|
||||||
|
body: where(fn("LOWER", col("body")), "LIKE", `%ADRIANO ROB%`)
|
||||||
|
},
|
||||||
|
required: false,
|
||||||
|
duplicating: false
|
||||||
|
}
|
||||||
|
];
|
||||||
|
|
||||||
whereCondition = {
|
whereCondition = {
|
||||||
...whereCondition,
|
...whereCondition,
|
||||||
[Op.or]: [
|
[Op.or]: [
|
||||||
|
@ -173,8 +188,21 @@ const ListTicketsService = async ({
|
||||||
// {
|
// {
|
||||||
// "$message.body$": where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`)
|
// "$message.body$": where(fn("LOWER", col("body")), "LIKE", `%${sanitizedSearchParam}%`)
|
||||||
// }
|
// }
|
||||||
]
|
],
|
||||||
|
"$message.body$": where(fn("LOWER", col("body")), "LIKE", `%ADRIANO ROB%`)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
|
// whereCondition = {
|
||||||
|
// ...whereCondition,
|
||||||
|
// "$message.body$": where(
|
||||||
|
// fn("LOWER", col("body")),
|
||||||
|
// "LIKE",
|
||||||
|
// `%${sanitizedSearchParam}%`
|
||||||
|
// )
|
||||||
|
// };
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (date) {
|
if (date) {
|
||||||
|
@ -200,8 +228,6 @@ const ListTicketsService = async ({
|
||||||
const offset = limit * (+pageNumber - 1);
|
const offset = limit * (+pageNumber - 1);
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
const { count, rows: tickets } = await Ticket.findAndCountAll({
|
const { count, rows: tickets } = await Ticket.findAndCountAll({
|
||||||
where: whereCondition,
|
where: whereCondition,
|
||||||
include: includeCondition,
|
include: includeCondition,
|
||||||
|
|
|
@ -0,0 +1,119 @@
|
||||||
|
import React, { useState, useEffect, useContext } from "react";
|
||||||
|
import { useHistory } from "react-router-dom";
|
||||||
|
import { toast } from "react-toastify";
|
||||||
|
|
||||||
|
import Button from "@material-ui/core/Button";
|
||||||
|
import Dialog from "@material-ui/core/Dialog";
|
||||||
|
import Select from "@material-ui/core/Select";
|
||||||
|
import FormControl from "@material-ui/core/FormControl";
|
||||||
|
import InputLabel from "@material-ui/core/InputLabel";
|
||||||
|
import MenuItem from "@material-ui/core/MenuItem";
|
||||||
|
import { makeStyles } from "@material-ui/core";
|
||||||
|
|
||||||
|
import DialogActions from "@material-ui/core/DialogActions";
|
||||||
|
import DialogContent from "@material-ui/core/DialogContent";
|
||||||
|
import DialogTitle from "@material-ui/core/DialogTitle";
|
||||||
|
|
||||||
|
import { i18n } from "../../translate/i18n";
|
||||||
|
import ButtonWithSpinner from "../ButtonWithSpinner";
|
||||||
|
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||||
|
|
||||||
|
import toastError from "../../errors/toastError";
|
||||||
|
|
||||||
|
import api from "../../services/api";
|
||||||
|
|
||||||
|
const useStyles = makeStyles((theme) => ({
|
||||||
|
maxWidth: {
|
||||||
|
width: "100%",
|
||||||
|
},
|
||||||
|
paper: {
|
||||||
|
minWidth: "300px"
|
||||||
|
}
|
||||||
|
}));
|
||||||
|
|
||||||
|
const ContactCreateTicketModal = ({ modalOpen, onClose, contactId }) => {
|
||||||
|
const { user } = useContext(AuthContext);
|
||||||
|
|
||||||
|
const history = useHistory();
|
||||||
|
const [queues, setQueues] = useState([]);
|
||||||
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [selectedQueue, setSelectedQueue] = useState('');
|
||||||
|
const classes = useStyles();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const userQueues = user.queues.map(({ id, name, color }) => { return { id, name, color } })
|
||||||
|
setQueues(userQueues)
|
||||||
|
}, [user]);
|
||||||
|
|
||||||
|
const handleClose = () => {
|
||||||
|
onClose();
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleSaveTicket = async (e) => {
|
||||||
|
e.preventDefault()
|
||||||
|
if (!contactId) return;
|
||||||
|
if (!selectedQueue) {
|
||||||
|
toast.warning("Nenhuma Fila Selecionada")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setLoading(true);
|
||||||
|
try {
|
||||||
|
const { data: ticket } = await api.post("/tickets", {
|
||||||
|
contactId: contactId,
|
||||||
|
userId: user?.id,
|
||||||
|
queueId: selectedQueue,
|
||||||
|
status: "open",
|
||||||
|
});
|
||||||
|
history.push(`/tickets/${ticket.id}`);
|
||||||
|
} catch (err) {
|
||||||
|
toastError(err);
|
||||||
|
onClose()
|
||||||
|
}
|
||||||
|
setLoading(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<Dialog open={modalOpen} onClose={handleClose} maxWidth="xs" scroll="paper" classes={{ paper: classes.paper }}>
|
||||||
|
<form onSubmit={handleSaveTicket}>
|
||||||
|
<DialogTitle id="form-dialog-title">
|
||||||
|
{i18n.t("newTicketModal.title")}
|
||||||
|
</DialogTitle>
|
||||||
|
<DialogContent dividers>
|
||||||
|
<FormControl variant="outlined" className={classes.maxWidth}>
|
||||||
|
<InputLabel>{i18n.t("Selecionar Fila")}</InputLabel>
|
||||||
|
<Select
|
||||||
|
value={selectedQueue}
|
||||||
|
onChange={(e) => setSelectedQueue(e.target.value)}
|
||||||
|
label={i18n.t("Filas")}
|
||||||
|
>
|
||||||
|
<MenuItem value={''}> </MenuItem>
|
||||||
|
{queues.map(({ id, name }) => (
|
||||||
|
<MenuItem key={id} value={id}>{name[0].toUpperCase() + name.slice(1).toLowerCase()}</MenuItem>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</FormControl>
|
||||||
|
</DialogContent>
|
||||||
|
<DialogActions>
|
||||||
|
<Button
|
||||||
|
onClick={handleClose}
|
||||||
|
color="secondary"
|
||||||
|
disabled={loading}
|
||||||
|
variant="outlined"
|
||||||
|
>
|
||||||
|
{i18n.t("newTicketModal.buttons.cancel")}
|
||||||
|
</Button>
|
||||||
|
<ButtonWithSpinner
|
||||||
|
variant="contained"
|
||||||
|
type="submit"
|
||||||
|
color="primary"
|
||||||
|
loading={loading}
|
||||||
|
>
|
||||||
|
{i18n.t("newTicketModal.buttons.ok")}
|
||||||
|
</ButtonWithSpinner>
|
||||||
|
</DialogActions>
|
||||||
|
</form>
|
||||||
|
</Dialog>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ContactCreateTicketModal;
|
|
@ -202,6 +202,7 @@ const TicketsList = (props) => {
|
||||||
});
|
});
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
||||||
if (!status && !searchParam) return;
|
if (!status && !searchParam) return;
|
||||||
|
|
||||||
// if (searchParam) {
|
// if (searchParam) {
|
||||||
|
|
|
@ -1,14 +1,18 @@
|
||||||
import React, { useContext, useEffect, useRef, useState } from "react";
|
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||||
|
|
||||||
import { makeStyles } from "@material-ui/core/styles";
|
import { makeStyles } from "@material-ui/core/styles";
|
||||||
|
import { IconButton } from "@mui/material";
|
||||||
import Paper from "@material-ui/core/Paper";
|
import Paper from "@material-ui/core/Paper";
|
||||||
import SearchIcon from "@material-ui/icons/Search";
|
|
||||||
import InputBase from "@material-ui/core/InputBase";
|
import InputBase from "@material-ui/core/InputBase";
|
||||||
import Tabs from "@material-ui/core/Tabs";
|
import Tabs from "@material-ui/core/Tabs";
|
||||||
import Tab from "@material-ui/core/Tab";
|
import Tab from "@material-ui/core/Tab";
|
||||||
import Badge from "@material-ui/core/Badge";
|
import Badge from "@material-ui/core/Badge";
|
||||||
|
|
||||||
|
import SearchIcon from "@material-ui/icons/Search";
|
||||||
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox";
|
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox";
|
||||||
import CheckBoxIcon from "@material-ui/icons/CheckBox";
|
import CheckBoxIcon from "@material-ui/icons/CheckBox";
|
||||||
|
import MenuIcon from "@material-ui/icons/Menu";
|
||||||
|
import FindInPageIcon from '@material-ui/icons/FindInPage';
|
||||||
|
|
||||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||||
import Switch from "@material-ui/core/Switch";
|
import Switch from "@material-ui/core/Switch";
|
||||||
|
@ -63,6 +67,25 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
|
|
||||||
serachInputWrapper: {
|
serachInputWrapper: {
|
||||||
|
flex: 1,
|
||||||
|
display: "flex",
|
||||||
|
flexDirection: "column",
|
||||||
|
gap: "10px",
|
||||||
|
borderRadius: 40,
|
||||||
|
padding: 4,
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
searchInputHeader: {
|
||||||
|
flex: 1,
|
||||||
|
background: "#fff",
|
||||||
|
display: "flex",
|
||||||
|
borderRadius: 40,
|
||||||
|
padding: 4,
|
||||||
|
marginRight: theme.spacing(1),
|
||||||
|
},
|
||||||
|
|
||||||
|
searchContentInput: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
background: "#fff",
|
background: "#fff",
|
||||||
display: "flex",
|
display: "flex",
|
||||||
|
@ -78,6 +101,11 @@ const useStyles = makeStyles((theme) => ({
|
||||||
alignSelf: "center",
|
alignSelf: "center",
|
||||||
},
|
},
|
||||||
|
|
||||||
|
menuSearch: {
|
||||||
|
color: "grey",
|
||||||
|
alignSelf: "center",
|
||||||
|
},
|
||||||
|
|
||||||
searchInput: {
|
searchInput: {
|
||||||
flex: 1,
|
flex: 1,
|
||||||
border: "none",
|
border: "none",
|
||||||
|
@ -95,6 +123,8 @@ const useStyles = makeStyles((theme) => ({
|
||||||
},
|
},
|
||||||
}));
|
}));
|
||||||
|
|
||||||
|
const DEFAULT_SEARCH_PARAM = { searchParam: "", searchParamContent: "" }
|
||||||
|
|
||||||
const TicketsManager = () => {
|
const TicketsManager = () => {
|
||||||
|
|
||||||
const { tabOption, setTabOption } = useContext(TabTicketContext);
|
const { tabOption, setTabOption } = useContext(TabTicketContext);
|
||||||
|
@ -103,12 +133,11 @@ const TicketsManager = () => {
|
||||||
|
|
||||||
const classes = useStyles();
|
const classes = useStyles();
|
||||||
|
|
||||||
const [searchParam, setSearchParam] = useState("");
|
const [searchParam, setSearchParam] = useState(DEFAULT_SEARCH_PARAM);
|
||||||
const [tab, setTab] = useState("open");
|
const [tab, setTab] = useState("open");
|
||||||
const [tabOpen, setTabOpen] = useState("open");
|
const [tabOpen, setTabOpen] = useState("open");
|
||||||
const [newTicketModalOpen, setNewTicketModalOpen] = useState(false);
|
const [newTicketModalOpen, setNewTicketModalOpen] = useState(false);
|
||||||
const [showAllTickets, setShowAllTickets] = useState(false);
|
const [showAllTickets, setShowAllTickets] = useState(false);
|
||||||
const searchInputRef = useRef();
|
|
||||||
const { user } = useContext(AuthContext);
|
const { user } = useContext(AuthContext);
|
||||||
|
|
||||||
const [openCount, setOpenCount] = useState(0);
|
const [openCount, setOpenCount] = useState(0);
|
||||||
|
@ -117,7 +146,11 @@ const TicketsManager = () => {
|
||||||
const userQueueIds = user.queues.map((q) => q.id);
|
const userQueueIds = user.queues.map((q) => q.id);
|
||||||
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []);
|
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []);
|
||||||
|
|
||||||
|
const [showContentSearch, setShowContentSearch] = useState(false)
|
||||||
|
const searchInputRef = useRef();
|
||||||
|
const searchContentInputRef = useRef();
|
||||||
const [inputSearch, setInputSearch] = useState('');
|
const [inputSearch, setInputSearch] = useState('');
|
||||||
|
const [inputContentSearch, setInputContentSearch] = useState("")
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (user.profile.toUpperCase() === "ADMIN") {
|
if (user.profile.toUpperCase() === "ADMIN") {
|
||||||
|
@ -141,7 +174,7 @@ const TicketsManager = () => {
|
||||||
if (tabOption === 'open') {
|
if (tabOption === 'open') {
|
||||||
|
|
||||||
setTabOption('')
|
setTabOption('')
|
||||||
setSearchParam('');
|
setSearchParam(DEFAULT_SEARCH_PARAM);
|
||||||
setInputSearch('');
|
setInputSearch('');
|
||||||
setTab("open");
|
setTab("open");
|
||||||
return;
|
return;
|
||||||
|
@ -163,12 +196,12 @@ const TicketsManager = () => {
|
||||||
|
|
||||||
setInputSearch(removeExtraSpace(searchedTerm))
|
setInputSearch(removeExtraSpace(searchedTerm))
|
||||||
|
|
||||||
setSearchTicket(searchParam)
|
setSearchTicket(searchParam.searchParam)
|
||||||
|
|
||||||
clearTimeout(searchTimeout);
|
clearTimeout(searchTimeout);
|
||||||
|
|
||||||
if (searchedTerm === "") {
|
if (searchedTerm === "") {
|
||||||
setSearchParam(searchedTerm);
|
setSearchParam(prev => ({ ...prev, searchParam: searchedTerm }))
|
||||||
setInputSearch(searchedTerm)
|
setInputSearch(searchedTerm)
|
||||||
setTab("open");
|
setTab("open");
|
||||||
return;
|
return;
|
||||||
|
@ -176,11 +209,16 @@ const TicketsManager = () => {
|
||||||
|
|
||||||
searchTimeout = setTimeout(() => {
|
searchTimeout = setTimeout(() => {
|
||||||
|
|
||||||
setSearchParam(searchedTerm);
|
setSearchParam(prev => ({ ...prev, searchParam: searchedTerm }));
|
||||||
|
|
||||||
}, 500);
|
}, 500);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleContentSearch = e => {
|
||||||
|
let searchedContentText = e.target.value.toLowerCase()
|
||||||
|
setInputContentSearch(searchedContentText)
|
||||||
|
}
|
||||||
|
|
||||||
const handleChangeTab = (e, newValue) => {
|
const handleChangeTab = (e, newValue) => {
|
||||||
setTab(newValue);
|
setTab(newValue);
|
||||||
};
|
};
|
||||||
|
@ -233,6 +271,7 @@ const TicketsManager = () => {
|
||||||
<Paper square elevation={0} className={classes.ticketOptionsBox}>
|
<Paper square elevation={0} className={classes.ticketOptionsBox}>
|
||||||
{tab === "search" ? (
|
{tab === "search" ? (
|
||||||
<div className={classes.serachInputWrapper}>
|
<div className={classes.serachInputWrapper}>
|
||||||
|
<div className={classes.searchInputHeader}>
|
||||||
<SearchIcon className={classes.searchIcon} />
|
<SearchIcon className={classes.searchIcon} />
|
||||||
<InputBase
|
<InputBase
|
||||||
className={classes.searchInput}
|
className={classes.searchInput}
|
||||||
|
@ -242,6 +281,24 @@ const TicketsManager = () => {
|
||||||
value={inputSearch}
|
value={inputSearch}
|
||||||
onChange={handleSearch}
|
onChange={handleSearch}
|
||||||
/>
|
/>
|
||||||
|
<IconButton onClick={() => setShowContentSearch(prev => !prev)}>
|
||||||
|
<MenuIcon className={classes.menuSearch} />
|
||||||
|
</IconButton>
|
||||||
|
</div>
|
||||||
|
{
|
||||||
|
showContentSearch ?
|
||||||
|
(<div className={classes.searchContentInput}>
|
||||||
|
<FindInPageIcon className={classes.searchIcon} />
|
||||||
|
<InputBase
|
||||||
|
className={classes.searchInput}
|
||||||
|
inputRef={searchContentInputRef}
|
||||||
|
placeholder={i18n.t("Busca por conteúdo")}
|
||||||
|
type="search"
|
||||||
|
value={inputContentSearch}
|
||||||
|
onChange={handleContentSearch}
|
||||||
|
/>
|
||||||
|
</div>) : null
|
||||||
|
}
|
||||||
</div>
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
|
@ -342,7 +399,7 @@ const TicketsManager = () => {
|
||||||
|
|
||||||
|
|
||||||
<TicketsList
|
<TicketsList
|
||||||
searchParam={searchParam}
|
searchParam={searchParam.searchParam}
|
||||||
tab={tab}
|
tab={tab}
|
||||||
showAll={true}
|
showAll={true}
|
||||||
selectedQueueIds={selectedQueueIds}
|
selectedQueueIds={selectedQueueIds}
|
||||||
|
|
|
@ -37,6 +37,7 @@ import { Can } from "../../components/Can";
|
||||||
|
|
||||||
import apiBroker from "../../services/apiBroker";
|
import apiBroker from "../../services/apiBroker";
|
||||||
import fileDownload from 'js-file-download'
|
import fileDownload from 'js-file-download'
|
||||||
|
import ContactCreateTicketModal from "../../components/ContactCreateTicketModal";
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
@ -111,6 +112,7 @@ const Contacts = () => {
|
||||||
const [contacts, dispatch] = useReducer(reducer, []);
|
const [contacts, dispatch] = useReducer(reducer, []);
|
||||||
const [selectedContactId, setSelectedContactId] = useState(null);
|
const [selectedContactId, setSelectedContactId] = useState(null);
|
||||||
const [contactModalOpen, setContactModalOpen] = useState(false);
|
const [contactModalOpen, setContactModalOpen] = useState(false);
|
||||||
|
const [isCreateTicketModalOpen, setIsCreateTicketModalOpen] = useState(false)
|
||||||
const [deletingContact, setDeletingContact] = useState(null);
|
const [deletingContact, setDeletingContact] = useState(null);
|
||||||
const [confirmOpen, setConfirmOpen] = useState(false);
|
const [confirmOpen, setConfirmOpen] = useState(false);
|
||||||
const [hasMore, setHasMore] = useState(false);
|
const [hasMore, setHasMore] = useState(false);
|
||||||
|
@ -302,6 +304,15 @@ const Contacts = () => {
|
||||||
setContactModalOpen(false);
|
setContactModalOpen(false);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const handleOpenCreateTicketModal = (contactId) => {
|
||||||
|
setSelectedContactId(contactId)
|
||||||
|
setIsCreateTicketModalOpen(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleCloseCreateTicketModal = () => {
|
||||||
|
setIsCreateTicketModalOpen(false)
|
||||||
|
}
|
||||||
|
|
||||||
const handleSaveTicket = async (contactId) => {
|
const handleSaveTicket = async (contactId) => {
|
||||||
if (!contactId) return;
|
if (!contactId) return;
|
||||||
setLoading(true);
|
setLoading(true);
|
||||||
|
@ -495,6 +506,11 @@ const Contacts = () => {
|
||||||
aria-labelledby="form-dialog-title"
|
aria-labelledby="form-dialog-title"
|
||||||
contactId={selectedContactId}
|
contactId={selectedContactId}
|
||||||
></ContactModal>
|
></ContactModal>
|
||||||
|
<ContactCreateTicketModal
|
||||||
|
modalOpen={isCreateTicketModalOpen}
|
||||||
|
onClose={handleCloseCreateTicketModal}
|
||||||
|
contactId={selectedContactId}
|
||||||
|
/>
|
||||||
<ConfirmationModal
|
<ConfirmationModal
|
||||||
title={
|
title={
|
||||||
deletingContact
|
deletingContact
|
||||||
|
@ -609,7 +625,7 @@ const Contacts = () => {
|
||||||
<TableCell align="center">
|
<TableCell align="center">
|
||||||
<IconButton
|
<IconButton
|
||||||
size="small"
|
size="small"
|
||||||
onClick={() => handleSaveTicket(contact.id)}
|
onClick={() => handleOpenCreateTicketModal(contact.id)}
|
||||||
>
|
>
|
||||||
<WhatsAppIcon />
|
<WhatsAppIcon />
|
||||||
</IconButton>
|
</IconButton>
|
||||||
|
|
Loading…
Reference in New Issue