Merge pull request from RenatoDiGiacomo/layout/tickets

Layout/tickets
pull/14/head
Renato Di Giacomo 2022-07-28 12:23:24 -03:00 committed by GitHub
commit 8cb36fc636
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 1693 additions and 762 deletions

15
.vscode/launch.json vendored 100644
View File

@ -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}"
}
]
}

View File

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

View File

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

View File

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

View File

@ -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)};
`;

View File

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

View File

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

View File

@ -1,7 +1,6 @@
import React from "react";
import PageTitle from "../../PageTitle/PageTitle";
import MainContainerStyled, {
TitleContainerStyled,
ContentContainerStyled,

View File

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

View File

@ -1,5 +1,5 @@
import React from "react";
import BtnComponent from "../Base/BTN/Btn";
import BtnComponent from "../Base/Btn/Btn";
import {
ConfirmationModalStyled,

View File

@ -1,9 +0,0 @@
import React from "react";
import LoadingStyled from "./Loading.style"
const Loading = () => {
return <LoadingStyled/>;
};
export default Loading;

View File

@ -0,0 +1,8 @@
import React from "react";
import LoadingStyled from "./LoadingScreen.style"
const LoadingScreen = () => {
return <LoadingStyled/>;
};
export default LoadingScreen;

View File

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

View File

@ -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} />

View File

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

View File

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

View File

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

View File

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

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

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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",
})}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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(() => {

View File

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

View File

@ -12,167 +12,147 @@ import ButtonWithSpinner from "../ButtonWithSpinner";
import toastError from "../../errors/toastError";
import { AuthContext } from "../../context/Auth/AuthContext";
import Modal from "../ChatEnd/ModalChatEnd";
import { render } from '@testing-library/react';
import Modal from "../ChatEnd/ModalChatEnd";
import { render } from "@testing-library/react";
const useStyles = makeStyles(theme => ({
actionButtons: {
marginRight: 6,
flex: "none",
alignSelf: "center",
marginLeft: "auto",
"& > *": {
margin: theme.spacing(1),
},
},
const useStyles = makeStyles((theme) => ({
actionButtons: {
marginRight: 6,
flex: "none",
alignSelf: "center",
marginLeft: "auto",
"& > *": {
margin: theme.spacing(1),
},
},
}));
const TicketActionButtons = ({ ticket, statusChatEnd }) => {
const classes = useStyles();
const history = useHistory();
const [anchorEl, setAnchorEl] = useState(null);
const [loading, setLoading] = useState(false);
const ticketOptionsMenuOpen = Boolean(anchorEl);
const { user } = useContext(AuthContext);
const handleOpenTicketOptionsMenu = e => {
setAnchorEl(e.currentTarget);
};
const classes = useStyles();
const history = useHistory();
const [anchorEl, setAnchorEl] = useState(null);
const [loading, setLoading] = useState(false);
const ticketOptionsMenuOpen = Boolean(anchorEl);
const { user } = useContext(AuthContext);
const handleCloseTicketOptionsMenu = e => {
setAnchorEl(null);
};
const handleOpenTicketOptionsMenu = (e) => {
setAnchorEl(e.currentTarget);
};
const chatEndVal = (data) => {
if(data){
const handleCloseTicketOptionsMenu = (e) => {
setAnchorEl(null);
};
data = {...data, 'ticketId': ticket.id}
console.log('ChatEnd: ',(data));
const chatEndVal = (data) => {
if (data) {
data = { ...data, ticketId: ticket.id };
handleUpdateTicketStatus(null, "closed", user?.id, data)
console.log("ChatEnd: ", data);
}
}
handleUpdateTicketStatus(null, "closed", user?.id, data);
}
};
const handleModal = (/*status, userId*/) => {
render(
<Modal
modal_header={"Finalização de Atendimento"}
func={chatEndVal}
statusChatEnd={statusChatEnd}
ticketId={ticket.id}
/>
);
};
const handleModal = (/*status, userId*/) => {
const handleUpdateTicketStatus = async (e, status, userId, schedulingData = {}) => {
setLoading(true);
try {
if (status === "closed") {
await api.put(`/tickets/${ticket.id}`, {
status: status,
userId: userId || null,
schedulingNotifyData: JSON.stringify(schedulingData),
});
} else {
await api.put(`/tickets/${ticket.id}`, {
status: status,
userId: userId || null,
});
}
render(<Modal
modal_header={'Finalização de Atendimento'}
func={chatEndVal}
statusChatEnd={statusChatEnd}
ticketId={ticket.id}
/>)
};
setLoading(false);
if (status === "open") {
history.push(`/tickets/${ticket.id}`);
} else {
history.push("/tickets");
}
} catch (err) {
setLoading(false);
toastError(err);
}
};
const handleUpdateTicketStatus = async (e, status, userId, schedulingData={}) => {
setLoading(true);
try {
if(status==='closed'){
return (
<div className={classes.actionButtons}>
{ticket.status === "closed" && (
<ButtonWithSpinner
loading={loading}
startIcon={<Replay />}
size="small"
onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
>
{i18n.t("messagesList.header.buttons.reopen")}
</ButtonWithSpinner>
)}
{ticket.status === "open" && (
<>
<ButtonWithSpinner
loading={loading}
startIcon={<Replay />}
size="small"
onClick={(e) => handleUpdateTicketStatus(e, "pending", null)}
>
{i18n.t("messagesList.header.buttons.return")}
</ButtonWithSpinner>
await api.put(`/tickets/${ticket.id}`, {
status: status,
userId: userId || null,
schedulingNotifyData: JSON.stringify(schedulingData)
});
<ButtonWithSpinner
loading={loading}
size="small"
variant="contained"
color="primary"
onClick={(e) => {
handleModal();
// handleUpdateTicketStatus(e, "closed", user?.id)
}}
>
{i18n.t("messagesList.header.buttons.resolve")}
</ButtonWithSpinner>
}
else{
await api.put(`/tickets/${ticket.id}`, {
status: status,
userId: userId || null
});
}
setLoading(false);
if (status === "open") {
history.push(`/tickets/${ticket.id}`);
} else {
history.push("/tickets");
}
} catch (err) {
setLoading(false);
toastError(err);
}
};
return (
<div className={classes.actionButtons}>
{ticket.status === "closed" && (
<ButtonWithSpinner
loading={loading}
startIcon={<Replay />}
size="small"
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)}
>
{i18n.t("messagesList.header.buttons.reopen")}
</ButtonWithSpinner>
)}
{ticket.status === "open" && (
<>
<ButtonWithSpinner
loading={loading}
startIcon={<Replay />}
size="small"
onClick={e => handleUpdateTicketStatus(e, "pending", null)}
>
{i18n.t("messagesList.header.buttons.return")}
</ButtonWithSpinner>
<ButtonWithSpinner
loading={loading}
size="small"
variant="contained"
color="primary"
onClick={e => {
handleModal()
// handleUpdateTicketStatus(e, "closed", user?.id)
}}
>
{i18n.t("messagesList.header.buttons.resolve")}
</ButtonWithSpinner>
<IconButton onClick={handleOpenTicketOptionsMenu}>
<MoreVert />
</IconButton>
<TicketOptionsMenu
ticket={ticket}
anchorEl={anchorEl}
menuOpen={ticketOptionsMenuOpen}
handleClose={handleCloseTicketOptionsMenu}
/>
</>
)}
{ticket.status === "pending" && (
<ButtonWithSpinner
loading={loading}
size="small"
variant="contained"
color="primary"
onClick={e => handleUpdateTicketStatus(e, "open", user?.id)}
>
{i18n.t("messagesList.header.buttons.accept")}
</ButtonWithSpinner>
)}
</div>
);
<IconButton onClick={handleOpenTicketOptionsMenu}>
<MoreVert />
</IconButton>
<TicketOptionsMenu
ticket={ticket}
anchorEl={anchorEl}
menuOpen={ticketOptionsMenuOpen}
handleClose={handleCloseTicketOptionsMenu}
/>
</>
)}
{ticket.status === "pending" && (
<ButtonWithSpinner
loading={loading}
size="small"
variant="contained"
color="primary"
onClick={(e) => handleUpdateTicketStatus(e, "open", user?.id)}
>
{i18n.t("messagesList.header.buttons.accept")}
</ButtonWithSpinner>
)}
</div>
);
};
export default TicketActionButtons;

View File

@ -6,55 +6,52 @@ 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 => {
onChange(e.target.value);
};
const TicketsQueueSelect = ({ userQueues, selectedQueueIds = [], onChange }) => {
const handleChange = (e) => {
onChange(e.target.value);
};
return (
<div style={{ width: 120, marginTop: -4 }}>
<FormControl fullWidth margin="dense">
<Select
multiple
displayEmpty
variant="outlined"
value={selectedQueueIds}
onChange={handleChange}
MenuProps={{
anchorOrigin: {
vertical: "bottom",
horizontal: "left",
},
transformOrigin: {
vertical: "top",
horizontal: "left",
},
getContentAnchorEl: null,
}}
renderValue={() => i18n.t("ticketsQueueSelect.placeholder")}
>
{userQueues?.length > 0 &&
userQueues.map(queue => (
<MenuItem dense key={queue.id} value={queue.id}>
<Checkbox
style={{
color: queue.color,
}}
size="small"
color="primary"
checked={selectedQueueIds.indexOf(queue.id) > -1}
/>
<ListItemText primary={queue.name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
return (
<div style={{ width: 120, marginTop: -4 }}>
<FormControl fullWidth margin="dense">
<Select
multiple
displayEmpty
variant="outlined"
value={selectedQueueIds}
onChange={handleChange}
MenuProps={{
anchorOrigin: {
vertical: "bottom",
horizontal: "left",
},
transformOrigin: {
vertical: "top",
horizontal: "left",
},
getContentAnchorEl: null,
}}
renderValue={() => i18n.t("ticketsQueueSelect.placeholder")}
>
{userQueues?.length > 0 &&
userQueues.map((queue) => (
<MenuItem dense key={queue.id} value={queue.id}>
<Checkbox
style={{
color: queue.color,
}}
size="small"
color="primary"
checked={selectedQueueIds.indexOf(queue.id) > -1}
/>
<ListItemText primary={queue.name} />
</MenuItem>
))}
</Select>
</FormControl>
</div>
);
};
export default TicketsQueueSelect;

View File

@ -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} />
@ -53,4 +85,5 @@ id: 2
name: "teste"
profile: "master"
queues: []
tokenVersion: 0 */
tokenVersion: 0 */

View File

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

File diff suppressed because it is too large Load Diff

View File

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

View File

@ -0,0 +1,10 @@
import styled from "styled-components";
const TicketsStyled = styled.div`
display: flex;
flex-direction: row;
min-height: auto;
`;
export default TicketsStyled

View File

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

View File

@ -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} />
</>
);

View File

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

View File

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