Left painel in construction , WIP

pull/14/head^2
RenatoDiGiacomo 2022-07-25 09:59:34 -03:00
parent a66355f43b
commit b5975993be
10 changed files with 707 additions and 767 deletions

View File

@ -541,11 +541,6 @@ const MessagesList = ({ ticketId, isGroup }) => {
})} })}
></span> ></span>
<div className={classes.quotedMsg}> <div className={classes.quotedMsg}>
{!message.quotedMsg?.fromMe && (
<span className={classes.messageContactName}>
{message.quotedMsg?.contact?.name}
</span>
)}
{message.quotedMsg?.body} {message.quotedMsg?.body}
</div> </div>
</div> </div>

View File

@ -19,64 +19,11 @@ import toastError from "../../errors/toastError";
const drawerWidth = 320; 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 Ticket = () => {
const { ticketId } = useParams(); const { ticketId } = useParams();
const history = useHistory(); const history = useHistory();
const classes = useStyles();
const [drawerOpen, setDrawerOpen] = useState(false); const [drawerOpen, setDrawerOpen] = useState(false);
const [loading, setLoading] = useState(true); const [loading, setLoading] = useState(true);
@ -93,9 +40,6 @@ const Ticket = () => {
const { data } = await api.get("/tickets/" + ticketId); const { data } = await api.get("/tickets/" + ticketId);
// setContact(data.contact);
// setTicket(data);
setContact(data.contact.contact); setContact(data.contact.contact);
setTicket(data.contact); setTicket(data.contact);
@ -153,23 +97,21 @@ const Ticket = () => {
}; };
return ( return (
<div className={classes.root} id="drawer-container"> <div id="drawer-container">
<Paper <Paper
variant="outlined" variant="outlined"
elevation={0} elevation={0}
className={clsx(classes.mainWrapper, {
[classes.mainWrapperShift]: drawerOpen,
})}
> >
<TicketHeader loading={loading}> <TicketHeader loading={loading}>
<div className={classes.ticketInfo}> <div >
<TicketInfo <TicketInfo
contact={contact} contact={contact}
ticket={ticket} ticket={ticket}
onClick={handleDrawerOpen} onClick={handleDrawerOpen}
/> />
</div> </div>
<div className={classes.ticketActionButtons}> <div >
<TicketActionButtons ticket={ticket} statusChatEnd={statusChatEnd}/> <TicketActionButtons ticket={ticket} statusChatEnd={statusChatEnd}/>
</div> </div>
</TicketHeader> </TicketHeader>

View File

@ -13,9 +13,9 @@ import toastError from "../../errors/toastError";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
import Modal from "../ChatEnd/ModalChatEnd"; import Modal from "../ChatEnd/ModalChatEnd";
import { render } from '@testing-library/react'; import { render } from "@testing-library/react";
const useStyles = makeStyles(theme => ({ const useStyles = makeStyles((theme) => ({
actionButtons: { actionButtons: {
marginRight: 6, marginRight: 6,
flex: "none", flex: "none",
@ -35,63 +35,49 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
const ticketOptionsMenuOpen = Boolean(anchorEl); const ticketOptionsMenuOpen = Boolean(anchorEl);
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext);
const handleOpenTicketOptionsMenu = e => { const handleOpenTicketOptionsMenu = (e) => {
setAnchorEl(e.currentTarget); setAnchorEl(e.currentTarget);
}; };
const handleCloseTicketOptionsMenu = e => { const handleCloseTicketOptionsMenu = (e) => {
setAnchorEl(null); setAnchorEl(null);
}; };
const chatEndVal = (data) => { const chatEndVal = (data) => {
if (data) {
data = { ...data, ticketId: ticket.id };
if(data){ console.log("ChatEnd: ", data);
data = {...data, 'ticketId': ticket.id}
console.log('ChatEnd: ',(data));
handleUpdateTicketStatus(null, "closed", user?.id, data)
handleUpdateTicketStatus(null, "closed", user?.id, data);
} }
};
}
const handleModal = (/*status, userId*/) => { const handleModal = (/*status, userId*/) => {
render(
render(<Modal <Modal
modal_header={'Finalização de Atendimento'} modal_header={"Finalização de Atendimento"}
func={chatEndVal} func={chatEndVal}
statusChatEnd={statusChatEnd} statusChatEnd={statusChatEnd}
ticketId={ticket.id} ticketId={ticket.id}
/>) />
);
}; };
const handleUpdateTicketStatus = async (e, status, userId, schedulingData = {}) => {
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
setLoading(true); setLoading(true);
try { try {
if (status === "closed") {
if(status==='closed'){
await api.put(`/tickets/${ticket.id}`, { await api.put(`/tickets/${ticket.id}`, {
status: status, status: status,
userId: userId || null, userId: userId || null,
schedulingNotifyData: JSON.stringify(schedulingData) schedulingNotifyData: JSON.stringify(schedulingData),
}); });
} else {
}
else{
await api.put(`/tickets/${ticket.id}`, { await api.put(`/tickets/${ticket.id}`, {
status: status, status: status,
userId: userId || null userId: userId || null,
}); });
} }
setLoading(false); setLoading(false);
@ -104,10 +90,6 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
setLoading(false); setLoading(false);
toastError(err); toastError(err);
} }
}; };
return ( return (
@ -117,7 +99,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
loading={loading} loading={loading}
startIcon={<Replay />} startIcon={<Replay />}
size="small" size="small"
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)} onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
> >
{i18n.t("messagesList.header.buttons.reopen")} {i18n.t("messagesList.header.buttons.reopen")}
</ButtonWithSpinner> </ButtonWithSpinner>
@ -128,7 +110,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
loading={loading} loading={loading}
startIcon={<Replay />} startIcon={<Replay />}
size="small" size="small"
onClick={e => handleUpdateTicketStatus(e, "pending", null)} onClick={(e) => handleUpdateTicketStatus(e, "pending", null)}
> >
{i18n.t("messagesList.header.buttons.return")} {i18n.t("messagesList.header.buttons.return")}
</ButtonWithSpinner> </ButtonWithSpinner>
@ -138,12 +120,9 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
size="small" size="small"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={e => { onClick={(e) => {
handleModal();
handleModal()
// handleUpdateTicketStatus(e, "closed", user?.id) // handleUpdateTicketStatus(e, "closed", user?.id)
}} }}
> >
{i18n.t("messagesList.header.buttons.resolve")} {i18n.t("messagesList.header.buttons.resolve")}
@ -166,7 +145,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
size="small" size="small"
variant="contained" variant="contained"
color="primary" color="primary"
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)} onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
> >
{i18n.t("messagesList.header.buttons.accept")} {i18n.t("messagesList.header.buttons.accept")}
</ButtonWithSpinner> </ButtonWithSpinner>
@ -176,3 +155,4 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
}; };
export default TicketActionButtons; export default TicketActionButtons;

View File

@ -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;

View File

@ -1,6 +1,6 @@
import React, { useState, useEffect, useRef, useContext } from "react"; 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 { parseISO, format, isSameDay } from "date-fns";
import clsx from "clsx"; import clsx from "clsx";
@ -102,10 +102,10 @@ const useStyles = makeStyles(theme => ({
})); }));
const TicketListItem = ({ ticket }) => { const TicketListItem = ({ ticket }) => {
const classes = useStyles(); const classes = ReactDOM.useStyles();
const history = useHistory(); const history = ReactDOM.useHistory();
const [loading, setLoading] = useState(false); const [loading, setLoading] = useState(false);
const { ticketId } = useParams(); const { ticketId } = ReactDOM.useParams();
const isMounted = useRef(true); const isMounted = useRef(true);
const { user } = useContext(AuthContext); const { user } = useContext(AuthContext);
@ -145,7 +145,7 @@ const TicketListItem = ({ ticket }) => {
if (ticket.status === "pending") return; if (ticket.status === "pending") return;
handleSelectTicket(ticket.id); handleSelectTicket(ticket.id);
}} }}
selected={ticketId && +ticketId === ticket.id} selected={ticketId && +ticketId === ticket.id} ///img src{id}.jpg
className={clsx(classes.ticket, { className={clsx(classes.ticket, {
[classes.pendingTicket]: ticket.status === "pending", [classes.pendingTicket]: ticket.status === "pending",
})} })}

View File

@ -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;

View File

@ -12,7 +12,7 @@ import useTickets from "../../hooks/useTickets";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
const useStyles = makeStyles((theme) => ({ const useStyles = makeStyles(theme => ({
ticketsListWrapper: { ticketsListWrapper: {
position: "relative", position: "relative",
display: "flex", display: "flex",
@ -75,8 +75,12 @@ const reducer = (state, action) => {
if (action.type === "LOAD_TICKETS") { if (action.type === "LOAD_TICKETS") {
const newTickets = action.payload; const newTickets = action.payload;
newTickets.forEach((ticket) => {
const ticketIndex = state.findIndex((t) => t.id === ticket.id); newTickets.forEach(ticket => {
// console.log('* ticket.unreadMessages: ',ticket.unreadMessages)
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
if (ticket.unreadMessages > 0) { if (ticket.unreadMessages > 0) {
@ -93,7 +97,7 @@ const reducer = (state, action) => {
if (action.type === "RESET_UNREAD") { if (action.type === "RESET_UNREAD") {
const ticketId = action.payload; const ticketId = action.payload;
const ticketIndex = state.findIndex((t) => t.id === ticketId); const ticketIndex = state.findIndex(t => t.id === ticketId);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex].unreadMessages = 0; state[ticketIndex].unreadMessages = 0;
} }
@ -104,7 +108,9 @@ const reducer = (state, action) => {
if (action.type === "UPDATE_TICKET") { if (action.type === "UPDATE_TICKET") {
const ticket = action.payload; const ticket = action.payload;
const ticketIndex = state.findIndex((t) => t.id === ticket.id); // console.log('++++++++++++ UPDATE_TICKET: ',ticket)
const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
} else { } else {
@ -115,19 +121,26 @@ const reducer = (state, action) => {
} }
if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") { if (action.type === "UPDATE_TICKET_UNREAD_MESSAGES") {
const message = action.payload.message;
const message = action.payload.message
const ticket = action.payload.ticket; const ticket = action.payload.ticket;
const ticketIndex = state.findIndex((t) => t.id === ticket.id); const ticketIndex = state.findIndex(t => t.id === ticket.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
if (!message.fromMe) {
ticket.unreadMessages += 1; // console.log('>>>>>> ticketIndex: ', ticketIndex)
// console.log('&&&&&&& UPDATE_TICKET_UNREAD_MESSAGES ticket: ',ticket, ' |\n MESSAGE: ', message)
if(!message.fromMe){
ticket.unreadMessages +=1
} }
state[ticketIndex] = ticket; state[ticketIndex] = ticket;
state.unshift(state.splice(ticketIndex, 1)[0]); state.unshift(state.splice(ticketIndex, 1)[0]);
} else { } else {
state.unshift(ticket); state.unshift(ticket);
} }
@ -137,7 +150,7 @@ const reducer = (state, action) => {
if (action.type === "UPDATE_TICKET_CONTACT") { if (action.type === "UPDATE_TICKET_CONTACT") {
const contact = action.payload; const contact = action.payload;
const ticketIndex = state.findIndex((t) => t.contactId === contact.id); const ticketIndex = state.findIndex(t => t.contactId === contact.id);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state[ticketIndex].contact = contact; state[ticketIndex].contact = contact;
} }
@ -146,7 +159,7 @@ const reducer = (state, action) => {
if (action.type === "DELETE_TICKET") { if (action.type === "DELETE_TICKET") {
const ticketId = action.payload; const ticketId = action.payload;
const ticketIndex = state.findIndex((t) => t.id === ticketId); const ticketIndex = state.findIndex(t => t.id === ticketId);
if (ticketIndex !== -1) { if (ticketIndex !== -1) {
state.splice(ticketIndex, 1); state.splice(ticketIndex, 1);
} }
@ -159,7 +172,9 @@ const reducer = (state, action) => {
} }
}; };
const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount, style }) => { const TicketsList = (props) => {
const { status, searchParam, showAll, selectedQueueIds, updateCount, style } =
props;
const classes = useStyles(); const classes = useStyles();
const [pageNumber, setPageNumber] = useState(1); const [pageNumber, setPageNumber] = useState(1);
const [ticketsList, dispatch] = useReducer(reducer, []); const [ticketsList, dispatch] = useReducer(reducer, []);
@ -189,11 +204,11 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
useEffect(() => { useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
const shouldUpdateTicket = (ticket) => const shouldUpdateTicket = ticket =>
(!ticket.userId || ticket.userId === user?.id || showAll) && (!ticket.userId || ticket.userId === user?.id || showAll) &&
(!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1); (!ticket.queueId || selectedQueueIds.indexOf(ticket.queueId) > -1);
const notBelongsToUserQueues = (ticket) => const notBelongsToUserQueues = ticket =>
ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1; ticket.queueId && selectedQueueIds.indexOf(ticket.queueId) === -1;
socket.on("connect", () => { socket.on("connect", () => {
@ -202,9 +217,10 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
} else { } else {
socket.emit("joinNotification"); socket.emit("joinNotification");
} }
}); });
socket.on("ticket", (data) => { socket.on("ticket", data => {
if (data.action === "updateUnread") { if (data.action === "updateUnread") {
dispatch({ dispatch({
type: "RESET_UNREAD", type: "RESET_UNREAD",
@ -228,8 +244,11 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
} }
}); });
socket.on("appMessage", (data) => { socket.on("appMessage", data => {
if (data.action === "create" && shouldUpdateTicket(data.ticket)) { if (data.action === "create" && shouldUpdateTicket(data.ticket)) {
// console.log('((((((((((((((((((( DATA.MESSAGE: ', data.message)
dispatch({ dispatch({
type: "UPDATE_TICKET_UNREAD_MESSAGES", type: "UPDATE_TICKET_UNREAD_MESSAGES",
// payload: data.ticket, // payload: data.ticket,
@ -238,7 +257,7 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
} }
}); });
socket.on("contact", (data) => { socket.on("contact", data => {
if (data.action === "update") { if (data.action === "update") {
dispatch({ dispatch({
type: "UPDATE_TICKET_CONTACT", type: "UPDATE_TICKET_CONTACT",
@ -253,16 +272,20 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
}, [status, showAll, user, selectedQueueIds]); }, [status, showAll, user, selectedQueueIds]);
useEffect(() => { useEffect(() => {
if (typeof updateCount === "function") { if (typeof updateCount === "function") {
updateCount(ticketsList.length); updateCount(ticketsList.length);
} }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ticketsList]); }, [ticketsList]);
const loadMore = () => { const loadMore = () => {
setPageNumber((prevState) => prevState + 1); setPageNumber(prevState => prevState + 1);
}; };
const handleScroll = (e) => { const handleScroll = e => {
if (!hasMore || loading) return; if (!hasMore || loading) return;
const { scrollTop, scrollHeight, clientHeight } = e.currentTarget; const { scrollTop, scrollHeight, clientHeight } = e.currentTarget;
@ -284,12 +307,16 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
<List style={{ paddingTop: 0 }}> <List style={{ paddingTop: 0 }}>
{ticketsList.length === 0 && !loading ? ( {ticketsList.length === 0 && !loading ? (
<div className={classes.noTicketsDiv}> <div className={classes.noTicketsDiv}>
<span className={classes.noTicketsTitle}>{i18n.t("ticketsList.noTicketsTitle")}</span> <span className={classes.noTicketsTitle}>
<p className={classes.noTicketsText}>{i18n.t("ticketsList.noTicketsMessage")}</p> {i18n.t("ticketsList.noTicketsTitle")}
</span>
<p className={classes.noTicketsText}>
{i18n.t("ticketsList.noTicketsMessage")}
</p>
</div> </div>
) : ( ) : (
<> <>
{ticketsList.map((ticket) => ( {ticketsList.map(ticket => (
<TicketListItem ticket={ticket} key={ticket.id} /> <TicketListItem ticket={ticket} key={ticket.id} />
))} ))}
</> </>
@ -302,4 +329,3 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
}; };
export default TicketsList; export default TicketsList;

View File

@ -1,337 +1,64 @@
import React, { useContext, useEffect, useRef, useState } from "react"; import React 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 NewTicketModal from "../NewTicketModal"; import NewTicketModal from "../NewTicketModal";
import TicketsList from "../TicketsList"; import TicketsList from "../TicketsList/TicketsList";
import TabPanel from "../TabPanel";
import { i18n } from "../../translate/i18n"; import { i18n } from "../../translate/i18n";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../Can"; import { Can } from "../Can";
import TicketsQueueSelect from "../TicketsQueueSelect"; import TicketsQueueSelect from "../TicketsQueueSelect";
import TicketsManagerStyled from "./TicketsManager.style";
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",
},
}));
const TicketsManager = () => { const TicketsManager = () => {
const classes = useStyles(); const [valueTab, setValueTab] = React.useState("open");
// Old New State const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false);
const [newPage, setNewPage] = React.useState(true); const [showAllTickets, setShowAllTickets] = React.useState(false);
// Old New State 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 userQueueIds = user.queues.map((q) => q.id);
const [selectedQueueIds, setSelectedQueueIds] = useState(userQueueIds || []); const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []);
const socket = openSocket.open(process.env.REACT_APP_BACKEND_URL)
console.log(user.queues) React.useEffect(() => {
console.log(selectedQueueIds)
socket.on("connect", () => {
if ("open") {
socket.emit("joinTickets", "open");
} else {
socket.emit("joinNotification");
}
});
socket.on("connect", ()=>{
})
useEffect(() => {
if (user.profile.toUpperCase() === "ADMIN") { if (user.profile.toUpperCase() === "ADMIN") {
setShowAllTickets(true); setShowAllTickets(true);
} }
}, [user.profile]); }, [user.profile]);
useEffect(() => {
if (tab === "search") {
searchInputRef.current.focus();
}
}, [tab]);
let searchTimeout;
const handleSearch = (e) => { const styleTmp = {
const searchedTerm = e.target.value.toLowerCase(); padding:"12px 0 ",
cursor: "pointer",
clearTimeout(searchTimeout);
if (searchedTerm === "") {
setSearchParam(searchedTerm);
setTab("open");
return;
} }
searchTimeout = setTimeout(() => {
setSearchParam(searchedTerm);
}, 500);
};
const handleChangeTab = (e, newValue) => { console.log(valueTab);
setTab(newValue);
};
const handleChangeTabOpen = (e, newValue) => {
setTabOpen(newValue);
};
const applyPanelStyle = (status) => {
if (tabOpen !== status) {
return { width: 0, height: 0 };
}
};
if (newPage) {
return ( return (
<> <TicketsManagerStyled>
<NewTicketModal <ul>
modalOpen={newTicketModalOpen} <li style={styleTmp} id="open" onClick={(e) => setValueTab(e.target.id)}>
onClose={(e) => setNewTicketModalOpen(false)} Abertos
/> </li>
<div> <li style={styleTmp} id="pending" onClick={(e) => setValueTab(e.target.id)}>
<TicketsQueueSelect Pendentes
style={{ marginLeft: 6 }} </li>
selectedQueueIds={selectedQueueIds} <li style={styleTmp} id="closed" onClick={(e) => setValueTab(e.target.id)}>
userQueues={user?.queues} Fechados
onChange={(values) => setSelectedQueueIds(values)} </li>
/> </ul>
<TicketsList <TicketsList
status="open" status={valueTab}
showAll={showAllTickets}
selectedQueueIds={selectedQueueIds}
updateCount={(val) => setOpenCount(val)}
style={applyPanelStyle("open")}
/>
<TicketsList
status="pending"
selectedQueueIds={selectedQueueIds} selectedQueueIds={selectedQueueIds}
updateCount={(val) => setPendingCount(val)} updateCount={(val) => setPendingCount(val)}
style={applyPanelStyle("pending")}
/> />
</div>
</>
);
}
return (
<Paper elevation={0} variant="outlined" className={classes.ticketsWrapper}>
<NewTicketModal <NewTicketModal
modalOpen={newTicketModalOpen} modalOpen={newTicketModalOpen}
onClose={(e) => setNewTicketModalOpen(false)} onClose={(e) => setNewTicketModalOpen(true)}
/> />
<Paper elevation={0} square className={classes.tabsHeader}> </TicketsManagerStyled>
<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>
); );
}; };

View File

@ -0,0 +1,7 @@
import styled from "styled-components"
const TicketsManagerStyled = styled.div`
display:flex;
flex-direction: column;
`
export default TicketsManagerStyled

View File

@ -3,7 +3,8 @@ import styled from "styled-components";
const TicketsStyled = styled.div` const TicketsStyled = styled.div`
display: flex; display: flex;
flex-direction: row; flex-direction: row;
height: 86vh; min-height: auto;
`; `;
export default TicketsStyled export default TicketsStyled