Left painel in construction , WIP
parent
a66355f43b
commit
b5975993be
|
@ -541,11 +541,6 @@ const MessagesList = ({ ticketId, isGroup }) => {
|
|||
})}
|
||||
></span>
|
||||
<div className={classes.quotedMsg}>
|
||||
{!message.quotedMsg?.fromMe && (
|
||||
<span className={classes.messageContactName}>
|
||||
{message.quotedMsg?.contact?.name}
|
||||
</span>
|
||||
)}
|
||||
{message.quotedMsg?.body}
|
||||
</div>
|
||||
</div>
|
||||
|
|
|
@ -19,64 +19,11 @@ import toastError from "../../errors/toastError";
|
|||
|
||||
const drawerWidth = 320;
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
root: {
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
position: "relative",
|
||||
overflow: "hidden",
|
||||
},
|
||||
|
||||
ticketInfo: {
|
||||
maxWidth: "50%",
|
||||
flexBasis: "50%",
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
maxWidth: "80%",
|
||||
flexBasis: "80%",
|
||||
},
|
||||
},
|
||||
ticketActionButtons: {
|
||||
maxWidth: "50%",
|
||||
flexBasis: "50%",
|
||||
display: "flex",
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
maxWidth: "100%",
|
||||
flexBasis: "100%",
|
||||
marginBottom: "5px",
|
||||
},
|
||||
},
|
||||
|
||||
mainWrapper: {
|
||||
flex: 1,
|
||||
height: "100%",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
borderTopLeftRadius: 0,
|
||||
borderBottomLeftRadius: 0,
|
||||
borderLeft: "0",
|
||||
marginRight: -drawerWidth,
|
||||
transition: theme.transitions.create("margin", {
|
||||
easing: theme.transitions.easing.sharp,
|
||||
duration: theme.transitions.duration.leavingScreen,
|
||||
}),
|
||||
},
|
||||
|
||||
mainWrapperShift: {
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
transition: theme.transitions.create("margin", {
|
||||
easing: theme.transitions.easing.easeOut,
|
||||
duration: theme.transitions.duration.enteringScreen,
|
||||
}),
|
||||
marginRight: 0,
|
||||
},
|
||||
}));
|
||||
|
||||
const Ticket = () => {
|
||||
const { ticketId } = useParams();
|
||||
const history = useHistory();
|
||||
const classes = useStyles();
|
||||
|
||||
|
||||
const [drawerOpen, setDrawerOpen] = useState(false);
|
||||
const [loading, setLoading] = useState(true);
|
||||
|
@ -93,9 +40,6 @@ const Ticket = () => {
|
|||
|
||||
const { data } = await api.get("/tickets/" + ticketId);
|
||||
|
||||
// setContact(data.contact);
|
||||
// setTicket(data);
|
||||
|
||||
setContact(data.contact.contact);
|
||||
setTicket(data.contact);
|
||||
|
||||
|
@ -153,23 +97,21 @@ const Ticket = () => {
|
|||
};
|
||||
|
||||
return (
|
||||
<div className={classes.root} id="drawer-container">
|
||||
<div id="drawer-container">
|
||||
<Paper
|
||||
variant="outlined"
|
||||
elevation={0}
|
||||
className={clsx(classes.mainWrapper, {
|
||||
[classes.mainWrapperShift]: drawerOpen,
|
||||
})}
|
||||
|
||||
>
|
||||
<TicketHeader loading={loading}>
|
||||
<div className={classes.ticketInfo}>
|
||||
<div >
|
||||
<TicketInfo
|
||||
contact={contact}
|
||||
ticket={ticket}
|
||||
onClick={handleDrawerOpen}
|
||||
/>
|
||||
</div>
|
||||
<div className={classes.ticketActionButtons}>
|
||||
<div >
|
||||
<TicketActionButtons ticket={ticket} statusChatEnd={statusChatEnd}/>
|
||||
</div>
|
||||
</TicketHeader>
|
||||
|
|
|
@ -12,167 +12,147 @@ import ButtonWithSpinner from "../ButtonWithSpinner";
|
|||
import toastError from "../../errors/toastError";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
|
||||
import Modal from "../ChatEnd/ModalChatEnd";
|
||||
import { render } from '@testing-library/react';
|
||||
import Modal from "../ChatEnd/ModalChatEnd";
|
||||
import { render } from "@testing-library/react";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
actionButtons: {
|
||||
marginRight: 6,
|
||||
flex: "none",
|
||||
alignSelf: "center",
|
||||
marginLeft: "auto",
|
||||
"& > *": {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
actionButtons: {
|
||||
marginRight: 6,
|
||||
flex: "none",
|
||||
alignSelf: "center",
|
||||
marginLeft: "auto",
|
||||
"& > *": {
|
||||
margin: theme.spacing(1),
|
||||
},
|
||||
},
|
||||
}));
|
||||
|
||||
const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
||||
const classes = useStyles();
|
||||
const history = useHistory();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const ticketOptionsMenuOpen = Boolean(anchorEl);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
const handleOpenTicketOptionsMenu = e => {
|
||||
setAnchorEl(e.currentTarget);
|
||||
};
|
||||
const classes = useStyles();
|
||||
const history = useHistory();
|
||||
const [anchorEl, setAnchorEl] = useState(null);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const ticketOptionsMenuOpen = Boolean(anchorEl);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
const handleCloseTicketOptionsMenu = e => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
const handleOpenTicketOptionsMenu = (e) => {
|
||||
setAnchorEl(e.currentTarget);
|
||||
};
|
||||
|
||||
|
||||
const chatEndVal = (data) => {
|
||||
|
||||
if(data){
|
||||
const handleCloseTicketOptionsMenu = (e) => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
data = {...data, 'ticketId': ticket.id}
|
||||
|
||||
console.log('ChatEnd: ',(data));
|
||||
const chatEndVal = (data) => {
|
||||
if (data) {
|
||||
data = { ...data, ticketId: ticket.id };
|
||||
|
||||
handleUpdateTicketStatus(null, "closed", user?.id, data)
|
||||
console.log("ChatEnd: ", data);
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
handleUpdateTicketStatus(null, "closed", user?.id, data);
|
||||
}
|
||||
};
|
||||
|
||||
const handleModal = (/*status, userId*/) => {
|
||||
render(
|
||||
<Modal
|
||||
modal_header={"Finalização de Atendimento"}
|
||||
func={chatEndVal}
|
||||
statusChatEnd={statusChatEnd}
|
||||
ticketId={ticket.id}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
const handleModal = (/*status, userId*/) => {
|
||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData = {}) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
if (status === "closed") {
|
||||
await api.put(`/tickets/${ticket.id}`, {
|
||||
status: status,
|
||||
userId: userId || null,
|
||||
schedulingNotifyData: JSON.stringify(schedulingData),
|
||||
});
|
||||
} else {
|
||||
await api.put(`/tickets/${ticket.id}`, {
|
||||
status: status,
|
||||
userId: userId || null,
|
||||
});
|
||||
}
|
||||
|
||||
render(<Modal
|
||||
modal_header={'Finalização de Atendimento'}
|
||||
func={chatEndVal}
|
||||
statusChatEnd={statusChatEnd}
|
||||
ticketId={ticket.id}
|
||||
/>)
|
||||
|
||||
};
|
||||
|
||||
setLoading(false);
|
||||
if (status === "open") {
|
||||
history.push(`/tickets/${ticket.id}`);
|
||||
} else {
|
||||
history.push("/tickets");
|
||||
}
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
toastError(err);
|
||||
}
|
||||
};
|
||||
|
||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
|
||||
|
||||
setLoading(true);
|
||||
try {
|
||||
|
||||
if(status==='closed'){
|
||||
return (
|
||||
<div className={classes.actionButtons}>
|
||||
{ticket.status === "closed" && (
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
startIcon={<Replay />}
|
||||
size="small"
|
||||
onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.reopen")}
|
||||
</ButtonWithSpinner>
|
||||
)}
|
||||
{ticket.status === "open" && (
|
||||
<>
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
startIcon={<Replay />}
|
||||
size="small"
|
||||
onClick={(e) => handleUpdateTicketStatus(e, "pending", null)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.return")}
|
||||
</ButtonWithSpinner>
|
||||
|
||||
await api.put(`/tickets/${ticket.id}`, {
|
||||
status: status,
|
||||
userId: userId || null,
|
||||
schedulingNotifyData: JSON.stringify(schedulingData)
|
||||
});
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={(e) => {
|
||||
handleModal();
|
||||
// handleUpdateTicketStatus(e, "closed", user?.id)
|
||||
}}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.resolve")}
|
||||
</ButtonWithSpinner>
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
await api.put(`/tickets/${ticket.id}`, {
|
||||
status: status,
|
||||
userId: userId || null
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
if (status === "open") {
|
||||
history.push(`/tickets/${ticket.id}`);
|
||||
} else {
|
||||
history.push("/tickets");
|
||||
}
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
toastError(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={classes.actionButtons}>
|
||||
{ticket.status === "closed" && (
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
startIcon={<Replay />}
|
||||
size="small"
|
||||
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.reopen")}
|
||||
</ButtonWithSpinner>
|
||||
)}
|
||||
{ticket.status === "open" && (
|
||||
<>
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
startIcon={<Replay />}
|
||||
size="small"
|
||||
onClick={e => handleUpdateTicketStatus(e, "pending", null)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.return")}
|
||||
</ButtonWithSpinner>
|
||||
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={e => {
|
||||
|
||||
|
||||
handleModal()
|
||||
// handleUpdateTicketStatus(e, "closed", user?.id)
|
||||
|
||||
}}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.resolve")}
|
||||
</ButtonWithSpinner>
|
||||
|
||||
<IconButton onClick={handleOpenTicketOptionsMenu}>
|
||||
<MoreVert />
|
||||
</IconButton>
|
||||
<TicketOptionsMenu
|
||||
ticket={ticket}
|
||||
anchorEl={anchorEl}
|
||||
menuOpen={ticketOptionsMenuOpen}
|
||||
handleClose={handleCloseTicketOptionsMenu}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{ticket.status === "pending" && (
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.accept")}
|
||||
</ButtonWithSpinner>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
<IconButton onClick={handleOpenTicketOptionsMenu}>
|
||||
<MoreVert />
|
||||
</IconButton>
|
||||
<TicketOptionsMenu
|
||||
ticket={ticket}
|
||||
anchorEl={anchorEl}
|
||||
menuOpen={ticketOptionsMenuOpen}
|
||||
handleClose={handleCloseTicketOptionsMenu}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
{ticket.status === "pending" && (
|
||||
<ButtonWithSpinner
|
||||
loading={loading}
|
||||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.accept")}
|
||||
</ButtonWithSpinner>
|
||||
)}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketActionButtons;
|
||||
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
import React from "react";
|
||||
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import { parseISO, format, isSameDay } from "date-fns";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
|
||||
import api from "../../services/api";
|
||||
import ButtonWithSpinner from "../ButtonWithSpinner";
|
||||
import MarkdownWrapper from "../MarkdownWrapper";
|
||||
import { Tooltip } from "@material-ui/core";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
import toastError from "../../errors/toastError";
|
||||
|
||||
const TicketListItem = ({ ticket, status }) => {
|
||||
const history = useHistory();
|
||||
const { ticketId } = useParams();
|
||||
const isMounted = React.useRef(true);
|
||||
const { user } = React.useContext(AuthContext);
|
||||
|
||||
const handleSelectTicket = (id) => {
|
||||
history.push(`/tickets/${id}`);
|
||||
};
|
||||
console.log(ticket.id);
|
||||
|
||||
if (ticket.status !== status) return null;
|
||||
return (
|
||||
<React.Fragment key={ticket.id}>
|
||||
<h1>{ticket.id}</h1>
|
||||
<img src={ticket.contact.profilePicUrl} alt={ticket.id} style={{ width: "50px" }} />
|
||||
<p>{ticket.lastMessage}</p>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketListItem;
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
import React, { useState, useEffect, useRef, useContext } from "react";
|
||||
|
||||
import { useHistory, useParams } from "react-router-dom";
|
||||
import ReactDOM, { useHistory, useParams } from "react-router-dom";
|
||||
import { parseISO, format, isSameDay } from "date-fns";
|
||||
import clsx from "clsx";
|
||||
|
||||
|
@ -102,10 +102,10 @@ const useStyles = makeStyles(theme => ({
|
|||
}));
|
||||
|
||||
const TicketListItem = ({ ticket }) => {
|
||||
const classes = useStyles();
|
||||
const history = useHistory();
|
||||
const classes = ReactDOM.useStyles();
|
||||
const history = ReactDOM.useHistory();
|
||||
const [loading, setLoading] = useState(false);
|
||||
const { ticketId } = useParams();
|
||||
const { ticketId } = ReactDOM.useParams();
|
||||
const isMounted = useRef(true);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
|
@ -145,7 +145,7 @@ const TicketListItem = ({ ticket }) => {
|
|||
if (ticket.status === "pending") return;
|
||||
handleSelectTicket(ticket.id);
|
||||
}}
|
||||
selected={ticketId && +ticketId === ticket.id}
|
||||
selected={ticketId && +ticketId === ticket.id} ///img src{id}.jpg
|
||||
className={clsx(classes.ticket, {
|
||||
[classes.pendingTicket]: ticket.status === "pending",
|
||||
})}
|
||||
|
|
|
@ -0,0 +1,225 @@
|
|||
import React, { useReducer } from "react";
|
||||
import openSocket from "socket.io-client";
|
||||
|
||||
import useTickets from "../../hooks/useTickets";
|
||||
import TicketListItem from "../TicketListItem/TicketListItem";
|
||||
import TicketsListSkeleton from "../TicketsListSkeleton";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
|
||||
const reducer = (state, action) => {
|
||||
if (action.type === "LOAD_TICKETS") {
|
||||
const newTickets = action.payload;
|
||||
|
||||
newTickets.forEach((ticket) => {
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticket.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex] = ticket;
|
||||
if (ticket.unreadMessages > 0) {
|
||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||
}
|
||||
} else {
|
||||
state.push(ticket);
|
||||
}
|
||||
});
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "RESET_UNREAD") {
|
||||
const ticketId = action.payload;
|
||||
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticketId);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex].unreadMessages = 0;
|
||||
}
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "UPDATE_TICKET") {
|
||||
const ticket = action.payload;
|
||||
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticket.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex] = ticket;
|
||||
} else {
|
||||
state.unshift(ticket);
|
||||
}
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") {
|
||||
const message = action.payload.message;
|
||||
|
||||
const ticket = action.payload.ticket;
|
||||
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticket.id);
|
||||
|
||||
if (ticketIndex !== -1) {
|
||||
if (!message.fromMe) {
|
||||
ticket.unreadMessages += 1;
|
||||
}
|
||||
|
||||
state[ticketIndex] = ticket;
|
||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||
} else {
|
||||
state.unshift(ticket);
|
||||
}
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "UPDATE_TICKET_CONTACT") {
|
||||
const contact = action.payload;
|
||||
const ticketIndex = state.findIndex((t) => t.contactId === contact.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex].contact = contact;
|
||||
}
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "DELETE_TICKET") {
|
||||
const ticketId = action.payload;
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticketId);
|
||||
if (ticketIndex !== -1) {
|
||||
state.splice(ticketIndex, 1);
|
||||
}
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "RESET") {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount }) => {
|
||||
const [pageNumber, setPageNumber] = React.useState(1);
|
||||
const { user } = React.useContext(AuthContext);
|
||||
const [ticketsList, dispatch] = useReducer(reducer, []);
|
||||
|
||||
const { tickets, hasMore, loading } = useTickets({
|
||||
pageNumber,
|
||||
searchParam,
|
||||
status,
|
||||
showAll,
|
||||
queueIds: JSON.stringify(selectedQueueIds),
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
dispatch({ type: "RESET" });
|
||||
setPageNumber(1);
|
||||
}, [status, searchParam, dispatch, showAll, selectedQueueIds]);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (!status && !searchParam) return;
|
||||
dispatch({
|
||||
type: "LOAD_TICKETS",
|
||||
payload: tickets,
|
||||
});
|
||||
}, [tickets, status, searchParam]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
|
||||
const shouldUpdateTicket = (ticket) =>
|
||||
(!ticket.userId || ticket.userId === user?.id || showAll) &&
|
||||
(!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1);
|
||||
|
||||
const notBelongsToUserQueues = (ticket) =>
|
||||
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1;
|
||||
|
||||
socket.on("connect", () => {
|
||||
if (status) {
|
||||
socket.emit("joinTickets", status);
|
||||
} else {
|
||||
socket.emit("joinNotification");
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("ticket", (data) => {
|
||||
if (data.action === "updateUnread") {
|
||||
dispatch({
|
||||
type: "RESET_UNREAD",
|
||||
payload: data.ticketId,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.action === "update" && shouldUpdateTicket(data.ticket)) {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET",
|
||||
payload: data.ticket,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.action === "update" && notBelongsToUserQueues(data.ticket)) {
|
||||
dispatch({ type: "DELETE_TICKET", payload: data.ticket.id });
|
||||
}
|
||||
|
||||
if (data.action === "delete") {
|
||||
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("appMessage", (data) => {
|
||||
if (data.action === "create" && shouldUpdateTicket(data.ticket)) {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET_UNREAD_MESSAGES",
|
||||
// payload: data.ticket,
|
||||
payload: data,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("contact", (data) => {
|
||||
if (data.action === "update") {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET_CONTACT",
|
||||
payload: data.contact,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [status, showAll, user, selectedQueueIds]);
|
||||
|
||||
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 (
|
||||
<ul>
|
||||
{ticketsList.length === 0 && !loading ? (
|
||||
<div>
|
||||
<span>{i18n.t("ticketsList.noTicketsTitle")}</span>
|
||||
<p>{i18n.t("ticketsList.noTicketsMessage")}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{ticketsList.map((ticket) => (
|
||||
<TicketListItem ticket={ticket} key={ticket.id} status={status} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{loading && <TicketsListSkeleton />}
|
||||
</ul>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketsList;
|
||||
|
|
@ -12,294 +12,320 @@ import useTickets from "../../hooks/useTickets";
|
|||
import { i18n } from "../../translate/i18n";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
ticketsListWrapper: {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
},
|
||||
const useStyles = makeStyles(theme => ({
|
||||
ticketsListWrapper: {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
},
|
||||
|
||||
ticketsList: {
|
||||
flex: 1,
|
||||
overflowY: "scroll",
|
||||
...theme.scrollbarStyles,
|
||||
borderTop: "2px solid rgba(0, 0, 0, 0.12)",
|
||||
},
|
||||
ticketsList: {
|
||||
flex: 1,
|
||||
overflowY: "scroll",
|
||||
...theme.scrollbarStyles,
|
||||
borderTop: "2px solid rgba(0, 0, 0, 0.12)",
|
||||
},
|
||||
|
||||
ticketsListHeader: {
|
||||
color: "rgb(67, 83, 105)",
|
||||
zIndex: 2,
|
||||
backgroundColor: "white",
|
||||
borderBottom: "1px solid rgba(0, 0, 0, 0.12)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
ticketsListHeader: {
|
||||
color: "rgb(67, 83, 105)",
|
||||
zIndex: 2,
|
||||
backgroundColor: "white",
|
||||
borderBottom: "1px solid rgba(0, 0, 0, 0.12)",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
justifyContent: "space-between",
|
||||
},
|
||||
|
||||
ticketsCount: {
|
||||
fontWeight: "normal",
|
||||
color: "rgb(104, 121, 146)",
|
||||
marginLeft: "8px",
|
||||
fontSize: "14px",
|
||||
},
|
||||
ticketsCount: {
|
||||
fontWeight: "normal",
|
||||
color: "rgb(104, 121, 146)",
|
||||
marginLeft: "8px",
|
||||
fontSize: "14px",
|
||||
},
|
||||
|
||||
noTicketsText: {
|
||||
textAlign: "center",
|
||||
color: "rgb(104, 121, 146)",
|
||||
fontSize: "14px",
|
||||
lineHeight: "1.4",
|
||||
},
|
||||
noTicketsText: {
|
||||
textAlign: "center",
|
||||
color: "rgb(104, 121, 146)",
|
||||
fontSize: "14px",
|
||||
lineHeight: "1.4",
|
||||
},
|
||||
|
||||
noTicketsTitle: {
|
||||
textAlign: "center",
|
||||
fontSize: "16px",
|
||||
fontWeight: "600",
|
||||
margin: "0px",
|
||||
},
|
||||
noTicketsTitle: {
|
||||
textAlign: "center",
|
||||
fontSize: "16px",
|
||||
fontWeight: "600",
|
||||
margin: "0px",
|
||||
},
|
||||
|
||||
noTicketsDiv: {
|
||||
display: "flex",
|
||||
height: "100px",
|
||||
margin: 40,
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
noTicketsDiv: {
|
||||
display: "flex",
|
||||
height: "100px",
|
||||
margin: 40,
|
||||
flexDirection: "column",
|
||||
alignItems: "center",
|
||||
justifyContent: "center",
|
||||
},
|
||||
}));
|
||||
|
||||
const reducer = (state, action) => {
|
||||
if (action.type === "LOAD_TICKETS") {
|
||||
const newTickets = action.payload;
|
||||
if (action.type === "LOAD_TICKETS") {
|
||||
const newTickets = action.payload;
|
||||
|
||||
|
||||
newTickets.forEach(ticket => {
|
||||
|
||||
newTickets.forEach((ticket) => {
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticket.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex] = ticket;
|
||||
if (ticket.unreadMessages > 0) {
|
||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||
}
|
||||
} else {
|
||||
state.push(ticket);
|
||||
}
|
||||
});
|
||||
// console.log('* ticket.unreadMessages: ',ticket.unreadMessages)
|
||||
|
||||
return [...state];
|
||||
}
|
||||
const ticketIndex = state.findIndex(t => t.id === ticket.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex] = ticket;
|
||||
if (ticket.unreadMessages > 0) {
|
||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||
}
|
||||
} else {
|
||||
state.push(ticket);
|
||||
}
|
||||
});
|
||||
|
||||
if (action.type === "RESET_UNREAD") {
|
||||
const ticketId = action.payload;
|
||||
return [...state];
|
||||
}
|
||||
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticketId);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex].unreadMessages = 0;
|
||||
}
|
||||
if (action.type === "RESET_UNREAD") {
|
||||
const ticketId = action.payload;
|
||||
|
||||
return [...state];
|
||||
}
|
||||
const ticketIndex = state.findIndex(t => t.id === ticketId);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex].unreadMessages = 0;
|
||||
}
|
||||
|
||||
if (action.type === "UPDATE_TICKET") {
|
||||
const ticket = action.payload;
|
||||
return [...state];
|
||||
}
|
||||
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticket.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex] = ticket;
|
||||
} else {
|
||||
state.unshift(ticket);
|
||||
}
|
||||
if (action.type === "UPDATE_TICKET") {
|
||||
const ticket = action.payload;
|
||||
|
||||
return [...state];
|
||||
}
|
||||
// console.log('++++++++++++ UPDATE_TICKET: ',ticket)
|
||||
|
||||
if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") {
|
||||
const message = action.payload.message;
|
||||
const ticketIndex = state.findIndex(t => t.id === ticket.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex] = ticket;
|
||||
} else {
|
||||
state.unshift(ticket);
|
||||
}
|
||||
|
||||
const ticket = action.payload.ticket;
|
||||
return [...state];
|
||||
}
|
||||
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticket.id);
|
||||
if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") {
|
||||
|
||||
if (ticketIndex !== -1) {
|
||||
if (!message.fromMe) {
|
||||
ticket.unreadMessages += 1;
|
||||
}
|
||||
const message = action.payload.message
|
||||
|
||||
state[ticketIndex] = ticket;
|
||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||
} else {
|
||||
state.unshift(ticket);
|
||||
}
|
||||
const ticket = action.payload.ticket;
|
||||
|
||||
return [...state];
|
||||
}
|
||||
const ticketIndex = state.findIndex(t => t.id === ticket.id);
|
||||
|
||||
if (action.type === "UPDATE_TICKET_CONTACT") {
|
||||
const contact = action.payload;
|
||||
const ticketIndex = state.findIndex((t) => t.contactId === contact.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex].contact = contact;
|
||||
}
|
||||
return [...state];
|
||||
}
|
||||
if (ticketIndex !== -1) {
|
||||
|
||||
// console.log('>>>>>> ticketIndex: ', ticketIndex)
|
||||
|
||||
if (action.type === "DELETE_TICKET") {
|
||||
const ticketId = action.payload;
|
||||
const ticketIndex = state.findIndex((t) => t.id === ticketId);
|
||||
if (ticketIndex !== -1) {
|
||||
state.splice(ticketIndex, 1);
|
||||
}
|
||||
// console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message)
|
||||
|
||||
if(!message.fromMe){
|
||||
ticket.unreadMessages +=1
|
||||
}
|
||||
|
||||
return [...state];
|
||||
}
|
||||
state[ticketIndex] = ticket;
|
||||
state.unshift(state.splice(ticketIndex, 1)[0]);
|
||||
|
||||
} else {
|
||||
state.unshift(ticket);
|
||||
}
|
||||
|
||||
if (action.type === "RESET") {
|
||||
return [];
|
||||
}
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "UPDATE_TICKET_CONTACT") {
|
||||
const contact = action.payload;
|
||||
const ticketIndex = state.findIndex(t => t.contactId === contact.id);
|
||||
if (ticketIndex !== -1) {
|
||||
state[ticketIndex].contact = contact;
|
||||
}
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "DELETE_TICKET") {
|
||||
const ticketId = action.payload;
|
||||
const ticketIndex = state.findIndex(t => t.id === ticketId);
|
||||
if (ticketIndex !== -1) {
|
||||
state.splice(ticketIndex, 1);
|
||||
}
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
if (action.type === "RESET") {
|
||||
return [];
|
||||
}
|
||||
};
|
||||
|
||||
const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount, style }) => {
|
||||
const classes = useStyles();
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
const [ticketsList, dispatch] = useReducer(reducer, []);
|
||||
const { user } = useContext(AuthContext);
|
||||
const TicketsList = (props) => {
|
||||
const { status, searchParam, showAll, selectedQueueIds, updateCount, style } =
|
||||
props;
|
||||
const classes = useStyles();
|
||||
const [pageNumber, setPageNumber] = useState(1);
|
||||
const [ticketsList, dispatch] = useReducer(reducer, []);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: "RESET" });
|
||||
setPageNumber(1);
|
||||
}, [status, searchParam, dispatch, showAll, selectedQueueIds]);
|
||||
useEffect(() => {
|
||||
dispatch({ type: "RESET" });
|
||||
setPageNumber(1);
|
||||
}, [status, searchParam, dispatch, showAll, selectedQueueIds]);
|
||||
|
||||
const { tickets, hasMore, loading } = useTickets({
|
||||
pageNumber,
|
||||
searchParam,
|
||||
status,
|
||||
showAll,
|
||||
queueIds: JSON.stringify(selectedQueueIds),
|
||||
});
|
||||
const { tickets, hasMore, loading } = useTickets({
|
||||
pageNumber,
|
||||
searchParam,
|
||||
status,
|
||||
showAll,
|
||||
queueIds: JSON.stringify(selectedQueueIds),
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
if (!status && !searchParam) return;
|
||||
dispatch({
|
||||
type: "LOAD_TICKETS",
|
||||
payload: tickets,
|
||||
});
|
||||
}, [tickets, status, searchParam]);
|
||||
useEffect(() => {
|
||||
if (!status && !searchParam) return;
|
||||
dispatch({
|
||||
type: "LOAD_TICKETS",
|
||||
payload: tickets,
|
||||
});
|
||||
}, [tickets, status, searchParam]);
|
||||
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
|
||||
const shouldUpdateTicket = (ticket) =>
|
||||
(!ticket.userId || ticket.userId === user?.id || showAll) &&
|
||||
(!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1);
|
||||
const shouldUpdateTicket = ticket =>
|
||||
(!ticket.userId || ticket.userId === user?.id || showAll) &&
|
||||
(!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1);
|
||||
|
||||
const notBelongsToUserQueues = (ticket) =>
|
||||
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1;
|
||||
const notBelongsToUserQueues = ticket =>
|
||||
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1;
|
||||
|
||||
socket.on("connect", () => {
|
||||
if (status) {
|
||||
socket.emit("joinTickets", status);
|
||||
} else {
|
||||
socket.emit("joinNotification");
|
||||
}
|
||||
});
|
||||
socket.on("connect", () => {
|
||||
if (status) {
|
||||
socket.emit("joinTickets", status);
|
||||
} else {
|
||||
socket.emit("joinNotification");
|
||||
}
|
||||
|
||||
socket.on("ticket", (data) => {
|
||||
if (data.action === "updateUnread") {
|
||||
dispatch({
|
||||
type: "RESET_UNREAD",
|
||||
payload: data.ticketId,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
if (data.action === "update" && shouldUpdateTicket(data.ticket)) {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET",
|
||||
payload: data.ticket,
|
||||
});
|
||||
}
|
||||
socket.on("ticket", data => {
|
||||
if (data.action === "updateUnread") {
|
||||
dispatch({
|
||||
type: "RESET_UNREAD",
|
||||
payload: data.ticketId,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.action === "update" && notBelongsToUserQueues(data.ticket)) {
|
||||
dispatch({ type: "DELETE_TICKET", payload: data.ticket.id });
|
||||
}
|
||||
if (data.action === "update" && shouldUpdateTicket(data.ticket)) {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET",
|
||||
payload: data.ticket,
|
||||
});
|
||||
}
|
||||
|
||||
if (data.action === "delete") {
|
||||
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
|
||||
}
|
||||
});
|
||||
if (data.action === "update" && notBelongsToUserQueues(data.ticket)) {
|
||||
dispatch({ type: "DELETE_TICKET", payload: data.ticket.id });
|
||||
}
|
||||
|
||||
socket.on("appMessage", (data) => {
|
||||
if (data.action === "create" && shouldUpdateTicket(data.ticket)) {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET_UNREAD_MESSAGES",
|
||||
// payload: data.ticket,
|
||||
payload: data,
|
||||
});
|
||||
}
|
||||
});
|
||||
if (data.action === "delete") {
|
||||
dispatch({ type: "DELETE_TICKET", payload: data.ticketId });
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("contact", (data) => {
|
||||
if (data.action === "update") {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET_CONTACT",
|
||||
payload: data.contact,
|
||||
});
|
||||
}
|
||||
});
|
||||
socket.on("appMessage", data => {
|
||||
if (data.action === "create" && shouldUpdateTicket(data.ticket)) {
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [status, showAll, user, selectedQueueIds]);
|
||||
// console.log('((((((((((((((((((( DATA.MESSAGE: ', data.message)
|
||||
|
||||
useEffect(() => {
|
||||
if (typeof updateCount === "function") {
|
||||
updateCount(ticketsList.length);
|
||||
}
|
||||
}, [ticketsList]);
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET_UNREAD_MESSAGES",
|
||||
// payload: data.ticket,
|
||||
payload: data,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const loadMore = () => {
|
||||
setPageNumber((prevState) => prevState + 1);
|
||||
};
|
||||
socket.on("contact", data => {
|
||||
if (data.action === "update") {
|
||||
dispatch({
|
||||
type: "UPDATE_TICKET_CONTACT",
|
||||
payload: data.contact,
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
const handleScroll = (e) => {
|
||||
if (!hasMore || loading) return;
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [status, showAll, user, selectedQueueIds]);
|
||||
|
||||
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
|
||||
useEffect(() => {
|
||||
|
||||
if (scrollHeight - (scrollTop + 100) < clientHeight) {
|
||||
loadMore();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Paper className={classes.ticketsListWrapper} style={style}>
|
||||
<Paper
|
||||
square
|
||||
name="closed"
|
||||
elevation={0}
|
||||
className={classes.ticketsList}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<List style={{ paddingTop: 0 }}>
|
||||
{ticketsList.length === 0 && !loading ? (
|
||||
<div className={classes.noTicketsDiv}>
|
||||
<span className={classes.noTicketsTitle}>{i18n.t("ticketsList.noTicketsTitle")}</span>
|
||||
<p className={classes.noTicketsText}>{i18n.t("ticketsList.noTicketsMessage")}</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{ticketsList.map((ticket) => (
|
||||
<TicketListItem ticket={ticket} key={ticket.id} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{loading && <TicketsListSkeleton />}
|
||||
</List>
|
||||
</Paper>
|
||||
</Paper>
|
||||
);
|
||||
|
||||
if (typeof updateCount === "function") {
|
||||
updateCount(ticketsList.length);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ticketsList]);
|
||||
|
||||
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 (
|
||||
<Paper className={classes.ticketsListWrapper} style={style}>
|
||||
<Paper
|
||||
square
|
||||
name="closed"
|
||||
elevation={0}
|
||||
className={classes.ticketsList}
|
||||
onScroll={handleScroll}
|
||||
>
|
||||
<List style={{ paddingTop: 0 }}>
|
||||
{ticketsList.length === 0 && !loading ? (
|
||||
<div className={classes.noTicketsDiv}>
|
||||
<span className={classes.noTicketsTitle}>
|
||||
{i18n.t("ticketsList.noTicketsTitle")}
|
||||
</span>
|
||||
<p className={classes.noTicketsText}>
|
||||
{i18n.t("ticketsList.noTicketsMessage")}
|
||||
</p>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
{ticketsList.map(ticket => (
|
||||
<TicketListItem ticket={ticket} key={ticket.id} />
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
{loading && <TicketsListSkeleton />}
|
||||
</List>
|
||||
</Paper>
|
||||
</Paper>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketsList;
|
||||
|
||||
|
|
|
@ -1,337 +1,64 @@
|
|||
import React, { useContext, useEffect, useRef, useState } from "react";
|
||||
import openSocket from "socket.io-client"
|
||||
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
import SearchIcon from "@material-ui/icons/Search";
|
||||
import InputBase from "@material-ui/core/InputBase";
|
||||
import Tabs from "@material-ui/core/Tabs";
|
||||
import Tab from "@material-ui/core/Tab";
|
||||
import Badge from "@material-ui/core/Badge";
|
||||
import MoveToInboxIcon from "@material-ui/icons/MoveToInbox";
|
||||
import CheckBoxIcon from "@material-ui/icons/CheckBox";
|
||||
import FormControlLabel from "@material-ui/core/FormControlLabel";
|
||||
import Switch from "@material-ui/core/Switch";
|
||||
import { Button } from "@material-ui/core";
|
||||
import React from "react";
|
||||
|
||||
import NewTicketModal from "../NewTicketModal";
|
||||
import TicketsList from "../TicketsList";
|
||||
import TabPanel from "../TabPanel";
|
||||
import TicketsList from "../TicketsList/TicketsList";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
import { Can } from "../Can";
|
||||
import TicketsQueueSelect from "../TicketsQueueSelect";
|
||||
|
||||
const useStyles = makeStyles((theme) => ({
|
||||
ticketsWrapper: {
|
||||
position: "relative",
|
||||
display: "flex",
|
||||
height: "100%",
|
||||
flexDirection: "column",
|
||||
overflow: "hidden",
|
||||
borderTopRightRadius: 0,
|
||||
borderBottomRightRadius: 0,
|
||||
},
|
||||
|
||||
tabsHeader: {
|
||||
flex: "none",
|
||||
backgroundColor: "#eee",
|
||||
},
|
||||
|
||||
settingsIcon: {
|
||||
alignSelf: "center",
|
||||
marginLeft: "auto",
|
||||
padding: 8,
|
||||
},
|
||||
|
||||
tab: {
|
||||
minWidth: 120,
|
||||
width: 120,
|
||||
},
|
||||
|
||||
ticketOptionsBox: {
|
||||
display: "flex",
|
||||
justifyContent: "space-between",
|
||||
alignItems: "center",
|
||||
background: "#fafafa",
|
||||
padding: theme.spacing(1),
|
||||
},
|
||||
|
||||
serachInputWrapper: {
|
||||
flex: 1,
|
||||
background: "#fff",
|
||||
display: "flex",
|
||||
borderRadius: 40,
|
||||
padding: 4,
|
||||
marginRight: theme.spacing(1),
|
||||
},
|
||||
|
||||
searchIcon: {
|
||||
color: "grey",
|
||||
marginLeft: 6,
|
||||
marginRight: 6,
|
||||
alignSelf: "center",
|
||||
},
|
||||
|
||||
searchInput: {
|
||||
flex: 1,
|
||||
border: "none",
|
||||
borderRadius: 30,
|
||||
},
|
||||
|
||||
badge: {
|
||||
right: "-10px",
|
||||
},
|
||||
show: {
|
||||
display: "block",
|
||||
},
|
||||
hide: {
|
||||
display: "none !important",
|
||||
},
|
||||
}));
|
||||
import TicketsManagerStyled from "./TicketsManager.style";
|
||||
|
||||
const TicketsManager = () => {
|
||||
const classes = useStyles();
|
||||
const [valueTab, setValueTab] = React.useState("open");
|
||||
|
||||
// Old New State
|
||||
const [newPage, setNewPage] = React.useState(true);
|
||||
// Old New State
|
||||
const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false);
|
||||
const [showAllTickets, setShowAllTickets] = React.useState(false);
|
||||
const { user } = React.useContext(AuthContext);
|
||||
|
||||
const [searchParam, setSearchParam] = useState("");
|
||||
const [tab, setTab] = useState("open");
|
||||
const [tabOpen, setTabOpen] = useState("open");
|
||||
const [newTicketModalOpen, setNewTicketModalOpen] = useState(false);
|
||||
const [showAllTickets, setShowAllTickets] = useState(false);
|
||||
const searchInputRef = useRef();
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
const [openCount, setOpenCount] = useState(0);
|
||||
const [pendingCount, setPendingCount] = useState(0);
|
||||
|
||||
const userQueueIds = user.queues.map((q) => q.id);
|
||||
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []);
|
||||
const socket = openSocket.open(process.env.REACT_APP_BACKEND_URL)
|
||||
const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []);
|
||||
|
||||
console.log(user.queues)
|
||||
console.log(selectedQueueIds)
|
||||
socket.on("connect", () => {
|
||||
if ("open") {
|
||||
socket.emit("joinTickets", "open");
|
||||
} else {
|
||||
socket.emit("joinNotification");
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
socket.on("connect", ()=>{
|
||||
|
||||
})
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
React.useEffect(() => {
|
||||
if (user.profile.toUpperCase() === "ADMIN") {
|
||||
setShowAllTickets(true);
|
||||
}
|
||||
}, [user.profile]);
|
||||
|
||||
useEffect(() => {
|
||||
if (tab === "search") {
|
||||
searchInputRef.current.focus();
|
||||
}
|
||||
}, [tab]);
|
||||
|
||||
let searchTimeout;
|
||||
|
||||
const handleSearch = (e) => {
|
||||
const searchedTerm = e.target.value.toLowerCase();
|
||||
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
if (searchedTerm === "") {
|
||||
setSearchParam(searchedTerm);
|
||||
setTab("open");
|
||||
return;
|
||||
}
|
||||
|
||||
searchTimeout = setTimeout(() => {
|
||||
setSearchParam(searchedTerm);
|
||||
}, 500);
|
||||
};
|
||||
|
||||
const handleChangeTab = (e, newValue) => {
|
||||
setTab(newValue);
|
||||
};
|
||||
|
||||
const handleChangeTabOpen = (e, newValue) => {
|
||||
setTabOpen(newValue);
|
||||
};
|
||||
|
||||
const applyPanelStyle = (status) => {
|
||||
if (tabOpen !== status) {
|
||||
return { width: 0, height: 0 };
|
||||
}
|
||||
};
|
||||
|
||||
if (newPage) {
|
||||
return (
|
||||
<>
|
||||
<NewTicketModal
|
||||
modalOpen={newTicketModalOpen}
|
||||
onClose={(e) => setNewTicketModalOpen(false)}
|
||||
/>
|
||||
<div>
|
||||
<TicketsQueueSelect
|
||||
style={{ marginLeft: 6 }}
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
userQueues={user?.queues}
|
||||
onChange={(values) => setSelectedQueueIds(values)}
|
||||
/>
|
||||
<TicketsList
|
||||
status="open"
|
||||
showAll={showAllTickets}
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
updateCount={(val) => setOpenCount(val)}
|
||||
style={applyPanelStyle("open")}
|
||||
/>
|
||||
<TicketsList
|
||||
status="pending"
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
updateCount={(val) => setPendingCount(val)}
|
||||
style={applyPanelStyle("pending")}
|
||||
/>
|
||||
</div>
|
||||
</>
|
||||
);
|
||||
const styleTmp = {
|
||||
padding:"12px 0 ",
|
||||
cursor: "pointer",
|
||||
}
|
||||
|
||||
|
||||
console.log(valueTab);
|
||||
|
||||
return (
|
||||
<Paper elevation={0} variant="outlined" className={classes.ticketsWrapper}>
|
||||
<TicketsManagerStyled>
|
||||
<ul>
|
||||
<li style={styleTmp} id="open" onClick={(e) => setValueTab(e.target.id)}>
|
||||
Abertos
|
||||
</li>
|
||||
<li style={styleTmp} id="pending" onClick={(e) => setValueTab(e.target.id)}>
|
||||
Pendentes
|
||||
</li>
|
||||
<li style={styleTmp} id="closed" onClick={(e) => setValueTab(e.target.id)}>
|
||||
Fechados
|
||||
</li>
|
||||
</ul>
|
||||
<TicketsList
|
||||
status={valueTab}
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
updateCount={(val) => setPendingCount(val)}
|
||||
/>
|
||||
<NewTicketModal
|
||||
modalOpen={newTicketModalOpen}
|
||||
onClose={(e) => setNewTicketModalOpen(false)}
|
||||
onClose={(e) => setNewTicketModalOpen(true)}
|
||||
/>
|
||||
<Paper elevation={0} square className={classes.tabsHeader}>
|
||||
<Tabs
|
||||
value={tab}
|
||||
onChange={handleChangeTab}
|
||||
variant="fullWidth"
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
aria-label="icon label tabs example"
|
||||
>
|
||||
<Tab
|
||||
value={"open"}
|
||||
icon={<MoveToInboxIcon />}
|
||||
label={i18n.t("tickets.tabs.open.title")}
|
||||
classes={{ root: classes.tab }}
|
||||
/>
|
||||
<Tab
|
||||
value={"closed"}
|
||||
icon={<CheckBoxIcon />}
|
||||
label={i18n.t("tickets.tabs.closed.title")}
|
||||
classes={{ root: classes.tab }}
|
||||
/>
|
||||
<Tab
|
||||
value={"search"}
|
||||
icon={<SearchIcon />}
|
||||
label={i18n.t("tickets.tabs.search.title")}
|
||||
classes={{ root: classes.tab }}
|
||||
/>
|
||||
</Tabs>
|
||||
</Paper>
|
||||
<Paper square elevation={0} className={classes.ticketOptionsBox}>
|
||||
{tab === "search" ? (
|
||||
<div className={classes.serachInputWrapper}>
|
||||
<SearchIcon className={classes.searchIcon} />
|
||||
<InputBase
|
||||
className={classes.searchInput}
|
||||
inputRef={searchInputRef}
|
||||
placeholder={i18n.t("tickets.search.placeholder")}
|
||||
type="search"
|
||||
onChange={handleSearch}
|
||||
/>
|
||||
</div>
|
||||
) : (
|
||||
<>
|
||||
<Button variant="outlined" color="primary" onClick={() => setNewTicketModalOpen(true)}>
|
||||
{i18n.t("ticketsManager.buttons.newTicket")}
|
||||
</Button>
|
||||
<Can
|
||||
role={user.profile}
|
||||
perform="tickets-manager:showall"
|
||||
yes={() => (
|
||||
<FormControlLabel
|
||||
label={i18n.t("tickets.buttons.showAll")}
|
||||
labelPlacement="start"
|
||||
control={
|
||||
<Switch
|
||||
size="small"
|
||||
checked={showAllTickets}
|
||||
onChange={() => setShowAllTickets((prevState) => !prevState)}
|
||||
name="showAllTickets"
|
||||
color="primary"
|
||||
/>
|
||||
}
|
||||
/>
|
||||
)}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
<TicketsQueueSelect
|
||||
style={{ marginLeft: 6 }}
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
userQueues={user?.queues}
|
||||
onChange={(values) => setSelectedQueueIds(values)}
|
||||
/>
|
||||
</Paper>
|
||||
<TabPanel value={tab} name="open" className={classes.ticketsWrapper}>
|
||||
<Tabs
|
||||
value={tabOpen}
|
||||
onChange={handleChangeTabOpen}
|
||||
indicatorColor="primary"
|
||||
textColor="primary"
|
||||
variant="fullWidth"
|
||||
>
|
||||
<Tab
|
||||
label={
|
||||
<Badge className={classes.badge} badgeContent={openCount} color="primary">
|
||||
{i18n.t("ticketsList.assignedHeader")}
|
||||
</Badge>
|
||||
}
|
||||
value={"open"}
|
||||
/>
|
||||
<Tab
|
||||
label={
|
||||
<Badge className={classes.badge} badgeContent={pendingCount} color="secondary">
|
||||
{i18n.t("ticketsList.pendingHeader")}
|
||||
</Badge>
|
||||
}
|
||||
value={"pending"}
|
||||
/>
|
||||
</Tabs>
|
||||
<Paper className={classes.ticketsWrapper}>
|
||||
<TicketsList
|
||||
status="open"
|
||||
showAll={showAllTickets}
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
updateCount={(val) => setOpenCount(val)}
|
||||
style={applyPanelStyle("open")}
|
||||
/>
|
||||
<TicketsList
|
||||
status="pending"
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
updateCount={(val) => setPendingCount(val)}
|
||||
style={applyPanelStyle("pending")}
|
||||
/>
|
||||
</Paper>
|
||||
</TabPanel>
|
||||
<TabPanel value={tab} name="closed" className={classes.ticketsWrapper}>
|
||||
<TicketsList status="closed" showAll={true} selectedQueueIds={selectedQueueIds} />
|
||||
</TabPanel>
|
||||
<TabPanel value={tab} name="search" className={classes.ticketsWrapper}>
|
||||
<TicketsList searchParam={searchParam} showAll={true} selectedQueueIds={selectedQueueIds} />
|
||||
</TabPanel>
|
||||
</Paper>
|
||||
</TicketsManagerStyled>
|
||||
);
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
import styled from "styled-components"
|
||||
|
||||
const TicketsManagerStyled = styled.div`
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
`
|
||||
export default TicketsManagerStyled
|
|
@ -3,7 +3,8 @@ import styled from "styled-components";
|
|||
const TicketsStyled = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
height: 86vh;
|
||||
min-height: auto;
|
||||
|
||||
`;
|
||||
|
||||
export default TicketsStyled
|
Loading…
Reference in New Issue