commit
8cb36fc636
|
@ -0,0 +1,15 @@
|
|||
{
|
||||
// Use IntelliSense to learn about possible attributes.
|
||||
// Hover to view descriptions of existing attributes.
|
||||
// For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"type": "pwa-chrome",
|
||||
"request": "launch",
|
||||
"name": "Launch Chrome against localhost",
|
||||
"url": "http://localhost:3000",
|
||||
"webRoot": "${workspaceFolder}"
|
||||
}
|
||||
]
|
||||
}
|
|
@ -0,0 +1,3 @@
|
|||
<svg width="32" height="32" viewBox="0 0 32 32" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path d="M20.6667 18.6667H19.6133L19.24 18.3067C20.5467 16.7867 21.3333 14.8133 21.3333 12.6667C21.3333 7.88 17.4533 4 12.6667 4C7.88 4 4 7.88 4 12.6667C4 17.4533 7.88 21.3333 12.6667 21.3333C14.8133 21.3333 16.7867 20.5467 18.3067 19.24L18.6667 19.6133V20.6667L25.3333 27.32L27.32 25.3333L20.6667 18.6667ZM12.6667 18.6667C9.34667 18.6667 6.66667 15.9867 6.66667 12.6667C6.66667 9.34667 9.34667 6.66667 12.6667 6.66667C15.9867 6.66667 18.6667 9.34667 18.6667 12.6667C18.6667 15.9867 15.9867 18.6667 12.6667 18.6667Z" fill="current" />
|
||||
</svg>
|
After Width: | Height: | Size: 641 B |
Binary file not shown.
After Width: | Height: | Size: 5.6 KiB |
|
@ -1,12 +0,0 @@
|
|||
import React from "react";
|
||||
import BtnBaseStyled from "./Btn.styled";
|
||||
|
||||
const BtnComponent = ({ text, bgcolor, fontcolor,...props }) => {
|
||||
return (
|
||||
<BtnBaseStyled bgcolor={bgcolor} fontcolor={fontcolor} {...props}>
|
||||
{text}
|
||||
</BtnBaseStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default BtnComponent;
|
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import { BadgeComponentStyled } from "./BadgeComponent.style";
|
||||
|
||||
const BadgeComponent = ({ counter,fontSize, position, top, left, right, bottom,bgcolor }) => {
|
||||
return (
|
||||
<BadgeComponentStyled position={position} top={top} left={left} right={right} bottom={bottom} fontSize={fontSize} bgcolor={bgcolor}>
|
||||
{counter}
|
||||
</BadgeComponentStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default BadgeComponent;
|
||||
|
|
@ -0,0 +1,22 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../style/varibles";
|
||||
|
||||
export const BadgeComponentStyled = styled.span`
|
||||
position: ${({ position }) => (position ? position : "relative")};
|
||||
top: ${({ top }) => (top ? top : "initial")};
|
||||
left: ${({ left }) => (left ? left : "initial")};
|
||||
right: ${({ right }) => (right ? right : "initial")};
|
||||
bottom: ${({ bottom }) => (bottom ? bottom : "initial")};
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
border-radius: 50px;
|
||||
object-fit: cover;
|
||||
width: 21px;
|
||||
height: 21px;
|
||||
font-size: ${({ fontSize }) => (fontSize ? fontSize : "16px")};
|
||||
color: ${color.pricinpal.blanco};
|
||||
background-color: ${({ bgcolor }) => (bgcolor ? bgcolor : color.status.yes)};
|
||||
`;
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
import React from "react";
|
||||
import BtnBaseStyled from "./Btn.styled";
|
||||
|
||||
const BtnComponent = ({ text, bgcolor, fontSize, fontcolor, ...props }) => {
|
||||
return (
|
||||
<BtnBaseStyled bgcolor={bgcolor} fontcolor={fontcolor} fontSize={fontSize} {...props}>
|
||||
{text}
|
||||
</BtnBaseStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default BtnComponent;
|
||||
|
|
@ -8,7 +8,7 @@ const BtnBaseStyled = styled.button`
|
|||
padding: 6px 16px 3px;
|
||||
border-radius: 5px;
|
||||
margin: 12px 0;
|
||||
font-size: 18px;
|
||||
font-size: ${({ fontSize }) => fontSize ? fontSize: "18px"};
|
||||
font-family: "Helvetica55";
|
||||
vertical-align: baseline;
|
||||
transition: all 0.2s linear;
|
|
@ -1,7 +1,6 @@
|
|||
import React from "react";
|
||||
import PageTitle from "../../PageTitle/PageTitle";
|
||||
|
||||
|
||||
import MainContainerStyled, {
|
||||
TitleContainerStyled,
|
||||
ContentContainerStyled,
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../style/varibles";
|
||||
|
||||
const MainContainerStyled = styled.main`
|
||||
padding-left: 97px;
|
||||
|
@ -21,6 +22,8 @@ const ContentContainerStyled = styled.div`
|
|||
height: 100vh;
|
||||
overflow: hidden;
|
||||
margin-top: 16px;
|
||||
border-radius: 5px;
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
`;
|
||||
|
||||
export default MainContainerStyled;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
import React from "react";
|
||||
import BtnComponent from "../Base/BTN/Btn";
|
||||
import BtnComponent from "../Base/Btn/Btn";
|
||||
|
||||
import {
|
||||
ConfirmationModalStyled,
|
||||
|
|
|
@ -1,9 +0,0 @@
|
|||
import React from "react";
|
||||
|
||||
import LoadingStyled from "./Loading.style"
|
||||
|
||||
const Loading = () => {
|
||||
return <LoadingStyled/>;
|
||||
};
|
||||
|
||||
export default Loading;
|
|
@ -0,0 +1,8 @@
|
|||
import React from "react";
|
||||
import LoadingStyled from "./LoadingScreen.style"
|
||||
|
||||
const LoadingScreen = () => {
|
||||
return <LoadingStyled/>;
|
||||
};
|
||||
|
||||
export default LoadingScreen;
|
|
@ -3,7 +3,7 @@ import { color } from "../../../../style/varibles";
|
|||
|
||||
const InputComponentStyled = styled.input`
|
||||
width: 100%;
|
||||
background: transparent;
|
||||
background: ${color.complement.azulOscuro};
|
||||
border: none;
|
||||
color: ${color.complement.azulCielo};
|
||||
`;
|
||||
|
|
|
@ -63,7 +63,7 @@ const MenuComponent = () => {
|
|||
<MenuItem icon={<Tickets />} text="Tickets" to="/tickets" hover={hover} />
|
||||
<MenuItem icon={<Contact />} text="Contatos" to="/contacts" hover={hover} />
|
||||
<MenuItem icon={<Reminder />} text="Lembretes" to="/schedulesReminder" hover={hover} />
|
||||
<MenuItem icon={<FastAanswer />} text="Respostas" to="" hover={hover} />
|
||||
<MenuItem icon={<FastAanswer />} text="Respostas" to="/quickAnswers" hover={hover} />
|
||||
<Divider />
|
||||
<MenuItem icon={<Users />} text="Usuários" to="/users" hover={hover} />
|
||||
<MenuItem icon={<Rows />} text="Filas" to="/Queues" hover={hover} />
|
||||
|
|
|
@ -45,6 +45,7 @@ const useStyles = makeStyles((theme) => ({
|
|||
flexGrow: 1,
|
||||
padding: "20px 20px 20px 20px",
|
||||
overflowY: "scroll",
|
||||
height: "50vh",
|
||||
[theme.breakpoints.down("sm")]: {
|
||||
paddingBottom: "90px",
|
||||
},
|
||||
|
@ -541,11 +542,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>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
import React from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
import { PageTitleStyled } from "./PageTitle.style";
|
||||
|
||||
import UserBtn from "./UserBtn/UserBtn";
|
||||
|
@ -7,15 +8,54 @@ import { AuthContext } from "../../context/Auth/AuthContext";
|
|||
import logo from "../../assets/images/Logo.png";
|
||||
|
||||
const PageTitle = () => {
|
||||
const path = useLocation();
|
||||
const [title, setTitle] = React.useState();
|
||||
const [modal, setModal] = React.useState(false);
|
||||
const { user } = React.useContext(AuthContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
switch (path.pathname) {
|
||||
case "/tickets":
|
||||
setTitle("Tickets");
|
||||
break;
|
||||
case "/contacts":
|
||||
setTitle("Contatos");
|
||||
break;
|
||||
case "/schedulesReminder":
|
||||
setTitle("Lembretes");
|
||||
break;
|
||||
case "/quickAnswers":
|
||||
setTitle("Respostas Rápidas");
|
||||
break;
|
||||
case "/users":
|
||||
setTitle("Usuários");
|
||||
break;
|
||||
case "/Queues":
|
||||
setTitle("Filas");
|
||||
break;
|
||||
case "/connections":
|
||||
setTitle("Conexões");
|
||||
break;
|
||||
case "/report":
|
||||
setTitle("Relatórios");
|
||||
break;
|
||||
case "/super":
|
||||
setTitle("Supervisão");
|
||||
break;
|
||||
case "/Settings":
|
||||
setTitle("Configurações");
|
||||
break;
|
||||
default:
|
||||
setTitle("Dashboard");
|
||||
}
|
||||
}, [path]);
|
||||
|
||||
const handleModal = () => {
|
||||
setModal(!modal);
|
||||
};
|
||||
return (
|
||||
<PageTitleStyled>
|
||||
<h1>PageTitle</h1>
|
||||
<h1>{title}</h1>
|
||||
<UserBtn user={user} img={logo} modal={modal} modalSet={handleModal} />
|
||||
</PageTitleStyled>
|
||||
);
|
||||
|
|
|
@ -34,7 +34,7 @@ const UserBtn = ({ user, img, modal, modalSet }) => {
|
|||
<UserItem title="Sair" icon={<Signoff />} onClick={handleModal} />
|
||||
</UserModalListStyled>
|
||||
</UserModalStyled>
|
||||
<UserModal modal={modalUser} click={handleModalUser}/>
|
||||
<UserModal modal={modalUser} click={handleModalUser} userId={user.id}/>
|
||||
<ConfirmationModal title="Sair?" modal={modalConfirm} click={{ handleModal, handleLogout }}>
|
||||
Deseja Sair do sistema?
|
||||
</ConfirmationModal>
|
||||
|
|
|
@ -0,0 +1,117 @@
|
|||
import React from "react";
|
||||
import { useParams, useHistory } from "react-router-dom";
|
||||
|
||||
import { toast } from "react-toastify";
|
||||
import openSocket from "socket.io-client";
|
||||
|
||||
import ContactDrawer from "../ContactDrawer";
|
||||
import MessageInput from "../MessageInput";
|
||||
import TicketHeader from "../TicketHeader";
|
||||
import TicketInfo from "../TicketInfo";
|
||||
import TicketActionButtons from "../TicketActionButtons";
|
||||
import MessagesList from "../MessagesList";
|
||||
import api from "../../services/api";
|
||||
import { ReplyMessageProvider } from "../../context/ReplyingMessage/ReplyingMessageContext";
|
||||
import toastError from "../../errors/toastError";
|
||||
|
||||
const Ticket = () => {
|
||||
const { ticketId } = useParams();
|
||||
const history = useHistory();
|
||||
|
||||
const [drawerOpen, setDrawerOpen] = React.useState(false);
|
||||
const [loading, setLoading] = React.useState(true);
|
||||
const [contact, setContact] = React.useState({});
|
||||
const [ticket, setTicket] = React.useState({});
|
||||
|
||||
const [statusChatEnd, setStatusChatEnd] = React.useState({});
|
||||
|
||||
React.useEffect(() => {
|
||||
setLoading(true);
|
||||
const delayDebounceFn = setTimeout(() => {
|
||||
const fetchTicket = async () => {
|
||||
try {
|
||||
const { data } = await api.get("/tickets/" + ticketId);
|
||||
|
||||
setContact(data.contact.contact);
|
||||
setTicket(data.contact);
|
||||
|
||||
setStatusChatEnd(data.statusChatEnd);
|
||||
|
||||
setLoading(false);
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
toastError(err);
|
||||
}
|
||||
};
|
||||
fetchTicket();
|
||||
}, 500);
|
||||
return () => clearTimeout(delayDebounceFn);
|
||||
}, [ticketId, history]);
|
||||
|
||||
React.useEffect(() => {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
|
||||
socket.on("connect", () => socket.emit("joinChatBox", ticketId));
|
||||
|
||||
socket.on("ticket", (data) => {
|
||||
if (data.action === "update") {
|
||||
setTicket(data.ticket);
|
||||
}
|
||||
|
||||
if (data.action === "delete") {
|
||||
toast.success("Ticket deleted sucessfully.");
|
||||
history.push("/tickets");
|
||||
}
|
||||
});
|
||||
|
||||
socket.on("contact", (data) => {
|
||||
if (data.action === "update") {
|
||||
setContact((prevState) => {
|
||||
if (prevState.id === data.contact?.id) {
|
||||
return { ...prevState, ...data.contact };
|
||||
}
|
||||
return prevState;
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
}, [ticketId, history]);
|
||||
|
||||
const handleDrawerOpen = () => {
|
||||
setDrawerOpen(true);
|
||||
};
|
||||
|
||||
const handleDrawerClose = () => {
|
||||
setDrawerOpen(false);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<div className="test" >
|
||||
<TicketHeader loading={loading}>
|
||||
<div>
|
||||
<TicketInfo contact={contact} ticket={ticket} onClick={handleDrawerOpen} />
|
||||
</div>
|
||||
<div>
|
||||
<TicketActionButtons ticket={ticket} statusChatEnd={statusChatEnd} />
|
||||
</div>
|
||||
</TicketHeader>
|
||||
<ReplyMessageProvider>
|
||||
<MessagesList ticketId={ticketId} isGroup={ticket.isGroup}></MessagesList>
|
||||
<MessageInput ticketStatus={ticket.status} />
|
||||
</ReplyMessageProvider>
|
||||
|
||||
<ContactDrawer
|
||||
open={drawerOpen}
|
||||
handleDrawerClose={handleDrawerClose}
|
||||
contact={contact}
|
||||
loading={loading}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
export default Ticket;
|
|
@ -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};
|
||||
`;
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react'
|
||||
import TicketSearchInput from './TicketSearchInput/TicketSearchInput'
|
||||
|
||||
const TicketSearch = ({spinning,setNewTicketModalOpen, handleSearch}) => {
|
||||
return (
|
||||
<TicketSearchInput spinning={spinning} setNewTicketModalOpen={setNewTicketModalOpen} handleSearch={handleSearch}/>
|
||||
)
|
||||
}
|
||||
|
||||
export default TicketSearch
|
|
@ -0,0 +1 @@
|
|||
|
|
@ -0,0 +1,10 @@
|
|||
import React from 'react';
|
||||
import {TicketSearchBtnStyled} from "./TicketSearchBtn.styled"
|
||||
|
||||
const TicketSearchBtn = ({setNewTicketModalOpen}) => {
|
||||
return (
|
||||
<TicketSearchBtnStyled onClick={()=>setNewTicketModalOpen(true)}>+</TicketSearchBtnStyled>
|
||||
)
|
||||
}
|
||||
|
||||
export default TicketSearchBtn
|
|
@ -0,0 +1,12 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../style/varibles";
|
||||
|
||||
export const TicketSearchBtnStyled = styled.button`
|
||||
cursor: pointer;
|
||||
background-color: transparent;
|
||||
color: ${color.pricinpal.blanco};
|
||||
font-size: 26px;
|
||||
border: none;
|
||||
font-family: "Helvetica55";
|
||||
`;
|
||||
|
|
@ -0,0 +1,25 @@
|
|||
import React from "react";
|
||||
|
||||
import {
|
||||
TicketSearchDivStyled,
|
||||
TicketSearchInputBoxStyled,
|
||||
TicketSearchInputStyled,
|
||||
} from "./TicketSearchInput.styled";
|
||||
|
||||
import { ReactComponent as SearchIcon } from "../../../../assets/icons/SearchInput/search.svg";
|
||||
import TicketSearchBtn from "../TicketSearchBtn/TicketSearchBtn";
|
||||
|
||||
const TicketSearchInput = ({ spinning, setNewTicketModalOpen, handleSearch }) => {
|
||||
return (
|
||||
<TicketSearchDivStyled>
|
||||
<TicketSearchInputBoxStyled spinning={spinning}>
|
||||
<SearchIcon />
|
||||
<TicketSearchInputStyled onChange={handleSearch} />
|
||||
</TicketSearchInputBoxStyled>
|
||||
<TicketSearchBtn setNewTicketModalOpen={setNewTicketModalOpen} />
|
||||
</TicketSearchDivStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketSearchInput;
|
||||
|
|
@ -0,0 +1,58 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../style/varibles";
|
||||
|
||||
export const TicketSearchDivStyled = styled.div`
|
||||
padding: 0 6px;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
`;
|
||||
export const TicketSearchInputBoxStyled = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
margin-right: 12px;
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
border: 2px solid ${color.pricinpal.blanco};
|
||||
color: ${color.pricinpal.blanco};
|
||||
border-radius: 4px;
|
||||
padding: 6px;
|
||||
& svg {
|
||||
fill: ${color.gradient.border};
|
||||
}
|
||||
&:after {
|
||||
position: absolute;
|
||||
right: 6px;
|
||||
content: "";
|
||||
display: ${({ spinning }) => (!spinning ? "none" : "block")};
|
||||
width: 16px;
|
||||
height: 16px;
|
||||
border-top: 2px solid ${color.gradient.border};
|
||||
border-left: 2px solid ${color.gradient.border};
|
||||
border-bottom: 2px solid ${color.gradient.border};
|
||||
border-right: 2px solid ${color.pricinpal.naranja};
|
||||
border-radius: 50%;
|
||||
animation: spining 0.5s infinite linear;
|
||||
}
|
||||
@keyframes spining {
|
||||
from {
|
||||
transform: rotate(0deg);
|
||||
}
|
||||
to {
|
||||
transform: rotate(360deg);
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TicketSearchInputStyled = styled.input`
|
||||
width: 100%;
|
||||
border: none;
|
||||
color: ${color.pricinpal.blanco};
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
&:focus,
|
||||
&:focus-visible {
|
||||
border: none;
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
outline: none;
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,108 @@
|
|||
import React from "react";
|
||||
import { color } from "../../../../../style/varibles";
|
||||
import { useHistory } from "react-router-dom";
|
||||
import { parseISO, format, isSameDay } from "date-fns";
|
||||
|
||||
import { i18n } from "../../../../../translate/i18n";
|
||||
|
||||
import api from "../../../../../services/api";
|
||||
import { AuthContext } from "../../../../../context/Auth/AuthContext";
|
||||
import toastError from "../../../../../errors/toastError";
|
||||
import Btn from "../../../../Base/Btn/Btn";
|
||||
import {
|
||||
TicketDateStyled,
|
||||
TicketImgStyled,
|
||||
TicketListItemStyled,
|
||||
TicketTitleStyled,
|
||||
} from "./TicketListItem.style";
|
||||
import LoadingScreen from "../../../../LoadingScreen/LoadingScreen";
|
||||
import BadgeComponent from "../../../../Base/Badge/BadgeComponent";
|
||||
import DefaultUser from "../../../../../assets/images/User/clientDefault.png";
|
||||
|
||||
const TicketListItem = ({ tickets }) => {
|
||||
const history = useHistory();
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
const isMounted = React.useRef(true);
|
||||
const { user } = React.useContext(AuthContext);
|
||||
React.useEffect(() => {
|
||||
return () => {
|
||||
isMounted.current = false;
|
||||
};
|
||||
}, []);
|
||||
|
||||
const handleAcepptTicket = async (id) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
await api.put(`/tickets/${id}`, {
|
||||
status: "open",
|
||||
userId: user?.id,
|
||||
});
|
||||
console.log("Passou no try", tickets.status, user?.id, id);
|
||||
} catch (err) {
|
||||
setLoading(false);
|
||||
toastError(err);
|
||||
}
|
||||
|
||||
if (isMounted.current) {
|
||||
setLoading(false);
|
||||
}
|
||||
|
||||
history.push(`/tickets/${id}`);
|
||||
};
|
||||
|
||||
const handleSelectTicket = (id) => {
|
||||
history.push(`/tickets/${id}`);
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
<React.Fragment key={tickets.id}>
|
||||
<TicketListItemStyled
|
||||
queuecolor={tickets.queue}
|
||||
onClick={() => handleSelectTicket(tickets.id)}
|
||||
>
|
||||
{tickets.contact.profilePicUrl ? (
|
||||
<TicketImgStyled src={tickets.contact.profilePicUrl} alt={tickets.id} />
|
||||
) : (
|
||||
<TicketImgStyled src={DefaultUser} alt={tickets.id} />
|
||||
)}
|
||||
|
||||
<TicketTitleStyled>
|
||||
<p>{tickets.contact.name}</p>
|
||||
<p>{tickets.lastMessage}</p>
|
||||
{tickets.unreadMessages ? (
|
||||
<BadgeComponent
|
||||
counter={tickets.unreadMessages}
|
||||
position="absolute"
|
||||
right="6px"
|
||||
top="6px"
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TicketTitleStyled>
|
||||
<TicketDateStyled>
|
||||
{isSameDay(parseISO(tickets.updatedAt), new Date()) ? (
|
||||
<>{format(parseISO(tickets.updatedAt), "HH:mm")}</>
|
||||
) : (
|
||||
<>{format(parseISO(tickets.updatedAt), "dd/MM/yyyy")}</>
|
||||
)}
|
||||
</TicketDateStyled>
|
||||
{tickets.status === "pending" ? (
|
||||
<Btn
|
||||
onClick={() => handleAcepptTicket(tickets.id)}
|
||||
text="Aceitar"
|
||||
bgcolor={color.complement.azulCielo}
|
||||
fontcolor={color.pricinpal.blanco}
|
||||
fontSize="12px"
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TicketListItemStyled>
|
||||
</React.Fragment>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketListItem;
|
||||
|
|
@ -0,0 +1,72 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../../style/varibles";
|
||||
|
||||
export const TicketListItemStyled = styled.li`
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 6px;
|
||||
border-bottom: 1.5px solid ${color.gradient.border};
|
||||
height: fit-content;
|
||||
transition: filter .2s linear;
|
||||
&:before {
|
||||
position: absolute;
|
||||
left: 0;
|
||||
content: "";
|
||||
display: block;
|
||||
background-color: ${({ queuecolor }) => (queuecolor ? queuecolor : color.gradient.border)};
|
||||
width: 4px;
|
||||
height: 55px;
|
||||
}
|
||||
&:nth-child(1) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
&:hover{
|
||||
filter: brightness(1.2);
|
||||
transition: filter .2s linear;
|
||||
}
|
||||
`;
|
||||
|
||||
export const TicketTitleStyled = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
row-gap: 6px;
|
||||
& p {
|
||||
color: ${color.pricinpal.blanco};
|
||||
width: 190px;
|
||||
font-size: 12px;
|
||||
font-family: "Helvetica55";
|
||||
&:nth-child(1) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
color: ${color.gradient.text};
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TicketDateStyled = styled.div`
|
||||
color: ${color.pricinpal.blanco};
|
||||
font-size: 12px;
|
||||
font-family: "Helvetica55";
|
||||
display: block;
|
||||
color: white;
|
||||
margin-right: 1rem;
|
||||
`;
|
||||
|
||||
export const TicketImgStyled = styled.img`
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
margin: 0 6px;
|
||||
`;
|
||||
|
|
@ -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";
|
||||
|
||||
|
@ -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,212 @@
|
|||
import React from "react";
|
||||
|
||||
import openSocket from "socket.io-client";
|
||||
|
||||
import LoadingScreen from "../../../LoadingScreen/LoadingScreen";
|
||||
|
||||
import TicketListStyled from "./TicketsList.style";
|
||||
import useTickets from "../../../../hooks/useTickets";
|
||||
import TicketListItem from "../TicketsList/TicketListItem/TicketListItem";
|
||||
import TicketsListSkeleton from "../TicketsListSkeleton/TicketListSkeleton";
|
||||
|
||||
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, valueTab }) => {
|
||||
const [pageNumber, setPageNumber] = React.useState(1);
|
||||
const [ticketsList, dispatch] = React.useReducer(reducer, []);
|
||||
const { user } = React.useContext(AuthContext);
|
||||
React.useEffect(() => {
|
||||
dispatch({ type: "RESET" });
|
||||
setPageNumber(1);
|
||||
}, [status, searchParam, dispatch, showAll, selectedQueueIds]);
|
||||
const { tickets, loading } = useTickets({
|
||||
pageNumber,
|
||||
searchParam,
|
||||
status,
|
||||
showAll,
|
||||
queueIds: JSON.stringify(selectedQueueIds),
|
||||
});
|
||||
|
||||
React.useEffect(() => {
|
||||
if (typeof updateCount === "function") {
|
||||
updateCount(ticketsList.length);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, [ticketsList]);
|
||||
|
||||
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)) {
|
||||
// console.log('((((((((((((((((((( DATA.MESSAGE: ', data.message)
|
||||
|
||||
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]);
|
||||
|
||||
if (loading) return <TicketsListSkeleton />;
|
||||
|
||||
if (status === valueTab)
|
||||
return (
|
||||
<TicketListStyled>
|
||||
{ticketsList.map((ticket) => (
|
||||
<TicketListItem tickets={ticket} key={ticket.id} />
|
||||
))}
|
||||
</TicketListStyled>
|
||||
);
|
||||
return null;
|
||||
};
|
||||
|
||||
export default TicketsList;
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../style/varibles";
|
||||
|
||||
const TicketListStyled = styled.ul`
|
||||
background-color: ${color.gradient.border};
|
||||
height: 100vh;
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
export default TicketListStyled;
|
|
@ -5,12 +5,12 @@ import { makeStyles } from "@material-ui/core/styles";
|
|||
import List from "@material-ui/core/List";
|
||||
import Paper from "@material-ui/core/Paper";
|
||||
|
||||
import TicketListItem from "../TicketListItem";
|
||||
import TicketsListSkeleton from "../TicketsListSkeleton";
|
||||
import TicketListItem from "../../../TicketListItem";
|
||||
import TicketsListSkeleton from "../../../TicketsListSkeleton";
|
||||
|
||||
import useTickets from "../../hooks/useTickets";
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
import useTickets from "../../../../hooks/useTickets";
|
||||
import { i18n } from "../../../../translate/i18n";
|
||||
import { AuthContext } from "../../../../context/Auth/AuthContext";
|
||||
|
||||
const useStyles = makeStyles(theme => ({
|
||||
ticketsListWrapper: {
|
||||
|
@ -74,8 +74,6 @@ const useStyles = makeStyles(theme => ({
|
|||
const reducer = (state, action) => {
|
||||
if (action.type === "LOAD_TICKETS") {
|
||||
const newTickets = action.payload;
|
||||
|
||||
|
||||
newTickets.forEach(ticket => {
|
||||
|
||||
// console.log('* ticket.unreadMessages: ',ticket.unreadMessages)
|
|
@ -0,0 +1,42 @@
|
|||
import React from "react";
|
||||
import {
|
||||
TicketListSkeletonStyled,
|
||||
TicketSkeletonItemStyled,
|
||||
TicketSkeletonTitleStyled,
|
||||
TicketSkeletonDateStyled,
|
||||
TicketSkeletonImgStyled,
|
||||
} from "./TicketListSkeleton.style";
|
||||
|
||||
const TicketsSkeleton = () => {
|
||||
return (
|
||||
<TicketListSkeletonStyled>
|
||||
<TicketSkeletonItemStyled>
|
||||
<TicketSkeletonImgStyled/>
|
||||
<TicketSkeletonTitleStyled>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</TicketSkeletonTitleStyled>
|
||||
<TicketSkeletonDateStyled/>
|
||||
</TicketSkeletonItemStyled>
|
||||
<TicketSkeletonItemStyled>
|
||||
<TicketSkeletonImgStyled/>
|
||||
<TicketSkeletonTitleStyled>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</TicketSkeletonTitleStyled>
|
||||
<TicketSkeletonDateStyled/>
|
||||
</TicketSkeletonItemStyled>
|
||||
<TicketSkeletonItemStyled>
|
||||
<TicketSkeletonImgStyled/>
|
||||
<TicketSkeletonTitleStyled>
|
||||
<div></div>
|
||||
<div></div>
|
||||
</TicketSkeletonTitleStyled>
|
||||
<TicketSkeletonDateStyled/>
|
||||
</TicketSkeletonItemStyled>
|
||||
</TicketListSkeletonStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketsSkeleton;
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../style/varibles";
|
||||
|
||||
export const TicketListSkeletonStyled = styled.ul`
|
||||
background-color: ${color.gradient.border};
|
||||
height: 100vh;
|
||||
margin-top: 16px;
|
||||
`;
|
||||
|
||||
export const TicketSkeletonItemStyled = styled.li`
|
||||
cursor: pointer;
|
||||
position: relative;
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
display: flex;
|
||||
align-items: center;
|
||||
padding: 0.5rem 6px;
|
||||
border-bottom: 1.5px solid ${color.gradient.border};
|
||||
height: fit-content;
|
||||
transition: filter 0.2s linear;
|
||||
&:nth-child(1) {
|
||||
margin-top: 6px;
|
||||
}
|
||||
&:hover {
|
||||
filter: brightness(1.2);
|
||||
transition: filter 0.2s linear;
|
||||
}
|
||||
`;
|
||||
|
||||
export const TicketSkeletonTitleStyled = styled.div`
|
||||
position: relative;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
flex-grow: 1;
|
||||
row-gap: 6px;
|
||||
opacity: 0.1;
|
||||
& div {
|
||||
color: ${color.pricinpal.blanco};
|
||||
width: 190px;
|
||||
font-size: 12px;
|
||||
&:nth-child(1) {
|
||||
width: 150px;
|
||||
height: 18px;
|
||||
background: linear-gradient(to right, ${color.gradient.border}, ${color.pricinpal.blanco});
|
||||
animation: wave 0.8s infinite;
|
||||
}
|
||||
&:nth-child(2) {
|
||||
width: 150px;
|
||||
height: 18px;
|
||||
background: linear-gradient(to right, ${color.gradient.border}, ${color.pricinpal.blanco});
|
||||
animation: wave 0.8s infinite;
|
||||
}
|
||||
}
|
||||
@keyframes wave {
|
||||
to {
|
||||
background-position: 150px;
|
||||
}
|
||||
}
|
||||
`;
|
||||
export const TicketSkeletonDateStyled = styled.div`
|
||||
background: linear-gradient(to right, ${color.gradient.border}, ${color.pricinpal.blanco});
|
||||
animation: wave 0.8s infinite;
|
||||
opacity: 0.1;
|
||||
display: block;
|
||||
width: 40px;
|
||||
height: 18px;
|
||||
font-size: 12px;
|
||||
margin-right: 1rem;
|
||||
`;
|
||||
|
||||
export const TicketSkeletonImgStyled = styled.div`
|
||||
background: linear-gradient(to right, ${color.gradient.border}, ${color.pricinpal.blanco});
|
||||
animation: wave 0.8s infinite;
|
||||
opacity: 0.1;
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
object-fit: contain;
|
||||
border-radius: 50%;
|
||||
margin: 0 6px;
|
||||
`;
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
import React from "react";
|
||||
|
||||
import TicketsManagerStyled from "./TicketsManager.style";
|
||||
import TicketsTabs from "./TicketsTabs/TicketsTabs";
|
||||
|
||||
import TicketSearch from "../TicketSearch/TicketSearch";
|
||||
import TicketsList from "./TicketsList/TicketsList";
|
||||
import NewTicketModal from "../../NewTicketModal";
|
||||
|
||||
import { AuthContext } from "../../../context/Auth/AuthContext";
|
||||
|
||||
const TicketsManager = () => {
|
||||
const [valueTab, setValueTab] = React.useState("open");
|
||||
const [searchParam, setSearchParam] = React.useState("");
|
||||
const [spinning, setSpinning] = 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 { user } = React.useContext(AuthContext);
|
||||
const userQueueIds = user.queues.map((q) => q.id);
|
||||
const [selectedQueueIds, setSelectedQueueIds] = React.useState(userQueueIds || []);
|
||||
|
||||
let searchTimeout;
|
||||
const handleSearch = (e) => {
|
||||
setSpinning(true);
|
||||
const searchedTerm = e.target.value.toLowerCase();
|
||||
|
||||
clearTimeout(searchTimeout);
|
||||
|
||||
searchTimeout = setTimeout(() => {
|
||||
setSearchParam(searchedTerm);
|
||||
setSpinning(false);
|
||||
}, 200);
|
||||
|
||||
if (searchedTerm === "") {
|
||||
setSearchParam(searchedTerm);
|
||||
return;
|
||||
}
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (user.profile.toUpperCase() === "ADMIN") {
|
||||
setShowAllTickets(true);
|
||||
}
|
||||
}, [user.profile]);
|
||||
|
||||
return (
|
||||
<TicketsManagerStyled>
|
||||
<TicketsTabs
|
||||
setValueTab={setValueTab}
|
||||
valueTab={valueTab}
|
||||
count={{ openCount, pendingCount, closedCount }}
|
||||
/>
|
||||
|
||||
<TicketSearch
|
||||
spinning={spinning}
|
||||
handleSearch={handleSearch}
|
||||
setNewTicketModalOpen={setNewTicketModalOpen}
|
||||
/>
|
||||
|
||||
<TicketsList
|
||||
status="open"
|
||||
updateCount={(v) => setOpenCount(v)}
|
||||
showAll={showAllTickets}
|
||||
selectedQueueIds={selectedQueueIds}
|
||||
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
|
||||
modalOpen={newTicketModalOpen}
|
||||
onClose={(e) => setNewTicketModalOpen(false)}
|
||||
/>
|
||||
|
||||
</TicketsManagerStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketsManager;
|
|
@ -0,0 +1,13 @@
|
|||
import styled from "styled-components";
|
||||
import {color} from "../../../style/varibles"
|
||||
|
||||
const TicketsManagerStyled = styled.div`
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
display:flex;
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
height: 100vh;
|
||||
max-width: 425px;
|
||||
border-right:1px solid ${color.gradient.border};
|
||||
`
|
||||
export default TicketsManagerStyled
|
|
@ -0,0 +1,43 @@
|
|||
import React from "react";
|
||||
import { TicketsTabStyled } from "./TicketsTab.style";
|
||||
import { color } from "../../../../../style/varibles";
|
||||
|
||||
import BadgeComponent from "../../../../Base/Badge/BadgeComponent";
|
||||
|
||||
const TicketsTab = ({ text, id, setValueTab, valueTab, count }) => {
|
||||
const [active, setActive] = React.useState(false);
|
||||
|
||||
const handleClick = ({ target }) => {
|
||||
setValueTab(target.id);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
valueTab === id ? setActive(true) : setActive(false);
|
||||
}, [valueTab, id]);
|
||||
|
||||
return (
|
||||
<TicketsTabStyled
|
||||
id={id}
|
||||
valueTab={valueTab}
|
||||
onClick={handleClick}
|
||||
className={active ? "active" : ""}
|
||||
>
|
||||
{text}
|
||||
{id !== "open" ? (
|
||||
<BadgeComponent
|
||||
counter={count}
|
||||
position="absolute"
|
||||
right="4px"
|
||||
top="6px"
|
||||
fontSize="12px"
|
||||
bgcolor={id === "pending" ? color.status.warning : color.status.no}
|
||||
/>
|
||||
) : (
|
||||
""
|
||||
)}
|
||||
</TicketsTabStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketsTab;
|
||||
|
|
@ -0,0 +1,37 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../../style/varibles";
|
||||
|
||||
export const TicketsTabStyled = styled.li`
|
||||
width: 100%;
|
||||
position: relative;
|
||||
cursor: pointer;
|
||||
background-color: ${color.complement.azulOscuro};
|
||||
color: ${color.pricinpal.blanco};
|
||||
font-size: 0.9rem;
|
||||
text-transform: uppercase;
|
||||
font-family: "Helvetica85";
|
||||
padding: 1rem 1.5rem;
|
||||
text-align: center;
|
||||
&:after {
|
||||
content: "";
|
||||
display: block;
|
||||
position: absolute;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 0;
|
||||
height: 2px;
|
||||
background-color: white;
|
||||
transition: width 0.2s linear;
|
||||
}
|
||||
&.active {
|
||||
&:after {
|
||||
width: 100%;
|
||||
transition: width 0.2s linear;
|
||||
}
|
||||
}
|
||||
&:nth-child(2) {
|
||||
border-left: 1px solid ${color.gradient.border};
|
||||
border-right: 1px solid ${color.gradient.border};
|
||||
}
|
||||
`;
|
||||
|
|
@ -0,0 +1,35 @@
|
|||
import React from "react";
|
||||
import TicketsTab from "../TicketsTabs/TicketsTab/TicketsTab";
|
||||
import { TicketTabsStyled } from "./TicketsTabs.style";
|
||||
|
||||
const TicketsTabs = ({ setValueTab, valueTab, count }) => {
|
||||
|
||||
return (
|
||||
<TicketTabsStyled>
|
||||
<TicketsTab
|
||||
text="Aberto"
|
||||
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>
|
||||
);
|
||||
};
|
||||
|
||||
export default TicketsTabs;
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
import styled from "styled-components";
|
||||
import { color } from "../../../../style/varibles";
|
||||
|
||||
export const TicketTabsStyled = styled.ul`
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
border-bottom: 1px solid ${color.gradient.border};
|
||||
margin-bottom: 1rem;
|
||||
margin-left: 0;
|
||||
margin-right: 0;
|
||||
`;
|
||||
|
|
@ -9,20 +9,19 @@ 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 NewTicketModal from "../NewTicketModal";
|
||||
import TicketsList from "../TicketsList";
|
||||
import TabPanel from "../TabPanel";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
import { Can } from "../Can";
|
||||
import TicketsQueueSelect from "../TicketsQueueSelect";
|
||||
import { Button } from "@material-ui/core";
|
||||
|
||||
import NewTicketModal from "../../NewTicketModal/index";
|
||||
import TicketsList from "../../TicketsList";
|
||||
import TabPanel from "../../TabPanel";
|
||||
|
||||
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",
|
||||
|
@ -112,7 +111,6 @@ const TicketsManager = () => {
|
|||
if (user.profile.toUpperCase() === "ADMIN") {
|
||||
setShowAllTickets(true);
|
||||
}
|
||||
// eslint-disable-next-line react-hooks/exhaustive-deps
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
|
@ -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);
|
||||
|
@ -91,13 +38,8 @@ const Ticket = () => {
|
|||
const fetchTicket = async () => {
|
||||
try {
|
||||
|
||||
// maria julia
|
||||
|
||||
const { data } = await api.get("/tickets/" + ticketId);
|
||||
|
||||
// setContact(data.contact);
|
||||
// setTicket(data);
|
||||
|
||||
setContact(data.contact.contact);
|
||||
setTicket(data.contact);
|
||||
|
||||
|
@ -155,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>
|
||||
|
|
|
@ -13,9 +13,9 @@ import toastError from "../../errors/toastError";
|
|||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
|
||||
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: {
|
||||
marginRight: 6,
|
||||
flex: "none",
|
||||
|
@ -35,63 +35,49 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
const ticketOptionsMenuOpen = Boolean(anchorEl);
|
||||
const { user } = useContext(AuthContext);
|
||||
|
||||
const handleOpenTicketOptionsMenu = e => {
|
||||
const handleOpenTicketOptionsMenu = (e) => {
|
||||
setAnchorEl(e.currentTarget);
|
||||
};
|
||||
|
||||
const handleCloseTicketOptionsMenu = e => {
|
||||
const handleCloseTicketOptionsMenu = (e) => {
|
||||
setAnchorEl(null);
|
||||
};
|
||||
|
||||
|
||||
const chatEndVal = (data) => {
|
||||
if (data) {
|
||||
data = { ...data, ticketId: ticket.id };
|
||||
|
||||
if(data){
|
||||
|
||||
data = {...data, 'ticketId': ticket.id}
|
||||
|
||||
console.log('ChatEnd: ',(data));
|
||||
|
||||
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'}
|
||||
render(
|
||||
<Modal
|
||||
modal_header={"Finalização de Atendimento"}
|
||||
func={chatEndVal}
|
||||
statusChatEnd={statusChatEnd}
|
||||
ticketId={ticket.id}
|
||||
/>)
|
||||
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
|
||||
|
||||
const handleUpdateTicketStatus = async (e, status, userId, schedulingData = {}) => {
|
||||
setLoading(true);
|
||||
try {
|
||||
|
||||
if(status==='closed'){
|
||||
|
||||
if (status === "closed") {
|
||||
await api.put(`/tickets/${ticket.id}`, {
|
||||
status: status,
|
||||
userId: userId || null,
|
||||
schedulingNotifyData: JSON.stringify(schedulingData)
|
||||
schedulingNotifyData: JSON.stringify(schedulingData),
|
||||
});
|
||||
|
||||
}
|
||||
else{
|
||||
|
||||
} else {
|
||||
await api.put(`/tickets/${ticket.id}`, {
|
||||
status: status,
|
||||
userId: userId || null
|
||||
userId: userId || null,
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
setLoading(false);
|
||||
|
@ -104,10 +90,6 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
setLoading(false);
|
||||
toastError(err);
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
return (
|
||||
|
@ -117,7 +99,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
loading={loading}
|
||||
startIcon={<Replay />}
|
||||
size="small"
|
||||
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.reopen")}
|
||||
</ButtonWithSpinner>
|
||||
|
@ -128,7 +110,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
loading={loading}
|
||||
startIcon={<Replay />}
|
||||
size="small"
|
||||
onClick={e => handleUpdateTicketStatus(e, "pending", null)}
|
||||
onClick={(e) => handleUpdateTicketStatus(e, "pending", null)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.return")}
|
||||
</ButtonWithSpinner>
|
||||
|
@ -138,12 +120,9 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={e => {
|
||||
|
||||
|
||||
handleModal()
|
||||
onClick={(e) => {
|
||||
handleModal();
|
||||
// handleUpdateTicketStatus(e, "closed", user?.id)
|
||||
|
||||
}}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.resolve")}
|
||||
|
@ -166,7 +145,7 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
size="small"
|
||||
variant="contained"
|
||||
color="primary"
|
||||
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
|
||||
>
|
||||
{i18n.t("messagesList.header.buttons.accept")}
|
||||
</ButtonWithSpinner>
|
||||
|
@ -176,3 +155,4 @@ const TicketActionButtons = ({ ticket, statusChatEnd }) => {
|
|||
};
|
||||
|
||||
export default TicketActionButtons;
|
||||
|
||||
|
|
|
@ -6,12 +6,8 @@ import Select from "@material-ui/core/Select";
|
|||
import { Checkbox, ListItemText } from "@material-ui/core";
|
||||
import { i18n } from "../../translate/i18n";
|
||||
|
||||
const TicketsQueueSelect = ({
|
||||
userQueues,
|
||||
selectedQueueIds = [],
|
||||
onChange,
|
||||
}) => {
|
||||
const handleChange = e => {
|
||||
const TicketsQueueSelect = ({ userQueues, selectedQueueIds = [], onChange }) => {
|
||||
const handleChange = (e) => {
|
||||
onChange(e.target.value);
|
||||
};
|
||||
|
||||
|
@ -38,7 +34,7 @@ const TicketsQueueSelect = ({
|
|||
renderValue={() => i18n.t("ticketsQueueSelect.placeholder")}
|
||||
>
|
||||
{userQueues?.length > 0 &&
|
||||
userQueues.map(queue => (
|
||||
userQueues.map((queue) => (
|
||||
<MenuItem dense key={queue.id} value={queue.id}>
|
||||
<Checkbox
|
||||
style={{
|
||||
|
@ -58,3 +54,4 @@ const TicketsQueueSelect = ({
|
|||
};
|
||||
|
||||
export default TicketsQueueSelect;
|
||||
|
||||
|
|
|
@ -6,30 +6,62 @@ import api from "../../services/api";
|
|||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
import { Can } from "../Can";
|
||||
|
||||
import BtnComponent from "../Base/BTN/Btn";
|
||||
import BtnComponent from "../Base/Btn/Btn";
|
||||
import FormComponent from "../Base/Form/FormComponent";
|
||||
import InputComponent from "../Base/Form/Input/InputComponent";
|
||||
import UserModalComponent from "./UserModalImg/UserModalComponent";
|
||||
|
||||
import UserImg from "../../assets/images/User/user.jpg";
|
||||
|
||||
const UserModal = ({ modal, click }) => {
|
||||
const UserModal = ({ modal, click, userId }) => {
|
||||
const { user } = React.useContext(AuthContext);
|
||||
|
||||
const initalData = {
|
||||
name: user.name,
|
||||
email: user.email,
|
||||
perfil:user.profile,
|
||||
}
|
||||
console.log(user);
|
||||
const InitalState = {
|
||||
name: "",
|
||||
email: "",
|
||||
password: "",
|
||||
profile: "",
|
||||
};
|
||||
const [userData, setUserData] = React.useState(InitalState);
|
||||
const [selectedQueueIds, setSelectedQueueIds] = React.useState([]);
|
||||
|
||||
React.useEffect(() => {
|
||||
// const fetchUser = async () => {
|
||||
// if (!userId) return;
|
||||
// try {
|
||||
// const { data } = await api.get(`/users/${userId}`);
|
||||
// setUserData((prevState) => {
|
||||
// return console.log({ ...prevState, ...data });
|
||||
// });
|
||||
// const userQueueIds = data.queues?.map((queue) => queue.id);
|
||||
// setSelectedQueueIds(userQueueIds);
|
||||
// } catch (err) {
|
||||
// alert(err);
|
||||
// }
|
||||
// };
|
||||
|
||||
// fetchUser();
|
||||
}, [userId]);
|
||||
|
||||
return (
|
||||
<ModalOverlayStyled modal={modal}>
|
||||
<UserModalStyled>
|
||||
<UserModalComponent img={UserImg} desc="Clique na imagem para alterar" />
|
||||
<FormComponent method="get">
|
||||
<InputComponent id="nome" label="Nome" type="text" value={initalData.name}/>
|
||||
<InputComponent id="email" label="E-mail" type="email" value={initalData.email}/>
|
||||
<InputComponent
|
||||
id="nome"
|
||||
label="Nome"
|
||||
type="text"
|
||||
value={userData.name}
|
||||
onChange={(event) => setUserData({ name: event.target.data })}
|
||||
/>
|
||||
<InputComponent
|
||||
id="email"
|
||||
label="E-mail"
|
||||
type="email"
|
||||
value={userData.email}
|
||||
onChange={(event) => setUserData({ email: event.target.data })}
|
||||
/>
|
||||
<InputComponent id="password" label="Senha" type="password" />
|
||||
<UserBtns>
|
||||
<BtnComponent text="Cancelar" onClick={click} />
|
||||
|
@ -54,3 +86,4 @@ name: "teste"
|
|||
profile: "master"
|
||||
queues: []
|
||||
tokenVersion: 0 */
|
||||
|
||||
|
|
|
@ -1,21 +1,16 @@
|
|||
import React from "react";
|
||||
|
||||
import Loading from "../components/LoadingScreen/Loading";
|
||||
import LoadingScreen from "../components/LoadingScreen/LoadingScreen";
|
||||
import MainContainer from "../components/Base/MainContainer/MainContainer";
|
||||
import { AuthContext } from "../context/Auth/AuthContext";
|
||||
import { i18n } from "../translate/i18n";
|
||||
import MenuComponent from "../components/Menu/MenuComponent";
|
||||
|
||||
const LoggedInLayout = ({ children }) => {
|
||||
const { handleLogout, loading, isAuth } = React.useContext(AuthContext);
|
||||
const logout = (e) => {
|
||||
handleLogout();
|
||||
};
|
||||
const { user } = React.useContext(AuthContext);
|
||||
console.log(user.name);
|
||||
const { loading, user } = React.useContext(AuthContext);
|
||||
|
||||
if (loading) {
|
||||
return <Loading />;
|
||||
return <LoadingScreen />;
|
||||
}
|
||||
|
||||
return (
|
||||
|
|
|
@ -3,101 +3,89 @@ import MainContainer from "../../components/MainContainer";
|
|||
import api from "../../services/api";
|
||||
import SelectField from "../../components/Report/SelectField";
|
||||
//import { data } from '../../components/Report/MTable/data';
|
||||
import DatePicker1 from '../../components/Report/DatePicker'
|
||||
import DatePicker2 from '../../components/Report/DatePicker'
|
||||
import DatePicker1 from "../../components/Report/DatePicker";
|
||||
import DatePicker2 from "../../components/Report/DatePicker";
|
||||
import MTable from "../../components/Report/MTable";
|
||||
import PropTypes from 'prop-types';
|
||||
import Box from '@mui/material/Box';
|
||||
import PropTypes from "prop-types";
|
||||
import Box from "@mui/material/Box";
|
||||
import { AuthContext } from "../../context/Auth/AuthContext";
|
||||
import { Can } from "../../components/Can";
|
||||
|
||||
import { Button } from "@material-ui/core";
|
||||
|
||||
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 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 = [
|
||||
{
|
||||
key: 'ticket.whatsapp.name',
|
||||
label: 'Loja',
|
||||
|
||||
key: "ticket.whatsapp.name",
|
||||
label: "Loja",
|
||||
},
|
||||
|
||||
{
|
||||
key: 'id',
|
||||
label: 'id Mensagem',
|
||||
key: "id",
|
||||
label: "id Mensagem",
|
||||
},
|
||||
|
||||
{
|
||||
key: 'ticket.id',
|
||||
label: 'id Conversa',
|
||||
key: "ticket.id",
|
||||
label: "id Conversa",
|
||||
},
|
||||
|
||||
{
|
||||
key: 'ticket.contact.name',
|
||||
label: 'Cliente',
|
||||
key: "ticket.contact.name",
|
||||
label: "Cliente",
|
||||
},
|
||||
|
||||
{
|
||||
key: 'ticket.user.name',
|
||||
label: 'Atendente',
|
||||
key: "ticket.user.name",
|
||||
label: "Atendente",
|
||||
},
|
||||
{
|
||||
key: 'body',
|
||||
label: 'Mensagem',
|
||||
key: "body",
|
||||
label: "Mensagem",
|
||||
},
|
||||
{
|
||||
key: 'fromMe',
|
||||
label: 'Sentido',
|
||||
key: "fromMe",
|
||||
label: "Sentido",
|
||||
},
|
||||
{
|
||||
key: 'createdAt',
|
||||
label: 'Criada',
|
||||
key: "createdAt",
|
||||
label: "Criada",
|
||||
},
|
||||
{
|
||||
key: 'ticket.contact.number',
|
||||
label: 'Telefone cliente',
|
||||
key: "ticket.contact.number",
|
||||
label: "Telefone cliente",
|
||||
},
|
||||
{
|
||||
key: 'ticket.queue.name',
|
||||
label: 'Fila',
|
||||
key: "ticket.queue.name",
|
||||
label: "Fila",
|
||||
},
|
||||
{
|
||||
key: 'ticket.status',
|
||||
label: 'Status',
|
||||
key: "ticket.status",
|
||||
label: "Status",
|
||||
},
|
||||
{
|
||||
key: 'ticket.statusChatEnd',
|
||||
label: 'Status de encerramento',
|
||||
}
|
||||
|
||||
]
|
||||
key: "ticket.statusChatEnd",
|
||||
label: "Status de encerramento",
|
||||
},
|
||||
];
|
||||
|
||||
//
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
const reducerQ = (state, action) => {
|
||||
|
||||
|
||||
|
||||
|
||||
if (action.type === "DELETE_USER_STATUS") {
|
||||
|
||||
const userId = action.payload;
|
||||
|
||||
//console.log('Entrou no delete user status userId: ', userId)
|
||||
|
@ -113,115 +101,90 @@ const reducerQ = (state, action) => {
|
|||
return [...state];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action.type === 'LOAD_QUERY') {
|
||||
|
||||
|
||||
const queries = action.payload
|
||||
const newQueries = []
|
||||
if (action.type === "LOAD_QUERY") {
|
||||
const queries = action.payload;
|
||||
const newQueries = [];
|
||||
|
||||
queries.forEach((query) => {
|
||||
|
||||
const queryIndex = state.findIndex((q) => q.id === query.id)
|
||||
const queryIndex = state.findIndex((q) => q.id === query.id);
|
||||
|
||||
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") {
|
||||
|
||||
let onlineUser = action.payload
|
||||
let index = -1
|
||||
let onlineUser = action.payload;
|
||||
let index = -1;
|
||||
|
||||
// console.log('sssssssssstate: ', state, ' | ONLINE USERS: onlineUser.userId ', onlineUser.userId)
|
||||
|
||||
if (onlineUser.sumOpen || onlineUser.sumClosed) {
|
||||
index = state.findIndex((e) => ((onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) || (onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId)))
|
||||
}
|
||||
else {
|
||||
index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`)
|
||||
index = state.findIndex(
|
||||
(e) =>
|
||||
(onlineUser.sumOpen && e.id === onlineUser.sumOpen.userId) ||
|
||||
(onlineUser.sumClosed && e.id === onlineUser.sumClosed.userId)
|
||||
);
|
||||
} else {
|
||||
index = state.findIndex((e) => `${e.id}` === `${onlineUser.userId}`);
|
||||
}
|
||||
|
||||
//console.log(' *********************** index: ', index)
|
||||
|
||||
|
||||
|
||||
if (index !== -1) {
|
||||
|
||||
// console.log('ENTROU NO 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 ("sumOnlineTime" in state[index]) {
|
||||
state[index].sumOnlineTime['sum'] = (onlineUser.onlineTime).split(" ")[1]
|
||||
}
|
||||
else if (!("sumOnlineTime" in state[index])) {
|
||||
state[index].sumOnlineTime = { userId: onlineUser.userId, sum: (onlineUser.onlineTime).split(" ")[1] }
|
||||
state[index].sumOnlineTime["sum"] = onlineUser.onlineTime.split(" ")[1];
|
||||
} else if (!("sumOnlineTime" in state[index])) {
|
||||
state[index].sumOnlineTime = {
|
||||
userId: onlineUser.userId,
|
||||
sum: onlineUser.onlineTime.split(" ")[1],
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
if (onlineUser.sumOpen) {
|
||||
|
||||
if ("sumOpen" in state[index]) {
|
||||
// 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])) {
|
||||
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1')
|
||||
state[index].sumOpen = onlineUser.sumOpen
|
||||
state[index].sumOpen = onlineUser.sumOpen;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
if (onlineUser.sumClosed) {
|
||||
|
||||
if ("sumClosed" in state[index]) {
|
||||
// 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])) {
|
||||
// console.log(' >>>>>>>>>>>>>>>>>> sumOpen 1')
|
||||
state[index].sumClosed = onlineUser.sumClosed
|
||||
state[index].sumClosed = onlineUser.sumClosed;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
return [...state]
|
||||
|
||||
return [...state];
|
||||
}
|
||||
|
||||
|
||||
|
||||
if (action.type === "RESET") {
|
||||
return [];
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
const reducer = (state, action) => {
|
||||
|
||||
if (action.type === "LOAD_USERS") {
|
||||
const users = action.payload;
|
||||
const newUsers = [];
|
||||
|
@ -253,24 +216,20 @@ const reducer = (state, action) => {
|
|||
}
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
function Item(props) {
|
||||
const { sx, ...other } = props;
|
||||
return (
|
||||
<Box
|
||||
sx={{
|
||||
bgcolor: (theme) => (theme.palette.mode === 'dark' ? '#101010' : '#fff'),
|
||||
color: (theme) => (theme.palette.mode === 'dark' ? 'grey.300' : 'grey.800'),
|
||||
border: '1px solid',
|
||||
borderColor: (theme) =>
|
||||
theme.palette.mode === 'dark' ? 'grey.800' : 'grey.300',
|
||||
bgcolor: (theme) => (theme.palette.mode === "dark" ? "#101010" : "#fff"),
|
||||
color: (theme) => (theme.palette.mode === "dark" ? "grey.300" : "grey.800"),
|
||||
border: "1px solid",
|
||||
borderColor: (theme) => (theme.palette.mode === "dark" ? "grey.800" : "grey.300"),
|
||||
p: 1,
|
||||
m: 1,
|
||||
borderRadius: 2,
|
||||
fontSize: '0.875rem',
|
||||
fontWeight: '700',
|
||||
fontSize: "0.875rem",
|
||||
fontWeight: "700",
|
||||
...sx,
|
||||
}}
|
||||
{...other}
|
||||
|
@ -280,33 +239,28 @@ function Item(props) {
|
|||
|
||||
Item.propTypes = {
|
||||
sx: PropTypes.oneOfType([
|
||||
PropTypes.arrayOf(
|
||||
PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool]),
|
||||
),
|
||||
PropTypes.arrayOf(PropTypes.oneOfType([PropTypes.func, PropTypes.object, PropTypes.bool])),
|
||||
PropTypes.func,
|
||||
PropTypes.object,
|
||||
]),
|
||||
};
|
||||
|
||||
|
||||
|
||||
let columnsData = [
|
||||
{ title: 'Unidade', field: 'whatsapp.name' },
|
||||
{ title: 'Atendente', field: 'user.name' },
|
||||
{ title: 'Contato', field: 'contact.number' },
|
||||
{ title: 'Nome', field: 'contact.name' },
|
||||
{ title: 'Assunto', field: 'queue.name' },
|
||||
{ title: "Unidade", field: "whatsapp.name" },
|
||||
{ title: "Atendente", field: "user.name" },
|
||||
{ title: "Contato", field: "contact.number" },
|
||||
{ title: "Nome", field: "contact.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: 'Status de encerramento', field: 'statusChatEnd'}];
|
||||
|
||||
{ title: "Status de encerramento", field: "statusChatEnd" },
|
||||
];
|
||||
|
||||
const Report = () => {
|
||||
|
||||
const csvLink = useRef()
|
||||
const csvLink = useRef();
|
||||
|
||||
const { user: userA } = useContext(AuthContext);
|
||||
|
||||
|
@ -317,39 +271,32 @@ const Report = () => {
|
|||
const [pageNumber, setPageNumber] = useState(1);
|
||||
const [users, dispatch] = useReducer(reducer, []);
|
||||
//const [columns, setColums] = useState([])
|
||||
const [startDate, setDatePicker1] = useState(new Date())
|
||||
const [endDate, setDatePicker2] = useState(new Date())
|
||||
const [userId, setUser] = useState(null)
|
||||
const [query, dispatchQ] = useReducer(reducerQ, [])
|
||||
const [startDate, setDatePicker1] = useState(new Date());
|
||||
const [endDate, setDatePicker2] = useState(new Date());
|
||||
const [userId, setUser] = useState(null);
|
||||
const [query, dispatchQ] = useReducer(reducerQ, []);
|
||||
|
||||
const [dataCSV, setDataCSV] = useState([])
|
||||
const [dataCSV, setDataCSV] = useState([]);
|
||||
const [isMount, setIsMount] = useState(true);
|
||||
|
||||
const [reportOption, setReport] = useState('1')
|
||||
const [reporList,] = useState(report)
|
||||
const [profile, setProfile] = useState('')
|
||||
const [reportOption, setReport] = useState("1");
|
||||
const [reporList] = useState(report);
|
||||
const [profile, setProfile] = useState("");
|
||||
const [dataRows, setData] = useState([]);
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
dispatch({ type: "RESET" });
|
||||
dispatchQ({ type: "RESET" })
|
||||
dispatchQ({ type: "RESET" });
|
||||
|
||||
setPageNumber(1);
|
||||
}, [searchParam, profile]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
//setLoading(true);
|
||||
|
||||
const delayDebounceFn = setTimeout(() => {
|
||||
|
||||
const fetchUsers = async () => {
|
||||
try {
|
||||
|
||||
//console.log('profile: ', profile)
|
||||
|
||||
const { data } = await api.get("/users/", {
|
||||
|
@ -359,34 +306,27 @@ const Report = () => {
|
|||
dispatch({ type: "LOAD_USERS", payload: data.users });
|
||||
//setHasMore(data.hasMore);
|
||||
//setLoading(false);
|
||||
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchUsers();
|
||||
|
||||
}, 500);
|
||||
return () => clearTimeout(delayDebounceFn);
|
||||
}, [searchParam, pageNumber, reportOption, profile]);
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
//setLoading(true);
|
||||
|
||||
const delayDebounceFn = setTimeout(() => {
|
||||
|
||||
const fetchQueries = async () => {
|
||||
try {
|
||||
|
||||
if (reportOption === '1') {
|
||||
|
||||
const dataQuery = await api.get("/reports/", { params: { userId, startDate, endDate }, });
|
||||
dispatchQ({ type: "RESET" })
|
||||
if (reportOption === "1") {
|
||||
const dataQuery = await api.get("/reports/", {
|
||||
params: { userId, startDate, endDate },
|
||||
});
|
||||
dispatchQ({ type: "RESET" });
|
||||
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data });
|
||||
|
||||
//setLoading(false);
|
||||
|
@ -394,12 +334,11 @@ const Report = () => {
|
|||
// console.log('dataQuery: ', dataQuery.data)
|
||||
|
||||
// console.log()
|
||||
|
||||
}
|
||||
else if (reportOption === '2') {
|
||||
|
||||
const dataQuery = await api.get("/reports/user/services", { params: { userId, startDate, endDate }, });
|
||||
dispatchQ({ type: "RESET" })
|
||||
} else if (reportOption === "2") {
|
||||
const dataQuery = await api.get("/reports/user/services", {
|
||||
params: { userId, startDate, endDate },
|
||||
});
|
||||
dispatchQ({ type: "RESET" });
|
||||
dispatchQ({ type: "LOAD_QUERY", payload: dataQuery.data });
|
||||
|
||||
//setLoading(false);
|
||||
|
@ -407,62 +346,46 @@ const Report = () => {
|
|||
// console.log('REPORT 2 dataQuery : ', dataQuery.data)
|
||||
|
||||
//console.log()
|
||||
|
||||
}
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchQueries();
|
||||
|
||||
}, 500);
|
||||
return () => clearTimeout(delayDebounceFn);
|
||||
|
||||
}, [userId, startDate, endDate, reportOption]);
|
||||
|
||||
|
||||
// Get from child 1
|
||||
const datePicker1Value = (data) => {
|
||||
|
||||
setDatePicker1(data)
|
||||
}
|
||||
setDatePicker1(data);
|
||||
};
|
||||
|
||||
// Get from child 2
|
||||
const datePicker2Value = (data) => {
|
||||
|
||||
setDatePicker2(data)
|
||||
}
|
||||
setDatePicker2(data);
|
||||
};
|
||||
|
||||
// Get from child 3
|
||||
const textFieldSelectUser = (data) => {
|
||||
|
||||
setUser(data)
|
||||
}
|
||||
|
||||
|
||||
|
||||
setUser(data);
|
||||
};
|
||||
|
||||
// Get from report option
|
||||
const reportValue = (data) => {
|
||||
|
||||
setReport(data)
|
||||
setReport(data);
|
||||
|
||||
// console.log(' data: ', data)
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (reportOption === '1') {
|
||||
setProfile('')
|
||||
if (reportOption === "1") {
|
||||
setProfile("");
|
||||
} else if (reportOption === "2") {
|
||||
setProfile("user");
|
||||
}
|
||||
else if (reportOption === '2') {
|
||||
setProfile('user')
|
||||
}
|
||||
|
||||
}, [reportOption])
|
||||
|
||||
}, [reportOption]);
|
||||
|
||||
// useEffect(() => {
|
||||
|
||||
|
@ -470,106 +393,85 @@ const Report = () => {
|
|||
|
||||
// }, [query])
|
||||
|
||||
|
||||
|
||||
// test del
|
||||
|
||||
const handleCSVMessages = () => {
|
||||
|
||||
// setLoading(true);
|
||||
|
||||
const fetchQueries = async () => {
|
||||
|
||||
try {
|
||||
|
||||
const dataQuery = await api.get("/reports/messages", { params: { userId, startDate, endDate }, });
|
||||
const dataQuery = await api.get("/reports/messages", {
|
||||
params: { userId, startDate, endDate },
|
||||
});
|
||||
|
||||
// console.log('dataQuery messages: ', dataQuery.data)
|
||||
|
||||
if (dataQuery.data.length > 0) {
|
||||
|
||||
let dataCSVFormat = dataQuery.data;
|
||||
|
||||
for (var i = 0; i < dataCSVFormat.length; i++) {
|
||||
if (dataCSVFormat[i].fromMe) {
|
||||
dataCSVFormat[i].fromMe = 'Atendente'
|
||||
}
|
||||
else {
|
||||
dataCSVFormat[i].fromMe = 'Cliente'
|
||||
dataCSVFormat[i].fromMe = "Atendente";
|
||||
} else {
|
||||
dataCSVFormat[i].fromMe = "Cliente";
|
||||
}
|
||||
}
|
||||
|
||||
// console.log('dataCSVFormat: ', dataCSVFormat)
|
||||
|
||||
setDataCSV(dataCSVFormat)
|
||||
setDataCSV(dataCSVFormat);
|
||||
setIsMount(false);
|
||||
// setDataCSV(dataQuery.data)
|
||||
}
|
||||
|
||||
// setLoading(false);
|
||||
|
||||
} catch (err) {
|
||||
console.log(err);
|
||||
}
|
||||
};
|
||||
|
||||
fetchQueries();
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (isMount) {
|
||||
return;
|
||||
}
|
||||
|
||||
csvLink.current.link.click()
|
||||
|
||||
csvLink.current.link.click();
|
||||
}, [dataCSV, isMount, csvLink]);
|
||||
|
||||
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
if (reportOption === '2') {
|
||||
|
||||
if (reportOption === "2") {
|
||||
const socket = openSocket(process.env.REACT_APP_BACKEND_URL);
|
||||
|
||||
socket.on("onlineStatus", (data) => {
|
||||
|
||||
// setLoading(true);
|
||||
|
||||
|
||||
let date = new Date().toLocaleDateString('pt-BR').split('/')
|
||||
let dateToday = `${date[2]}-${date[1]}-${date[0]}`
|
||||
let date = new Date().toLocaleDateString("pt-BR").split("/");
|
||||
let dateToday = `${date[2]}-${date[1]}-${date[0]}`;
|
||||
|
||||
// console.log('date: ', new Date(startDate).toLocaleDateString('pt-BR'))
|
||||
// console.log('date2: ', startDate)
|
||||
|
||||
|
||||
if (data.action === "logout" || (data.action === "update" &&
|
||||
((`${startDate}` === `${endDate}`) && (`${endDate}` === `${dateToday}`) && (`${startDate}` === `${dateToday}`)))) {
|
||||
|
||||
if (
|
||||
data.action === "logout" ||
|
||||
(data.action === "update" &&
|
||||
`${startDate}` === `${endDate}` &&
|
||||
`${endDate}` === `${dateToday}` &&
|
||||
`${startDate}` === `${dateToday}`)
|
||||
) {
|
||||
//console.log('UPDATE FROM ONLINE/OFFLINE LOGED USERS: ', data.userOnlineTime, ' | data.action : ', data.action)
|
||||
|
||||
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 });
|
||||
}
|
||||
|
||||
// setLoading(false);
|
||||
|
||||
});
|
||||
|
||||
socket.on("user", (data) => {
|
||||
|
||||
if (data.action === "delete") {
|
||||
// console.log(' entrou no delete user: ', data)
|
||||
dispatch({ type: "DELETE_USER", payload: +data.userId });
|
||||
|
@ -579,13 +481,9 @@ const Report = () => {
|
|||
return () => {
|
||||
socket.disconnect();
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
|
||||
}, [reportOption, startDate, endDate]);
|
||||
|
||||
|
||||
// const handleDeleteRows = (id) => {
|
||||
|
||||
// let _data = [...dataRows];
|
||||
|
@ -597,20 +495,20 @@ const Report = () => {
|
|||
|
||||
// };
|
||||
|
||||
|
||||
useEffect(() => {
|
||||
|
||||
//if (!loading) {
|
||||
|
||||
|
||||
// setData(query.map(({ scheduleReminder, ...others }) => (
|
||||
// { ...others, 'scheduleReminder': `${others.statusChatEndId}` === '3' ? 'Agendamento' : 'Lembrete' }
|
||||
// )))
|
||||
// }
|
||||
|
||||
setData(query.map((column) => { return { ...column } }))
|
||||
|
||||
}, [query])
|
||||
setData(
|
||||
query.map((column) => {
|
||||
return { ...column };
|
||||
})
|
||||
);
|
||||
}, [query]);
|
||||
|
||||
const handleLogouOnlineUser = async (userId) => {
|
||||
try {
|
||||
|
@ -620,19 +518,15 @@ const Report = () => {
|
|||
} catch (err) {
|
||||
// toastError(err);
|
||||
}
|
||||
|
||||
|
||||
};
|
||||
|
||||
|
||||
return (
|
||||
|
||||
<Can
|
||||
role={userA.profile}
|
||||
perform="ticket-report:show"
|
||||
yes={() => (
|
||||
|
||||
<MainContainer>
|
||||
|
||||
<Box sx={{ display: 'grid', gridTemplateColumns: 'repeat(3, 1fr)' }}>
|
||||
|
||||
<Item><SelectField func={textFieldSelectUser} emptyField={true} header={'Usuário'} currencies={users.map((obj) => {
|
||||
|
@ -642,13 +536,27 @@ const Report = () => {
|
|||
<Item><DatePicker1 func={datePicker1Value} minDate={true} startEmpty={false} title={'Data inicio'} /></Item>
|
||||
<Item><DatePicker2 func={datePicker2Value} minDate={false} startEmpty={false} title={'Data fim'} /></Item>
|
||||
|
||||
<Item sx={{ display: 'grid', gridColumn: '4 / 5', }}>
|
||||
<Item>
|
||||
<DatePicker1
|
||||
func={datePicker1Value}
|
||||
minDate={false}
|
||||
startEmpty={false}
|
||||
title={"Data inicio"}
|
||||
/>
|
||||
</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} />
|
||||
|
||||
<div style={{ margin: '2px' }}></div>
|
||||
|
||||
{reportOption === '1' &&
|
||||
<div style={{ margin: "2px" }}></div>
|
||||
|
||||
<div>
|
||||
{/* <Button
|
||||
|
@ -663,125 +571,95 @@ const Report = () => {
|
|||
<CSVLink
|
||||
data={dataCSV}
|
||||
headers={columns}
|
||||
filename={'Relatorio_detalhado_atendimento_atendentes.csv'}
|
||||
target={'_blank'}
|
||||
ref={csvLink} />
|
||||
filename={"Relatorio_detalhado_atendimento_atendentes.csv"}
|
||||
target={"_blank"}
|
||||
ref={csvLink}
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
}
|
||||
|
||||
|
||||
)}
|
||||
</Item>
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
</Box>
|
||||
|
||||
<Box sx={{
|
||||
display: 'grid',
|
||||
}}>
|
||||
|
||||
<Item sx={{ gridColumn: '1', gridRow: 'span 1' }}>
|
||||
|
||||
{reportOption === '1' &&
|
||||
|
||||
<MTable data={query}
|
||||
<Box
|
||||
sx={{
|
||||
display: "grid",
|
||||
}}
|
||||
>
|
||||
<Item sx={{ gridColumn: "1", gridRow: "span 1" }}>
|
||||
{reportOption === "1" && (
|
||||
<MTable
|
||||
data={query}
|
||||
columns={columnsData}
|
||||
hasChild={true}
|
||||
removeClickRow={false}
|
||||
table_title={'Atendimento por atendentes'} />
|
||||
}
|
||||
{reportOption === '2' &&
|
||||
|
||||
table_title={"Atendimento por atendentes"}
|
||||
/>
|
||||
)}
|
||||
{reportOption === "2" && (
|
||||
<MaterialTable
|
||||
|
||||
localization={{
|
||||
|
||||
header: {
|
||||
actions: 'Deslogar'
|
||||
actions: "Deslogar",
|
||||
},
|
||||
|
||||
}}
|
||||
|
||||
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: '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) => {
|
||||
|
||||
if (rowData['statusOnline'] && rowData['statusOnline'].status) {
|
||||
|
||||
if (rowData['statusOnline'].status === 'offline') {
|
||||
|
||||
if (rowData["statusOnline"] && rowData["statusOnline"].status) {
|
||||
if (rowData["statusOnline"].status === "offline") {
|
||||
return { color: "red" };
|
||||
}
|
||||
else if (rowData['statusOnline'].status === 'online') {
|
||||
} else if (rowData["statusOnline"].status === "online") {
|
||||
return { color: "green" };
|
||||
}
|
||||
else if (rowData['statusOnline'].status === 'logout...') {
|
||||
} else if (rowData["statusOnline"].status === "logout...") {
|
||||
return { color: "orange" };
|
||||
}
|
||||
else if (rowData['statusOnline'].status === 'waiting...') {
|
||||
} else if (rowData["statusOnline"].status === "waiting...") {
|
||||
return { color: "orange" };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
},
|
||||
},
|
||||
|
||||
},
|
||||
|
||||
{ title: 'Tempo online', field: 'sumOnlineTime.sum' },
|
||||
{ title: 'Data inicio', field: 'startDate' },
|
||||
{ title: 'Data fim', field: 'endDate' },
|
||||
{ title: 'Em atendimento', field: 'sumOpen.count' },
|
||||
{ title: 'Finalizado', field: 'sumClosed.count' },
|
||||
|
||||
]
|
||||
}
|
||||
{ title: "Tempo online", field: "sumOnlineTime.sum" },
|
||||
{ title: "Data inicio", field: "startDate" },
|
||||
{ title: "Data fim", field: "endDate" },
|
||||
{ title: "Em atendimento", field: "sumOpen.count" },
|
||||
{ title: "Finalizado", field: "sumClosed.count" },
|
||||
]}
|
||||
data={dataRows}
|
||||
// icons={tableIcons}
|
||||
|
||||
actions={[
|
||||
(rowData) => {
|
||||
|
||||
if (rowData.statusOnline &&
|
||||
rowData.statusOnline['status'] &&
|
||||
rowData.statusOnline['status'] === 'online') {
|
||||
|
||||
|
||||
if (
|
||||
rowData.statusOnline &&
|
||||
rowData.statusOnline["status"] &&
|
||||
rowData.statusOnline["status"] === "online"
|
||||
) {
|
||||
return {
|
||||
icon: LogoutIcon,
|
||||
tooltip: 'deslogar',
|
||||
tooltip: "deslogar",
|
||||
disable: false,
|
||||
onClick: (event, rowData) => {
|
||||
|
||||
// console.log(' ROW DATA INFO: ', rowData, ' | rowData: ', rowData.id)
|
||||
handleLogouOnlineUser(rowData.id)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
handleLogouOnlineUser(rowData.id);
|
||||
},
|
||||
};
|
||||
}
|
||||
},
|
||||
]}
|
||||
|
||||
|
||||
options={
|
||||
{
|
||||
options={{
|
||||
search: true,
|
||||
selection: false,
|
||||
paging: false,
|
||||
padding: 'dense',
|
||||
padding: "dense",
|
||||
sorting: true,
|
||||
//loadingType: 'linear',
|
||||
searchFieldStyle: {
|
||||
|
@ -791,14 +669,13 @@ const Report = () => {
|
|||
pageSize: 20,
|
||||
headerStyle: {
|
||||
position: "sticky",
|
||||
top: "0"
|
||||
top: "0",
|
||||
},
|
||||
maxBodyHeight: "400px",
|
||||
|
||||
rowStyle: {
|
||||
fontSize: 14,
|
||||
}
|
||||
|
||||
},
|
||||
|
||||
// cellStyle: (rowData) => {
|
||||
// return {
|
||||
|
@ -807,26 +684,16 @@ const Report = () => {
|
|||
|
||||
// };
|
||||
// }
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
}}
|
||||
/>
|
||||
|
||||
}
|
||||
|
||||
)}
|
||||
</Item>
|
||||
|
||||
</Box>
|
||||
</MainContainer>
|
||||
|
||||
)}
|
||||
/>
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
export default Report;
|
||||
|
||||
|
|
|
@ -0,0 +1,20 @@
|
|||
import React from "react";
|
||||
import { useParams } from "react-router-dom";
|
||||
|
||||
import TicketsStyled from "./Tickets.style";
|
||||
|
||||
import TicketsManager from "../../components/Ticket/TicketsManager/TicketsManager";
|
||||
import Ticket from "../../components/Ticket/Ticket";
|
||||
|
||||
const Tickets = () => {
|
||||
const { ticketId } = useParams();
|
||||
return (
|
||||
<TicketsStyled>
|
||||
<TicketsManager />
|
||||
{ticketId ? <Ticket /> : <div>Não tem nada</div>}
|
||||
</TicketsStyled>
|
||||
);
|
||||
};
|
||||
|
||||
export default Tickets;
|
||||
|
|
@ -0,0 +1,10 @@
|
|||
import styled from "styled-components";
|
||||
|
||||
const TicketsStyled = styled.div`
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
min-height: auto;
|
||||
|
||||
`;
|
||||
|
||||
export default TicketsStyled
|
|
@ -4,7 +4,7 @@ import Grid from "@material-ui/core/Grid";
|
|||
import Paper from "@material-ui/core/Paper";
|
||||
import { makeStyles } from "@material-ui/core/styles";
|
||||
|
||||
import TicketsManager from "../../components/TicketsManager/";
|
||||
import TicketsManager from "../../components/Ticket/TicketsManager/index";
|
||||
import Ticket from "../../components/Ticket/";
|
||||
|
||||
import { i18n } from "../../translate/i18n";
|
||||
|
|
|
@ -2,7 +2,7 @@ import React, { useContext } from "react";
|
|||
import { Route as RouterRoute, Redirect } from "react-router-dom";
|
||||
|
||||
import { AuthContext } from "../context/Auth/AuthContext";
|
||||
import Loading from "../components/LoadingScreen/Loading"
|
||||
import LoadingScreen from "../components/LoadingScreen/LoadingScreen"
|
||||
|
||||
const Route = ({ component: Component, isPrivate = false, ...rest }) => {
|
||||
const { isAuth, loading } = useContext(AuthContext);
|
||||
|
@ -10,7 +10,7 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => {
|
|||
if (!isAuth && isPrivate) {
|
||||
return (
|
||||
<>
|
||||
{loading && <Loading />}
|
||||
{loading && <LoadingScreen />}
|
||||
<Redirect to={{ pathname: "/login", state: { from: rest.location } }} />
|
||||
</>
|
||||
);
|
||||
|
@ -19,7 +19,7 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => {
|
|||
if (isAuth && !isPrivate) {
|
||||
return (
|
||||
<>
|
||||
{loading && <Loading />}
|
||||
{loading && <LoadingScreen />}
|
||||
<Redirect to={{ pathname: "/", state: { from: rest.location } }} />;
|
||||
</>
|
||||
);
|
||||
|
@ -27,7 +27,7 @@ const Route = ({ component: Component, isPrivate = false, ...rest }) => {
|
|||
|
||||
return (
|
||||
<>
|
||||
{loading && <Loading />}
|
||||
{loading && <LoadingScreen />}
|
||||
<RouterRoute {...rest} component={Component} />
|
||||
</>
|
||||
);
|
||||
|
|
|
@ -10,7 +10,7 @@ import SchedulesReminder from "../pages/SchedulesReminder/";
|
|||
|
||||
import Login from "../pages/Login/";
|
||||
import Signup from "../pages/Signup/";
|
||||
import Tickets from "../pages/Tickets/";
|
||||
import Tickets from "../pages/Tickets/Tickets";
|
||||
import Connections from "../pages/Connections/";
|
||||
import Settings from "../pages/Settings/";
|
||||
import Users from "../pages/Users";
|
||||
|
|
|
@ -39,22 +39,24 @@ export const color = {
|
|||
grisOscuro: "#3C3C3B",
|
||||
blanco: "#FFFFFF",
|
||||
},
|
||||
complement:{
|
||||
complement: {
|
||||
azulCielo: "#55A5DC",
|
||||
azulOscuro: "#212F3C",
|
||||
crisClaro: "#F6F6F6",
|
||||
},
|
||||
status:{
|
||||
status: {
|
||||
no: "#FF0000",
|
||||
yes: "#00BE1E",
|
||||
warning: "#FFC700"
|
||||
warning: "#ffc800c7",
|
||||
},
|
||||
gradient:{
|
||||
bgOpacity:"#212F3CD8",
|
||||
placeholder:"#ffffff83"
|
||||
gradient: {
|
||||
bgOpacity: "#212f3cd7",
|
||||
placeholder: "#ffffff83",
|
||||
border: "#55A5DC3F",
|
||||
text: "#AEBAC1",
|
||||
},
|
||||
shadow: {
|
||||
dark: "2px 2px 4px 2px",
|
||||
},
|
||||
shadow:{
|
||||
dark:"2px 2px 4px 2px"
|
||||
}
|
||||
};
|
||||
|
||||
|
|
Loading…
Reference in New Issue