Tabs and Manager list fix some functions

WIP

Project stop by order chef
pull/14/head^2
RenatoDiGiacomo 2022-07-28 12:15:28 -03:00
parent 3c43d78cfc
commit 4c24f81336
12 changed files with 425 additions and 476 deletions

View File

@ -1,9 +1,9 @@
import React from "react"; import React from "react";
import { BadgeComponentStyled } from "./BadgeComponent.style"; import { BadgeComponentStyled } from "./BadgeComponent.style";
const BadgeComponent = ({ counter, position, top, left, right, bottom }) => { const BadgeComponent = ({ counter,fontSize, position, top, left, right, bottom,bgcolor }) => {
return ( return (
<BadgeComponentStyled position={position} top={top} left={left} right={right} bottom={bottom}> <BadgeComponentStyled position={position} top={top} left={left} right={right} bottom={bottom} fontSize={fontSize} bgcolor={bgcolor}>
{counter} {counter}
</BadgeComponentStyled> </BadgeComponentStyled>
); );

View File

@ -15,8 +15,8 @@ export const BadgeComponentStyled = styled.span`
object-fit: cover; object-fit: cover;
width: 21px; width: 21px;
height: 21px; height: 21px;
font-size: 16px; font-size: ${({ fontSize }) => (fontSize ? fontSize : "16px")};
color: ${color.pricinpal.blanco}; color: ${color.pricinpal.blanco};
background-color: ${color.status.yes}; background-color: ${({ bgcolor }) => (bgcolor ? bgcolor : color.status.yes)};
`; `;

View File

@ -3,7 +3,7 @@ import { color } from "../../../../style/varibles";
const InputComponentStyled = styled.input` const InputComponentStyled = styled.input`
width: 100%; width: 100%;
background: transparent; background: ${color.complement.azulOscuro};
border: none; border: none;
color: ${color.complement.azulCielo}; color: ${color.complement.azulCielo};
`; `;

View File

@ -88,13 +88,9 @@ const Ticket = () => {
setDrawerOpen(false); setDrawerOpen(false);
}; };
const style ={
height: "fit-content",
width: "100%",
position: "relative",
}
return ( return (
<div className="test" style={style} > <div className="test" >
<TicketHeader loading={loading}> <TicketHeader loading={loading}>
<div> <div>
<TicketInfo contact={contact} ticket={ticket} onClick={handleDrawerOpen} /> <TicketInfo contact={contact} ticket={ticket} onClick={handleDrawerOpen} />

View File

@ -0,0 +1,7 @@
import styled from "styled-components";
import { color } from "../../style/varibles";
export const TicketStyled = styled.div`
width: 100%;
background-color: ${color.status.no};
`;

View File

@ -8,7 +8,7 @@ import { i18n } from "../../../../../translate/i18n";
import api from "../../../../../services/api"; import api from "../../../../../services/api";
import { AuthContext } from "../../../../../context/Auth/AuthContext"; import { AuthContext } from "../../../../../context/Auth/AuthContext";
import toastError from "../../../../../errors/toastError"; import toastError from "../../../../../errors/toastError";
import Btn from "../../../../Base/Btn/Btn" import Btn from "../../../../Base/Btn/Btn";
import { import {
TicketDateStyled, TicketDateStyled,
TicketImgStyled, TicketImgStyled,
@ -24,7 +24,6 @@ const TicketListItem = ({ tickets }) => {
const [loading, setLoading] = React.useState(false); const [loading, setLoading] = React.useState(false);
const isMounted = React.useRef(true); const isMounted = React.useRef(true);
const { user } = React.useContext(AuthContext); const { user } = React.useContext(AuthContext);
React.useEffect(() => { React.useEffect(() => {
return () => { return () => {
isMounted.current = false; isMounted.current = false;
@ -32,7 +31,7 @@ const TicketListItem = ({ tickets }) => {
}, []); }, []);
const handleAcepptTicket = async (id) => { const handleAcepptTicket = async (id) => {
setLoading(true) setLoading(true);
try { try {
await api.put(`/tickets/${id}`, { await api.put(`/tickets/${id}`, {
status: "open", status: "open",
@ -54,10 +53,14 @@ const TicketListItem = ({ tickets }) => {
const handleSelectTicket = (id) => { const handleSelectTicket = (id) => {
history.push(`/tickets/${id}`); history.push(`/tickets/${id}`);
}; };
if (loading) return <LoadingScreen />;
return ( return (
<React.Fragment key={tickets.id}> <React.Fragment key={tickets.id}>
<TicketListItemStyled queuecolor={tickets.queue} onClick={()=>handleSelectTicket(tickets.id)}> <TicketListItemStyled
queuecolor={tickets.queue}
onClick={() => handleSelectTicket(tickets.id)}
>
{tickets.contact.profilePicUrl ? ( {tickets.contact.profilePicUrl ? (
<TicketImgStyled src={tickets.contact.profilePicUrl} alt={tickets.id} /> <TicketImgStyled src={tickets.contact.profilePicUrl} alt={tickets.id} />
) : ( ) : (
@ -86,7 +89,13 @@ const TicketListItem = ({ tickets }) => {
)} )}
</TicketDateStyled> </TicketDateStyled>
{tickets.status === "pending" ? ( {tickets.status === "pending" ? (
<Btn text="Aceitar" bgcolor={color.complement.azulCielo} fontcolor={color.pricinpal.blanco} fontSize="12px"/> <Btn
onClick={() => handleAcepptTicket(tickets.id)}
text="Aceitar"
bgcolor={color.complement.azulCielo}
fontcolor={color.pricinpal.blanco}
fontSize="12px"
/>
) : ( ) : (
"" ""
)} )}

View File

@ -2,6 +2,8 @@ import React from "react";
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
import LoadingScreen from "../../../LoadingScreen/LoadingScreen";
import TicketListStyled from "./TicketsList.style"; import TicketListStyled from "./TicketsList.style";
import useTickets from "../../../../hooks/useTickets"; import useTickets from "../../../../hooks/useTickets";
import TicketListItem from "../TicketsList/TicketListItem/TicketListItem"; import TicketListItem from "../TicketsList/TicketListItem/TicketListItem";
@ -94,10 +96,14 @@ const reducer = (state, action) => {
return []; return [];
} }
}; };
const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount }) => { const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCount, valueTab }) => {
const [pageNumber, setPageNumber] = React.useState(1); const [pageNumber, setPageNumber] = React.useState(1);
const [ticketsList, dispatch] = React.useReducer(reducer, []); const [ticketsList, dispatch] = React.useReducer(reducer, []);
const { user } = React.useContext(AuthContext); const { user } = React.useContext(AuthContext);
React.useEffect(() => {
dispatch({ type: "RESET" });
setPageNumber(1);
}, [status, searchParam, dispatch, showAll, selectedQueueIds]);
const { tickets, loading } = useTickets({ const { tickets, loading } = useTickets({
pageNumber, pageNumber,
searchParam, searchParam,
@ -105,10 +111,13 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
showAll, showAll,
queueIds: JSON.stringify(selectedQueueIds), queueIds: JSON.stringify(selectedQueueIds),
}); });
React.useEffect(() => { React.useEffect(() => {
dispatch({ type: "RESET" }); if (typeof updateCount === "function") {
setPageNumber(1); updateCount(ticketsList.length);
}, [status, searchParam, dispatch, showAll, selectedQueueIds]); }
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [ticketsList]);
React.useEffect(() => { React.useEffect(() => {
if (!status && !searchParam) return; if (!status && !searchParam) return;
@ -116,7 +125,7 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
type: "LOAD_TICKETS", type: "LOAD_TICKETS",
payload: tickets, payload: tickets,
}); });
}, [tickets, searchParam]); }, [tickets, status, searchParam]);
React.useEffect(() => { React.useEffect(() => {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
@ -162,6 +171,8 @@ 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,
@ -185,13 +196,16 @@ const TicketsList = ({ status, searchParam, showAll, selectedQueueIds, updateCou
}, [status, showAll, user, selectedQueueIds]); }, [status, showAll, user, selectedQueueIds]);
if (loading) return <TicketsListSkeleton />; if (loading) return <TicketsListSkeleton />;
if (status === valueTab)
return ( return (
<TicketListStyled> <TicketListStyled>
{ticketsList.map((ticket) => ( {ticketsList.map((ticket) => (
<TicketListItem tickets={ticket} status={status} key={ticket.id} /> <TicketListItem tickets={ticket} key={ticket.id} />
))} ))}
</TicketListStyled> </TicketListStyled>
); );
return null;
}; };
export default TicketsList; export default TicketsList;

View File

@ -2,24 +2,25 @@ import React from "react";
import TicketsManagerStyled from "./TicketsManager.style"; import TicketsManagerStyled from "./TicketsManager.style";
import TicketsTabs from "./TicketsTabs/TicketsTabs"; import TicketsTabs from "./TicketsTabs/TicketsTabs";
import TicketSearch from "../TicketSearch/TicketSearch";
import TicketsList from "./TicketsList/TicketsList"; import TicketsList from "./TicketsList/TicketsList";
import NewTicketModal from "../../NewTicketModal"; import NewTicketModal from "../../NewTicketModal";
import useTickets from "../../../hooks/useTickets";
import { AuthContext } from "../../../context/Auth/AuthContext"; import { AuthContext } from "../../../context/Auth/AuthContext";
import TicketSearch from "../TicketSearch/TicketSearch";
const TicketsManager = () => { const TicketsManager = () => {
const [valueTab, setValueTab] = React.useState("open"); const [valueTab, setValueTab] = React.useState("open");
const [searchParam, setSearchParam] = React.useState(""); const [searchParam, setSearchParam] = React.useState("");
const [spinning, setSpinning] = React.useState(false); const [spinning, setSpinning] = React.useState(false);
const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false); const [newTicketModalOpen, setNewTicketModalOpen] = React.useState(false);
const [openCount, setOpenCount] = React.useState(0);
const [pendingCount, setPendingCount] = React.useState(0);
const [closedCount, setClosedCount] = React.useState(0);
const [showAllTickets, setShowAllTickets] = React.useState(false); const [showAllTickets, setShowAllTickets] = React.useState(false);
const { user } = React.useContext(AuthContext); const { user } = React.useContext(AuthContext);
const userQueueIds = user.queues.map((q) => q.id); const userQueueIds = user.queues.map((q) => q.id);
const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []); const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []);
const { tickets } = useTickets({
searchParam
});
let searchTimeout; let searchTimeout;
const handleSearch = (e) => { const handleSearch = (e) => {
@ -47,23 +48,47 @@ const TicketsManager = () => {
return ( return (
<TicketsManagerStyled> <TicketsManagerStyled>
<TicketsTabs tickets={tickets} setValueTab={setValueTab} valueTab={valueTab} /> <TicketsTabs
setValueTab={setValueTab}
valueTab={valueTab}
count={{ openCount, pendingCount, closedCount }}
/>
<TicketSearch <TicketSearch
spinning={spinning} spinning={spinning}
handleSearch={handleSearch} handleSearch={handleSearch}
setNewTicketModalOpen={setNewTicketModalOpen} setNewTicketModalOpen={setNewTicketModalOpen}
/> />
<TicketsList <TicketsList
status="open"
updateCount={(v) => setOpenCount(v)}
showAll={showAllTickets} showAll={showAllTickets}
status={valueTab}
selectedQueueIds={selectedQueueIds} selectedQueueIds={selectedQueueIds}
searchParam={searchParam} searchParam={searchParam}
valueTab={valueTab}
/>
<TicketsList
status="pending"
updateCount={(v) => setPendingCount(v)}
selectedQueueIds={selectedQueueIds}
searchParam={searchParam}
valueTab={valueTab}
/>
<TicketsList
status="closed"
updateCount={(v) => setClosedCount(v)}
selectedQueueIds={selectedQueueIds}
searchParam={searchParam}
valueTab={valueTab}
/> />
<NewTicketModal <NewTicketModal
modalOpen={newTicketModalOpen} modalOpen={newTicketModalOpen}
onClose={(e) => setNewTicketModalOpen(false)} onClose={(e) => setNewTicketModalOpen(false)}
/> />
</TicketsManagerStyled> </TicketsManagerStyled>
); );
}; };

View File

@ -1,7 +1,10 @@
import React from "react"; import React from "react";
import { TicketsTabStyled } from "./TicketsTab.style"; import { TicketsTabStyled } from "./TicketsTab.style";
import { color } from "../../../../../style/varibles";
const TicketsTab = ({ text, id, setValueTab, valueTab }) => { import BadgeComponent from "../../../../Base/Badge/BadgeComponent";
const TicketsTab = ({ text, id, setValueTab, valueTab, count }) => {
const [active, setActive] = React.useState(false); const [active, setActive] = React.useState(false);
const handleClick = ({ target }) => { const handleClick = ({ target }) => {
@ -11,6 +14,7 @@ const TicketsTab = ({ text, id, setValueTab, valueTab }) => {
React.useEffect(() => { React.useEffect(() => {
valueTab === id ? setActive(true) : setActive(false); valueTab === id ? setActive(true) : setActive(false);
}, [valueTab, id]); }, [valueTab, id]);
return ( return (
<TicketsTabStyled <TicketsTabStyled
id={id} id={id}
@ -19,6 +23,18 @@ const TicketsTab = ({ text, id, setValueTab, valueTab }) => {
className={active ? "active" : ""} className={active ? "active" : ""}
> >
{text} {text}
{id !== "open" ? (
<BadgeComponent
counter={count}
position="absolute"
right="4px"
top="6px"
fontSize="12px"
bgcolor={id === "pending" ? color.status.warning : color.status.no}
/>
) : (
""
)}
</TicketsTabStyled> </TicketsTabStyled>
); );
}; };

View File

@ -1,16 +1,32 @@
import React from "react"; import React from "react";
import TicketsTab from "../TicketsTabs/TicketsTab/TicketsTab";
import { TicketTabsStyled } from "./TicketsTabs.style"; import { TicketTabsStyled } from "./TicketsTabs.style";
import TicketsTab from "./TicketsTab/TicketsTab"; const TicketsTabs = ({ setValueTab, valueTab, count }) => {
const TicketsTabs = ({ tickets, setValueTab, valueTab }) => {
if (!setValueTab) return null;
return ( return (
<TicketTabsStyled> <TicketTabsStyled>
<TicketsTab text="Aberto" id="open" setValueTab={setValueTab} valueTab={valueTab} /> <TicketsTab
<TicketsTab text="Aguardando" id="pending" setValueTab={setValueTab} valueTab={valueTab} /> text="Aberto"
<TicketsTab text="Fechado" id="closed" setValueTab={setValueTab} valueTab={valueTab} /> id="open"
setValueTab={setValueTab}
valueTab={valueTab}
count={count.openCount}
/>
<TicketsTab
text="Aguardando"
id="pending"
setValueTab={setValueTab}
valueTab={valueTab}
count={count.pendingCount}
/>
<TicketsTab
text="Fechado"
id="closed"
setValueTab={setValueTab}
valueTab={valueTab}
count={count.closedCount}
/>
</TicketTabsStyled> </TicketTabsStyled>
); );
}; };

View File

@ -3,101 +3,89 @@ import MainContainer from "../../components/MainContainer";
import api from "../../services/api"; import api from "../../services/api";
import SelectField from "../../components/Report/SelectField"; import SelectField from "../../components/Report/SelectField";
//import { data } from '../../components/Report/MTable/data'; //import { data } from '../../components/Report/MTable/data';
import DatePicker1 from '../../components/Report/DatePicker' import DatePicker1 from "../../components/Report/DatePicker";
import DatePicker2 from '../../components/Report/DatePicker' import DatePicker2 from "../../components/Report/DatePicker";
import MTable from "../../components/Report/MTable"; import MTable from "../../components/Report/MTable";
import PropTypes from 'prop-types'; import PropTypes from "prop-types";
import Box from '@mui/material/Box'; import Box from "@mui/material/Box";
import { AuthContext } from "../../context/Auth/AuthContext"; import { AuthContext } from "../../context/Auth/AuthContext";
import { Can } from "../../components/Can"; import { Can } from "../../components/Can";
import { Button } from "@material-ui/core"; import { Button } from "@material-ui/core";
import ReportModal from "../../components/ReportModal"; import ReportModal from "../../components/ReportModal";
import MaterialTable from 'material-table'; import MaterialTable from "material-table";
import LogoutIcon from '@material-ui/icons/CancelOutlined'; import LogoutIcon from "@material-ui/icons/CancelOutlined";
import { CSVLink } from "react-csv"; import { CSVLink } from "react-csv";
import openSocket from "socket.io-client"; import openSocket from "socket.io-client";
const report = [{ 'value': '1', 'label': 'Atendimento por atendentes' }, { 'value': '2', 'label': 'Usuários online/offline' }] const report = [
{ value: "1", label: "Atendimento por atendentes" },
{ value: "2", label: "Usuários online/offline" },
];
let columns = [ let columns = [
{ {
key: 'ticket.whatsapp.name', key: "ticket.whatsapp.name",
label: 'Loja', label: "Loja",
}, },
{ {
key: 'id', key: "id",
label: 'id Mensagem', label: "id Mensagem",
}, },
{ {
key: 'ticket.id', key: "ticket.id",
label: 'id Conversa', label: "id Conversa",
}, },
{ {
key: 'ticket.contact.name', key: "ticket.contact.name",
label: 'Cliente', label: "Cliente",
}, },
{ {
key: 'ticket.user.name', key: "ticket.user.name",
label: 'Atendente', label: "Atendente",
}, },
{ {
key: 'body', key: "body",
label: 'Mensagem', label: "Mensagem",
}, },
{ {
key: 'fromMe', key: "fromMe",
label: 'Sentido', label: "Sentido",
}, },
{ {
key: 'createdAt', key: "createdAt",
label: 'Criada', label: "Criada",
}, },
{ {
key: 'ticket.contact.number', key: "ticket.contact.number",
label: 'Telefone cliente', label: "Telefone cliente",
}, },
{ {
key: 'ticket.queue.name', key: "ticket.queue.name",
label: 'Fila', label: "Fila",
}, },
{ {
key: 'ticket.status', key: "ticket.status",
label: 'Status', label: "Status",
}, },
{ {
key: 'ticket.statusChatEnd', key: "ticket.statusChatEnd",
label: 'Status de encerramento', label: "Status de encerramento",
} },
];
]
// //
const reducerQ = (state, action) => { const reducerQ = (state, action) => {
if (action.type === "DELETE_USER_STATUS") { if (action.type === "DELETE_USER_STATUS") {
const userId = action.payload; const userId = action.payload;
//console.log('Entrou no delete user status userId: ', userId) //console.log('Entrou no delete user status userId: ', userId)
@ -113,115 +101,90 @@ const reducerQ = (state, action) => {
return [...state]; return [...state];
} }
if (action.type === "LOAD_QUERY") {
const queries = action.payload;
if (action.type === 'LOAD_QUERY') { const newQueries = [];
const queries = action.payload
const newQueries = []
queries.forEach((query) => { queries.forEach((query) => {
const queryIndex = state.findIndex((q) => q.id === query.id);
const queryIndex = state.findIndex((q) => q.id === query.id)
if (queryIndex !== -1) { if (queryIndex !== -1) {
state[queryIndex] = query state[queryIndex] = query;
} else {
newQueries.push(query);
} }
else { });
newQueries.push(query)
return [...state, ...newQueries];
} }
})
return [...state, ...newQueries]
}
if (action.type === "UPDATE_STATUS_ONLINE") { if (action.type === "UPDATE_STATUS_ONLINE") {
let onlineUser = action.payload;
let onlineUser = action.payload let index = -1;
let index = -1
// console.log('sssssssssstate: ', state, ' | ONLINE USERS: onlineUser.userId ', onlineUser.userId) // console.log('sssssssssstate: ', state, ' | ONLINE USERS: onlineUser.userId ', onlineUser.userId)
if (onlineUser.sumOpen || onlineUser.sumClosed) { if (onlineUser.sumOpen || onlineUser.sumClosed) {
index = state.findIndex((e) => ((onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) || (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId))) index = state.findIndex(
} (e) =>
else { (onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) ||
index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`) (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId)
);
} else {
index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`);
} }
//console.log(' *********************** index: ', index) //console.log(' *********************** index: ', index)
if (index !== -1) { if (index !== -1) {
// console.log('ENTROU NO INDEX') // console.log('ENTROU NO INDEX')
if (!("statusOnline" in state[index])) { if (!("statusOnline" in state[index])) {
state[index].statusOnline = onlineUser state[index].statusOnline = onlineUser;
} else if ("statusOnline" in state[index]) {
state[index].statusOnline["status"] = onlineUser.status;
} }
else if ("statusOnline" in state[index]) {
state[index].statusOnline['status'] = onlineUser.status
}
if ("onlineTime" in onlineUser) { if ("onlineTime" in onlineUser) {
if ("sumOnlineTime" in state[index]) { if ("sumOnlineTime" in state[index]) {
state[index].sumOnlineTime['sum'] = (onlineUser.onlineTime).split(" ")[1] state[index].sumOnlineTime["sum"] = onlineUser.onlineTime.split(" ")[1];
} } else if (!("sumOnlineTime" in state[index])) {
else if (!("sumOnlineTime" in state[index])) { state[index].sumOnlineTime = {
state[index].sumOnlineTime = { userId: onlineUser.userId, sum: (onlineUser.onlineTime).split(" ")[1] } userId: onlineUser.userId,
sum: onlineUser.onlineTime.split(" ")[1],
};
} }
} }
if (onlineUser.sumOpen) { if (onlineUser.sumOpen) {
if ("sumOpen" in state[index]) { if ("sumOpen" in state[index]) {
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1 | state[index].sumOpen["count"]: ', state[index].sumOpen['count'], ' | onlineUser.sumOpen.count: ', onlineUser.sumOpen.count) // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1 | state[index].sumOpen["count"]: ', state[index].sumOpen['count'], ' | onlineUser.sumOpen.count: ', onlineUser.sumOpen.count)
state[index].sumOpen['count'] = onlineUser.sumOpen.count state[index].sumOpen["count"] = onlineUser.sumOpen.count;
} else if (!("sumOpen" in state[index])) { } else if (!("sumOpen" in state[index])) {
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1')
state[index].sumOpen = onlineUser.sumOpen state[index].sumOpen = onlineUser.sumOpen;
} }
} }
if (onlineUser.sumClosed) { if (onlineUser.sumClosed) {
if ("sumClosed" in state[index]) { if ("sumClosed" in state[index]) {
// console.log(' >>>>>>>>>>>>>>>>>> sumClosed 1 | state[index].sumClosed["count"]: ', state[index].sumClosed['count'], ' | onlineUser.sumClosed.count: ', onlineUser.sumClosed.count) // console.log(' >>>>>>>>>>>>>>>>>> sumClosed 1 | state[index].sumClosed["count"]: ', state[index].sumClosed['count'], ' | onlineUser.sumClosed.count: ', onlineUser.sumClosed.count)
state[index].sumClosed['count'] = onlineUser.sumClosed.count state[index].sumClosed["count"] = onlineUser.sumClosed.count;
} else if (!("sumClosed" in state[index])) { } else if (!("sumClosed" in state[index])) {
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1') // console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1')
state[index].sumClosed = onlineUser.sumClosed state[index].sumClosed = onlineUser.sumClosed;
} }
} }
} }
return [...state] return [...state];
} }
if (action.type === "RESET") { if (action.type === "RESET") {
return []; return [];
} }
};
}
const reducer = (state, action) => { const reducer = (state, action) => {
if (action.type === "LOAD_USERS") { if (action.type === "LOAD_USERS") {
const users = action.payload; const users = action.payload;
const newUsers = []; const newUsers = [];
@ -253,24 +216,20 @@ const reducer = (state, action) => {
} }
}; };
function Item(props) { function Item(props) {
const { sx, ...other } = props; const { sx, ...other } = props;
return ( return (
<Box <Box
sx={{ sx={{
bgcolor: (theme) => (theme.palette.mode === 'dark' ? '#101010' : '#fff'), bgcolor: (theme) => (theme.palette.mode === "dark" ? "#101010" : "#fff"),
color: (theme) => (theme.palette.mode === 'dark' ? 'grey.300' : 'grey.800'), color: (theme) => (theme.palette.mode === "dark" ? "grey.300" : "grey.800"),
border: '1px solid', border: "1px solid",
borderColor: (theme) => borderColor: (theme) => (theme.palette.mode === "dark" ? "grey.800" : "grey.300"),
theme.palette.mode === 'dark' ? 'grey.800' : 'grey.300',
p: 1, p: 1,
m: 1, m: 1,
borderRadius: 2, borderRadius: 2,
fontSize: '0.875rem', fontSize: "0.875rem",
fontWeight: '700', fontWeight: "700",
...sx, ...sx,
}} }}
{...other} {...other}
@ -280,33 +239,28 @@ function Item(props) {
Item.propTypes = { Item.propTypes = {
sx: PropTypes.oneOfType([ sx: PropTypes.oneOfType([
PropTypes.arrayOf( PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])),
PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]),
),
PropTypes.func, PropTypes.func,
PropTypes.object, PropTypes.object,
]), ]),
}; };
let columnsData = [ let columnsData = [
{ title: 'Unidade', field: 'whatsapp.name' }, { title: "Unidade", field: "whatsapp.name" },
{ title: 'Atendente', field: 'user.name' }, { title: "Atendente", field: "user.name" },
{ title: 'Contato', field: 'contact.number' }, { title: "Contato", field: "contact.number" },
{ title: 'Nome', field: 'contact.name' }, { title: "Nome", field: "contact.name" },
{ title: 'Assunto', field: 'queue.name' }, { title: "Assunto", field: "queue.name" },
{ title: 'Status', field: 'status' }, { title: "Status", field: "status" },
{ title: 'Criado', field: 'createdAt' }, { title: "Criado", field: "createdAt" },
//{title: 'Atualizado', field: 'updatedAt'}, //{title: 'Atualizado', field: 'updatedAt'},
{title: 'Status de encerramento', field: 'statusChatEnd'}]; { title: "Status de encerramento", field: "statusChatEnd" },
];
const Report = () => { const Report = () => {
const csvLink = useRef();
const csvLink = useRef()
const { user: userA } = useContext(AuthContext); const { user: userA } = useContext(AuthContext);
@ -317,39 +271,32 @@ const Report = () => {
const [pageNumber, setPageNumber] = useState(1); const [pageNumber, setPageNumber] = useState(1);
const [users, dispatch] = useReducer(reducer, []); const [users, dispatch] = useReducer(reducer, []);
//const [columns, setColums] = useState([]) //const [columns, setColums] = useState([])
const [startDate, setDatePicker1] = useState(new Date()) const [startDate, setDatePicker1] = useState(new Date());
const [endDate, setDatePicker2] = useState(new Date()) const [endDate, setDatePicker2] = useState(new Date());
const [userId, setUser] = useState(null) const [userId, setUser] = useState(null);
const [query, dispatchQ] = useReducer(reducerQ, []) const [query, dispatchQ] = useReducer(reducerQ, []);
const [dataCSV, setDataCSV] = useState([]) const [dataCSV, setDataCSV] = useState([]);
const [isMount, setIsMount] = useState(true); const [isMount, setIsMount] = useState(true);
const [reportOption, setReport] = useState('1') const [reportOption, setReport] = useState("1");
const [reporList,] = useState(report) const [reporList] = useState(report);
const [profile, setProfile] = useState('') const [profile, setProfile] = useState("");
const [dataRows, setData] = useState([]); const [dataRows, setData] = useState([]);
useEffect(() => { useEffect(() => {
dispatch({ type: "RESET" }); dispatch({ type: "RESET" });
dispatchQ({ type: "RESET" }) dispatchQ({ type: "RESET" });
setPageNumber(1); setPageNumber(1);
}, [searchParam, profile]); }, [searchParam, profile]);
useEffect(() => { useEffect(() => {
//setLoading(true); //setLoading(true);
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchUsers = async () => { const fetchUsers = async () => {
try { try {
//console.log('profile: ', profile) //console.log('profile: ', profile)
const { data } = await api.get("/users/", { const { data } = await api.get("/users/", {
@ -359,34 +306,27 @@ const Report = () => {
dispatch({ type: "LOAD_USERS", payload: data.users }); dispatch({ type: "LOAD_USERS", payload: data.users });
//setHasMore(data.hasMore); //setHasMore(data.hasMore);
//setLoading(false); //setLoading(false);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
fetchUsers(); fetchUsers();
}, 500); }, 500);
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn);
}, [searchParam, pageNumber, reportOption, profile]); }, [searchParam, pageNumber, reportOption, profile]);
useEffect(() => { useEffect(() => {
//setLoading(true); //setLoading(true);
const delayDebounceFn = setTimeout(() => { const delayDebounceFn = setTimeout(() => {
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
if (reportOption === "1") {
if (reportOption === '1') { const dataQuery = await api.get("/reports/", {
params: { userId, startDate, endDate },
const dataQuery = await api.get("/reports/", { params: { userId, startDate, endDate }, }); });
dispatchQ({ type: "RESET" }) dispatchQ({ type: "RESET" });
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data }); dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data });
//setLoading(false); //setLoading(false);
@ -394,12 +334,11 @@ const Report = () => {
// console.log('dataQuery: ', dataQuery.data) // console.log('dataQuery: ', dataQuery.data)
// console.log() // console.log()
} else if (reportOption === "2") {
} const dataQuery = await api.get("/reports/user/services", {
else if (reportOption === '2') { params: { userId, startDate, endDate },
});
const dataQuery = await api.get("/reports/user/services", { params: { userId, startDate, endDate }, }); dispatchQ({ type: "RESET" });
dispatchQ({ type: "RESET" })
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data }); dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data });
//setLoading(false); //setLoading(false);
@ -407,62 +346,46 @@ const Report = () => {
// console.log('REPORT 2 dataQuery : ', dataQuery.data) // console.log('REPORT 2 dataQuery : ', dataQuery.data)
//console.log() //console.log()
} }
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
fetchQueries(); fetchQueries();
}, 500); }, 500);
return () => clearTimeout(delayDebounceFn); return () => clearTimeout(delayDebounceFn);
}, [userId, startDate, endDate, reportOption]); }, [userId, startDate, endDate, reportOption]);
// Get from child 1 // Get from child 1
const datePicker1Value = (data) => { const datePicker1Value = (data) => {
setDatePicker1(data);
setDatePicker1(data) };
}
// Get from child 2 // Get from child 2
const datePicker2Value = (data) => { const datePicker2Value = (data) => {
setDatePicker2(data);
setDatePicker2(data) };
}
// Get from child 3 // Get from child 3
const textFieldSelectUser = (data) => { const textFieldSelectUser = (data) => {
setUser(data);
setUser(data) };
}
// Get from report option // Get from report option
const reportValue = (data) => { const reportValue = (data) => {
setReport(data);
setReport(data)
// console.log(' data: ', data) // console.log(' data: ', data)
} };
useEffect(() => { useEffect(() => {
if (reportOption === "1") {
if (reportOption === '1') { setProfile("");
setProfile('') } else if (reportOption === "2") {
setProfile("user");
} }
else if (reportOption === '2') { }, [reportOption]);
setProfile('user')
}
}, [reportOption])
// useEffect(() => { // useEffect(() => {
@ -470,106 +393,85 @@ const Report = () => {
// }, [query]) // }, [query])
// test del // test del
const handleCSVMessages = () => { const handleCSVMessages = () => {
// setLoading(true); // setLoading(true);
const fetchQueries = async () => { const fetchQueries = async () => {
try { try {
const dataQuery = await api.get("/reports/messages", {
const dataQuery = await api.get("/reports/messages", { params: { userId, startDate, endDate }, }); params: { userId, startDate, endDate },
});
// console.log('dataQuery messages: ', dataQuery.data) // console.log('dataQuery messages: ', dataQuery.data)
if (dataQuery.data.length > 0) { if (dataQuery.data.length > 0) {
let dataCSVFormat = dataQuery.data; let dataCSVFormat = dataQuery.data;
for (var i = 0; i < dataCSVFormat.length; i++) { for (var i = 0; i < dataCSVFormat.length; i++) {
if (dataCSVFormat[i].fromMe) { if (dataCSVFormat[i].fromMe) {
dataCSVFormat[i].fromMe = 'Atendente' dataCSVFormat[i].fromMe = "Atendente";
} } else {
else { dataCSVFormat[i].fromMe = "Cliente";
dataCSVFormat[i].fromMe = 'Cliente'
} }
} }
// console.log('dataCSVFormat: ', dataCSVFormat) // console.log('dataCSVFormat: ', dataCSVFormat)
setDataCSV(dataCSVFormat) setDataCSV(dataCSVFormat);
setIsMount(false); setIsMount(false);
// setDataCSV(dataQuery.data) // setDataCSV(dataQuery.data)
} }
// setLoading(false); // setLoading(false);
} catch (err) { } catch (err) {
console.log(err); console.log(err);
} }
}; };
fetchQueries(); fetchQueries();
};
}
useEffect(() => { useEffect(() => {
if (isMount) { if (isMount) {
return; return;
} }
csvLink.current.link.click() csvLink.current.link.click();
}, [dataCSV, isMount, csvLink]); }, [dataCSV, isMount, csvLink]);
useEffect(() => { useEffect(() => {
if (reportOption === "2") {
if (reportOption === '2') {
const socket = openSocket(process.env.REACT_APP_BACKEND_URL); const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
socket.on("onlineStatus", (data) => { socket.on("onlineStatus", (data) => {
// setLoading(true); // setLoading(true);
let date = new Date().toLocaleDateString("pt-BR").split("/");
let date = new Date().toLocaleDateString('pt-BR').split('/') let dateToday = `${date[2]}-${date[1]}-${date[0]}`;
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
// console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR')) // console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR'))
// console.log('date2: ', startDate) // console.log('date2: ', startDate)
if (
if (data.action === "logout" || (data.action === "update" && data.action === "logout" ||
((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) { (data.action === "update" &&
`${startDate}` === `${endDate}` &&
`${endDate}` === `${dateToday}` &&
`${startDate}` === `${dateToday}`)
) {
//console.log('UPDATE FROM ONLINE/OFFLINE LOGED USERS: ', data.userOnlineTime, ' | data.action : ', data.action) //console.log('UPDATE FROM ONLINE/OFFLINE LOGED USERS: ', data.userOnlineTime, ' | data.action : ', data.action)
dispatchQ({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime }); dispatchQ({ type: "UPDATE_STATUS_ONLINE", payload: data.userOnlineTime });
} else if (data.action === "delete") {
}
else if (data.action === "delete") {
dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime }); dispatchQ({ type: "DELETE_USER_STATUS", payload: data.userOnlineTime });
} }
// setLoading(false); // setLoading(false);
}); });
socket.on("user", (data) => { socket.on("user", (data) => {
if (data.action === "delete") { if (data.action === "delete") {
// console.log(' entrou no delete user: ', data) // console.log(' entrou no delete user: ', data)
dispatch({ type: "DELETE_USER", payload: +data.userId }); dispatch({ type: "DELETE_USER", payload: +data.userId });
@ -579,13 +481,9 @@ const Report = () => {
return () => { return () => {
socket.disconnect(); socket.disconnect();
}; };
} }
}, [reportOption, startDate, endDate]); }, [reportOption, startDate, endDate]);
// const handleDeleteRows = (id) => { // const handleDeleteRows = (id) => {
// let _data = [...dataRows]; // let _data = [...dataRows];
@ -597,20 +495,20 @@ const Report = () => {
// }; // };
useEffect(() => { useEffect(() => {
//if (!loading) { //if (!loading) {
// setData(query.map(({ scheduleReminder, ...others }) => ( // setData(query.map(({ scheduleReminder, ...others }) => (
// { ...others, 'scheduleReminder': `${others.statusChatEndId}` === '3' ? 'Agendamento' : 'Lembrete' } // { ...others, 'scheduleReminder': `${others.statusChatEndId}` === '3' ? 'Agendamento' : 'Lembrete' }
// ))) // )))
// } // }
setData(query.map((column) => { return { ...column } })) setData(
query.map((column) => {
}, [query]) return { ...column };
})
);
}, [query]);
const handleLogouOnlineUser = async (userId) => { const handleLogouOnlineUser = async (userId) => {
try { try {
@ -620,42 +518,51 @@ const Report = () => {
} catch (err) { } catch (err) {
// toastError(err); // toastError(err);
} }
}; };
return ( return (
<Can <Can
role={userA.profile} role={userA.profile}
perform="ticket-report:show" perform="ticket-report:show"
yes={() => ( yes={() => (
<MainContainer> <MainContainer>
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}> <Box sx={{ display: "grid", gridTemplateColumns: "repeat(3, 1fr)" }}>
<Item>
<SelectField
func={textFieldSelectUser}
emptyField={true}
header={"Usuário"}
currencies={users.map((obj) => {
return { value: obj.id, label: obj.name };
})}
/>
</Item>
<Item><SelectField func={textFieldSelectUser} emptyField={true} header={'Usuário'} currencies={users.map((obj) => { <Item>
return { 'value': obj.id, 'label': obj.name } <DatePicker1
})} /></Item> func={datePicker1Value}
minDate={false}
<Item><DatePicker1 func={datePicker1Value} minDate={false} startEmpty={false} title={'Data inicio'} /></Item> startEmpty={false}
<Item><DatePicker2 func={datePicker2Value} minDate={false} startEmpty={false} title={'Data fim'} /></Item> title={"Data inicio"}
/>
<Item sx={{ display: 'grid', gridColumn: '4 / 5', }}> </Item>
<Item>
<DatePicker2
func={datePicker2Value}
minDate={false}
startEmpty={false}
title={"Data fim"}
/>
</Item>
<Item sx={{ display: "grid", gridColumn: "4 / 5" }}>
<ReportModal currencies={reporList} func={reportValue} reportOption={reportOption} /> <ReportModal currencies={reporList} func={reportValue} reportOption={reportOption} />
<div style={{ margin: '2px' }}></div> <div style={{ margin: "2px" }}></div>
{reportOption === '1' &&
{reportOption === "1" && (
<div> <div>
<Button <Button variant="contained" color="primary" onClick={(e) => handleCSVMessages()}>
variant="contained"
color="primary"
onClick={(e) => handleCSVMessages()}
>
{"CSV ALL"} {"CSV ALL"}
</Button> </Button>
@ -663,125 +570,95 @@ const Report = () => {
<CSVLink <CSVLink
data={dataCSV} data={dataCSV}
headers={columns} headers={columns}
filename={'Relatorio_detalhado_atendimento_atendentes.csv'} filename={"Relatorio_detalhado_atendimento_atendentes.csv"}
target={'_blank'} target={"_blank"}
ref={csvLink} /> ref={csvLink}
/>
</div> </div>
</div> </div>
)}
}
</Item> </Item>
</Box> </Box>
<Box sx={{ <Box
display: 'grid', sx={{
}}> display: "grid",
}}
<Item sx={{ gridColumn: '1', gridRow: 'span 1' }}> >
<Item sx={{ gridColumn: "1", gridRow: "span 1" }}>
{reportOption === '1' && {reportOption === "1" && (
<MTable
<MTable data={query} data={query}
columns={columnsData} columns={columnsData}
hasChild={true} hasChild={true}
removeClickRow={false} removeClickRow={false}
table_title={'Atendimento por atendentes'} /> table_title={"Atendimento por atendentes"}
} />
{reportOption === '2' && )}
{reportOption === "2" && (
<MaterialTable <MaterialTable
localization={{ localization={{
header: { header: {
actions: 'Deslogar' actions: "Deslogar",
}, },
}} }}
title="Usuários online/offline" title="Usuários online/offline"
columns={ columns={[
[
// { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => <img src={rowData['ticket.contact.profilePicUrl']} alt="imagem de perfil do whatsapp" style={{ width: 40, borderRadius: '50%' }} /> }, // { title: 'Foto', field: 'ticket.contact.profilePicUrl', render: rowData => <img src={rowData['ticket.contact.profilePicUrl']} alt="imagem de perfil do whatsapp" style={{ width: 40, borderRadius: '50%' }} /> },
{ title: 'Nome', field: 'name', cellStyle: {whiteSpace: 'nowrap'}, }, { title: "Nome", field: "name", cellStyle: { whiteSpace: "nowrap" } },
{ {
title: 'Status', field: 'statusOnline.status', title: "Status",
field: "statusOnline.status",
cellStyle: (e, rowData) => { cellStyle: (e, rowData) => {
if (rowData["statusOnline"] && rowData["statusOnline"].status) {
if (rowData['statusOnline'] && rowData['statusOnline'].status) { if (rowData["statusOnline"].status === "offline") {
if (rowData['statusOnline'].status === 'offline') {
return { color: "red" }; return { color: "red" };
} } else if (rowData["statusOnline"].status === "online") {
else if (rowData['statusOnline'].status === 'online') {
return { color: "green" }; return { color: "green" };
} } else if (rowData["statusOnline"].status === "logout...") {
else if (rowData['statusOnline'].status === 'logout...') {
return { color: "orange" }; return { color: "orange" };
} } else if (rowData["statusOnline"].status === "waiting...") {
else if (rowData['statusOnline'].status === 'waiting...') {
return { color: "orange" }; return { color: "orange" };
} }
} }
},
}, },
}, { title: "Tempo online", field: "sumOnlineTime.sum" },
{ title: "Data inicio", field: "startDate" },
{ title: 'Tempo online', field: 'sumOnlineTime.sum' }, { title: "Data fim", field: "endDate" },
{ title: 'Data inicio', field: 'startDate' }, { title: "Em atendimento", field: "sumOpen.count" },
{ title: 'Data fim', field: 'endDate' }, { title: "Finalizado", field: "sumClosed.count" },
{ title: 'Em atendimento', field: 'sumOpen.count' }, ]}
{ title: 'Finalizado', field: 'sumClosed.count' },
]
}
data={dataRows} data={dataRows}
// icons={tableIcons} // icons={tableIcons}
actions={[ actions={[
(rowData) => { (rowData) => {
if (
if (rowData.statusOnline && rowData.statusOnline &&
rowData.statusOnline['status'] && rowData.statusOnline["status"] &&
rowData.statusOnline['status'] === 'online') { rowData.statusOnline["status"] === "online"
) {
return { return {
icon: LogoutIcon, icon: LogoutIcon,
tooltip: 'deslogar', tooltip: "deslogar",
disable: false, disable: false,
onClick: (event, rowData) => { onClick: (event, rowData) => {
// console.log(' ROW DATA INFO: ', rowData, ' | rowData: ', rowData.id) // console.log(' ROW DATA INFO: ', rowData, ' | rowData: ', rowData.id)
handleLogouOnlineUser(rowData.id) handleLogouOnlineUser(rowData.id);
} },
} };
}
} }
},
]} ]}
options={{
options={
{
search: true, search: true,
selection: false, selection: false,
paging: false, paging: false,
padding: 'dense', padding: "dense",
sorting: true, sorting: true,
//loadingType: 'linear', //loadingType: 'linear',
searchFieldStyle: { searchFieldStyle: {
@ -791,14 +668,13 @@ const Report = () => {
pageSize: 20, pageSize: 20,
headerStyle: { headerStyle: {
position: "sticky", position: "sticky",
top: "0" top: "0",
}, },
maxBodyHeight: "400px", maxBodyHeight: "400px",
rowStyle: { rowStyle: {
fontSize: 14, fontSize: 14,
} },
// cellStyle: (rowData) => { // cellStyle: (rowData) => {
// return { // return {
@ -807,26 +683,16 @@ const Report = () => {
// }; // };
// } // }
}} }}
/> />
)}
}
</Item> </Item>
</Box> </Box>
</MainContainer> </MainContainer>
)} )}
/> />
) );
}; };
export default Report; export default Report;

View File

@ -47,7 +47,7 @@ export const color = {
status: { status: {
no: "#FF0000", no: "#FF0000",
yes: "#00BE1E", yes: "#00BE1E",
warning: "#FFC700", warning: "#ffc800c7",
}, },
gradient: { gradient: {
bgOpacity: "#212f3cd7", bgOpacity: "#212f3cd7",